bindmc 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bindmc-0.1.0/PKG-INFO +22 -0
- bindmc-0.1.0/pyproject.toml +110 -0
- bindmc-0.1.0/setup.cfg +4 -0
- bindmc-0.1.0/src/bindmc/main.py +67 -0
- bindmc-0.1.0/src/bindmc/webgui/__init__.py +0 -0
- bindmc-0.1.0/src/bindmc/webgui/app.py +54 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/BindingConstant.py +23 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/ChemicalShiftParam.py +40 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/Component.py +111 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/ExptData.py +485 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/ExptDataType.py +92 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/FitResult.py +173 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/MCMCSim.py +232 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/Model.py +86 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/RawData.py +36 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/Simulation.py +104 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/UIBindings.py +19 -0
- bindmc-0.1.0/src/bindmc/webgui/classes/__init__.py +28 -0
- bindmc-0.1.0/src/bindmc/webgui/components/__init__.py +29 -0
- bindmc-0.1.0/src/bindmc/webgui/components/base.py +24 -0
- bindmc-0.1.0/src/bindmc/webgui/components/bayes.py +689 -0
- bindmc-0.1.0/src/bindmc/webgui/components/bayes_priors.py +351 -0
- bindmc-0.1.0/src/bindmc/webgui/components/binding_model.py +330 -0
- bindmc-0.1.0/src/bindmc/webgui/components/body.py +276 -0
- bindmc-0.1.0/src/bindmc/webgui/components/data_gen.py +419 -0
- bindmc-0.1.0/src/bindmc/webgui/components/data_import.py +450 -0
- bindmc-0.1.0/src/bindmc/webgui/components/data_model.py +609 -0
- bindmc-0.1.0/src/bindmc/webgui/components/fitting.py +886 -0
- bindmc-0.1.0/src/bindmc/webgui/components/graph.py +649 -0
- bindmc-0.1.0/src/bindmc/webgui/components/header.py +124 -0
- bindmc-0.1.0/src/bindmc/webgui/components/simulation.py +385 -0
- bindmc-0.1.0/src/bindmc/webgui/export/__init__.py +0 -0
- bindmc-0.1.0/src/bindmc/webgui/export/notebook_exporter.py +727 -0
- bindmc-0.1.0/src/bindmc/webgui/state/__init__.py +1 -0
- bindmc-0.1.0/src/bindmc/webgui/state/statemanager.py +2043 -0
- bindmc-0.1.0/src/bindmc/webgui/utils.py +322 -0
- bindmc-0.1.0/src/bindmc.egg-info/PKG-INFO +22 -0
- bindmc-0.1.0/src/bindmc.egg-info/SOURCES.txt +56 -0
- bindmc-0.1.0/src/bindmc.egg-info/dependency_links.txt +1 -0
- bindmc-0.1.0/src/bindmc.egg-info/requires.txt +18 -0
- bindmc-0.1.0/src/bindmc.egg-info/top_level.txt +1 -0
- bindmc-0.1.0/tests/test_active_context_state_manager.py +245 -0
- bindmc-0.1.0/tests/test_analytical_fast_exchange_backend.py +203 -0
- bindmc-0.1.0/tests/test_bayes_plot_sizing.py +31 -0
- bindmc-0.1.0/tests/test_body_tab_guidance.py +101 -0
- bindmc-0.1.0/tests/test_component_concentration.py +87 -0
- bindmc-0.1.0/tests/test_data_model_analytical_defaults.py +19 -0
- bindmc-0.1.0/tests/test_exptdata_shift_param_naming.py +104 -0
- bindmc-0.1.0/tests/test_fit_analytical_fast_exchange_screen.py +167 -0
- bindmc-0.1.0/tests/test_fit_graph_subset_dep_vars.py +137 -0
- bindmc-0.1.0/tests/test_model_setups.py +165 -0
- bindmc-0.1.0/tests/test_model_to_python.py +22 -0
- bindmc-0.1.0/tests/test_notebook_export.py +548 -0
- bindmc-0.1.0/tests/test_sim_flow.py +59 -0
- bindmc-0.1.0/tests/test_simulation_workflow_screen.py +144 -0
- bindmc-0.1.0/tests/test_startup.py +17 -0
- bindmc-0.1.0/tests/test_uvvis_fluorescence.py +520 -0
- bindmc-0.1.0/tests/testutils.py +15 -0
bindmc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bindmc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Requires-Python: >=3.12
|
|
5
|
+
Requires-Dist: arviz==0.21.0
|
|
6
|
+
Requires-Dist: bindtools>=0.1.0
|
|
7
|
+
Requires-Dist: corner==2.2.3
|
|
8
|
+
Requires-Dist: emcee==3.1.6
|
|
9
|
+
Requires-Dist: h5py==3.13.0
|
|
10
|
+
Requires-Dist: latex2mathml>=3.0.0
|
|
11
|
+
Requires-Dist: lmfit==1.3.3
|
|
12
|
+
Requires-Dist: matplotlib==3.10.7
|
|
13
|
+
Requires-Dist: nicegui[plotly]==3.3.1
|
|
14
|
+
Requires-Dist: numba>=0.65.1
|
|
15
|
+
Requires-Dist: numpy>=2.3.5
|
|
16
|
+
Requires-Dist: openpyxl==3.1.2
|
|
17
|
+
Requires-Dist: pandas>=2.3.3
|
|
18
|
+
Requires-Dist: platformdirs>=3.0.0
|
|
19
|
+
Requires-Dist: pywebview>=5
|
|
20
|
+
Requires-Dist: scipy==1.16.3
|
|
21
|
+
Requires-Dist: tqdm==4.66.3
|
|
22
|
+
Requires-Dist: uncertainties==3.2.3
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "bindmc"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
requires-python = ">=3.12"
|
|
5
|
+
dependencies = [
|
|
6
|
+
"arviz==0.21.0",
|
|
7
|
+
"bindtools>=0.1.0",
|
|
8
|
+
"corner==2.2.3",
|
|
9
|
+
"emcee==3.1.6",
|
|
10
|
+
"h5py==3.13.0",
|
|
11
|
+
"latex2mathml>=3.0.0",
|
|
12
|
+
"lmfit==1.3.3",
|
|
13
|
+
"matplotlib==3.10.7",
|
|
14
|
+
"nicegui[plotly]==3.3.1",
|
|
15
|
+
"numba>=0.65.1",
|
|
16
|
+
"numpy>=2.3.5",
|
|
17
|
+
"openpyxl==3.1.2",
|
|
18
|
+
"pandas>=2.3.3",
|
|
19
|
+
"platformdirs>=3.0.0",
|
|
20
|
+
"pywebview>=5",
|
|
21
|
+
"scipy==1.16.3",
|
|
22
|
+
"tqdm==4.66.3",
|
|
23
|
+
"uncertainties==3.2.3",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[tool.uv.sources]
|
|
27
|
+
bindtools = { path = "./bindtools", editable = true }
|
|
28
|
+
|
|
29
|
+
[tool.ruff]
|
|
30
|
+
# Exclude a variety of commonly ignored directories.
|
|
31
|
+
exclude = [
|
|
32
|
+
".bzr",
|
|
33
|
+
".direnv",
|
|
34
|
+
".eggs",
|
|
35
|
+
".git",
|
|
36
|
+
".git-rewrite",
|
|
37
|
+
".hg",
|
|
38
|
+
".ipynb_checkpoints",
|
|
39
|
+
".mypy_cache",
|
|
40
|
+
".nox",
|
|
41
|
+
".pants.d",
|
|
42
|
+
".pyenv",
|
|
43
|
+
".pytest_cache",
|
|
44
|
+
".pytype",
|
|
45
|
+
".ruff_cache",
|
|
46
|
+
".svn",
|
|
47
|
+
".tox",
|
|
48
|
+
".venv",
|
|
49
|
+
".vscode",
|
|
50
|
+
"__pypackages__",
|
|
51
|
+
"_build",
|
|
52
|
+
"buck-out",
|
|
53
|
+
"build",
|
|
54
|
+
"dist",
|
|
55
|
+
"node_modules",
|
|
56
|
+
"site-packages",
|
|
57
|
+
"venv",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
# Same as Black.
|
|
61
|
+
line-length = 120
|
|
62
|
+
indent-width = 4
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
67
|
+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
|
68
|
+
# McCabe complexity (`C901`) by default.
|
|
69
|
+
select = ["E4", "E7", "E9", "F"]
|
|
70
|
+
ignore = []
|
|
71
|
+
|
|
72
|
+
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
73
|
+
fixable = ["ALL"]
|
|
74
|
+
unfixable = []
|
|
75
|
+
|
|
76
|
+
# Allow unused variables when underscore-prefixed.
|
|
77
|
+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
78
|
+
|
|
79
|
+
[tool.ruff.format]
|
|
80
|
+
# Like Black, use double quotes for strings.
|
|
81
|
+
quote-style = "double"
|
|
82
|
+
|
|
83
|
+
# Like Black, indent with spaces, rather than tabs.
|
|
84
|
+
indent-style = "space"
|
|
85
|
+
|
|
86
|
+
# Like Black, respect magic trailing commas.
|
|
87
|
+
skip-magic-trailing-comma = false
|
|
88
|
+
|
|
89
|
+
# Like Black, automatically detect the appropriate line ending.
|
|
90
|
+
line-ending = "auto"
|
|
91
|
+
|
|
92
|
+
# Enable auto-formatting of code examples in docstrings. Markdown,
|
|
93
|
+
# reStructuredText code/literal blocks and doctests are all supported.
|
|
94
|
+
#
|
|
95
|
+
# This is currently disabled by default, but it is planned for this
|
|
96
|
+
# to be opt-out in the future.
|
|
97
|
+
docstring-code-format = false
|
|
98
|
+
|
|
99
|
+
# Set the line length limit used when formatting code snippets in
|
|
100
|
+
# docstrings.
|
|
101
|
+
#
|
|
102
|
+
# This only has an effect when the `docstring-code-format` setting is
|
|
103
|
+
# enabled.
|
|
104
|
+
docstring-code-line-length = "dynamic"
|
|
105
|
+
|
|
106
|
+
[dependency-groups]
|
|
107
|
+
dev = [
|
|
108
|
+
"pyinstaller>=6.21.0",
|
|
109
|
+
"pytest>=9.1.0",
|
|
110
|
+
]
|
bindmc-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#to build: rm -fr build && rm -fr dist && nicegui-pack --onefile --add-data "C:\Users\mpeeks\miniforge3\envs\binding-nicegui\Lib\site-packages\arviz\static;arviz/static" --add-data "C:\Users\mpeeks\miniforge3\envs\binding-nicegui\Lib\site-packages\arviz\data;arviz/data" --add-data "C:\Users\mpeeks\miniforge3\envs\binding-nicegui\Lib\site-packages\latex2mathml\unimathsymbols.txt;latex2mathml" main.py
|
|
2
|
+
|
|
3
|
+
# on windows:
|
|
4
|
+
# nicegui-pack main.py --no-build
|
|
5
|
+
|
|
6
|
+
# macOS packaging support
|
|
7
|
+
from multiprocessing import freeze_support # noqa
|
|
8
|
+
freeze_support() # noqa
|
|
9
|
+
|
|
10
|
+
# hidden import for pyinstaller
|
|
11
|
+
import matplotlib
|
|
12
|
+
matplotlib.use('module://matplotlib.backends.backend_svg')
|
|
13
|
+
import sys
|
|
14
|
+
from nicegui import native,ui,app
|
|
15
|
+
from webgui.app import BindToolsServer
|
|
16
|
+
import logging
|
|
17
|
+
import nicegui
|
|
18
|
+
from packaging.version import InvalidVersion, Version
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from platformdirs import user_data_dir
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
nicegui_version = Version(nicegui.__version__)
|
|
26
|
+
except InvalidVersion:
|
|
27
|
+
logger.warning(
|
|
28
|
+
"Could not parse NiceGUI version '%s'; continuing, but NiceGUI >= 3 is required.",
|
|
29
|
+
nicegui.__version__,
|
|
30
|
+
)
|
|
31
|
+
else:
|
|
32
|
+
if nicegui_version.is_prerelease or nicegui_version.is_devrelease:
|
|
33
|
+
logger.warning(
|
|
34
|
+
"NiceGUI %s is a pre-release/dev build; recommended to install the latest stable 3.x.",
|
|
35
|
+
nicegui.__version__,
|
|
36
|
+
)
|
|
37
|
+
elif nicegui_version.major < 3:
|
|
38
|
+
raise RuntimeError(f"NiceGUI >= 3 is required; found {nicegui.__version__}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
app.native.settings['ALLOW_DOWNLOADS'] = True
|
|
42
|
+
|
|
43
|
+
#logging.basicConfig(level=logging.INFO, filename='bindtools.log')
|
|
44
|
+
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(message)s')
|
|
45
|
+
|
|
46
|
+
logger.info("Starting BindTools NiceGUI server...")
|
|
47
|
+
BindToolsServer()
|
|
48
|
+
|
|
49
|
+
# Set DEV based on whether running from PyInstaller bundle
|
|
50
|
+
DEV = not getattr(sys, 'frozen', False)
|
|
51
|
+
|
|
52
|
+
if DEV:
|
|
53
|
+
native_mode=False
|
|
54
|
+
reload=True
|
|
55
|
+
else:
|
|
56
|
+
native_mode=True
|
|
57
|
+
reload=False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# make a sensible storage path for native mode
|
|
61
|
+
storage_path = Path(user_data_dir(appname="BindTools", appauthor=False))
|
|
62
|
+
storage_path.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
|
|
64
|
+
# Redirect native window persistence data away from default paths
|
|
65
|
+
app.native.start_args['storage_path'] = str(storage_path)
|
|
66
|
+
|
|
67
|
+
ui.run(title='BindTools', reload=reload, native=native_mode, port=native.find_open_port(), storage_secret='bindtools_secret')
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from nicegui import ui
|
|
2
|
+
|
|
3
|
+
from .components import (
|
|
4
|
+
BindToolsHeader,
|
|
5
|
+
Body
|
|
6
|
+
)
|
|
7
|
+
from .state.statemanager import StateManager
|
|
8
|
+
|
|
9
|
+
class BindToolsServer:
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
|
|
13
|
+
self.state_manager: StateManager = StateManager(load_prior_state=False) # Initialize state_manager attribute
|
|
14
|
+
self.sm: StateManager =self.state_manager
|
|
15
|
+
self.components = {}
|
|
16
|
+
self.body_components={}
|
|
17
|
+
self.tabs = {}
|
|
18
|
+
self.setup_routes()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def setup_routes(self):
|
|
22
|
+
"""Set up the application routes and UI components."""
|
|
23
|
+
|
|
24
|
+
@ui.page("/")
|
|
25
|
+
|
|
26
|
+
def index():
|
|
27
|
+
self.state_manager = StateManager()
|
|
28
|
+
self.sm = self.state_manager # alias
|
|
29
|
+
self._generate_header()
|
|
30
|
+
self._generate_body()
|
|
31
|
+
self.body_components = self.components["body"].components
|
|
32
|
+
self._load_prior_state()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _load_prior_state(self):
|
|
37
|
+
# if simulations have been run already, populate the graph
|
|
38
|
+
if len(self.sm.simulations) > 0:
|
|
39
|
+
self.body_components["simulation"].graph.load_simulations_data()
|
|
40
|
+
if self.sm.active_expt_data_id is not None and self.sm.active_expt_data.data is not None and not self.sm.active_expt_data.data.empty:
|
|
41
|
+
self.sm.notify_listeners("data_imported")
|
|
42
|
+
if len(self.sm.fits) > 0:
|
|
43
|
+
self.sm.notify_listeners("fits_loaded")
|
|
44
|
+
# self.components["fit_results"].sync_graphs()
|
|
45
|
+
# self.components["fit_results"].generate_delete_fit_dropdown()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _generate_header(self):
|
|
49
|
+
ui.colors(primary='#000000', secondary='grey-5', accent='blue-grey-5')
|
|
50
|
+
|
|
51
|
+
self.components["header"] = BindToolsHeader(state_manager=self.state_manager)
|
|
52
|
+
|
|
53
|
+
def _generate_body(self):
|
|
54
|
+
self.components["body"]= Body(self.state_manager)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from dataclasses import asdict, dataclass, field, InitVar
|
|
3
|
+
from typing import Optional,Any
|
|
4
|
+
import unicodedata
|
|
5
|
+
from nicegui import binding
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from lmfit import Parameter as LMFitParameter
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class BindingConstant:
|
|
12
|
+
species: str = ""
|
|
13
|
+
logK: Optional[float] = None
|
|
14
|
+
vary: bool = False
|
|
15
|
+
isComp: bool = False
|
|
16
|
+
min: Optional[float] = None
|
|
17
|
+
max: Optional[float] = None
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def name(self) -> str:
|
|
21
|
+
return self.species
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import uuid
|
|
3
|
+
from dataclasses import asdict, dataclass, field, InitVar
|
|
4
|
+
from typing import Optional,Any
|
|
5
|
+
import unicodedata
|
|
6
|
+
from nicegui import binding
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from lmfit import Parameter as LMFitParameter
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ChemicalShiftParam():
|
|
13
|
+
"""Data class to represent a chemical shift parameter."""
|
|
14
|
+
species: str = ""
|
|
15
|
+
col: str | None = None # raw_data column this resonance/shift belongs to (fast-exchange)
|
|
16
|
+
value: float | None = None
|
|
17
|
+
fixed: bool = False # Whether the parameter is fixed or not
|
|
18
|
+
_min: float |None = None # Minimum value for the parameter
|
|
19
|
+
_max: float |None = None # Maximum value for the parameter
|
|
20
|
+
|
|
21
|
+
init_min: InitVar[Optional[float]] = None # Initial minimum value for the parameter
|
|
22
|
+
init_max: InitVar[Optional[float]] = None # Initial maximum value for
|
|
23
|
+
|
|
24
|
+
def __post_init__(self, init_min: Optional[float] = None, init_max: Optional[float] = None) -> None:
|
|
25
|
+
# if not self.fixed and not isinstance(init_min,float):
|
|
26
|
+
# raise ValueError("Variable parameters must have a valid initial minimum value.")
|
|
27
|
+
# if not self.fixed and not isinstance(init_max,float):
|
|
28
|
+
# raise ValueError("Variable parameters must have a valid initial maximum value.")
|
|
29
|
+
if init_min is not None and init_max is not None and self.value is not None:
|
|
30
|
+
if init_min >= init_max or init_min>self.value or init_max<self.value:
|
|
31
|
+
raise ValueError("Initial min and max values must be valid and lesser/greater than the value.")
|
|
32
|
+
if init_min is None and self._min is not None:
|
|
33
|
+
return # No need to set if already defined
|
|
34
|
+
if init_max is None and self._max is not None:
|
|
35
|
+
return # No need to set if already defined
|
|
36
|
+
self._min = init_min
|
|
37
|
+
self._max = init_max
|
|
38
|
+
# else:
|
|
39
|
+
# raise ValueError("Initial min and max values must be provided for variable parameters.")
|
|
40
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from dataclasses import asdict, dataclass, field, InitVar
|
|
3
|
+
from typing import Optional,Any
|
|
4
|
+
import unicodedata
|
|
5
|
+
from nicegui import binding
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from lmfit import Parameter as LMFitParameter
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Component:
|
|
12
|
+
name: str = ""
|
|
13
|
+
start_conc: float|None = 0
|
|
14
|
+
end_conc: float|None = 0
|
|
15
|
+
constant: bool = False
|
|
16
|
+
_start_units: str = "mM"
|
|
17
|
+
_end_units: str = "mM"
|
|
18
|
+
spacing: str = "lin" # 'lin' or 'log', default is linear spacing
|
|
19
|
+
|
|
20
|
+
UNIT_CONVERSIONS = {
|
|
21
|
+
"M": 1,
|
|
22
|
+
"mM": 1e3,
|
|
23
|
+
"μM": 1e6,
|
|
24
|
+
"µM": 1e6,
|
|
25
|
+
"uM": 1e6, # support ASCII 'u'
|
|
26
|
+
"nM": 1e9,
|
|
27
|
+
"pM": 1e12,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def start_conc_nice(self) -> float | None:
|
|
32
|
+
"""Get start concentration in the specified units (user-friendly)."""
|
|
33
|
+
factor = self.UNIT_CONVERSIONS.get(self.start_units, 1)
|
|
34
|
+
return self.start_conc * factor if self.start_conc is not None else None
|
|
35
|
+
|
|
36
|
+
@start_conc_nice.setter
|
|
37
|
+
def start_conc_nice(self, value: float):
|
|
38
|
+
"""Set start concentration from user-friendly units to base units (M)."""
|
|
39
|
+
factor = self.UNIT_CONVERSIONS.get(self.start_units, 1)
|
|
40
|
+
self.start_conc = value / factor if value is not None else None
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def end_conc_nice(self) -> float|None:
|
|
44
|
+
"""Get end concentration in the specified units (user-friendly)."""
|
|
45
|
+
factor = self.UNIT_CONVERSIONS.get(self.end_units, 1)
|
|
46
|
+
return self.end_conc * factor if self.end_conc is not None else None
|
|
47
|
+
|
|
48
|
+
@end_conc_nice.setter
|
|
49
|
+
def end_conc_nice(self, value: float):
|
|
50
|
+
"""Set end concentration from user-friendly units to base units (M)."""
|
|
51
|
+
factor = self.UNIT_CONVERSIONS.get(self.end_units, 1)
|
|
52
|
+
self.end_conc = value / factor if value is not None else None
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def start_units(self) -> str:
|
|
56
|
+
"""Get the start concentration units."""
|
|
57
|
+
return self._start_units
|
|
58
|
+
|
|
59
|
+
@start_units.setter
|
|
60
|
+
def start_units(self, value: str):
|
|
61
|
+
"""Set the start concentration units and recalculate start_conc."""
|
|
62
|
+
oldunits = self._start_units
|
|
63
|
+
convfactor = self.UNIT_CONVERSIONS.get(value, 1) / self.UNIT_CONVERSIONS.get(
|
|
64
|
+
oldunits, 1
|
|
65
|
+
)
|
|
66
|
+
self._start_units = value
|
|
67
|
+
|
|
68
|
+
self.start_conc = (
|
|
69
|
+
self.start_conc / convfactor if self.start_conc is not None else None
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def end_units(self) -> str:
|
|
74
|
+
"""Get the end concentration units."""
|
|
75
|
+
return self._end_units
|
|
76
|
+
|
|
77
|
+
@end_units.setter
|
|
78
|
+
def end_units(self, value: str):
|
|
79
|
+
"""Set the end concentration units and recalculate end."""
|
|
80
|
+
oldunits = self._end_units
|
|
81
|
+
convfactor = self.UNIT_CONVERSIONS.get(value, 1) / self.UNIT_CONVERSIONS.get(
|
|
82
|
+
oldunits, 1
|
|
83
|
+
)
|
|
84
|
+
self._end_units = value
|
|
85
|
+
|
|
86
|
+
self.end_conc = (
|
|
87
|
+
self.end_conc / convfactor if self.end_conc is not None else None
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# """Class to represent a component in the simulation."""
|
|
91
|
+
# def __init__(self, name, start_conc=None, end_conc=None, constant=False, start_unit='mM', end_unit='mM'):
|
|
92
|
+
|
|
93
|
+
# self.name = name
|
|
94
|
+
# self.start_conc = start_conc
|
|
95
|
+
# self.end_conc = end_conc
|
|
96
|
+
# self.constant = constant
|
|
97
|
+
# self.start_unit = start_unit
|
|
98
|
+
# self.end_unit = end_unit
|
|
99
|
+
|
|
100
|
+
# def to_dict(self):
|
|
101
|
+
# """Convert Component to a dictionary."""
|
|
102
|
+
# return {
|
|
103
|
+
# 'name': self.name,
|
|
104
|
+
# 'start_conc': self.start_conc,
|
|
105
|
+
# 'end_conc': self.end_conc,
|
|
106
|
+
# 'constant': self.constant,
|
|
107
|
+
# 'start_unit': self.start_unit,
|
|
108
|
+
# 'end_unit': self.end_unit
|
|
109
|
+
# }
|
|
110
|
+
|
|
111
|
+
|