PyFemtet 0.3.2__tar.gz → 0.3.3__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.
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/PKG-INFO +2 -1
- pyfemtet-0.3.3/pyfemtet/__init__.py +1 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/dispatch_extensions.py +40 -42
- pyfemtet-0.3.3/pyfemtet/logger.py +99 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/opt/base.py +46 -33
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/opt/interface.py +22 -18
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyproject.toml +2 -1
- pyfemtet-0.3.2/pyfemtet/__init__.py +0 -1
- pyfemtet-0.3.2/pyfemtet/opt/_optimizer.py +0 -241
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/LICENSE +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/README.md +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/her_ex40_parametric.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/_old/_SW_Femtet.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/core.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/opt/_FemtetWithNX/update_model.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/opt/__init__.py +0 -0
- {pyfemtet-0.3.2 → pyfemtet-0.3.3}/pyfemtet/opt/monitor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyfemtet
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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.3"
|
|
@@ -3,8 +3,6 @@ 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
|
|
|
9
7
|
import psutil
|
|
10
8
|
from dask.distributed import Lock
|
|
@@ -18,15 +16,13 @@ from femtetutils import util
|
|
|
18
16
|
from multiprocessing.context import BaseContext, SpawnProcess, _concrete_contexts
|
|
19
17
|
from multiprocessing.process import _children, _cleanup
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
logger
|
|
23
|
-
|
|
24
|
-
handler.setFormatter(logging.Formatter('[%(process)d] %(message)s'))
|
|
25
|
-
logger.addHandler(handler)
|
|
19
|
+
import logging
|
|
20
|
+
from .logger import get_logger
|
|
21
|
+
logger = get_logger('dispatch')
|
|
26
22
|
logger.setLevel(logging.INFO)
|
|
27
23
|
|
|
28
24
|
|
|
29
|
-
DISPATCH_TIMEOUT =
|
|
25
|
+
DISPATCH_TIMEOUT = 120
|
|
30
26
|
|
|
31
27
|
|
|
32
28
|
class DispatchExtensionException(Exception):
|
|
@@ -114,11 +110,6 @@ _concrete_contexts.update(
|
|
|
114
110
|
)
|
|
115
111
|
|
|
116
112
|
|
|
117
|
-
def _log_prefix():
|
|
118
|
-
prefix = current_process().name + ' : '
|
|
119
|
-
return prefix
|
|
120
|
-
|
|
121
|
-
|
|
122
113
|
def _get_hwnds(pid):
|
|
123
114
|
"""Proces ID から window handle を取得します."""
|
|
124
115
|
def callback(hwnd, _hwnds):
|
|
@@ -160,7 +151,6 @@ def launch_and_dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
|
|
|
160
151
|
Returns:
|
|
161
152
|
Tuple[IFemtet, int]:
|
|
162
153
|
"""
|
|
163
|
-
|
|
164
154
|
util.execute_femtet()
|
|
165
155
|
pid = util.get_last_executed_femtet_process_id()
|
|
166
156
|
logger.debug(f'Target pid is {pid}.')
|
|
@@ -169,7 +159,7 @@ def launch_and_dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
|
|
|
169
159
|
return Femtet, pid
|
|
170
160
|
|
|
171
161
|
|
|
172
|
-
def dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
|
|
162
|
+
def dispatch_femtet(timeout=DISPATCH_TIMEOUT, subprocess_log_prefix='') -> Tuple[IFemtet, int]:
|
|
173
163
|
"""Connect to existing Femtet process.
|
|
174
164
|
|
|
175
165
|
Args:
|
|
@@ -181,11 +171,13 @@ def dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
|
|
|
181
171
|
Returns:
|
|
182
172
|
Tuple[IFemtet, int]:
|
|
183
173
|
"""
|
|
184
|
-
|
|
185
174
|
# Dispatch
|
|
186
|
-
|
|
175
|
+
if subprocess_log_prefix:
|
|
176
|
+
logger.debug('%s'+'Try to connect Femtet.', subprocess_log_prefix)
|
|
177
|
+
else:
|
|
178
|
+
logger.info('Try to connect Femtet.')
|
|
187
179
|
Femtet = Dispatch('FemtetMacro.Femtet')
|
|
188
|
-
logger.debug(
|
|
180
|
+
logger.debug('%s'+'Dispatch finished.', subprocess_log_prefix)
|
|
189
181
|
|
|
190
182
|
# timeout 秒以内に接続が確立するか
|
|
191
183
|
start = time()
|
|
@@ -194,27 +186,31 @@ def dispatch_femtet(timeout=DISPATCH_TIMEOUT) -> Tuple[IFemtet, int]:
|
|
|
194
186
|
|
|
195
187
|
# 接続が確立
|
|
196
188
|
if hwnd > 0:
|
|
197
|
-
logger.debug(f'{
|
|
189
|
+
logger.debug('%s'+f'Dispatched hwnd is {hwnd} and its pid is {_get_pid(hwnd)}. Connection established.', subprocess_log_prefix)
|
|
198
190
|
break
|
|
199
191
|
else:
|
|
200
|
-
logger.debug(f'
|
|
192
|
+
logger.debug('%s'+f'Dispatched hwnd is {hwnd}. Waiting for establishing connection.', subprocess_log_prefix)
|
|
201
193
|
|
|
202
194
|
# 接続がタイムアウト
|
|
203
195
|
if time()-start > timeout:
|
|
204
|
-
raise FemtetConnectionTimeoutError(f'
|
|
196
|
+
raise FemtetConnectionTimeoutError(f'Connection trial with Femtet is timed out in {timeout} sec')
|
|
205
197
|
|
|
206
198
|
sleep(1)
|
|
207
199
|
|
|
208
200
|
# pid を取得
|
|
209
201
|
pid = _get_pid(hwnd)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
202
|
+
if subprocess_log_prefix:
|
|
203
|
+
logger.debug('%s'+f'Successfully connected. The pid of Femtet is {pid}.', subprocess_log_prefix)
|
|
204
|
+
else:
|
|
205
|
+
logger.info(f'Successfully connected. The pid of Femtet is {pid}.')
|
|
214
206
|
|
|
215
207
|
return Femtet, pid
|
|
216
208
|
|
|
217
209
|
|
|
210
|
+
def _get_subprocess_log_prefix():
|
|
211
|
+
return f'({current_process().name}) '
|
|
212
|
+
|
|
213
|
+
|
|
218
214
|
def _block_other_femtets(
|
|
219
215
|
target_pid,
|
|
220
216
|
subprocess_idx,
|
|
@@ -239,9 +235,10 @@ def _block_other_femtets(
|
|
|
239
235
|
else:
|
|
240
236
|
lock_inter_subproc.acquire()
|
|
241
237
|
try:
|
|
242
|
-
logger.debug(f'
|
|
243
|
-
_, my_pid = dispatch_femtet(2)
|
|
238
|
+
logger.debug('%s'+f'Subprocess started.', _get_subprocess_log_prefix())
|
|
239
|
+
_, my_pid = dispatch_femtet(2, _get_subprocess_log_prefix())
|
|
244
240
|
except DispatchExtensionException:
|
|
241
|
+
logger.debug('%s'+f'Connection failed. The other subprocesses will skip.', _get_subprocess_log_prefix())
|
|
245
242
|
my_pid = 0
|
|
246
243
|
# ひとつでもここに来れば、それ以降は探しても無駄なのでスキップフラグを立てる
|
|
247
244
|
for i in range(len(connection_flags)-1):
|
|
@@ -251,8 +248,6 @@ def _block_other_femtets(
|
|
|
251
248
|
|
|
252
249
|
# # target なら Dispatch の終了を通知する前に main を lock する
|
|
253
250
|
# 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
251
|
# lock_main.acquire()
|
|
257
252
|
|
|
258
253
|
# Dispatch の終了を通知
|
|
@@ -266,29 +261,30 @@ def _block_other_femtets(
|
|
|
266
261
|
# if all(connection_flags[:-1]):
|
|
267
262
|
# break
|
|
268
263
|
# if time()-start > timeout_main_wait_for_us:
|
|
269
|
-
# logger.error(f'{_log_prefix()}Timed out while waiting for other subprocesses.')
|
|
270
264
|
# return -1
|
|
271
265
|
# sleep(1)
|
|
272
|
-
|
|
273
|
-
logger.debug(f'{_log_prefix()}Release {my_pid}.')
|
|
266
|
+
logger.debug('%s'+f'Release {my_pid}.', _get_subprocess_log_prefix())
|
|
274
267
|
# lock_main.release()
|
|
275
268
|
return 0
|
|
276
269
|
|
|
277
270
|
# my_pid が非正なら何にも関与しないので即刻終了する
|
|
278
271
|
elif my_pid <= 0:
|
|
279
|
-
logger.debug(
|
|
272
|
+
logger.debug('%s'+'Failed or timed out to connect Femtet.', _get_subprocess_log_prefix())
|
|
280
273
|
return 1
|
|
281
274
|
|
|
282
275
|
# そうでなければメインプロセスが Dispatch を終えるまで解放しない
|
|
283
276
|
else:
|
|
284
|
-
logger.debug(f'
|
|
277
|
+
logger.debug('%s'+f'Connected to {my_pid}. Keep connection to block.', _get_subprocess_log_prefix())
|
|
285
278
|
start = time()
|
|
286
279
|
while True:
|
|
287
280
|
if connection_flags[-1]:
|
|
288
|
-
logger.debug(
|
|
281
|
+
logger.debug('%s'+'Main process seems to connect target Femtet.', _get_subprocess_log_prefix())
|
|
289
282
|
return 1
|
|
290
283
|
if time()-start > timeout_main_wait_for_us:
|
|
291
|
-
logger.
|
|
284
|
+
logger.warning(
|
|
285
|
+
'%s'+f'Timed out to wait for the main process to {timeout_main_wait_for_us}',
|
|
286
|
+
_get_subprocess_log_prefix()
|
|
287
|
+
)
|
|
292
288
|
return -1
|
|
293
289
|
sleep(1)
|
|
294
290
|
|
|
@@ -379,7 +375,6 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
|
|
|
379
375
|
Returns:
|
|
380
376
|
Tuple[IFemtet, int]:
|
|
381
377
|
"""
|
|
382
|
-
|
|
383
378
|
# TODO: 安定性を見て lock_main を復活させるか決める
|
|
384
379
|
|
|
385
380
|
if timeout < 5:
|
|
@@ -387,13 +382,12 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
|
|
|
387
382
|
|
|
388
383
|
# 存在する Femtet プロセスの列挙
|
|
389
384
|
pids = _get_pids('Femtet.exe')
|
|
390
|
-
logger.info(f'
|
|
385
|
+
logger.info(f'PID of existing Femtet processes: {pids}')
|
|
391
386
|
if not (pid in pids):
|
|
392
387
|
raise FemtetNotFoundException(f"Femtet (pid = {pid}) doesn't exist.")
|
|
393
388
|
|
|
394
389
|
# 子プロセスの準備
|
|
395
390
|
# with Manager() as manager:
|
|
396
|
-
start = time()
|
|
397
391
|
from multiprocessing.managers import SyncManager
|
|
398
392
|
m = SyncManager(ctx=_NestableSpawnContext())
|
|
399
393
|
m.start()
|
|
@@ -406,9 +400,11 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
|
|
|
406
400
|
connection_flags.append(False)
|
|
407
401
|
|
|
408
402
|
# 目的以外の Femtet をブロックする子プロセスを開始
|
|
403
|
+
logger.debug('Start subprocess to block Femtet other than target pid.')
|
|
409
404
|
processes = []
|
|
410
405
|
for subprocess_id in range(len(pids)):
|
|
411
406
|
p = _NestableSpawnProcess(
|
|
407
|
+
# p = Process(
|
|
412
408
|
target=_block_other_femtets,
|
|
413
409
|
args=(
|
|
414
410
|
pid,
|
|
@@ -423,6 +419,7 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
|
|
|
423
419
|
processes.append(p)
|
|
424
420
|
|
|
425
421
|
# 子プロセスの Dispatch 完了を待つ
|
|
422
|
+
start = time()
|
|
426
423
|
while True:
|
|
427
424
|
if all(connection_flags[:-1]):
|
|
428
425
|
break
|
|
@@ -443,7 +440,7 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
|
|
|
443
440
|
|
|
444
441
|
# 子プロセスによるブロックが終了しているので Dispatch する
|
|
445
442
|
try:
|
|
446
|
-
logger.debug(
|
|
443
|
+
logger.debug('Block process seems to be finished. Try Dispatch.')
|
|
447
444
|
Femtet, my_pid = dispatch_femtet()
|
|
448
445
|
except DispatchExtensionException as e:
|
|
449
446
|
# lock_main.release()
|
|
@@ -458,8 +455,9 @@ def dispatch_specific_femtet_core(pid, timeout=DISPATCH_TIMEOUT) -> Tuple[IFemte
|
|
|
458
455
|
|
|
459
456
|
if my_pid != pid: # pid の結果が違う場合
|
|
460
457
|
txt = f'Target pid is {pid}, but connected pid is {my_pid}. '
|
|
461
|
-
txt += f'The common reason is that
|
|
462
|
-
|
|
458
|
+
txt += f'The common reason is that THIS python process once connected {pid}.'
|
|
459
|
+
logger.warn(txt)
|
|
460
|
+
# warnings.warn(txt)
|
|
463
461
|
|
|
464
462
|
return Femtet, my_pid
|
|
465
463
|
|
|
@@ -0,0 +1,99 @@
|
|
|
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
|
+
# address = worker.address
|
|
16
|
+
name = f'(Worker:{worker.name}) '
|
|
17
|
+
except ValueError:
|
|
18
|
+
pass
|
|
19
|
+
return name
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DaskLogRecord(logging.LogRecord):
|
|
23
|
+
"""Generate a log message with dask worker name."""
|
|
24
|
+
|
|
25
|
+
# def __init__(self, *args, **kwargs):
|
|
26
|
+
# super().__init__(*args, **kwargs)
|
|
27
|
+
# self.worker = _get_worker_name_as_prefix()
|
|
28
|
+
|
|
29
|
+
def getMessage(self):
|
|
30
|
+
"""Add worker name to loggin message.
|
|
31
|
+
|
|
32
|
+
This function is originated from logging.LogRecord.
|
|
33
|
+
|
|
34
|
+
# Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
msg = str(self.msg)
|
|
38
|
+
if self.args:
|
|
39
|
+
msg = msg % self.args
|
|
40
|
+
msg = _get_worker_name_as_prefix() + msg
|
|
41
|
+
return msg
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
logging.setLogRecordFactory(DaskLogRecord) # すべての logging %(message)s の前に prefix を入れる
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _color_supported() -> bool:
|
|
48
|
+
"""Detection of color support.
|
|
49
|
+
|
|
50
|
+
This function is originated from optuna.logging.
|
|
51
|
+
|
|
52
|
+
# Copyright (c) 2018 Preferred Networks, Inc.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# NO_COLOR environment variable:
|
|
57
|
+
if os.environ.get("NO_COLOR", None):
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
if not hasattr(sys.stderr, "isatty") or not sys.stderr.isatty():
|
|
61
|
+
return False
|
|
62
|
+
else:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _create_formatter() -> logging.Formatter:
|
|
67
|
+
"""Create a formatter."""
|
|
68
|
+
# header = f"[pyfemtet %(name)s] %(levelname).4s %(worker)s]"
|
|
69
|
+
header = f"[pyfemtet %(name)s %(levelname).4s]"
|
|
70
|
+
message = "%(message)s"
|
|
71
|
+
|
|
72
|
+
formatter = ColoredFormatter(
|
|
73
|
+
f"%(log_color)s{header}%(reset)s {message}",
|
|
74
|
+
datefmt=None,
|
|
75
|
+
reset=True,
|
|
76
|
+
log_colors={
|
|
77
|
+
"DEBUG": "purple",
|
|
78
|
+
"INFO": "cyan",
|
|
79
|
+
"WARNING": "yellow",
|
|
80
|
+
"ERROR": "light_red",
|
|
81
|
+
"CRITICAL": "red",
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return formatter
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_logger(logger_name):
|
|
89
|
+
"""Return a logger with a default ColoredFormatter."""
|
|
90
|
+
|
|
91
|
+
formatter = _create_formatter()
|
|
92
|
+
|
|
93
|
+
logger = logging.getLogger(logger_name)
|
|
94
|
+
handler = logging.StreamHandler()
|
|
95
|
+
handler.setFormatter(formatter)
|
|
96
|
+
logger.addHandler(handler)
|
|
97
|
+
logger.setLevel(logging.DEBUG)
|
|
98
|
+
|
|
99
|
+
return logger
|
|
@@ -9,7 +9,6 @@ from time import time, sleep
|
|
|
9
9
|
from threading import Thread
|
|
10
10
|
from subprocess import Popen
|
|
11
11
|
import warnings
|
|
12
|
-
import logging
|
|
13
12
|
|
|
14
13
|
import numpy as np
|
|
15
14
|
import pandas as pd
|
|
@@ -19,7 +18,7 @@ from optuna.study import MaxTrialsCallback
|
|
|
19
18
|
from optuna.trial import TrialState
|
|
20
19
|
from optuna.exceptions import ExperimentalWarning
|
|
21
20
|
from optuna._hypervolume import WFG
|
|
22
|
-
from dask.distributed import LocalCluster, Client, Lock
|
|
21
|
+
from dask.distributed import LocalCluster, Client, Lock
|
|
23
22
|
|
|
24
23
|
from win32com.client import constants, Constants
|
|
25
24
|
|
|
@@ -27,12 +26,10 @@ from ..core import ModelError, MeshError, SolveError
|
|
|
27
26
|
from .interface import FEMInterface, FemtetInterface
|
|
28
27
|
from .monitor import Monitor
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
logger
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
logger.addHandler(handler)
|
|
35
|
-
logger.setLevel(logging.INFO) # TODO: optuna, dask の logger の書式を統一する
|
|
29
|
+
import logging
|
|
30
|
+
from ..logger import get_logger
|
|
31
|
+
logger = get_logger('opt')
|
|
32
|
+
logger.setLevel(logging.INFO)
|
|
36
33
|
|
|
37
34
|
|
|
38
35
|
warnings.filterwarnings('ignore', category=ExperimentalWarning)
|
|
@@ -350,6 +347,7 @@ class History:
|
|
|
350
347
|
|
|
351
348
|
# 引数の処理
|
|
352
349
|
self.path = history_path # .csv
|
|
350
|
+
self.is_restart = False
|
|
353
351
|
self._future = client.submit(HistoryDfCore, actor=True)
|
|
354
352
|
self._actor_data = self._future.result()
|
|
355
353
|
self.param_names = []
|
|
@@ -359,6 +357,7 @@ class History:
|
|
|
359
357
|
# path が存在すれば dataframe を読み込む
|
|
360
358
|
if os.path.isfile(self.path):
|
|
361
359
|
self.actor_data = pd.read_csv(self.path, encoding='shift-jis')
|
|
360
|
+
self.is_restart = True
|
|
362
361
|
|
|
363
362
|
@property
|
|
364
363
|
def actor_data(self):
|
|
@@ -597,7 +596,7 @@ class OptimizationStatus:
|
|
|
597
596
|
# self._actor.status_int = status_const
|
|
598
597
|
# self._actor.status = self.const_to_str(status_const)
|
|
599
598
|
self._actor.set(status_const, self.const_to_str(status_const)).result()
|
|
600
|
-
|
|
599
|
+
logger.info(f'---{self.const_to_str(status_const)}---')
|
|
601
600
|
|
|
602
601
|
def get(self):
|
|
603
602
|
return self._actor.status_int
|
|
@@ -722,6 +721,7 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
722
721
|
self.study_name = None
|
|
723
722
|
self.storage = None
|
|
724
723
|
self.study = None
|
|
724
|
+
self.optimize_callbacks = []
|
|
725
725
|
self.sampler_class = optuna.samplers.TPESampler if sampler_class is None else sampler_class
|
|
726
726
|
self.sampler_kwargs = dict() if sampler_kwargs is None else sampler_kwargs
|
|
727
727
|
self.additional_initial_parameter = []
|
|
@@ -759,7 +759,7 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
759
759
|
if cns.ub is not None:
|
|
760
760
|
feasible = feasible and (cns.ub >= cns_value)
|
|
761
761
|
if not feasible:
|
|
762
|
-
|
|
762
|
+
logger.info(f'以下の変数で拘束 {cns.name} が満たされませんでした。')
|
|
763
763
|
print(self.get_parameter('dict'))
|
|
764
764
|
raise optuna.TrialPruned() # set TrialState PRUNED because FAIL causes similar candidate loop.
|
|
765
765
|
|
|
@@ -767,8 +767,9 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
767
767
|
try:
|
|
768
768
|
_, _y, c = self.f(x)
|
|
769
769
|
except (ModelError, MeshError, SolveError) as e:
|
|
770
|
-
|
|
771
|
-
|
|
770
|
+
logger.info(str(type(e)).split("'")[1].split('.')[-1], e)
|
|
771
|
+
logger.info('以下の変数で FEM 解析に失敗しました。')
|
|
772
|
+
print(self.get_parameter('dict'))
|
|
772
773
|
|
|
773
774
|
# 中断の確認 (解析中に interrupt されている場合対策)
|
|
774
775
|
if self.status.get() == OptimizationStatus.INTERRUPTING:
|
|
@@ -807,8 +808,19 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
807
808
|
if self.is_cluster: # remote cluster なら scheduler の working dir に保存
|
|
808
809
|
storage_path = os.path.basename(self.history.path).replace('.csv', '.db')
|
|
809
810
|
|
|
810
|
-
#
|
|
811
|
-
if not
|
|
811
|
+
# callback to terminate
|
|
812
|
+
if self.n_trials is not None:
|
|
813
|
+
n_trials = self.n_trials
|
|
814
|
+
|
|
815
|
+
# restart である場合、追加 N 回と見做す
|
|
816
|
+
if self.history.is_restart:
|
|
817
|
+
n_existing_trials = len(self.history.actor_data)
|
|
818
|
+
n_trials += n_existing_trials
|
|
819
|
+
|
|
820
|
+
self.optimize_callbacks.append(MaxTrialsCallback(n_trials, states=(TrialState.COMPLETE,)))
|
|
821
|
+
|
|
822
|
+
# if not restart, create study if storage is not exists
|
|
823
|
+
if not self.history.is_restart:
|
|
812
824
|
|
|
813
825
|
self.storage = optuna.integration.dask.DaskStorage(
|
|
814
826
|
f'sqlite:///{storage_path}',
|
|
@@ -852,12 +864,17 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
852
864
|
d, user_attrs={"message": "additional initial (Latin Hypercube Sampling)"}
|
|
853
865
|
)
|
|
854
866
|
|
|
867
|
+
# if is_restart, load study
|
|
855
868
|
else:
|
|
856
|
-
|
|
869
|
+
if not os.path.exists(storage_path):
|
|
870
|
+
raise FileNotFoundError(f'{storage_path} が見つかりません。.db ファイルは .csv ファイルと同じフォルダに生成されます。クラスター解析の場合は、スケジューラを起動したフォルダに生成されます。')
|
|
857
871
|
self.storage = optuna.integration.dask.DaskStorage(
|
|
858
872
|
f'sqlite:///{storage_path}',
|
|
859
873
|
)
|
|
860
874
|
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
|
|
861
878
|
def add_init_parameter(
|
|
862
879
|
self,
|
|
863
880
|
parameter: dict or Iterable,
|
|
@@ -901,18 +918,11 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
901
918
|
sampler=sampler,
|
|
902
919
|
)
|
|
903
920
|
|
|
904
|
-
# callback to terminate
|
|
905
|
-
callbacks = []
|
|
906
|
-
n_existing_trials = len(self.history.actor_data)
|
|
907
|
-
if self.n_trials is not None:
|
|
908
|
-
n_trials = n_existing_trials + self.n_trials
|
|
909
|
-
callbacks.append(MaxTrialsCallback(n_trials, states=(TrialState.COMPLETE,)))
|
|
910
|
-
|
|
911
921
|
# run
|
|
912
922
|
study.optimize(
|
|
913
923
|
self._objective,
|
|
914
924
|
timeout=self.timeout,
|
|
915
|
-
callbacks=
|
|
925
|
+
callbacks=self.optimize_callbacks,
|
|
916
926
|
)
|
|
917
927
|
|
|
918
928
|
# finalize
|
|
@@ -1224,13 +1234,13 @@ class FEMOpt:
|
|
|
1224
1234
|
|
|
1225
1235
|
else:
|
|
1226
1236
|
logger.info('Launching single machine cluster. This may take tens of seconds.')
|
|
1227
|
-
cluster = LocalCluster(processes=True
|
|
1237
|
+
cluster = LocalCluster(processes=True)
|
|
1228
1238
|
self.client = Client(cluster, direct_to_workers=False)
|
|
1229
1239
|
self.scheduler_address = self.client.scheduler.address
|
|
1230
1240
|
|
|
1231
1241
|
# monitor 用 worker を起動
|
|
1232
1242
|
logger.info('Launching monitor server. This may take a few seconds.')
|
|
1233
|
-
self.monitor_process_worker_name = datetime.datetime.now().strftime("
|
|
1243
|
+
self.monitor_process_worker_name = datetime.datetime.now().strftime("Monitor-%Y%m%d-%H%M%S")
|
|
1234
1244
|
cmd = f'dask worker {self.client.scheduler.address} --name {self.monitor_process_worker_name} --no-nanny'
|
|
1235
1245
|
worker_addresses = list(self.client.has_what().keys())
|
|
1236
1246
|
Popen(cmd, shell=True) #, stdout=PIPE) --> cause stream error
|
|
@@ -1331,9 +1341,8 @@ class FEMOpt:
|
|
|
1331
1341
|
# 一応
|
|
1332
1342
|
t_save_history.join()
|
|
1333
1343
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
print(f'結果は{self.history.path}を確認してください.')
|
|
1344
|
+
logger.info(f'計算が終了しました. 実行時間は {int(end - start)} 秒でした。ウィンドウを閉じると終了します.')
|
|
1345
|
+
logger.info(f'結果は{self.history.path}を確認してください.')
|
|
1337
1346
|
|
|
1338
1347
|
def terminate_all(self):
|
|
1339
1348
|
|
|
@@ -1342,13 +1351,13 @@ class FEMOpt:
|
|
|
1342
1351
|
|
|
1343
1352
|
# terminate monitor process
|
|
1344
1353
|
self.status.set(OptimizationStatus.TERMINATE_ALL)
|
|
1345
|
-
|
|
1354
|
+
logger.info(self.monitor_process_future.result())
|
|
1346
1355
|
sleep(1)
|
|
1347
1356
|
|
|
1348
1357
|
# terminate actors
|
|
1349
1358
|
self.client.cancel(self.history._future, force=True)
|
|
1350
1359
|
self.client.cancel(self.status._future, force=True)
|
|
1351
|
-
|
|
1360
|
+
logger.info('Terminate actors.')
|
|
1352
1361
|
sleep(1)
|
|
1353
1362
|
|
|
1354
1363
|
# terminate monitor worker
|
|
@@ -1360,19 +1369,23 @@ class FEMOpt:
|
|
|
1360
1369
|
)
|
|
1361
1370
|
while n_workers == len(self.client.nthreads()):
|
|
1362
1371
|
sleep(1)
|
|
1363
|
-
|
|
1372
|
+
logger.info('Terminate monitor processes worker.')
|
|
1364
1373
|
sleep(1)
|
|
1365
1374
|
|
|
1366
1375
|
# close scheduler, other workers(, cluster)
|
|
1367
1376
|
self.client.close()
|
|
1368
1377
|
while self.client.scheduler is not None:
|
|
1369
1378
|
sleep(1)
|
|
1370
|
-
|
|
1379
|
+
logger.info('Terminate client.')
|
|
1380
|
+
|
|
1381
|
+
# close FEM (if specified to quit when deconstruct)
|
|
1382
|
+
del self.fem
|
|
1383
|
+
logger.info('Terminate FEM.')
|
|
1371
1384
|
|
|
1372
1385
|
# terminate dask relative processes.
|
|
1373
1386
|
if not self.opt.is_cluster:
|
|
1374
1387
|
self.client.shutdown()
|
|
1375
|
-
|
|
1388
|
+
logger.info('Terminate all relative processes.')
|
|
1376
1389
|
sleep(3)
|
|
1377
1390
|
|
|
1378
1391
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
-
from time import sleep
|
|
3
|
+
from time import sleep, time
|
|
4
4
|
import json
|
|
5
5
|
import subprocess
|
|
6
|
-
import
|
|
6
|
+
import signal
|
|
7
7
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
import psutil
|
|
@@ -26,14 +26,11 @@ from ..dispatch_extensions import (
|
|
|
26
26
|
DispatchExtensionException,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
logger
|
|
31
|
-
|
|
32
|
-
handler.setFormatter(logging.Formatter('[pid %(process)d] %(message)s'))
|
|
33
|
-
logger.addHandler(handler)
|
|
29
|
+
import logging
|
|
30
|
+
from ..logger import get_logger
|
|
31
|
+
logger = get_logger('FEM')
|
|
34
32
|
logger.setLevel(logging.INFO)
|
|
35
33
|
|
|
36
|
-
|
|
37
34
|
here, me = os.path.split(__file__)
|
|
38
35
|
|
|
39
36
|
|
|
@@ -187,7 +184,11 @@ class FemtetInterface(FEMInterface):
|
|
|
187
184
|
hwnd = self.Femtet.hWnd
|
|
188
185
|
pid = _get_pid(hwnd)
|
|
189
186
|
util.close_femtet(hwnd, 1, True)
|
|
187
|
+
start = time()
|
|
190
188
|
while psutil.pid_exists(pid):
|
|
189
|
+
if time() - start > 30: # 30 秒経っても存在するのは何かおかしい
|
|
190
|
+
os.kill(pid, signal.SIGKILL)
|
|
191
|
+
break
|
|
191
192
|
sleep(1)
|
|
192
193
|
sleep(1)
|
|
193
194
|
|
|
@@ -299,6 +300,10 @@ class FemtetInterface(FEMInterface):
|
|
|
299
300
|
-------
|
|
300
301
|
|
|
301
302
|
"""
|
|
303
|
+
|
|
304
|
+
# FIXME: Gaudi へのアクセスなど、self.Femtet.Gaudi.somefunc() のような場合、この関数を呼び出す前に Gaudi へのアクセスの時点で com_error が起こる
|
|
305
|
+
# FIXME: => 文字列で渡して eval() すればよい。
|
|
306
|
+
|
|
302
307
|
if args is None:
|
|
303
308
|
args = tuple()
|
|
304
309
|
if kwargs is None:
|
|
@@ -309,7 +314,7 @@ class FemtetInterface(FEMInterface):
|
|
|
309
314
|
# 2. API 実行時に成功失敗を示す戻り値を返し、ShowLastError で例外にアクセスできる状態になる
|
|
310
315
|
|
|
311
316
|
# Gaudi コマンドなら Gaudi.Activate する
|
|
312
|
-
|
|
317
|
+
logger.debug(' '*print_indent + f'Femtet API:{fun.__name__}, args:{args}, kwargs:{kwargs}')
|
|
313
318
|
if is_Gaudi_method: # Optimizer は Gogh に触らないので全部にこれをつけてもいい気がする
|
|
314
319
|
try:
|
|
315
320
|
self._call_femtet_api(
|
|
@@ -333,7 +338,7 @@ class FemtetInterface(FEMInterface):
|
|
|
333
338
|
returns = ret_if_failed
|
|
334
339
|
else:
|
|
335
340
|
returns = [ret_if_failed]*(ret_for_check_idx+1)
|
|
336
|
-
|
|
341
|
+
logger.debug(' '*print_indent + f'Femtet API result:{returns}')
|
|
337
342
|
|
|
338
343
|
# チェックすべき値の抽出
|
|
339
344
|
if ret_for_check_idx is None:
|
|
@@ -352,29 +357,28 @@ class FemtetInterface(FEMInterface):
|
|
|
352
357
|
if self.femtet_is_alive():
|
|
353
358
|
# 生きていてもここにきているなら
|
|
354
359
|
# 指定された Exception を送出する
|
|
355
|
-
|
|
360
|
+
logger.debug(' ' * print_indent + error_message)
|
|
356
361
|
raise if_error(error_message)
|
|
357
362
|
|
|
358
363
|
# 死んでいるなら再起動
|
|
359
364
|
else:
|
|
360
365
|
# 再起動試行回数の上限に達していたら諦める
|
|
361
|
-
|
|
366
|
+
logger.debug(' ' * print_indent + f'現在の Femtet 再起動回数: {recourse_depth}')
|
|
362
367
|
if recourse_depth >= self.max_api_retry:
|
|
363
368
|
raise Exception('Femtet のプロセスが異常終了し、正常に再起動できませんでした.')
|
|
364
369
|
|
|
365
370
|
# 再起動
|
|
366
|
-
|
|
367
|
-
|
|
371
|
+
logger.warn('Femtet プロセスの異常終了が検知されました. 回復を試みます.')
|
|
372
|
+
CoInitialize()
|
|
368
373
|
self.connect_femtet(connect_method='new')
|
|
369
374
|
self.open(self.femprj_path, self.model_name)
|
|
370
375
|
|
|
371
376
|
# 状態を復元するために一度変数を渡して解析を行う(fun.__name__がSolveなら2度手間だが)
|
|
372
|
-
|
|
377
|
+
logger.info(' ' * print_indent + f'Femtet が再起動されました。解析を行い、状態回復を試みます。')
|
|
373
378
|
self.update(self.parameters)
|
|
374
379
|
|
|
375
380
|
# 与えられた API の再帰的再試行
|
|
376
|
-
|
|
377
|
-
print('回復されました.')
|
|
381
|
+
logger.info(' ' * print_indent + f'Femtet が回復されました。コマンド {fun.__name__} を再試行します。')
|
|
378
382
|
return self._call_femtet_api(
|
|
379
383
|
fun,
|
|
380
384
|
ret_if_failed,
|
|
@@ -507,7 +511,7 @@ class FemtetInterface(FEMInterface):
|
|
|
507
511
|
args=(name, value),
|
|
508
512
|
)
|
|
509
513
|
else:
|
|
510
|
-
|
|
514
|
+
logger.warn(f'変数 {name} は .femprj に含まれていません。無視されます。')
|
|
511
515
|
|
|
512
516
|
# ここでは ReExecute しない
|
|
513
517
|
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pyfemtet"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3" # 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"
|
|
@@ -24,6 +24,7 @@ dash-bootstrap-components = "^1.5.0"
|
|
|
24
24
|
dask = "^2023.12.1"
|
|
25
25
|
distributed = "^2023.12.1"
|
|
26
26
|
botorch = { version = "^0.9.5", python = "<3.12" }
|
|
27
|
+
colorlog = "^6.8.0"
|
|
27
28
|
|
|
28
29
|
[tool.poetry.group.dev.dependencies]
|
|
29
30
|
pip-licenses = "^4.3.3"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.2"
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
# import os
|
|
2
|
-
# import gc
|
|
3
|
-
# import warnings
|
|
4
|
-
#
|
|
5
|
-
# import numpy as np
|
|
6
|
-
# from scipy.stats.qmc import LatinHypercube
|
|
7
|
-
# import optuna
|
|
8
|
-
# from optuna.study import MaxTrialsCallback
|
|
9
|
-
# from optuna.trial import TrialState
|
|
10
|
-
# from optuna.exceptions import ExperimentalWarning
|
|
11
|
-
# # optuna.logging.disable_default_handler()
|
|
12
|
-
#
|
|
13
|
-
# from .core import UserInterruption, ModelError, MeshError, SolveError
|
|
14
|
-
# from .base import OptimizerBase
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# warnings.filterwarnings('ignore', category=ExperimentalWarning)
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# def generate_lhs(bounds: list[list[float]], seed: int or None = None) -> np.ndarray:
|
|
21
|
-
# """Latin Hypercube Sampling from given design parameter bounds.
|
|
22
|
-
#
|
|
23
|
-
# If the number of parameters is d,
|
|
24
|
-
# sampler returns (N, d) shape ndarray.
|
|
25
|
-
# N equals p**2, p is the minimum prime number over d.
|
|
26
|
-
# For example, when d=3, then p=5 and N=25.
|
|
27
|
-
#
|
|
28
|
-
# Args:
|
|
29
|
-
# bounds (list[list[float]]): List of [lower_bound, upper_bound] of parameters.
|
|
30
|
-
# seed (int or None, optional): Random seed. Defaults to None.
|
|
31
|
-
#
|
|
32
|
-
# Returns:
|
|
33
|
-
# np.ndarray: (N, d) shape ndarray.
|
|
34
|
-
# """
|
|
35
|
-
#
|
|
36
|
-
# d = len(bounds)
|
|
37
|
-
#
|
|
38
|
-
# sampler = LatinHypercube(
|
|
39
|
-
# d,
|
|
40
|
-
# scramble=False,
|
|
41
|
-
# strength=2,
|
|
42
|
-
# # optimization='lloyd',
|
|
43
|
-
# optimization='random-cd',
|
|
44
|
-
# seed=seed,
|
|
45
|
-
# )
|
|
46
|
-
#
|
|
47
|
-
# LIMIT = 100
|
|
48
|
-
#
|
|
49
|
-
# def is_prime(p):
|
|
50
|
-
# for i in range(2, p):
|
|
51
|
-
# if p % i == 0:
|
|
52
|
-
# return False
|
|
53
|
-
# return True
|
|
54
|
-
#
|
|
55
|
-
# def get_prime(minimum):
|
|
56
|
-
# for p in range(minimum, LIMIT):
|
|
57
|
-
# if is_prime(p):
|
|
58
|
-
# return p
|
|
59
|
-
#
|
|
60
|
-
# n = get_prime(d + 1) ** 2
|
|
61
|
-
# data = sampler.random(n) # [0,1)
|
|
62
|
-
#
|
|
63
|
-
# for i, (data_range, datum) in enumerate(zip(bounds, data.T)):
|
|
64
|
-
# minimum, maximum = data_range
|
|
65
|
-
# band = maximum - minimum
|
|
66
|
-
# converted_datum = datum * band + minimum
|
|
67
|
-
# data[:, i] = converted_datum
|
|
68
|
-
#
|
|
69
|
-
# return data # data.shape = (N, d)
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# class OptimizerOptuna(OptimizerBase):
|
|
73
|
-
# """Optimizer class using Optuna.
|
|
74
|
-
#
|
|
75
|
-
# """
|
|
76
|
-
#
|
|
77
|
-
# def _objective(self, trial):
|
|
78
|
-
#
|
|
79
|
-
# # 中断指令の処理
|
|
80
|
-
# if self.ipv.get_state() == 'interrupting':
|
|
81
|
-
# raise UserInterruption
|
|
82
|
-
#
|
|
83
|
-
# # message の設定
|
|
84
|
-
# try:
|
|
85
|
-
# message = trial.user_attrs["message"] # あれば
|
|
86
|
-
# except (AttributeError, KeyError):
|
|
87
|
-
# message = '' # なければ
|
|
88
|
-
#
|
|
89
|
-
# # x の生成
|
|
90
|
-
# x = []
|
|
91
|
-
# for i, row in self.parameters.iterrows():
|
|
92
|
-
# x.append(trial.suggest_float(row['name'], row['lb'], row['ub']))
|
|
93
|
-
# x = np.array(x)
|
|
94
|
-
# self.parameters['value'] = x
|
|
95
|
-
#
|
|
96
|
-
# # strict 拘束の計算で Prune することになったとき
|
|
97
|
-
# # constraint attr がないとエラーになるのでダミーを置いておく
|
|
98
|
-
# trial.set_user_attr("constraint", (1.,)) # 非正が feasible 扱い
|
|
99
|
-
#
|
|
100
|
-
# # GetVariableValue 経由で変数にアクセスするなどの場合
|
|
101
|
-
# self.fem.update_parameter(self.parameters)
|
|
102
|
-
#
|
|
103
|
-
# # strict 拘束の計算
|
|
104
|
-
# tmp = [[cns.calc(self.fem), cns.lb, cns.ub, name] for name, cns in self.constraints.items() if cns.strict]
|
|
105
|
-
# for val, lb, ub, name in tmp:
|
|
106
|
-
# if lb is not None:
|
|
107
|
-
# if not (lb <= val):
|
|
108
|
-
# print(f'拘束 {name} が満たされませんでした。')
|
|
109
|
-
# print('変数の組み合わせは以下の通りです。')
|
|
110
|
-
# print(self.parameters)
|
|
111
|
-
# raise optuna.TrialPruned()
|
|
112
|
-
# if ub is not None:
|
|
113
|
-
# if not (val <= ub):
|
|
114
|
-
# print(f'拘束 {name} が満たされませんでした。')
|
|
115
|
-
# print('変数の組み合わせは以下の通りです。')
|
|
116
|
-
# print(self.parameters)
|
|
117
|
-
# raise optuna.TrialPruned()
|
|
118
|
-
#
|
|
119
|
-
# # 解析実行
|
|
120
|
-
# try:
|
|
121
|
-
# obj_values = self.f(x, message) # obj_val と cns_val の更新
|
|
122
|
-
# except (ModelError, MeshError, SolveError):
|
|
123
|
-
# print('FEM 解析に失敗しました。')
|
|
124
|
-
# print('変数の組み合わせは以下の通りです。')
|
|
125
|
-
# print(self.parameters)
|
|
126
|
-
# raise optuna.TrialPruned()
|
|
127
|
-
#
|
|
128
|
-
# # 拘束 attr の更新
|
|
129
|
-
# _cns_values = [] # 非正なら OK
|
|
130
|
-
# for (name, cns), cns_val in zip(self.constraints.items(), self.cns_values):
|
|
131
|
-
# lb, ub = cns.lb, cns.ub
|
|
132
|
-
# if lb is not None: # fun >= lb <=> lb - fun <= 0
|
|
133
|
-
# _cns_values.append(lb - cns_val)
|
|
134
|
-
# if ub is not None: # ub >= fun <=> fun - ub <= 0
|
|
135
|
-
# _cns_values.append(cns_val - ub)
|
|
136
|
-
# trial.set_user_attr("constraint", _cns_values)
|
|
137
|
-
#
|
|
138
|
-
# # 一応
|
|
139
|
-
# gc.collect()
|
|
140
|
-
#
|
|
141
|
-
# return tuple(obj_values)
|
|
142
|
-
#
|
|
143
|
-
# def _constraint_function(self, trial):
|
|
144
|
-
# return trial.user_attrs["constraint"]
|
|
145
|
-
#
|
|
146
|
-
# def setup_concrete_main(self, use_lhs_init=True):
|
|
147
|
-
# """Performs the setup for the optimization using Optuna.
|
|
148
|
-
#
|
|
149
|
-
# Do sampler settings, study creation or loading and initial trials settings.
|
|
150
|
-
#
|
|
151
|
-
# Args:
|
|
152
|
-
# use_lhs_init (bool, optional): Flag indicating whether to use Latin Hypercube Sampling for initializing trials. Defaults to True.
|
|
153
|
-
#
|
|
154
|
-
# """
|
|
155
|
-
#
|
|
156
|
-
# # sampler の設定
|
|
157
|
-
# self.sampler_kwargs = dict(
|
|
158
|
-
# # n_startup_trials=5,
|
|
159
|
-
# constraints_func=self._constraint_function
|
|
160
|
-
# )
|
|
161
|
-
# self.sampler_class = optuna.samplers.TPESampler
|
|
162
|
-
# if self.method == 'botorch':
|
|
163
|
-
# self.sampler_class = optuna.integration.BoTorchSampler
|
|
164
|
-
# # self.sampler_kwargs.update(
|
|
165
|
-
# # dict(
|
|
166
|
-
# # consider_running_trials=True
|
|
167
|
-
# # )
|
|
168
|
-
# # )
|
|
169
|
-
#
|
|
170
|
-
# # study name の設定
|
|
171
|
-
# self.study_name = os.path.splitext(os.path.basename(self.history.path))[0]
|
|
172
|
-
# self.storage_path = os.path.splitext(self.history.path)[0] + '.db'
|
|
173
|
-
# self.storage = f"sqlite:///{self.storage_path}"
|
|
174
|
-
#
|
|
175
|
-
# # storage の設定
|
|
176
|
-
# study = optuna.create_study(
|
|
177
|
-
# study_name=self.study_name,
|
|
178
|
-
# storage=self.storage,
|
|
179
|
-
# load_if_exists=True,
|
|
180
|
-
# directions=['minimize']*len(self.objectives),
|
|
181
|
-
# )
|
|
182
|
-
#
|
|
183
|
-
# # 初期値の設定
|
|
184
|
-
# if len(study.trials) == 0: # リスタートでなければ
|
|
185
|
-
# # ユーザーの指定した初期値
|
|
186
|
-
# params = self.get_parameter('dict')
|
|
187
|
-
# study.enqueue_trial(params, user_attrs={"message": "initial"})
|
|
188
|
-
#
|
|
189
|
-
# # LHS を初期値にする
|
|
190
|
-
# if use_lhs_init:
|
|
191
|
-
# names = []
|
|
192
|
-
# bounds = []
|
|
193
|
-
# for i, row in self.parameters.iterrows():
|
|
194
|
-
# names.append(row['name'])
|
|
195
|
-
# lb = row['lb']
|
|
196
|
-
# ub = row['ub']
|
|
197
|
-
# bounds.append([lb, ub])
|
|
198
|
-
# data = generate_lhs(bounds, seed=self.seed)
|
|
199
|
-
# for datum in data:
|
|
200
|
-
# d = {}
|
|
201
|
-
# for name, v in zip(names, datum):
|
|
202
|
-
# d[name] = v
|
|
203
|
-
# study.enqueue_trial(d, user_attrs={"message": "initial Latin Hypercube Sampling"})
|
|
204
|
-
#
|
|
205
|
-
# def concrete_main(self, subprocess_idx=None):
|
|
206
|
-
# """Optimization using Optuna."""
|
|
207
|
-
#
|
|
208
|
-
# # 乱数シードをプロセス固有にする
|
|
209
|
-
# seed = self.seed
|
|
210
|
-
# if seed is not None:
|
|
211
|
-
# if subprocess_idx is not None:
|
|
212
|
-
# seed = seed + (1 + subprocess_idx) # main process と subprocess0 が重複する
|
|
213
|
-
#
|
|
214
|
-
# # sampler の restore
|
|
215
|
-
# sampler = self.sampler_class(
|
|
216
|
-
# seed=seed,
|
|
217
|
-
# **self.sampler_kwargs
|
|
218
|
-
# )
|
|
219
|
-
#
|
|
220
|
-
# # storage の restore
|
|
221
|
-
# study = optuna.load_study(
|
|
222
|
-
# study_name=self.study_name,
|
|
223
|
-
# storage=self.storage,
|
|
224
|
-
# sampler=sampler,
|
|
225
|
-
# )
|
|
226
|
-
#
|
|
227
|
-
# # 最大実行回数の指定
|
|
228
|
-
# callbacks = []
|
|
229
|
-
# n_existing_trials = len(self.history.actor_data)
|
|
230
|
-
# if self.n_trials is not None:
|
|
231
|
-
# n_trials = n_existing_trials + self.n_trials
|
|
232
|
-
# callbacks.append(MaxTrialsCallback(n_trials, states=(TrialState.COMPLETE,)))
|
|
233
|
-
#
|
|
234
|
-
# # run
|
|
235
|
-
# study.optimize(
|
|
236
|
-
# self._objective,
|
|
237
|
-
# timeout=self.timeout,
|
|
238
|
-
# callbacks=callbacks,
|
|
239
|
-
# )
|
|
240
|
-
#
|
|
241
|
-
# return study
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|