From c2db40ca6d788e9286ba6aba4b78c16a3cedb38c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofus=20Albert=20H=C3=B8gsbro=20Rose?= Date: Wed, 3 Apr 2024 10:13:10 +0200 Subject: [PATCH] fix: Broken GN unit evaluation --- .../assets/geonodes/primitives/box.blend | 4 +- .../assets/geonodes/primitives/box.blend11 | Bin 0 -> 851789 bytes src/blender_maxwell/assets/import_geonodes.py | 1 - .../maxwell_sim_nodes/bl_socket_map.py | 55 +++--- .../contracts/unit_systems.py | 5 +- .../managed_objs/__init__.py | 4 +- .../managed_objs/managed_bl_collection.py | 7 +- .../managed_objs/managed_bl_mesh.py | 36 ++-- .../managed_objs/managed_bl_modifier.py | 13 +- .../node_trees/maxwell_sim_nodes/node_tree.py | 13 ++ .../maxwell_sim_nodes/nodes/base.py | 35 +++- .../maxwell_sim_nodes/nodes/events.py | 22 +-- .../nodes/monitors/eh_field_monitor.py | 143 +++++++-------- .../monitors/field_power_flux_monitor.py | 163 ++++++++---------- .../maxwell_sim_nodes/nodes/outputs/viewer.py | 69 ++++---- .../nodes/simulations/sim_domain.py | 126 ++++++-------- .../nodes/sources/plane_wave_source.py | 113 ++++++------ .../nodes/sources/point_dipole_source.py | 81 +++++---- .../nodes/structures/geonodes_structure.py | 17 +- .../structures/primitives/box_structure.py | 13 +- .../structures/primitives/sphere_structure.py | 108 ++++++------ .../maxwell_sim_nodes/sockets/base.py | 11 +- .../sockets/physical/length.py | 10 +- .../utils/extra_sympy_units.py | 6 +- src/blender_maxwell/utils/pydantic_sympy.py | 28 ++- 25 files changed, 536 insertions(+), 547 deletions(-) create mode 100644 src/blender_maxwell/assets/geonodes/primitives/box.blend11 diff --git a/src/blender_maxwell/assets/geonodes/primitives/box.blend b/src/blender_maxwell/assets/geonodes/primitives/box.blend index f8d5249..755b978 100644 --- a/src/blender_maxwell/assets/geonodes/primitives/box.blend +++ b/src/blender_maxwell/assets/geonodes/primitives/box.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f66cf11120ea204a947a6edc0bf4bfd706668e64b9267fb598fc242b58ef4b6 -size 911867 +oid sha256:59d82a5231448784c9b1107fa439ff500e376b9e4ee906a95022476a8b7755d8 +size 852005 diff --git a/src/blender_maxwell/assets/geonodes/primitives/box.blend11 b/src/blender_maxwell/assets/geonodes/primitives/box.blend11 new file mode 100644 index 0000000000000000000000000000000000000000..c8b67af27c2fe97e82a4d6fe5a4d33dc7e70d183 GIT binary patch literal 851789 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%0DW&DLT*GxPYo(0=;V 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 w$xXK`4PRmvkr>9 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 ct.schemas.SocketDef: """Computes an appropriate (no-arg) SocketDef from the given `bl_interface_socket`, by parsing it.""" return _socket_def_from_bl_socket( - bl_interface_socket.description, bl_interface_socket.socket_type + bl_interface_socket.description, bl_interface_socket.bl_socket_idname ) #################### # - Extract Default Interface Socket Value #################### -@functools.lru_cache(maxsize=4096) def _read_bl_socket_default_value( description: str, bl_socket_type: BLSocketType, bl_socket_value: BLSocketValue, unit_system: dict | None = None, + allow_unit_not_in_unit_system: bool = False, ) -> typ.Any: # Parse the BL Socket Type and Value ## The 'lambda' delays construction until size is determined. @@ -157,21 +163,20 @@ def _read_bl_socket_default_value( ## Use the matching socket type to lookup the unit in the unit system. if unit_system is not None: if (unit := unit_system.get(socket_type)) is None: + if allow_unit_not_in_unit_system: + return parsed_socket_value + msg = f'Unit system does not provide a unit for {socket_type}' raise RuntimeError(msg) - if unit not in (valid_units := ct.SOCKET_UNITS[socket_type]['values'].values()): - msg = f'Unit system provided a unit "{unit}" that is invalid for socket type "{socket_type}" (valid units: {valid_units})' - raise RuntimeError(msg) - return parsed_socket_value * unit - return parsed_socket_value def read_bl_socket_default_value( bl_interface_socket: bpy.types.NodeTreeInterfaceSocket, unit_system: dict | None = None, + allow_unit_not_in_unit_system: bool = False, ) -> typ.Any: """Reads the `default_value` of a Blender socket, guaranteeing a well-formed value consistent with the passed unit system. @@ -185,33 +190,41 @@ def read_bl_socket_default_value( """ return _read_bl_socket_default_value( bl_interface_socket.description, - bl_interface_socket.socket_type, + bl_interface_socket.bl_socket_idname, bl_interface_socket.default_value, - unit_system, + unit_system=unit_system, + allow_unit_not_in_unit_system=allow_unit_not_in_unit_system, ) -@functools.lru_cache(maxsize=4096) def _writable_bl_socket_value( description: str, bl_socket_type: BLSocketType, value: typ.Any, unit_system: dict | None = None, + allow_unit_not_in_unit_system: bool = False, ) -> typ.Any: socket_type = _socket_type_from_bl_socket(description, bl_socket_type) # Retrieve Unit-System Unit if unit_system is not None: if (unit := unit_system.get(socket_type)) is None: - msg = f'Unit system does not provide a unit for {socket_type}' - raise RuntimeError(msg) - - _bl_socket_value = spux.scale_to_unit(value, unit) + if allow_unit_not_in_unit_system: + _bl_socket_value = value + else: + msg = f'Unit system does not provide a unit for {socket_type}' + raise RuntimeError(msg) + else: + _bl_socket_value = spux.scale_to_unit(value, unit) else: _bl_socket_value = value # Compute Blender Socket Value - bl_socket_value = spux.sympy_to_python(_bl_socket_value) + if isinstance(_bl_socket_value, sp.Basic): + bl_socket_value = spux.sympy_to_python(_bl_socket_value) + else: + bl_socket_value = _bl_socket_value + if _size_from_bl_socket(description, bl_socket_type) == 2: # noqa: PLR2004 bl_socket_value = bl_socket_value[:2] return bl_socket_value @@ -221,6 +234,7 @@ def writable_bl_socket_value( bl_interface_socket: bpy.types.NodeTreeInterfaceSocket, value: typ.Any, unit_system: dict | None = None, + allow_unit_not_in_unit_system: bool = False, ) -> typ.Any: """Processes a value to be ready-to-write to a Blender socket. @@ -234,7 +248,8 @@ def writable_bl_socket_value( """ return _writable_bl_socket_value( bl_interface_socket.description, - bl_interface_socket.bl_socket_type, + bl_interface_socket.bl_socket_idname, value, - unit_system, + unit_system=unit_system, + allow_unit_not_in_unit_system=allow_unit_not_in_unit_system, ) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/unit_systems.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/unit_systems.py index 05a49d8..6d98162 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/unit_systems.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/unit_systems.py @@ -37,7 +37,8 @@ UNITS_BLENDER: UnitSystem = { } ## TODO: Load (dynamically?) from addon preferences UNITS_TIDY3D: UnitSystem = { - ST.PhysicalTime: spu.picosecond, + ## https://docs.flexcompute.com/projects/tidy3d/en/latest/faq/docs/faq/What-are-the-units-used-in-the-simulation.html + ST.PhysicalTime: spu.second, ST.PhysicalAngle: spu.radian, ST.PhysicalLength: spu.micrometer, ST.PhysicalArea: spu.micrometer**2, @@ -52,6 +53,6 @@ UNITS_TIDY3D: UnitSystem = { ST.PhysicalForceScalar: spux.micronewton, ST.PhysicalAccel3D: spu.um / spu.second**2, ST.PhysicalForce3D: spux.micronewton, - ST.PhysicalFreq: spux.terahertz, + ST.PhysicalFreq: spu.hertz, ST.PhysicalPol: spu.radian, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/__init__.py index 57e1b5c..83285d1 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/__init__.py @@ -1,4 +1,4 @@ -from .managed_bl_empty import ManagedBLEmpty +#from .managed_bl_empty import ManagedBLEmpty from .managed_bl_image import ManagedBLImage # from .managed_bl_collection import ManagedBLCollection @@ -9,7 +9,7 @@ from .managed_bl_mesh import ManagedBLMesh from .managed_bl_modifier import ManagedBLModifier __all__ = [ - 'ManagedBLEmpty', + #'ManagedBLEmpty', 'ManagedBLImage', #'ManagedBLCollection', #'ManagedBLObject', diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_collection.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_collection.py index 85470f0..b139c34 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_collection.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_collection.py @@ -1,3 +1,5 @@ +import functools + import bpy from ....utils import logger @@ -11,6 +13,7 @@ PREVIEW_COLLECTION_NAME = 'BLMaxwell Visible' #################### # - Global Collection Handling #################### +@functools.cache def collection(collection_name: str, view_layer_exclude: bool) -> bpy.types.Collection: # Init the "Managed Collection" # Ensure Collection exists (and is in the Scene collection) @@ -32,8 +35,8 @@ def collection(collection_name: str, view_layer_exclude: bool) -> bpy.types.Coll def managed_collection() -> bpy.types.Collection: - return collection(MANAGED_COLLECTION_NAME, view_layer_exclude=False) + return collection(MANAGED_COLLECTION_NAME, view_layer_exclude=True) def preview_collection() -> bpy.types.Collection: - return collection(PREVIEW_COLLECTION_NAME, view_layer_exclude=True) + return collection(PREVIEW_COLLECTION_NAME, view_layer_exclude=False) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_mesh.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_mesh.py index 429e20c..df9c957 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_mesh.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_mesh.py @@ -31,7 +31,7 @@ class ManagedBLMesh(ct.schemas.ManagedObj): 'Changing BLMesh w/Name "%s" to Name "%s"', self._bl_object_name, value ) - if not bpy.data.objects.get(value): + if (bl_object := bpy.data.objects.get(value)) is None: log.info( 'Desired BLMesh Name "%s" Not Taken', value, @@ -42,7 +42,7 @@ class ManagedBLMesh(ct.schemas.ManagedObj): 'Set New BLMesh Name to "%s"', value, ) - elif bl_object := bpy.data.objects.get(self._bl_object_name): + elif (bl_object := bpy.data.objects.get(self._bl_object_name)) is not None: log.info( 'Changed BLMesh Name to "%s"', value, @@ -97,28 +97,26 @@ class ManagedBLMesh(ct.schemas.ManagedObj): If it's already included, do nothing. """ - if ( - bl_object := bpy.data.objects.get(self.name) - ) is not None and bl_object.name not in preview_collection().objects: - log.info('Moving "%s" to Preview Collection', bl_object.name) - preview_collection().objects.link(bl_object) - - msg = 'Managed BLMesh does not exist' - raise ValueError(msg) + if (bl_object := bpy.data.objects.get(self.name)) is not None: + if bl_object.name not in preview_collection().objects: + log.info('Moving "%s" to Preview Collection', bl_object.name) + preview_collection().objects.link(bl_object) + else: + msg = 'Managed BLMesh does not exist' + raise ValueError(msg) def hide_preview(self) -> None: """Removes the managed Blender object from the preview collection. If it's already removed, do nothing. """ - if ( - bl_object := bpy.data.objects.get(self.name) - ) is not None and bl_object.name in preview_collection().objects: - log.info('Removing "%s" from Preview Collection', bl_object.name) - preview_collection.objects.unlink(bl_object) - - msg = 'Managed BLMesh does not exist' - raise ValueError(msg) + if (bl_object := bpy.data.objects.get(self.name)) is not None: + if bl_object.name in preview_collection().objects: + log.info('Removing "%s" from Preview Collection', bl_object.name) + preview_collection().objects.unlink(bl_object) + else: + msg = 'Managed BLMesh does not exist' + raise ValueError(msg) def bl_select(self) -> None: """Selects the managed Blender object, causing it to be ex. outlined in the 3D viewport.""" @@ -138,7 +136,7 @@ class ManagedBLMesh(ct.schemas.ManagedObj): if not (bl_object := bpy.data.objects.get(self.name)): log.info( 'Creating BLMesh Object "%s"', - bl_object.name, + self.name, ) bl_data = bpy.data.meshes.new(self.name) bl_object = bpy.data.objects.new(self.name, bl_data) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_modifier.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_modifier.py index 616eacc..e3f83d4 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_modifier.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_modifier.py @@ -53,6 +53,7 @@ def write_modifier_geonodes( bl_modifier: bpy.types.Modifier, modifier_attrs: ModifierAttrsNODES, ) -> bool: + modifier_altered = False # Alter GeoNodes Group if bl_modifier.node_group != modifier_attrs['node_group']: log.info( @@ -66,7 +67,7 @@ def write_modifier_geonodes( # Alter GeoNodes Modifier Inputs ## First we retrieve the interface items by-Socket Name geonodes_interface = analyze_geonodes.interface( - bl_modifier.node_group, direct='INPUT' + bl_modifier.node_group, direc='INPUT' ) for ( socket_name, @@ -74,11 +75,12 @@ def write_modifier_geonodes( ) in modifier_attrs['inputs'].items(): # Compute Writable BL Socket Value ## Analyzes the socket and unitsys to prep a ready-to-write value. - ## Writte directly to the modifier dict. + ## Write directly to the modifier dict. bl_socket_value = bl_socket_map.writable_bl_socket_value( geonodes_interface[socket_name], value, - modifier_attrs['unit_system'], + unit_system=modifier_attrs['unit_system'], + allow_unit_not_in_unit_system=True, ) # Compute Interface ID from Socket Name @@ -91,6 +93,7 @@ def write_modifier_geonodes( for i, bl_socket_subvalue in enumerate(bl_socket_value): if bl_modifier[iface_id][i] != bl_socket_subvalue: bl_modifier[iface_id][i] = bl_socket_subvalue + modifier_altered = True # IF int/float Mismatch: Assign Float-Cast of Integer ## Blender is strict; only floats can set float vals. @@ -105,6 +108,8 @@ def write_modifier_geonodes( bl_modifier[iface_id] = bl_socket_value modifier_altered = True + return modifier_altered + def write_modifier( bl_modifier: bpy.types.Modifier, @@ -144,7 +149,7 @@ class ManagedBLModifier(ct.schemas.ManagedObj): def name(self, value: str) -> None: ## TODO: Handle name conflict within same BLObject log.info( - 'Changing BLModifier w/Name "%s" to Name "%s"', self._bl_object_name, value + 'Changing BLModifier w/Name "%s" to Name "%s"', self._modifier_name, value ) self._modifier_name = value diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py index d19d743..96c5c06 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py @@ -2,7 +2,11 @@ import typing as typ import bpy +from ...utils import logger from . import contracts as ct +from .managed_objs.managed_bl_collection import preview_collection + +log = logger.get(__name__) #################### # - Cache Management @@ -73,6 +77,15 @@ class MaxwellSimTree(bpy.types.NodeTree): for bl_socket in [*node.inputs, *node.outputs]: bl_socket.locked = False + def unpreview_all(self): + log.info('Disabling All 3D Previews') + for node in self.nodes: + if node.preview_active: + node.preview_active = False + + for bl_object in preview_collection().objects.values(): + preview_collection().objects.unlink(bl_object) + #################### # - Init Methods #################### 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 3a0acc8..04d6288 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 @@ -87,8 +87,8 @@ class MaxwellSimNode(bpy.types.Node): cls.__annotations__['preview_active'] = bpy.props.BoolProperty( name='Preview Active', description='Whether the preview (if any) is currently active', - default='', - update=lambda self, context: self.sync_prop('preview_active', context), + default=False, + update=lambda self, context: self.sync_preview_active(context), ) # Setup Locked Property for Node @@ -211,6 +211,21 @@ class MaxwellSimNode(bpy.types.Node): ## - If altered, set the 'sim_node_name' to the altered name. ## - This will cause recursion, but only once. + def sync_preview_active(self, _: bpy.types.Context): + log.info( + 'Changed Preview Active in "%s" to "%s"', + self.name, + self.preview_active, + ) + for method in self._on_value_changed_methods: + if 'preview_active' in method.extra_data['changed_props']: + log.info( + 'Running Previewer Callback "%s" in "%s")', + method.__name__, + self.name, + ) + method(self) + #################### # - Managed Object Properties #################### @@ -571,6 +586,13 @@ class MaxwellSimNode(bpy.types.Node): Invalidates (recursively) the cache of any managed object or output socket method that implicitly depends on this input socket. """ + #log.debug( + # 'Action "%s" Triggered in "%s" (socket_name="%s", prop_name="%s")', + # action, + # self.name, + # socket_name, + # prop_name, + #) # Forwards Chains if action == 'value_changed': # Run User Callbacks @@ -589,6 +611,11 @@ class MaxwellSimNode(bpy.types.Node): and socket_name in self.loose_input_sockets ) ): + #log.debug( + # 'Running Value-Change Callback "%s" in "%s")', + # method.__name__, + # self.name, + #) method(self) # Propagate via Output Sockets @@ -616,6 +643,10 @@ class MaxwellSimNode(bpy.types.Node): ## ...which simply hook into the 'preview_active' property. ## By (maybe) altering 'preview_active', callbacks run as needed. if not self.preview_active: + log.info( + 'Activating Preview in "%s")', + self.name, + ) self.preview_active = True ## Propagate via Input Sockets 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 fd5baf3..b812312 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 @@ -3,10 +3,13 @@ import inspect import typing as typ from types import MappingProxyType -from ....utils import sympy_extra_units as spux +from ....utils import extra_sympy_units as spux +from ....utils import logger from .. import contracts as ct from .base import MaxwellSimNode +log = logger.get(__name__) + UnitSystemID = str UnitSystem = dict[ct.SocketType, typ.Any] @@ -206,6 +209,10 @@ def event_decorator( } method_kw_args |= {'loose_output_sockets': _loose_output_sockets} + # Unit Systems + if unit_systems: + method_kw_args |= {'unit_systems': unit_systems} + # Call Method return method( node, @@ -253,19 +260,6 @@ def on_value_changed( any_loose_input_socket: bool = False, **kwargs, ): - if ( - sum( - [ - int(socket_name is not None), - int(prop_name is not None), - int(any_loose_input_socket), - ] - ) - > 1 - ): - msg = 'Define only one of socket_name, prop_name or any_loose_input_socket' - raise ValueError(msg) - return event_decorator( action_type=EventCallbackType.on_value_changed, extra_data={ 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 2df1653..88e651f 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 @@ -1,18 +1,18 @@ -import bpy +import typing as typ + import sympy as sp import sympy.physics.units as spu import tidy3d as td -from .....utils import analyze_geonodes, logger +from .....assets.import_geonodes import GeoNodes, import_geonodes from .....utils import extra_sympy_units as spux +from .....utils import logger from ... import contracts as ct from ... import managed_objs, sockets from .. import base, events log = logger.get(__name__) -GEONODES_MONITOR_BOX = 'monitor_box' - class EHFieldMonitorNode(base.MaxwellSimNode): """Node providing for the monitoring of electromagnetic fields within a given planar region or volume.""" @@ -24,14 +24,14 @@ class EHFieldMonitorNode(base.MaxwellSimNode): #################### # - Sockets #################### - input_sockets = { + input_sockets: typ.ClassVar = { 'Center': sockets.PhysicalPoint3DSocketDef(), 'Size': sockets.PhysicalSize3DSocketDef(), 'Samples/Space': sockets.Integer3DVectorSocketDef( default_value=sp.Matrix([10, 10, 10]) ), } - input_socket_sets = { + input_socket_sets: typ.ClassVar = { 'Freq Domain': { 'Freqs': sockets.PhysicalFreqSocketDef( is_list=True, @@ -45,15 +45,17 @@ class EHFieldMonitorNode(base.MaxwellSimNode): ), }, } - output_sockets = { + output_sockets: typ.ClassVar = { 'Monitor': sockets.MaxwellMonitorSocketDef(), } - managed_obj_defs = { - 'monitor_box': ct.schemas.ManagedObjDef( - mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix='', - ) + managed_obj_defs: typ.ClassVar = { + 'mesh': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLMesh(name), + ), + 'modifier': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLModifier(name), + ), } #################### @@ -61,6 +63,7 @@ class EHFieldMonitorNode(base.MaxwellSimNode): #################### @events.computes_output_socket( 'Monitor', + props={'active_socket_set', 'sim_node_name'}, input_sockets={ 'Rec Start', 'Rec Stop', @@ -70,60 +73,52 @@ class EHFieldMonitorNode(base.MaxwellSimNode): 'Samples/Time', 'Freqs', }, - props={'active_socket_set', 'sim_node_name'}, + 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', + }, ) def compute_monitor( - self, input_sockets: dict, props: dict + self, input_sockets: dict, props: dict, unit_systems: dict, ) -> td.FieldMonitor | td.FieldTimeMonitor: - """Computes the value of the 'Monitor' output socket, which the user can select as being either a `td.FieldMonitor` or `td.FieldTimeMonitor`.""" - _center = input_sockets['Center'] - _size = input_sockets['Size'] - _samples_space = input_sockets['Samples/Space'] - - center = tuple(spu.convert_to(_center, spu.um) / spu.um) - size = tuple(spu.convert_to(_size, spu.um) / spu.um) - samples_space = tuple(_samples_space) - if props['active_socket_set'] == 'Freq Domain': freqs = input_sockets['Freqs'] log.info( - 'Computing FieldMonitor (name=%s) with center=%s, size=%s', + 'Computing FieldMonitor (name="%s") with center="%s", size="%s"', props['sim_node_name'], - center, - size, + input_sockets['Center'], + input_sockets['Size'], ) return td.FieldMonitor( - center=center, - size=size, + center=input_sockets['Center'], + size=input_sockets['Size'], name=props['sim_node_name'], - interval_space=samples_space, + interval_space=input_sockets['Samples/Space'], freqs=[ float(spu.convert_to(freq, spu.hertz) / spu.hertz) for freq in freqs ], ) ## Time Domain - _rec_start = input_sockets['Rec Start'] - _rec_stop = input_sockets['Rec Stop'] - samples_time = input_sockets['Samples/Time'] - - rec_start = spu.convert_to(_rec_start, spu.second) / spu.second - rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second - log.info( 'Computing FieldTimeMonitor (name=%s) with center=%s, size=%s', props['sim_node_name'], - center, - size, + input_sockets['Center'], + input_sockets['Size'], ) return td.FieldTimeMonitor( - center=center, - size=size, + center=input_sockets['Center'], + size=input_sockets['Size'], name=props['sim_node_name'], - start=rec_start, - stop=rec_stop, - interval=samples_time, - interval_space=samples_space, + start=input_sockets['Rec Start'], + stop=input_sockets['Rec Stop'], + interval=input_sockets['Samples/Time'], + interval_space=input_sockets['Samples/Space'], ) #################### @@ -131,49 +126,37 @@ class EHFieldMonitorNode(base.MaxwellSimNode): #################### @events.on_value_changed( socket_name={'Center', 'Size'}, + prop_name='preview_active', + props={'preview_active'}, input_sockets={'Center', 'Size'}, - managed_objs={'monitor_box'}, + managed_objs={'mesh', 'modifier'}, + unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, + scale_input_sockets={ + 'Center': 'BlenderUnits', + }, ) - def on_value_changed__center_size( + def on_inputs_changed( self, - input_sockets: dict, + props: dict, managed_objs: dict[str, ct.schemas.ManagedObj], + input_sockets: dict, + unit_systems: dict, ): - """Alters the managed 3D preview objects whenever the center or size input sockets are changed.""" - _center = input_sockets['Center'] - center = tuple([float(el) for el in spu.convert_to(_center, spu.um) / spu.um]) - - _size = input_sockets['Size'] - size = tuple([float(el) for el in spu.convert_to(_size, spu.um) / spu.um]) - - # Retrieve Hard-Coded GeoNodes and Analyze Input - geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX] - geonodes_interface = analyze_geonodes.interface(geo_nodes, direc='INPUT') - - # Sync Modifier Inputs - managed_objs['monitor_box'].sync_geonodes_modifier( - geonodes_node_group=geo_nodes, - geonodes_identifier_to_value={ - geonodes_interface['Size'].identifier: size, + # Push Input Values to GeoNodes Modifier + managed_objs['modifier'].bl_modifier( + managed_objs['mesh'].bl_object(location=input_sockets['Center']), + 'NODES', + { + 'node_group': import_geonodes(GeoNodes.PrimitiveBox, 'link'), + 'unit_system': unit_systems['BlenderUnits'], + 'inputs': { + 'Size': input_sockets['Size'], + }, }, ) - - # Sync Object Position - managed_objs['monitor_box'].bl_object('MESH').location = center - - #################### - # - Preview - Show Preview - #################### - @events.on_show_preview( - managed_objs={'monitor_box'}, - ) - def on_show_preview( - self, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - """Requests that the managed object be previewed in response to a user request to show the preview.""" - managed_objs['monitor_box'].show_preview('MESH') - self.on_value_changed__center_size() + # Push Preview State + if props['preview_active']: + managed_objs['mesh'].show_preview() #################### 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 3c17939..ffdb6fb 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 @@ -1,15 +1,18 @@ +import typing as typ + import bpy import sympy as sp import sympy.physics.units as spu import tidy3d as td -from .....utils import analyze_geonodes +from .....assets.import_geonodes import GeoNodes, import_geonodes from .....utils import extra_sympy_units as spux +from .....utils import logger from ... import contracts as ct from ... import managed_objs, sockets from .. import base, events -GEONODES_MONITOR_BOX = 'monitor_flux_box' +log = logger.get(__name__) class FieldPowerFluxMonitorNode(base.MaxwellSimNode): @@ -20,7 +23,7 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode): #################### # - Sockets #################### - input_sockets = { + input_sockets: typ.ClassVar = { 'Center': sockets.PhysicalPoint3DSocketDef(), 'Size': sockets.PhysicalSize3DSocketDef(), 'Samples/Space': sockets.Integer3DVectorSocketDef( @@ -28,7 +31,7 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode): ), 'Direction': sockets.BoolSocketDef(), } - input_socket_sets = { + input_socket_sets: typ.ClassVar = { 'Freq Domain': { 'Freqs': sockets.PhysicalFreqSocketDef( is_list=True, @@ -42,35 +45,25 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode): ), }, } - output_sockets = { + output_sockets: typ.ClassVar = { 'Monitor': sockets.MaxwellMonitorSocketDef(), } - managed_obj_defs = { - 'monitor_box': ct.schemas.ManagedObjDef( - mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix='', - ) + managed_obj_defs: typ.ClassVar = { + 'mesh': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLMesh(name), + ), + 'modifier': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLModifier(name), + ), } #################### - # - Properties - #################### - - #################### - # - UI - #################### - def draw_props(self, context, layout): - pass - - def draw_info(self, context, col): - pass - - #################### - # - Output Sockets + # - Event Methods: Computation #################### @events.computes_output_socket( 'Monitor', + props={'active_socket_set', 'sim_node_name'}, input_sockets={ 'Rec Start', 'Rec Stop', @@ -81,102 +74,86 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode): 'Freqs', 'Direction', }, - props={'active_socket_set', 'sim_node_name'}, + 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', + }, ) def compute_monitor(self, input_sockets: dict, props: dict) -> td.FieldTimeMonitor: - _center = input_sockets['Center'] - _size = input_sockets['Size'] - _samples_space = input_sockets['Samples/Space'] - - center = tuple(spu.convert_to(_center, spu.um) / spu.um) - size = tuple(spu.convert_to(_size, spu.um) / spu.um) - samples_space = tuple(_samples_space) - direction = '+' if input_sockets['Direction'] else '-' if props['active_socket_set'] == 'Freq Domain': freqs = input_sockets['Freqs'] + log.info( + 'Computing FluxMonitor (name="%s") with center="%s", size="%s"', + props['sim_node_name'], + input_sockets['Center'], + input_sockets['Size'], + ) return td.FluxMonitor( - center=center, - size=size, + center=input_sockets['Center'], + size=input_sockets['Size'], name=props['sim_node_name'], - interval_space=samples_space, + interval_space=input_sockets['Samples/Space'], freqs=[ float(spu.convert_to(freq, spu.hertz) / spu.hertz) for freq in freqs ], normal_dir=direction, ) - else: ## Time Domain - _rec_start = input_sockets['Rec Start'] - _rec_stop = input_sockets['Rec Stop'] - samples_time = input_sockets['Samples/Time'] - rec_start = spu.convert_to(_rec_start, spu.second) / spu.second - rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second - - return td.FieldTimeMonitor( - center=center, - size=size, - name=props['sim_node_name'], - start=rec_start, - stop=rec_stop, - interval=samples_time, - interval_space=samples_space, - ) + return td.FluxTimeMonitor( + 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'], + normal_dir=direction, + ) #################### # - Preview - Changes to Input Sockets #################### @events.on_value_changed( socket_name={'Center', 'Size'}, - input_sockets={'Center', 'Size', 'Direction'}, - managed_objs={'monitor_box'}, + prop_name='preview_active', + props={'preview_active'}, + input_sockets={'Center', 'Size'}, + managed_objs={'mesh', 'modifier'}, + unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, + scale_input_sockets={ + 'Center': 'BlenderUnits', + }, ) - def on_value_changed__center_size( + def on_inputs_changed( self, - input_sockets: dict, + props: dict, managed_objs: dict[str, ct.schemas.ManagedObj], + input_sockets: dict, + unit_systems: dict, ): - _center = input_sockets['Center'] - center = tuple([float(el) for el in spu.convert_to(_center, spu.um) / spu.um]) - - _size = input_sockets['Size'] - size = tuple([float(el) for el in spu.convert_to(_size, spu.um) / spu.um]) - ## TODO: Preview unit system?? Presume um for now - - # Retrieve Hard-Coded GeoNodes and Analyze Input - geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX] - geonodes_interface = analyze_geonodes.interface(geo_nodes, direc='INPUT') - - # Sync Modifier Inputs - managed_objs['monitor_box'].sync_geonodes_modifier( - geonodes_node_group=geo_nodes, - geonodes_identifier_to_value={ - geonodes_interface['Size'].identifier: size, - geonodes_interface['Direction'].identifier: input_sockets['Direction'], - ## TODO: Use 'bl_socket_map.value_to_bl`! - ## - This accounts for auto-conversion, unit systems, etc. . - ## - We could keep it in the node base class... - ## - ...But it needs aligning with Blender, too. Hmm. + # Push Input Values to GeoNodes Modifier + managed_objs['modifier'].bl_modifier( + managed_objs['mesh'].bl_object(location=input_sockets['Center']), + 'NODES', + { + 'node_group': import_geonodes(GeoNodes.PrimitiveBox, 'link'), + 'unit_system': unit_systems['BlenderUnits'], + 'inputs': { + 'Size': input_sockets['Size'], + }, }, ) - - # Sync Object Position - managed_objs['monitor_box'].bl_object('MESH').location = center - - #################### - # - Preview - Show Preview - #################### - @events.on_show_preview( - managed_objs={'monitor_box'}, - ) - def on_show_preview( - self, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - managed_objs['monitor_box'].show_preview('MESH') - self.on_value_changed__center_size() + # Push Preview State + if props['preview_active']: + managed_objs['mesh'].show_preview() #################### 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 013006f..53077d4 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 @@ -1,10 +1,12 @@ +import typing as typ + import bpy import sympy as sp from .....utils import logger from ... import contracts as ct from ... import sockets -from ...managed_objs import managed_bl_object +from ...managed_objs.managed_bl_collection import preview_collection from .. import base, events log = logger.get(__name__) @@ -46,7 +48,7 @@ class ViewerNode(base.MaxwellSimNode): node_type = ct.NodeType.Viewer bl_label = 'Viewer' - input_sockets = { + input_sockets: typ.ClassVar = { 'Data': sockets.AnySocketDef(), } @@ -67,6 +69,12 @@ class ViewerNode(base.MaxwellSimNode): update=lambda self, context: self.sync_prop('auto_3d_preview', context), ) + cache__data_was_unlinked: bpy.props.BoolProperty( + name='Data Was Unlinked', + description="Whether the Data input was unlinked last time it was checked.", + default=True, + ) + #################### # - UI #################### @@ -111,46 +119,39 @@ class ViewerNode(base.MaxwellSimNode): console.print(data) #################### - # - Updates + # - Event Methods #################### + @events.on_value_changed( + socket_name='Data', + props={'auto_plot'}, + ) + def on_changed_2d_data(self, props): + # Show Plot + ## Don't have to un-show other plots. + if self.inputs['Data'].is_linked and props['auto_plot']: + self.trigger_action('show_plot') + @events.on_value_changed( socket_name='Data', props={'auto_3d_preview'}, ) - def on_value_changed__data(self, props): - # Show Plot - ## Don't have to un-show other plots. - if self.auto_plot: - self.trigger_action('show_plot') + def on_changed_3d_data(self, props): + # Data Not Attached + if not self.inputs['Data'].is_linked: + self.cache__data_was_unlinked = True - # Remove Anything Previewed - preview_collection = managed_bl_object.bl_collection( - managed_bl_object.PREVIEW_COLLECTION_NAME, - view_layer_exclude=False, - ) - for bl_object in preview_collection.objects.values(): - preview_collection.objects.unlink(bl_object) + # Data Just Attached + elif self.cache__data_was_unlinked: + node_tree = self.id_data - # Preview Anything that Should be Previewed (maybe) - if props['auto_3d_preview']: - self.trigger_action('show_preview') + # Unpreview Everything + node_tree.unpreview_all() - @events.on_value_changed( - prop_name='auto_3d_preview', - props={'auto_3d_preview'}, - ) - def on_value_changed__auto_3d_preview(self, props): - # Remove Anything Previewed - preview_collection = managed_bl_object.bl_collection( - managed_bl_object.PREVIEW_COLLECTION_NAME, - view_layer_exclude=False, - ) - for bl_object in preview_collection.objects.values(): - preview_collection.objects.unlink(bl_object) - - # Preview Anything that Should be Previewed (maybe) - if props['auto_3d_preview']: - self.trigger_action('show_preview') + # Enable Previews in Tree + if props['auto_3d_preview']: + log.info('Enabling 3D Previews from "%s"', self.name) + self.trigger_action('show_preview') + self.cache__data_was_unlinked = False #################### 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 0348a2e..49917ff 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 @@ -1,116 +1,102 @@ -import bpy +import typing as typ + import sympy as sp import sympy.physics.units as spu -from .....utils import analyze_geonodes +from .....assets.import_geonodes import GeoNodes, import_geonodes from ... import contracts as ct from ... import managed_objs, sockets from .. import base, events -GEONODES_DOMAIN_BOX = 'simdomain_box' - class SimDomainNode(base.MaxwellSimNode): node_type = ct.NodeType.SimDomain bl_label = 'Sim Domain' + use_sim_node_name = True - input_sockets = { + input_sockets: typ.ClassVar = { 'Duration': sockets.PhysicalTimeSocketDef( default_value=5 * spu.ps, default_unit=spu.ps, ), - 'Center': sockets.PhysicalSize3DSocketDef(), + 'Center': sockets.PhysicalPoint3DSocketDef(), 'Size': sockets.PhysicalSize3DSocketDef(), 'Grid': sockets.MaxwellSimGridSocketDef(), 'Ambient Medium': sockets.MaxwellMediumSocketDef(), } - output_sockets = { + output_sockets: typ.ClassVar = { 'Domain': sockets.MaxwellSimDomainSocketDef(), } - managed_obj_defs = { - 'domain_box': ct.schemas.ManagedObjDef( - mk=lambda name: managed_objs.ManagedBLObject(name), + managed_obj_defs: typ.ClassVar = { + 'mesh': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLMesh(name), name_prefix='', - ) + ), + 'modifier': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLModifier(name), + ), } #################### - # - Callbacks + # - Event Methods #################### @events.computes_output_socket( 'Domain', input_sockets={'Duration', 'Center', 'Size', 'Grid', 'Ambient Medium'}, + unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D}, + scale_input_sockets={ + 'Duration': 'Tidy3DUnits', + 'Center': 'Tidy3DUnits', + 'Size': 'Tidy3DUnits', + }, ) - def compute_sim_domain(self, input_sockets: dict) -> sp.Expr: - if all( - [ - (_duration := input_sockets['Duration']), - (_center := input_sockets['Center']), - (_size := input_sockets['Size']), - (grid := input_sockets['Grid']), - (medium := input_sockets['Ambient Medium']), - ] - ): - duration = spu.convert_to(_duration, spu.second) / spu.second - center = tuple(spu.convert_to(_center, spu.um) / spu.um) - size = tuple(spu.convert_to(_size, spu.um) / spu.um) - return dict( - run_time=duration, - center=center, - size=size, - grid_spec=grid, - medium=medium, - ) + def compute_output(self, input_sockets: dict) -> sp.Expr: + return { + 'run_time': input_sockets['Duration'], + 'center': input_sockets['Center'], + 'size': input_sockets['Size'], + 'grid_spec': input_sockets['Grid'], + 'medium': input_sockets['Ambient Medium'], + } - #################### - # - Preview - #################### @events.on_value_changed( socket_name={'Center', 'Size'}, + prop_name='preview_active', + props={'preview_active'}, input_sockets={'Center', 'Size'}, - managed_objs={'domain_box'}, + managed_objs={'mesh', 'modifier'}, + unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, + scale_input_sockets={ + 'Center': 'BlenderUnits', + }, ) - def on_value_changed__center_size( + def on_input_changed( self, - input_sockets: dict, + props: dict, managed_objs: dict[str, ct.schemas.ManagedObj], + input_sockets: dict, + unit_systems: dict, ): - _center = input_sockets['Center'] - center = tuple([float(el) for el in spu.convert_to(_center, spu.um) / spu.um]) - - _size = input_sockets['Size'] - size = tuple([float(el) for el in spu.convert_to(_size, spu.um) / spu.um]) - ## TODO: Preview unit system?? Presume um for now - - # Retrieve Hard-Coded GeoNodes and Analyze Input - geo_nodes = bpy.data.node_groups[GEONODES_DOMAIN_BOX] - geonodes_interface = analyze_geonodes.interface(geo_nodes, direc='INPUT') - - # Sync Modifier Inputs - managed_objs['domain_box'].sync_geonodes_modifier( - geonodes_node_group=geo_nodes, - geonodes_identifier_to_value={ - geonodes_interface['Size'].identifier: size, - ## TODO: Use 'bl_socket_map.value_to_bl`! - ## - This accounts for auto-conversion, unit systems, etc. . - ## - We could keep it in the node base class... - ## - ...But it needs aligning with Blender, too. Hmm. + # Push Input Values to GeoNodes Modifier + managed_objs['modifier'].bl_modifier( + managed_objs['mesh'].bl_object(location=input_sockets['Center']), + 'NODES', + { + 'node_group': import_geonodes(GeoNodes.PrimitiveBox, 'link'), + 'unit_system': unit_systems['BlenderUnits'], + 'inputs': { + 'Size': input_sockets['Size'], + }, }, ) + # Push Preview State + if props['preview_active']: + managed_objs['mesh'].show_preview() - # Sync Object Position - managed_objs['domain_box'].bl_object('MESH').location = center - - @events.on_show_preview( - managed_objs={'domain_box'}, - ) - def on_show_preview( - self, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - managed_objs['domain_box'].show_preview('MESH') - self.on_value_changed__center_size() + @events.on_init() + def on_init(self): + self.on_input_change() #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py index e23e46f..7629831 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py @@ -1,17 +1,13 @@ import math +import typing as typ -import bpy import sympy as sp -import sympy.physics.units as spu import tidy3d as td -from .....utils import analyze_geonodes from ... import contracts as ct from ... import managed_objs, sockets from .. import base, events -GEONODES_PLANE_WAVE = 'source_plane_wave' - def convert_vector_to_spherical( v: sp.MatrixBase, @@ -50,17 +46,17 @@ class PlaneWaveSourceNode(base.MaxwellSimNode): #################### # - Sockets #################### - input_sockets = { + input_sockets: typ.ClassVar = { 'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(), 'Center': sockets.PhysicalPoint3DSocketDef(), 'Direction': sockets.Real3DVectorSocketDef(default_value=sp.Matrix([0, 0, -1])), 'Pol Angle': sockets.PhysicalAngleSocketDef(), } - output_sockets = { + output_sockets: typ.ClassVar = { 'Source': sockets.MaxwellSourceSocketDef(), } - managed_obj_defs = { + managed_obj_defs: typ.ClassVar = { 'plane_wave_source': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), name_prefix='', @@ -73,26 +69,29 @@ class PlaneWaveSourceNode(base.MaxwellSimNode): @events.computes_output_socket( 'Source', input_sockets={'Temporal Shape', 'Center', 'Direction', 'Pol Angle'}, + unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D}, + scale_input_sockets={ + 'Center': 'Tidy3DUnits', + }, ) def compute_source(self, input_sockets: dict): - temporal_shape = input_sockets['Temporal Shape'] - _center = input_sockets['Center'] direction = input_sockets['Direction'] pol_angle = input_sockets['Pol Angle'] - injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical(direction) + injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical( + input_sockets['Direction'] + ) size = { 'x': (0, math.inf, math.inf), 'y': (math.inf, 0, math.inf), 'z': (math.inf, math.inf, 0), }[injection_axis] - center = tuple(spu.convert_to(_center, spu.um) / spu.um) # Display the results return td.PlaneWave( - center=center, - source_time=temporal_shape, + center=input_sockets['Center'], + source_time=input_sockets['Temporal Shape'], size=size, direction=dir_sgn, angle_theta=theta, @@ -100,54 +99,54 @@ class PlaneWaveSourceNode(base.MaxwellSimNode): pol_angle=pol_angle, ) - #################### - # - Preview - #################### - @events.on_value_changed( - socket_name={'Center', 'Direction'}, - input_sockets={'Center', 'Direction'}, - managed_objs={'plane_wave_source'}, - ) - def on_value_changed__center_direction( - self, - input_sockets: dict, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - _center = input_sockets['Center'] - center = tuple([float(el) for el in spu.convert_to(_center, spu.um) / spu.um]) + ##################### + ## - Preview + ##################### + # @events.on_value_changed( + # socket_name={'Center', 'Direction'}, + # input_sockets={'Center', 'Direction'}, + # managed_objs={'plane_wave_source'}, + # ) + # def on_value_changed__center_direction( + # self, + # input_sockets: dict, + # managed_objs: dict[str, ct.schemas.ManagedObj], + # ): + # _center = input_sockets['Center'] + # center = tuple([float(el) for el in spu.convert_to(_center, spu.um) / spu.um]) - _direction = input_sockets['Direction'] - direction = tuple([float(el) for el in _direction]) - ## TODO: Preview unit system?? Presume um for now + # _direction = input_sockets['Direction'] + # direction = tuple([float(el) for el in _direction]) + # ## TODO: Preview unit system?? Presume um for now - # Retrieve Hard-Coded GeoNodes and Analyze Input - geo_nodes = bpy.data.node_groups[GEONODES_PLANE_WAVE] - geonodes_interface = analyze_geonodes.interface(geo_nodes, direc='INPUT') + # # Retrieve Hard-Coded GeoNodes and Analyze Input + # geo_nodes = bpy.data.node_groups[GEONODES_PLANE_WAVE] + # geonodes_interface = analyze_geonodes.interface(geo_nodes, direc='INPUT') - # Sync Modifier Inputs - managed_objs['plane_wave_source'].sync_geonodes_modifier( - geonodes_node_group=geo_nodes, - geonodes_identifier_to_value={ - geonodes_interface['Direction'].identifier: direction, - ## TODO: Use 'bl_socket_map.value_to_bl`! - ## - This accounts for auto-conversion, unit systems, etc. . - ## - We could keep it in the node base class... - ## - ...But it needs aligning with Blender, too. Hmm. - }, - ) + # # Sync Modifier Inputs + # managed_objs['plane_wave_source'].sync_geonodes_modifier( + # geonodes_node_group=geo_nodes, + # geonodes_identifier_to_value={ + # geonodes_interface['Direction'].identifier: direction, + # ## TODO: Use 'bl_socket_map.value_to_bl`! + # ## - This accounts for auto-conversion, unit systems, etc. . + # ## - We could keep it in the node base class... + # ## - ...But it needs aligning with Blender, too. Hmm. + # }, + # ) - # Sync Object Position - managed_objs['plane_wave_source'].bl_object('MESH').location = center + # # Sync Object Position + # managed_objs['plane_wave_source'].bl_object('MESH').location = center - @events.on_show_preview( - managed_objs={'plane_wave_source'}, - ) - def on_show_preview( - self, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - managed_objs['plane_wave_source'].show_preview('MESH') - self.on_value_changed__center_direction() + # @events.on_show_preview( + # managed_objs={'plane_wave_source'}, + # ) + # def on_show_preview( + # self, + # managed_objs: dict[str, ct.schemas.ManagedObj], + # ): + # managed_objs['plane_wave_source'].show_preview('MESH') + # self.on_value_changed__center_direction() #################### 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 9514cef..13b92c4 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 @@ -68,6 +68,10 @@ class PointDipoleSourceNode(base.MaxwellSimNode): 'Source', input_sockets={'Temporal Shape', 'Center', 'Interpolate'}, props={'pol_axis'}, + unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D}, + scale_input_sockets={ + 'Center': 'Tidy3DUnits', + }, ) def compute_source( self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any] @@ -78,53 +82,46 @@ class PointDipoleSourceNode(base.MaxwellSimNode): 'EZ': 'Ez', }[props['pol_axis']] - temporal_shape = input_sockets['Temporal Shape'] - _center = input_sockets['Center'] - interpolate = input_sockets['Interpolate'] - - center = tuple(spu.convert_to(_center, spu.um) / spu.um) - - _res = td.PointDipole( - center=center, - source_time=temporal_shape, - interpolate=interpolate, + return td.PointDipole( + center=input_sockets['Center'], + source_time=input_sockets['Temporal Shape'], + interpolate=input_sockets['Interpolate'], polarization=pol_axis, ) - return _res - #################### - # - Preview - #################### - @events.on_value_changed( - socket_name='Center', - input_sockets={'Center'}, - managed_objs={'sphere_empty'}, - ) - def on_value_changed__center( - self, - input_sockets: dict, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - _center = input_sockets['Center'] - center = tuple(spu.convert_to(_center, spu.um) / spu.um) - ## TODO: Preview unit system?? Presume um for now + ##################### + ## - Preview + ##################### + # @events.on_value_changed( + # socket_name='Center', + # input_sockets={'Center'}, + # managed_objs={'sphere_empty'}, + # ) + # def on_value_changed__center( + # self, + # input_sockets: dict, + # managed_objs: dict[str, ct.schemas.ManagedObj], + # ): + # _center = input_sockets['Center'] + # center = tuple(spu.convert_to(_center, spu.um) / spu.um) + # ## TODO: Preview unit system?? Presume um for now - mobj = managed_objs['sphere_empty'] - bl_object = mobj.bl_object('EMPTY') - bl_object.location = center # tuple([float(el) for el in center]) + # mobj = managed_objs['sphere_empty'] + # bl_object = mobj.bl_object('EMPTY') + # bl_object.location = center # tuple([float(el) for el in center]) - @events.on_show_preview( - managed_objs={'sphere_empty'}, - ) - def on_show_preview( - self, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - managed_objs['sphere_empty'].show_preview( - 'EMPTY', - empty_display_type='SPHERE', - ) - managed_objs['sphere_empty'].bl_object('EMPTY').empty_display_size = 0.2 + # @events.on_show_preview( + # managed_objs={'sphere_empty'}, + # ) + # def on_show_preview( + # self, + # managed_objs: dict[str, ct.schemas.ManagedObj], + # ): + # managed_objs['sphere_empty'].show_preview( + # 'EMPTY', + # empty_display_type='SPHERE', + # ) + # managed_objs['sphere_empty'].bl_object('EMPTY').empty_display_size = 0.2 #################### 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 78cd167..aa89bd0 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 @@ -71,10 +71,10 @@ class GeoNodesStructureNode(base.MaxwellSimNode): socket_name='GeoNodes', prop_name='preview_active', any_loose_input_socket=True, - # Method Data + props={'preview_active'}, managed_objs={'mesh', 'modifier'}, - input_sockets={'GeoNodes'}, - # Unit System Scaling + input_sockets={'Center', 'GeoNodes'}, + all_loose_input_sockets=True, unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, ) def on_input_changed( @@ -127,15 +127,22 @@ class GeoNodesStructureNode(base.MaxwellSimNode): ## Changing socket.value invokes recursion of this function. ## The else: below ensures that only one push occurs. ## (well, one push per .value set, which simplifies to one push) - log.debug( + log.info( 'Setting Loose Input Sockets of "%s" to GeoNodes Defaults', self.bl_label, ) for socket_name in self.loose_input_sockets: socket = self.inputs[socket_name] socket.value = bl_socket_map.read_bl_socket_default_value( - geonodes_interface[socket_name] + geonodes_interface[socket_name], + unit_systems['BlenderUnits'], + allow_unit_not_in_unit_system=True, ) + log.info( + 'Set Loose Input Sockets of "%s" to: %s', + self.bl_label, + str(self.loose_input_sockets), + ) else: # Push Loose Input Values to GeoNodes Modifier managed_objs['modifier'].bl_modifier( diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py index ab856b9..9bfffd6 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py @@ -4,13 +4,11 @@ import sympy as sp import sympy.physics.units as spu import tidy3d as td -from .....assets.import_geonodes import import_geonodes +from ......assets.import_geonodes import GeoNodes, import_geonodes from .... import contracts as ct from .... import managed_objs, sockets from ... import base, events -GEONODES_BOX = 'box' - class BoxStructureNode(base.MaxwellSimNode): node_type = ct.NodeType.BoxStructure @@ -64,16 +62,15 @@ class BoxStructureNode(base.MaxwellSimNode): @events.on_value_changed( socket_name={'Center', 'Size'}, prop_name='preview_active', - # Method Data + props={'preview_active'}, input_sockets={'Center', 'Size'}, managed_objs={'mesh', 'modifier'}, - # Unit System Scaling unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, scale_input_sockets={ 'Center': 'BlenderUnits', }, ) - def on_input_changed( + def on_inputs_changed( self, props: dict, managed_objs: dict[str, ct.schemas.ManagedObj], @@ -85,7 +82,7 @@ class BoxStructureNode(base.MaxwellSimNode): managed_objs['mesh'].bl_object(location=input_sockets['Center']), 'NODES', { - 'node_group': import_geonodes(GEONODES_BOX, 'link'), + 'node_group': import_geonodes(GeoNodes.PrimitiveBox, 'link'), 'unit_system': unit_systems['BlenderUnits'], 'inputs': { 'Size': input_sockets['Size'], @@ -98,7 +95,7 @@ class BoxStructureNode(base.MaxwellSimNode): @events.on_init() def on_init(self): - self.on_input_change() + self.on_inputs_changed() #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py index 5b1ef66..88c2e68 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py @@ -1,38 +1,40 @@ -import bpy +import typing as typ + import sympy.physics.units as spu import tidy3d as td -from ......utils import analyze_geonodes +from ......assets.import_geonodes import GeoNodes, import_geonodes from .... import contracts as ct from .... import managed_objs, sockets from ... import base, events -GEONODES_STRUCTURE_SPHERE = 'structure_sphere' - class SphereStructureNode(base.MaxwellSimNode): node_type = ct.NodeType.SphereStructure bl_label = 'Sphere Structure' + use_sim_node_name = True #################### # - Sockets #################### - input_sockets = { + input_sockets: typ.ClassVar = { 'Center': sockets.PhysicalPoint3DSocketDef(), 'Radius': sockets.PhysicalLengthSocketDef( default_value=150 * spu.nm, ), 'Medium': sockets.MaxwellMediumSocketDef(), } - output_sockets = { + output_sockets: typ.ClassVar = { 'Structure': sockets.MaxwellStructureSocketDef(), } - managed_obj_defs = { - 'structure_sphere': ct.schemas.ManagedObjDef( - mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix='', - ) + managed_obj_defs: typ.ClassVar = { + 'mesh': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLMesh(name), + ), + 'modifier': ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLModifier(name), + ), } #################### @@ -41,21 +43,19 @@ class SphereStructureNode(base.MaxwellSimNode): @events.computes_output_socket( 'Structure', input_sockets={'Center', 'Radius', 'Medium'}, + unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D}, + scale_input_sockets={ + 'Center': 'Tidy3DUnits', + 'Radius': 'Tidy3DUnits', + }, ) def compute_structure(self, input_sockets: dict) -> td.Box: - medium = input_sockets['Medium'] - _center = input_sockets['Center'] - _radius = input_sockets['Radius'] - - center = tuple(spu.convert_to(_center, spu.um) / spu.um) - radius = spu.convert_to(_radius, spu.um) / spu.um - return td.Structure( geometry=td.Sphere( - radius=radius, - center=center, + radius=input_sockets['Radius'], + center=input_sockets['Center'], ), - medium=medium, + medium=input_sockets['Medium'], ) #################### @@ -63,52 +63,42 @@ class SphereStructureNode(base.MaxwellSimNode): #################### @events.on_value_changed( socket_name={'Center', 'Radius'}, + prop_name='preview_active', + props={'preview_active'}, input_sockets={'Center', 'Radius'}, - managed_objs={'structure_sphere'}, + managed_objs={'mesh', 'modifier'}, + unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, + scale_input_sockets={ + 'Center': 'Tidy3DUnits', + 'Radius': 'Tidy3DUnits', + }, ) - def on_value_changed__center_radius( + def on_inputs_changed( self, - input_sockets: dict, + props: dict, managed_objs: dict[str, ct.schemas.ManagedObj], + input_sockets: dict, + unit_systems: dict, ): - _center = input_sockets['Center'] - center = tuple([float(el) for el in spu.convert_to(_center, spu.um) / spu.um]) - - _radius = input_sockets['Radius'] - radius = float(spu.convert_to(_radius, spu.um) / spu.um) - ## TODO: Preview unit system?? Presume um for now - - # Retrieve Hard-Coded GeoNodes and Analyze Input - geo_nodes = bpy.data.node_groups[GEONODES_STRUCTURE_SPHERE] - geonodes_interface = analyze_geonodes.interface(geo_nodes, direc='INPUT') - - # Sync Modifier Inputs - managed_objs['structure_sphere'].sync_geonodes_modifier( - geonodes_node_group=geo_nodes, - geonodes_identifier_to_value={ - geonodes_interface['Radius'].identifier: radius, - ## TODO: Use 'bl_socket_map.value_to_bl`! - ## - This accounts for auto-conversion, unit systems, etc. . - ## - We could keep it in the node base class... - ## - ...But it needs aligning with Blender, too. Hmm. + # Push Input Values to GeoNodes Modifier + managed_objs['modifier'].bl_modifier( + managed_objs['mesh'].bl_object(location=input_sockets['Center']), + 'NODES', + { + 'node_group': import_geonodes(GeoNodes.PrimitiveSphere, 'link'), + 'unit_system': unit_systems['BlenderUnits'], + 'inputs': { + 'Radius': input_sockets['Radius'], + }, }, ) + # Push Preview State + if props['preview_active']: + managed_objs['mesh'].show_preview() - # Sync Object Position - managed_objs['structure_sphere'].bl_object('MESH').location = center - - #################### - # - Preview - Show Preview - #################### - @events.on_show_preview( - managed_objs={'structure_sphere'}, - ) - def on_show_preview( - self, - managed_objs: dict[str, ct.schemas.ManagedObj], - ): - managed_objs['structure_sphere'].show_preview('MESH') - self.on_value_changed__center_radius() + @events.on_init() + def on_init(self): + self.on_inputs_changed() #################### 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 4d36db1..dd0fa90 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 @@ -42,7 +42,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket): # - Initialization #################### def __init_subclass__(cls, **kwargs: typ.Any): - super().__init_subclass__(**kwargs) ## Yucky superclass setup. + super().__init_subclass__(**kwargs) # Setup Blender ID for Node if not hasattr(cls, 'socket_type'): @@ -266,15 +266,12 @@ class MaxwellSimSocket(bpy.types.NodeSocket): if kind == ct.DataFlowKind.Value: if self.is_list: return self.value_list - else: - return self.value - elif kind == ct.DataFlowKind.LazyValue: + return self.value + if kind == ct.DataFlowKind.LazyValue: if self.is_list: return self.lazy_value_list - else: - return self.lazy_value return self.lazy_value - elif kind == ct.DataFlowKind.Capabilities: + if kind == ct.DataFlowKind.Capabilities: return self.capabilities return None 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 d851254..2a7dae8 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 @@ -3,10 +3,14 @@ import numpy as np import pydantic as pyd import sympy.physics.units as spu +from .....utils import logger +from .....utils import extra_sympy_units as spux from .....utils.pydantic_sympy import SympyExpr from ... import contracts as ct from .. import base +log = logger.get(__name__) + #################### # - Blender Socket @@ -20,8 +24,8 @@ class PhysicalLengthBLSocket(base.MaxwellSimSocket): # - Properties #################### raw_value: bpy.props.FloatProperty( - name='Unitless Force', - description='Represents the unitless part of the force', + name='Unitless Length', + description='Represents the unitless part of the length', default=0.0, precision=6, update=(lambda self, context: self.sync_prop('raw_value', context)), @@ -68,7 +72,7 @@ class PhysicalLengthBLSocket(base.MaxwellSimSocket): @value.setter def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit + self.raw_value = spux.sympy_to_python(spux.scale_to_unit(value, self.unit)) @property def value_list(self) -> list[SympyExpr]: diff --git a/src/blender_maxwell/utils/extra_sympy_units.py b/src/blender_maxwell/utils/extra_sympy_units.py index e09c56c..48126b5 100644 --- a/src/blender_maxwell/utils/extra_sympy_units.py +++ b/src/blender_maxwell/utils/extra_sympy_units.py @@ -12,7 +12,6 @@ with pydeps.syspath_from_bpy_prefs(): #################### # - Useful Methods #################### -@functools.lru_cache(maxsize=4096) def uses_units(expression: sp.Expr) -> bool: ## TODO: An LFU cache could do better than an LRU. """Checks if an expression uses any units (`Quantity`).""" @@ -23,7 +22,6 @@ def uses_units(expression: sp.Expr) -> bool: # Function to return a set containing all units used in the expression -@functools.lru_cache(maxsize=4096) def get_units(expression: sp.Expr): ## TODO: An LFU cache could do better than an LRU. """Gets all the units of an expression (as `Quantity`).""" @@ -94,7 +92,6 @@ def parse_abbrev_symbols_to_units(expr: sp.Basic) -> sp.Basic: #################### # - Units <-> Scalars #################### -@functools.lru_cache(maxsize=8192) def scale_to_unit(expr: sp.Expr, unit: spu.Quantity) -> typ.Any: ## TODO: An LFU cache could do better than an LRU. unitless_expr = spu.convert_to(expr, unit) / unit @@ -108,7 +105,6 @@ def scale_to_unit(expr: sp.Expr, unit: spu.Quantity) -> typ.Any: #################### # - Sympy <-> Scalars #################### -@functools.lru_cache(maxsize=8192) def sympy_to_python(scalar: sp.Basic) -> int | float | complex | tuple | list: """Convert a scalar sympy expression to the directly corresponding Python type. @@ -128,7 +124,7 @@ def sympy_to_python(scalar: sp.Basic) -> int | float | complex | tuple | list: # Detect Row / Column Vector ## When it's "actually" a 1D structure, flatten and return as tuple. if 1 in scalar.shape: - return tuple(itertools.from_iterable(list_2d)) + return tuple(itertools.chain.from_iterable(list_2d)) return list_2d if scalar.is_integer: diff --git a/src/blender_maxwell/utils/pydantic_sympy.py b/src/blender_maxwell/utils/pydantic_sympy.py index f6f745f..97b3f71 100644 --- a/src/blender_maxwell/utils/pydantic_sympy.py +++ b/src/blender_maxwell/utils/pydantic_sympy.py @@ -111,28 +111,24 @@ def ConstrSympyExpr( allowed_sets and isinstance(expr, sp.Expr) and not any( - [ - { - 'integer': expr.is_integer, - 'rational': expr.is_rational, - 'real': expr.is_real, - 'complex': expr.is_complex, - }[allowed_set] - for allowed_set in allowed_sets - ] + { + 'integer': expr.is_integer, + 'rational': expr.is_rational, + 'real': expr.is_real, + 'complex': expr.is_complex, + }[allowed_set] + for allowed_set in allowed_sets ) ): msgs.add( f"allowed_sets={allowed_sets} does not match expression {expr} (remember to add assumptions to symbols, ex. `x = sp.Symbol('x', real=True))" ) if allowed_structures and not any( - [ - { - 'matrix': isinstance(expr, sp.MatrixBase), - }[allowed_set] - for allowed_set in allowed_structures - if allowed_structures != 'scalar' - ] + { + 'matrix': isinstance(expr, sp.MatrixBase), + }[allowed_set] + for allowed_set in allowed_structures + if allowed_structures != 'scalar' ): msgs.add( f"allowed_structures={allowed_structures} does not match expression {expr} (remember to add assumptions to symbols, ex. `x = sp.Symbol('x', real=True))"