PyFemtet 0.3.2__tar.gz → 0.3.4__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.
Files changed (26) hide show
  1. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/PKG-INFO +2 -1
  2. pyfemtet-0.3.4/pyfemtet/__init__.py +1 -0
  3. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/dispatch_extensions.py +49 -46
  4. pyfemtet-0.3.4/pyfemtet/logger.py +101 -0
  5. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/opt/base.py +236 -167
  6. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/opt/interface.py +23 -19
  7. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyproject.toml +2 -1
  8. pyfemtet-0.3.2/pyfemtet/__init__.py +0 -1
  9. pyfemtet-0.3.2/pyfemtet/opt/_optimizer.py +0 -241
  10. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/LICENSE +0 -0
  11. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/README.md +0 -0
  12. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
  13. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
  14. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +0 -0
  15. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
  16. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +0 -0
  17. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
  18. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/her_ex40_parametric.py +0 -0
  19. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +0 -0
  20. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
  21. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +0 -0
  22. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/_old/_SW_Femtet.py +0 -0
  23. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/core.py +0 -0
  24. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/opt/_FemtetWithNX/update_model.py +0 -0
  25. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/opt/__init__.py +0 -0
  26. {pyfemtet-0.3.2 → pyfemtet-0.3.4}/pyfemtet/opt/monitor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyfemtet
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Design parameter optimization using Femtet.
5
5
  Home-page: https://github.com/pyfemtet/pyfemtet
6
6
  License: BSD-3-Clause
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Requires-Dist: botorch (>=0.9.5,<0.10.0) ; python_version < "3.12"
17
+ Requires-Dist: colorlog (>=6.8.0,<7.0.0)
17
18
  Requires-Dist: dash (>=2.14.2,<3.0.0)
18
19
  Requires-Dist: dash-bootstrap-components (>=1.5.0,<2.0.0)
19
20
  Requires-Dist: dask (>=2023.12.1,<2024.0.0)
@@ -0,0 +1 @@
1
+ __version__ = "0.3.4"
@@ -3,9 +3,8 @@ from typing import Tuple
3
3
  import os
4
4
  from time import time, sleep
5
5
  from multiprocessing import current_process
6
- import logging
7
- import warnings
8
6
 
7
+ from tqdm import tqdm
9
8
  import psutil
10
9
  from dask.distributed import Lock
11
10
 
@@ -17,16 +16,15 @@ from femtetutils import util
17
16
 
18
17
  from multiprocessing.context import BaseContext, SpawnProcess, _concrete_contexts
19
18
  from multiprocessing.process import _children, _cleanup
19
+ from multiprocessing.managers import SyncManager
20
20
 
21
-
22
- logger = logging.getLogger('dispatch_log')
23
- handler = logging.StreamHandler()
24
- handler.setFormatter(logging.Formatter('[%(process)d] %(message)s'))
25
- logger.addHandler(handler)
21
+ import logging
22
+ from .logger import get_logger
23
+ logger = get_logger('dispatch')
26
24
  logger.setLevel(logging.INFO)
27
25
 
28
26
 
29
- DISPATCH_TIMEOUT = 60
27
+ DISPATCH_TIMEOUT = 120
30
28
 
31
29
 
32
30
  class DispatchExtensionException(Exception):
@@ -114,11 +112,6 @@ _concrete_contexts.update(
114
112
  )
115
113
 
116
114
 
117
- def _log_prefix():
118
- prefix = current_process().name + ' : '
119
- return prefix
120
-
121
-
122
115
  def _get_hwnds(pid):
123
116
  """Proces ID から window handle を取得します."""
124
117
  def callback(hwnd, _hwnds):
@@ -147,6 +140,10 @@ def _get_pids(process_name):
147
140
  return pids
148
141
 
149
142
 
143
+ def _get_subprocess_log_prefix():
144
+ return f'({current_process().name}) '
145
+
146
+
150
147
  def launch_and_dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
151
148
  """Launch Femtet by new process and connect to it.
152
149
 
@@ -160,16 +157,16 @@ def launch_and_dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
160
157
  Returns:
161
158
  Tuple[IFemtet, int]:
162
159
  """
163
-
164
160
  util.execute_femtet()
165
161
  pid = util.get_last_executed_femtet_process_id()
166
162
  logger.debug(f'Target pid is {pid}.')
167
- sleep(5)
163
+ for i in tqdm(range(5), 'wait for launch femtet...'):
164
+ sleep(1)
168
165
  Femtet, pid = dispatch_specific_femtet(pid, timeout)
169
166
  return Femtet, pid
170
167
 
171
168
 
172
- def dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
169
+ def dispatch_femtet(timeout=DISPATCH_TIMEOUT, subprocess_log_prefix='') -> Tuple[IFemtet, int]:
173
170
  """Connect to existing Femtet process.
174
171
 
175
172
  Args:
@@ -181,11 +178,13 @@ def dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
181
178
  Returns:
182
179
  Tuple[IFemtet, int]:
183
180
  """
184
-
185
181
  # Dispatch
186
- logger.debug(f'{_log_prefix()}Try to connect Femtet.')
182
+ if subprocess_log_prefix:
183
+ logger.debug('%s'+'Try to connect Femtet.', subprocess_log_prefix)
184
+ else:
185
+ logger.info('Try to connect Femtet.')
187
186
  Femtet = Dispatch('FemtetMacro.Femtet')
188
- logger.debug(f'{_log_prefix()}Dispatched.')
187
+ logger.debug('%s'+'Dispatch finished.', subprocess_log_prefix)
189
188
 
190
189
  # timeout 秒以内に接続が確立するか
191
190
  start = time()
@@ -194,27 +193,28 @@ def dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
194
193
 
195
194
  # 接続が確立
196
195
  if hwnd > 0:
197
- logger.debug(f'{_log_prefix()}Current hwnd is {hwnd}.')
196
+ logger.debug('%s'+f'Dispatched hwnd is {hwnd} and its pid is {_get_pid(hwnd)}. Connection established.', subprocess_log_prefix)
198
197
  break
199
198
  else:
200
- logger.debug(f'{_log_prefix()}Current hwnd is {hwnd}. Wait for established connection.')
199
+ logger.debug('%s'+f'Dispatched hwnd is {hwnd}. Waiting for establishing connection.', subprocess_log_prefix)
201
200
 
202
201
  # 接続がタイムアウト
203
202
  if time()-start > timeout:
204
- raise FemtetConnectionTimeoutError(f'Connect trial with Femtet is timed out in {timeout} sec')
203
+ raise FemtetConnectionTimeoutError(f'Connection trial with Femtet is timed out in {timeout} sec')
205
204
 
206
205
  sleep(1)
207
206
 
208
207
  # pid を取得
209
208
  pid = _get_pid(hwnd)
210
- logger.info(f'{_log_prefix()}hwnd is {hwnd} and pid is {pid} of connected Femtet.')
211
-
212
- if ('MainProcess' in _log_prefix()) or 'Dask Worker Process' in _log_prefix():
213
- print(f'INFO: Process id of connected Femtet = {pid}')
209
+ if subprocess_log_prefix:
210
+ logger.debug('%s'+f'Successfully connected. The pid of Femtet is {pid}.', subprocess_log_prefix)
211
+ else:
212
+ logger.info(f'Successfully connected. The pid of Femtet is {pid}.')
214
213
 
215
214
  return Femtet, pid
216
215
 
217
216
 
217
+
218
218
  def _block_other_femtets(
219
219
  target_pid,
220
220
  subprocess_idx,
@@ -239,9 +239,10 @@ def _block_other_femtets(
239
239
  else:
240
240
  lock_inter_subproc.acquire()
241
241
  try:
242
- logger.debug(f'{_log_prefix()}Try to block Femtet.')
243
- _, my_pid = dispatch_femtet(2)
242
+ logger.debug('%s'+f'Subprocess started.', _get_subprocess_log_prefix())
243
+ _, my_pid = dispatch_femtet(2, _get_subprocess_log_prefix())
244
244
  except DispatchExtensionException:
245
+ logger.debug('%s'+f'Connection failed. The other subprocesses will skip.', _get_subprocess_log_prefix())
245
246
  my_pid = 0
246
247
  # ひとつでもここに来れば、それ以降は探しても無駄なのでスキップフラグを立てる
247
248
  for i in range(len(connection_flags)-1):
@@ -251,8 +252,6 @@ def _block_other_femtets(
251
252
 
252
253
  # # target なら Dispatch の終了を通知する前に main を lock する
253
254
  # if my_pid == target_pid:
254
- # logger.info(f'{_log_prefix()}Connected to {my_pid}. This is the target.')
255
- # logger.info(f'{_log_prefix()}Start to block main process.')
256
255
  # lock_main.acquire()
257
256
 
258
257
  # Dispatch の終了を通知
@@ -266,29 +265,30 @@ def _block_other_femtets(
266
265
  # if all(connection_flags[:-1]):
267
266
  # break
268
267
  # if time()-start > timeout_main_wait_for_us:
269
- # logger.error(f'{_log_prefix()}Timed out while waiting for other subprocesses.')
270
268
  # return -1
271
269
  # sleep(1)
272
- # logger.debug(f'{_log_prefix()}All subprocesses seems to finish connection. Release {my_pid} and main.')
273
- logger.debug(f'{_log_prefix()}Release {my_pid}.')
270
+ logger.debug('%s'+f'Release {my_pid}.', _get_subprocess_log_prefix())
274
271
  # lock_main.release()
275
272
  return 0
276
273
 
277
274
  # my_pid が非正なら何にも関与しないので即刻終了する
278
275
  elif my_pid <= 0:
279
- logger.debug(f'{_log_prefix()}Failed or timed out to connect Femtet. Exit immediately.')
276
+ logger.debug('%s'+'Failed or timed out to connect Femtet.', _get_subprocess_log_prefix())
280
277
  return 1
281
278
 
282
279
  # そうでなければメインプロセスが Dispatch を終えるまで解放しない
283
280
  else:
284
- logger.debug(f'{_log_prefix()}Connected to {my_pid}. Keep connection to block.')
281
+ logger.debug('%s'+f'Connected to {my_pid}. Keep connection to block.', _get_subprocess_log_prefix())
285
282
  start = time()
286
283
  while True:
287
284
  if connection_flags[-1]:
288
- logger.debug(f'{_log_prefix()}Main process seems to connect target Femtet. Exit immediately.')
285
+ logger.debug('%s'+'Main process seems to connect target Femtet.', _get_subprocess_log_prefix())
289
286
  return 1
290
287
  if time()-start > timeout_main_wait_for_us:
291
- logger.error(f'{_log_prefix()}Timed out while waiting for the main process.')
288
+ logger.warning(
289
+ '%s'+f'Timed out to wait for the main process to {timeout_main_wait_for_us}',
290
+ _get_subprocess_log_prefix()
291
+ )
292
292
  return -1
293
293
  sleep(1)
294
294
 
@@ -379,7 +379,6 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
379
379
  Returns:
380
380
  Tuple[IFemtet, int]:
381
381
  """
382
-
383
382
  # TODO: 安定性を見て lock_main を復活させるか決める
384
383
 
385
384
  if timeout < 5:
@@ -387,15 +386,15 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
387
386
 
388
387
  # 存在する Femtet プロセスの列挙
389
388
  pids = _get_pids('Femtet.exe')
390
- logger.info(f'{_log_prefix()}PID of existing Femtet processes: {pids}')
389
+ logger.info(f'PID of existing Femtet processes: {pids}')
391
390
  if not (pid in pids):
392
391
  raise FemtetNotFoundException(f"Femtet (pid = {pid}) doesn't exist.")
393
392
 
393
+ logger.info('Searching specific Femtet...')
394
+
394
395
  # 子プロセスの準備
395
396
  # with Manager() as manager:
396
- start = time()
397
- from multiprocessing.managers import SyncManager
398
- m = SyncManager(ctx=_NestableSpawnContext())
397
+ m = SyncManager(ctx=_NestableSpawnContext()) # このへんが時間のかかる処理
399
398
  m.start()
400
399
  with m as manager:
401
400
  # フラグの準備
@@ -406,9 +405,11 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
406
405
  connection_flags.append(False)
407
406
 
408
407
  # 目的以外の Femtet をブロックする子プロセスを開始
408
+ logger.debug('Start subprocess to block Femtet other than target pid.')
409
409
  processes = []
410
- for subprocess_id in range(len(pids)):
410
+ for subprocess_id in tqdm(range(len(pids)), 'Specifying connection...'):
411
411
  p = _NestableSpawnProcess(
412
+ # p = Process(
412
413
  target=_block_other_femtets,
413
414
  args=(
414
415
  pid,
@@ -423,6 +424,7 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
423
424
  processes.append(p)
424
425
 
425
426
  # 子プロセスの Dispatch 完了を待つ
427
+ start = time()
426
428
  while True:
427
429
  if all(connection_flags[:-1]):
428
430
  break
@@ -443,7 +445,7 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
443
445
 
444
446
  # 子プロセスによるブロックが終了しているので Dispatch する
445
447
  try:
446
- logger.debug(f'{_log_prefix()}All Femtets except to target seem to be blocked. Try Dispatch.')
448
+ logger.debug('Block process seems to be finished. Try Dispatch.')
447
449
  Femtet, my_pid = dispatch_femtet()
448
450
  except DispatchExtensionException as e:
449
451
  # lock_main.release()
@@ -458,8 +460,9 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
458
460
 
459
461
  if my_pid != pid: # pid の結果が違う場合
460
462
  txt = f'Target pid is {pid}, but connected pid is {my_pid}. '
461
- txt += f'The common reason is that this python process once connected {pid}.'
462
- warnings.warn(txt)
463
+ txt += f'The common reason is that THIS python process once connected {pid}.'
464
+ logger.warn(txt)
465
+ # warnings.warn(txt)
463
466
 
464
467
  return Femtet, my_pid
465
468
 
@@ -0,0 +1,101 @@
1
+ import os
2
+ import sys
3
+ import logging
4
+ from colorlog import ColoredFormatter
5
+ from dask.distributed import get_worker
6
+
7
+
8
+ __all__ = ['get_logger']
9
+
10
+
11
+ def _get_worker_name_as_prefix():
12
+ name = '(Main) '
13
+ try:
14
+ worker = get_worker()
15
+ if isinstance(worker.name, str): # local なら index, cluster なら tcp address
16
+ name = f'({worker.name}) '
17
+ else:
18
+ name = f'(Sub{worker.name}) '
19
+ except ValueError:
20
+ pass
21
+ return name
22
+
23
+
24
+ class DaskLogRecord(logging.LogRecord):
25
+ """Generate a log message with dask worker name."""
26
+
27
+ # def __init__(self, *args, **kwargs):
28
+ # super().__init__(*args, **kwargs)
29
+ # self.worker = _get_worker_name_as_prefix()
30
+
31
+ def getMessage(self):
32
+ """Add worker name to loggin message.
33
+
34
+ This function is originated from logging.LogRecord.
35
+
36
+ # Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved.
37
+
38
+ """
39
+ msg = str(self.msg)
40
+ if self.args:
41
+ msg = msg % self.args
42
+ msg = _get_worker_name_as_prefix() + msg
43
+ return msg
44
+
45
+
46
+ logging.setLogRecordFactory(DaskLogRecord) # すべての logging %(message)s の前に prefix を入れる
47
+
48
+
49
+ def _color_supported() -> bool:
50
+ """Detection of color support.
51
+
52
+ This function is originated from optuna.logging.
53
+
54
+ # Copyright (c) 2018 Preferred Networks, Inc.
55
+
56
+ """
57
+
58
+ # NO_COLOR environment variable:
59
+ if os.environ.get("NO_COLOR", None):
60
+ return False
61
+
62
+ if not hasattr(sys.stderr, "isatty") or not sys.stderr.isatty():
63
+ return False
64
+ else:
65
+ return True
66
+
67
+
68
+ def _create_formatter() -> logging.Formatter:
69
+ """Create a formatter."""
70
+ # header = f"[pyfemtet %(name)s] %(levelname).4s %(worker)s]"
71
+ header = f"[pyfemtet %(name)s %(levelname).4s]"
72
+ message = "%(message)s"
73
+
74
+ formatter = ColoredFormatter(
75
+ f"%(log_color)s{header}%(reset)s {message}",
76
+ datefmt=None,
77
+ reset=True,
78
+ log_colors={
79
+ "DEBUG": "purple",
80
+ "INFO": "cyan",
81
+ "WARNING": "yellow",
82
+ "ERROR": "light_red",
83
+ "CRITICAL": "red",
84
+ },
85
+ )
86
+
87
+ return formatter
88
+
89
+
90
+ def get_logger(logger_name):
91
+ """Return a logger with a default ColoredFormatter."""
92
+
93
+ formatter = _create_formatter()
94
+
95
+ logger = logging.getLogger(logger_name)
96
+ handler = logging.StreamHandler()
97
+ handler.setFormatter(formatter)
98
+ logger.addHandler(handler)
99
+ logger.setLevel(logging.DEBUG)
100
+
101
+ return logger