steer-core 0.1.26__py3-none-any.whl → 0.1.27__py3-none-any.whl
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.
- steer_core/{Apps → ContextManagers}/ContextManagers.py +1 -1
- steer_core/Data/database.db +0 -0
- steer_core/DataManager.py +1 -0
- steer_core/__init__.py +10 -1
- {steer_core-0.1.26.dist-info → steer_core-0.1.27.dist-info}/METADATA +1 -1
- steer_core-0.1.27.dist-info/RECORD +26 -0
- steer_core/Apps/Callbacks/ConfigInteractions.py +0 -377
- steer_core/Apps/Callbacks/StyleManagement.py +0 -60
- steer_core/Apps/Components/MaterialSelectors.py +0 -836
- steer_core/Apps/Components/RangeSliderComponents.py +0 -552
- steer_core/Apps/Components/SliderComponents.py +0 -693
- steer_core/Apps/Components/__init__.py +0 -0
- steer_core/Apps/Performance/CallbackTimer.py +0 -18
- steer_core/Apps/Performance/__init__.py +0 -0
- steer_core/Apps/Utils/SliderControls.py +0 -728
- steer_core/Apps/Utils/__init__.py +0 -0
- steer_core/Apps/__init__.py +0 -0
- steer_core-0.1.26.dist-info/RECORD +0 -37
- {steer_core-0.1.26.dist-info → steer_core-0.1.27.dist-info}/WHEEL +0 -0
- {steer_core-0.1.26.dist-info → steer_core-0.1.27.dist-info}/top_level.txt +0 -0
|
@@ -1,836 +0,0 @@
|
|
|
1
|
-
import dash as ds
|
|
2
|
-
from dash import Input, Output, dcc, html
|
|
3
|
-
from typing import Union, List, Dict
|
|
4
|
-
from .SliderComponents import SliderWithTextInput
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class MaterialSelector:
|
|
8
|
-
"""
|
|
9
|
-
A custom Dash component that combines material selection controls in a horizontal layout.
|
|
10
|
-
|
|
11
|
-
This component creates a horizontal row containing:
|
|
12
|
-
- A dropdown menu for selecting material names
|
|
13
|
-
- An input box for specifying weight fraction of the material
|
|
14
|
-
- Two SliderWithTextInput components for specific cost and density
|
|
15
|
-
|
|
16
|
-
The component is designed for material composition interfaces where users need to
|
|
17
|
-
select materials and specify their properties and proportions.
|
|
18
|
-
|
|
19
|
-
Attributes:
|
|
20
|
-
id_base (dict): Base identifier dictionary used to construct unique IDs for child components
|
|
21
|
-
material_options (List[Dict]): List of material options for the dropdown
|
|
22
|
-
default_material (str): Default selected material name
|
|
23
|
-
default_weight_fraction (float): Default weight fraction value
|
|
24
|
-
cost_config (dict): Configuration for the cost slider (min_val, max_val, step, etc.)
|
|
25
|
-
density_config (dict): Configuration for the density slider (min_val, max_val, step, etc.)
|
|
26
|
-
property_name (str): Property identifier used in component ID construction
|
|
27
|
-
title (str): Display title for the component
|
|
28
|
-
slider_disable (bool): Whether the sliders should be disabled
|
|
29
|
-
dropdown_id (dict): Computed ID for the dropdown component
|
|
30
|
-
weight_fraction_id (dict): Computed ID for the weight fraction input
|
|
31
|
-
cost_slider (SliderWithTextInput): Cost slider component
|
|
32
|
-
density_slider (SliderWithTextInput): Density slider component
|
|
33
|
-
|
|
34
|
-
Example:
|
|
35
|
-
>>> material_selector = MaterialSelector(
|
|
36
|
-
... id_base={'type': 'material', 'index': 0},
|
|
37
|
-
... material_options=[
|
|
38
|
-
... {'label': 'Aluminum', 'value': 'aluminum'},
|
|
39
|
-
... {'label': 'Steel', 'value': 'steel'},
|
|
40
|
-
... {'label': 'Carbon Fiber', 'value': 'carbon_fiber'}
|
|
41
|
-
... ],
|
|
42
|
-
... default_material='aluminum',
|
|
43
|
-
... default_weight_fraction=0.5,
|
|
44
|
-
... cost_config={
|
|
45
|
-
... 'min_val': 0.0,
|
|
46
|
-
... 'max_val': 100.0,
|
|
47
|
-
... 'step': 0.1,
|
|
48
|
-
... 'mark_interval': 10.0,
|
|
49
|
-
... 'default_val': 25.0,
|
|
50
|
-
... 'title': 'Cost ($/kg)'
|
|
51
|
-
... },
|
|
52
|
-
... density_config={
|
|
53
|
-
... 'min_val': 0.0,
|
|
54
|
-
... 'max_val': 10.0,
|
|
55
|
-
... 'step': 0.01,
|
|
56
|
-
... 'mark_interval': 1.0,
|
|
57
|
-
... 'default_val': 2.7,
|
|
58
|
-
... 'title': 'Density (g/cm³)'
|
|
59
|
-
... },
|
|
60
|
-
... property_name='material_1',
|
|
61
|
-
... title='Material 1'
|
|
62
|
-
... )
|
|
63
|
-
>>> layout_element = material_selector() # Returns Dash HTML Div component
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
def __init__(
|
|
67
|
-
self,
|
|
68
|
-
id_base: dict,
|
|
69
|
-
material_options: List[Dict] = None,
|
|
70
|
-
slider_configs: dict = None,
|
|
71
|
-
cost_config: dict = None,
|
|
72
|
-
density_config: dict = None,
|
|
73
|
-
title: str = "Material",
|
|
74
|
-
default_material: str = None,
|
|
75
|
-
default_weight_percent: float = 0,
|
|
76
|
-
slider_disable: bool = False,
|
|
77
|
-
div_width: str = "100%",
|
|
78
|
-
hidden: bool = False,
|
|
79
|
-
):
|
|
80
|
-
"""
|
|
81
|
-
Initialize the MaterialSelector component.
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
id_base (dict): Base dictionary for generating component IDs.
|
|
85
|
-
material_options (List[Dict], optional): List of material options for dropdown.
|
|
86
|
-
Each dict should have 'label' and 'value' keys.
|
|
87
|
-
If None, defaults to empty list (no options).
|
|
88
|
-
slider_configs (dict, optional): Output from create_slider_config with arrays where:
|
|
89
|
-
- Index 0 = density configuration
|
|
90
|
-
- Index 1 = cost configuration
|
|
91
|
-
If provided, cost_config and density_config are ignored.
|
|
92
|
-
If empty dictionary {}, uses sensible defaults with preset values.
|
|
93
|
-
If None (default), uses sensible defaults with no initial values.
|
|
94
|
-
cost_config (dict, optional): Legacy cost slider configuration. Ignored if slider_config provided.
|
|
95
|
-
density_config (dict, optional): Legacy density slider configuration. Ignored if slider_config provided.
|
|
96
|
-
title (str, optional): Title displayed for the component. Defaults to "Material".
|
|
97
|
-
default_material (str, optional): Default selected material. If None, no material
|
|
98
|
-
will be initially selected in the dropdown.
|
|
99
|
-
default_weight_percent (float, optional): Default weight percentage (0.0-100.0). Defaults to 100.0.
|
|
100
|
-
slider_disable (bool, optional): Whether to disable sliders. Defaults to False.
|
|
101
|
-
div_width (str, optional): CSS width specification for the container div.
|
|
102
|
-
Defaults to '100%'.
|
|
103
|
-
hidden (bool, optional): Whether to hide the entire component. When True,
|
|
104
|
-
the component will be rendered with display: none.
|
|
105
|
-
Defaults to False.
|
|
106
|
-
"""
|
|
107
|
-
self.id_base = id_base
|
|
108
|
-
self.material_options = material_options or [] # Default to empty list if None
|
|
109
|
-
self.default_material = default_material # Keep as None if not provided
|
|
110
|
-
self.default_weight_percent = default_weight_percent
|
|
111
|
-
self.cost_config = cost_config
|
|
112
|
-
self.density_config = density_config
|
|
113
|
-
self.title = title
|
|
114
|
-
self.slider_disable = slider_disable
|
|
115
|
-
self.div_width = div_width
|
|
116
|
-
self.hidden = hidden
|
|
117
|
-
|
|
118
|
-
# Generate component IDs
|
|
119
|
-
self.dropdown_id = self._make_id("dropdown")
|
|
120
|
-
self.weight_fraction_id = self._make_id("weight_fraction")
|
|
121
|
-
|
|
122
|
-
# Normalize configurations to handle both legacy and create_slider_config formats
|
|
123
|
-
if slider_configs is not None and slider_configs: # Check if not empty
|
|
124
|
-
# Use slider_configs arrays (index 0 = density, index 1 = cost)
|
|
125
|
-
density_normalized = self._normalize_config_from_arrays(
|
|
126
|
-
slider_configs, 0, "Density (g/cm³)"
|
|
127
|
-
)
|
|
128
|
-
cost_normalized = self._normalize_config_from_arrays(
|
|
129
|
-
slider_configs, 1, "Specific Cost ($/kg)"
|
|
130
|
-
)
|
|
131
|
-
elif slider_configs is not None and not slider_configs: # Empty dictionary
|
|
132
|
-
# Use sensible defaults for materials
|
|
133
|
-
density_normalized = {
|
|
134
|
-
"min_val": 0.0,
|
|
135
|
-
"max_val": 0.1,
|
|
136
|
-
"step": 0.01,
|
|
137
|
-
"mark_interval": 0.1,
|
|
138
|
-
"title": "Density (g/cm³)",
|
|
139
|
-
"default_val": 0.05,
|
|
140
|
-
}
|
|
141
|
-
cost_normalized = {
|
|
142
|
-
"min_val": 0.0,
|
|
143
|
-
"max_val": 0.1,
|
|
144
|
-
"step": 0.01,
|
|
145
|
-
"mark_interval": 0.1,
|
|
146
|
-
"title": "Specific Cost ($/kg)",
|
|
147
|
-
"default_val": 0.05,
|
|
148
|
-
}
|
|
149
|
-
else:
|
|
150
|
-
# slider_configs is None - use sensible defaults with None values
|
|
151
|
-
density_normalized = {
|
|
152
|
-
"min_val": 0.0,
|
|
153
|
-
"max_val": 0.1,
|
|
154
|
-
"step": 0.01,
|
|
155
|
-
"mark_interval": 0.1,
|
|
156
|
-
"title": "Density (g/cm³)",
|
|
157
|
-
"default_val": None, # No initial value
|
|
158
|
-
}
|
|
159
|
-
cost_normalized = {
|
|
160
|
-
"min_val": 0.0,
|
|
161
|
-
"max_val": 0.1,
|
|
162
|
-
"step": 0.01,
|
|
163
|
-
"mark_interval": 0.1,
|
|
164
|
-
"title": "Specific Cost ($/kg)",
|
|
165
|
-
"default_val": None, # No initial value
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
# Create slider components
|
|
169
|
-
cost_slider_kwargs = {
|
|
170
|
-
"id_base": id_base,
|
|
171
|
-
"min_val": cost_normalized["min_val"],
|
|
172
|
-
"max_val": cost_normalized["max_val"],
|
|
173
|
-
"step": cost_normalized["step"],
|
|
174
|
-
"mark_interval": cost_normalized["mark_interval"],
|
|
175
|
-
"property_name": "specific_cost",
|
|
176
|
-
"title": cost_normalized["title"],
|
|
177
|
-
"default_val": cost_normalized.get(
|
|
178
|
-
"default_val", cost_normalized["min_val"]
|
|
179
|
-
),
|
|
180
|
-
"with_slider_titles": True,
|
|
181
|
-
"slider_disable": slider_disable,
|
|
182
|
-
"div_width": "100%",
|
|
183
|
-
}
|
|
184
|
-
# Add marks if available
|
|
185
|
-
if "marks" in cost_normalized:
|
|
186
|
-
cost_slider_kwargs["marks"] = cost_normalized["marks"]
|
|
187
|
-
|
|
188
|
-
self.cost_slider = SliderWithTextInput(**cost_slider_kwargs)
|
|
189
|
-
|
|
190
|
-
density_slider_kwargs = {
|
|
191
|
-
"id_base": id_base,
|
|
192
|
-
"min_val": density_normalized["min_val"],
|
|
193
|
-
"max_val": density_normalized["max_val"],
|
|
194
|
-
"step": density_normalized["step"],
|
|
195
|
-
"mark_interval": density_normalized["mark_interval"],
|
|
196
|
-
"property_name": "density",
|
|
197
|
-
"title": density_normalized["title"],
|
|
198
|
-
"default_val": density_normalized.get(
|
|
199
|
-
"default_val", density_normalized["min_val"]
|
|
200
|
-
),
|
|
201
|
-
"with_slider_titles": True,
|
|
202
|
-
"slider_disable": slider_disable,
|
|
203
|
-
"div_width": "100%",
|
|
204
|
-
}
|
|
205
|
-
# Add marks if available
|
|
206
|
-
if "marks" in density_normalized:
|
|
207
|
-
density_slider_kwargs["marks"] = density_normalized["marks"]
|
|
208
|
-
|
|
209
|
-
self.density_slider = SliderWithTextInput(**density_slider_kwargs)
|
|
210
|
-
|
|
211
|
-
def _normalize_config_from_arrays(
|
|
212
|
-
self, config: dict, index: int, default_title: str
|
|
213
|
-
) -> dict:
|
|
214
|
-
"""
|
|
215
|
-
Normalize slider configuration from create_slider_config output arrays.
|
|
216
|
-
|
|
217
|
-
Args:
|
|
218
|
-
config (dict): create_slider_config output with array values
|
|
219
|
-
index (int): Index to extract from each array (0 = density, 1 = cost)
|
|
220
|
-
default_title (str): Default title if not provided
|
|
221
|
-
|
|
222
|
-
Returns:
|
|
223
|
-
dict: Normalized configuration with min_val, max_val, step, mark_interval, title keys
|
|
224
|
-
"""
|
|
225
|
-
normalized = {
|
|
226
|
-
"min_val": config["min_vals"][index],
|
|
227
|
-
"max_val": config["max_vals"][index],
|
|
228
|
-
"step": config["step_vals"][index],
|
|
229
|
-
"title": default_title, # Use provided title
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
# Get pre-computed marks if available, otherwise calculate mark_interval
|
|
233
|
-
if "mark_vals" in config and len(config["mark_vals"]) > index:
|
|
234
|
-
marks = config["mark_vals"][index]
|
|
235
|
-
normalized["marks"] = marks # Pass the actual marks dictionary
|
|
236
|
-
if len(marks) >= 2:
|
|
237
|
-
# Calculate interval from first two marks
|
|
238
|
-
mark_positions = sorted(marks.keys())
|
|
239
|
-
normalized["mark_interval"] = mark_positions[1] - mark_positions[0]
|
|
240
|
-
else:
|
|
241
|
-
# Fallback: use step as mark interval
|
|
242
|
-
normalized["mark_interval"] = normalized["step"]
|
|
243
|
-
else:
|
|
244
|
-
normalized["mark_interval"] = normalized["step"]
|
|
245
|
-
|
|
246
|
-
# Add default value if available from grid values
|
|
247
|
-
if "grid_slider_vals" in config and len(config["grid_slider_vals"]) > index:
|
|
248
|
-
normalized["default_val"] = config["grid_slider_vals"][index]
|
|
249
|
-
else:
|
|
250
|
-
normalized["default_val"] = normalized["min_val"]
|
|
251
|
-
|
|
252
|
-
return normalized
|
|
253
|
-
|
|
254
|
-
def _normalize_config(self, config: dict, config_type: str) -> dict:
|
|
255
|
-
"""
|
|
256
|
-
Normalize slider configuration to handle both legacy and create_slider_config formats.
|
|
257
|
-
|
|
258
|
-
Args:
|
|
259
|
-
config (dict): Either legacy format or create_slider_config output
|
|
260
|
-
config_type (str): Type identifier ('cost' or 'density') for default title
|
|
261
|
-
|
|
262
|
-
Returns:
|
|
263
|
-
dict: Normalized configuration with min_val, max_val, step, mark_interval, title keys
|
|
264
|
-
"""
|
|
265
|
-
# Check if this is a create_slider_config output (has min_vals, max_vals, etc.)
|
|
266
|
-
if "min_vals" in config and "max_vals" in config:
|
|
267
|
-
# This is create_slider_config output - use first element from arrays
|
|
268
|
-
normalized = {
|
|
269
|
-
"min_val": config["min_vals"][0],
|
|
270
|
-
"max_val": config["max_vals"][0],
|
|
271
|
-
"step": config["step_vals"][0],
|
|
272
|
-
"title": config_type.capitalize(), # Default title
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
# Get pre-computed marks if available, otherwise calculate mark_interval
|
|
276
|
-
if "mark_vals" in config and len(config["mark_vals"]) > 0:
|
|
277
|
-
marks = config["mark_vals"][0]
|
|
278
|
-
normalized["marks"] = marks # Pass the actual marks dictionary
|
|
279
|
-
if len(marks) >= 2:
|
|
280
|
-
# Calculate interval from first two marks
|
|
281
|
-
mark_positions = sorted(marks.keys())
|
|
282
|
-
normalized["mark_interval"] = mark_positions[1] - mark_positions[0]
|
|
283
|
-
else:
|
|
284
|
-
# Fallback: use step as mark interval
|
|
285
|
-
normalized["mark_interval"] = normalized["step"]
|
|
286
|
-
else:
|
|
287
|
-
normalized["mark_interval"] = normalized["step"]
|
|
288
|
-
|
|
289
|
-
# Add default value if available from grid values
|
|
290
|
-
if "grid_slider_vals" in config and len(config["grid_slider_vals"]) > 0:
|
|
291
|
-
normalized["default_val"] = config["grid_slider_vals"][0]
|
|
292
|
-
else:
|
|
293
|
-
normalized["default_val"] = normalized["min_val"]
|
|
294
|
-
|
|
295
|
-
else:
|
|
296
|
-
# This is legacy format - use as-is but ensure all required keys exist
|
|
297
|
-
normalized = config.copy()
|
|
298
|
-
if "title" not in normalized:
|
|
299
|
-
normalized["title"] = config_type.capitalize()
|
|
300
|
-
if "default_val" not in normalized:
|
|
301
|
-
normalized["default_val"] = normalized.get("min_val", 0)
|
|
302
|
-
|
|
303
|
-
return normalized
|
|
304
|
-
|
|
305
|
-
def _make_id(self, subtype: str):
|
|
306
|
-
"""
|
|
307
|
-
Generate a unique ID dictionary for component sub-elements.
|
|
308
|
-
|
|
309
|
-
Args:
|
|
310
|
-
subtype (str): The specific component subtype.
|
|
311
|
-
|
|
312
|
-
Returns:
|
|
313
|
-
dict: Complete ID dictionary containing base ID information plus subtype.
|
|
314
|
-
"""
|
|
315
|
-
return {**self.id_base, "subtype": subtype}
|
|
316
|
-
|
|
317
|
-
def _make_dropdown(self):
|
|
318
|
-
"""
|
|
319
|
-
Create and configure the material selection dropdown.
|
|
320
|
-
|
|
321
|
-
Returns:
|
|
322
|
-
dash.dcc.Dropdown: Configured dropdown component for material selection.
|
|
323
|
-
"""
|
|
324
|
-
return dcc.Dropdown(
|
|
325
|
-
id=self.dropdown_id,
|
|
326
|
-
options=self.material_options,
|
|
327
|
-
value=self.default_material, # Can be None for no initial selection
|
|
328
|
-
disabled=self.slider_disable,
|
|
329
|
-
style={"width": "100%", "margin-bottom": "10px"},
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
def _make_weight_fraction_input(self):
|
|
333
|
-
"""
|
|
334
|
-
Create and configure the weight percentage input.
|
|
335
|
-
|
|
336
|
-
Returns:
|
|
337
|
-
dash.dcc.Input: Configured numeric input for weight percentage.
|
|
338
|
-
"""
|
|
339
|
-
return dcc.Input(
|
|
340
|
-
id=self.weight_fraction_id,
|
|
341
|
-
type="number",
|
|
342
|
-
value=self.default_weight_percent, # Use percentage directly
|
|
343
|
-
min=0.0,
|
|
344
|
-
max=100.0,
|
|
345
|
-
step=0.1,
|
|
346
|
-
disabled=self.slider_disable,
|
|
347
|
-
style={"width": "100%", "margin-bottom": "10px"},
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
def __call__(self):
|
|
351
|
-
"""
|
|
352
|
-
Generate the complete component layout as a callable object.
|
|
353
|
-
|
|
354
|
-
Creates and returns a Dash HTML Div containing all components arranged
|
|
355
|
-
in a horizontal layout with proper spacing and styling.
|
|
356
|
-
|
|
357
|
-
Returns:
|
|
358
|
-
dash.html.Div: Complete component layout with horizontal arrangement.
|
|
359
|
-
"""
|
|
360
|
-
return html.Div(
|
|
361
|
-
[
|
|
362
|
-
# Main horizontal layout
|
|
363
|
-
html.Div(
|
|
364
|
-
[
|
|
365
|
-
# Material selection column
|
|
366
|
-
html.Div(
|
|
367
|
-
[
|
|
368
|
-
html.P(
|
|
369
|
-
"Material:",
|
|
370
|
-
style={
|
|
371
|
-
"margin": "0px 0px 5px 0px",
|
|
372
|
-
"font-weight": "bold",
|
|
373
|
-
},
|
|
374
|
-
),
|
|
375
|
-
self._make_dropdown(),
|
|
376
|
-
],
|
|
377
|
-
style={
|
|
378
|
-
"width": "20%",
|
|
379
|
-
"display": "inline-block",
|
|
380
|
-
"vertical-align": "top",
|
|
381
|
-
"padding-right": "25px",
|
|
382
|
-
},
|
|
383
|
-
),
|
|
384
|
-
# Weight percentage column
|
|
385
|
-
html.Div(
|
|
386
|
-
[
|
|
387
|
-
html.P(
|
|
388
|
-
"Weight (%):",
|
|
389
|
-
style={
|
|
390
|
-
"margin": "0px 0px 5px 0px",
|
|
391
|
-
"font-weight": "bold",
|
|
392
|
-
},
|
|
393
|
-
),
|
|
394
|
-
self._make_weight_fraction_input(),
|
|
395
|
-
],
|
|
396
|
-
style={
|
|
397
|
-
"width": "15%",
|
|
398
|
-
"display": "inline-block",
|
|
399
|
-
"vertical-align": "top",
|
|
400
|
-
"padding-right": "25px",
|
|
401
|
-
},
|
|
402
|
-
),
|
|
403
|
-
# Density slider column
|
|
404
|
-
html.Div(
|
|
405
|
-
[self.density_slider()],
|
|
406
|
-
style={
|
|
407
|
-
"width": "30%",
|
|
408
|
-
"display": "inline-block",
|
|
409
|
-
"vertical-align": "top",
|
|
410
|
-
"padding-right": "25px",
|
|
411
|
-
},
|
|
412
|
-
),
|
|
413
|
-
# Cost slider column
|
|
414
|
-
html.Div(
|
|
415
|
-
[self.cost_slider()],
|
|
416
|
-
style={
|
|
417
|
-
"width": "30%",
|
|
418
|
-
"display": "inline-block",
|
|
419
|
-
"vertical-align": "top",
|
|
420
|
-
},
|
|
421
|
-
),
|
|
422
|
-
],
|
|
423
|
-
style={
|
|
424
|
-
"display": "flex",
|
|
425
|
-
"align-items": "flex-start",
|
|
426
|
-
"width": "100%",
|
|
427
|
-
"gap": "15px",
|
|
428
|
-
},
|
|
429
|
-
)
|
|
430
|
-
],
|
|
431
|
-
id=self.id_base,
|
|
432
|
-
style={
|
|
433
|
-
"border": "1px solid #ddd",
|
|
434
|
-
"border-radius": "5px",
|
|
435
|
-
"padding": "15px",
|
|
436
|
-
"margin": "10px 0px",
|
|
437
|
-
"background-color": "#f9f9f9",
|
|
438
|
-
"width": self.div_width,
|
|
439
|
-
"display": "none" if self.hidden else "block",
|
|
440
|
-
},
|
|
441
|
-
)
|
|
442
|
-
|
|
443
|
-
@property
|
|
444
|
-
def components(self):
|
|
445
|
-
"""
|
|
446
|
-
Get a dictionary mapping component types to their IDs.
|
|
447
|
-
|
|
448
|
-
Returns:
|
|
449
|
-
dict: Dictionary with component type keys mapping to their ID dictionaries.
|
|
450
|
-
"""
|
|
451
|
-
return {
|
|
452
|
-
"dropdown": self.dropdown_id,
|
|
453
|
-
"weight_fraction": self.weight_fraction_id,
|
|
454
|
-
"cost_slider": self.cost_slider.slider_id,
|
|
455
|
-
"cost_input": self.cost_slider.input_id,
|
|
456
|
-
"density_slider": self.density_slider.slider_id,
|
|
457
|
-
"density_input": self.density_slider.input_id,
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
def get_all_inputs(self):
|
|
461
|
-
"""
|
|
462
|
-
Get Input objects for all component values.
|
|
463
|
-
|
|
464
|
-
Returns:
|
|
465
|
-
list: List containing Input objects for all components.
|
|
466
|
-
"""
|
|
467
|
-
return [
|
|
468
|
-
Input(self.dropdown_id, "value"),
|
|
469
|
-
Input(self.weight_fraction_id, "value"),
|
|
470
|
-
Input(self.cost_slider.slider_id, "value"),
|
|
471
|
-
Input(self.cost_slider.input_id, "value"),
|
|
472
|
-
Input(self.density_slider.slider_id, "value"),
|
|
473
|
-
Input(self.density_slider.input_id, "value"),
|
|
474
|
-
]
|
|
475
|
-
|
|
476
|
-
def get_all_outputs(self):
|
|
477
|
-
"""
|
|
478
|
-
Get Output objects for updating all component values.
|
|
479
|
-
|
|
480
|
-
Returns:
|
|
481
|
-
list: List containing Output objects for all components.
|
|
482
|
-
"""
|
|
483
|
-
return [
|
|
484
|
-
Output(self.dropdown_id, "value"),
|
|
485
|
-
Output(self.weight_fraction_id, "value"),
|
|
486
|
-
Output(self.cost_slider.slider_id, "value"),
|
|
487
|
-
Output(self.cost_slider.input_id, "value"),
|
|
488
|
-
Output(self.density_slider.slider_id, "value"),
|
|
489
|
-
Output(self.density_slider.input_id, "value"),
|
|
490
|
-
]
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
class ActiveMaterialSelector(MaterialSelector):
|
|
494
|
-
"""
|
|
495
|
-
A custom Dash component for active material selection with capacity controls.
|
|
496
|
-
|
|
497
|
-
This component extends MaterialSelector functionality by adding reversible and
|
|
498
|
-
irreversible capacity sliders alongside the standard density and cost controls.
|
|
499
|
-
It creates a horizontal row containing:
|
|
500
|
-
- A dropdown menu for selecting material names
|
|
501
|
-
- An input box for specifying weight percentage of the material
|
|
502
|
-
- Four SliderWithTextInput components for:
|
|
503
|
-
* Density
|
|
504
|
-
* Specific cost
|
|
505
|
-
* Reversible capacity scaling
|
|
506
|
-
* Irreversible capacity scaling
|
|
507
|
-
|
|
508
|
-
This component is designed for battery active material interfaces where users need to
|
|
509
|
-
specify both physical properties (cost, density) and electrochemical scaling factors
|
|
510
|
-
(reversible/irreversible capacity scaling).
|
|
511
|
-
"""
|
|
512
|
-
|
|
513
|
-
def __init__(
|
|
514
|
-
self,
|
|
515
|
-
id_base: dict,
|
|
516
|
-
material_options: List[Dict] = None,
|
|
517
|
-
slider_configs: dict = None,
|
|
518
|
-
title: str = "Active Material",
|
|
519
|
-
default_material: str = None,
|
|
520
|
-
default_weight_percent: float = 0,
|
|
521
|
-
slider_disable: bool = False,
|
|
522
|
-
div_width: str = "100%",
|
|
523
|
-
hidden: bool = False,
|
|
524
|
-
):
|
|
525
|
-
"""
|
|
526
|
-
Initialize the ActiveMaterialSelector component.
|
|
527
|
-
|
|
528
|
-
Args:
|
|
529
|
-
id_base (dict): Base dictionary for generating component IDs.
|
|
530
|
-
material_options (List[Dict], optional): List of material options for dropdown.
|
|
531
|
-
Each dict should have 'label' and 'value' keys.
|
|
532
|
-
If None, defaults to empty list (no options).
|
|
533
|
-
slider_configs (dict): Output from create_slider_config with arrays where:
|
|
534
|
-
- Index 0 = density configuration
|
|
535
|
-
- Index 1 = cost configuration
|
|
536
|
-
- Index 2 = reversible capacity scaling configuration
|
|
537
|
-
- Index 3 = irreversible capacity scaling configuration
|
|
538
|
-
If empty dictionary {}, uses sensible defaults with preset values.
|
|
539
|
-
If None (default), uses sensible defaults with no initial values.
|
|
540
|
-
title (str, optional): Title displayed for the component. Defaults to "Active Material".
|
|
541
|
-
default_material (str, optional): Default selected material. If None, no material
|
|
542
|
-
will be initially selected in the dropdown.
|
|
543
|
-
default_weight_percent (float, optional): Default weight percentage (0.0-100.0). Defaults to 100.0.
|
|
544
|
-
slider_disable (bool, optional): Whether to disable sliders. Defaults to False.
|
|
545
|
-
div_width (str, optional): CSS width specification for the container div.
|
|
546
|
-
Defaults to '100%'.
|
|
547
|
-
hidden (bool, optional): Whether to hide the entire component. When True,
|
|
548
|
-
the component will be rendered with display: none.
|
|
549
|
-
Defaults to False.
|
|
550
|
-
"""
|
|
551
|
-
# Handle different slider_configs scenarios
|
|
552
|
-
if slider_configs is not None and slider_configs:
|
|
553
|
-
# Use provided configuration
|
|
554
|
-
pass # slider_configs will be passed to parent
|
|
555
|
-
elif slider_configs is not None and not slider_configs:
|
|
556
|
-
# Empty dictionary - create sensible defaults with preset values
|
|
557
|
-
from ..Utils.SliderControls import create_slider_config
|
|
558
|
-
|
|
559
|
-
min_vals = [
|
|
560
|
-
0.0,
|
|
561
|
-
0.0,
|
|
562
|
-
0.0,
|
|
563
|
-
0.0,
|
|
564
|
-
] # density, cost, rev_cap_scaling, irrev_cap_scaling
|
|
565
|
-
max_vals = [
|
|
566
|
-
0.1,
|
|
567
|
-
0.1,
|
|
568
|
-
0.1,
|
|
569
|
-
0.1,
|
|
570
|
-
] # density, cost, rev_cap_scaling, irrev_cap_scaling
|
|
571
|
-
default_vals = [
|
|
572
|
-
0.05,
|
|
573
|
-
0.05,
|
|
574
|
-
0.05,
|
|
575
|
-
0.05,
|
|
576
|
-
] # density, cost, rev_cap_scaling, irrev_cap_scaling
|
|
577
|
-
slider_configs = create_slider_config(min_vals, max_vals, default_vals)
|
|
578
|
-
else:
|
|
579
|
-
# slider_configs is None - create sensible defaults with None values
|
|
580
|
-
# Create manual config since create_slider_config doesn't handle None values
|
|
581
|
-
slider_configs = {
|
|
582
|
-
"min_vals": [0.0, 0.0, 0.0, 0.0],
|
|
583
|
-
"max_vals": [0.1, 0.1, 0.1, 0.1],
|
|
584
|
-
"step_vals": [0.01, 0.01, 0.01, 0.01],
|
|
585
|
-
"grid_slider_vals": [None, None, None, None],
|
|
586
|
-
"mark_vals": [
|
|
587
|
-
{0.0: "", 0.1: ""}, # density
|
|
588
|
-
{0.0: "", 0.1: ""}, # cost
|
|
589
|
-
{0.0: "", 0.1: ""}, # rev_cap_scaling
|
|
590
|
-
{0.0: "", 0.1: ""}, # irrev_cap_scaling
|
|
591
|
-
],
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
# Initialize the parent MaterialSelector with first two sliders (density, cost)
|
|
595
|
-
super().__init__(
|
|
596
|
-
id_base=id_base,
|
|
597
|
-
material_options=material_options,
|
|
598
|
-
slider_configs=slider_configs, # Parent will use indices 0 and 1
|
|
599
|
-
title=title,
|
|
600
|
-
default_material=default_material,
|
|
601
|
-
default_weight_percent=default_weight_percent,
|
|
602
|
-
slider_disable=slider_disable,
|
|
603
|
-
div_width=div_width,
|
|
604
|
-
hidden=hidden,
|
|
605
|
-
)
|
|
606
|
-
|
|
607
|
-
# Add the additional capacity sliders (indices 2 and 3)
|
|
608
|
-
reversible_capacity_normalized = self._normalize_config_from_arrays(
|
|
609
|
-
slider_configs, 2, "Reversible Capacity Scaling"
|
|
610
|
-
)
|
|
611
|
-
irreversible_capacity_normalized = self._normalize_config_from_arrays(
|
|
612
|
-
slider_configs, 3, "Irreversible Capacity Scaling"
|
|
613
|
-
)
|
|
614
|
-
|
|
615
|
-
# Create additional slider components
|
|
616
|
-
reversible_capacity_slider_kwargs = {
|
|
617
|
-
"id_base": id_base,
|
|
618
|
-
"min_val": reversible_capacity_normalized["min_val"],
|
|
619
|
-
"max_val": reversible_capacity_normalized["max_val"],
|
|
620
|
-
"step": reversible_capacity_normalized["step"],
|
|
621
|
-
"mark_interval": reversible_capacity_normalized["mark_interval"],
|
|
622
|
-
"property_name": "reversible_capacity_scaling",
|
|
623
|
-
"title": reversible_capacity_normalized["title"],
|
|
624
|
-
"default_val": reversible_capacity_normalized.get(
|
|
625
|
-
"default_val", reversible_capacity_normalized["min_val"]
|
|
626
|
-
),
|
|
627
|
-
"with_slider_titles": True,
|
|
628
|
-
"slider_disable": slider_disable,
|
|
629
|
-
"div_width": "100%",
|
|
630
|
-
}
|
|
631
|
-
if "marks" in reversible_capacity_normalized:
|
|
632
|
-
reversible_capacity_slider_kwargs["marks"] = reversible_capacity_normalized[
|
|
633
|
-
"marks"
|
|
634
|
-
]
|
|
635
|
-
self.reversible_capacity_slider = SliderWithTextInput(
|
|
636
|
-
**reversible_capacity_slider_kwargs
|
|
637
|
-
)
|
|
638
|
-
|
|
639
|
-
irreversible_capacity_slider_kwargs = {
|
|
640
|
-
"id_base": id_base,
|
|
641
|
-
"min_val": irreversible_capacity_normalized["min_val"],
|
|
642
|
-
"max_val": irreversible_capacity_normalized["max_val"],
|
|
643
|
-
"step": irreversible_capacity_normalized["step"],
|
|
644
|
-
"mark_interval": irreversible_capacity_normalized["mark_interval"],
|
|
645
|
-
"property_name": "irreversible_capacity_scaling",
|
|
646
|
-
"title": irreversible_capacity_normalized["title"],
|
|
647
|
-
"default_val": irreversible_capacity_normalized.get(
|
|
648
|
-
"default_val", irreversible_capacity_normalized["min_val"]
|
|
649
|
-
),
|
|
650
|
-
"with_slider_titles": True,
|
|
651
|
-
"slider_disable": slider_disable,
|
|
652
|
-
"div_width": "100%",
|
|
653
|
-
}
|
|
654
|
-
if "marks" in irreversible_capacity_normalized:
|
|
655
|
-
irreversible_capacity_slider_kwargs[
|
|
656
|
-
"marks"
|
|
657
|
-
] = irreversible_capacity_normalized["marks"]
|
|
658
|
-
self.irreversible_capacity_slider = SliderWithTextInput(
|
|
659
|
-
**irreversible_capacity_slider_kwargs
|
|
660
|
-
)
|
|
661
|
-
|
|
662
|
-
def __call__(self):
|
|
663
|
-
"""
|
|
664
|
-
Generate the complete component layout as a callable object.
|
|
665
|
-
|
|
666
|
-
Creates and returns a Dash HTML Div containing all components arranged
|
|
667
|
-
in a horizontal layout with proper spacing and styling.
|
|
668
|
-
|
|
669
|
-
Returns:
|
|
670
|
-
dash.html.Div: Complete component layout with horizontal arrangement.
|
|
671
|
-
"""
|
|
672
|
-
return html.Div(
|
|
673
|
-
[
|
|
674
|
-
# Main horizontal layout
|
|
675
|
-
html.Div(
|
|
676
|
-
[
|
|
677
|
-
# Material selection column (15%)
|
|
678
|
-
html.Div(
|
|
679
|
-
[
|
|
680
|
-
html.P(
|
|
681
|
-
"Material:",
|
|
682
|
-
style={
|
|
683
|
-
"margin": "0px 0px 5px 0px",
|
|
684
|
-
"font-weight": "bold",
|
|
685
|
-
},
|
|
686
|
-
),
|
|
687
|
-
self._make_dropdown(),
|
|
688
|
-
],
|
|
689
|
-
style={
|
|
690
|
-
"width": "15%",
|
|
691
|
-
"display": "inline-block",
|
|
692
|
-
"vertical-align": "top",
|
|
693
|
-
"padding-right": "15px",
|
|
694
|
-
},
|
|
695
|
-
),
|
|
696
|
-
# Weight percentage column (15%)
|
|
697
|
-
html.Div(
|
|
698
|
-
[
|
|
699
|
-
html.P(
|
|
700
|
-
"Weight (%):",
|
|
701
|
-
style={
|
|
702
|
-
"margin": "0px 0px 5px 0px",
|
|
703
|
-
"font-weight": "bold",
|
|
704
|
-
},
|
|
705
|
-
),
|
|
706
|
-
self._make_weight_fraction_input(),
|
|
707
|
-
],
|
|
708
|
-
style={
|
|
709
|
-
"width": "15%",
|
|
710
|
-
"display": "inline-block",
|
|
711
|
-
"vertical-align": "top",
|
|
712
|
-
"padding-right": "15px",
|
|
713
|
-
},
|
|
714
|
-
),
|
|
715
|
-
# Density slider column (17.5%)
|
|
716
|
-
html.Div(
|
|
717
|
-
[self.density_slider()],
|
|
718
|
-
style={
|
|
719
|
-
"width": "17.5%",
|
|
720
|
-
"display": "inline-block",
|
|
721
|
-
"vertical-align": "top",
|
|
722
|
-
"padding-right": "15px",
|
|
723
|
-
},
|
|
724
|
-
),
|
|
725
|
-
# Cost slider column (17.5%)
|
|
726
|
-
html.Div(
|
|
727
|
-
[self.cost_slider()],
|
|
728
|
-
style={
|
|
729
|
-
"width": "17.5%",
|
|
730
|
-
"display": "inline-block",
|
|
731
|
-
"vertical-align": "top",
|
|
732
|
-
"padding-right": "15px",
|
|
733
|
-
},
|
|
734
|
-
),
|
|
735
|
-
# Reversible capacity slider column (17.5%)
|
|
736
|
-
html.Div(
|
|
737
|
-
[self.reversible_capacity_slider()],
|
|
738
|
-
style={
|
|
739
|
-
"width": "17.5%",
|
|
740
|
-
"display": "inline-block",
|
|
741
|
-
"vertical-align": "top",
|
|
742
|
-
"padding-right": "15px",
|
|
743
|
-
},
|
|
744
|
-
),
|
|
745
|
-
# Irreversible capacity slider column (17.5%)
|
|
746
|
-
html.Div(
|
|
747
|
-
[self.irreversible_capacity_slider()],
|
|
748
|
-
style={
|
|
749
|
-
"width": "17.5%",
|
|
750
|
-
"display": "inline-block",
|
|
751
|
-
"vertical-align": "top",
|
|
752
|
-
},
|
|
753
|
-
),
|
|
754
|
-
],
|
|
755
|
-
style={
|
|
756
|
-
"display": "flex",
|
|
757
|
-
"align-items": "flex-start",
|
|
758
|
-
"width": "100%",
|
|
759
|
-
"gap": "10px",
|
|
760
|
-
},
|
|
761
|
-
)
|
|
762
|
-
],
|
|
763
|
-
id=self.id_base,
|
|
764
|
-
style={
|
|
765
|
-
"border": "1px solid #ddd",
|
|
766
|
-
"border-radius": "5px",
|
|
767
|
-
"padding": "15px",
|
|
768
|
-
"margin": "10px 0px",
|
|
769
|
-
"background-color": "#f9f9f9",
|
|
770
|
-
"width": self.div_width,
|
|
771
|
-
"display": "none" if self.hidden else "block",
|
|
772
|
-
},
|
|
773
|
-
)
|
|
774
|
-
|
|
775
|
-
@property
|
|
776
|
-
def components(self):
|
|
777
|
-
"""
|
|
778
|
-
Get a dictionary mapping component types to their IDs.
|
|
779
|
-
|
|
780
|
-
Returns:
|
|
781
|
-
dict: Dictionary with component type keys mapping to their ID dictionaries.
|
|
782
|
-
"""
|
|
783
|
-
# Get base components from parent
|
|
784
|
-
base_components = super().components
|
|
785
|
-
|
|
786
|
-
# Add the additional capacity slider components
|
|
787
|
-
base_components.update(
|
|
788
|
-
{
|
|
789
|
-
"reversible_capacity_scaling_slider": self.reversible_capacity_slider.slider_id,
|
|
790
|
-
"reversible_capacity_scaling_input": self.reversible_capacity_slider.input_id,
|
|
791
|
-
"irreversible_capacity_scaling_slider": self.irreversible_capacity_slider.slider_id,
|
|
792
|
-
"irreversible_capacity_scaling_input": self.irreversible_capacity_slider.input_id,
|
|
793
|
-
}
|
|
794
|
-
)
|
|
795
|
-
|
|
796
|
-
return base_components
|
|
797
|
-
|
|
798
|
-
def get_all_inputs(self):
|
|
799
|
-
"""
|
|
800
|
-
Get Input objects for all component values.
|
|
801
|
-
|
|
802
|
-
Returns:
|
|
803
|
-
list: List containing Input objects for all components.
|
|
804
|
-
"""
|
|
805
|
-
# Get base inputs from parent
|
|
806
|
-
base_inputs = super().get_all_inputs()
|
|
807
|
-
|
|
808
|
-
# Add the additional capacity slider inputs
|
|
809
|
-
capacity_inputs = [
|
|
810
|
-
Input(self.reversible_capacity_slider.slider_id, "value"),
|
|
811
|
-
Input(self.reversible_capacity_slider.input_id, "value"),
|
|
812
|
-
Input(self.irreversible_capacity_slider.slider_id, "value"),
|
|
813
|
-
Input(self.irreversible_capacity_slider.input_id, "value"),
|
|
814
|
-
]
|
|
815
|
-
|
|
816
|
-
return base_inputs + capacity_inputs
|
|
817
|
-
|
|
818
|
-
def get_all_outputs(self):
|
|
819
|
-
"""
|
|
820
|
-
Get Output objects for updating all component values.
|
|
821
|
-
|
|
822
|
-
Returns:
|
|
823
|
-
list: List containing Output objects for all components.
|
|
824
|
-
"""
|
|
825
|
-
# Get base outputs from parent
|
|
826
|
-
base_outputs = super().get_all_outputs()
|
|
827
|
-
|
|
828
|
-
# Add the additional capacity slider outputs
|
|
829
|
-
capacity_outputs = [
|
|
830
|
-
Output(self.reversible_capacity_slider.slider_id, "value"),
|
|
831
|
-
Output(self.reversible_capacity_slider.input_id, "value"),
|
|
832
|
-
Output(self.irreversible_capacity_slider.slider_id, "value"),
|
|
833
|
-
Output(self.irreversible_capacity_slider.input_id, "value"),
|
|
834
|
-
]
|
|
835
|
-
|
|
836
|
-
return base_outputs + capacity_outputs
|