pyfemtet 0.4.10__py3-none-any.whl → 0.4.12__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/_test_util.py +26 -0
- pyfemtet/opt/__init__.py +1 -1
- pyfemtet/opt/_femopt.py +41 -5
- pyfemtet/opt/interface/_base.py +6 -1
- pyfemtet/opt/interface/_femtet.py +32 -10
- pyfemtet/opt/interface/_femtet_parametric.py +5 -25
- pyfemtet/opt/opt/__init__.py +3 -1
- pyfemtet/opt/opt/_base.py +88 -8
- pyfemtet/opt/opt/_optuna.py +17 -2
- pyfemtet/opt/opt/_scipy.py +144 -0
- pyfemtet/opt/opt/_scipy_scalar.py +104 -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.10.dist-info → pyfemtet-0.4.12.dist-info}/METADATA +1 -1
- pyfemtet-0.4.12.dist-info/RECORD +138 -0
- {pyfemtet-0.4.10.dist-info → pyfemtet-0.4.12.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.10.dist-info/RECORD +0 -83
- {pyfemtet-0.4.10.dist-info → pyfemtet-0.4.12.dist-info}/LICENSE +0 -0
- {pyfemtet-0.4.10.dist-info → pyfemtet-0.4.12.dist-info}/WHEEL +0 -0
pyfemtet/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.4.
|
|
1
|
+
__version__ = "0.4.12"
|
pyfemtet/_test_util.py
CHANGED
|
@@ -15,6 +15,32 @@ from femtetutils import util
|
|
|
15
15
|
from pyfemtet.opt import FEMOpt
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
class SuperSphere:
|
|
19
|
+
def __init__(self, n):
|
|
20
|
+
self.n = n
|
|
21
|
+
|
|
22
|
+
def x(self, radius, *angles):
|
|
23
|
+
assert len(angles) == self.n - 1, 'invalid angles length'
|
|
24
|
+
|
|
25
|
+
out = []
|
|
26
|
+
|
|
27
|
+
for i in range(self.n):
|
|
28
|
+
if i == 0:
|
|
29
|
+
out.append(radius * np.cos(angles[0]))
|
|
30
|
+
elif i < self.n - 1:
|
|
31
|
+
product = radius
|
|
32
|
+
for j in range(i):
|
|
33
|
+
product *= np.sin(angles[j])
|
|
34
|
+
product *= np.cos(angles[i])
|
|
35
|
+
out.append(product)
|
|
36
|
+
else:
|
|
37
|
+
product = radius
|
|
38
|
+
for j in range(i):
|
|
39
|
+
product *= np.sin(angles[j])
|
|
40
|
+
out.append(product)
|
|
41
|
+
return out
|
|
42
|
+
|
|
43
|
+
|
|
18
44
|
def _open_femprj(femprj_path):
|
|
19
45
|
Femtet = Dispatch('FemtetMacro.Femtet')
|
|
20
46
|
for _ in tqdm(range(5), 'wait for dispatch Femtet'):
|
pyfemtet/opt/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@ from pyfemtet.opt.interface import FemtetInterface
|
|
|
4
4
|
from pyfemtet.opt.interface import FemtetWithNXInterface
|
|
5
5
|
from pyfemtet.opt.interface import FemtetWithSolidworksInterface
|
|
6
6
|
|
|
7
|
-
from pyfemtet.opt.opt import OptunaOptimizer
|
|
7
|
+
from pyfemtet.opt.opt import OptunaOptimizer, ScipyOptimizer
|
|
8
8
|
from pyfemtet.opt.opt import AbstractOptimizer
|
|
9
9
|
|
|
10
10
|
from pyfemtet.opt._femopt import FEMOpt
|
pyfemtet/opt/_femopt.py
CHANGED
|
@@ -13,7 +13,7 @@ from dask.distributed import LocalCluster, Client
|
|
|
13
13
|
# pyfemtet relative
|
|
14
14
|
from pyfemtet.opt.interface import FEMInterface, FemtetInterface
|
|
15
15
|
from pyfemtet.opt.opt import AbstractOptimizer, OptunaOptimizer
|
|
16
|
-
from pyfemtet.opt.visualization.
|
|
16
|
+
from pyfemtet.opt.visualization.process_monitor.application import main as process_monitor_main
|
|
17
17
|
from pyfemtet.opt._femopt_core import (
|
|
18
18
|
_check_bound,
|
|
19
19
|
_is_access_gogh,
|
|
@@ -107,6 +107,7 @@ class FEMOpt:
|
|
|
107
107
|
initial_value: float or None = None,
|
|
108
108
|
lower_bound: float or None = None,
|
|
109
109
|
upper_bound: float or None = None,
|
|
110
|
+
step: float or None = None,
|
|
110
111
|
memo: str = ''
|
|
111
112
|
):
|
|
112
113
|
"""Adds a parameter to the optimization problem.
|
|
@@ -133,8 +134,9 @@ class FEMOpt:
|
|
|
133
134
|
d = {
|
|
134
135
|
'name': name,
|
|
135
136
|
'value': float(initial_value),
|
|
136
|
-
'lb': float(lower_bound),
|
|
137
|
-
'ub': float(upper_bound),
|
|
137
|
+
'lb': float(lower_bound) if lower_bound is not None else None,
|
|
138
|
+
'ub': float(upper_bound) if upper_bound is not None else None,
|
|
139
|
+
'step': float(step) if step is not None else None,
|
|
138
140
|
'memo': memo,
|
|
139
141
|
}
|
|
140
142
|
pdf = pd.DataFrame(d, index=[0], dtype=object)
|
|
@@ -314,6 +316,34 @@ class FEMOpt:
|
|
|
314
316
|
|
|
315
317
|
"""
|
|
316
318
|
|
|
319
|
+
# method checker
|
|
320
|
+
if n_parallel > 1:
|
|
321
|
+
self.opt.method_checker.check_parallel()
|
|
322
|
+
|
|
323
|
+
if timeout is not None:
|
|
324
|
+
self.opt.method_checker.check_timeout()
|
|
325
|
+
|
|
326
|
+
if len(self.opt.objectives) > 1:
|
|
327
|
+
self.opt.method_checker.check_multi_objective()
|
|
328
|
+
|
|
329
|
+
if len(self.opt.constraints) > 0:
|
|
330
|
+
self.opt.method_checker.check_constraint()
|
|
331
|
+
|
|
332
|
+
for key, value in self.opt.constraints.items():
|
|
333
|
+
if value.strict:
|
|
334
|
+
self.opt.method_checker.check_strict_constraint()
|
|
335
|
+
break
|
|
336
|
+
|
|
337
|
+
if self.opt.seed is not None:
|
|
338
|
+
self.opt.method_checker.check_seed()
|
|
339
|
+
|
|
340
|
+
is_incomplete_bounds = False
|
|
341
|
+
for _, row in self.opt.parameters.iterrows():
|
|
342
|
+
lb, ub = row['lb'], row['ub']
|
|
343
|
+
is_incomplete_bounds = is_incomplete_bounds + (lb is None) + (ub is None)
|
|
344
|
+
if is_incomplete_bounds:
|
|
345
|
+
self.opt.method_checker.check_incomplete_bounds()
|
|
346
|
+
|
|
317
347
|
# 共通引数
|
|
318
348
|
self.opt.n_trials = n_trials
|
|
319
349
|
self.opt.timeout = timeout
|
|
@@ -574,6 +604,12 @@ def _start_monitor_server(
|
|
|
574
604
|
host=None,
|
|
575
605
|
port=None,
|
|
576
606
|
):
|
|
577
|
-
|
|
578
|
-
|
|
607
|
+
process_monitor_main(
|
|
608
|
+
history,
|
|
609
|
+
status,
|
|
610
|
+
worker_addresses,
|
|
611
|
+
worker_status_list,
|
|
612
|
+
host,
|
|
613
|
+
port,
|
|
614
|
+
)
|
|
579
615
|
return 'Exit monitor server process gracefully'
|
pyfemtet/opt/interface/_base.py
CHANGED
|
@@ -43,7 +43,12 @@ class FEMInterface(ABC):
|
|
|
43
43
|
pass
|
|
44
44
|
|
|
45
45
|
def update_parameter(self, parameters: pd.DataFrame, with_warning=False) -> Optional[List[str]]:
|
|
46
|
-
"""Updates only FEM variables (if implemented in concrete class).
|
|
46
|
+
"""Updates only FEM variables (if implemented in concrete class).
|
|
47
|
+
|
|
48
|
+
If this method is implemented,
|
|
49
|
+
it is able to get parameter via FEMInterface.
|
|
50
|
+
|
|
51
|
+
"""
|
|
47
52
|
pass
|
|
48
53
|
|
|
49
54
|
def _setup_before_parallel(self, client) -> None:
|
|
@@ -3,15 +3,19 @@ from typing import Optional, List
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
from time import sleep, time
|
|
6
|
-
import signal
|
|
7
6
|
|
|
8
7
|
import pandas as pd
|
|
9
8
|
import psutil
|
|
10
9
|
from dask.distributed import get_worker
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
# noinspection PyUnresolvedReferences
|
|
12
|
+
from pywintypes import com_error, error
|
|
13
|
+
# noinspection PyUnresolvedReferences
|
|
13
14
|
from pythoncom import CoInitialize, CoUninitialize
|
|
15
|
+
# noinspection PyUnresolvedReferences
|
|
14
16
|
from win32com.client import constants
|
|
17
|
+
import win32con
|
|
18
|
+
import win32gui
|
|
15
19
|
from femtetutils import util
|
|
16
20
|
|
|
17
21
|
from pyfemtet.core import (
|
|
@@ -31,6 +35,10 @@ from pyfemtet.dispatch_extensions import (
|
|
|
31
35
|
from pyfemtet.opt.interface import FEMInterface, logger
|
|
32
36
|
|
|
33
37
|
|
|
38
|
+
def post_activate_message(hwnd):
|
|
39
|
+
win32gui.PostMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
|
|
40
|
+
|
|
41
|
+
|
|
34
42
|
class FemtetInterface(FEMInterface):
|
|
35
43
|
"""Concrete class for the interface with Femtet.
|
|
36
44
|
|
|
@@ -113,7 +121,7 @@ class FemtetInterface(FEMInterface):
|
|
|
113
121
|
if self.original_femprj_path is None:
|
|
114
122
|
# dask worker でなければ original のはず
|
|
115
123
|
try:
|
|
116
|
-
|
|
124
|
+
_ = get_worker()
|
|
117
125
|
except ValueError:
|
|
118
126
|
self.original_femprj_path = self.femprj_path
|
|
119
127
|
|
|
@@ -252,7 +260,7 @@ class FemtetInterface(FEMInterface):
|
|
|
252
260
|
|
|
253
261
|
"""
|
|
254
262
|
|
|
255
|
-
# FIXME: Gaudi へのアクセスなど、self.Femtet.Gaudi.
|
|
263
|
+
# FIXME: Gaudi へのアクセスなど、self.Femtet.Gaudi.SomeFunc() のような場合、この関数を呼び出す前に Gaudi へのアクセスの時点で com_error が起こる
|
|
256
264
|
# FIXME: => 文字列で渡して eval() すればよい。
|
|
257
265
|
|
|
258
266
|
if args is None:
|
|
@@ -282,9 +290,16 @@ class FemtetInterface(FEMInterface):
|
|
|
282
290
|
|
|
283
291
|
# API を実行
|
|
284
292
|
try:
|
|
285
|
-
|
|
286
|
-
|
|
293
|
+
# 解析結果を開いた状態で Gaudi.Activate して ReExecute する場合、ReExecute の前後にアクティブ化イベントが必要
|
|
294
|
+
if fun.__name__ == 'ReExecute':
|
|
295
|
+
post_activate_message(self.Femtet.hWnd) # can raise pywintypes.error
|
|
296
|
+
returns = fun(*args, **kwargs)
|
|
297
|
+
post_activate_message(self.Femtet.hWnd)
|
|
298
|
+
else:
|
|
299
|
+
returns = fun(*args, **kwargs)
|
|
300
|
+
except (com_error, error):
|
|
287
301
|
# パターン 2 エラーが生じたことは確定なのでエラーが起こるよう returns を作る
|
|
302
|
+
# com_error ではなく error の場合はおそらく Femtet が落ちている
|
|
288
303
|
if ret_for_check_idx is None:
|
|
289
304
|
returns = return_value_if_failed
|
|
290
305
|
else:
|
|
@@ -665,6 +680,13 @@ class FemtetInterface(FEMInterface):
|
|
|
665
680
|
)
|
|
666
681
|
return out
|
|
667
682
|
|
|
683
|
+
@staticmethod
|
|
684
|
+
def create_pdt_path(femprj_path, model_name, trial):
|
|
685
|
+
result_dir = femprj_path.replace('.femprj', '.Results')
|
|
686
|
+
pdt_path = os.path.join(result_dir, model_name + f'_trial{trial}.pdt')
|
|
687
|
+
return pdt_path
|
|
688
|
+
|
|
689
|
+
# noinspection PyMethodOverriding
|
|
668
690
|
@staticmethod
|
|
669
691
|
def postprocess_func(
|
|
670
692
|
trial: int,
|
|
@@ -676,7 +698,7 @@ class FemtetInterface(FEMInterface):
|
|
|
676
698
|
):
|
|
677
699
|
result_dir = original_femprj_path.replace('.femprj', '.Results')
|
|
678
700
|
if pdt_file_content is not None:
|
|
679
|
-
pdt_path =
|
|
701
|
+
pdt_path = FemtetInterface.create_pdt_path(original_femprj_path, model_name, trial)
|
|
680
702
|
with open(pdt_path, 'wb') as f:
|
|
681
703
|
f.write(pdt_file_content)
|
|
682
704
|
|
|
@@ -710,13 +732,12 @@ class FemtetInterface(FEMInterface):
|
|
|
710
732
|
jpg_path = os.path.join(result_dir, self.model_name + '.jpg')
|
|
711
733
|
|
|
712
734
|
# モデル表示画面の設定
|
|
713
|
-
self.Femtet.SetWindowSize(
|
|
735
|
+
self.Femtet.SetWindowSize(600, 600)
|
|
714
736
|
self.Femtet.Fit()
|
|
715
|
-
self.Femtet.ViewNumeric.SetCoord(1, 1, 1)
|
|
716
737
|
|
|
717
738
|
# ---モデルの画面を保存---
|
|
718
739
|
self.Femtet.Redraw() # 再描画
|
|
719
|
-
succeed = self.Femtet.SavePicture(jpg_path,
|
|
740
|
+
succeed = self.Femtet.SavePicture(jpg_path, 600, 600, 80)
|
|
720
741
|
|
|
721
742
|
self.Femtet.RedrawMode = True # 逐一の描画をオン
|
|
722
743
|
|
|
@@ -745,6 +766,7 @@ class _UnPicklableNoFEM(FemtetInterface):
|
|
|
745
766
|
Femtet = None
|
|
746
767
|
quit_when_destruct = False
|
|
747
768
|
|
|
769
|
+
# noinspection PyMissingConstructor
|
|
748
770
|
def __init__(self):
|
|
749
771
|
CoInitialize()
|
|
750
772
|
self.unpicklable_member = Dispatch('FemtetMacro.Femtet')
|
|
@@ -1,36 +1,15 @@
|
|
|
1
|
+
from femtetutils import util
|
|
1
2
|
from pyfemtet.dispatch_extensions import _get_pid
|
|
2
3
|
|
|
3
|
-
import winreg
|
|
4
4
|
import ctypes
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def _get_dll(
|
|
8
|
-
|
|
9
|
-
dll_path =
|
|
7
|
+
def _get_dll():
|
|
8
|
+
femtet_exe_path = util.get_femtet_exe_path()
|
|
9
|
+
dll_path = femtet_exe_path.replace('Femtet.exe', 'ParametricIF.dll')
|
|
10
10
|
return ctypes.cdll.LoadLibrary(dll_path)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def _get_femtet_major_version(Femtet):
|
|
14
|
-
from pyfemtet.core import _version
|
|
15
|
-
version_int8 = _version(Femtet=Femtet)
|
|
16
|
-
return str(version_int8)[0:4]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _get_parametric_dll_path(femtet_major_version) -> str:
|
|
20
|
-
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\FemtetInfo\InstallVersion\x64')
|
|
21
|
-
_, nValues, _ = winreg.QueryInfoKey(key)
|
|
22
|
-
for i in range(nValues):
|
|
23
|
-
name, data, _ = winreg.EnumValue(key, i)
|
|
24
|
-
if name == str(femtet_major_version):
|
|
25
|
-
winreg.CloseKey(key)
|
|
26
|
-
import os
|
|
27
|
-
dll_path = os.path.join(data, 'Program', 'ParametricIF.dll')
|
|
28
|
-
return dll_path
|
|
29
|
-
# ここまで来ていたら失敗
|
|
30
|
-
winreg.CloseKey(key)
|
|
31
|
-
raise RuntimeError('パラメトリック解析機能へのアクセスに失敗しました')
|
|
32
|
-
|
|
33
|
-
|
|
34
13
|
def _get_dll_with_set_femtet(Femtet):
|
|
35
14
|
dll = _get_dll(Femtet)
|
|
36
15
|
pid = _get_pid(Femtet.hWnd)
|
|
@@ -40,6 +19,7 @@ def _get_dll_with_set_femtet(Femtet):
|
|
|
40
19
|
|
|
41
20
|
|
|
42
21
|
def _get_prm_result_names(Femtet):
|
|
22
|
+
"""Used by pyfemtet-opt-gui"""
|
|
43
23
|
out = []
|
|
44
24
|
|
|
45
25
|
# load dll and set target femtet
|
pyfemtet/opt/opt/__init__.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from pyfemtet.opt.opt._base import AbstractOptimizer, logger
|
|
1
|
+
from pyfemtet.opt.opt._base import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
2
2
|
from pyfemtet.opt.opt._optuna import OptunaOptimizer
|
|
3
|
+
from pyfemtet.opt.opt._scipy import ScipyOptimizer
|
|
3
4
|
|
|
4
5
|
__all__ = [
|
|
6
|
+
'ScipyOptimizer',
|
|
5
7
|
'OptunaOptimizer',
|
|
6
8
|
'AbstractOptimizer',
|
|
7
9
|
'logger',
|
pyfemtet/opt/opt/_base.py
CHANGED
|
@@ -20,6 +20,77 @@ logger = get_logger('opt')
|
|
|
20
20
|
logger.setLevel(logging.INFO)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
class OptimizationMethodChecker:
|
|
24
|
+
"""Check implementation of PyFemtet functions."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, opt):
|
|
27
|
+
self.opt = opt
|
|
28
|
+
|
|
29
|
+
def check_parallel(self, raise_error=True):
|
|
30
|
+
function = 'parallel computing'
|
|
31
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
32
|
+
if raise_error:
|
|
33
|
+
raise NotImplementedError(message)
|
|
34
|
+
else:
|
|
35
|
+
logger.warning(message)
|
|
36
|
+
|
|
37
|
+
def check_timeout(self, raise_error=True):
|
|
38
|
+
function = 'timeout'
|
|
39
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
40
|
+
if raise_error:
|
|
41
|
+
raise NotImplementedError(message)
|
|
42
|
+
else:
|
|
43
|
+
logger.warning(message)
|
|
44
|
+
|
|
45
|
+
def check_multi_objective(self, raise_error=True):
|
|
46
|
+
function = 'multi-objective'
|
|
47
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
48
|
+
if raise_error:
|
|
49
|
+
raise NotImplementedError(message)
|
|
50
|
+
else:
|
|
51
|
+
logger.warning(message)
|
|
52
|
+
|
|
53
|
+
def check_strict_constraint(self, raise_error=True):
|
|
54
|
+
function = 'strict_constraint'
|
|
55
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
56
|
+
if raise_error:
|
|
57
|
+
raise NotImplementedError(message)
|
|
58
|
+
else:
|
|
59
|
+
logger.warning(message)
|
|
60
|
+
|
|
61
|
+
def check_constraint(self, raise_error=True):
|
|
62
|
+
function = 'strict_constraint'
|
|
63
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
64
|
+
if raise_error:
|
|
65
|
+
raise NotImplementedError(message)
|
|
66
|
+
else:
|
|
67
|
+
logger.warning(message)
|
|
68
|
+
|
|
69
|
+
def check_skip(self, raise_error=True):
|
|
70
|
+
function = 'skip'
|
|
71
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
72
|
+
if raise_error:
|
|
73
|
+
raise NotImplementedError(message)
|
|
74
|
+
else:
|
|
75
|
+
logger.warning(message)
|
|
76
|
+
|
|
77
|
+
def check_seed(self, raise_error=True):
|
|
78
|
+
function = 'random seed setting'
|
|
79
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
80
|
+
if raise_error:
|
|
81
|
+
raise NotImplementedError(message)
|
|
82
|
+
else:
|
|
83
|
+
logger.warning(message)
|
|
84
|
+
|
|
85
|
+
def check_incomplete_bounds(self, raise_error=True):
|
|
86
|
+
function = 'optimize with no or incomplete bounds'
|
|
87
|
+
message = f'{type(self.opt)} is not implement "{function}"'
|
|
88
|
+
if raise_error:
|
|
89
|
+
raise NotImplementedError(message)
|
|
90
|
+
else:
|
|
91
|
+
logger.warning(message)
|
|
92
|
+
|
|
93
|
+
|
|
23
94
|
class AbstractOptimizer(ABC):
|
|
24
95
|
"""Abstract base class for an interface of optimization library.
|
|
25
96
|
|
|
@@ -45,9 +116,9 @@ class AbstractOptimizer(ABC):
|
|
|
45
116
|
self.fem = None
|
|
46
117
|
self.fem_class = None
|
|
47
118
|
self.fem_kwargs = dict()
|
|
48
|
-
self.parameters = pd.DataFrame()
|
|
49
|
-
self.objectives = dict()
|
|
50
|
-
self.constraints = dict()
|
|
119
|
+
self.parameters: pd.DataFrame = pd.DataFrame()
|
|
120
|
+
self.objectives: dict = dict()
|
|
121
|
+
self.constraints: dict = dict()
|
|
51
122
|
self.entire_status = None # actor
|
|
52
123
|
self.history = None # actor
|
|
53
124
|
self.worker_status = None # actor
|
|
@@ -58,6 +129,7 @@ class AbstractOptimizer(ABC):
|
|
|
58
129
|
self.is_cluster = False
|
|
59
130
|
self.subprocess_idx = None
|
|
60
131
|
self._is_error_exit = False
|
|
132
|
+
self.method_checker: OptimizationMethodChecker = OptimizationMethodChecker(self)
|
|
61
133
|
|
|
62
134
|
def f(self, x):
|
|
63
135
|
"""Get x, update fem analysis, return objectives (and constraints)."""
|
|
@@ -65,10 +137,15 @@ class AbstractOptimizer(ABC):
|
|
|
65
137
|
|
|
66
138
|
# x の更新
|
|
67
139
|
self.parameters['value'] = x
|
|
140
|
+
logger.info(f'Start calculation with input: {x}')
|
|
68
141
|
|
|
69
142
|
# FEM の更新
|
|
70
143
|
logger.debug('fem.update() start')
|
|
71
|
-
|
|
144
|
+
try:
|
|
145
|
+
self.fem.update(self.parameters)
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.warning('An exception has occurred during FEM update.')
|
|
148
|
+
raise e
|
|
72
149
|
|
|
73
150
|
# y, _y, c の更新
|
|
74
151
|
logger.debug('calculate y start')
|
|
@@ -93,6 +170,9 @@ class AbstractOptimizer(ABC):
|
|
|
93
170
|
)
|
|
94
171
|
|
|
95
172
|
logger.debug('history.record end')
|
|
173
|
+
|
|
174
|
+
logger.info(f'End calculation with output: {_y}')
|
|
175
|
+
|
|
96
176
|
return np.array(y), np.array(_y), np.array(c)
|
|
97
177
|
|
|
98
178
|
def _reconstruct_fem(self, skip_reconstruct=False):
|
|
@@ -191,10 +271,10 @@ class AbstractOptimizer(ABC):
|
|
|
191
271
|
try:
|
|
192
272
|
self.run()
|
|
193
273
|
except Exception as e:
|
|
194
|
-
logger.error("
|
|
195
|
-
logger.error("An unexpected error has
|
|
196
|
-
logger.error("
|
|
197
|
-
logger.error(e)
|
|
274
|
+
logger.error("=================================")
|
|
275
|
+
logger.error("An unexpected error has occurred!")
|
|
276
|
+
logger.error("=================================")
|
|
277
|
+
logger.error(f'{type(e)}: {e}')
|
|
198
278
|
self._is_error_exit = True
|
|
199
279
|
self.worker_status.set(OptimizationStatus.CRASHED)
|
|
200
280
|
finally:
|
pyfemtet/opt/opt/_optuna.py
CHANGED
|
@@ -12,15 +12,29 @@ from optuna.study import MaxTrialsCallback
|
|
|
12
12
|
|
|
13
13
|
# pyfemtet relative
|
|
14
14
|
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
15
|
-
from pyfemtet.opt.opt import AbstractOptimizer, logger
|
|
15
|
+
from pyfemtet.opt.opt import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
16
16
|
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
17
17
|
|
|
18
18
|
# filter warnings
|
|
19
19
|
import warnings
|
|
20
20
|
from optuna.exceptions import ExperimentalWarning
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
optuna.logging.set_verbosity(optuna.logging.ERROR)
|
|
24
|
+
|
|
21
25
|
warnings.filterwarnings('ignore', category=ExperimentalWarning)
|
|
22
26
|
|
|
23
27
|
|
|
28
|
+
class OptunaMethodChecker(OptimizationMethodChecker):
|
|
29
|
+
def check_multi_objective(self, raise_error=True): return True
|
|
30
|
+
def check_timeout(self, raise_error=True): return True
|
|
31
|
+
def check_parallel(self, raise_error=True): return True
|
|
32
|
+
def check_constraint(self, raise_error=True): return True
|
|
33
|
+
def check_strict_constraint(self, raise_error=True): return True
|
|
34
|
+
def check_skip(self, raise_error=True): return True
|
|
35
|
+
def check_seed(self, raise_error=True): return True
|
|
36
|
+
|
|
37
|
+
|
|
24
38
|
class OptunaOptimizer(AbstractOptimizer):
|
|
25
39
|
|
|
26
40
|
def __init__(
|
|
@@ -38,6 +52,7 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
38
52
|
self.sampler_kwargs = dict() if sampler_kwargs is None else sampler_kwargs
|
|
39
53
|
self.additional_initial_parameter = []
|
|
40
54
|
self.additional_initial_methods = add_init_method if hasattr(add_init_method, '__iter__') else [add_init_method]
|
|
55
|
+
self.method_checker = OptunaMethodChecker(self)
|
|
41
56
|
|
|
42
57
|
def _objective(self, trial):
|
|
43
58
|
|
|
@@ -50,7 +65,7 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
50
65
|
# candidate x
|
|
51
66
|
x = []
|
|
52
67
|
for i, row in self.parameters.iterrows():
|
|
53
|
-
v = trial.suggest_float(row['name'], row['lb'], row['ub'])
|
|
68
|
+
v = trial.suggest_float(row['name'], row['lb'], row['ub'], step=row['step'])
|
|
54
69
|
x.append(v)
|
|
55
70
|
x = np.array(x).astype(float)
|
|
56
71
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# typing
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
|
|
5
|
+
# built-in
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
# 3rd-party
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
import scipy.optimize
|
|
12
|
+
from scipy.optimize import minimize, OptimizeResult
|
|
13
|
+
|
|
14
|
+
# pyfemtet relative
|
|
15
|
+
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
16
|
+
from pyfemtet.opt.opt import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
17
|
+
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StopIteration2(Exception):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class StopIterationCallback:
|
|
25
|
+
def __init__(self, opt):
|
|
26
|
+
self.opt: ScipyOptimizer = opt
|
|
27
|
+
self.res: OptimizeResult = None
|
|
28
|
+
|
|
29
|
+
def stop_iteration(self):
|
|
30
|
+
# stop iteration gimmick
|
|
31
|
+
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
|
|
32
|
+
if self.opt.minimize_kwargs['method'] == "trust-constr":
|
|
33
|
+
raise StopIteration2 # supports nothing
|
|
34
|
+
elif (
|
|
35
|
+
self.opt.minimize_kwargs['method'] == 'TNC'
|
|
36
|
+
or self.opt.minimize_kwargs['method'] == 'SLSQP'
|
|
37
|
+
or self.opt.minimize_kwargs['method'] == 'COBYLA'
|
|
38
|
+
):
|
|
39
|
+
raise StopIteration2 # supports xk
|
|
40
|
+
else:
|
|
41
|
+
raise StopIteration # supports xk , intermediate_result and StopIteration
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __call__(self, xk=None, intermediate_result=None):
|
|
45
|
+
self.res = intermediate_result
|
|
46
|
+
if self.opt.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
47
|
+
self.opt.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
48
|
+
self.stop_iteration()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ScipyMethodChecker(OptimizationMethodChecker):
|
|
52
|
+
def check_incomplete_bounds(self, raise_error=True): return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ScipyOptimizer(AbstractOptimizer):
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
**minimize_kwargs,
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
Args:
|
|
63
|
+
**minimize_kwargs: Kwargs of `scipy.optimize.minimize`.
|
|
64
|
+
"""
|
|
65
|
+
super().__init__()
|
|
66
|
+
|
|
67
|
+
# define members
|
|
68
|
+
self.minimize_kwargs: dict = dict(
|
|
69
|
+
method='L-BFGS-B',
|
|
70
|
+
)
|
|
71
|
+
self.minimize_kwargs.update(minimize_kwargs)
|
|
72
|
+
self.res: OptimizeResult = None
|
|
73
|
+
self.method_checker: OptimizationMethodChecker = ScipyMethodChecker(self)
|
|
74
|
+
self.stop_iteration_callback = StopIterationCallback(self)
|
|
75
|
+
|
|
76
|
+
def _objective(self, x: np.ndarray): # x: candidate parameter
|
|
77
|
+
# update parameter
|
|
78
|
+
self.parameters['value'] = x
|
|
79
|
+
self.fem.update_parameter(self.parameters)
|
|
80
|
+
|
|
81
|
+
# strict constraints
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
# fem
|
|
85
|
+
try:
|
|
86
|
+
_, obj_values, cns_values = self.f(x)
|
|
87
|
+
except (ModelError, MeshError, SolveError) as e:
|
|
88
|
+
logger.info(e)
|
|
89
|
+
logger.info('以下の変数で FEM 解析に失敗しました。')
|
|
90
|
+
print(self.get_parameter('dict'))
|
|
91
|
+
|
|
92
|
+
# 現状、エラーが起きたらスキップできない
|
|
93
|
+
raise StopIteration2
|
|
94
|
+
|
|
95
|
+
# constraints
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
# # check interruption command
|
|
99
|
+
# if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
100
|
+
# self.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
101
|
+
# raise StopOptimize
|
|
102
|
+
|
|
103
|
+
# objectives to objective
|
|
104
|
+
|
|
105
|
+
return obj_values[0]
|
|
106
|
+
|
|
107
|
+
def _setup_before_parallel(self):
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
def run(self):
|
|
111
|
+
|
|
112
|
+
# create init
|
|
113
|
+
x0 = self.parameters['value'].values
|
|
114
|
+
|
|
115
|
+
# create bounds
|
|
116
|
+
if 'bounds' not in self.minimize_kwargs.keys():
|
|
117
|
+
bounds = []
|
|
118
|
+
for i, row in self.parameters.iterrows():
|
|
119
|
+
lb, ub = row['lb'], row['ub']
|
|
120
|
+
if lb is None: lb = -np.inf
|
|
121
|
+
if ub is None: ub = np.inf
|
|
122
|
+
bounds.append([lb, ub])
|
|
123
|
+
self.minimize_kwargs.update(
|
|
124
|
+
{'bounds': bounds}
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# run optimize
|
|
128
|
+
try:
|
|
129
|
+
res = minimize(
|
|
130
|
+
fun=self._objective,
|
|
131
|
+
x0=x0,
|
|
132
|
+
**self.minimize_kwargs,
|
|
133
|
+
callback=self.stop_iteration_callback,
|
|
134
|
+
)
|
|
135
|
+
except StopIteration2:
|
|
136
|
+
res = None
|
|
137
|
+
logger.warn('Optimization has been interrupted. '
|
|
138
|
+
'Note that you cannot acquire the OptimizationResult '
|
|
139
|
+
'in case of `trust-constr`, `TNC`, `SLSQP` or `COBYLA`.')
|
|
140
|
+
|
|
141
|
+
if res is None:
|
|
142
|
+
self.res = self.stop_iteration_callback.res
|
|
143
|
+
else:
|
|
144
|
+
self.res = res
|