pyfemtet 0.3.4__tar.gz → 0.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (25) hide show
  1. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/PKG-INFO +1 -1
  2. pyfemtet-0.3.6/pyfemtet/__init__.py +1 -0
  3. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/dispatch_extensions.py +18 -3
  4. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/opt/base.py +13 -7
  5. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/opt/interface.py +8 -3
  6. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/opt/monitor.py +80 -7
  7. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyproject.toml +1 -1
  8. pyfemtet-0.3.4/pyfemtet/__init__.py +0 -1
  9. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/LICENSE +0 -0
  10. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/README.md +0 -0
  11. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
  12. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
  13. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +0 -0
  14. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
  15. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +0 -0
  16. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
  17. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/her_ex40_parametric.py +0 -0
  18. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +0 -0
  19. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
  20. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +0 -0
  21. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/_old/_SW_Femtet.py +0 -0
  22. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/core.py +0 -0
  23. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/logger.py +0 -0
  24. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/opt/_FemtetWithNX/update_model.py +0 -0
  25. {pyfemtet-0.3.4 → pyfemtet-0.3.6}/pyfemtet/opt/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyfemtet
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Design parameter optimization using Femtet.
5
5
  Home-page: https://github.com/pyfemtet/pyfemtet
6
6
  License: BSD-3-Clause
@@ -0,0 +1 @@
1
+ __version__ = "0.3.6"
@@ -144,11 +144,12 @@ def _get_subprocess_log_prefix():
144
144
  return f'({current_process().name}) '
145
145
 
146
146
 
147
- def launch_and_dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
147
+ def launch_and_dispatch_femtet(timeout=DISPATCH_TIMEOUT, strict_pid_specify=True) -> Tuple[IFemtet, int]:
148
148
  """Launch Femtet by new process and connect to it.
149
149
 
150
150
  Args:
151
151
  timeout (int or float, optional): Seconds to wait for connection. Defaults to DISPATCH_TIMEOUT.
152
+ strict_pid_specify (bool): If True, try to connect the launched femtet process strictly. If False, launch new process but try to connect any connectable femtet.
152
153
 
153
154
  Raises:
154
155
  FemtetNotFoundException: Launched Femtet is not found for some reason (i.e. failed to launch Femtet).
@@ -157,12 +158,26 @@ def launch_and_dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
157
158
  Returns:
158
159
  Tuple[IFemtet, int]:
159
160
  """
161
+ # launch femtet
160
162
  util.execute_femtet()
161
163
  pid = util.get_last_executed_femtet_process_id()
162
164
  logger.debug(f'Target pid is {pid}.')
163
- for i in tqdm(range(5), 'wait for launch femtet...'):
165
+ for _ in tqdm(range(5), 'wait for launch femtet...'):
164
166
  sleep(1)
165
- Femtet, pid = dispatch_specific_femtet(pid, timeout)
167
+
168
+ # dispatch femtet
169
+ if strict_pid_specify:
170
+ Femtet, pid = dispatch_specific_femtet(pid, timeout)
171
+ else:
172
+ # worker process なら排他処理する
173
+ try:
174
+ with Lock('simply-dispatch-femtet'):
175
+ Femtet, pid = dispatch_femtet()
176
+ except RuntimeError as e:
177
+ if "distributed.lock.Lock" in str(e):
178
+ Femtet, pid = dispatch_femtet()
179
+ else:
180
+ raise e
166
181
  return Femtet, pid
167
182
 
168
183
 
@@ -709,7 +709,7 @@ class AbstractOptimizer(ABC):
709
709
  subprocess_idx,
710
710
  worker_status_list,
711
711
  wait_setup,
712
- skip_set_fem,
712
+ skip_set_fem=False,
713
713
  ) -> None:
714
714
 
715
715
  # 自分の worker_status の取得
@@ -784,6 +784,7 @@ class OptunaOptimizer(AbstractOptimizer):
784
784
 
785
785
  # 中断の確認 (FAIL loop に陥る対策)
786
786
  if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
787
+ self.worker_status.set(OptimizationStatus.INTERRUPTING)
787
788
  trial.study.stop() # 現在実行中の trial を最後にする
788
789
  return None # set TrialState FAIL
789
790
 
@@ -825,6 +826,7 @@ class OptunaOptimizer(AbstractOptimizer):
825
826
 
826
827
  # 中断の確認 (解析中に interrupt されている場合対策)
827
828
  if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
829
+ self.worker_status.set(OptimizationStatus.INTERRUPTING)
828
830
  trial.study.stop() # 現在実行中の trial を最後にする
829
831
  return None # set TrialState FAIL
830
832
 
@@ -842,6 +844,7 @@ class OptunaOptimizer(AbstractOptimizer):
842
844
 
843
845
  # 中断の確認 (解析中に interrupt されている場合対策)
844
846
  if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
847
+ self.worker_status.set(OptimizationStatus.INTERRUPTING)
845
848
  trial.study.stop() # 現在実行中の trial を最後にする
846
849
  return None # set TrialState FAIL
847
850
 
@@ -1293,7 +1296,7 @@ class FEMOpt:
1293
1296
  if not self.opt.is_cluster:
1294
1297
  subprocess_indices = subprocess_indices[1:]
1295
1298
  worker_addresses = list(self.client.nthreads().keys())
1296
- assert len(subprocess_indices) <= len(worker_addresses), 'コア数が不足しています。'
1299
+ assert max(subprocess_indices) <= len(worker_addresses)-1, f'コア数{len(worker_addresses)}は不足しています。'
1297
1300
  worker_addresses = worker_addresses[:len(range(n_parallel))] # TODO: ノードごとに適度に振り分ける
1298
1301
  if not self.opt.is_cluster:
1299
1302
  worker_addresses[0] = 'Main'
@@ -1310,7 +1313,7 @@ class FEMOpt:
1310
1313
 
1311
1314
  # actor の設定
1312
1315
  self.status = OptimizationStatus(self.client)
1313
- self.worker_status_list = [OptimizationStatus(self.client, name) for name in worker_addresses]
1316
+ self.worker_status_list = [OptimizationStatus(self.client, name) for name in worker_addresses] # tqdm 検討
1314
1317
  self.status.set(OptimizationStatus.SETTING_UP)
1315
1318
  self.history = History(self.history_path, self.client)
1316
1319
  self.history.init(
@@ -1332,7 +1335,10 @@ class FEMOpt:
1332
1335
  # launch monitor
1333
1336
  self.monitor_process_future = self.client.submit(
1334
1337
  start_monitor_server_forever,
1335
- self.history, self.status, # args
1338
+ self.history,
1339
+ self.status,
1340
+ worker_addresses,
1341
+ self.worker_status_list,
1336
1342
  **self.monitor_server_kwargs, # kwargs
1337
1343
  workers=self.monitor_process_worker_name, # if invalid arg,
1338
1344
  allow_other_workers=False
@@ -1446,7 +1452,7 @@ class FEMOpt:
1446
1452
  sleep(3)
1447
1453
 
1448
1454
 
1449
- def start_monitor_server_forever(history, status, host='localhost', port=8080):
1450
- monitor = Monitor(history, status)
1451
- monitor.start_server(host, port)
1455
+ def start_monitor_server_forever(history, status, worker_addresses, worker_status_list, host='localhost', port=8080):
1456
+ monitor = Monitor(history, status, worker_addresses, worker_status_list)
1457
+ monitor.start_server(worker_addresses, worker_status_list, host, port)
1452
1458
  return 'Exit monitor server process gracefully'
@@ -9,7 +9,7 @@ import pandas as pd
9
9
  import psutil
10
10
  from pywintypes import com_error
11
11
  from pythoncom import CoInitialize, CoUninitialize
12
- from win32com.client import constants
12
+ from win32com.client import constants, Dispatch
13
13
  from dask.distributed import get_worker
14
14
  from femtetutils import util
15
15
 
@@ -119,6 +119,7 @@ class FemtetInterface(FEMInterface):
119
119
  femprj_path=None,
120
120
  model_name=None,
121
121
  connect_method='auto',
122
+ strict_pid_specify=False,
122
123
  **kwargs
123
124
  ):
124
125
  """Initializes the FemtetInterface.
@@ -144,12 +145,13 @@ class FemtetInterface(FEMInterface):
144
145
  self.model_name = model_name
145
146
  self.connect_method = connect_method
146
147
 
147
- # その他のメンバーの宣言や初期化初期化
148
+ # その他のメンバーの宣言や初期化
148
149
  self.Femtet = None
149
150
  self.quit_when_destruct = False
150
151
  self.connected_method = 'unconnected'
151
152
  self.parameters = None
152
153
  self.max_api_retry = 3
154
+ self.strict_pid_specify = strict_pid_specify
153
155
 
154
156
  # dask サブプロセスのときは femprj を更新し connect_method を new にする
155
157
  try:
@@ -198,9 +200,12 @@ class FemtetInterface(FEMInterface):
198
200
 
199
201
  def _connect_new_femtet(self):
200
202
  logger.info('└ Try to launch and connect new Femtet process.')
201
- self.Femtet, _ = launch_and_dispatch_femtet()
203
+
204
+ self.Femtet, _ = launch_and_dispatch_femtet(strict_pid_specify=self.strict_pid_specify)
205
+
202
206
  self.connected_method = 'new'
203
207
 
208
+
204
209
  def _connect_existing_femtet(self, pid: int or None = None):
205
210
  logger.info('└ Try to connect existing Femtet process.')
206
211
  # 既存の Femtet を探して Dispatch する。
@@ -76,7 +76,7 @@ def update_scatter_matrix(history, data):
76
76
  return fig
77
77
 
78
78
 
79
- def setup_home():
79
+ def create_home_layout():
80
80
  # components の設定
81
81
  # https://dash-bootstrap-components.opensource.faculty.ai/docs/components/accordion/
82
82
  dummy = html.Div('', id='dummy')
@@ -131,17 +131,57 @@ OptimizerBase.set_monitor_host() を実行してください。
131
131
  return layout
132
132
 
133
133
 
134
+ def create_worker_monitor_layout(
135
+ worker_addresses,
136
+ worker_status_int_list
137
+ ):
138
+ from .base import OptimizationStatus
139
+
140
+ interval = dcc.Interval(
141
+ id='worker-status-update-interval',
142
+ interval=1*1000, # in milliseconds
143
+ n_intervals=0,
144
+ )
145
+
146
+ rows = [interval]
147
+ for i, (worker_address, status_int) in enumerate(zip(worker_addresses, worker_status_int_list)):
148
+ status_msg = OptimizationStatus.const_to_str(status_int)
149
+ a = dbc.Alert(
150
+ children=[
151
+ f'({worker_address}) ',
152
+ html.P(
153
+ status_msg,
154
+ id=f'worker-status-msg-{i}'
155
+ ),
156
+ ],
157
+ id=f'worker-status-color-{i}',
158
+ color="primary",
159
+ )
160
+ rows.append(dbc.Row([dbc.Col(a)]))
161
+
162
+ layout = dbc.Container(
163
+ rows,
164
+ fluid=True
165
+ )
166
+
167
+ return layout
168
+
169
+
170
+
134
171
  class Monitor(object):
135
172
 
136
- def __init__(self, history, status):
173
+ def __init__(self, history, status, worker_addresses, worker_status_list):
137
174
 
138
175
  from .base import OptimizationStatus
139
176
 
140
177
  # 引数の処理
141
178
  self.history = history
142
179
  self.status = status
180
+
181
+ # メインスレッドで更新してもらうメンバー
143
182
  self.current_status_int = self.status.get()
144
183
  self.current_status = self.status.get_text()
184
+ self.current_worker_status_list = [s.get() for s in worker_status_list]
145
185
  self.df = self.history.actor_data.copy()
146
186
 
147
187
  # ログファイルの保存場所
@@ -153,7 +193,8 @@ class Monitor(object):
153
193
  self.app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
154
194
 
155
195
  # ページの components と layout の設定
156
- self.home = setup_home()
196
+ self.home = create_home_layout()
197
+ self.worker_monitor = create_worker_monitor_layout(worker_addresses, self.current_worker_status_list)
157
198
 
158
199
  # setup sidebar
159
200
  # https://dash-bootstrap-components.opensource.faculty.ai/examples/simple-sidebar/
@@ -186,7 +227,7 @@ class Monitor(object):
186
227
  dbc.Nav(
187
228
  [
188
229
  dbc.NavLink("Home", href="/", active="exact"),
189
- # dbc.NavLink("ペアプロット", href="/page-1", active="exact"),
230
+ dbc.NavLink("Workers", href="/page-1", active="exact"),
190
231
  ],
191
232
  vertical=True,
192
233
  pills=True,
@@ -202,8 +243,8 @@ class Monitor(object):
202
243
  def render_page_content(pathname):
203
244
  if pathname == "/": # p0
204
245
  return self.home
205
- # elif pathname == "/page-1":
206
- # return self.multi_pairplot_layout
246
+ elif pathname == "/page-1":
247
+ return self.worker_monitor
207
248
  # elif pathname == "/page-2":
208
249
  # return html.P("Oh cool, this is page 2!")
209
250
  # If the user tries to reach a different page, return a 404 message
@@ -297,10 +338,41 @@ class Monitor(object):
297
338
 
298
339
  return max_intervals, button_disable, button_disable, toggle_text, graph, status_children, status_color
299
340
 
341
+ # worker_monitor のための callback
342
+ @self.app.callback(
343
+ [Output(f'worker-status-msg-{i}', 'children') for i in range(len(worker_addresses))],
344
+ [Output(f'worker-status-color-{i}', 'color') for i in range(len(worker_addresses))],
345
+ [Input('worker-status-update-interval', 'n_intervals'),]
346
+ )
347
+ def update_worker_state(_):
348
+ msgs = [OptimizationStatus.const_to_str(i) for i in self.current_worker_status_list]
349
+
350
+ colors = []
351
+ for status_int in self.current_worker_status_list:
352
+ if status_int == OptimizationStatus.INTERRUPTING:
353
+ colors.append('warning')
354
+ elif status_int == OptimizationStatus.TERMINATED:
355
+ colors.append('dark')
356
+ elif status_int == OptimizationStatus.TERMINATE_ALL:
357
+ colors.append('dark')
358
+ else:
359
+ colors.append('primary')
360
+
361
+ ret = msgs
362
+ ret.extend(colors)
363
+
364
+ return tuple(ret)
365
+
300
366
  def _run_server_forever(self, app, host, port):
301
367
  app.run(debug=False, host=host, port=port)
302
368
 
303
- def start_server(self, host='localhost', port=8080):
369
+ def start_server(
370
+ self,
371
+ worker_addresses,
372
+ worker_status_list, # [actor]
373
+ host='localhost',
374
+ port=8080,
375
+ ):
304
376
 
305
377
  from .base import OptimizationStatus
306
378
 
@@ -339,6 +411,7 @@ class Monitor(object):
339
411
  self.current_status_int = self.status.get()
340
412
  self.current_status = self.status.get_text()
341
413
  self.df = self.history.actor_data.copy()
414
+ self.current_worker_status_list = [s.get() for s in worker_status_list]
342
415
 
343
416
  # terminate_all 指令があれば monitor server をホストするプロセスごと終了する
344
417
  if self.status.get() == OptimizationStatus.TERMINATE_ALL:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyfemtet"
3
- version = "0.3.4" # ignored by versioning plugin
3
+ version = "0.3.6" # ignored by versioning plugin
4
4
  description = "Design parameter optimization using Femtet."
5
5
  authors = ["kazuma.naito <kazuma.naito@murata.com>"]
6
6
  readme = "README.md"
@@ -1 +0,0 @@
1
- __version__ = "0.3.4"
File without changes
File without changes
File without changes
File without changes