feat: Added Tidy3D file import node
parent
627457ff40
commit
fd6df15b62
|
@ -0,0 +1,3 @@
|
||||||
|
*.html
|
||||||
|
*.pdf
|
||||||
|
*_files/
|
|
@ -0,0 +1,7 @@
|
||||||
|
title: Interlinks
|
||||||
|
author: Michael Chow
|
||||||
|
version: 1.1.0
|
||||||
|
quarto-required: ">=1.2.0"
|
||||||
|
contributes:
|
||||||
|
filters:
|
||||||
|
- interlinks.lua
|
|
@ -0,0 +1,254 @@
|
||||||
|
local function read_inv_text(filename)
|
||||||
|
-- read file
|
||||||
|
local file = io.open(filename, "r")
|
||||||
|
if file == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local str = file:read("a")
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
|
||||||
|
local project = str:match("# Project: (%S+)")
|
||||||
|
local version = str:match("# Version: (%S+)")
|
||||||
|
|
||||||
|
local data = {project = project, version = version, items = {}}
|
||||||
|
|
||||||
|
local ptn_data =
|
||||||
|
"^" ..
|
||||||
|
"(.-)%s+" .. -- name
|
||||||
|
"([%S:]-):" .. -- domain
|
||||||
|
"([%S]+)%s+" .. -- role
|
||||||
|
"(%-?%d+)%s+" .. -- priority
|
||||||
|
"(%S*)%s+" .. -- uri
|
||||||
|
"(.-)\r?$" -- dispname
|
||||||
|
|
||||||
|
|
||||||
|
-- Iterate through each line in the file content
|
||||||
|
for line in str:gmatch("[^\r\n]+") do
|
||||||
|
if not line:match("^#") then
|
||||||
|
-- Match each line against the pattern
|
||||||
|
local name, domain, role, priority, uri, dispName = line:match(ptn_data)
|
||||||
|
|
||||||
|
-- if name is nil, raise an error
|
||||||
|
if name == nil then
|
||||||
|
error("Error parsing line: " .. line)
|
||||||
|
end
|
||||||
|
|
||||||
|
data.items[#data.items + 1] = {
|
||||||
|
name = name,
|
||||||
|
domain = domain,
|
||||||
|
role = role,
|
||||||
|
priority = priority,
|
||||||
|
uri = uri,
|
||||||
|
dispName = dispName
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
local function read_json(filename)
|
||||||
|
|
||||||
|
local file = io.open(filename, "r")
|
||||||
|
if file == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local str = file:read("a")
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
local decoded = quarto.json.decode(str)
|
||||||
|
return decoded
|
||||||
|
end
|
||||||
|
|
||||||
|
local function read_inv_text_or_json(base_name)
|
||||||
|
local file = io.open(base_name .. ".txt", "r")
|
||||||
|
if file then
|
||||||
|
-- TODO: refactors so we don't just close the file immediately
|
||||||
|
io.close(file)
|
||||||
|
json = read_inv_text(base_name .. ".txt")
|
||||||
|
|
||||||
|
else
|
||||||
|
json = read_json(base_name .. ".json")
|
||||||
|
end
|
||||||
|
|
||||||
|
return json
|
||||||
|
end
|
||||||
|
|
||||||
|
local inventory = {}
|
||||||
|
|
||||||
|
local function lookup(search_object)
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
for _, inv in ipairs(inventory) do
|
||||||
|
for _, item in ipairs(inv.items) do
|
||||||
|
-- e.g. :external+<inv_name>:<domain>:<role>:`<name>`
|
||||||
|
if item.inv_name and item.inv_name ~= search_object.inv_name then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if item.name ~= search_object.name then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if search_object.role and item.role ~= search_object.role then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if search_object.domain and item.domain ~= search_object.domain then
|
||||||
|
goto continue
|
||||||
|
else
|
||||||
|
if search_object.domain or item.domain == "py" then
|
||||||
|
table.insert(results, item)
|
||||||
|
end
|
||||||
|
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #results == 1 then
|
||||||
|
return results[1]
|
||||||
|
end
|
||||||
|
if #results > 1 then
|
||||||
|
quarto.log.warning("Found multiple matches for " .. search_object.name .. ", using the first match.")
|
||||||
|
return results[1]
|
||||||
|
end
|
||||||
|
if #results == 0 then
|
||||||
|
quarto.log.warning("Found no matches for object:\n", search_object)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mysplit (inputstr, sep)
|
||||||
|
if sep == nil then
|
||||||
|
sep = "%s"
|
||||||
|
end
|
||||||
|
local t={}
|
||||||
|
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
|
||||||
|
table.insert(t, str)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function normalize_role(role)
|
||||||
|
if role == "func" then
|
||||||
|
return "function"
|
||||||
|
end
|
||||||
|
return role
|
||||||
|
end
|
||||||
|
|
||||||
|
local function build_search_object(str)
|
||||||
|
local starts_with_colon = str:sub(1, 1) == ":"
|
||||||
|
local search = {}
|
||||||
|
if starts_with_colon then
|
||||||
|
local t = mysplit(str, ":")
|
||||||
|
if #t == 2 then
|
||||||
|
-- e.g. :py:func:`my_func`
|
||||||
|
search.role = normalize_role(t[1])
|
||||||
|
search.name = t[2]:match("%%60(.*)%%60")
|
||||||
|
elseif #t == 3 then
|
||||||
|
-- e.g. :py:func:`my_func`
|
||||||
|
search.domain = t[1]
|
||||||
|
search.role = normalize_role(t[2])
|
||||||
|
search.name = t[3]:match("%%60(.*)%%60")
|
||||||
|
elseif #t == 4 then
|
||||||
|
-- e.g. :ext+inv:py:func:`my_func`
|
||||||
|
search.external = true
|
||||||
|
|
||||||
|
search.inv_name = t[1]:match("external%+(.*)")
|
||||||
|
search.domain = t[2]
|
||||||
|
search.role = normalize_role(t[3])
|
||||||
|
search.name = t[4]:match("%%60(.*)%%60")
|
||||||
|
else
|
||||||
|
quarto.log.warning("couldn't parse this link: " .. str)
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
search.name = str:match("%%60(.*)%%60")
|
||||||
|
end
|
||||||
|
|
||||||
|
if search.name == nil then
|
||||||
|
quarto.log.warning("couldn't parse this link: " .. str)
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if search.name:sub(1, 1) == "~" then
|
||||||
|
search.shortened = true
|
||||||
|
search.name = search.name:sub(2, -1)
|
||||||
|
end
|
||||||
|
return search
|
||||||
|
end
|
||||||
|
|
||||||
|
local function report_broken_link(link, search_object, replacement)
|
||||||
|
-- TODO: how to unescape html elements like [?
|
||||||
|
return pandoc.Code(pandoc.utils.stringify(link.content))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Link(link)
|
||||||
|
-- do not process regular links ----
|
||||||
|
if not link.target:match("%%60") then
|
||||||
|
return link
|
||||||
|
end
|
||||||
|
|
||||||
|
-- lookup item ----
|
||||||
|
local search = build_search_object(link.target)
|
||||||
|
local item = lookup(search)
|
||||||
|
|
||||||
|
-- determine replacement, used if no link text specified ----
|
||||||
|
local original_text = pandoc.utils.stringify(link.content)
|
||||||
|
local replacement = search.name
|
||||||
|
if search.shortened then
|
||||||
|
local t = mysplit(search.name, ".")
|
||||||
|
replacement = t[#t]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set link text ----
|
||||||
|
if original_text == "" and replacement ~= nil then
|
||||||
|
link.content = pandoc.Code(replacement)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- report broken links ----
|
||||||
|
if item == nil then
|
||||||
|
return report_broken_link(link, search)
|
||||||
|
end
|
||||||
|
link.target = item.uri:gsub("%$$", search.name)
|
||||||
|
|
||||||
|
|
||||||
|
return link
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fixup_json(json, prefix)
|
||||||
|
for _, item in ipairs(json.items) do
|
||||||
|
item.uri = prefix .. item.uri
|
||||||
|
end
|
||||||
|
table.insert(inventory, json)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
Meta = function(meta)
|
||||||
|
local json
|
||||||
|
local prefix
|
||||||
|
if meta.interlinks and meta.interlinks.sources then
|
||||||
|
for k, v in pairs(meta.interlinks.sources) do
|
||||||
|
local base_name = quarto.project.offset .. "/_inv/" .. k .. "_objects"
|
||||||
|
json = read_inv_text_or_json(base_name)
|
||||||
|
prefix = pandoc.utils.stringify(v.url)
|
||||||
|
if json ~= nil then
|
||||||
|
fixup_json(json, prefix)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
json = read_inv_text_or_json(quarto.project.offset .. "/objects")
|
||||||
|
if json ~= nil then
|
||||||
|
fixup_json(json, "/")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Link = Link
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
The root of the website.
|
|
@ -204,7 +204,6 @@ def _writable_bl_socket_value(
|
||||||
unit_system: dict | None = None,
|
unit_system: dict | None = None,
|
||||||
allow_unit_not_in_unit_system: bool = False,
|
allow_unit_not_in_unit_system: bool = False,
|
||||||
) -> typ.Any:
|
) -> typ.Any:
|
||||||
log.debug('Writing BL Socket Value (%s)', str(value))
|
|
||||||
socket_type = _socket_type_from_bl_socket(description, bl_socket_type)
|
socket_type = _socket_type_from_bl_socket(description, bl_socket_type)
|
||||||
|
|
||||||
# Retrieve Unit-System Unit
|
# Retrieve Unit-System Unit
|
||||||
|
|
|
@ -16,9 +16,12 @@ class NodeType(BlenderTypeEnum):
|
||||||
## Inputs / Scene
|
## Inputs / Scene
|
||||||
Time = enum.auto()
|
Time = enum.auto()
|
||||||
|
|
||||||
## Inputs / Importers
|
## Inputs / Web Importers
|
||||||
Tidy3DWebImporter = enum.auto()
|
Tidy3DWebImporter = enum.auto()
|
||||||
|
|
||||||
|
## Inputs / File Importers
|
||||||
|
Tidy3DFileImporter = enum.auto()
|
||||||
|
|
||||||
## Inputs / Parameters
|
## Inputs / Parameters
|
||||||
NumberParameter = enum.auto()
|
NumberParameter = enum.auto()
|
||||||
PhysicalParameter = enum.auto()
|
PhysicalParameter = enum.auto()
|
||||||
|
@ -34,8 +37,6 @@ class NodeType(BlenderTypeEnum):
|
||||||
RealList = enum.auto()
|
RealList = enum.auto()
|
||||||
ComplexList = enum.auto()
|
ComplexList = enum.auto()
|
||||||
|
|
||||||
## Inputs /
|
|
||||||
InputFile = enum.auto()
|
|
||||||
|
|
||||||
# Outputs
|
# Outputs
|
||||||
## Outputs / Viewers
|
## Outputs / Viewers
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from . import (
|
from . import (
|
||||||
constants,
|
constants,
|
||||||
unit_system,
|
file_importers,
|
||||||
wave_constant,
|
unit_system,
|
||||||
web_importers,
|
wave_constant,
|
||||||
|
web_importers,
|
||||||
)
|
)
|
||||||
|
|
||||||
# from . import file_importers
|
# from . import file_importers
|
||||||
|
@ -12,12 +13,12 @@ BL_REGISTER = [
|
||||||
*unit_system.BL_REGISTER,
|
*unit_system.BL_REGISTER,
|
||||||
*constants.BL_REGISTER,
|
*constants.BL_REGISTER,
|
||||||
*web_importers.BL_REGISTER,
|
*web_importers.BL_REGISTER,
|
||||||
# *file_importers.BL_REGISTER,
|
*file_importers.BL_REGISTER,
|
||||||
]
|
]
|
||||||
BL_NODES = {
|
BL_NODES = {
|
||||||
**wave_constant.BL_NODES,
|
**wave_constant.BL_NODES,
|
||||||
**unit_system.BL_NODES,
|
**unit_system.BL_NODES,
|
||||||
**constants.BL_NODES,
|
**constants.BL_NODES,
|
||||||
**web_importers.BL_NODES,
|
**web_importers.BL_NODES,
|
||||||
# *file_importers.BL_REGISTER,
|
**file_importers.BL_NODES,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from . import tidy_3d_file_importer
|
||||||
|
|
||||||
|
BL_REGISTER = [
|
||||||
|
*tidy_3d_file_importer.BL_REGISTER,
|
||||||
|
]
|
||||||
|
BL_NODES = {
|
||||||
|
**tidy_3d_file_importer.BL_NODES,
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
import typing as typ
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import tidy3d as td
|
||||||
|
|
||||||
|
from ......utils import logger
|
||||||
|
from .... import contracts as ct
|
||||||
|
from .... import sockets
|
||||||
|
from ... import base, events
|
||||||
|
|
||||||
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
TD_FILE_EXTS = {
|
||||||
|
'.hdf5.gz',
|
||||||
|
'.hdf5',
|
||||||
|
'.json',
|
||||||
|
'.yaml',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Node
|
||||||
|
####################
|
||||||
|
class Tidy3DFileImporterNode(base.MaxwellSimNode):
|
||||||
|
node_type = ct.NodeType.Tidy3DFileImporter
|
||||||
|
bl_label = 'Tidy3D File Importer'
|
||||||
|
|
||||||
|
input_sockets: typ.ClassVar = {
|
||||||
|
'File Path': sockets.FilePathSocketDef(),
|
||||||
|
}
|
||||||
|
|
||||||
|
tidy3d_type: bpy.props.EnumProperty(
|
||||||
|
name='Tidy3D Type',
|
||||||
|
description='Type of Tidy3D object to load',
|
||||||
|
items=[
|
||||||
|
(
|
||||||
|
'SIMULATION_DATA',
|
||||||
|
'Simulation Data',
|
||||||
|
'Data from Completed Tidy3D Simulation',
|
||||||
|
),
|
||||||
|
('SIMULATION', 'Simulation', 'Tidy3D Simulation'),
|
||||||
|
('MEDIUM', 'Medium', 'A Tidy3D Medium'),
|
||||||
|
],
|
||||||
|
default='SIMULATION_DATA',
|
||||||
|
update=lambda self, context: self.sync_prop('tidy3d_type', context),
|
||||||
|
)
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Event Methods: Compute Output Data
|
||||||
|
####################
|
||||||
|
def _compute_sim_data_for(
|
||||||
|
self, output_socket_name: str, file_path: Path
|
||||||
|
) -> td.components.base.Tidy3dBaseModel:
|
||||||
|
return {
|
||||||
|
'Sim': td.Simulation,
|
||||||
|
'Sim Data': td.SimulationData,
|
||||||
|
'Medium': td.Medium,
|
||||||
|
}[output_socket_name].from_file(str(file_path))
|
||||||
|
|
||||||
|
@events.computes_output_socket(
|
||||||
|
'Sim',
|
||||||
|
input_sockets={'File Path'},
|
||||||
|
)
|
||||||
|
def compute_sim(self, input_sockets: dict) -> td.Simulation:
|
||||||
|
return self._compute_sim_data_for('Sim', input_sockets['File Path'])
|
||||||
|
|
||||||
|
@events.computes_output_socket(
|
||||||
|
'Sim Data',
|
||||||
|
input_sockets={'File Path'},
|
||||||
|
)
|
||||||
|
def compute_sim_data(self, input_sockets: dict) -> td.SimulationData:
|
||||||
|
return self._compute_sim_data_for('Sim Data', input_sockets['File Path'])
|
||||||
|
|
||||||
|
@events.computes_output_socket(
|
||||||
|
'Medium',
|
||||||
|
input_sockets={'File Path'},
|
||||||
|
)
|
||||||
|
def compute_medium(self, input_sockets: dict) -> td.Medium:
|
||||||
|
return self._compute_sim_data_for('Medium', input_sockets['File Path'])
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Event Methods: Setup Output Socket
|
||||||
|
####################
|
||||||
|
@events.on_value_changed(
|
||||||
|
socket_name='File Path',
|
||||||
|
prop_name='tidy3d_type',
|
||||||
|
input_sockets={'File Path'},
|
||||||
|
props={'tidy3d_type'},
|
||||||
|
)
|
||||||
|
def on_file_changed(self, input_sockets: dict, props: dict):
|
||||||
|
file_ext = ''.join(input_sockets['File Path'].suffixes)
|
||||||
|
if not (input_sockets['File Path'].is_file() and file_ext in TD_FILE_EXTS):
|
||||||
|
self.loose_output_sockets = {}
|
||||||
|
else:
|
||||||
|
self.loose_output_sockets = {
|
||||||
|
'SIMULATION_DATA': {
|
||||||
|
'Sim Data': sockets.MaxwellFDTDSimSocketDef(),
|
||||||
|
},
|
||||||
|
'SIMULATION': {'Sim': sockets.MaxwellFDTDSimDataSocketDef()},
|
||||||
|
'MEDIUM': {'Medium': sockets.MaxwellMediumSocketDef()},
|
||||||
|
}[props['tidy3d_type']]
|
||||||
|
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Blender Registration
|
||||||
|
####################
|
||||||
|
BL_REGISTER = [
|
||||||
|
Tidy3DFileImporterNode,
|
||||||
|
]
|
||||||
|
BL_NODES = {
|
||||||
|
ct.NodeType.Tidy3DFileImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_IMPORTERS)
|
||||||
|
}
|
|
@ -112,15 +112,6 @@ class ViewerNode(base.MaxwellSimNode):
|
||||||
# - Methods
|
# - Methods
|
||||||
####################
|
####################
|
||||||
def print_data_to_console(self):
|
def print_data_to_console(self):
|
||||||
import sys
|
|
||||||
|
|
||||||
for module_name, module in sys.modules.copy().items():
|
|
||||||
if module_name == '__mp_main__':
|
|
||||||
print(
|
|
||||||
'Anything, even repr(), with this module just crashes:', module_name
|
|
||||||
)
|
|
||||||
print(module) ## Crash
|
|
||||||
|
|
||||||
if not self.inputs['Data'].is_linked:
|
if not self.inputs['Data'].is_linked:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ class SimDomainNode(base.MaxwellSimNode):
|
||||||
'Size': 'Tidy3DUnits',
|
'Size': 'Tidy3DUnits',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def compute_output(self, input_sockets: dict) -> sp.Expr:
|
def compute_domain(self, input_sockets: dict) -> sp.Expr:
|
||||||
return {
|
return {
|
||||||
'run_time': input_sockets['Duration'],
|
'run_time': input_sockets['Duration'],
|
||||||
'center': input_sockets['Center'],
|
'center': input_sockets['Center'],
|
||||||
|
|
|
@ -42,9 +42,9 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Structure',
|
'Structure',
|
||||||
input_sockets={'Medium'},
|
input_sockets={'Medium'},
|
||||||
managed_objs={'geometry'},
|
managed_objs={'mesh'},
|
||||||
)
|
)
|
||||||
def compute_output(
|
def compute_structure(
|
||||||
self,
|
self,
|
||||||
input_sockets: dict[str, typ.Any],
|
input_sockets: dict[str, typ.Any],
|
||||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||||
|
|
|
@ -50,7 +50,7 @@ class BoxStructureNode(base.MaxwellSimNode):
|
||||||
'Size': 'Tidy3DUnits',
|
'Size': 'Tidy3DUnits',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def compute_output(self, input_sockets: dict, unit_systems: dict) -> td.Box:
|
def compute_structure(self, input_sockets: dict, unit_systems: dict) -> td.Box:
|
||||||
return td.Structure(
|
return td.Structure(
|
||||||
geometry=td.Box(
|
geometry=td.Box(
|
||||||
center=input_sockets['Center'],
|
center=input_sockets['Center'],
|
||||||
|
|
|
@ -6,8 +6,11 @@ import sympy as sp
|
||||||
import sympy.physics.units as spu
|
import sympy.physics.units as spu
|
||||||
import typing_extensions as typx
|
import typing_extensions as typx
|
||||||
|
|
||||||
|
from ....utils import logger
|
||||||
from .. import contracts as ct
|
from .. import contracts as ct
|
||||||
|
|
||||||
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MaxwellSimSocket(bpy.types.NodeSocket):
|
class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
# Fundamentals
|
# Fundamentals
|
||||||
|
@ -280,10 +283,12 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
self,
|
self,
|
||||||
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
|
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
|
||||||
):
|
):
|
||||||
"""Computes the value of this socket, including all relevant factors:
|
"""Computes the value of this socket, including all relevant factors.
|
||||||
- If input socket, and unlinked, compute internal data.
|
|
||||||
- If input socket, and linked, compute linked socket data.
|
Notes:
|
||||||
- If output socket, ask node for data.
|
- If input socket, and unlinked, compute internal data.
|
||||||
|
- If input socket, and linked, compute linked socket data.
|
||||||
|
- If output socket, ask node for data.
|
||||||
"""
|
"""
|
||||||
# Compute Output Socket
|
# Compute Output Socket
|
||||||
## List-like sockets guarantee that a list of a thing is passed.
|
## List-like sockets guarantee that a list of a thing is passed.
|
||||||
|
@ -339,11 +344,11 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
if value == unit_sympy
|
if value == unit_sympy
|
||||||
]
|
]
|
||||||
if len(matching_unit_names) == 0:
|
if len(matching_unit_names) == 0:
|
||||||
msg = f"Tried to set unit for socket {self} with value {value}, but it is not one of possible units {''.join(possible.units.values())} for this socket (as defined in `contracts.SOCKET_UNITS`)"
|
msg = f"Tried to set unit for socket {self} with value {value}, but it is not one of possible units {''.join(self.possible_units.values())} for this socket (as defined in `contracts.SOCKET_UNITS`)"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
if len(matching_unit_names) > 1:
|
if len(matching_unit_names) > 1:
|
||||||
msg = f"Tried to set unit for socket {self} with value {value}, but multiple possible matching units {''.join(possible.units.values())} for this socket (as defined in `contracts.SOCKET_UNITS`); there may only be one"
|
msg = f"Tried to set unit for socket {self} with value {value}, but multiple possible matching units {''.join(self.possible_units.values())} for this socket (as defined in `contracts.SOCKET_UNITS`); there may only be one"
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
self.active_unit = matching_unit_names[0]
|
self.active_unit = matching_unit_names[0]
|
||||||
|
|
Loading…
Reference in New Issue