pyfemtet 0.4.13__py3-none-any.whl → 0.4.14__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.

Potentially problematic release.


This version of pyfemtet might be problematic. Click here for more details.

Files changed (104) hide show
  1. pyfemtet/__init__.py +1 -1
  2. pyfemtet/dispatch_extensions.py +4 -0
  3. pyfemtet/message/1. make_pot.bat +12 -0
  4. pyfemtet/message/2. make_mo.bat +6 -0
  5. pyfemtet/message/__init__.py +5 -0
  6. pyfemtet/message/babel.cfg +2 -0
  7. pyfemtet/message/locales/ja/LC_MESSAGES/messages.po +449 -0
  8. pyfemtet/message/locales/messages.pot +439 -0
  9. pyfemtet/message/messages.py +174 -0
  10. pyfemtet/opt/_femopt.py +9 -9
  11. pyfemtet/opt/_femopt_core.py +16 -14
  12. pyfemtet/opt/femprj_sample/ParametricIF_test_result.reccsv +13 -13
  13. pyfemtet/opt/femprj_sample/cad_ex01_NX_test_result.reccsv +13 -13
  14. pyfemtet/opt/femprj_sample/cad_ex01_SW_test_result.reccsv +13 -13
  15. pyfemtet/opt/femprj_sample/gal_ex58_parametric_test_result.reccsv +13 -13
  16. pyfemtet/opt/femprj_sample/gau_ex08_parametric_test_result.reccsv +23 -23
  17. pyfemtet/opt/femprj_sample/her_ex40_parametric_test_result.reccsv +18 -18
  18. pyfemtet/opt/femprj_sample/paswat_ex1_parametric_test_result.reccsv +18 -18
  19. pyfemtet/opt/femprj_sample/tutorial.femprj +0 -0
  20. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14.pdt +0 -0
  21. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial1.jpg +0 -0
  22. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial1.pdt +0 -0
  23. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial10.jpg +0 -0
  24. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial10.pdt +0 -0
  25. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial11.jpg +0 -0
  26. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial11.pdt +0 -0
  27. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial12.jpg +0 -0
  28. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial12.pdt +0 -0
  29. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial13.jpg +0 -0
  30. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial13.pdt +0 -0
  31. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial14.jpg +0 -0
  32. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial14.pdt +0 -0
  33. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial15.jpg +0 -0
  34. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial15.pdt +0 -0
  35. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial2.jpg +0 -0
  36. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial2.pdt +0 -0
  37. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial3.jpg +0 -0
  38. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial3.pdt +0 -0
  39. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial4.jpg +0 -0
  40. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial4.pdt +0 -0
  41. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial5.jpg +0 -0
  42. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial5.pdt +0 -0
  43. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial6.jpg +0 -0
  44. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial6.pdt +0 -0
  45. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial7.jpg +0 -0
  46. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial7.pdt +0 -0
  47. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial8.jpg +0 -0
  48. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial8.pdt +0 -0
  49. pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial9.jpg +0 -0
  50. pyfemtet/opt/{visualization/result_viewer/tutorial → femprj_sample}/wat_ex14_parametric.Results/Ex14_trial9.pdt +0 -0
  51. pyfemtet/opt/femprj_sample/wat_ex14_parametric_test_result.reccsv +18 -18
  52. pyfemtet/opt/interface/_femtet.py +42 -46
  53. pyfemtet/opt/interface/_femtet_with_nx/_interface.py +4 -4
  54. pyfemtet/opt/interface/_femtet_with_nx/update_model.py +6 -6
  55. pyfemtet/opt/interface/_femtet_with_sldworks.py +5 -4
  56. pyfemtet/opt/opt/__init__.py +2 -0
  57. pyfemtet/opt/opt/_base.py +41 -20
  58. pyfemtet/opt/opt/_optuna.py +8 -13
  59. pyfemtet/opt/opt/_scipy.py +4 -8
  60. pyfemtet/opt/opt/_scipy_scalar.py +1 -5
  61. pyfemtet/opt/prediction/__init__.py +0 -0
  62. pyfemtet/opt/prediction/base.py +52 -0
  63. pyfemtet/opt/prediction/single_task_gp.py +82 -0
  64. pyfemtet/opt/visualization/complex_components/control_femtet.py +9 -11
  65. pyfemtet/opt/visualization/complex_components/main_figure_creator.py +24 -11
  66. pyfemtet/opt/visualization/complex_components/main_graph.py +3 -4
  67. pyfemtet/opt/visualization/complex_components/pm_graph.py +715 -0
  68. pyfemtet/opt/visualization/complex_components/pm_graph_creator.py +168 -0
  69. pyfemtet/opt/visualization/process_monitor/application.py +16 -12
  70. pyfemtet/opt/visualization/process_monitor/pages.py +22 -7
  71. pyfemtet/opt/visualization/result_viewer/application.py +8 -3
  72. pyfemtet/opt/visualization/result_viewer/pages.py +59 -47
  73. {pyfemtet-0.4.13.dist-info → pyfemtet-0.4.14.dist-info}/METADATA +2 -1
  74. {pyfemtet-0.4.13.dist-info → pyfemtet-0.4.14.dist-info}/RECORD +77 -70
  75. pyfemtet/opt/visualization/result_viewer/tutorial/tutorial.csv +0 -18
  76. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14.jpg +0 -0
  77. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14.log +0 -81
  78. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14.pdt +0 -0
  79. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_heatflow.csv +0 -28
  80. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_heatflow_el.csv +0 -22
  81. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial1.jpg +0 -0
  82. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial10.jpg +0 -0
  83. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial11.jpg +0 -0
  84. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial11.pdt +0 -0
  85. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial12.jpg +0 -0
  86. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial12.pdt +0 -0
  87. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial13.jpg +0 -0
  88. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial13.pdt +0 -0
  89. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial14.jpg +0 -0
  90. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial14.pdt +0 -0
  91. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial15.jpg +0 -0
  92. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial15.pdt +0 -0
  93. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial2.jpg +0 -0
  94. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial3.jpg +0 -0
  95. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial4.jpg +0 -0
  96. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial5.jpg +0 -0
  97. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial6.jpg +0 -0
  98. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial7.jpg +0 -0
  99. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial8.jpg +0 -0
  100. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial9.jpg +0 -0
  101. pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.femprj +0 -0
  102. {pyfemtet-0.4.13.dist-info → pyfemtet-0.4.14.dist-info}/LICENSE +0 -0
  103. {pyfemtet-0.4.13.dist-info → pyfemtet-0.4.14.dist-info}/WHEEL +0 -0
  104. {pyfemtet-0.4.13.dist-info → pyfemtet-0.4.14.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,715 @@
1
+ # type hint
2
+ from dash.development.base_component import Component
3
+
4
+ # callback
5
+ from dash import Output, Input, State, no_update, callback_context, ALL, MATCH
6
+ from dash.exceptions import PreventUpdate
7
+
8
+ # components
9
+ from dash import dash_table
10
+ from pyfemtet.opt.visualization.wrapped_components import html, dcc, dbc
11
+
12
+ # graph
13
+ import pandas as pd
14
+ # import plotly.express as px
15
+ import plotly.graph_objs as go
16
+
17
+ # the others
18
+ import os
19
+ import base64
20
+ import json
21
+ import numpy as np
22
+
23
+ from pyfemtet.opt.visualization.complex_components.pm_graph_creator import PredictionModelCreator
24
+ from pyfemtet.opt.visualization.base import PyFemtetApplicationBase, AbstractPage, logger
25
+ from pyfemtet.message import Msg
26
+
27
+
28
+ FLEXBOX_STYLE_ALLOW_VERTICAL_FILL = {
29
+ 'display': 'flex',
30
+ 'flex-direction': 'column',
31
+ 'flex-grow': '1',
32
+ }
33
+
34
+
35
+ # noinspection PyAttributeOutsideInit
36
+ class PredictionModelGraph(AbstractPage):
37
+ """"""
38
+ """
39
+ +=================+
40
+ |3d|scatterplot| | <- CardHeader
41
+ | +--------------+
42
+ | +-------------+ |
43
+ | | Loading | | <- CardBody
44
+ | | +---------+ | |
45
+ | | | graph <-------- ToolTip
46
+ | | +---------+ | |
47
+ | +-------------+ |
48
+ | |
49
+ | +-------------+ |
50
+ | |p1, p2, obj <------ Dropdown (axis selection)
51
+ | | | |
52
+ | | p3 --o----- | |
53
+ | | p4 -----o-- <----- Slider(s) (choose the value of the others)
54
+ | | | |
55
+ | +-------------+ |
56
+ | |
57
+ +=================+
58
+
59
+ Data(data-selection)
60
+
61
+ """
62
+
63
+ def __init__(self):
64
+ self.rsm_creator: PredictionModelCreator = PredictionModelCreator()
65
+ super().__init__()
66
+
67
+ def setup_component(self):
68
+ self.location = dcc.Location(id='rsm-graph-location', refresh=True)
69
+
70
+ # setup header
71
+ self.tab_list = [dbc.Tab(label=Msg.TAB_LABEL_PREDICTION_MODEL)]
72
+ self.tabs = dbc.Tabs(self.tab_list)
73
+
74
+ # setup body
75
+ # self.tooltip = dcc.Tooltip()
76
+
77
+ # set kwargs of Graph to reconstruct Graph in ProcessMonitor.
78
+ self.graph_kwargs = dict(
79
+ # animate=True, # THIS CAUSE THE UPDATED DATA / NOT-UPDATED FIGURE STATE.
80
+ clear_on_unhover=True,
81
+ style={
82
+ 'height': '60vh',
83
+ },
84
+ figure=go.Figure()
85
+ )
86
+
87
+ self.graph: dcc.Graph = dcc.Graph(
88
+ **self.graph_kwargs
89
+ )
90
+
91
+ self.loading = dcc.Loading(
92
+ children=self.graph,
93
+ )
94
+
95
+ # setup selection data
96
+ # self.selection_data_property = 'data-selection' # must be starts with "data-"
97
+ # self.selection_data = html.Data(id='selection-data', **{self.selection_data_property: {}})
98
+
99
+ # update rsm button
100
+ self.fit_rsm_button_spinner = dbc.Spinner(size='sm', spinner_style={'display': 'none'})
101
+ self.fit_rsm_button = dbc.Button([self.fit_rsm_button_spinner, Msg.LABEL_OF_CREATE_PREDICTION_MODEL_BUTTON], color='success')
102
+ self.redraw_graph_button_spinner = dbc.Spinner(size='sm', spinner_style={'display': 'none'})
103
+ self.redraw_graph_button = dbc.Button([self.redraw_graph_button_spinner, Msg.LABEL_OF_REDRAW_PREDICTION_MODEL_GRAPH_BUTTON])
104
+
105
+ # # set data length (to notify data updated to application)
106
+ # self.data_length_prop = 'data-df-length' # must start with "data-"
107
+ # self.data_length = html.Data(id='df-length-data', **{self.data_length_prop: None})
108
+
109
+ # Dropdowns
110
+ self.axis1_prm_dropdown = dbc.DropdownMenu()
111
+ self.axis2_prm_dropdown = dbc.DropdownMenu()
112
+ self.axis3_obj_dropdown = dbc.DropdownMenu()
113
+ self.axis_controllers = [
114
+ self.axis1_prm_dropdown,
115
+ self.axis2_prm_dropdown,
116
+ self.axis3_obj_dropdown,
117
+ ]
118
+
119
+ # sliders
120
+ self.slider_stack_data_prop = 'data-prm-slider-values'
121
+ self.slider_stack_data = html.Data(**{self.slider_stack_data_prop: {}})
122
+ self.slider_container = html.Div()
123
+
124
+ def setup_layout(self):
125
+ self.card_header = dbc.CardHeader(self.tabs)
126
+
127
+ self.card_body = dbc.CardBody(
128
+ children=html.Div([self.loading]), # children=html.Div([self.loading, self.tooltip]),
129
+ )
130
+
131
+ dropdown_rows = [
132
+ dbc.Row([dbc.Col(html.Span(Msg.LABEL_OF_AXIS1_SELECTION), align='center', style={'text-align': 'end'}, width=2), dbc.Col(self.axis_controllers[0])]),
133
+ dbc.Row([dbc.Col(html.Span(Msg.LABEL_OF_AXIS2_SELECTION), align='center', style={'text-align': 'end'}, width=2), dbc.Col(self.axis_controllers[1])], id='prm-axis-2-dropdown'),
134
+ dbc.Row([dbc.Col(html.Span(Msg.LABEL_OF_AXIS3_SELECTION), align='center', style={'text-align': 'end'}, width=2), dbc.Col(self.axis_controllers[2])]),
135
+ ]
136
+
137
+ self.card_footer = dbc.CardFooter(
138
+ children=[
139
+ dbc.Stack([self.fit_rsm_button, self.redraw_graph_button], direction='horizontal', gap=2),
140
+ *dropdown_rows,
141
+ self.slider_container,
142
+ self.slider_stack_data,
143
+ ],
144
+ )
145
+
146
+ self.graph_card = dbc.Card(
147
+ [
148
+ self.location,
149
+ # self.data_length,
150
+ self.card_header,
151
+ self.card_body,
152
+ self.card_footer,
153
+ ],
154
+ # style=FLEXBOX_STYLE_ALLOW_VERTICAL_FILL,
155
+ )
156
+
157
+ self.layout = dbc.Container(
158
+ children=[
159
+ dbc.Row(self.graph_card),
160
+ ]
161
+ )
162
+
163
+ def setup_callback(self):
164
+ # setup callback of subpages
165
+ super().setup_callback()
166
+
167
+ app = self.application.app
168
+
169
+ # ===== disable fit buttons when clicked =====
170
+ @app.callback(
171
+ Output(self.fit_rsm_button_spinner, 'spinner_style', allow_duplicate=True),
172
+ Output(self.fit_rsm_button, 'disabled', allow_duplicate=True),
173
+ Output(self.redraw_graph_button_spinner, 'spinner_style', allow_duplicate=True),
174
+ Output(self.redraw_graph_button, 'disabled', allow_duplicate=True),
175
+ Input(self.fit_rsm_button, 'n_clicks'),
176
+ Input(self.redraw_graph_button, 'n_clicks'),
177
+ State(self.fit_rsm_button_spinner, 'spinner_style'),
178
+ State(self.redraw_graph_button_spinner, 'spinner_style'),
179
+ prevent_initial_call=True,
180
+ )
181
+ def disable_fit_button(_1, _2, state1, state2):
182
+ if 'display' in state1.keys(): state1.pop('display')
183
+ if 'display' in state2.keys(): state2.pop('display')
184
+ return state1, True, state2, True
185
+
186
+ # ===== recreate RSM =====
187
+ @app.callback(
188
+ Output(self.redraw_graph_button, 'n_clicks'),
189
+ Input(self.fit_rsm_button, 'n_clicks'),
190
+ prevent_initial_call=True,
191
+ )
192
+ def recalculate_rsm(*args):
193
+ # just in case
194
+ if callback_context.triggered_id is None:
195
+ raise PreventUpdate
196
+
197
+ # load history
198
+ if self.application.history is None:
199
+ return 1 # error handling in the next `redraw_graph()` callback
200
+
201
+ # check history
202
+ if len(self.data_accessor()) == 0:
203
+ return 1 # error handling in the next `redraw_graph()` callback
204
+
205
+ # create model
206
+ self.rsm_creator.fit(
207
+ self.application.history,
208
+ self.data_accessor(),
209
+ )
210
+
211
+ return 1
212
+
213
+ # ===== Update Graph =====
214
+ @app.callback(
215
+ Output(self.graph, 'figure'),
216
+ # Output(self.data_length.id, self.data_length_prop), # To determine whether Process Monitor should update the graph, the main graph remembers the current amount of data.
217
+ Input(self.redraw_graph_button, 'n_clicks'),
218
+ State(self.tabs, 'active_tab'),
219
+ State(self.axis1_prm_dropdown, 'label'),
220
+ State(self.axis2_prm_dropdown, 'label'),
221
+ State(self.axis3_obj_dropdown, 'label'),
222
+ State(self.slider_container, 'children'), # for callback chain
223
+ State({'type': 'prm-slider', 'index': ALL}, 'value'),
224
+ prevent_initial_call=True,
225
+ )
226
+ def redraw_graph(_1, active_tab_id, axis1_label, axis2_label, axis3_label, _2, prm_values):
227
+ # just in case
228
+ if callback_context.triggered_id is None:
229
+ raise PreventUpdate
230
+
231
+ # load history
232
+ if self.application.history is None:
233
+ logger.error(Msg.ERR_NO_HISTORY_SELECTED)
234
+ return go.Figure() # to re-enable buttons, fire callback chain
235
+ prm_names = self.application.history.prm_names
236
+
237
+ # check history
238
+ if len(self.data_accessor()) == 0:
239
+ logger.error(Msg.ERR_NO_FEM_RESULT)
240
+ return go.Figure() # to re-enable buttons, fire callback chain
241
+
242
+ # check fit
243
+ if not hasattr(self.rsm_creator, 'history'):
244
+ logger.error(Msg.ERR_NO_PREDICTION_MODEL)
245
+ return go.Figure() # to re-enable buttons, fire callback chain
246
+
247
+ # get indices to remove
248
+ idx1 = prm_names.index(axis1_label) if axis1_label in prm_names else None
249
+ idx2 = prm_names.index(axis2_label) if axis2_label in prm_names else None
250
+
251
+ # replace values to remove to None
252
+ if idx1 is not None:
253
+ prm_values[idx1] = None
254
+ if idx2 is not None:
255
+ prm_values[idx2] = None
256
+
257
+ # remove all None from array: prm_values is now remaining_x
258
+ while None in prm_values:
259
+ prm_values.remove(None)
260
+
261
+ # create figure
262
+ fig = self.rsm_creator.create_figure(
263
+ axis1_label,
264
+ axis3_label,
265
+ remaining_x=prm_values,
266
+ prm_name_2=axis2_label,
267
+ )
268
+
269
+ return fig
270
+
271
+ # ===== When the graph is updated, enable buttons =====
272
+ @app.callback(
273
+ Output(self.fit_rsm_button, 'disabled', allow_duplicate=True),
274
+ Output(self.fit_rsm_button_spinner, 'spinner_style', allow_duplicate=True),
275
+ Output(self.redraw_graph_button, 'disabled', allow_duplicate=True),
276
+ Output(self.redraw_graph_button_spinner, 'spinner_style', allow_duplicate=True),
277
+ Input(self.graph, 'figure'),
278
+ State(self.fit_rsm_button_spinner, 'spinner_style'),
279
+ State(self.redraw_graph_button_spinner, 'spinner_style'),
280
+ prevent_initial_call=True,
281
+ )
282
+ def enable_buttons(_, state1, state2):
283
+ state1.update({'display': 'none'})
284
+ state2.update({'display': 'none'})
285
+ return False, state1, False, state2
286
+
287
+ # ===== setup dropdown and sliders from history =====
288
+ @app.callback(
289
+ Output(self.axis1_prm_dropdown, 'children'),
290
+ Output(self.axis2_prm_dropdown, 'children'),
291
+ Output(self.axis3_obj_dropdown, 'children'),
292
+ Output(self.slider_container, 'children'),
293
+ Output(self.slider_stack_data, self.slider_stack_data_prop, allow_duplicate=True),
294
+ Input(self.location, self.location.Prop.pathname),
295
+ prevent_initial_call=True,
296
+ )
297
+ def setup_dropdown_and_sliders(*_):
298
+ # just in case
299
+ if callback_context.triggered_id is None:
300
+ raise PreventUpdate
301
+
302
+ # load history
303
+ if self.application.history is None:
304
+ logger.error(Msg.ERR_NO_HISTORY_SELECTED)
305
+ raise PreventUpdate
306
+
307
+ # add dropdown item to dropdown
308
+ axis1_dropdown_items, axis2_dropdown_items = [], []
309
+ for i, prm_name in enumerate(self.application.history.prm_names):
310
+ dm_item_1 = dbc.DropdownMenuItem(
311
+ children=prm_name,
312
+ id={'type': 'axis1-dropdown-menu-item', 'index': prm_name},
313
+ )
314
+ axis1_dropdown_items.append(dm_item_1)
315
+
316
+ dm_item_2 = dbc.DropdownMenuItem(
317
+ children=prm_name,
318
+ id={'type': 'axis2-dropdown-menu-item', 'index': prm_name},
319
+ )
320
+ axis2_dropdown_items.append(dm_item_2)
321
+ axis3_dropdown_items = []
322
+ for i, obj_name in enumerate(self.application.history.obj_names):
323
+ dm_item = dbc.DropdownMenuItem(
324
+ children=obj_name,
325
+ id={'type': 'axis3-dropdown-menu-item', 'index': obj_name},
326
+ )
327
+ axis3_dropdown_items.append(dm_item)
328
+
329
+ # add sliders
330
+ sliders = []
331
+ slider_values = {}
332
+ for prm_name in self.application.history.prm_names:
333
+ # get ub and lb
334
+ lb_column = prm_name + '_lower_bound'
335
+ ub_column = prm_name + '_upper_bound'
336
+ # get minimum lb and maximum ub
337
+ df = self.data_accessor()
338
+ lb = df[lb_column].min()
339
+ ub = df[ub_column].max()
340
+ # if lb or ub is not specified, use value instead
341
+ lb = df[prm_name].min() if np.isnan(lb) else lb
342
+ ub = df[prm_name].max() if np.isnan(ub) else ub
343
+ # create slider
344
+ value = (lb + ub) / 2
345
+ slider_values.update({prm_name: value})
346
+ stack = dbc.Stack(
347
+ id={'type': 'prm-slider-stack', 'index': prm_name},
348
+ style={'display': 'inline'},
349
+ children=[
350
+ html.Div(f'{prm_name}: '),
351
+ dcc.Slider(
352
+ lb,
353
+ ub,
354
+ marks=None,
355
+ value=value,
356
+ id={'type': 'prm-slider', 'index': prm_name},
357
+ tooltip={"placement": "bottom", "always_visible": True},
358
+ )
359
+ ]
360
+ )
361
+ sliders.append(stack)
362
+
363
+ return axis1_dropdown_items, axis2_dropdown_items, axis3_dropdown_items, sliders, slider_values
364
+
365
+ # ===== control dropdown and slider visibility =====
366
+ @app.callback(
367
+ Output(self.axis1_prm_dropdown, 'label'), # label of dropdown
368
+ Output(self.axis2_prm_dropdown, 'label'),
369
+ Output(self.axis3_obj_dropdown, 'label'),
370
+ Output({'type': 'prm-slider-stack', 'index': ALL}, 'style'), # visibility of slider
371
+ Output('prm-axis-2-dropdown', 'hidden'),
372
+ Input({'type': 'axis1-dropdown-menu-item', 'index': ALL}, 'n_clicks'), # when the dropdown item is clicked
373
+ Input({'type': 'axis2-dropdown-menu-item', 'index': ALL}, 'n_clicks'),
374
+ Input({'type': 'axis3-dropdown-menu-item', 'index': ALL}, 'n_clicks'),
375
+ Input(self.axis1_prm_dropdown, 'children'), # for callback chain timing
376
+ State(self.axis1_prm_dropdown, 'label'),
377
+ State(self.axis2_prm_dropdown, 'label'),
378
+ State(self.axis3_obj_dropdown, 'label'),
379
+ State({'type': 'prm-slider-stack', 'index': ALL}, 'style'), # visibility of slider
380
+ prevent_initial_call=True,
381
+ )
382
+ def update_controller(*args):
383
+ # argument processing
384
+ current_ax1_label = args[4]
385
+ current_ax2_label = args[5]
386
+ current_ax3_label = args[6]
387
+ current_styles: list[dict] = args[7]
388
+
389
+ # just in case
390
+ if callback_context.triggered_id is None:
391
+ raise PreventUpdate
392
+
393
+ # load history
394
+ if self.application.history is None:
395
+ logger.error(Msg.ERR_NO_HISTORY_SELECTED)
396
+ raise PreventUpdate
397
+ prm_names = self.application.history.prm_names
398
+ obj_names = self.application.history.obj_names
399
+
400
+ # default return values
401
+ ret = {
402
+ (ax1_label_key := 1): no_update,
403
+ (ax2_label_key := 2): no_update,
404
+ (ax3_label_key := 3): no_update,
405
+ (slider_style_list_key := 4): [(style.update({'display': 'inline'}), style)[1] for style in current_styles],
406
+ (ax2_hidden := 5): False,
407
+ }
408
+
409
+ # ===== hide dropdown of axis 2 if prm number is 1 =====
410
+ if len(prm_names) < 2:
411
+ ret[ax2_hidden] = True
412
+
413
+ # ===== update dropdown label =====
414
+
415
+ # by callback chain on loaded after setup_dropdown_and_sliders()
416
+ if callback_context.triggered_id == self.axis1_prm_dropdown.id:
417
+ ret[ax1_label_key] = prm_names[0]
418
+ ret[ax2_label_key] = prm_names[1] if len(prm_names) >= 2 else ''
419
+ ret[ax3_label_key] = obj_names[0]
420
+
421
+ # by dropdown clicked
422
+ elif isinstance(callback_context.triggered_id, dict):
423
+ # example of triggerd_id: {'index': 'd', 'type': 'axis1-dropdown-menu-item'}
424
+ new_label = callback_context.triggered_id['index']
425
+
426
+ # ax1
427
+ if callback_context.triggered_id['type'] == 'axis1-dropdown-menu-item':
428
+ if new_label != current_ax2_label:
429
+ ret[ax1_label_key] = new_label
430
+ else:
431
+ logger.error(Msg.ERR_CANNOT_SELECT_SAME_PARAMETER)
432
+
433
+ # ax2
434
+ elif callback_context.triggered_id['type'] == 'axis2-dropdown-menu-item':
435
+ if new_label != current_ax1_label:
436
+ ret[ax2_label_key] = new_label
437
+ else:
438
+ logger.error(Msg.ERR_CANNOT_SELECT_SAME_PARAMETER)
439
+
440
+ # ax3
441
+ elif callback_context.triggered_id['type'] == 'axis3-dropdown-menu-item':
442
+ ret[ax3_label_key] = new_label
443
+
444
+ # ===== update visibility of sliders =====
445
+ for label_key, current_label in zip((ax1_label_key, ax2_label_key), (current_ax1_label, current_ax2_label)):
446
+ # get label of output
447
+ label = ret[label_key] if ret[label_key] != no_update else current_label
448
+ # update display style of slider
449
+ idx = prm_names.index(label) if label in prm_names else None
450
+ if idx is not None:
451
+ current_styles[idx].update({'display': 'none'})
452
+ ret[slider_style_list_key][idx] = current_styles[idx]
453
+
454
+ return tuple(ret.values())
455
+
456
+ # # # ===== update axis1-prm-dropdown =====
457
+ # # @app.callback(
458
+ # # Output(self.axis1_prm_dropdown, 'label', allow_duplicate=True),
459
+ # # Input(self.location, self.location.Prop.pathname),
460
+ # # [Input(item, 'n_clicks') for item in self.prm1_items],
461
+ # # State(self.axis2_prm_dropdown, 'label'),
462
+ # # prevent_initial_call=True,
463
+ # # )
464
+ # # def update_prm1_dropdown_menu_label(*args):
465
+ # # prm2_label = args[-1]
466
+ # #
467
+ # # # 一応
468
+ # # if callback_context.triggered_id is None:
469
+ # # raise PreventUpdate
470
+ # #
471
+ # # # load history
472
+ # # if self.application.history is None:
473
+ # # return 'History is not selected.'
474
+ # # prm_names = self.application.history.prm_names
475
+ # #
476
+ # # # 1st parameter on loaded
477
+ # # if callback_context.triggered_id == self.location.id:
478
+ # # return prm_names[0]
479
+ # #
480
+ # # # clicked
481
+ # # for i, item in enumerate(self.prm1_items):
482
+ # # if item.id == callback_context.triggered_id:
483
+ # # if prm_names[i] != prm2_label:
484
+ # # return prm_names[i]
485
+ # # else:
486
+ # # logger.error('Cannot select same parameter')
487
+ # # raise PreventUpdate
488
+ # #
489
+ # # # something wrong
490
+ # # # logger.debug('something wrong in `update_dropdown_menu_label`')
491
+ # # raise PreventUpdate
492
+ # #
493
+ # # # ===== update axis2-prm-dropdown =====
494
+ # # @app.callback(
495
+ # # Output(self.axis2_prm_dropdown, 'label', allow_duplicate=True),
496
+ # # Output(self.axis2_prm_dropdown, 'hidden', allow_duplicate=True),
497
+ # # Input(self.location, self.location.Prop.pathname),
498
+ # # [Input(item, 'n_clicks') for item in self.prm2_items],
499
+ # # State(self.axis1_prm_dropdown, 'label'),
500
+ # # prevent_initial_call=True,
501
+ # # )
502
+ # # def update_prm2_dropdown_menu_label(*args):
503
+ # # prm1_label = args[-1]
504
+ # #
505
+ # # # 一応
506
+ # # if callback_context.triggered_id is None:
507
+ # # raise PreventUpdate
508
+ # #
509
+ # # # load history
510
+ # # if self.application.history is None:
511
+ # # return 'History is not selected.', no_update
512
+ # # prm_names = self.application.history.prm_names
513
+ # #
514
+ # # # disable axis2 if only 1 parameter optimization
515
+ # # if len(prm_names) == 1:
516
+ # # return no_update, True
517
+ # #
518
+ # # # 2nd parameter on loaded
519
+ # # if callback_context.triggered_id == self.location.id:
520
+ # # return prm_names[1], False
521
+ # #
522
+ # # # clicked
523
+ # # for i, item in enumerate(self.prm2_items):
524
+ # # if item.id == callback_context.triggered_id:
525
+ # # if prm_names[i] != prm1_label:
526
+ # # return prm_names[i], False
527
+ # # else:
528
+ # # logger.error('Cannot select same parameter')
529
+ # # raise PreventUpdate
530
+ # #
531
+ # # # something wrong
532
+ # # # logger.debug('something wrong in `update_dropdown_menu_label`')
533
+ # # raise PreventUpdate
534
+ # #
535
+ # # # ===== update axis3-obj-dropdown =====
536
+ # # @app.callback(
537
+ # # Output(self.axis3_obj_dropdown, 'label', allow_duplicate=True),
538
+ # # Input(self.location, self.location.Prop.pathname),
539
+ # # [Input(item, 'n_clicks') for item in self.obj_items],
540
+ # # prevent_initial_call=True,
541
+ # # )
542
+ # # def update_obj_dropdown_menu_label(*args):
543
+ # # # 一応
544
+ # # if callback_context.triggered_id is None:
545
+ # # raise PreventUpdate
546
+ # #
547
+ # # if self.application.history is None:
548
+ # # return 'History is not selected.'
549
+ # #
550
+ # # obj_names = self.application.history.obj_names
551
+ # #
552
+ # # # 1st objective on loaded
553
+ # # if callback_context.triggered_id == self.location.id:
554
+ # # return obj_names[0]
555
+ # #
556
+ # # # clicked
557
+ # # for i, item in enumerate(self.obj_items):
558
+ # # if item.id == callback_context.triggered_id:
559
+ # # return obj_names[i]
560
+ # #
561
+ # # # something wrong
562
+ # # # logger.debug('something wrong in `update_dropdown_menu_label`')
563
+ # # raise PreventUpdate
564
+ #
565
+ # # ===== setup sliders =====
566
+ # @app.callback(
567
+ # Output(self.slider_container, 'children'),
568
+ # Output(self.slider_stack_data, self.slider_stack_data_prop),
569
+ # Input(self.location, self.location.Prop.pathname),
570
+ # Input(self.axis1_prm_dropdown, 'label'),
571
+ # Input(self.axis2_prm_dropdown, 'label'),
572
+ # State(self.slider_stack_data, self.slider_stack_data_prop),
573
+ # prevent_initial_call=True,
574
+ # )
575
+ # def update_sliders(_, label1, label2, slider_values):
576
+ # # Just in case
577
+ # if callback_context.triggered_id is None:
578
+ # raise PreventUpdate
579
+ #
580
+ # # load history
581
+ # if self.application.history is None:
582
+ # return 'History is not selected.', no_update
583
+ # prm_names: list = list(self.application.history.prm_names) # shallow copy
584
+ #
585
+ # prm_names.remove(label1) if label1 in prm_names else None
586
+ # prm_names.remove(label2) if label2 in prm_names else None
587
+ #
588
+ # out = []
589
+ # for prm_name in prm_names:
590
+ # # get ub and lb
591
+ # lb_column = prm_name + '_lower_bound'
592
+ # ub_column = prm_name + '_upper_bound'
593
+ # # get minimum lb and maximum ub
594
+ # df = self.data_accessor()
595
+ # lb = df[lb_column].min()
596
+ # ub = df[ub_column].max()
597
+ # # if lb or ub is not specified, use value instead
598
+ # lb = df[prm_name].min() if np.isnan(lb) else lb
599
+ # ub = df[prm_name].max() if np.isnan(ub) else ub
600
+ # # create slider
601
+ # if prm_name in slider_values.keys():
602
+ # value = slider_values[prm_name]
603
+ # print('-----')
604
+ # print(slider_values)
605
+ # else:
606
+ # value = (lb + ub) / 2
607
+ # slider_values.update({'value': value})
608
+ # print('ooooo')
609
+ # print(lb, ub)
610
+ # print('=====')
611
+ # print(value)
612
+ # stack = dbc.Stack(
613
+ # children=[
614
+ # html.Div(f'{prm_name}: '),
615
+ # dcc.Slider(
616
+ # lb,
617
+ # ub,
618
+ # marks=None,
619
+ # value=value,
620
+ # id={'type': f'prm-slider', 'index': prm_name},
621
+ # tooltip={"placement": "bottom", "always_visible": True},
622
+ # )
623
+ # ]
624
+ # )
625
+ # out.append(stack)
626
+ #
627
+ # return out, slider_values
628
+ #
629
+ # # ===== update slider values =====
630
+ # @app.callback(
631
+ # Output(self.slider_stack_data, self.slider_stack_data_prop, allow_duplicate=True),
632
+ # Input([{'type': 'prm-slider', 'index': prm_name}, 'value') ],
633
+ # prevent_initial_call=True,
634
+ # )
635
+ # def update_slider_values(values):
636
+ # # Just in case
637
+ # if callback_context.triggered_id is None:
638
+ # raise PreventUpdate
639
+ #
640
+ # # load history
641
+ # if self.application.history is None:
642
+ # return 'History is not selected.', no_update
643
+ # prm_names = self.application.history.prm_names
644
+ #
645
+ # print('==========')
646
+ # print(callback_context.triggered_id)
647
+ # print(callback_context.triggered_prop_ids)
648
+ # print(callback_context.triggered)
649
+ # print(values)
650
+ # float = callback_context.triggered[0]['value'][0]
651
+ #
652
+ # return {}
653
+
654
+ def create_formatted_parameter(self, row) -> Component:
655
+ metadata = self.application.history.metadata
656
+ pd.options.display.float_format = '{:.4e}'.format
657
+ parameters = row.iloc[:, np.where(np.array(metadata) == 'prm')[0]]
658
+ names = parameters.columns
659
+ values = [f'{value:.3e}' for value in parameters.values.ravel()]
660
+ data = pd.DataFrame(dict(
661
+ name=names, value=values
662
+ ))
663
+ table = dash_table.DataTable(
664
+ columns=[{'name': col, 'id': col} for col in data.columns],
665
+ data=data.to_dict('records')
666
+ )
667
+ return table
668
+
669
+ def create_image_content_if_femtet(self, trial) -> Component:
670
+ img_url = None
671
+ metadata = self.application.history.metadata
672
+ if metadata[0] != '':
673
+ # get img path
674
+ d = json.loads(metadata[0])
675
+ femprj_path = d['femprj_path']
676
+ model_name = d['model_name']
677
+ femprj_result_dir = femprj_path.replace('.femprj', '.Results')
678
+ img_path = os.path.join(femprj_result_dir, f'{model_name}_trial{trial}.jpg')
679
+ if os.path.exists(img_path):
680
+ # create encoded image
681
+ with open(img_path, 'rb') as f:
682
+ content = f.read()
683
+ encoded_image = base64.b64encode(content).decode('utf-8')
684
+ img_url = 'data:image/jpeg;base64, ' + encoded_image
685
+ return html.Img(src=img_url, style={"width": "200px"}) if img_url is not None else html.Div()
686
+
687
+ # def get_fig_by_tab_id(self, tab_id, with_length=False):
688
+ # # If the history is not loaded, do nothing
689
+ # if self.application.history is None:
690
+ # raise PreventUpdate
691
+ #
692
+ # # else, get creator by tab_id
693
+ # if tab_id == 'default':
694
+ # creator = self.figure_creators[0]['creator']
695
+ # else:
696
+ # creators = [d['creator'] for d in self.figure_creators if d['tab_id'] == tab_id]
697
+ # if len(creators) == 0:
698
+ # raise PreventUpdate
699
+ # creator = creators[0]
700
+ #
701
+ # # create figure
702
+ # df = self.data_accessor()
703
+ # fig = creator(self.application.history, df)
704
+ # if with_length:
705
+ # return fig, len(df)
706
+ # else:
707
+ # return fig
708
+
709
+ def data_accessor(self) -> pd.DataFrame:
710
+ from pyfemtet.opt.visualization.process_monitor.application import ProcessMonitorApplication
711
+ if isinstance(self.application, ProcessMonitorApplication):
712
+ df = self.application.local_data
713
+ else:
714
+ df = self.application.history.local_data
715
+ return df