pyfemtet 0.4.9__py3-none-any.whl → 0.4.11__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.
- pyfemtet/__init__.py +1 -1
- pyfemtet/opt/_femopt.py +9 -3
- pyfemtet/opt/femprj_sample/wat_ex14_parametric_parallel.py +66 -0
- pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +64 -0
- pyfemtet/opt/interface/_femtet.py +32 -10
- pyfemtet/opt/interface/_femtet_parametric.py +5 -25
- pyfemtet/opt/opt/_base.py +9 -1
- pyfemtet/opt/opt/_optuna.py +4 -0
- pyfemtet/opt/visualization/__init__.py +0 -7
- pyfemtet/opt/visualization/_create_wrapped_components.py +93 -0
- pyfemtet/opt/visualization/base.py +254 -0
- pyfemtet/opt/visualization/complex_components/__init__.py +0 -0
- pyfemtet/opt/visualization/complex_components/alert_region.py +71 -0
- pyfemtet/opt/visualization/complex_components/control_femtet.py +195 -0
- pyfemtet/opt/visualization/{_graphs.py → complex_components/main_figure_creator.py} +13 -49
- pyfemtet/opt/visualization/complex_components/main_graph.py +263 -0
- pyfemtet/opt/visualization/process_monitor/__init__.py +0 -0
- pyfemtet/opt/visualization/process_monitor/application.py +201 -0
- pyfemtet/opt/visualization/process_monitor/pages.py +276 -0
- pyfemtet/opt/visualization/result_viewer/.gitignore +1 -0
- pyfemtet/opt/visualization/result_viewer/__init__.py +0 -0
- pyfemtet/opt/visualization/result_viewer/application.py +44 -0
- pyfemtet/opt/visualization/result_viewer/pages.py +692 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/tutorial.csv +18 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14.log +81 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_heatflow.csv +28 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_heatflow_el.csv +22 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial1.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial1.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial10.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial10.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial11.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial11.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial12.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial12.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial13.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial13.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial14.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial14.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial15.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial15.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial2.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial2.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial3.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial3.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial4.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial4.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial5.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial5.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial6.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial6.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial7.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial7.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial8.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial8.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial9.jpg +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.Results/Ex14_trial9.pdt +0 -0
- pyfemtet/opt/visualization/result_viewer/tutorial/wat_ex14_parametric.femprj +0 -0
- pyfemtet/opt/visualization/wrapped_components/__init__.py +0 -0
- pyfemtet/opt/visualization/wrapped_components/dbc.py +1518 -0
- pyfemtet/opt/visualization/wrapped_components/dcc.py +609 -0
- pyfemtet/opt/visualization/wrapped_components/html.py +3570 -0
- pyfemtet/opt/visualization/wrapped_components/str_enum.py +43 -0
- {pyfemtet-0.4.9.dist-info → pyfemtet-0.4.11.dist-info}/METADATA +1 -1
- pyfemtet-0.4.11.dist-info/RECORD +136 -0
- {pyfemtet-0.4.9.dist-info → pyfemtet-0.4.11.dist-info}/entry_points.txt +1 -1
- pyfemtet/opt/visualization/_monitor.py +0 -1227
- pyfemtet/opt/visualization/result_viewer.py +0 -13
- pyfemtet-0.4.9.dist-info/RECORD +0 -81
- {pyfemtet-0.4.9.dist-info → pyfemtet-0.4.11.dist-info}/LICENSE +0 -0
- {pyfemtet-0.4.9.dist-info → pyfemtet-0.4.11.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
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
|
|
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 import main_figure_creator
|
|
24
|
+
from pyfemtet.opt.visualization.base import PyFemtetApplicationBase, AbstractPage, logger
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
FLEXBOX_STYLE_ALLOW_VERTICAL_FILL = {
|
|
28
|
+
'display': 'flex',
|
|
29
|
+
'flex-direction': 'column',
|
|
30
|
+
'flex-grow': '1',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# noinspection PyAttributeOutsideInit
|
|
35
|
+
class MainGraph(AbstractPage):
|
|
36
|
+
""""""
|
|
37
|
+
"""
|
|
38
|
+
+=================+
|
|
39
|
+
|tab1|tab2| | <- CardHeader
|
|
40
|
+
| +------------+
|
|
41
|
+
| +-------------+ |
|
|
42
|
+
| | Loading | | <- CardBody
|
|
43
|
+
| | +---------+ | |
|
|
44
|
+
| | | graph <-------- ToolTip
|
|
45
|
+
| | +---------+ | |
|
|
46
|
+
| +-------------+ |
|
|
47
|
+
+=================+
|
|
48
|
+
|
|
49
|
+
Data(data-selection)
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self):
|
|
54
|
+
self.setup_figure_creator()
|
|
55
|
+
super().__init__()
|
|
56
|
+
|
|
57
|
+
def setup_figure_creator(self):
|
|
58
|
+
# setup figure creators
|
|
59
|
+
# list[0] is the default tab
|
|
60
|
+
self.figure_creators = [
|
|
61
|
+
dict(
|
|
62
|
+
tab_id='tab-objective-plot',
|
|
63
|
+
label='objectives',
|
|
64
|
+
creator=main_figure_creator.get_default_figure,
|
|
65
|
+
),
|
|
66
|
+
dict(
|
|
67
|
+
tab_id='tab-hypervolume-plot',
|
|
68
|
+
label='hypervolume',
|
|
69
|
+
creator=main_figure_creator.get_hypervolume_plot,
|
|
70
|
+
),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
def setup_component(self):
|
|
74
|
+
self.dummy = html.Div(id='main-graph-dummy')
|
|
75
|
+
self.location = dcc.Location(id='main-graph-location', refresh=True)
|
|
76
|
+
|
|
77
|
+
# setup header
|
|
78
|
+
self.tab_list = [dbc.Tab(label=d['label'], tab_id=d['tab_id']) for d in self.figure_creators]
|
|
79
|
+
self.tabs = dbc.Tabs(self.tab_list, id='main-graph-tabs')
|
|
80
|
+
self.card_header = dbc.CardHeader(self.tabs)
|
|
81
|
+
|
|
82
|
+
# setup body
|
|
83
|
+
self.tooltip = dcc.Tooltip(id='main-graph-tooltip')
|
|
84
|
+
|
|
85
|
+
# set kwargs of Graph to reconstruct Graph in ProcessMonitor.
|
|
86
|
+
self.graph_kwargs = dict(
|
|
87
|
+
id='main-graph',
|
|
88
|
+
# animate=True, # THIS CAUSE THE UPDATED DATA / NOT-UPDATED FIGURE STATE.
|
|
89
|
+
clear_on_unhover=True,
|
|
90
|
+
style={
|
|
91
|
+
# 'flex-grow': '1', # Uncomment if the plotly's specification if fixed, and we can use dcc.Graph with FlexBox.
|
|
92
|
+
'height': '60vh',
|
|
93
|
+
},
|
|
94
|
+
figure=go.Figure()
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
self.graph: dcc.Graph = dcc.Graph(
|
|
98
|
+
**self.graph_kwargs
|
|
99
|
+
) # Graph make an element by js, so Flexbox CSS cannot apply to the graph (The element can be bigger, but cannot be smaller.)
|
|
100
|
+
|
|
101
|
+
self.loading = dcc.Loading(
|
|
102
|
+
children=self.graph,
|
|
103
|
+
id='loading-main-graph',
|
|
104
|
+
) # Loading make an element that doesn't contain a style attribute, so Flexbox CSS cannot apply to the graph
|
|
105
|
+
|
|
106
|
+
self.card_body = dbc.CardBody(
|
|
107
|
+
children=html.Div([self.loading, self.tooltip]), # If list is passed to CardBody's children, create element that doesn't contain a style attribute, so Flexbox CSS cannot apply to graph
|
|
108
|
+
id='main-graph-card-body',
|
|
109
|
+
# style=FLEXBOX_STYLE_ALLOW_VERTICAL_FILL,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# setup selection data
|
|
113
|
+
self.selection_data_property = 'data-selection' # must be starts with "data-"
|
|
114
|
+
self.selection_data = html.Data(id='selection-data', **{self.selection_data_property: {}})
|
|
115
|
+
|
|
116
|
+
# set data length
|
|
117
|
+
self.data_length_prop = 'data-df-length' # must start with "data-"
|
|
118
|
+
self.data_length = html.Data(id='df-length-data', **{self.data_length_prop: None})
|
|
119
|
+
|
|
120
|
+
def setup_layout(self):
|
|
121
|
+
# setup component
|
|
122
|
+
self.graph_card = dbc.Card(
|
|
123
|
+
[
|
|
124
|
+
self.dummy,
|
|
125
|
+
self.location,
|
|
126
|
+
self.data_length,
|
|
127
|
+
self.card_header,
|
|
128
|
+
self.card_body,
|
|
129
|
+
self.selection_data,
|
|
130
|
+
],
|
|
131
|
+
# style=FLEXBOX_STYLE_ALLOW_VERTICAL_FILL,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
self.layout = self.graph_card
|
|
135
|
+
|
|
136
|
+
def setup_callback(self):
|
|
137
|
+
# setup callback of subpages
|
|
138
|
+
super().setup_callback()
|
|
139
|
+
|
|
140
|
+
app = self.application.app
|
|
141
|
+
|
|
142
|
+
# ===== Update Graph =====
|
|
143
|
+
@app.callback(
|
|
144
|
+
Output(self.graph.id, 'figure'),
|
|
145
|
+
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.
|
|
146
|
+
Input(self.tabs.id, 'active_tab'),
|
|
147
|
+
Input(self.dummy, 'children'),
|
|
148
|
+
prevent_initial_call=False,)
|
|
149
|
+
def redraw_main_graph(active_tab_id, _):
|
|
150
|
+
logger.debug('====================')
|
|
151
|
+
logger.debug(f'redraw_main_graph called by {callback_context.triggered_id}')
|
|
152
|
+
figure, length = self.get_fig_by_tab_id(active_tab_id, with_length=True)
|
|
153
|
+
return figure, length
|
|
154
|
+
|
|
155
|
+
# ===== Save Selection =====
|
|
156
|
+
@app.callback(
|
|
157
|
+
Output(self.selection_data.id, self.selection_data_property),
|
|
158
|
+
Input(self.graph.id, 'selectedData'))
|
|
159
|
+
def save_selection(data):
|
|
160
|
+
return data
|
|
161
|
+
|
|
162
|
+
# ===== Show Image and Parameter on Hover =====
|
|
163
|
+
@app.callback(
|
|
164
|
+
Output(self.tooltip.id, "show"),
|
|
165
|
+
Output(self.tooltip.id, "bbox"),
|
|
166
|
+
Output(self.tooltip.id, "children"),
|
|
167
|
+
Input(self.graph.id, "hoverData"),)
|
|
168
|
+
def show_hover(hoverData):
|
|
169
|
+
if hoverData is None:
|
|
170
|
+
return False, no_update, no_update
|
|
171
|
+
|
|
172
|
+
if self.application.history is None:
|
|
173
|
+
raise PreventUpdate
|
|
174
|
+
|
|
175
|
+
# get hover location
|
|
176
|
+
pt = hoverData["points"][0]
|
|
177
|
+
bbox = pt["bbox"]
|
|
178
|
+
|
|
179
|
+
# get row of the history from customdata defined in main_figure
|
|
180
|
+
trial = pt['customdata'][0]
|
|
181
|
+
|
|
182
|
+
df = self.data_accessor()
|
|
183
|
+
row = df[df['trial'] == trial]
|
|
184
|
+
|
|
185
|
+
# create component
|
|
186
|
+
title_component = html.H3(f"trial{trial}", style={"color": "darkblue"})
|
|
187
|
+
img_component = self.create_image_content_if_femtet(trial)
|
|
188
|
+
tbl_component = self.create_formatted_parameter(row)
|
|
189
|
+
|
|
190
|
+
# create layout
|
|
191
|
+
description = html.Div([
|
|
192
|
+
title_component,
|
|
193
|
+
tbl_component,
|
|
194
|
+
])
|
|
195
|
+
tooltip_layout = html.Div([
|
|
196
|
+
html.Div(img_component, style={'display': 'inline-block', 'margin-right': '10px', 'vertical-align': 'top'}),
|
|
197
|
+
html.Div(description, style={'display': 'inline-block', 'margin-right': '10px'})
|
|
198
|
+
])
|
|
199
|
+
|
|
200
|
+
return True, bbox, tooltip_layout
|
|
201
|
+
|
|
202
|
+
def create_formatted_parameter(self, row) -> Component:
|
|
203
|
+
metadata = self.application.history.metadata
|
|
204
|
+
pd.options.display.float_format = '{:.4e}'.format
|
|
205
|
+
parameters = row.iloc[:, np.where(np.array(metadata) == 'prm')[0]]
|
|
206
|
+
names = parameters.columns
|
|
207
|
+
values = [f'{value:.3e}' for value in parameters.values.ravel()]
|
|
208
|
+
data = pd.DataFrame(dict(
|
|
209
|
+
name=names, value=values
|
|
210
|
+
))
|
|
211
|
+
table = dash_table.DataTable(
|
|
212
|
+
columns=[{'name': col, 'id': col} for col in data.columns],
|
|
213
|
+
data=data.to_dict('records')
|
|
214
|
+
)
|
|
215
|
+
return table
|
|
216
|
+
|
|
217
|
+
def create_image_content_if_femtet(self, trial) -> Component:
|
|
218
|
+
img_url = None
|
|
219
|
+
metadata = self.application.history.metadata
|
|
220
|
+
if metadata[0] != '':
|
|
221
|
+
# get img path
|
|
222
|
+
d = json.loads(metadata[0])
|
|
223
|
+
femprj_path = d['femprj_path']
|
|
224
|
+
model_name = d['model_name']
|
|
225
|
+
femprj_result_dir = femprj_path.replace('.femprj', '.Results')
|
|
226
|
+
img_path = os.path.join(femprj_result_dir, f'{model_name}_trial{trial}.jpg')
|
|
227
|
+
if os.path.exists(img_path):
|
|
228
|
+
# create encoded image
|
|
229
|
+
with open(img_path, 'rb') as f:
|
|
230
|
+
content = f.read()
|
|
231
|
+
encoded_image = base64.b64encode(content).decode('utf-8')
|
|
232
|
+
img_url = 'data:image/jpeg;base64, ' + encoded_image
|
|
233
|
+
return html.Img(src=img_url, style={"width": "200px"}) if img_url is not None else html.Div()
|
|
234
|
+
|
|
235
|
+
def get_fig_by_tab_id(self, tab_id, with_length=False):
|
|
236
|
+
# If the history is not loaded, do nothing
|
|
237
|
+
if self.application.history is None:
|
|
238
|
+
raise PreventUpdate
|
|
239
|
+
|
|
240
|
+
# else, get creator by tab_id
|
|
241
|
+
if tab_id == 'default':
|
|
242
|
+
creator = self.figure_creators[0]['creator']
|
|
243
|
+
else:
|
|
244
|
+
creators = [d['creator'] for d in self.figure_creators if d['tab_id'] == tab_id]
|
|
245
|
+
if len(creators) == 0:
|
|
246
|
+
raise PreventUpdate
|
|
247
|
+
creator = creators[0]
|
|
248
|
+
|
|
249
|
+
# create figure
|
|
250
|
+
df = self.data_accessor()
|
|
251
|
+
fig = creator(self.application.history, df)
|
|
252
|
+
if with_length:
|
|
253
|
+
return fig, len(df)
|
|
254
|
+
else:
|
|
255
|
+
return fig
|
|
256
|
+
|
|
257
|
+
def data_accessor(self) -> pd.DataFrame:
|
|
258
|
+
from pyfemtet.opt.visualization.process_monitor.application import ProcessMonitorApplication
|
|
259
|
+
if isinstance(self.application, ProcessMonitorApplication):
|
|
260
|
+
df = self.application.local_data
|
|
261
|
+
else:
|
|
262
|
+
df = self.application.history.local_data
|
|
263
|
+
return df
|
|
File without changes
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from time import sleep
|
|
3
|
+
from threading import Thread
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from pyfemtet.opt.visualization.base import PyFemtetApplicationBase, logger
|
|
8
|
+
from pyfemtet.opt.visualization.process_monitor.pages import HomePage, WorkerPage
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ProcessMonitorApplication(PyFemtetApplicationBase):
|
|
12
|
+
""""""
|
|
13
|
+
"""
|
|
14
|
+
+------+--------+
|
|
15
|
+
| side | con- |
|
|
16
|
+
| bar | tent |
|
|
17
|
+
+--^---+--^-----+
|
|
18
|
+
│ └─ pages (dict(href: str = layout: Component))
|
|
19
|
+
└──────── nav_links (dict(order: float) = NavLink)
|
|
20
|
+
|
|
21
|
+
Accessible members:
|
|
22
|
+
- history: History
|
|
23
|
+
└ local_df: pd.DataFrame
|
|
24
|
+
- app: Dash
|
|
25
|
+
- local_entire_status_int: int <----------------> femopt.statue: OptimizationStatus(Actor)
|
|
26
|
+
- local_worker_status_int_list: List[int] <-----> femopt.opt.statue: List[OptimizationStatus(Actor)]
|
|
27
|
+
^
|
|
28
|
+
|
|
|
29
|
+
sync "while" statement in start_server()
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
DEFAULT_PORT = 8080
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
history,
|
|
37
|
+
status,
|
|
38
|
+
worker_addresses,
|
|
39
|
+
worker_status_list,
|
|
40
|
+
is_debug=False,
|
|
41
|
+
):
|
|
42
|
+
super().__init__(
|
|
43
|
+
title='PyFemtet Monitor',
|
|
44
|
+
subtitle='visualize optimization progress',
|
|
45
|
+
history=history,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
self._should_get_actor_data = False
|
|
49
|
+
self.is_debug = is_debug
|
|
50
|
+
|
|
51
|
+
# type hint (avoid circular import)
|
|
52
|
+
from pyfemtet.opt._femopt_core import OptimizationStatus
|
|
53
|
+
|
|
54
|
+
# register arguments
|
|
55
|
+
self.worker_addresses: List[str] = worker_addresses
|
|
56
|
+
self.entire_status: OptimizationStatus = status # include actor
|
|
57
|
+
self.worker_status_list: List[OptimizationStatus] = worker_status_list # include actor
|
|
58
|
+
|
|
59
|
+
# initialize local members (from actors)
|
|
60
|
+
self._df: pd.DataFrame = self.local_data
|
|
61
|
+
self.local_entire_status_int: int = self.entire_status.get()
|
|
62
|
+
self.local_worker_status_int_list: List[int] = [s.get() for s in self.worker_status_list]
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def local_data(self) -> pd.DataFrame:
|
|
66
|
+
if self._should_get_actor_data:
|
|
67
|
+
return self._df
|
|
68
|
+
else:
|
|
69
|
+
return self.history.local_data
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@local_data.setter
|
|
73
|
+
def local_data(self, value: pd.DataFrame):
|
|
74
|
+
if self._should_get_actor_data:
|
|
75
|
+
raise NotImplementedError('If should_get_actor_data, ProcessMonitorApplication.local_df is read_only.')
|
|
76
|
+
else:
|
|
77
|
+
self.history.local_data = value
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def setup_callback(self, debug=False):
|
|
81
|
+
if not debug:
|
|
82
|
+
super().setup_callback()
|
|
83
|
+
|
|
84
|
+
def start_server(self, host=None, port=None):
|
|
85
|
+
"""Callback の中で使いたい Actor のデータを Application クラスのメンバーとやり取りしつつ、server を落とす関数"""
|
|
86
|
+
|
|
87
|
+
self._should_get_actor_data = True
|
|
88
|
+
|
|
89
|
+
# avoid circular import
|
|
90
|
+
from pyfemtet.opt._femopt_core import OptimizationStatus
|
|
91
|
+
|
|
92
|
+
host = host or 'localhost'
|
|
93
|
+
port = port or self.DEFAULT_PORT
|
|
94
|
+
|
|
95
|
+
# dash app server を daemon thread で起動
|
|
96
|
+
server_thread = Thread(
|
|
97
|
+
target=self.run,
|
|
98
|
+
args=(host, port,),
|
|
99
|
+
daemon=True,
|
|
100
|
+
)
|
|
101
|
+
server_thread.start()
|
|
102
|
+
|
|
103
|
+
# dash app (=flask server) の callback で dask の actor にアクセスすると
|
|
104
|
+
# おかしくなることがあるので、ここで必要な情報のみやり取りする
|
|
105
|
+
while True:
|
|
106
|
+
# running 以前に monitor で current status を interrupting にしていれば actor に反映
|
|
107
|
+
if (
|
|
108
|
+
(self.entire_status.get() <= OptimizationStatus.RUNNING) # メインプロセスが RUNNING 以前である
|
|
109
|
+
and (self.local_entire_status_int == OptimizationStatus.INTERRUPTING) # Application で status を INTERRUPT にした
|
|
110
|
+
):
|
|
111
|
+
self.entire_status.set(OptimizationStatus.INTERRUPTING)
|
|
112
|
+
for worker_status in self.worker_status_list:
|
|
113
|
+
worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
114
|
+
|
|
115
|
+
# status と df を actor から application に反映する
|
|
116
|
+
self._df = self.history.actor_data.copy()
|
|
117
|
+
self.local_entire_status_int = self.entire_status.get()
|
|
118
|
+
self.local_worker_status_int_list = [s.get() for s in self.worker_status_list]
|
|
119
|
+
|
|
120
|
+
# terminate_all 指令があれば flask server をホストするプロセスごと終了する
|
|
121
|
+
if self.entire_status.get() >= OptimizationStatus.TERMINATE_ALL:
|
|
122
|
+
return 0 # take server down with me
|
|
123
|
+
|
|
124
|
+
# interval
|
|
125
|
+
sleep(1)
|
|
126
|
+
|
|
127
|
+
def get_status_color(self, status_int):
|
|
128
|
+
from pyfemtet.opt._femopt_core import OptimizationStatus
|
|
129
|
+
# set color
|
|
130
|
+
if status_int <= OptimizationStatus.SETTING_UP:
|
|
131
|
+
color = 'secondary'
|
|
132
|
+
elif status_int <= OptimizationStatus.WAIT_OTHER_WORKERS:
|
|
133
|
+
color = 'primary'
|
|
134
|
+
elif status_int <= OptimizationStatus.RUNNING:
|
|
135
|
+
color = 'primary'
|
|
136
|
+
elif status_int <= OptimizationStatus.INTERRUPTING:
|
|
137
|
+
color = 'warning'
|
|
138
|
+
elif status_int <= OptimizationStatus.TERMINATE_ALL:
|
|
139
|
+
color = 'dark'
|
|
140
|
+
elif status_int <= OptimizationStatus.CRASHED:
|
|
141
|
+
color = 'danger'
|
|
142
|
+
else:
|
|
143
|
+
color = 'danger'
|
|
144
|
+
return color
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def g_debug():
|
|
148
|
+
import os
|
|
149
|
+
os.chdir(os.path.dirname(__file__))
|
|
150
|
+
|
|
151
|
+
from pyfemtet.opt._femopt_core import History, OptimizationStatus
|
|
152
|
+
|
|
153
|
+
class _OS(OptimizationStatus):
|
|
154
|
+
|
|
155
|
+
# noinspection PyMissingConstructor
|
|
156
|
+
def __init__(self, name):
|
|
157
|
+
self.name = name
|
|
158
|
+
self.st = self.INITIALIZING
|
|
159
|
+
|
|
160
|
+
def set(self, status_const):
|
|
161
|
+
self.st = status_const
|
|
162
|
+
|
|
163
|
+
def get(self) -> int:
|
|
164
|
+
return self.st
|
|
165
|
+
|
|
166
|
+
def get_text(self) -> int:
|
|
167
|
+
return self.const_to_str(self.st)
|
|
168
|
+
|
|
169
|
+
g_application = ProcessMonitorApplication(
|
|
170
|
+
history=History(history_path=os.path.join(os.path.dirname(__file__), '..', 'result_viewer', 'sample', 'sample.csv')),
|
|
171
|
+
status=_OS('entire'),
|
|
172
|
+
worker_addresses=['worker1', 'worker2', 'worker3'],
|
|
173
|
+
worker_status_list=[_OS('worker1'), _OS('worker2'), _OS('worker3')],
|
|
174
|
+
is_debug=True,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
g_home_page = HomePage('Progress')
|
|
178
|
+
g_worker_page = WorkerPage('Workers', '/workers', g_application)
|
|
179
|
+
|
|
180
|
+
g_application.add_page(g_home_page, 0)
|
|
181
|
+
g_application.add_page(g_worker_page, 1)
|
|
182
|
+
g_application.setup_callback(debug=False)
|
|
183
|
+
|
|
184
|
+
g_application.run(debug=True)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def main(history, status, worker_addresses, worker_status_list, host=None, port=None):
|
|
188
|
+
g_application = ProcessMonitorApplication(history, status, worker_addresses, worker_status_list)
|
|
189
|
+
|
|
190
|
+
g_home_page = HomePage('Progress')
|
|
191
|
+
g_worker_page = WorkerPage('Workers', '/workers', g_application)
|
|
192
|
+
|
|
193
|
+
g_application.add_page(g_home_page, 0)
|
|
194
|
+
g_application.add_page(g_worker_page, 1)
|
|
195
|
+
g_application.setup_callback()
|
|
196
|
+
|
|
197
|
+
g_application.start_server(host, port)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == '__main__':
|
|
201
|
+
g_debug()
|