feat: Scientific constant node.

main
Sofus Albert Høgsbro Rose 2024-04-09 10:37:50 +02:00
parent a75f697acd
commit 56abb3fb46
Signed by: so-rose
GPG Key ID: AD901CB0F3701434
5 changed files with 242 additions and 17 deletions

15
TODO.md
View File

@ -13,9 +13,9 @@
- [x] Unit System - [x] Unit System
- [ ] Implement presets, including "Tidy3D" and "Blender", shown in the label row. - [ ] Implement presets, including "Tidy3D" and "Blender", shown in the label row.
- [ ] Constants / Scientific Constant - [x] Constants / Scientific Constant
- [ ] Create `utils.sci_constants` to map `scipy` constants to `sympy` units. - [x] Create `utils.sci_constants` to map `scipy` constants to `sympy` units.
- [ ] Utilize `utils.sci_constants` to make it easy for the user to select appropriate constants with two-layered dropdowns. - [x] Utilize `utils.sci_constants` to make it easy for the user to select appropriate constants with two-layered dropdowns.
- [x] Constants / Number Constant - [x] Constants / Number Constant
- [ ] Constants / Physical Constant - [ ] Constants / Physical Constant
- [ ] Pol: Elliptical viz as 2D plot. - [ ] Pol: Elliptical viz as 2D plot.
@ -455,6 +455,13 @@
We're trying to do our part by reporting bugs we find! We're trying to do our part by reporting bugs we find!
This is where we keep track of them for now. This is where we keep track of them for now.
## Blender Maxwell Bugs
- [ ] Slow changing of socket sets / range on wave constant.
- [ ] API auth shouldn't show if everything is fine in Cloud Task socket
- [ ] Cloud task socket loads folders before its node shows, which can be slow (and error prone if offline)
- [ ] Dispersive fit is slow, which means lag on normal operations that rely on the fit result - fit computation should be integrated into the node, and the output socket should only appear when the fit is available.
- [ ] Numerical, Physical Constant is missing entries
## Blender Bugs ## Blender Bugs
Reported: Reported:
- (SOLVED) <https://projects.blender.org/blender/blender/issues/119664> - (SOLVED) <https://projects.blender.org/blender/blender/issues/119664>
@ -462,6 +469,6 @@ Reported:
Unreported: Unreported:
- The `__mp_main__` bug. - The `__mp_main__` bug.
# 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.

View File

@ -1,15 +1,15 @@
# from . import scientific_constant # from . import scientific_constant
# from . import physical_constant # from . import physical_constant
from . import blender_constant, number_constant from . import blender_constant, number_constant, scientific_constant
BL_REGISTER = [ BL_REGISTER = [
# *scientific_constant.BL_REGISTER, *scientific_constant.BL_REGISTER,
*number_constant.BL_REGISTER, *number_constant.BL_REGISTER,
# *physical_constant.BL_REGISTER, # *physical_constant.BL_REGISTER,
*blender_constant.BL_REGISTER, *blender_constant.BL_REGISTER,
] ]
BL_NODES = { BL_NODES = {
# **scientific_constant.BL_NODES, **scientific_constant.BL_NODES,
**number_constant.BL_NODES, **number_constant.BL_NODES,
# **physical_constant.BL_NODES, # **physical_constant.BL_NODES,
**blender_constant.BL_NODES, **blender_constant.BL_NODES,

View File

@ -1,6 +1,85 @@
## TODO: Discover dropdown options from sci_constants.py import typing as typ
import bpy
from ......utils import sci_constants as constants
from .... import contracts as ct
from .... import sockets
from ... import base, events
class ScientificConstantNode(base.MaxwellSimNode):
node_type = ct.NodeType.ScientificConstant
bl_label = 'Scientific Constant'
output_sockets: typ.ClassVar = {
'Value': sockets.AnySocketDef(),
}
####################
# - Properties
####################
sci_constant: bpy.props.StringProperty(
name='Sci Constant',
description='The name of a scientific constant',
default='',
search=lambda self, _, edit_text: self.search_sci_constants(edit_text),
update=lambda self, context: self.on_update_sci_constant(context),
)
cache__units: bpy.props.StringProperty(default='')
cache__uncertainty: bpy.props.StringProperty(default='')
def search_sci_constants(
self,
edit_text: str,
):
return [name for name in constants.SCI_CONSTANTS if edit_text in name]
def on_update_sci_constant(
self,
context: bpy.types.Context,
):
if self.sci_constant:
self.cache__units = str(
constants.SCI_CONSTANTS_INFO[self.sci_constant]['units']
)
self.cache__uncertainty = str(
constants.SCI_CONSTANTS_INFO[self.sci_constant]['uncertainty']
)
else:
self.cache__units = ''
self.cache__uncertainty = ''
self.sync_prop('sci_constant', context)
####################
# - UI
####################
def draw_props(self, _: bpy.types.Context, col: bpy.types.UILayout) -> None:
col.prop(self, 'sci_constant', text='')
def draw_info(self, _: bpy.types.Context, col: bpy.types.UILayout) -> None:
if self.sci_constant:
col.label(text=f'Units: {self.cache__units}')
col.label(text=f'Uncertainty: {self.cache__uncertainty}')
col.label(text=f'Ref: {constants.SCI_CONSTANTS_REF[0]}')
####################
# - Callbacks
####################
@events.computes_output_socket('Value', props={'sci_constant'})
def compute_value(self, props: dict) -> typ.Any:
return constants.SCI_CONSTANTS[props['sci_constant']]
#################### ####################
# - Blender Registration # - Blender Registration
#################### ####################
BL_REGISTER = [] BL_REGISTER = [
BL_NODES = {} ScientificConstantNode,
]
BL_NODES = {
ct.NodeType.ScientificConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS)
}

View File

@ -2,11 +2,8 @@ import functools
import itertools import itertools
import typing as typ import typing as typ
from . import pydeps import sympy as sp
import sympy.physics.units as spu
with pydeps.syspath_from_bpy_prefs():
import sympy as sp
import sympy.physics.units as spu
#################### ####################
@ -39,6 +36,20 @@ femtosecond = fs = spu.Quantity('femtosecond', abbrev='fs')
femtosecond.set_global_relative_scale_factor(spu.femto, spu.second) femtosecond.set_global_relative_scale_factor(spu.femto, spu.second)
####################
# - Length
####################
femtometer = fm = spu.Quantity('femtometer', abbrev='fm')
femtometer.set_global_relative_scale_factor(spu.femto, spu.meter)
####################
# - Lum Flux
####################
lumen = lm = spu.Quantity('lumen', abbrev='lm')
lumen.set_global_relative_scale_factor(1, spu.candela * spu.steradian)
#################### ####################
# - Force # - Force
#################### ####################

View File

@ -1,6 +1,134 @@
"""Access `scipy.constants` using `sympy` units.
Notes:
See <https://docs.scipy.org/doc/scipy/reference/constants.html#scipy.constants.physical_constants>
Attributes:
_SCIPY_COMBINED_UNITS: A mapping from scipy constant names to its combined unit, represented as a tuple of "base" units.
SCIPY_UNIT_TO_SYMPY_UNIT: A mapping from scipy constant names to its combined unit, represented as a tuple of "base" units.
SCI_CONSTANTS: A mapping from scipy constant names to an equivalent sympy expression.expression.
SCI_CONSTANTS_REF: Original source of constant data / choices.
"""
import functools
import scipy as sc import scipy as sc
import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
# from . import extra_sympy_units as spux from . import extra_sympy_units as spux
vac_speed_of_light = sc.constants.speed_of_light * spu.meter / spu.second SUPPORTED_SCIPY_PREFIX = '1.12'
if not sc.version.full_version.startswith(SUPPORTED_SCIPY_PREFIX):
msg = f'The active scipy version "{sc.version.full_version}" has not been tested with the "sci_constants.py" module, which only supports scipy version(s prefixed with) "{SUPPORTED_SCIPY_PREFIX}". While the module may otherwise work, constants available in other versions of scipy may not conform to hard-coded unit lookups; as such, we throw an error, to ensure the absence of unfortunate resulting bugs'
raise RuntimeError(msg)
####################
# - Fundamental Constants
####################
vac_speed_of_light = sp.nsimplify(sc.constants.speed_of_light) * spu.meter / spu.second
hartree_energy = (
sp.nsimplify(sc.constants.physical_constants['Hartree energy in eV'][0])
* spu.electronvolt
)
####################
# - Parse scipy.constants
####################
_SCIPY_EXCLUDED_ENTRIES: set[str] = {
'Faraday constant for conventional electric current',
}
_SCIPY_COMBINED_UNITS: dict[str, tuple[str]] = {
name: tuple(cst[1].split(' '))
for name, cst in sc.constants.physical_constants.items()
if cst[1] and name not in _SCIPY_EXCLUDED_ENTRIES
}
## TODO: Better interface to the official Units section? See https://docs.scipy.org/doc/scipy/reference/constants.html#units
SCIPY_UNIT_TO_SYMPY_UNIT = {
#'C_90': spu.coulombs, ## "Conventional electric current"
'H': spu.henry,
'C^4': spu.coulombs**4,
'm^4': spu.meters**4,
'm^-1': 1 / spu.meter,
'J^-3': 1 / spu.joules**3,
's^-2': 1 / spu.second**2,
'kg': spu.kilogram,
'eV': spu.electronvolt,
'GeV': 10**9 * spu.electronvolt,
'S': spu.siemens,
'(GeV/c^2)^-2': (10**9 * spu.electronvolt / vac_speed_of_light**2) ** -2,
'GeV^-2': (10**9 * spu.electronvolt) ** -2,
'E_h': hartree_energy,
'Hz': spu.hertz,
'MeV/c': (10**6 * spu.electronvolt) / vac_speed_of_light,
'Pa': spu.pascal,
'ohm': spu.ohms,
'Wb': spu.weber,
'W^-1': 1 / spu.watt,
'u': spu.atomic_mass_constant,
'm^-3': 1 / spu.meters**3,
'J': spu.joules,
'sr^-1': 1 / spu.steradian,
'T^-1': 1 / spu.tesla,
'C^2': spu.coulombs**2,
'F': spu.farad,
'MeV': 10**6 * spu.electronvolt,
'J^-2': 1 / spu.joules**2,
'mol^-1': 1 / spu.mole,
'kg^-1': 1 / spu.kilogram,
'T^-2': 1 / spu.tesla**2,
'fm': spux.femtometer,
'V^-1': 1 / spu.volt,
'N': spu.newton,
'A': spu.ampere,
's^-1': 1 / spu.second,
'lm': spux.lumen,
'm^3': spu.meters**3,
'm^2': spu.meters**2,
'K^-1': 1 / spu.kelvin,
'm^-2': 1 / spu.meters**2,
'MHz': 10**6 * spu.hertz,
'J^-1': 1 / spu.joules,
'Hz^-1': 1 / spu.hertz,
'm': spu.meter,
'C^3': spu.coulombs**3,
'K': spu.kelvin,
'A^-2': 1 / spu.ampere**2,
'T': spu.tesla,
'K^-4': 1 / spu.kelvin**4,
'W': spu.watt,
'C': spu.coulombs,
's': spu.second,
'V': spu.volt,
}
####################
# - Extract Units
####################
SCI_CONSTANT_UNITS = {
name: functools.reduce(
lambda a, b: a * SCIPY_UNIT_TO_SYMPY_UNIT[b],
combined_unit,
sp.S(1),
)
for name, combined_unit in _SCIPY_COMBINED_UNITS.items()
}
SCI_CONSTANTS_INFO = {
name: {
'units': SCI_CONSTANT_UNITS[name],
'uncertainty': sc.constants.physical_constants[name][2],
'scipy_units': sc.constants.physical_constants[name][1],
}
for name, combined_unit in _SCIPY_COMBINED_UNITS.items()
}
SCI_CONSTANTS_REF = (
'[CODATA2018]',
'CODATA Recommended Values of the Fundamental Physical Constants 2018.',
'https://physics.nist.gov/cuu/Constants/',
)
SCI_CONSTANTS = {
name: sc.constants.physical_constants[name][0] * SCI_CONSTANT_UNITS[name]
for name, combined_unit in _SCIPY_COMBINED_UNITS.items()
}