Compare commits

...

2 Commits

7 changed files with 323 additions and 66 deletions

View File

@ -369,15 +369,6 @@ class FuncFlow:
return data | {info.output: self.realize(params, symbol_values=symbol_values)} return data | {info.output: self.realize(params, symbol_values=symbol_values)}
# return {
# dim: (
# dim_idx
# if info.has_idx_cont(dim) or info.has_idx_labels(dim)
# else ??
# )
# for dim, dim_idx in self.dims
# } | {info.output: output_data}
#################### ####################
# - Composition Operations # - Composition Operations
#################### ####################

View File

@ -251,7 +251,7 @@ class ParamsFlow:
func_args=self.func_args + other.func_args, func_args=self.func_args + other.func_args,
func_kwargs=self.func_kwargs | other.func_kwargs, func_kwargs=self.func_kwargs | other.func_kwargs,
symbols=self.symbols | other.symbols, symbols=self.symbols | other.symbols,
is_differentiable=self.is_differentiable & other.is_differentiable, is_differentiable=self.is_differentiable and other.is_differentiable,
) )
def compose_within( def compose_within(

View File

@ -453,10 +453,17 @@ class MaxwellSimNode(bpy.types.Node, bl_instance.BLInstance):
created_sockets[socket_name] = socket_def created_sockets[socket_name] = socket_def
# Initialize Just-Created BL Sockets # Initialize Just-Created BL Sockets
for socket_name, socket_def in created_sockets.items(): for bl_socket_name, socket_def in created_sockets.items():
socket_def.preinit(all_bl_sockets[socket_name]) socket_def.preinit(all_bl_sockets[bl_socket_name])
socket_def.init(all_bl_sockets[socket_name]) socket_def.init(all_bl_sockets[bl_socket_name])
socket_def.postinit(all_bl_sockets[socket_name]) socket_def.postinit(all_bl_sockets[bl_socket_name])
# Invalidate Cached NoFlows
self._compute_input.invalidate(
input_socket_name=bl_socket_name,
kind=...,
unit_system=...,
)
def _sync_sockets(self) -> None: def _sync_sockets(self) -> None:
"""Synchronize the node's sockets with the active sockets. """Synchronize the node's sockets with the active sockets.

View File

@ -110,10 +110,11 @@ class AdiabAbsorbBoundCondNode(base.MaxwellSimNode):
col.label(text='2ε₀/Δt') col.label(text='2ε₀/Δt')
#################### ####################
# - Output # - FlowKind.Value
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'BC', 'BC',
# Loaded
props={'active_socket_set'}, props={'active_socket_set'},
input_sockets={ input_sockets={
'Layers', 'Layers',
@ -124,33 +125,154 @@ class AdiabAbsorbBoundCondNode(base.MaxwellSimNode):
'σ Order': True, 'σ Order': True,
'σ Range': True, 'σ Range': True,
}, },
output_sockets={'BC'},
output_socket_kinds={'BC': ct.FlowKind.Params},
) )
def compute_adiab_absorber_bound_cond(self, props, input_sockets) -> td.Absorber: def compute_bc_value(self, props, input_sockets, output_sockets) -> td.Absorber:
r"""Computes the adiabatic absorber boundary condition based on the active socket set. r"""Computes the adiabatic absorber boundary condition based on the active socket set.
- **Simple**: Use `tidy3d`'s default parameters for defining the absorber parameters (apart from number of layers). - **Simple**: Use `tidy3d`'s default parameters for defining the absorber parameters (apart from number of layers).
- **Full**: Use the user-defined $\sigma$ parameters, specifically polynomial order and sim-relative min/max conductivity values. - **Full**: Use the user-defined $\sigma$ parameters, specifically polynomial order and sim-relative min/max conductivity values.
""" """
log.debug( output_params = output_sockets['BC']
'%s: Computing "%s" Adiabatic Absorber Boundary Condition (Input Sockets = %s)', layers = input_sockets['Layers']
self.sim_node_name,
props['active_socket_set'],
input_sockets,
)
has_output_params = not ct.FlowSignal.check(output_params)
has_layers = not ct.FlowSignal.check(layers)
active_socket_set = props['active_socket_set']
if has_layers and has_output_params and not output_params.symbols:
# Simple PML # Simple PML
if props['active_socket_set'] == 'Simple': if active_socket_set == 'Simple':
return td.Absorber(num_layers=input_sockets['Layers']) return td.Absorber(num_layers=layers)
# Full PML # Full PML
sig_order = input_sockets['σ Order']
sig_range = input_sockets['σ Range']
has_sig_order = not ct.FlowSignal.check(sig_order)
has_sig_range = not ct.FlowSignal.check(sig_range)
if has_sig_order and has_sig_range:
return td.Absorber( return td.Absorber(
num_layers=input_sockets['Layers'], num_layers=layers,
parameters=td.AbsorberParams( parameters=td.AbsorberParams(
sigma_order=input_sockets['σ Order'], sigma_order=sig_order,
sigma_min=input_sockets['σ Range'][0], sigma_min=sig_range[0],
sigma_max=input_sockets['σ Range'][1], sigma_max=sig_range[1],
), ),
) )
return ct.FlowSignal.FlowPending
####################
# - FlowKind.Func
####################
@events.computes_output_socket(
'BC',
kind=ct.FlowKind.Func,
# Loaded
props={'active_socket_set'},
input_sockets={
'Layers',
'σ Order',
'σ Range',
},
input_socket_kinds={
'Layers': ct.FlowKind.Func,
'σ Order': ct.FlowKind.Func,
'σ Range': ct.FlowKind.Func,
},
input_sockets_optional={
'σ Order': True,
'σ Range': True,
},
output_sockets={'BC'},
output_socket_kinds={'BC': ct.FlowKind.Params},
)
def compute_bc_func(self, props, input_sockets, output_sockets) -> td.Absorber:
r"""Computes the adiabatic absorber boundary condition based on the active socket set.
- **Simple**: Use `tidy3d`'s default parameters for defining the absorber parameters (apart from number of layers).
- **Full**: Use the user-defined $\sigma$ parameters, specifically polynomial order and sim-relative min/max conductivity values.
"""
layers = input_sockets['Layers']
has_layers = not ct.FlowSignal.check(layers)
active_socket_set = props['active_socket_set']
if has_layers:
# Simple PML
if active_socket_set == 'Simple':
return layers.compose_within(
enclosing_func=lambda _layers: td.Absorber(num_layers=_layers),
supports_jax=False,
)
# Full PML
sig_order = input_sockets['σ Order']
sig_range = input_sockets['σ Range']
has_sig_order = not ct.FlowSignal.check(sig_order)
has_sig_range = not ct.FlowSignal.check(sig_range)
if has_sig_order and has_sig_range:
return (layers | sig_order | sig_range).compose_within(
enclosing_func=lambda els: td.Absorber(
num_layers=els[0][0],
parameters=td.AbsorberParams(
sigma_order=els[0][1],
sigma_min=els[1][0],
sigma_max=els[1][1],
),
),
supports_jax=False,
)
return ct.FlowSignal.FlowPending
####################
# - FlowKind.Params
####################
@events.computes_output_socket(
'BC',
kind=ct.FlowKind.Params,
# Loaded
props={'active_socket_set'},
input_sockets={
'Layers',
'σ Order',
'σ Range',
},
input_socket_kinds={
'Layers': ct.FlowKind.Params,
'σ Order': ct.FlowKind.Params,
'σ Range': ct.FlowKind.Params,
},
input_sockets_optional={
'σ Order': True,
'σ Range': True,
},
)
def compute_params(self, props, input_sockets) -> td.Box:
layers = input_sockets['Layers']
has_layers = not ct.FlowSignal.check(layers)
active_socket_set = props['active_socket_set']
if has_layers:
# Simple PML
if active_socket_set == 'Simple':
return layers
# Full PML
sig_order = input_sockets['σ Order']
sig_range = input_sockets['σ Range']
has_sig_order = not ct.FlowSignal.check(sig_order)
has_sig_range = not ct.FlowSignal.check(sig_range)
if has_sig_order and has_sig_range:
return layers | sig_order | sig_range
return ct.FlowSignal.FlowPending
#################### ####################

View File

@ -187,10 +187,11 @@ class BlochBoundCondNode(base.MaxwellSimNode):
} }
#################### ####################
# - Output # - FlowKind.Value
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'BC', 'BC',
# Loaded
props={'active_socket_set', 'valid_sim_axis'}, props={'active_socket_set', 'valid_sim_axis'},
input_sockets={ input_sockets={
'Angled Source', 'Angled Source',
@ -202,9 +203,11 @@ class BlochBoundCondNode(base.MaxwellSimNode):
'Sim Domain': True, 'Sim Domain': True,
'Bloch Vector': True, 'Bloch Vector': True,
}, },
output_sockets={'BC'},
output_socket_kinds={'BC': ct.FlowKind.Params},
) )
def compute_bloch_bound_cond( def compute_value(
self, props, input_sockets self, props, input_sockets, output_sockets
) -> td.Periodic | td.BlochBoundary: ) -> td.Periodic | td.BlochBoundary:
r"""Computes the Bloch boundary condition. r"""Computes the Bloch boundary condition.
@ -213,34 +216,165 @@ class BlochBoundCondNode(base.MaxwellSimNode):
The Bloch boundary axis **must** be orthogonal to the source's injection axis. The Bloch boundary axis **must** be orthogonal to the source's injection axis.
- **Manual**: Set the Bloch vector to the user-specified value. - **Manual**: Set the Bloch vector to the user-specified value.
""" """
log.debug( output_params = output_sockets['BC']
'%s: Computing Bloch Boundary Condition (Socket Set = %s)', has_output_params = not ct.FlowSignal.check(output_params)
self.sim_node_name, if not has_output_params or (has_output_params and output_params.symbols):
props['active_socket_set'], return ct.FlowSignal.FlowPending
)
# Naive active_socket_set = props['active_socket_set']
if props['active_socket_set'] == 'Naive': match active_socket_set:
case 'Naive':
return td.Periodic() return td.Periodic()
# Source-Derived case 'Source-Derived':
if props['active_socket_set'] == 'Source-Derived': angled_source = input_sockets['Angled Source']
sim_domain = input_sockets['Sim Domain'] sim_domain = input_sockets['Sim Domain']
valid_sim_axis = props['valid_sim_axis']
has_angled_source = not ct.FlowSignal.check(angled_source)
has_sim_domain = not ct.FlowSignal.check(sim_domain) has_sim_domain = not ct.FlowSignal.check(sim_domain)
if has_sim_domain: if has_angled_source and has_sim_domain:
valid_sim_axis = props['valid_sim_axis']
return td.BlochBoundary.from_source( return td.BlochBoundary.from_source(
source=input_sockets['Angled Source'], source=angled_source,
domain_size=sim_domain['size'][valid_sim_axis.axis], domain_size=sim_domain['size'][valid_sim_axis.axis],
axis=valid_sim_axis.axis, axis=valid_sim_axis.axis,
medium=sim_domain['medium'], medium=sim_domain['medium'],
) )
return ct.FlowSignal.FlowPending return ct.FlowSignal.FlowPending
# Manual case 'Manual':
return td.BlochBoundary(bloch_vec=input_sockets['Bloch Vector']) bloch_vector = input_sockets['Bloch Vector']
has_bloch_vector = not ct.FlowSignal.check(bloch_vector)
if has_bloch_vector:
return td.BlochBoundary(bloch_vec=bloch_vector)
return ct.FlowSignal.FlowPending
####################
# - FlowKind.Func
####################
@events.computes_output_socket(
'BC',
kind=ct.FlowKind.Func,
# Loaded
props={'active_socket_set', 'valid_sim_axis'},
input_sockets={
'Angled Source',
'Sim Domain',
'Bloch Vector',
},
input_socket_kinds={
'Angled Source': ct.FlowKind.Func,
'Sim Domain': ct.FlowKind.Func,
'Bloch Vector': ct.FlowKind.Func,
},
input_sockets_optional={
'Angled Source': True,
'Sim Domain': True,
'Bloch Vector': True,
},
output_sockets={'BC'},
output_socket_kinds={'BC': ct.FlowKind.Params},
)
def compute_bc_func(self, props, input_sockets, output_sockets) -> td.Absorber:
r"""Computes the adiabatic absorber boundary condition based on the active socket set.
- **Simple**: Use `tidy3d`'s default parameters for defining the absorber parameters (apart from number of layers).
- **Full**: Use the user-defined $\sigma$ parameters, specifically polynomial order and sim-relative min/max conductivity values.
"""
output_params = output_sockets['BC']
has_output_params = not ct.FlowSignal.check(output_params)
if not has_output_params:
return ct.FlowSignal.FlowPending
active_socket_set = props['active_socket_set']
match active_socket_set:
case 'Naive':
return ct.FuncFlow(
func=lambda: td.Periodic(),
supports_jax=False,
)
case 'Source-Derived':
angled_source = input_sockets['Angled Source']
sim_domain = input_sockets['Sim Domain']
has_angled_source = not ct.FlowSignal.check(angled_source)
has_sim_domain = not ct.FlowSignal.check(sim_domain)
if has_angled_source and has_sim_domain:
valid_sim_axis = props['valid_sim_axis']
return (angled_source | sim_domain).compose_within(
enclosing_func=lambda els: td.BlochBoundary.from_source(
source=els[0],
domain_size=els[1]['size'][valid_sim_axis.axis],
axis=valid_sim_axis.axis,
medium=els[1]['medium'],
),
supports_jax=False,
)
return ct.FlowSignal.FlowPending
case 'Manual':
bloch_vector = input_sockets['Bloch Vector']
has_bloch_vector = not ct.FlowSignal.check(bloch_vector)
if has_bloch_vector:
return bloch_vector.compose_within(
enclosing_func=lambda: td.BlochBoundary(bloch_vec=bloch_vector),
supports_jax=False,
)
return ct.FlowSignal.FlowPending
####################
# - FlowKind.Params
####################
@events.computes_output_socket(
'BC',
kind=ct.FlowKind.Params,
# Loaded
props={'active_socket_set'},
input_sockets={
'Angled Source',
'Sim Domain',
'Bloch Vector',
},
input_socket_kinds={
'Angled Source': ct.FlowKind.Params,
'Sim Domain': ct.FlowKind.Params,
'Bloch Vector': ct.FlowKind.Params,
},
input_sockets_optional={
'Angled Source': True,
'Sim Domain': True,
'Bloch Vector': True,
},
)
def compute_bc_params(self, props, input_sockets) -> ct.ParamsFlow | ct.FlowSignal:
active_socket_set = props['active_socket_set']
match active_socket_set:
case 'Naive':
return ct.ParamsFlow()
case 'Source-Derived':
angled_source = input_sockets['Angled Source']
sim_domain = input_sockets['Sim Domain']
has_angled_source = not ct.FlowSignal.check(angled_source)
has_sim_domain = not ct.FlowSignal.check(sim_domain)
if has_sim_domain and has_angled_source:
return angled_source | sim_domain
return ct.FlowSignal.FlowPending
case 'Manual':
bloch_vector = input_sockets['Bloch Vector']
has_bloch_vector = not ct.FlowSignal.check(bloch_vector)
if has_bloch_vector:
return bloch_vector
return ct.FlowSignal.FlowPending
#################### ####################

View File

@ -93,10 +93,11 @@ class BoundCondsNode(base.MaxwellSimNode):
} }
#################### ####################
# - Output Socket Computation # - FlowKind.Value
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'BCs', 'BCs',
kind=ct.FlowKind.Value,
input_sockets={'X', 'Y', 'Z', '+X', '-X', '+Y', '-Y', '+Z', '-Z'}, input_sockets={'X', 'Y', 'Z', '+X', '-X', '+Y', '-Y', '+Z', '-Z'},
input_sockets_optional={ input_sockets_optional={
'X': True, 'X': True,

View File

@ -34,6 +34,8 @@ log = logger.get(__name__)
class BoxStructureNode(base.MaxwellSimNode): class BoxStructureNode(base.MaxwellSimNode):
"""A generic, differentiable box structure with configurable size and center."""
node_type = ct.NodeType.BoxStructure node_type = ct.NodeType.BoxStructure
bl_label = 'Box Structure' bl_label = 'Box Structure'
use_sim_node_name = True use_sim_node_name = True
@ -205,8 +207,8 @@ class BoxStructureNode(base.MaxwellSimNode):
if has_center and has_size and has_medium: if has_center and has_size and has_medium:
if props['differentiable'] == ( if props['differentiable'] == (
center.is_differentiable center.is_differentiable
& size.is_differentiable and size.is_differentiable
& medium.is_differentiable and medium.is_differentiable
): ):
return center | size | medium return center | size | medium
return ct.FlowSignal.FlowPending return ct.FlowSignal.FlowPending