pyfemtet 0.6.3__py3-none-any.whl → 0.6.5__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/_femtet_config_util/autosave.py +28 -8
- pyfemtet/_util/__init__.py +0 -0
- pyfemtet/_util/excel_macro_util.py +197 -0
- pyfemtet/_warning.py +13 -5
- pyfemtet/opt/interface/_excel_interface.py +20 -17
- pyfemtet/opt/interface/_femtet_with_sldworks.py +163 -54
- pyfemtet/opt/optimizer/_optuna/_optuna.py +62 -48
- {pyfemtet-0.6.3.dist-info → pyfemtet-0.6.5.dist-info}/METADATA +1 -1
- {pyfemtet-0.6.3.dist-info → pyfemtet-0.6.5.dist-info}/RECORD +13 -11
- {pyfemtet-0.6.3.dist-info → pyfemtet-0.6.5.dist-info}/LICENSE +0 -0
- {pyfemtet-0.6.3.dist-info → pyfemtet-0.6.5.dist-info}/WHEEL +0 -0
- {pyfemtet-0.6.3.dist-info → pyfemtet-0.6.5.dist-info}/entry_points.txt +0 -0
pyfemtet/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.6.
|
|
1
|
+
__version__ = "0.6.5"
|
|
@@ -4,20 +4,31 @@ from typing import Final
|
|
|
4
4
|
# レジストリのパスと値の名前
|
|
5
5
|
_REGISTRY_PATH: Final[str] = r"SOFTWARE\Murata Software\Femtet2014\Femtet"
|
|
6
6
|
_VALUE_NAME: Final[str] = "AutoSave"
|
|
7
|
+
_DEFAULT_VALUE: Final[bool] = True
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def _get_autosave_enabled() -> bool:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
try:
|
|
12
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, _REGISTRY_PATH) as key:
|
|
13
|
+
value, regtype = winreg.QueryValueEx(key, _VALUE_NAME)
|
|
14
|
+
if regtype == winreg.REG_DWORD:
|
|
15
|
+
return bool(value)
|
|
16
|
+
else:
|
|
17
|
+
raise ValueError("Unexpected registry value type.")
|
|
18
|
+
|
|
19
|
+
except FileNotFoundError: # [WinError 2] 指定されたファイルが見つかりません。
|
|
20
|
+
__create_registry_key()
|
|
21
|
+
return _get_autosave_enabled()
|
|
16
22
|
|
|
17
23
|
|
|
18
24
|
def _set_autosave_enabled(enable: bool) -> None:
|
|
19
|
-
|
|
20
|
-
winreg.
|
|
25
|
+
try:
|
|
26
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, _REGISTRY_PATH, 0, winreg.KEY_SET_VALUE) as key:
|
|
27
|
+
winreg.SetValueEx(key, _VALUE_NAME, 0, winreg.REG_DWORD, int(enable))
|
|
28
|
+
|
|
29
|
+
except FileNotFoundError: # [WinError 2] 指定されたファイルが見つかりません。
|
|
30
|
+
__create_registry_key()
|
|
31
|
+
_set_autosave_enabled(enable)
|
|
21
32
|
|
|
22
33
|
|
|
23
34
|
def __test_autosave_setting():
|
|
@@ -36,3 +47,12 @@ def __test_autosave_setting():
|
|
|
36
47
|
print(f"Current AutoSave setting is {'enabled' if after_setting else 'disabled'}.")
|
|
37
48
|
|
|
38
49
|
assert new_setting == after_setting, "レジストリ編集に失敗しました。"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def __create_registry_key():
|
|
53
|
+
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, _REGISTRY_PATH) as key:
|
|
54
|
+
winreg.SetValueEx(key, _VALUE_NAME, 0, winreg.REG_DWORD, int(_DEFAULT_VALUE))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == '__main__':
|
|
58
|
+
print(_get_autosave_enabled())
|
|
File without changes
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Excel のエラーダイアログを補足します。"""
|
|
2
|
+
import sys
|
|
3
|
+
from time import sleep
|
|
4
|
+
from threading import Thread
|
|
5
|
+
import logging
|
|
6
|
+
import asyncio # for timeout
|
|
7
|
+
import win32gui
|
|
8
|
+
import win32con
|
|
9
|
+
import win32api
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
if __name__ == '__main__':
|
|
13
|
+
formatter = logging.Formatter(logging.BASIC_FORMAT)
|
|
14
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
15
|
+
handler.setFormatter(formatter)
|
|
16
|
+
logger.addHandler(handler)
|
|
17
|
+
logger.setLevel(logging.DEBUG)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _ExcelDialogProcessor:
|
|
21
|
+
|
|
22
|
+
def __init__(self, excel_, timeout):
|
|
23
|
+
self.excel = excel_
|
|
24
|
+
self.__excel_window_title = f' - Excel' # {basename} - Excel
|
|
25
|
+
self.__error_dialog_title = 'Microsoft Visual Basic'
|
|
26
|
+
self.__vbe_window_title = f'Microsoft Visual Basic for Applications - ' # Microsoft Visual Basic for Applications - {basename}
|
|
27
|
+
self.should_stop = False
|
|
28
|
+
self.timeout = timeout
|
|
29
|
+
self.__timed_out = False
|
|
30
|
+
self.__workbook_paths = [wb.FullName for wb in excel_.Workbooks]
|
|
31
|
+
self.__error_raised = False
|
|
32
|
+
self.__excel_state_stash = dict()
|
|
33
|
+
self.__watch_thread = None
|
|
34
|
+
|
|
35
|
+
async def watch(self):
|
|
36
|
+
|
|
37
|
+
while True:
|
|
38
|
+
if self.should_stop:
|
|
39
|
+
logger.debug('エラーダイアログの監視を終了')
|
|
40
|
+
return
|
|
41
|
+
logger.debug('エラーダイアログを監視中...')
|
|
42
|
+
|
|
43
|
+
win32gui.EnumWindows(self.enum_callback_to_activate, [])
|
|
44
|
+
await asyncio.sleep(0.5)
|
|
45
|
+
|
|
46
|
+
found = []
|
|
47
|
+
win32gui.EnumWindows(self.enum_callback_to_close_dialog, found)
|
|
48
|
+
await asyncio.sleep(0.5)
|
|
49
|
+
if any(found):
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
logger.debug('ブックを閉じます。')
|
|
53
|
+
win32gui.EnumWindows(self.enum_callback_to_close_book, []) # 成功していればこの時点でメイン処理では例外がスローされる
|
|
54
|
+
await asyncio.sleep(1)
|
|
55
|
+
|
|
56
|
+
logger.debug('確認ダイアログがあれば閉じます。')
|
|
57
|
+
win32gui.EnumWindows(self.enum_callback_to_close_confirm_dialog, [])
|
|
58
|
+
await asyncio.sleep(1)
|
|
59
|
+
self.__error_raised = True
|
|
60
|
+
|
|
61
|
+
def enum_callback_to_activate(self, hwnd, _):
|
|
62
|
+
title = win32gui.GetWindowText(hwnd)
|
|
63
|
+
# Excel 本体
|
|
64
|
+
if self.__excel_window_title in title:
|
|
65
|
+
# Visible == True の際、エラーが発生した際、
|
|
66
|
+
# 一度 Excel ウィンドウをアクティブ化しないと dialog が出てこない
|
|
67
|
+
# が、これだけではダメかも。
|
|
68
|
+
win32gui.PostMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
|
|
69
|
+
|
|
70
|
+
def enum_callback_to_close_dialog(self, hwnd, found):
|
|
71
|
+
title = win32gui.GetWindowText(hwnd)
|
|
72
|
+
# エラーダイアログ
|
|
73
|
+
if self.__error_dialog_title == title:
|
|
74
|
+
# 何故かこのコマンド以外受け付けず、
|
|
75
|
+
# このコマンドで問答無用でデバッグモードに入る
|
|
76
|
+
logger.debug('エラーダイアログを見つけました。')
|
|
77
|
+
win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
|
|
78
|
+
win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0)
|
|
79
|
+
logger.debug('エラーダイアログを閉じました。')
|
|
80
|
+
found.append(True)
|
|
81
|
+
|
|
82
|
+
def enum_callback_to_close_confirm_dialog(self, hwnd, _):
|
|
83
|
+
title = win32gui.GetWindowText(hwnd)
|
|
84
|
+
# 確認ダイアログ
|
|
85
|
+
if "Microsoft Excel" in title:
|
|
86
|
+
# DisplayAlerts が False の場合は不要
|
|
87
|
+
win32gui.SendMessage(hwnd, win32con.WM_SYSCOMMAND, win32con.SC_CLOSE, 0)
|
|
88
|
+
|
|
89
|
+
def enum_callback_to_close_book(self, hwnd, _):
|
|
90
|
+
title = win32gui.GetWindowText(hwnd)
|
|
91
|
+
# VBE
|
|
92
|
+
if self.__vbe_window_title in title:
|
|
93
|
+
# 何故かこれで book 本体が閉じる
|
|
94
|
+
win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0)
|
|
95
|
+
|
|
96
|
+
async def watch_main(self):
|
|
97
|
+
try:
|
|
98
|
+
await asyncio.wait_for(self.watch(), timeout=self.timeout)
|
|
99
|
+
except asyncio.TimeoutError:
|
|
100
|
+
logger.debug('タイムアウトしました。')
|
|
101
|
+
self.should_stop = True
|
|
102
|
+
self.__timed_out = True
|
|
103
|
+
|
|
104
|
+
def __enter__(self):
|
|
105
|
+
logger.debug('Excel を 不可視にします。')
|
|
106
|
+
self.__excel_state_stash['visible'] = self.excel.Visible
|
|
107
|
+
self.__excel_state_stash['display_alerts'] = self.excel.DisplayAlerts
|
|
108
|
+
self.excel.Visible = False
|
|
109
|
+
self.excel.DisplayAlerts = False
|
|
110
|
+
|
|
111
|
+
logger.debug('エラー監視を開始します。')
|
|
112
|
+
self.__watch_thread = Thread(
|
|
113
|
+
target=asyncio.run,
|
|
114
|
+
args=(self.watch_main(),),
|
|
115
|
+
)
|
|
116
|
+
self.__watch_thread.start()
|
|
117
|
+
|
|
118
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
119
|
+
|
|
120
|
+
logger.debug('Excel の状態を回復します。')
|
|
121
|
+
|
|
122
|
+
self.should_stop = True
|
|
123
|
+
self.__watch_thread.join()
|
|
124
|
+
|
|
125
|
+
self.excel.Visible = self.__excel_state_stash['visible']
|
|
126
|
+
self.excel.DisplayAlerts = self.__excel_state_stash['display_alerts']
|
|
127
|
+
|
|
128
|
+
if self.__timed_out:
|
|
129
|
+
logger.debug('Excel プロセスを強制終了します。')
|
|
130
|
+
logger.error('Excel プロセス強制終了は未実装です。')
|
|
131
|
+
raise TimeoutError('マクロの実行がタイムアウトしました。')
|
|
132
|
+
|
|
133
|
+
# if exc_type is not None:
|
|
134
|
+
# if issubclass(exc_type, com_error) and self.__error_raised:
|
|
135
|
+
if self.__error_raised:
|
|
136
|
+
logger.debug('エラーハンドリングの副作用でブックを閉じているので'
|
|
137
|
+
'Excel のブックを開きなおします。')
|
|
138
|
+
for wb_path in self.__workbook_paths:
|
|
139
|
+
self.excel.Workbooks.Open(wb_path)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def watch_excel_macro_error(excel_, timeout):
|
|
143
|
+
"""Excel のエラーダイアログの出現を監視し、検出されればブックを閉じます。"""
|
|
144
|
+
return _ExcelDialogProcessor(excel_, timeout)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == '__main__':
|
|
148
|
+
|
|
149
|
+
import os
|
|
150
|
+
os.chdir(os.path.dirname(__file__))
|
|
151
|
+
|
|
152
|
+
path = os.path.abspath('sample.xlsm')
|
|
153
|
+
path2 = os.path.abspath('sample2.xlsm')
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
from win32com.client import Dispatch
|
|
157
|
+
from pythoncom import com_error
|
|
158
|
+
|
|
159
|
+
logger.debug('Excel を起動しています。')
|
|
160
|
+
excel = Dispatch('Excel.Application')
|
|
161
|
+
excel.Visible = True
|
|
162
|
+
excel.DisplayAlerts = False
|
|
163
|
+
excel.Interactive = True
|
|
164
|
+
|
|
165
|
+
logger.debug('Workbook を開いています。')
|
|
166
|
+
excel.Workbooks.Open(path)
|
|
167
|
+
|
|
168
|
+
logger.debug('別の Workbook を開いています。')
|
|
169
|
+
excel.Workbooks.Open(path2)
|
|
170
|
+
|
|
171
|
+
logger.debug('Workbook に変更を加えます。')
|
|
172
|
+
excel.Workbooks(1).ActiveSheet.Range('A1').Value = 1.
|
|
173
|
+
|
|
174
|
+
logger.debug('開いている Workbooks 数:')
|
|
175
|
+
logger.debug(excel.Workbooks.Count)
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
with watch_excel_macro_error(excel, timeout=10):
|
|
179
|
+
sleep(3)
|
|
180
|
+
try:
|
|
181
|
+
excel.Run('raise_error')
|
|
182
|
+
except com_error as e:
|
|
183
|
+
logger.debug('この段階ではまだ Excel 回復機能が働きません。')
|
|
184
|
+
logger.debug('開いている Workbooks 数:')
|
|
185
|
+
logger.debug(excel.Workbooks.Count)
|
|
186
|
+
raise e
|
|
187
|
+
|
|
188
|
+
# excel.Run('no_error')
|
|
189
|
+
|
|
190
|
+
except com_error as e:
|
|
191
|
+
logger.debug('メイン処理でエラーを補足しました。:')
|
|
192
|
+
logger.debug(e)
|
|
193
|
+
|
|
194
|
+
logger.debug('開いている Workbooks 数:')
|
|
195
|
+
logger.debug(excel.Workbooks.Count)
|
|
196
|
+
|
|
197
|
+
logger.debug('保存していない場合、Workbook の変更は失われます。')
|
pyfemtet/_warning.py
CHANGED
|
@@ -2,15 +2,19 @@ import warnings
|
|
|
2
2
|
from functools import wraps
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
def show_experimental_warning(feature_name):
|
|
6
|
+
warnings.warn(
|
|
7
|
+
f"The function '{feature_name}' is experimental and may change in the future.",
|
|
8
|
+
category=UserWarning,
|
|
9
|
+
stacklevel=2
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
5
13
|
def experimental_feature(func):
|
|
6
14
|
|
|
7
15
|
@wraps(func)
|
|
8
16
|
def wrapper(*args, **kwargs):
|
|
9
|
-
|
|
10
|
-
f"The function '{func.__name__}' is experimental and may change in the future.",
|
|
11
|
-
category=UserWarning,
|
|
12
|
-
stacklevel=2
|
|
13
|
-
)
|
|
17
|
+
show_experimental_warning(func.__name__)
|
|
14
18
|
return func(*args, **kwargs)
|
|
15
19
|
|
|
16
20
|
return wrapper
|
|
@@ -62,6 +66,10 @@ if __name__ == '__main__':
|
|
|
62
66
|
print("This is an experimental function.")
|
|
63
67
|
|
|
64
68
|
class Sample:
|
|
69
|
+
|
|
70
|
+
def __init__(self):
|
|
71
|
+
show_experimental_warning("Sample")
|
|
72
|
+
|
|
65
73
|
@experimental_feature
|
|
66
74
|
def add(self, a, b):
|
|
67
75
|
return a + b
|
|
@@ -25,7 +25,9 @@ from pyfemtet.dispatch_extensions._impl import _NestableSpawnProcess
|
|
|
25
25
|
from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
|
|
26
26
|
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
|
|
27
27
|
|
|
28
|
-
from pyfemtet.
|
|
28
|
+
from pyfemtet._util.excel_macro_util import watch_excel_macro_error
|
|
29
|
+
|
|
30
|
+
from pyfemtet._warning import show_experimental_warning
|
|
29
31
|
|
|
30
32
|
import logging
|
|
31
33
|
|
|
@@ -33,7 +35,6 @@ logger = logging.getLogger(__name__)
|
|
|
33
35
|
logger.setLevel(logging.INFO)
|
|
34
36
|
|
|
35
37
|
|
|
36
|
-
@experimental_class
|
|
37
38
|
class ExcelInterface(FEMInterface):
|
|
38
39
|
|
|
39
40
|
input_xlsm_path: Path # 操作対象の xlsm パス
|
|
@@ -52,8 +53,8 @@ class ExcelInterface(FEMInterface):
|
|
|
52
53
|
wb_output: CDispatch # システムを構成する Workbook
|
|
53
54
|
sh_output: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
|
|
54
55
|
|
|
55
|
-
visible: bool =
|
|
56
|
-
display_alerts: bool =
|
|
56
|
+
visible: bool = False # excel を可視化するかどうか
|
|
57
|
+
display_alerts: bool = False # ダイアログを表示するかどうか
|
|
57
58
|
|
|
58
59
|
_load_problem_from_me: bool = True # TODO: add_parameter() 等を省略するかどうか。定義するだけでフラグとして機能する。
|
|
59
60
|
_excel_pid: int
|
|
@@ -69,7 +70,9 @@ class ExcelInterface(FEMInterface):
|
|
|
69
70
|
procedure_name: str = None,
|
|
70
71
|
procedure_args: list or tuple = None,
|
|
71
72
|
connect_method: str = 'auto', # or 'new'
|
|
73
|
+
procedure_timeout: float or None = None,
|
|
72
74
|
):
|
|
75
|
+
show_experimental_warning("ExcelInterface")
|
|
73
76
|
|
|
74
77
|
# 初期化
|
|
75
78
|
self.input_xlsm_path = None # あとで取得する
|
|
@@ -81,6 +84,7 @@ class ExcelInterface(FEMInterface):
|
|
|
81
84
|
assert connect_method in ['new', 'auto']
|
|
82
85
|
self.connect_method = connect_method
|
|
83
86
|
self._femtet_autosave_buffer = _get_autosave_enabled()
|
|
87
|
+
self.procedure_timeout = procedure_timeout
|
|
84
88
|
|
|
85
89
|
# dask サブプロセスのときは space 直下の input_xlsm_path を参照する
|
|
86
90
|
try:
|
|
@@ -119,8 +123,9 @@ class ExcelInterface(FEMInterface):
|
|
|
119
123
|
procedure_name=self.procedure_name,
|
|
120
124
|
procedure_args=self.procedure_args,
|
|
121
125
|
connect_method='new', # subprocess で connect する際は new を強制する
|
|
126
|
+
procedure_timeout=self.procedure_timeout,
|
|
122
127
|
)
|
|
123
|
-
|
|
128
|
+
FEMInterface.__init__(self, **kwargs)
|
|
124
129
|
|
|
125
130
|
def __del__(self):
|
|
126
131
|
try:
|
|
@@ -256,20 +261,18 @@ class ExcelInterface(FEMInterface):
|
|
|
256
261
|
|
|
257
262
|
# マクロ実行
|
|
258
263
|
try:
|
|
259
|
-
self.excel.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout):
|
|
265
|
+
self.excel.Run(
|
|
266
|
+
f'{self.wb_input.Name}!{self.procedure_name}',
|
|
267
|
+
*self.procedure_args
|
|
268
|
+
)
|
|
263
269
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
raise SolveError('Excelアップデートに失敗しました')
|
|
270
|
+
# 再計算
|
|
271
|
+
self.excel.CalculateFull()
|
|
272
|
+
|
|
273
|
+
except com_error:
|
|
274
|
+
raise SolveError(f'Failed to run macro {self.wb_input.Name}!{self.procedure_name}')
|
|
270
275
|
|
|
271
|
-
# 再計算
|
|
272
|
-
self.excel.CalculateFull()
|
|
273
276
|
|
|
274
277
|
def quit(self):
|
|
275
278
|
logger.info('Excel-Femtet の終了処理を開始します。') # FIXME: message にする
|
|
@@ -3,10 +3,11 @@ import re
|
|
|
3
3
|
from time import sleep, time
|
|
4
4
|
|
|
5
5
|
import pandas as pd
|
|
6
|
-
from dask.distributed import get_worker
|
|
6
|
+
from dask.distributed import get_worker, Lock
|
|
7
7
|
|
|
8
8
|
from win32com.client import DispatchEx
|
|
9
|
-
|
|
9
|
+
# noinspection PyUnresolvedReferences
|
|
10
|
+
from pythoncom import CoInitialize, CoUninitialize, com_error
|
|
10
11
|
|
|
11
12
|
from pyfemtet.core import ModelError
|
|
12
13
|
from pyfemtet.opt.interface import FemtetInterface, logger
|
|
@@ -47,34 +48,58 @@ class FemtetWithSolidworksInterface(FemtetInterface):
|
|
|
47
48
|
def __init__(
|
|
48
49
|
self,
|
|
49
50
|
sldprt_path,
|
|
51
|
+
quit_sldworks_on_terminate=False,
|
|
50
52
|
**kwargs
|
|
51
53
|
):
|
|
52
54
|
# 引数の処理
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
self._orig_sldprt_basename = os.path.basename(sldprt_path)
|
|
56
|
+
|
|
57
|
+
# # dask サブプロセスのときは space 直下の sldprt_path を参照する
|
|
58
|
+
# try:
|
|
59
|
+
# worker = get_worker()
|
|
60
|
+
# space = worker.local_directory
|
|
61
|
+
# name_ext = os.path.basename(sldprt_path)
|
|
62
|
+
# name, ext = os.path.splitext(name_ext)
|
|
63
|
+
# self.sldprt_path = os.path.join(space, name_ext)
|
|
64
|
+
#
|
|
65
|
+
# # ただし solidworks は 1 プロセスで同名のファイルを開けないので
|
|
66
|
+
# # 名前を更新する
|
|
67
|
+
# new_sldprt_path = os.path.join(
|
|
68
|
+
# space,
|
|
69
|
+
# f'{name}'
|
|
70
|
+
# f'_{os.path.basename(space)}' # worker に対し一意
|
|
71
|
+
# f'{ext}' # ext は . を含む
|
|
72
|
+
# )
|
|
73
|
+
# os.rename(
|
|
74
|
+
# self.sldprt_path,
|
|
75
|
+
# new_sldprt_path
|
|
76
|
+
# )
|
|
77
|
+
# self.sldprt_path = new_sldprt_path
|
|
78
|
+
#
|
|
79
|
+
# except ValueError: # get_worker に失敗した場合
|
|
80
|
+
# self.sldprt_path = os.path.abspath(sldprt_path)
|
|
81
|
+
self.sldprt_path = os.path.abspath(sldprt_path)
|
|
82
|
+
self.quit_sldworks_on_terminate = quit_sldworks_on_terminate
|
|
60
83
|
|
|
61
84
|
# FemtetInterface の設定 (femprj_path, model_name の更新など)
|
|
62
85
|
# + restore 情報の上書き
|
|
63
86
|
super().__init__(
|
|
64
87
|
sldprt_path=self.sldprt_path,
|
|
88
|
+
quit_sldworks_on_terminate=self.quit_sldworks_on_terminate,
|
|
65
89
|
**kwargs
|
|
66
90
|
)
|
|
67
91
|
|
|
68
92
|
def initialize_sldworks_connection(self):
|
|
69
93
|
# SolidWorks を捕まえ、ファイルを開く
|
|
94
|
+
|
|
70
95
|
self.swApp = DispatchEx('SLDWORKS.Application')
|
|
71
96
|
self.swApp.Visible = True
|
|
72
97
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
98
|
+
# solidworks は単一プロセスなので開くファイルはひとつだけ
|
|
99
|
+
try:
|
|
100
|
+
get_worker()
|
|
101
|
+
except ValueError:
|
|
102
|
+
self.swApp.OpenDoc(self.sldprt_path, self.swDocPART)
|
|
78
103
|
|
|
79
104
|
def check_param_value(self, param_name):
|
|
80
105
|
"""Override FemtetInterface.check_param_value().
|
|
@@ -104,25 +129,39 @@ class FemtetWithSolidworksInterface(FemtetInterface):
|
|
|
104
129
|
# Femtet が参照している x_t パスを取得する
|
|
105
130
|
x_t_path = self.Femtet.Gaudi.LastXTPath
|
|
106
131
|
|
|
132
|
+
# dask サブプロセスならば競合しないよう保存先を scratch 直下にしておく
|
|
133
|
+
try:
|
|
134
|
+
get_worker()
|
|
135
|
+
x_t_path = os.path.splitext(self.sldprt_path)[0] + '.x_t'
|
|
136
|
+
|
|
137
|
+
except ValueError: # No worker found
|
|
138
|
+
pass
|
|
139
|
+
|
|
107
140
|
# 前のが存在するならば消しておく
|
|
108
141
|
if os.path.isfile(x_t_path):
|
|
109
142
|
os.remove(x_t_path)
|
|
110
143
|
|
|
111
144
|
# solidworks のモデルの更新
|
|
112
|
-
|
|
145
|
+
try:
|
|
146
|
+
with Lock('update-model-sldworks'):
|
|
147
|
+
sleep(0.5) # 並列処理でクラッシュすることが多かったため試験的に導入
|
|
148
|
+
self.update_sw_model(parameters, x_t_path)
|
|
113
149
|
|
|
114
|
-
#
|
|
115
|
-
|
|
150
|
+
# femopt を使わない場合
|
|
151
|
+
except RuntimeError: # <class 'distributed.lock.Lock'> object not properly initialized. ...
|
|
152
|
+
self.update_sw_model(parameters, x_t_path)
|
|
116
153
|
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
154
|
+
# dask サブプロセスならば LastXTPath を更新する
|
|
155
|
+
try:
|
|
156
|
+
get_worker()
|
|
157
|
+
try:
|
|
158
|
+
self.Femtet.Gaudi.LastXTPath = x_t_path
|
|
159
|
+
except (KeyError, AttributeError, com_error):
|
|
160
|
+
raise RuntimeError('This feature is available from Femtet version 2023.2. Please update Femtet.')
|
|
161
|
+
|
|
162
|
+
# dask を使わない場合
|
|
163
|
+
except ValueError: # No worker found
|
|
164
|
+
pass
|
|
126
165
|
|
|
127
166
|
# モデルの再インポート
|
|
128
167
|
self._call_femtet_api(
|
|
@@ -145,40 +184,78 @@ class FemtetWithSolidworksInterface(FemtetInterface):
|
|
|
145
184
|
# femprj モデルの変数も更新
|
|
146
185
|
super().update_model(parameters)
|
|
147
186
|
|
|
148
|
-
def update_sw_model(self, parameters: pd.DataFrame):
|
|
187
|
+
def update_sw_model(self, parameters: pd.DataFrame, x_t_path):
|
|
149
188
|
"""Update .sldprt"""
|
|
189
|
+
|
|
150
190
|
# df を dict に変換
|
|
151
191
|
user_param_dict = {}
|
|
152
192
|
for i, row in parameters.iterrows():
|
|
153
193
|
user_param_dict[row['name']] = row['value']
|
|
154
194
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
195
|
+
while True:
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
|
|
199
|
+
# ===== model を取得 =====
|
|
200
|
+
swModel = get_model_by_basename(self.swApp, os.path.basename(self.sldprt_path))
|
|
201
|
+
|
|
202
|
+
# ===== equation manager を取得 =====
|
|
203
|
+
swEqnMgr = swModel.GetEquationMgr
|
|
204
|
+
nEquation = swEqnMgr.GetCount
|
|
205
|
+
|
|
206
|
+
# プロパティを退避
|
|
207
|
+
buffer_aso = swEqnMgr.AutomaticSolveOrder
|
|
208
|
+
buffer_ar = swEqnMgr.AutomaticRebuild
|
|
209
|
+
swEqnMgr.AutomaticSolveOrder = False
|
|
210
|
+
swEqnMgr.AutomaticRebuild = False
|
|
211
|
+
|
|
212
|
+
for i in range(nEquation):
|
|
213
|
+
# name, equation の取得
|
|
214
|
+
current_equation = swEqnMgr.Equation(i)
|
|
215
|
+
current_name = self._get_name_from_equation(current_equation)
|
|
216
|
+
# 対象なら処理
|
|
217
|
+
if current_name in list(user_param_dict.keys()):
|
|
218
|
+
new_equation = f'"{current_name}" = {user_param_dict[current_name]}'
|
|
219
|
+
swEqnMgr.Equation(i, new_equation)
|
|
220
|
+
|
|
221
|
+
# 式の計算
|
|
222
|
+
# noinspection PyStatementEffect
|
|
223
|
+
swEqnMgr.EvaluateAll # always returns -1
|
|
224
|
+
|
|
225
|
+
# プロパティをもとに戻す
|
|
226
|
+
swEqnMgr.AutomaticSolveOrder = buffer_aso
|
|
227
|
+
swEqnMgr.AutomaticRebuild = buffer_ar
|
|
228
|
+
|
|
229
|
+
# 更新する(ここで失敗はしうる)
|
|
230
|
+
result = swModel.EditRebuild3 # モデル再構築
|
|
231
|
+
if not result:
|
|
232
|
+
raise ModelError(Msg.ERR_UPDATE_SOLIDWORKS_MODEL_FAILED)
|
|
233
|
+
|
|
234
|
+
# export as x_t
|
|
235
|
+
swModel.SaveAs(x_t_path)
|
|
236
|
+
|
|
237
|
+
# 30 秒待っても x_t ができてなければエラー(COM なのでありうる)
|
|
238
|
+
timeout = 30
|
|
239
|
+
start = time()
|
|
240
|
+
while True:
|
|
241
|
+
if os.path.isfile(x_t_path):
|
|
242
|
+
break
|
|
243
|
+
if time() - start > timeout:
|
|
244
|
+
raise ModelError(Msg.ERR_MODEL_UPDATE_FAILED)
|
|
245
|
+
sleep(1)
|
|
246
|
+
|
|
247
|
+
except AttributeError as e:
|
|
248
|
+
if 'SLDWORKS.Application.' in str(e):
|
|
249
|
+
# re-launch solidworks
|
|
250
|
+
self.swApp = DispatchEx('SLDWORKS.Application')
|
|
251
|
+
self.swApp.Visible = True
|
|
252
|
+
self.swApp.OpenDoc(self.sldprt_path, self.swDocPART)
|
|
253
|
+
continue
|
|
254
|
+
|
|
255
|
+
else:
|
|
256
|
+
raise e
|
|
257
|
+
|
|
258
|
+
break
|
|
182
259
|
|
|
183
260
|
def _get_name_from_equation(self, equation: str):
|
|
184
261
|
pattern = r'^\s*"(.+?)"\s*$'
|
|
@@ -187,3 +264,35 @@ class FemtetWithSolidworksInterface(FemtetInterface):
|
|
|
187
264
|
return matched.group(1)
|
|
188
265
|
else:
|
|
189
266
|
return None
|
|
267
|
+
|
|
268
|
+
def quit(self, timeout=1, force=True):
|
|
269
|
+
if self.quit_sldworks_on_terminate:
|
|
270
|
+
try:
|
|
271
|
+
get_worker()
|
|
272
|
+
except ValueError:
|
|
273
|
+
try:
|
|
274
|
+
self.swApp.ExitApp()
|
|
275
|
+
except AttributeError:
|
|
276
|
+
pass
|
|
277
|
+
|
|
278
|
+
super().quit(timeout, force)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def get_model_by_basename(swApp, basename):
|
|
282
|
+
swModel = swApp.GetFirstDocument
|
|
283
|
+
while swModel is not None:
|
|
284
|
+
pathname = swModel.GetPathName
|
|
285
|
+
if os.path.basename(pathname) == basename:
|
|
286
|
+
from win32com.client import Dispatch
|
|
287
|
+
# swModel_ = swApp.ActivateDoc3(
|
|
288
|
+
# basename,
|
|
289
|
+
# False,
|
|
290
|
+
# 1, # swRebuildOnActivation_e.swDontRebuildActiveDoc,
|
|
291
|
+
# Dispatch("Scripting.List"),
|
|
292
|
+
# )
|
|
293
|
+
swApp.OpenDoc(pathname, 1)
|
|
294
|
+
swModel_ = swApp.ActiveDoc
|
|
295
|
+
return swModel_
|
|
296
|
+
else:
|
|
297
|
+
swModel = swModel.GetNext
|
|
298
|
+
raise ModuleNotFoundError(f'No model named {basename}')
|
|
@@ -7,8 +7,8 @@ import inspect
|
|
|
7
7
|
|
|
8
8
|
# 3rd-party
|
|
9
9
|
import optuna
|
|
10
|
-
from optuna.trial import TrialState
|
|
11
|
-
from optuna.study import MaxTrialsCallback
|
|
10
|
+
from optuna.trial import TrialState, FrozenTrial
|
|
11
|
+
from optuna.study import MaxTrialsCallback, Study
|
|
12
12
|
|
|
13
13
|
# pyfemtet relative
|
|
14
14
|
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
@@ -86,6 +86,8 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
86
86
|
self.additional_initial_parameter = []
|
|
87
87
|
self.additional_initial_methods = add_init_method if hasattr(add_init_method, '__iter__') else [add_init_method]
|
|
88
88
|
self.method_checker = OptunaMethodChecker(self)
|
|
89
|
+
self._temporary_storage = None
|
|
90
|
+
self._temporary_storage_path = '_pyfemtet_temporary_file.db'
|
|
89
91
|
|
|
90
92
|
def _objective(self, trial):
|
|
91
93
|
|
|
@@ -277,6 +279,35 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
277
279
|
f'sqlite:///{storage_path}',
|
|
278
280
|
)
|
|
279
281
|
|
|
282
|
+
# if TPESampler, create temporary study
|
|
283
|
+
# due to instability in case of many pruned trials.
|
|
284
|
+
from optuna.samplers import TPESampler
|
|
285
|
+
if issubclass(self.sampler_class, TPESampler):
|
|
286
|
+
|
|
287
|
+
if os.path.exists(self._temporary_storage_path):
|
|
288
|
+
os.remove(self._temporary_storage_path)
|
|
289
|
+
|
|
290
|
+
self._temporary_storage = optuna.integration.dask.DaskStorage(
|
|
291
|
+
f'sqlite:///{self._temporary_storage_path}',
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
study = optuna.load_study(
|
|
295
|
+
study_name=None,
|
|
296
|
+
storage=self.storage,
|
|
297
|
+
sampler=None,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
_study = optuna.create_study(
|
|
301
|
+
study_name='tmp',
|
|
302
|
+
storage=self._temporary_storage,
|
|
303
|
+
load_if_exists=True,
|
|
304
|
+
directions=['minimize'] * len(self.objectives),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Copy COMPLETE trials to temporary study.
|
|
308
|
+
existing_trials = study.get_trials(states=(optuna.trial.TrialState.COMPLETE,))
|
|
309
|
+
_study.add_trials(existing_trials)
|
|
310
|
+
|
|
280
311
|
def add_init_parameter(
|
|
281
312
|
self,
|
|
282
313
|
parameter: dict or Iterable,
|
|
@@ -336,62 +367,45 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
336
367
|
sampler=sampler,
|
|
337
368
|
)
|
|
338
369
|
|
|
339
|
-
#
|
|
340
|
-
|
|
341
|
-
# Pruned が多いとエラーを起こす挙動があるので、
|
|
342
|
-
# Pruned な Trial は remove したい。
|
|
343
|
-
# study.remove_trial がないので、一度ダミー
|
|
344
|
-
# study を作成して最適化終了後に結果をコピーする。
|
|
345
|
-
if isinstance(sampler, optuna.samplers.TPESampler):
|
|
346
|
-
tmp_db = f"tmp{self.subprocess_idx}.db"
|
|
347
|
-
if os.path.exists(tmp_db):
|
|
348
|
-
os.remove(tmp_db)
|
|
349
|
-
|
|
350
|
-
_study = optuna.create_study(
|
|
351
|
-
study_name="tmp",
|
|
352
|
-
storage=f"sqlite:///{tmp_db}",
|
|
353
|
-
sampler=sampler,
|
|
354
|
-
directions=['minimize']*len(self.objectives),
|
|
355
|
-
load_if_exists=False,
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
# 既存の trials のうち COMPLETE のものを取得
|
|
359
|
-
existing_trials = study.get_trials(states=(optuna.trial.TrialState.COMPLETE,))
|
|
360
|
-
_study.add_trials(existing_trials)
|
|
361
|
-
|
|
370
|
+
# use temporary storage or not
|
|
371
|
+
if self._temporary_storage is None:
|
|
362
372
|
# run
|
|
363
|
-
|
|
373
|
+
study.optimize(
|
|
364
374
|
self._objective,
|
|
365
375
|
timeout=self.timeout,
|
|
366
376
|
callbacks=self.optimize_callbacks,
|
|
367
377
|
)
|
|
368
378
|
|
|
369
|
-
|
|
370
|
-
#
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
# Write back added trials to the existing study.
|
|
378
|
-
study.add_trials(added_trials)
|
|
379
|
+
else:
|
|
380
|
+
# load study
|
|
381
|
+
_study = optuna.load_study(
|
|
382
|
+
study_name="tmp",
|
|
383
|
+
storage=self._temporary_storage,
|
|
384
|
+
sampler=sampler,
|
|
385
|
+
)
|
|
379
386
|
|
|
380
|
-
#
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
import gc
|
|
387
|
-
gc.collect()
|
|
388
|
-
if os.path.exists(tmp_db):
|
|
389
|
-
os.remove(tmp_db)
|
|
387
|
+
# Add callback to coppy back each trial.
|
|
388
|
+
class CopyBack:
|
|
389
|
+
def __call__(self, _: Study, trial: FrozenTrial) -> None:
|
|
390
|
+
# Write back added trials to the existing study.
|
|
391
|
+
study.add_trial(trial)
|
|
392
|
+
self.optimize_callbacks.append(CopyBack())
|
|
390
393
|
|
|
391
|
-
else:
|
|
392
394
|
# run
|
|
393
|
-
|
|
395
|
+
_study.optimize(
|
|
394
396
|
self._objective,
|
|
395
397
|
timeout=self.timeout,
|
|
396
398
|
callbacks=self.optimize_callbacks,
|
|
397
399
|
)
|
|
400
|
+
|
|
401
|
+
# clean up
|
|
402
|
+
try:
|
|
403
|
+
self._temporary_storage.remove_session()
|
|
404
|
+
del self._temporary_storage
|
|
405
|
+
del _study
|
|
406
|
+
import gc
|
|
407
|
+
gc.collect()
|
|
408
|
+
if os.path.exists(self._temporary_storage_path):
|
|
409
|
+
os.remove(self._temporary_storage_path)
|
|
410
|
+
except:
|
|
411
|
+
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
pyfemtet/__init__.py,sha256=
|
|
1
|
+
pyfemtet/__init__.py,sha256=_f8whmZJ36ZCTk0Zldzy3KONxDBm0bxyKt7pCSHC3cE,21
|
|
2
2
|
pyfemtet/_femtet_config_util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
pyfemtet/_femtet_config_util/autosave.py,sha256=
|
|
3
|
+
pyfemtet/_femtet_config_util/autosave.py,sha256=dNirA9XGuFehas8_Jkj2BW9GOzMbPyhnt1WHcH_ObSU,2070
|
|
4
4
|
pyfemtet/_femtet_config_util/exit.py,sha256=sx8Wcepgi-lL5qJpJl6WvlzbeVheU9_Dtf1f38mmoTo,1822
|
|
5
5
|
pyfemtet/_message/1. make_pot.bat,sha256=wrTA0YaL7nUfNB0cS8zljOmwq2qgyG6RMwHQbrwjvY4,476
|
|
6
6
|
pyfemtet/_message/2. make_mo.bat,sha256=6shJ3Yn4BXjDc0hhv_kiGUtVTq4oSRz8-iS4vW29rNE,155
|
|
@@ -9,7 +9,9 @@ pyfemtet/_message/babel.cfg,sha256=AQIFCQ7NlAA84PhV0gowHhbIXH41zA55mzhgyROniJk,7
|
|
|
9
9
|
pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po,sha256=F2bJGHVMtk086pekjVwY2dluCSl7qeYPgJe1A9CSrxA,24526
|
|
10
10
|
pyfemtet/_message/locales/messages.pot,sha256=8Yjf462pJdEtxBLySKT34zMG5CH5uLB_8VaJQll_QsY,14493
|
|
11
11
|
pyfemtet/_message/messages.py,sha256=F8ENLZKoHq5irn-Ag7rqA3aSDsTmRWDyNHvOLY76ROI,13368
|
|
12
|
-
pyfemtet/
|
|
12
|
+
pyfemtet/_util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
pyfemtet/_util/excel_macro_util.py,sha256=Euxz3QmvTnnQCkWnbnPhvX7xXxppRONX69VlFxNCojA,7540
|
|
14
|
+
pyfemtet/_warning.py,sha256=TSOj8mOhuyfOUJB24LsW6GNhTA3IzIEevJw_hLKTrq8,2205
|
|
13
15
|
pyfemtet/core.py,sha256=3lqfBGJ5IuKz2Nqj5pRo7YQqKwx_0ZDL72u95Ur_1p0,1386
|
|
14
16
|
pyfemtet/dispatch_extensions/__init__.py,sha256=MI9b6oIS2IXnTNHy8jvZ4QURdTHQd9PN-gifYxqVvk4,272
|
|
15
17
|
pyfemtet/dispatch_extensions/_impl.py,sha256=HU7rKRAzEe5yYukWrKtdi1aIbUas_kLyaa_KZZGCELE,16244
|
|
@@ -24,19 +26,19 @@ pyfemtet/opt/_test_utils/hyper_sphere.py,sha256=nQhw8EIY0DwvcTqrbKhkxiITLZifr4-n
|
|
|
24
26
|
pyfemtet/opt/_test_utils/record_history.py,sha256=JCNJLZMCNTpJ6VT7iwEt2DIbwmsuQmgC0ClQSfcatj4,3915
|
|
25
27
|
pyfemtet/opt/interface/__init__.py,sha256=5hel-mP6tuxzIEJFMZJZWUEWEbFSsskzCWlQ3HORTYI,466
|
|
26
28
|
pyfemtet/opt/interface/_base.py,sha256=aPJ55dTp4-Q4KMkUZVRlquuBBWWOIOdC6yQsYZR4Jy0,2626
|
|
27
|
-
pyfemtet/opt/interface/_excel_interface.py,sha256=
|
|
29
|
+
pyfemtet/opt/interface/_excel_interface.py,sha256=7wJ8SOJaCyl6rVnmjLjw67Yv95bO1gwFBThc-cDLRYE,16649
|
|
28
30
|
pyfemtet/opt/interface/_femtet.py,sha256=dmKyRG8sWuX2JHjcXpvJ2q632oZh4I94iVo4u7Z7w_M,34742
|
|
29
31
|
pyfemtet/opt/interface/_femtet_parametric.py,sha256=KDG8SB43AgwuhpCStjvx10G0RzyHhga6k4dfvp0gvYU,2175
|
|
30
32
|
pyfemtet/opt/interface/_femtet_with_nx/__init__.py,sha256=-6W2g2FDEcKzGHmI5KAKQe-4U5jDpMj0CXuma-GZca0,83
|
|
31
33
|
pyfemtet/opt/interface/_femtet_with_nx/_interface.py,sha256=BXWdzIFcId1EovpbRD5DmkW0BwqhpDvOuGBv9kdCGy8,5994
|
|
32
34
|
pyfemtet/opt/interface/_femtet_with_nx/update_model.py,sha256=P7VH0i_o-X9OUe6AGaLF1fACPeHNrMjcrOBCA3MMrI4,3092
|
|
33
|
-
pyfemtet/opt/interface/_femtet_with_sldworks.py,sha256=
|
|
35
|
+
pyfemtet/opt/interface/_femtet_with_sldworks.py,sha256=A9o-IY8Npq0CHK7yGxBw47wmOtNv5IYR80U2XAabSWM,11019
|
|
34
36
|
pyfemtet/opt/optimizer/__init__.py,sha256=Ia6viowECkG0IFXtFef0tJ4jDKsoDzJLqMJ9xLFH2LQ,543
|
|
35
37
|
pyfemtet/opt/optimizer/_base.py,sha256=0jX68VEfLI08Qr01BvfPCHKKlr3Bj6gWQ0T81qpX0y4,12507
|
|
36
38
|
pyfemtet/opt/optimizer/_optuna/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
39
|
pyfemtet/opt/optimizer/_optuna/_botorch_patch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
40
|
pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py,sha256=2hUP2c8mokkRaSQ8nXxgCCmz8e0JKvEz8R2qIGnTGm0,8863
|
|
39
|
-
pyfemtet/opt/optimizer/_optuna/_optuna.py,sha256=
|
|
41
|
+
pyfemtet/opt/optimizer/_optuna/_optuna.py,sha256=nKlEYizSu6BQu8OMhRWRJxk5eXJ0LAPR7h6CQOjbdxE,16460
|
|
40
42
|
pyfemtet/opt/optimizer/_optuna/_pof_botorch.py,sha256=yVyg1V3trqirSDtbRepgftvS02AEkAhrgjou21JS124,72717
|
|
41
43
|
pyfemtet/opt/optimizer/_scipy.py,sha256=_2whhMNq6hC1lr5PlYhpZ8Zlh6-DkAjz8SVB5qHIpYg,4766
|
|
42
44
|
pyfemtet/opt/optimizer/_scipy_scalar.py,sha256=rGvrLjrgfYzxK9GA0-r2Hhoaqt6A0TQsT_1M3moyklc,3615
|
|
@@ -116,8 +118,8 @@ pyfemtet/opt/visualization/result_viewer/.gitignore,sha256=ryvb4aqbbsHireHWlPQfx
|
|
|
116
118
|
pyfemtet/opt/visualization/result_viewer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
117
119
|
pyfemtet/opt/visualization/result_viewer/application.py,sha256=WcHBx_J5eNLKSaprpk9BGifwhO04oN8FiNGYTWorrXA,1691
|
|
118
120
|
pyfemtet/opt/visualization/result_viewer/pages.py,sha256=laEAKHAtdshCAHxgXo-zMNg3RP6lCxfszO3XwLnF1dU,32156
|
|
119
|
-
pyfemtet-0.6.
|
|
120
|
-
pyfemtet-0.6.
|
|
121
|
-
pyfemtet-0.6.
|
|
122
|
-
pyfemtet-0.6.
|
|
123
|
-
pyfemtet-0.6.
|
|
121
|
+
pyfemtet-0.6.5.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
|
|
122
|
+
pyfemtet-0.6.5.dist-info/METADATA,sha256=04L0xEjP4MQaiIYEV0YZ6rXzf2P1ruOZzyW4hvhsXts,3287
|
|
123
|
+
pyfemtet-0.6.5.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
124
|
+
pyfemtet-0.6.5.dist-info/entry_points.txt,sha256=ZfYqRaoiPtuWqFi2_msccyrVF0LurMn-IHlYamAegZo,104
|
|
125
|
+
pyfemtet-0.6.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|