diff --git a/TODO.md b/TODO.md index cd1fc1e..d6e354a 100644 --- a/TODO.md +++ b/TODO.md @@ -27,10 +27,11 @@ - [ ] 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. -- [ ] File Import / Material Import - - [ ] Dropdown to choose import format -- [ ] File Import / Tidy3D File Import - - [ ] HDF and JSON file support, with appropriate choice of loose output socket. +- [x] File Import / Material Import + - [x] Dropdown to choose import format + - MERGED w/TIDY3D FILE IMPORT +- [x] File Import / Tidy3D File Import + - [x] HDF and JSON file support, with appropriate choice of loose output socket. - [ ] File Import / Array File Import - [ ] 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. @@ -40,8 +41,7 @@ ## Outputs - [x] Viewer - [ ] Remove image preview when disabling plots. - - [ ] BUG: CTRL+SHIFT+CLICK not on a node shows an error; should just do nothing. - - [ ] Auto-enable 3D preview when creating. + - [x] Auto-enable 3D preview when creating. - [ ] Test/support multiple viewers at the same time. - [ ] 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 - [ ] 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. +- [ ] `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 - [ ] 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. ## 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. - [ ] 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) - [ ] 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 +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 Reported: - (SOLVED) Unreported: - The `__mp_main__` bug. +- Animated properties within custom node trees don't update with the frame. See: ## Tidy3D bugs Unreported: diff --git a/pyproject.toml b/pyproject.toml index ce3e636..db57e4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "idna==3.3", "charset-normalizer==2.0.10", "certifi==2021.10.8", + "jax[cpu]>=0.4.26", ] readme = "README.md" requires-python = "~= 3.11" diff --git a/requirements-dev.lock b/requirements-dev.lock index ffb2f83..28fae0b 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -46,6 +46,9 @@ idna==3.3 importlib-metadata==6.11.0 # via dask # via tidy3d +jax==0.4.26 +jaxlib==0.4.26 + # via jax jmespath==1.0.1 # via boto3 # via botocore @@ -55,18 +58,27 @@ locket==1.0.0 # via partd matplotlib==3.8.3 # via tidy3d +ml-dtypes==0.4.0 + # via jax + # via jaxlib mpmath==1.3.0 # via sympy networkx==3.2 numpy==1.24.3 # via contourpy # via h5py + # via jax + # via jaxlib # via matplotlib + # via ml-dtypes + # via opt-einsum # via scipy # via shapely # via tidy3d # via trimesh # via xarray +opt-einsum==3.3.0 + # via jax packaging==24.0 # via dask # via h5netcdf @@ -112,6 +124,8 @@ ruff==0.3.2 s3transfer==0.5.2 # via boto3 scipy==1.12.0 + # via jax + # via jaxlib # via tidy3d shapely==2.0.3 # via tidy3d diff --git a/requirements.lock b/requirements.lock index a55b299..dad5d98 100644 --- a/requirements.lock +++ b/requirements.lock @@ -45,6 +45,9 @@ idna==3.3 importlib-metadata==6.11.0 # via dask # via tidy3d +jax==0.4.26 +jaxlib==0.4.26 + # via jax jmespath==1.0.1 # via boto3 # via botocore @@ -54,18 +57,27 @@ locket==1.0.0 # via partd matplotlib==3.8.3 # via tidy3d +ml-dtypes==0.4.0 + # via jax + # via jaxlib mpmath==1.3.0 # via sympy networkx==3.2 numpy==1.24.3 # via contourpy # via h5py + # via jax + # via jaxlib # via matplotlib + # via ml-dtypes + # via opt-einsum # via scipy # via shapely # via tidy3d # via trimesh # via xarray +opt-einsum==3.3.0 + # via jax packaging==24.0 # via dask # via h5netcdf @@ -110,6 +122,8 @@ rtree==1.2.0 s3transfer==0.5.2 # via boto3 scipy==1.12.0 + # via jax + # via jaxlib # via tidy3d shapely==2.0.3 # via tidy3d diff --git a/src/blender_maxwell/assets/import_geonodes.py b/src/blender_maxwell/assets/import_geonodes.py index d76474e..15f094b 100644 --- a/src/blender_maxwell/assets/import_geonodes.py +++ b/src/blender_maxwell/assets/import_geonodes.py @@ -49,7 +49,7 @@ class GeoNodes(enum.StrEnum): StructurePrimitiveCone = '_structure_primitive_cone' ## Monitor MonitorEHField = '_monitor_eh_field' - MonitorFieldPowerFlux = '_monitor_field_power_flux' + MonitorPowerFlux = '_monitor_power_flux' MonitorEpsTensor = '_monitor_eps_tensor' MonitorDiffraction = '_monitor_diffraction' MonitorProjCartEHField = '_monitor_proj_eh_field' @@ -114,7 +114,7 @@ GN_PARENT_PATHS: dict[GeoNodes, Path] = { GeoNodes.StructurePrimitiveCone: GN_INTERNAL_STRUCTURES_PATH, ## Monitor 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.MonitorDiffraction: GN_INTERNAL_STRUCTURES_PATH, GeoNodes.MonitorProjCartEHField: GN_INTERNAL_STRUCTURES_PATH, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py index 0eb21cc..e915224 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py @@ -5,6 +5,7 @@ import typing as typ from types import MappingProxyType # import colour ## TODO +import jax import numpy as np import sympy as sp import sympy.physics.units as spu @@ -105,7 +106,7 @@ class DataValueArray: """A simple, flat array of values with an optionally-attached unit. 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. None if unitless. """ @@ -181,12 +182,14 @@ class LazyDataValueRange: scaling: typx.Literal['lin', 'geom', 'log'] = 'lin' has_unit: bool = False + unit: spu.Quantity = False def rescale_to_unit(self, unit: spu.Quantity) -> typ.Self: if self.has_unit: return LazyDataValueRange( symbols=self.symbols, has_unit=self.has_unit, + unit=unit, start=spu.convert_to(self.start, unit), stop=spu.convert_to(self.stop, unit), steps=self.steps, @@ -205,8 +208,13 @@ class LazyDataValueRange: return LazyDataValueRange( symbols=self.symbols, has_unit=self.has_unit, - start=bound_cb(self.start if not reverse else self.stop), - stop=bound_cb(self.stop if not reverse else self.start), + unit=self.unit, + 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, scaling=self.scaling, ) @@ -276,3 +284,37 @@ class LazyDataValueSpectrum: values=self.as_func(*list(symbol_values.values())), 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 diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py index 580dead..2c2fb84 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py @@ -1,18 +1,19 @@ from .node_cats import NodeCategory as NC NODE_CAT_LABELS = { + # Analysis/ + NC.MAXWELLSIM_ANALYSIS: 'Analysis', + NC.MAXWELLSIM_ANALYSIS_MATH: 'Math', # Inputs/ NC.MAXWELLSIM_INPUTS: 'Inputs', - NC.MAXWELLSIM_INPUTS_IMPORTERS: 'Importers', NC.MAXWELLSIM_INPUTS_SCENE: 'Scene', - NC.MAXWELLSIM_INPUTS_PARAMETERS: 'Parameters', NC.MAXWELLSIM_INPUTS_CONSTANTS: 'Constants', - NC.MAXWELLSIM_INPUTS_LISTS: 'Lists', + NC.MAXWELLSIM_INPUTS_FILEIMPORTERS: 'File Importers', + NC.MAXWELLSIM_INPUTS_WEBIMPORTERS: 'Web Importers', # Outputs/ NC.MAXWELLSIM_OUTPUTS: 'Outputs', - NC.MAXWELLSIM_OUTPUTS_VIEWERS: 'Viewers', - NC.MAXWELLSIM_OUTPUTS_EXPORTERS: 'Exporters', - NC.MAXWELLSIM_OUTPUTS_PLOTTERS: 'Plotters', + NC.MAXWELLSIM_OUTPUTS_FILEEXPORTERS: 'File Exporters', + NC.MAXWELLSIM_OUTPUTS_WEBEXPORTERS: 'Web Exporters', # Sources/ NC.MAXWELLSIM_SOURCES: 'Sources', NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: 'Temporal Shapes', @@ -27,14 +28,10 @@ NODE_CAT_LABELS = { NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: 'Bound Conds', # Monitors/ NC.MAXWELLSIM_MONITORS: 'Monitors', - NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: 'Near-Field Projections', + NC.MAXWELLSIM_MONITORS_PROJECTED: 'Projected', # Simulations/ NC.MAXWELLSIM_SIMS: 'Simulations', - NC.MAXWELLSIM_SIMGRIDAXES: 'Sim Grid Axes', + NC.MAXWELLSIM_SIMS_SIMGRIDAXES: 'Sim Grid Axes', # Utilities/ NC.MAXWELLSIM_UTILITIES: 'Utilities', - NC.MAXWELLSIM_UTILITIES_CONVERTERS: 'Converters', - NC.MAXWELLSIM_UTILITIES_OPERATIONS: 'Operations', - # Viz/ - NC.MAXWELLSIM_VIZ: 'Viz', } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py index 35e2b4c..36085bc 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py @@ -7,19 +7,21 @@ from ....utils.blender_type_enum import BlenderTypeEnum, wrap_values_in_MT class NodeCategory(BlenderTypeEnum): MAXWELLSIM = enum.auto() + # Analysis/ + MAXWELLSIM_ANALYSIS = enum.auto() + MAXWELLSIM_ANALYSIS_MATH = enum.auto() + # Inputs/ MAXWELLSIM_INPUTS = enum.auto() - MAXWELLSIM_INPUTS_IMPORTERS = enum.auto() MAXWELLSIM_INPUTS_SCENE = enum.auto() - MAXWELLSIM_INPUTS_PARAMETERS = enum.auto() MAXWELLSIM_INPUTS_CONSTANTS = enum.auto() - MAXWELLSIM_INPUTS_LISTS = enum.auto() + MAXWELLSIM_INPUTS_FILEIMPORTERS = enum.auto() + MAXWELLSIM_INPUTS_WEBIMPORTERS = enum.auto() # Outputs/ MAXWELLSIM_OUTPUTS = enum.auto() - MAXWELLSIM_OUTPUTS_VIEWERS = enum.auto() - MAXWELLSIM_OUTPUTS_EXPORTERS = enum.auto() - MAXWELLSIM_OUTPUTS_PLOTTERS = enum.auto() + MAXWELLSIM_OUTPUTS_FILEEXPORTERS = enum.auto() + MAXWELLSIM_OUTPUTS_WEBEXPORTERS = enum.auto() # Sources/ MAXWELLSIM_SOURCES = enum.auto() @@ -39,19 +41,14 @@ class NodeCategory(BlenderTypeEnum): # Monitors/ MAXWELLSIM_MONITORS = enum.auto() - MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS = enum.auto() + MAXWELLSIM_MONITORS_PROJECTED = enum.auto() # Simulations/ MAXWELLSIM_SIMS = enum.auto() - MAXWELLSIM_SIMGRIDAXES = enum.auto() + MAXWELLSIM_SIMS_SIMGRIDAXES = enum.auto() # Utilities/ MAXWELLSIM_UTILITIES = enum.auto() - MAXWELLSIM_UTILITIES_CONVERTERS = enum.auto() - MAXWELLSIM_UTILITIES_OPERATIONS = enum.auto() - - # Viz/ - MAXWELLSIM_VIZ = enum.auto() @classmethod def get_tree(cls): diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py index 754cf23..1b71989 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py @@ -8,140 +8,114 @@ from ....utils.blender_type_enum import ( @append_cls_name_to_values 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 + WaveConstant = enum.auto() UnitSystem = enum.auto() ## Inputs / Scene - Time = enum.auto() - + #Time = enum.auto() ## Inputs / Web Importers Tidy3DWebImporter = enum.auto() - ## Inputs / File Importers Tidy3DFileImporter = enum.auto() - - ## Inputs / Parameters - NumberParameter = enum.auto() - PhysicalParameter = enum.auto() - ## Inputs / Constants - WaveConstant = enum.auto() ScientificConstant = enum.auto() NumberConstant = enum.auto() PhysicalConstant = enum.auto() BlenderConstant = enum.auto() - ## Inputs / Lists - RealList = enum.auto() - ComplexList = enum.auto() - # Outputs - ## Outputs / Viewers Viewer = enum.auto() - ValueViewer = enum.auto() - ConsoleViewer = enum.auto() - - ## Outputs / Exporters - JSONFileExporter = enum.auto() + ## Outputs / File Exporters Tidy3DWebExporter = enum.auto() + ## Outputs / Web Exporters + JSONFileExporter = enum.auto() # Sources - ## Sources / Temporal Shapes - GaussianPulseTemporalShape = enum.auto() - ContinuousWaveTemporalShape = enum.auto() - ListTemporalShape = enum.auto() - ## Sources / PointDipoleSource = enum.auto() - UniformCurrentSource = enum.auto() PlaneWaveSource = enum.auto() - ModeSource = enum.auto() - GaussianBeamSource = enum.auto() - AstigmaticGaussianBeamSource = enum.auto() - TFSFSource = enum.auto() - - EHEquivalenceSource = enum.auto() - EHSource = enum.auto() + UniformCurrentSource = enum.auto() + #ModeSource = enum.auto() + #GaussianBeamSource = enum.auto() + #AstigmaticGaussianBeamSource = enum.auto() + #TFSFSource = enum.auto() + #EHEquivalenceSource = enum.auto() + #EHSource = enum.auto() + ## Sources / Temporal Shapes + GaussianPulseTemporalShape = enum.auto() + #ContinuousWaveTemporalShape = enum.auto() + #ArrayTemporalShape = enum.auto() # Mediums LibraryMedium = enum.auto() - - PECMedium = enum.auto() - IsotropicMedium = enum.auto() - AnisotropicMedium = enum.auto() - - TripleSellmeierMedium = enum.auto() - SellmeierMedium = enum.auto() - PoleResidueMedium = enum.auto() - DrudeMedium = enum.auto() - DrudeLorentzMedium = enum.auto() - DebyeMedium = enum.auto() + #PECMedium = enum.auto() + #IsotropicMedium = enum.auto() + #AnisotropicMedium = enum.auto() + #TripleSellmeierMedium = enum.auto() + #SellmeierMedium = enum.auto() + #PoleResidueMedium = enum.auto() + #DrudeMedium = enum.auto() + #DrudeLorentzMedium = enum.auto() + #DebyeMedium = enum.auto() ## Mediums / Non-Linearities - AddNonLinearity = enum.auto() - ChiThreeSusceptibilityNonLinearity = enum.auto() - TwoPhotonAbsorptionNonLinearity = enum.auto() - KerrNonLinearity = enum.auto() + #AddNonLinearity = enum.auto() + #ChiThreeSusceptibilityNonLinearity = enum.auto() + #TwoPhotonAbsorptionNonLinearity = enum.auto() + #KerrNonLinearity = enum.auto() # Structures - ObjectStructure = enum.auto() + #ObjectStructure = enum.auto() GeoNodesStructure = enum.auto() - ScriptedStructure = enum.auto() - + #ScriptedStructure = enum.auto() ## Structures / Primitives BoxStructure = enum.auto() SphereStructure = enum.auto() - CylinderStructure = enum.auto() + #CylinderStructure = enum.auto() # Bounds BoundConds = enum.auto() - - ## Bounds / Bound Faces + ## Bounds / Bound Conds PMLBoundCond = enum.auto() PECBoundCond = enum.auto() PMCBoundCond = enum.auto() - BlochBoundCond = enum.auto() PeriodicBoundCond = enum.auto() AbsorbingBoundCond = enum.auto() # Monitors EHFieldMonitor = enum.auto() - FieldPowerFluxMonitor = enum.auto() - EpsilonTensorMonitor = enum.auto() - DiffractionMonitor = enum.auto() - - ## Monitors / Near-Field Projections - CartesianNearFieldProjectionMonitor = enum.auto() - ObservationAngleNearFieldProjectionMonitor = enum.auto() - KSpaceNearFieldProjectionMonitor = enum.auto() + PowerFluxMonitor = enum.auto() + #EpsilonTensorMonitor = enum.auto() + #DiffractionMonitor = enum.auto() + ## Monitors / Projected + #CartesianNearFieldProjectionMonitor = enum.auto() + #ObservationAngleNearFieldProjectionMonitor = enum.auto() + #KSpaceNearFieldProjectionMonitor = enum.auto() # Sims + FDTDSim = enum.auto() SimDomain = enum.auto() SimGrid = enum.auto() - ## Sims / Sim Grid Axis - AutomaticSimGridAxis = enum.auto() - ManualSimGridAxis = enum.auto() - UniformSimGridAxis = enum.auto() - ArraySimGridAxis = enum.auto() - - ## Sim / - FDTDSim = enum.auto() + #AutomaticSimGridAxis = enum.auto() + #ManualSimGridAxis = enum.auto() + #UniformSimGridAxis = enum.auto() + #ArraySimGridAxis = enum.auto() # Utilities Combine = enum.auto() Separate = enum.auto() - Math = enum.auto() - - ## Utilities / Converters - WaveConverter = enum.auto() - - ## Utilities / Operations - ArrayOperation = enum.auto() - - # Viz - FDTDSimDataViz = enum.auto() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py index d2a085b..ad99c0d 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py @@ -1,7 +1,11 @@ import io +import time import typing as typ import bpy +import jax +import jax.numpy as jnp +import matplotlib import matplotlib.axis as mpl_ax import numpy as np import typing_extensions as typx @@ -14,6 +18,63 @@ log = logger.get(__name__) AREA_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): managed_obj_type = ct.ManagedObjType.ManagedBLImage @@ -81,6 +142,7 @@ class ManagedBLImage(ct.schemas.ManagedObj): self.name, width=width_px, height=height_px, + float_buffer=dtype == 'float32', ) return bl_image @@ -120,18 +182,14 @@ class ManagedBLImage(ct.schemas.ManagedObj): self.preview_space.image = bl_image #################### - # - Special Methods + # - Image Geometry #################### - def mpl_plot_to_image( + def gen_image_geometry( 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, ): - import matplotlib.pyplot as plt - # Compute Image Geometry if preview_area := self.preview_area: # 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`' raise ValueError(msg) - # Compute Plot Dimensions aspect_ratio = _width_inches / _height_inches - log.debug( - 'Create MPL Axes (aspect=%d, width=%d, height=%d)', - aspect_ratio, - _width_inches, - _height_inches, + return aspect_ratio, _dpi, _width_inches, _height_inches, width_px, height_px + + #################### + # - Special Methods + #################### + 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 fig, ax = plt.subplots( figsize=[_width_inches, _height_inches], dpi=_dpi, ) + # log.debug('Created MPL Axes (%f)', time.perf_counter() - time_start) ax.set_aspect(aspect_ratio) cmp_width_px, cmp_height_px = fig.canvas.get_width_height() ## Use computed pixel w/h to preempt off-by-one size errors. ax.set_aspect('auto') ## Workaround aspect-ratio bugs + # log.debug('Set MPL Aspect (%f)', time.perf_counter() - time_start) # Plot w/User Parameter func_plotter(ax) + # log.debug('User Plot Function (%f)', time.perf_counter() - time_start) # Save Figure to BytesIO with io.BytesIO() as buff: + # log.debug('Made BytesIO (%f)', time.perf_counter() - time_start) fig.savefig(buff, format='raw', dpi=dpi) + # log.debug('Saved Figure to BytesIO (%f)', time.perf_counter() - time_start) buff.seek(0) image_data = np.frombuffer( buff.getvalue(), dtype=np.uint8, ).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 + # log.debug('Flipped Image Data (%f)', time.perf_counter() - time_start) plt.close(fig) # Optimized Write to Blender Image 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()) + # log.debug('Set BL Image Pixels (%f)', time.perf_counter() - time_start) bl_image.update() + # log.debug('Updated BL Image (%f)', time.perf_counter() - time_start) if bl_select: self.bl_select() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py index 866a976..169ed19 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py @@ -1,7 +1,7 @@ # from . import kitchen_sink - # from . import bounds from . import ( + analysis, inputs, mediums, monitors, @@ -10,11 +10,11 @@ from . import ( sources, structures, utilities, - viz, ) BL_REGISTER = [ # *kitchen_sink.BL_REGISTER, + *analysis.BL_REGISTER, *inputs.BL_REGISTER, *outputs.BL_REGISTER, *sources.BL_REGISTER, @@ -24,10 +24,10 @@ BL_REGISTER = [ *monitors.BL_REGISTER, *simulations.BL_REGISTER, *utilities.BL_REGISTER, - *viz.BL_REGISTER, ] BL_NODES = { # **kitchen_sink.BL_NODES, + **analysis.BL_NODES, **inputs.BL_NODES, **outputs.BL_NODES, **sources.BL_NODES, @@ -37,5 +37,4 @@ BL_NODES = { **monitors.BL_NODES, **simulations.BL_NODES, **utilities.BL_NODES, - **viz.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/__init__.py new file mode 100644 index 0000000..0c6cd6e --- /dev/null +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/__init__.py @@ -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, +} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/extract_data.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/extract_data.py new file mode 100644 index 0000000..0257712 --- /dev/null +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/extract_data.py @@ -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)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/viz.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/viz.py new file mode 100644 index 0000000..21d8990 --- /dev/null +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/analysis/viz.py @@ -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)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py index 4a6f81c..33c88b8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py @@ -105,12 +105,10 @@ class MaxwellSimNode(bpy.types.Node): # Setup Callback Methods cls._output_socket_methods = { - (method.extra_data['output_socket_name'], method.extra_data['kind']): method + method for attr_name in dir(cls) if hasattr(method := getattr(cls, attr_name), 'action_type') and method.action_type == 'computes_output_socket' - and hasattr(method, 'extra_data') - and method.extra_data } cls._on_value_changed_methods = { method @@ -553,10 +551,21 @@ class MaxwellSimNode(bpy.types.Node): The value of the output socket, as computed by the dedicated method registered using the `@computes_output_socket` decorator. """ - if output_socket_method := self._output_socket_methods.get( - (output_socket_name, kind) - ): - return output_socket_method(self) + possible_output_socket_methods = [ + output_socket_method + for output_socket_method in self._output_socket_methods + 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}' raise ValueError(msg) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/events.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/events.py index 435b170..a7b26e9 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/events.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/events.py @@ -30,6 +30,7 @@ class EventCallbackData_ComputesOutputSocket(typ.TypedDict): # noqa: N801 """Extra data used to select a method to compute output sockets.""" output_socket_name: ct.SocketName + any_loose_output_socket: bool kind: ct.DataFlowKind @@ -194,8 +195,8 @@ def event_decorator( _output_sockets = { output_socket_name: node.compute_output( output_socket_name, - kind=input_socket_kinds.get( - input_socket_name, ct.DataFlowKind.Value + kind=output_socket_kinds.get( + output_socket_name, ct.DataFlowKind.Value ), ) for output_socket_name in output_sockets @@ -231,7 +232,10 @@ def event_decorator( ## Compute All Loose Input Sockets if all_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 } method_kw_args |= {'loose_input_sockets': _loose_input_sockets} @@ -239,7 +243,10 @@ def event_decorator( ## Compute All Loose Output Sockets if all_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 } method_kw_args |= {'loose_output_sockets': _loose_output_sockets} @@ -274,7 +281,8 @@ def event_decorator( # - Simplified Event Callbacks #################### 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, **kwargs, ): @@ -282,6 +290,7 @@ def computes_output_socket( action_type='computes_output_socket', extra_data={ 'output_socket_name': output_socket_name, + 'any_loose_output_socket': any_loose_output_socket, 'kind': kind, }, **kwargs, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/file_importers/tidy_3d_file_importer.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/file_importers/tidy_3d_file_importer.py index 4a9e6b7..60774d8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/file_importers/tidy_3d_file_importer.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/file_importers/tidy_3d_file_importer.py @@ -196,9 +196,9 @@ class Tidy3DFileImporterNode(base.MaxwellSimNode): else: self.loose_output_sockets = { 'SIMULATION_DATA': { - 'Sim Data': sockets.MaxwellFDTDSimSocketDef(), + 'Sim Data': sockets.MaxwellFDTDSimDataSocketDef(), }, - 'SIMULATION': {'Sim': sockets.MaxwellFDTDSimDataSocketDef()}, + 'SIMULATION': {'Sim': sockets.MaxwellFDTDSimSocketDef()}, 'MEDIUM': {'Medium': sockets.MaxwellMediumSocketDef()}, 'EXPERIM_DISP_MEDIUM': { 'Experim Disp Medium': sockets.MaxwellMediumSocketDef() @@ -245,5 +245,5 @@ BL_REGISTER = [ Tidy3DFileImporterNode, ] BL_NODES = { - ct.NodeType.Tidy3DFileImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_IMPORTERS) + ct.NodeType.Tidy3DFileImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_FILEIMPORTERS) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py index 68186f3..77e61ca 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py @@ -35,9 +35,34 @@ class WaveConstantNode(base.MaxwellSimNode): #################### @events.computes_output_socket( 'WL', + kind=ct.DataFlowKind.Value, 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: return wl @@ -52,9 +77,10 @@ class WaveConstantNode(base.MaxwellSimNode): @events.computes_output_socket( 'Freq', + kind=ct.DataFlowKind.LazyValueRange, 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: return freq diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py index 1ed3ee9..77146c1 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py @@ -102,5 +102,5 @@ BL_REGISTER = [ Tidy3DWebImporterNode, ] BL_NODES = { - ct.NodeType.Tidy3DWebImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_IMPORTERS) + ct.NodeType.Tidy3DWebImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_WEBIMPORTERS) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py index d517afc..bb61dfd 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py @@ -18,7 +18,7 @@ class EHFieldMonitorNode(base.MaxwellSimNode): """Node providing for the monitoring of electromagnetic fields within a given planar region or volume.""" node_type = ct.NodeType.EHFieldMonitor - bl_label = 'E/H Field Monitor' + bl_label = 'EH Field Monitor' use_sim_node_name = True #################### @@ -45,8 +45,9 @@ class EHFieldMonitorNode(base.MaxwellSimNode): ), }, } - output_sockets: typ.ClassVar = { - 'Monitor': sockets.MaxwellMonitorSocketDef(), + output_socket_sets: typ.ClassVar = { + 'Freq Domain': {'Freq Monitor': sockets.MaxwellMonitorSocketDef()}, + 'Time Domain': {'Time Monitor': sockets.MaxwellMonitorSocketDef()}, } managed_obj_defs: typ.ClassVar = { @@ -62,63 +63,45 @@ class EHFieldMonitorNode(base.MaxwellSimNode): # - Output Sockets #################### @events.computes_output_socket( - 'Monitor', - props={'active_socket_set', 'sim_node_name'}, + 'Freq Monitor', + props={'sim_node_name'}, input_sockets={ - 'Rec Start', - 'Rec Stop', 'Center', 'Size', 'Samples/Space', - 'Samples/Time', 'Freqs', }, + input_socket_kinds={ + 'Freqs': ct.DataFlowKind.LazyValueRange, + }, unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D}, scale_input_sockets={ 'Center': 'Tidy3DUnits', 'Size': 'Tidy3DUnits', - 'Samples/Space': 'Tidy3DUnits', - 'Rec Start': 'Tidy3DUnits', - 'Rec Stop': 'Tidy3DUnits', - 'Samples/Time': 'Tidy3DUnits', + 'Freqs': 'Tidy3DUnits', }, ) - def compute_monitor( - self, input_sockets: dict, props: dict, unit_systems: dict, - ) -> td.FieldMonitor | td.FieldTimeMonitor: - if props['active_socket_set'] == 'Freq Domain': - freqs = input_sockets['Freqs'] - - log.info( - 'Computing FieldMonitor (name="%s") with center="%s", size="%s"', - props['sim_node_name'], - input_sockets['Center'], - input_sockets['Size'], - ) - return td.FieldMonitor( - center=input_sockets['Center'], - size=input_sockets['Size'], - name=props['sim_node_name'], - interval_space=input_sockets['Samples/Space'], - freqs=[ - float(spu.convert_to(freq, spu.hertz) / spu.hertz) for freq in freqs - ], - ) - ## Time Domain + def compute_freq_monitor( + self, + input_sockets: dict, + props: dict, + unit_systems: dict, + ) -> td.FieldMonitor: log.info( - 'Computing FieldTimeMonitor (name=%s) with center=%s, size=%s', + 'Computing FieldMonitor (name="%s") with center="%s", size="%s"', props['sim_node_name'], input_sockets['Center'], input_sockets['Size'], ) - return td.FieldTimeMonitor( + return td.FieldMonitor( 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'], + interval_space=tuple(input_sockets['Samples/Space']), + freqs=input_sockets['Freqs'].realize().values, + #freqs=[ + # float(spu.convert_to(freq, spu.hertz) / spu.hertz) for freq in freqs + #], ) #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py index 2dafaf8..3452d8f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py @@ -15,9 +15,9 @@ from .. import base, events log = logger.get(__name__) -class FieldPowerFluxMonitorNode(base.MaxwellSimNode): - node_type = ct.NodeType.FieldPowerFluxMonitor - bl_label = 'Field Power Flux Monitor' +class PowerFluxMonitorNode(base.MaxwellSimNode): + node_type = ct.NodeType.PowerFluxMonitor + bl_label = 'Power Flux Monitor' use_sim_node_name = True #################### @@ -160,6 +160,6 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode): # - Blender Registration #################### BL_REGISTER = [ - FieldPowerFluxMonitorNode, + PowerFluxMonitorNode, ] -BL_NODES = {ct.NodeType.FieldPowerFluxMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)} +BL_NODES = {ct.NodeType.PowerFluxMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/__init__.py index 48c3c80..5cbc8f8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/__init__.py @@ -1,10 +1,12 @@ -from . import exporters, viewer +from . import file_exporters, viewer, web_exporters BL_REGISTER = [ *viewer.BL_REGISTER, - *exporters.BL_REGISTER, + *file_exporters.BL_REGISTER, + *web_exporters.BL_REGISTER, ] BL_NODES = { **viewer.BL_NODES, - **exporters.BL_NODES, + **file_exporters.BL_NODES, + **web_exporters.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/__init__.py deleted file mode 100644 index d07e2d9..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/__init__.py +++ /dev/null @@ -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, -} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/file_exporters/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/file_exporters/__init__.py new file mode 100644 index 0000000..7f2f7b3 --- /dev/null +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/file_exporters/__init__.py @@ -0,0 +1,8 @@ +from . import json_file_exporter + +BL_REGISTER = [ + *json_file_exporter.BL_REGISTER, +] +BL_NODES = { + **json_file_exporter.BL_NODES, +} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/file_exporters/json_file_exporter.py similarity index 99% rename from src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py rename to src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/file_exporters/json_file_exporter.py index bb81a4e..a955bbb 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/file_exporters/json_file_exporter.py @@ -98,5 +98,5 @@ BL_REGISTER = [ JSONFileExporterNode, ] BL_NODES = { - ct.NodeType.JSONFileExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_EXPORTERS) + ct.NodeType.JSONFileExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_FILEEXPORTERS) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py index af9bb11..04ec4af 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py @@ -130,7 +130,7 @@ class ViewerNode(base.MaxwellSimNode): ) def on_changed_plot_preview(self, props): 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) @events.on_value_changed( @@ -139,22 +139,26 @@ class ViewerNode(base.MaxwellSimNode): ) def on_changed_3d_preview(self, props): # Unpreview Everything - node_tree = self.id_data - node_tree.unpreview_all() + if props['auto_3d_preview']: + node_tree = self.id_data + node_tree.unpreview_all() # Trigger Preview Action 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) @events.on_value_changed( socket_name='Data', ) def on_changed_3d_data(self): - # Just Linked / Just Unlinked: Preview/Unpreview - if self.inputs['Data'].is_linked ^ self.cache__data_socket_linked: + # Is Linked: Re-Preview + if self.inputs['Data'].is_linked: self.on_changed_3d_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 diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/__init__.py new file mode 100644 index 0000000..1f026e9 --- /dev/null +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/__init__.py @@ -0,0 +1,8 @@ +from . import tidy3d_web_exporter + +BL_REGISTER = [ + *tidy3d_web_exporter.BL_REGISTER, +] +BL_NODES = { + **tidy3d_web_exporter.BL_NODES, +} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/tidy3d_web_exporter.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/tidy3d_web_exporter.py similarity index 99% rename from src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/tidy3d_web_exporter.py rename to src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/tidy3d_web_exporter.py index 7fa55b2..cd0b2cc 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/tidy3d_web_exporter.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/tidy3d_web_exporter.py @@ -243,7 +243,6 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode): task_name=new_task[0], cloud_folder=new_task[1], sim=sim, - upload_progress_cb=lambda uploaded_bytes: None, ## TODO: Use! verbose=True, ) @@ -428,5 +427,5 @@ BL_REGISTER = [ Tidy3DWebExporterNode, ] BL_NODES = { - ct.NodeType.Tidy3DWebExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_EXPORTERS) + ct.NodeType.Tidy3DWebExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_WEBEXPORTERS) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py index 813d924..6796a7d 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py @@ -51,7 +51,7 @@ class SimDomainNode(base.MaxwellSimNode): 'Size': 'Tidy3DUnits', }, ) - def compute_domain(self, input_sockets: dict) -> sp.Expr: + def compute_domain(self, input_sockets: dict, unit_systems) -> sp.Expr: return { 'run_time': input_sockets['Duration'], 'center': input_sockets['Center'], @@ -96,7 +96,7 @@ class SimDomainNode(base.MaxwellSimNode): @events.on_init() def on_init(self): - self.on_input_change() + self.on_input_changed() #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py index 13b92c4..c976e0f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py @@ -74,7 +74,10 @@ class PointDipoleSourceNode(base.MaxwellSimNode): }, ) 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: pol_axis = { 'EX': 'Ex', diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py index e12f36b..9724104 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py @@ -67,7 +67,7 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode): #################### # - UI #################### - def draw_props(self, context, layout): + def draw_props(self, _, layout): layout.label(text='Plot Settings') split = layout.split(factor=0.6) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py index 42a2d29..5555997 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py @@ -76,6 +76,9 @@ class GeoNodesStructureNode(base.MaxwellSimNode): input_sockets={'Center', 'GeoNodes'}, all_loose_input_sockets=True, unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, + scale_input_sockets={ + 'Center': 'BlenderUnits' + } ) def on_input_changed( self, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py index 92a8ca9..71d2768 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py @@ -90,27 +90,27 @@ class CombineNode(base.MaxwellSimNode): @events.computes_output_socket( 'Sources', - input_sockets={f'Source #{i}' for i in range(MAX_AMOUNT)}, + all_loose_input_sockets=True, props={'amount'}, ) - def compute_sources(self, input_sockets, props) -> sp.Expr: - return [input_sockets[f'Source #{i}'] for i in range(props['amount'])] + def compute_sources(self, loose_input_sockets, props) -> sp.Expr: + return [loose_input_sockets[f'Source #{i}'] for i in range(props['amount'])] @events.computes_output_socket( 'Structures', - input_sockets={f'Structure #{i}' for i in range(MAX_AMOUNT)}, + all_loose_input_sockets=True, props={'amount'}, ) - def compute_structures(self, input_sockets, props) -> sp.Expr: - return [input_sockets[f'Structure #{i}'] for i in range(props['amount'])] + def compute_structures(self, loose_input_sockets, props) -> sp.Expr: + return [loose_input_sockets[f'Structure #{i}'] for i in range(props['amount'])] @events.computes_output_socket( 'Monitors', - input_sockets={f'Monitor #{i}' for i in range(MAX_AMOUNT)}, + all_loose_input_sockets=True, props={'amount'}, ) - def compute_monitors(self, input_sockets, props) -> sp.Expr: - return [input_sockets[f'Monitor #{i}'] for i in range(props['amount'])] + def compute_monitors(self, loose_input_sockets, props) -> sp.Expr: + return [loose_input_sockets[f'Monitor #{i}'] for i in range(props['amount'])] #################### # - Input Socket Compilation diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/__init__.py deleted file mode 100644 index 8b12464..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from . import sim_data_viz - -BL_REGISTER = [ - *sim_data_viz.BL_REGISTER, -] -BL_NODES = { - **sim_data_viz.BL_NODES, -} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/flux_analysis.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/flux_analysis.py deleted file mode 100644 index 0f68933..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/flux_analysis.py +++ /dev/null @@ -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)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/sim_data_viz.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/sim_data_viz.py deleted file mode 100644 index 1b5a9b7..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/sim_data_viz.py +++ /dev/null @@ -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)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py index bc5c754..03924f1 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py @@ -176,11 +176,24 @@ class MaxwellSimSocket(bpy.types.NodeSocket): raise RuntimeError(msg) def sync_link_added(self, link) -> bool: - """Called when a link has been added to this (input) socket. - - Returns a bool, whether or not the socket consents to the link change. - """ + """Called when a link has been added to this (input) socket.""" 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 if self.is_output: msg = "Tried to sync 'link add' on output socket" diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/data.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/data.py new file mode 100644 index 0000000..e69de29 diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py index 7893494..408b2d7 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py @@ -19,7 +19,7 @@ class MaxwellMonitorSocketDef(pyd.BaseModel): def init(self, bl_socket: MaxwellMonitorBLSocket) -> None: if self.is_list: - bl_socket.active_kind = ct.DataValueArray + bl_socket.active_kind = ct.DataFlowKind.ValueArray #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py index b830762..7418d9a 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py @@ -19,7 +19,7 @@ class MaxwellSourceSocketDef(pyd.BaseModel): def init(self, bl_socket: MaxwellSourceBLSocket) -> None: if self.is_list: - bl_socket.active_kind = ct.DataValueArray + bl_socket.active_kind = ct.DataFlowKind.ValueArray #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py index 1fa707c..456c7ec 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py @@ -19,7 +19,7 @@ class MaxwellStructureSocketDef(pyd.BaseModel): def init(self, bl_socket: MaxwellStructureBLSocket) -> None: if self.is_list: - bl_socket.active_kind = ct.DataValueArray + bl_socket.active_kind = ct.DataFlowKind.ValueArray #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py index f1c410c..8297830 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py @@ -79,6 +79,7 @@ class PhysicalFreqBLSocket(base.MaxwellSimSocket): return ct.LazyDataValueRange( symbols=set(), has_unit=True, + unit=self.unit, start=sp.S(self.min_freq) * self.unit, stop=sp.S(self.max_freq) * self.unit, steps=self.steps, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py index 53fc832..fd23a2b 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py @@ -79,6 +79,7 @@ class PhysicalLengthBLSocket(base.MaxwellSimSocket): return ct.LazyDataValueRange( symbols=set(), has_unit=True, + unit=self.unit, start=sp.S(self.min_len) * self.unit, stop=sp.S(self.max_len) * self.unit, steps=self.steps, @@ -116,7 +117,6 @@ class PhysicalLengthSocketDef(pyd.BaseModel): bl_socket.lazy_value_range = (self.min_len, self.max_len, self.steps) - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/nodeps/operators/install_deps.py b/src/blender_maxwell/nodeps/operators/install_deps.py index 9c8538e..8426a11 100644 --- a/src/blender_maxwell/nodeps/operators/install_deps.py +++ b/src/blender_maxwell/nodeps/operators/install_deps.py @@ -66,15 +66,6 @@ class InstallPyDeps(bpy.types.Operator): 'Running pip w/cmdline: %s', ' '.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) except subprocess.CalledProcessError: log.exception('Failed to install PyDeps')