fix: Unit conversion of LazyValueRange.
The unit conversion was indeed botched, with a typo causing the start of the range to be converted as if it were the end of the range. Closes #3.main
parent
3a53e4ce46
commit
3c00530524
7
TODO.md
7
TODO.md
|
@ -510,7 +510,7 @@ Header color style can't be done, unfortunately. Body color feels unclean, so no
|
||||||
|
|
||||||
## BLCache
|
## BLCache
|
||||||
- [ ] Replace every raw property with `BLField`.
|
- [ ] Replace every raw property with `BLField`.
|
||||||
- [ ] Add matrix property support: https://developer.blender.org/docs/release_notes/3.0/python_api/#other-additions
|
- [x] Add matrix property support: https://developer.blender.org/docs/release_notes/3.0/python_api/#other-additions
|
||||||
- [ ] Fix many problems by persisting `_enum_cb_cache` and `_str_cb_cache`.
|
- [ ] Fix many problems by persisting `_enum_cb_cache` and `_str_cb_cache`.
|
||||||
- [ ] Docstring parser for descriptions.
|
- [ ] Docstring parser for descriptions.
|
||||||
- [ ] Method of dynamically setting property options after creation, using `idproperty_ui_data`
|
- [ ] Method of dynamically setting property options after creation, using `idproperty_ui_data`
|
||||||
|
@ -524,6 +524,10 @@ We're trying to do our part by reporting bugs we find!
|
||||||
This is where we keep track of them for now, if they're not covered by the above listings.
|
This is where we keep track of them for now, if they're not covered by the above listings.
|
||||||
|
|
||||||
## Blender Maxwell Bugs
|
## Blender Maxwell Bugs
|
||||||
|
See Issues.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- [ ] `pytest` integration exhibits a bootstrapping problem when using https://github.com/mondeja/pytest-blender
|
||||||
|
|
||||||
## Blender Bugs
|
## Blender Bugs
|
||||||
Reported:
|
Reported:
|
||||||
|
@ -545,6 +549,7 @@ Unreported:
|
||||||
## Tidy3D bugs
|
## Tidy3D bugs
|
||||||
Unreported:
|
Unreported:
|
||||||
- Directly running `SimulationTask.get()` is missing fields - it doesn't return some fields, including `created_at`. Listing tasks by folder is not broken.
|
- Directly running `SimulationTask.get()` is missing fields - it doesn't return some fields, including `created_at`. Listing tasks by folder is not broken.
|
||||||
|
- Frequency ranges don't check for repeated elements.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,8 @@ managed = true
|
||||||
virtual = true
|
virtual = true
|
||||||
dev-dependencies = [
|
dev-dependencies = [
|
||||||
"ruff>=0.3.2",
|
"ruff>=0.3.2",
|
||||||
"fake-bpy-module-4-0>=20231118", ## TODO: Update to Blender 4.1.0
|
"fake-bpy-module-4-0>=20231118",
|
||||||
|
## TODO: Update to Blender 4.1.0
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.rye.scripts]
|
[tool.rye.scripts]
|
||||||
|
@ -143,3 +144,9 @@ max-args = 6
|
||||||
quote-style = "single"
|
quote-style = "single"
|
||||||
indent-style = "tab"
|
indent-style = "tab"
|
||||||
docstring-code-format = false
|
docstring-code-format = false
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Tooling: Pytest
|
||||||
|
####################
|
||||||
|
#[tool.pytest.ini_options]
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
from .array import ArrayFlow
|
from .array import ArrayFlow
|
||||||
from .capabiltiies import CapabilitiesFlow
|
from .capabilities import CapabilitiesFlow
|
||||||
from .flow_kinds import FlowKind
|
from .flow_kinds import FlowKind
|
||||||
from .lazy_array_range import LazyArrayRange
|
from .info import InfoFlow
|
||||||
from .lazy_value_func import LazyValueFunc
|
from .lazy_array_range import LazyArrayRangeFlow
|
||||||
from .params import Params
|
from .lazy_value_func import LazyValueFuncFlow
|
||||||
|
from .params import ParamsFlow
|
||||||
from .value import ValueFlow
|
from .value import ValueFlow
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ArrayFlow',
|
'ArrayFlow',
|
||||||
'CapabilitiesFlow',
|
'CapabilitiesFlow',
|
||||||
'FlowKind',
|
'FlowKind',
|
||||||
'LazyArrayRange',
|
'InfoFlow',
|
||||||
'LazyValueFunc',
|
'LazyArrayRangeFlow',
|
||||||
'Params',
|
'LazyValueFuncFlow',
|
||||||
|
'ParamsFlow',
|
||||||
'ValueFlow',
|
'ValueFlow',
|
||||||
]
|
]
|
||||||
|
|
|
@ -50,19 +50,21 @@ class FlowKind(enum.StrEnum):
|
||||||
def scale_to_unit_system(
|
def scale_to_unit_system(
|
||||||
cls,
|
cls,
|
||||||
kind: typ.Self,
|
kind: typ.Self,
|
||||||
value,
|
flow_obj,
|
||||||
unit_system: spux.UnitSystem,
|
unit_system: spux.UnitSystem,
|
||||||
):
|
):
|
||||||
if kind == cls.Value:
|
log.debug('%s: Scaling "%s" to Unit System', kind, str(flow_obj))
|
||||||
|
if kind == FlowKind.Value:
|
||||||
return spux.scale_to_unit_system(
|
return spux.scale_to_unit_system(
|
||||||
value,
|
flow_obj,
|
||||||
unit_system,
|
unit_system,
|
||||||
)
|
)
|
||||||
if kind == cls.LazyArrayRange:
|
if kind == FlowKind.LazyArrayRange:
|
||||||
return value.rescale_to_unit_system(unit_system)
|
log.debug([kind, flow_obj, unit_system])
|
||||||
|
return flow_obj.rescale_to_unit_system(unit_system)
|
||||||
|
|
||||||
if kind == cls.Params:
|
if kind == FlowKind.Params:
|
||||||
return value.rescale_to_unit_system(unit_system)
|
return flow_obj.rescale_to_unit_system(unit_system)
|
||||||
|
|
||||||
msg = 'Tried to scale unknown kind'
|
msg = 'Tried to scale unknown kind'
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
|
@ -8,11 +8,14 @@ import jaxtyping as jtyp
|
||||||
import sympy as sp
|
import sympy as sp
|
||||||
|
|
||||||
from blender_maxwell.utils import extra_sympy_units as spux
|
from blender_maxwell.utils import extra_sympy_units as spux
|
||||||
|
from blender_maxwell.utils import logger
|
||||||
|
|
||||||
from .array import ArrayFlow
|
from .array import ArrayFlow
|
||||||
from .flow_kinds import FlowKind
|
from .flow_kinds import FlowKind
|
||||||
from .lazy_value_func import LazyValueFuncFlow
|
from .lazy_value_func import LazyValueFuncFlow
|
||||||
|
|
||||||
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(frozen=True, kw_only=True)
|
@dataclasses.dataclass(frozen=True, kw_only=True)
|
||||||
class LazyArrayRangeFlow:
|
class LazyArrayRangeFlow:
|
||||||
|
@ -84,6 +87,16 @@ class LazyArrayRangeFlow:
|
||||||
|
|
||||||
@functools.cached_property
|
@functools.cached_property
|
||||||
def mathtype(self) -> spux.MathType:
|
def mathtype(self) -> spux.MathType:
|
||||||
|
"""Conservatively compute the most stringent `spux.MathType` that can represent both `self.start` and `self.stop`.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The mathtype is determined from start/stop either using `sympy` assumptions, or as Python types.
|
||||||
|
|
||||||
|
For precise information on how start/stop are "combined", see `spux.MathType.combine()`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
All symbols valid for use in the expression.
|
||||||
|
"""
|
||||||
# Get Start Mathtype
|
# Get Start Mathtype
|
||||||
if isinstance(self.start, spux.SympyType):
|
if isinstance(self.start, spux.SympyType):
|
||||||
start_mathtype = spux.MathType.from_expr(self.start)
|
start_mathtype = spux.MathType.from_expr(self.start)
|
||||||
|
@ -97,12 +110,22 @@ class LazyArrayRangeFlow:
|
||||||
stop_mathtype = spux.MathType.from_pytype(type(self.stop))
|
stop_mathtype = spux.MathType.from_pytype(type(self.stop))
|
||||||
|
|
||||||
# Check Equal
|
# Check Equal
|
||||||
if start_mathtype != stop_mathtype:
|
combined_mathtype = spux.MathType.combine(start_mathtype, stop_mathtype)
|
||||||
return spux.MathType.combine(start_mathtype, stop_mathtype)
|
log.debug(
|
||||||
|
'%s: Computed MathType as %s (start_mathtype=%s, stop_mathtype=%s)',
|
||||||
return start_mathtype
|
self,
|
||||||
|
combined_mathtype,
|
||||||
|
start_mathtype,
|
||||||
|
stop_mathtype,
|
||||||
|
)
|
||||||
|
return combined_mathtype
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
"""Compute the length of the array to be realized.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of steps.
|
||||||
|
"""
|
||||||
return self.steps
|
return self.steps
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
@ -121,6 +144,11 @@ class LazyArrayRangeFlow:
|
||||||
ValueError: If the existing unit is `None`, indicating that there is no unit to correct.
|
ValueError: If the existing unit is `None`, indicating that there is no unit to correct.
|
||||||
"""
|
"""
|
||||||
if self.unit is not None:
|
if self.unit is not None:
|
||||||
|
log.debug(
|
||||||
|
'%s: Corrected unit to %s',
|
||||||
|
self,
|
||||||
|
corrected_unit,
|
||||||
|
)
|
||||||
return LazyArrayRangeFlow(
|
return LazyArrayRangeFlow(
|
||||||
start=self.start,
|
start=self.start,
|
||||||
stop=self.stop,
|
stop=self.stop,
|
||||||
|
@ -146,6 +174,11 @@ class LazyArrayRangeFlow:
|
||||||
ValueError: If the existing unit is `None`, indicating that there is no unit to correct.
|
ValueError: If the existing unit is `None`, indicating that there is no unit to correct.
|
||||||
"""
|
"""
|
||||||
if self.unit is not None:
|
if self.unit is not None:
|
||||||
|
log.debug(
|
||||||
|
'%s: Scaled to unit %s',
|
||||||
|
self,
|
||||||
|
unit,
|
||||||
|
)
|
||||||
return LazyArrayRangeFlow(
|
return LazyArrayRangeFlow(
|
||||||
start=spux.scale_to_unit(self.start * self.unit, unit),
|
start=spux.scale_to_unit(self.start * self.unit, unit),
|
||||||
stop=spux.scale_to_unit(self.stop * self.unit, unit),
|
stop=spux.scale_to_unit(self.stop * self.unit, unit),
|
||||||
|
@ -171,13 +204,18 @@ class LazyArrayRangeFlow:
|
||||||
ValueError: If the existing unit is `None`, indicating that there is no unit to correct.
|
ValueError: If the existing unit is `None`, indicating that there is no unit to correct.
|
||||||
"""
|
"""
|
||||||
if self.unit is not None:
|
if self.unit is not None:
|
||||||
|
log.debug(
|
||||||
|
'%s: Scaled to unit system: %s',
|
||||||
|
self,
|
||||||
|
str(unit_system),
|
||||||
|
)
|
||||||
return LazyArrayRangeFlow(
|
return LazyArrayRangeFlow(
|
||||||
start=spux.strip_unit_system(
|
start=spux.strip_unit_system(
|
||||||
spux.convert_to_unit_system(self.start * self.unit, unit_system),
|
spux.convert_to_unit_system(self.start * self.unit, unit_system),
|
||||||
unit_system,
|
unit_system,
|
||||||
),
|
),
|
||||||
stop=spux.strip_unit_system(
|
stop=spux.strip_unit_system(
|
||||||
spux.convert_to_unit_system(self.start * self.unit, unit_system),
|
spux.convert_to_unit_system(self.stop * self.unit, unit_system),
|
||||||
unit_system,
|
unit_system,
|
||||||
),
|
),
|
||||||
steps=self.steps,
|
steps=self.steps,
|
||||||
|
|
|
@ -320,7 +320,7 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
||||||
def on_sim_changed(self, props) -> None:
|
def on_sim_changed(self, props) -> None:
|
||||||
# Sim Linked | First Value Change
|
# Sim Linked | First Value Change
|
||||||
if self.inputs['Sim'].is_linked and not props['sim_info_available']:
|
if self.inputs['Sim'].is_linked and not props['sim_info_available']:
|
||||||
log.critical('First Change: Mark Sim Info Available')
|
log.debug('%s: First Change; Mark Sim Info Available', self.sim_node_name)
|
||||||
self.sim = bl_cache.Signal.InvalidateCache
|
self.sim = bl_cache.Signal.InvalidateCache
|
||||||
self.total_monitor_data = bl_cache.Signal.InvalidateCache
|
self.total_monitor_data = bl_cache.Signal.InvalidateCache
|
||||||
self.is_sim_uploadable = bl_cache.Signal.InvalidateCache
|
self.is_sim_uploadable = bl_cache.Signal.InvalidateCache
|
||||||
|
@ -332,7 +332,7 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
||||||
and props['sim_info_available']
|
and props['sim_info_available']
|
||||||
and not props['sim_info_invalidated']
|
and not props['sim_info_invalidated']
|
||||||
):
|
):
|
||||||
log.critical('Second Change: Mark Sim Info Invalided')
|
log.debug('%s: Second Change; Mark Sim Info Invalided', self.sim_node_name)
|
||||||
self.sim_info_invalidated = True
|
self.sim_info_invalidated = True
|
||||||
|
|
||||||
# Sim Linked | Nth Time
|
# Sim Linked | Nth Time
|
||||||
|
@ -346,7 +346,9 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
||||||
## -> Luckily, since we know there's no sim, invalidation is cheap.
|
## -> Luckily, since we know there's no sim, invalidation is cheap.
|
||||||
## -> Ends up being a "circuit breaker" for sim_info_invalidated.
|
## -> Ends up being a "circuit breaker" for sim_info_invalidated.
|
||||||
elif not self.inputs['Sim'].is_linked:
|
elif not self.inputs['Sim'].is_linked:
|
||||||
log.critical('Unlinked: Short Circuit Zap Cache')
|
log.debug(
|
||||||
|
'%s: Unlinked; Short Circuit the Sim Info Cache', self.sim_node_name
|
||||||
|
)
|
||||||
self.sim = bl_cache.Signal.InvalidateCache
|
self.sim = bl_cache.Signal.InvalidateCache
|
||||||
self.total_monitor_data = bl_cache.Signal.InvalidateCache
|
self.total_monitor_data = bl_cache.Signal.InvalidateCache
|
||||||
self.is_sim_uploadable = bl_cache.Signal.InvalidateCache
|
self.is_sim_uploadable = bl_cache.Signal.InvalidateCache
|
||||||
|
@ -368,7 +370,7 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
||||||
props={'uploaded_task_id'},
|
props={'uploaded_task_id'},
|
||||||
)
|
)
|
||||||
def on_uploaded_task_changed(self, props):
|
def on_uploaded_task_changed(self, props):
|
||||||
log.critical('Uploaded Task Changed')
|
log.debug('Uploaded Task Changed')
|
||||||
self.is_sim_uploadable = bl_cache.Signal.InvalidateCache
|
self.is_sim_uploadable = bl_cache.Signal.InvalidateCache
|
||||||
|
|
||||||
if props['uploaded_task_id'] != '':
|
if props['uploaded_task_id'] != '':
|
||||||
|
|
|
@ -49,15 +49,19 @@ def set_offline():
|
||||||
|
|
||||||
def check_online() -> bool:
|
def check_online() -> bool:
|
||||||
global IS_ONLINE # noqa: PLW0603
|
global IS_ONLINE # noqa: PLW0603
|
||||||
|
log.info('Checking Internet Connection...')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
urllib.request.urlopen(
|
urllib.request.urlopen(
|
||||||
'https://docs.flexcompute.com/projects/tidy3d/en/latest/index.html',
|
'https://docs.flexcompute.com/projects/tidy3d/en/latest/index.html',
|
||||||
timeout=2,
|
timeout=2,
|
||||||
)
|
)
|
||||||
except:
|
except: # noqa: E722
|
||||||
|
log.info('Internet is currently offline')
|
||||||
IS_ONLINE = False
|
IS_ONLINE = False
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
log.info('Internet connection is working')
|
||||||
IS_ONLINE = True
|
IS_ONLINE = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -67,26 +71,25 @@ def check_online() -> bool:
|
||||||
####################
|
####################
|
||||||
def check_authentication() -> bool:
|
def check_authentication() -> bool:
|
||||||
global IS_AUTHENTICATED # noqa: PLW0603
|
global IS_AUTHENTICATED # noqa: PLW0603
|
||||||
log.critical('Checking Authentication')
|
log.info('Checking Tidy3D Authentication...')
|
||||||
|
|
||||||
# Check Previous Authentication
|
|
||||||
## If we authenticated once, we presume that it'll work again.
|
|
||||||
## TODO: API keys can change... It would just look like "offline" for now.
|
|
||||||
if IS_AUTHENTICATED:
|
|
||||||
return True
|
|
||||||
|
|
||||||
api_key = td_web.core.http_util.api_key()
|
api_key = td_web.core.http_util.api_key()
|
||||||
if api_key is not None:
|
if api_key is not None:
|
||||||
|
log.info('Found stored Tidy3D API key')
|
||||||
try:
|
try:
|
||||||
td_web.test()
|
td_web.test()
|
||||||
set_online()
|
|
||||||
except td.exceptions.WebError:
|
except td.exceptions.WebError:
|
||||||
set_offline()
|
set_offline()
|
||||||
|
log.info('Authenticated to Tidy3D cloud')
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
set_online()
|
||||||
|
log.info('Authenticated to Tidy3D cloud')
|
||||||
|
|
||||||
IS_AUTHENTICATED = True
|
IS_AUTHENTICATED = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
log.info('Tidy3D API key is missing')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,6 +102,7 @@ TD_CONFIG = Path(td_web.cli.constants.CONFIG_FILE)
|
||||||
|
|
||||||
## TODO: Robustness is key - internet might be down.
|
## TODO: Robustness is key - internet might be down.
|
||||||
## -> I'm not a huge fan of the max 2sec startup time burden
|
## -> I'm not a huge fan of the max 2sec startup time burden
|
||||||
|
## -> I also don't love "calling" Tidy3D on startup, privacy-wise
|
||||||
if TD_CONFIG.is_file() and check_online():
|
if TD_CONFIG.is_file() and check_online():
|
||||||
check_authentication()
|
check_authentication()
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,6 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# Run Addon
|
# Run Addon
|
||||||
print(f'Blender: Running "{info.ADDON_NAME}"...')
|
print(f'Blender: Running "{info.ADDON_NAME}"...')
|
||||||
subprocess.run
|
|
||||||
return_code, output = run_blender(
|
return_code, output = run_blender(
|
||||||
None, headless=False, load_devfile=True, monitor=True
|
None, headless=False, load_devfile=True, monitor=True
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue