pyfemtet 0.6.6__py3-none-any.whl → 0.7.1__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/exit.py +3 -2
- pyfemtet/_util/dask_util.py +10 -0
- pyfemtet/_util/excel_macro_util.py +25 -16
- pyfemtet/dispatch_extensions/__init__.py +0 -1
- pyfemtet/dispatch_extensions/_impl.py +2 -5
- pyfemtet/logger/__init__.py +21 -2
- pyfemtet/logger/_impl.py +189 -65
- pyfemtet/opt/_femopt.py +27 -17
- pyfemtet/opt/_femopt_core.py +36 -30
- pyfemtet/opt/_test_utils/control_femtet.py +0 -6
- pyfemtet/opt/interface/__init__.py +1 -1
- pyfemtet/opt/interface/_base.py +2 -4
- pyfemtet/opt/interface/_excel_interface.py +433 -104
- pyfemtet/opt/interface/_femtet.py +14 -21
- pyfemtet/opt/interface/_femtet_parametric.py +1 -1
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py +1 -1
- pyfemtet/opt/interface/_femtet_with_sldworks.py +1 -1
- pyfemtet/opt/optimizer/_base.py +5 -5
- pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py +4 -1
- pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +0 -1
- pyfemtet/opt/visualization/_base.py +4 -4
- {pyfemtet-0.6.6.dist-info → pyfemtet-0.7.1.dist-info}/METADATA +2 -1
- {pyfemtet-0.6.6.dist-info → pyfemtet-0.7.1.dist-info}/RECORD +27 -26
- {pyfemtet-0.6.6.dist-info → pyfemtet-0.7.1.dist-info}/LICENSE +0 -0
- {pyfemtet-0.6.6.dist-info → pyfemtet-0.7.1.dist-info}/WHEEL +0 -0
- {pyfemtet-0.6.6.dist-info → pyfemtet-0.7.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from time import sleep
|
|
4
|
+
import gc
|
|
4
5
|
|
|
5
6
|
import pandas as pd
|
|
6
7
|
import numpy as np
|
|
@@ -20,29 +21,165 @@ from pyfemtet.core import SolveError
|
|
|
20
21
|
from pyfemtet.opt.optimizer.parameter import Parameter
|
|
21
22
|
|
|
22
23
|
from pyfemtet.dispatch_extensions import _get_pid, dispatch_specific_femtet
|
|
23
|
-
from pyfemtet.dispatch_extensions._impl import _NestableSpawnProcess
|
|
24
24
|
|
|
25
25
|
from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
|
|
26
|
-
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
|
|
27
26
|
|
|
28
27
|
from pyfemtet._util.excel_macro_util import watch_excel_macro_error
|
|
28
|
+
from pyfemtet._util.dask_util import lock_or_no_lock
|
|
29
29
|
|
|
30
30
|
from pyfemtet._warning import show_experimental_warning
|
|
31
31
|
|
|
32
|
-
import
|
|
33
|
-
|
|
34
|
-
logger = logging.getLogger(__name__)
|
|
35
|
-
logger.setLevel(logging.INFO)
|
|
32
|
+
from pyfemtet.opt.interface._base import logger
|
|
36
33
|
|
|
37
34
|
|
|
38
35
|
class ExcelInterface(FEMInterface):
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
"""Excel を計算コアとして利用するためのクラス。
|
|
37
|
+
|
|
38
|
+
通常の有限要素法を Excel に
|
|
39
|
+
置き換えて使用することが可能です。
|
|
40
|
+
|
|
41
|
+
すでに Excel マクロと Femtet を
|
|
42
|
+
連携させた自動解析システムを
|
|
43
|
+
構築している場合、このクラスは
|
|
44
|
+
それをラップします。これにより、
|
|
45
|
+
PyFemtet を用いた最適化を
|
|
46
|
+
行う際に便利な機能を提供します。
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
input_xlsm_path (str or Path):
|
|
50
|
+
設計変数の定義を含む Excel ファイルのパスを指定
|
|
51
|
+
します。
|
|
52
|
+
|
|
53
|
+
input_sheet_name (str):
|
|
54
|
+
設計変数の定義を含むシートの名前を指定します。
|
|
55
|
+
|
|
56
|
+
output_xlsm_path (str or Path, optional):
|
|
57
|
+
目的関数の定義を含む Excel ファイルのパスを指定
|
|
58
|
+
します。指定しない場合は ``input_xlsm_path`` と
|
|
59
|
+
同じと見做します。
|
|
60
|
+
|
|
61
|
+
output_sheet_name (str, optional):
|
|
62
|
+
目的関数の定義を含む含むシートの名前を指定します。
|
|
63
|
+
指定しない場合は ``input_sheet_name`` と同じと見
|
|
64
|
+
做します。
|
|
65
|
+
|
|
66
|
+
procedure_name (str, optional):
|
|
67
|
+
Excel マクロ関数名を指定します。指定しない場合は
|
|
68
|
+
``FemtetMacro.FemtetMain`` と見做します。
|
|
69
|
+
|
|
70
|
+
procedure_args (list or tuple, optional):
|
|
71
|
+
Excel マクロ関数に渡す引数をリストまたはタプルで
|
|
72
|
+
指定します。
|
|
73
|
+
|
|
74
|
+
connect_method (str, optional):
|
|
75
|
+
Excel との接続方法を指定します。 'auto' または
|
|
76
|
+
'new' が利用可能です。デフォルトは 'auto' です。
|
|
77
|
+
|
|
78
|
+
procedure_timeout (float or None, optional):
|
|
79
|
+
Excel マクロ関数のタイムアウト時間を秒単位で指定
|
|
80
|
+
します。 None の場合はタイムアウトなしとなります。
|
|
81
|
+
|
|
82
|
+
setup_xlsm_path (str or Path, optional):
|
|
83
|
+
セットアップ時に呼ぶ関数を含む xlsm のパスです。
|
|
84
|
+
指定しない場合は ``input_xlsm_path`` と
|
|
85
|
+
同じと見做します。
|
|
86
|
+
|
|
87
|
+
setup_procedure_name (str, optional):
|
|
88
|
+
セットアップ時に呼ぶマクロ関数名です。
|
|
89
|
+
指定しない場合、セットアップ時に何もしません。
|
|
90
|
+
|
|
91
|
+
setup_procedure_args (list or tuple, optional):
|
|
92
|
+
セットアップ時に呼ぶマクロ関数の引数です。
|
|
93
|
+
|
|
94
|
+
teardown_xlsm_path (str or Path, optional):
|
|
95
|
+
終了時に呼ぶ関数を含む xlsm のパスです。
|
|
96
|
+
指定しない場合は ``input_xlsm_path`` と
|
|
97
|
+
同じと見做します。
|
|
98
|
+
|
|
99
|
+
teardown_procedure_name (str, optional):
|
|
100
|
+
終了時に呼ぶマクロ関数名です。
|
|
101
|
+
指定しない場合、終了時に何もしません。
|
|
102
|
+
|
|
103
|
+
teardown_procedure_args (list or tuple, optional):
|
|
104
|
+
終了時に呼ぶマクロ関数の引数です。
|
|
105
|
+
|
|
106
|
+
visible (bool):
|
|
107
|
+
excel を可視化するかどうかです。
|
|
108
|
+
ただし、 True を指定した場合でもマクロの実行中は
|
|
109
|
+
不可視になります。
|
|
110
|
+
デフォルトは False です。
|
|
111
|
+
|
|
112
|
+
display_alerts (bool):
|
|
113
|
+
excel ダイアログを表示するかどうかです。
|
|
114
|
+
デバッグ目的の場合以外は True にしないでください。
|
|
115
|
+
デフォルトは False です。
|
|
116
|
+
|
|
117
|
+
terminate_excel_when_quit (bool):
|
|
118
|
+
終了時に Excel を終了するかどうかです。
|
|
119
|
+
指定しない場合、 connect_method が 'new' の場合
|
|
120
|
+
True とふるまい 'auto' の場合 False と振舞います。
|
|
121
|
+
|
|
122
|
+
interactive (bool):
|
|
123
|
+
excel を対話モードにするかどうかです。
|
|
124
|
+
False にすると、 visible == True であっても
|
|
125
|
+
自動化プロセス中にユーザーが誤って
|
|
126
|
+
Excel 本体を操作できないようにします。
|
|
127
|
+
デフォルトは True です。
|
|
128
|
+
|
|
129
|
+
Attributes:
|
|
130
|
+
input_xlsm_path (Path):
|
|
131
|
+
設計変数の定義を含む Excel ファイルのパス。
|
|
132
|
+
|
|
133
|
+
input_sheet_name (str):
|
|
134
|
+
設計変数の定義を含むシートの名前。
|
|
135
|
+
|
|
136
|
+
output_xlsm_path (Path):
|
|
137
|
+
目的関数の定義を含む Excel ファイルのパス。
|
|
138
|
+
|
|
139
|
+
output_sheet_name (str):
|
|
140
|
+
目的関数の定義を含む含むシートの名前。
|
|
141
|
+
|
|
142
|
+
procedure_name (str):
|
|
143
|
+
実行する Excel マクロ関数名。
|
|
144
|
+
|
|
145
|
+
procedure_args (list or tuple):
|
|
146
|
+
Excel マクロ関数に渡す引数のリストまたはタプル。
|
|
147
|
+
|
|
148
|
+
connect_method (str):
|
|
149
|
+
接続方法。'new' または 'auto'。
|
|
150
|
+
|
|
151
|
+
procedure_timeout (float or None):
|
|
152
|
+
Excel マクロ関数の実行タイムアウト。
|
|
153
|
+
Noneの場合は無制限。
|
|
154
|
+
|
|
155
|
+
terminate_excel_when_quit (bool):
|
|
156
|
+
プログラム終了時に Excel を終了するかどうか。
|
|
157
|
+
connect_method が 'new' の場合 True,
|
|
158
|
+
'auto' の場合 False。
|
|
159
|
+
|
|
160
|
+
excel (CDispatch):
|
|
161
|
+
Excel の COM オブジェクト。
|
|
162
|
+
|
|
163
|
+
input_sheet (CDispatch):
|
|
164
|
+
設計変数を含むシートの COM オブジェクト。
|
|
165
|
+
|
|
166
|
+
output_sheet (CDispatch):
|
|
167
|
+
目的関数を含むシートの COM オブジェクト。
|
|
168
|
+
|
|
169
|
+
input_workbook (CDispatch):
|
|
170
|
+
設計変数を含む xlsm ファイルの COM オブジェクト。
|
|
171
|
+
|
|
172
|
+
output_workbook (CDispatch):
|
|
173
|
+
設計変数を含む xlsm ファイルの COM オブジェクト。
|
|
174
|
+
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
input_xlsm_path: str # 操作対象の xlsm パス
|
|
41
178
|
input_sheet_name: str # 変数セルを定義しているシート名
|
|
42
|
-
output_xlsm_path:
|
|
179
|
+
output_xlsm_path: str # 操作対象の xlsm パス (指定しない場合、input と同一)
|
|
43
180
|
output_sheet_name: str # 計算結果セルを定義しているシート名 (指定しない場合、input と同一)
|
|
44
181
|
|
|
45
|
-
|
|
182
|
+
related_file_paths: list[str] # 並列時に個別に並列プロセスの space にアップロードする必要のあるパス
|
|
46
183
|
|
|
47
184
|
procedure_name: str # マクロ関数名(or モジュール名.関数名)
|
|
48
185
|
procedure_args: list # マクロ関数の引数
|
|
@@ -52,15 +189,27 @@ class ExcelInterface(FEMInterface):
|
|
|
52
189
|
sh_input: CDispatch # 変数の定義された WorkSheet
|
|
53
190
|
wb_output: CDispatch # システムを構成する Workbook
|
|
54
191
|
sh_output: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
|
|
192
|
+
wb_setup: CDispatch # システムを構成する Workbook
|
|
193
|
+
wb_teardown: CDispatch # システムを構成する Workbook
|
|
55
194
|
|
|
56
|
-
visible: bool
|
|
57
|
-
display_alerts: bool
|
|
195
|
+
visible: bool # excel を可視化するかどうか
|
|
196
|
+
display_alerts: bool # ダイアログを表示するかどうか
|
|
197
|
+
terminate_excel_when_quit: bool # 終了時に Excel を終了するかどうか
|
|
198
|
+
interactive: bool # excel を対話モードにするかどうか
|
|
58
199
|
|
|
59
|
-
_load_problem_from_me: bool = True
|
|
200
|
+
_load_problem_from_me: bool = True
|
|
60
201
|
_excel_pid: int
|
|
61
202
|
_excel_hwnd: int
|
|
203
|
+
_with_femtet_autosave_setting: bool = True # Femtet の自動保存機能の自動設定を行うかどうか。Femtet がインストールされていない場合はオフにする。クラス変数なので、インスタンス化前に設定する。
|
|
62
204
|
_femtet_autosave_buffer: bool # Femtet の自動保存機能の一時退避場所。最適化中はオフにする。
|
|
63
205
|
|
|
206
|
+
setup_xlsm_path: str
|
|
207
|
+
setup_procedure_name: str
|
|
208
|
+
setup_procedure_args: list or tuple
|
|
209
|
+
teardown_xlsm_path: str
|
|
210
|
+
teardown_procedure_name: str
|
|
211
|
+
teardown_procedure_args: list or tuple
|
|
212
|
+
|
|
64
213
|
def __init__(
|
|
65
214
|
self,
|
|
66
215
|
input_xlsm_path: str or Path,
|
|
@@ -71,48 +220,67 @@ class ExcelInterface(FEMInterface):
|
|
|
71
220
|
procedure_args: list or tuple = None,
|
|
72
221
|
connect_method: str = 'auto', # or 'new'
|
|
73
222
|
procedure_timeout: float or None = None,
|
|
223
|
+
setup_xlsm_path: str or Path = None,
|
|
224
|
+
setup_procedure_name: str = None,
|
|
225
|
+
setup_procedure_args: list or tuple = None,
|
|
226
|
+
teardown_xlsm_path: str or Path = None,
|
|
227
|
+
teardown_procedure_name: str = None,
|
|
228
|
+
teardown_procedure_args: list or tuple = None,
|
|
229
|
+
related_file_paths: list[str or Path] = None,
|
|
230
|
+
visible: bool = False,
|
|
231
|
+
display_alerts: bool = False,
|
|
232
|
+
terminate_excel_when_quit: bool = None,
|
|
233
|
+
interactive: bool = True,
|
|
74
234
|
):
|
|
235
|
+
|
|
75
236
|
show_experimental_warning("ExcelInterface")
|
|
76
237
|
|
|
77
238
|
# 初期化
|
|
78
|
-
self.input_xlsm_path =
|
|
239
|
+
self.input_xlsm_path = str(input_xlsm_path) # あとで再取得する
|
|
79
240
|
self.input_sheet_name = input_sheet_name
|
|
80
|
-
self.output_xlsm_path = None
|
|
241
|
+
self.output_xlsm_path = str(input_xlsm_path) if output_xlsm_path is None else str(output_xlsm_path)
|
|
81
242
|
self.output_sheet_name = output_sheet_name or self.input_sheet_name
|
|
82
243
|
self.procedure_name = procedure_name or 'FemtetMacro.FemtetMain'
|
|
83
244
|
self.procedure_args = procedure_args or []
|
|
84
245
|
assert connect_method in ['new', 'auto']
|
|
85
246
|
self.connect_method = connect_method
|
|
86
|
-
self._femtet_autosave_buffer = _get_autosave_enabled()
|
|
87
247
|
self.procedure_timeout = procedure_timeout
|
|
248
|
+
if terminate_excel_when_quit is None:
|
|
249
|
+
self.terminate_excel_when_quit = self.connect_method == 'new'
|
|
250
|
+
else:
|
|
251
|
+
self.terminate_excel_when_quit = terminate_excel_when_quit
|
|
252
|
+
|
|
253
|
+
self.setup_xlsm_path = str(input_xlsm_path) if setup_xlsm_path is None else str(setup_xlsm_path) # あとで取得する
|
|
254
|
+
self.setup_procedure_name = setup_procedure_name
|
|
255
|
+
self.setup_procedure_args = setup_procedure_args or []
|
|
256
|
+
|
|
257
|
+
self.teardown_xlsm_path = str(input_xlsm_path) if teardown_xlsm_path is None else str(teardown_xlsm_path) # あとで取得する
|
|
258
|
+
self.teardown_procedure_name = teardown_procedure_name
|
|
259
|
+
self.teardown_procedure_args = teardown_procedure_args or []
|
|
260
|
+
|
|
261
|
+
self.related_file_paths = [str(p) for p in related_file_paths] if related_file_paths is not None else []
|
|
262
|
+
|
|
263
|
+
self.visible = visible
|
|
264
|
+
self.interactive = interactive
|
|
265
|
+
self.display_alerts = display_alerts
|
|
88
266
|
|
|
89
267
|
# dask サブプロセスのときは space 直下の input_xlsm_path を参照する
|
|
90
268
|
try:
|
|
91
269
|
worker = get_worker()
|
|
92
|
-
space = worker.local_directory
|
|
93
|
-
self.input_xlsm_path =
|
|
94
|
-
self.output_xlsm_path =
|
|
270
|
+
space = os.path.abspath(worker.local_directory)
|
|
271
|
+
self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
|
|
272
|
+
self.output_xlsm_path = os.path.join(space, os.path.basename(self.output_xlsm_path))
|
|
273
|
+
self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
|
|
274
|
+
self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
|
|
275
|
+
self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
|
|
95
276
|
|
|
96
277
|
# main プロセスの場合は絶対パスを参照する
|
|
97
278
|
except ValueError:
|
|
98
|
-
self.input_xlsm_path =
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# FIXME:
|
|
105
|
-
# そもそも ExcelInterface なので Femtet 関連のことをやらなくていいと思う。
|
|
106
|
-
# FemtetRef が文句なく動けばいいのだが、手元環境ではなぜか動いたり動かなかったりするため
|
|
107
|
-
# 仕方なく Femtet を Python 側から動かしている仮実装。
|
|
108
|
-
# 先に femtet を起動
|
|
109
|
-
util.execute_femtet()
|
|
110
|
-
|
|
111
|
-
# 直後の Excel 起動に間に合わない場合があったため
|
|
112
|
-
# Femtet が Dispatch 可能になるまで捨てプロセスで待つ
|
|
113
|
-
p = _NestableSpawnProcess(target=wait_femtet)
|
|
114
|
-
p.start()
|
|
115
|
-
p.join()
|
|
279
|
+
self.input_xlsm_path = os.path.abspath(self.input_xlsm_path)
|
|
280
|
+
self.output_xlsm_path = os.path.abspath(self.output_xlsm_path)
|
|
281
|
+
self.setup_xlsm_path = os.path.abspath(self.setup_xlsm_path)
|
|
282
|
+
self.teardown_xlsm_path = os.path.abspath(self.teardown_xlsm_path)
|
|
283
|
+
self.related_file_paths = [os.path.abspath(p) for p in self.related_file_paths]
|
|
116
284
|
|
|
117
285
|
# サブプロセスでの restore のための情報保管
|
|
118
286
|
kwargs = dict(
|
|
@@ -123,25 +291,109 @@ class ExcelInterface(FEMInterface):
|
|
|
123
291
|
procedure_name=self.procedure_name,
|
|
124
292
|
procedure_args=self.procedure_args,
|
|
125
293
|
connect_method='new', # subprocess で connect する際は new を強制する
|
|
294
|
+
terminate_excel_when_quit=True, # なので終了時は破棄する
|
|
126
295
|
procedure_timeout=self.procedure_timeout,
|
|
296
|
+
setup_xlsm_path=self.setup_xlsm_path,
|
|
297
|
+
setup_procedure_name=self.setup_procedure_name,
|
|
298
|
+
setup_procedure_args=self.setup_procedure_args,
|
|
299
|
+
teardown_xlsm_path=self.teardown_xlsm_path,
|
|
300
|
+
teardown_procedure_name=self.teardown_procedure_name,
|
|
301
|
+
teardown_procedure_args=self.teardown_procedure_args,
|
|
302
|
+
related_file_paths=self.related_file_paths,
|
|
303
|
+
visible=self.visible,
|
|
304
|
+
interactive=self.interactive,
|
|
305
|
+
display_alerts=self.display_alerts,
|
|
127
306
|
)
|
|
128
307
|
FEMInterface.__init__(self, **kwargs)
|
|
129
308
|
|
|
130
309
|
def __del__(self):
|
|
131
|
-
|
|
132
|
-
_set_autosave_enabled(self._femtet_autosave_buffer)
|
|
133
|
-
finally:
|
|
134
|
-
pass
|
|
310
|
+
pass
|
|
135
311
|
|
|
136
312
|
def _setup_before_parallel(self, client) -> None:
|
|
137
313
|
# メインプロセスで、並列プロセスを開始する前に行う前処理
|
|
138
314
|
|
|
139
|
-
|
|
140
|
-
|
|
315
|
+
client.upload_file(self.input_xlsm_path, False)
|
|
316
|
+
|
|
317
|
+
if not is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
318
|
+
client.upload_file(self.output_xlsm_path, False)
|
|
319
|
+
|
|
320
|
+
if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
321
|
+
client.upload_file(self.setup_xlsm_path, False)
|
|
141
322
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
323
|
+
if not is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
|
|
324
|
+
client.upload_file(self.setup_xlsm_path, False)
|
|
325
|
+
|
|
326
|
+
for path in self.related_file_paths:
|
|
327
|
+
client.upload_file(path, False)
|
|
328
|
+
|
|
329
|
+
def _setup_after_parallel(self, *args, **kwargs):
|
|
330
|
+
"""サブプロセス又はメインプロセスのサブスレッドで、最適化を開始する前の前処理"""
|
|
331
|
+
|
|
332
|
+
# kwargs で space_dir が与えられている場合、そちらを使用する
|
|
333
|
+
# メインプロセスで呼ばれることを想定
|
|
334
|
+
if 'space_dir' in kwargs.keys():
|
|
335
|
+
space = kwargs['space_dir']
|
|
336
|
+
if space is not None:
|
|
337
|
+
self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
|
|
338
|
+
self.output_xlsm_path = os.path.join(space, os.path.basename(self.output_xlsm_path))
|
|
339
|
+
self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
|
|
340
|
+
self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
|
|
341
|
+
self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
|
|
342
|
+
|
|
343
|
+
# connect_method が auto でかつ使用中のファイルを開こうとする場合に備えて excel のファイル名を変更
|
|
344
|
+
subprocess_idx = kwargs['opt'].subprocess_idx
|
|
345
|
+
|
|
346
|
+
def proc_path(path, ignore_no_exists):
|
|
347
|
+
exclude_ext, ext = os.path.splitext(path)
|
|
348
|
+
new_path = exclude_ext + f'{subprocess_idx}' + ext
|
|
349
|
+
if os.path.exists(path): # input と output が同じの場合など。input がないのはおかしい
|
|
350
|
+
os.rename(path, new_path)
|
|
351
|
+
elif not ignore_no_exists:
|
|
352
|
+
raise FileNotFoundError(f'{path} が見つかりません。')
|
|
353
|
+
return new_path
|
|
354
|
+
|
|
355
|
+
self.input_xlsm_path = proc_path(self.input_xlsm_path, False)
|
|
356
|
+
self.output_xlsm_path = proc_path(self.output_xlsm_path, True)
|
|
357
|
+
self.setup_xlsm_path = proc_path(self.setup_xlsm_path, True)
|
|
358
|
+
self.teardown_xlsm_path = proc_path(self.teardown_xlsm_path, True)
|
|
359
|
+
|
|
360
|
+
# スレッドが変わっているかもしれないので win32com の初期化
|
|
361
|
+
CoInitialize()
|
|
362
|
+
|
|
363
|
+
# 最適化中は femtet の autosave を無効にする
|
|
364
|
+
if self._with_femtet_autosave_setting:
|
|
365
|
+
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
|
|
366
|
+
self._femtet_autosave_buffer = _get_autosave_enabled()
|
|
367
|
+
_set_autosave_enabled(False)
|
|
368
|
+
|
|
369
|
+
# excel に繋ぐ
|
|
370
|
+
self.connect_excel(self.connect_method)
|
|
371
|
+
|
|
372
|
+
# load_objective は 1 回目に呼ばれたのが main thread なので
|
|
373
|
+
# subprocess に入った後でもう一度 load objective を行う
|
|
374
|
+
from pyfemtet.opt.optimizer import AbstractOptimizer
|
|
375
|
+
from pyfemtet.opt._femopt_core import Objective
|
|
376
|
+
opt: AbstractOptimizer = kwargs['opt']
|
|
377
|
+
obj: Objective
|
|
378
|
+
for obj_name, obj in opt.objectives.items():
|
|
379
|
+
if isinstance(obj.fun, ScapeGoatObjective):
|
|
380
|
+
opt.objectives[obj_name].fun = self.objective_from_excel
|
|
381
|
+
|
|
382
|
+
# excel の setup 関数を必要なら実行する
|
|
383
|
+
if self.setup_procedure_name is not None:
|
|
384
|
+
with lock_or_no_lock('excel_setup_procedure'):
|
|
385
|
+
try:
|
|
386
|
+
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout, restore_book=False):
|
|
387
|
+
self.excel.Run(
|
|
388
|
+
f'{self.setup_procedure_name}',
|
|
389
|
+
*self.setup_procedure_args
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# 再計算
|
|
393
|
+
self.excel.CalculateFull()
|
|
394
|
+
|
|
395
|
+
except com_error as e:
|
|
396
|
+
raise RuntimeError(f'Failed to run macro {self.setup_procedure_args}. The original message is: {e}')
|
|
145
397
|
|
|
146
398
|
def connect_excel(self, connect_method):
|
|
147
399
|
|
|
@@ -162,6 +414,7 @@ class ExcelInterface(FEMInterface):
|
|
|
162
414
|
# 可視性の設定
|
|
163
415
|
self.excel.Visible = self.visible
|
|
164
416
|
self.excel.DisplayAlerts = self.display_alerts
|
|
417
|
+
self.excel.Interactive = self.interactive
|
|
165
418
|
|
|
166
419
|
# 開く
|
|
167
420
|
self.excel.Workbooks.Open(str(self.input_xlsm_path))
|
|
@@ -180,11 +433,10 @@ class ExcelInterface(FEMInterface):
|
|
|
180
433
|
else:
|
|
181
434
|
raise RuntimeError(f'Sheet {self.input_sheet_name} does not exist in the book {self.wb_input.Name}.')
|
|
182
435
|
|
|
183
|
-
|
|
436
|
+
# 開く (output)
|
|
437
|
+
if is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
184
438
|
self.wb_output = self.wb_input
|
|
185
|
-
|
|
186
439
|
else:
|
|
187
|
-
# 開く (output)
|
|
188
440
|
self.excel.Workbooks.Open(str(self.output_xlsm_path))
|
|
189
441
|
for wb in self.excel.Workbooks:
|
|
190
442
|
if wb.Name == os.path.basename(self.output_xlsm_path):
|
|
@@ -201,49 +453,84 @@ class ExcelInterface(FEMInterface):
|
|
|
201
453
|
else:
|
|
202
454
|
raise RuntimeError(f'Sheet {self.output_sheet_name} does not exist in the book {self.wb_output.Name}.')
|
|
203
455
|
|
|
456
|
+
# 開く (setup)
|
|
457
|
+
if is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
458
|
+
self.wb_setup = self.wb_input
|
|
459
|
+
else:
|
|
460
|
+
self.excel.Workbooks.Open(self.setup_xlsm_path)
|
|
461
|
+
for wb in self.excel.Workbooks:
|
|
462
|
+
if wb.Name == os.path.basename(self.setup_xlsm_path):
|
|
463
|
+
self.wb_setup = wb
|
|
464
|
+
break
|
|
465
|
+
else:
|
|
466
|
+
raise RuntimeError(f'Cannot open {self.setup_xlsm_path}')
|
|
467
|
+
|
|
468
|
+
# 開く (teardown)
|
|
469
|
+
if is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
|
|
470
|
+
self.wb_teardown = self.wb_input
|
|
471
|
+
else:
|
|
472
|
+
self.excel.Workbooks.Open(self.teardown_xlsm_path)
|
|
473
|
+
for wb in self.excel.Workbooks:
|
|
474
|
+
if wb.Name == os.path.basename(self.teardown_xlsm_path):
|
|
475
|
+
self.wb_teardown = wb
|
|
476
|
+
break
|
|
477
|
+
else:
|
|
478
|
+
raise RuntimeError(f'Cannot open {self.teardown_xlsm_path}')
|
|
479
|
+
|
|
204
480
|
# book に参照設定を追加する
|
|
205
481
|
self.add_femtet_ref_xla(self.wb_input)
|
|
206
482
|
self.add_femtet_ref_xla(self.wb_output)
|
|
483
|
+
self.add_femtet_ref_xla(self.wb_setup)
|
|
484
|
+
self.add_femtet_ref_xla(self.wb_teardown)
|
|
207
485
|
|
|
208
486
|
def add_femtet_ref_xla(self, wb):
|
|
209
487
|
|
|
488
|
+
# search
|
|
489
|
+
ref_file_1 = r'C:\Program Files\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
490
|
+
if not os.path.exists(ref_file_1):
|
|
491
|
+
# 32bit
|
|
492
|
+
ref_file_1 = r'C:\Program Files (x86)\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
493
|
+
if not os.path.exists(ref_file_1):
|
|
494
|
+
raise FileNotFoundError(f'{ref_file_1} not found. Please check the "Enable Macros" command was fired.')
|
|
495
|
+
contain_1 = False
|
|
496
|
+
for ref in wb.VBProject.References:
|
|
497
|
+
if ref.FullPath is not None:
|
|
498
|
+
if ref.FullPath.lower() == ref_file_1.lower():
|
|
499
|
+
contain_1 = True
|
|
500
|
+
# add
|
|
501
|
+
if not contain_1:
|
|
502
|
+
wb.VBProject.References.AddFromFile(ref_file_1)
|
|
503
|
+
|
|
210
504
|
# search
|
|
211
505
|
ref_file_2 = os.path.abspath(util._get_femtetmacro_dllpath())
|
|
212
|
-
|
|
506
|
+
contain_2 = False
|
|
213
507
|
for ref in wb.VBProject.References:
|
|
214
508
|
if ref.Description is not None:
|
|
215
509
|
if ref.Description == 'FemtetMacro': # FemtetMacro
|
|
216
|
-
|
|
510
|
+
contain_2 = True
|
|
217
511
|
# add
|
|
218
|
-
if not
|
|
512
|
+
if not contain_2:
|
|
219
513
|
wb.VBProject.References.AddFromFile(ref_file_2)
|
|
220
514
|
|
|
221
|
-
def
|
|
222
|
-
# サブプロセス又はメインプロセスのサブスレッドで、最適化を開始する前の前処理
|
|
223
|
-
|
|
224
|
-
# スレッドが変わっているかもしれないので win32com の初期化
|
|
225
|
-
CoInitialize()
|
|
226
|
-
|
|
227
|
-
# 最適化中は femtet の autosave を無効にする
|
|
228
|
-
_set_autosave_enabled(False)
|
|
229
|
-
|
|
230
|
-
# excel に繋ぎなおす
|
|
231
|
-
self.connect_excel(self.connect_method)
|
|
515
|
+
def remove_femtet_ref_xla(self, wb):
|
|
232
516
|
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
517
|
+
# search
|
|
518
|
+
ref_file_1 = r'C:\Program Files\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
519
|
+
if not os.path.exists(ref_file_1):
|
|
520
|
+
# 32bit
|
|
521
|
+
ref_file_1 = r'C:\Program Files (x86)\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
522
|
+
if not os.path.exists(ref_file_1):
|
|
523
|
+
raise FileNotFoundError(f'{ref_file_1} not found. Please check the "Enable Macros" command was fired.')
|
|
524
|
+
for ref in wb.VBProject.References:
|
|
525
|
+
if ref.FullPath is not None:
|
|
526
|
+
if ref.FullPath == ref_file_1: # or ``FemtetMacroを使用するための参照設定を自動で行ないます。``
|
|
527
|
+
wb.VBProject.References.Remove(ref)
|
|
242
528
|
|
|
243
|
-
#
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
529
|
+
# search
|
|
530
|
+
for ref in wb.VBProject.References:
|
|
531
|
+
if ref.Description is not None:
|
|
532
|
+
if ref.Description == 'FemtetMacro': # FemtetMacro
|
|
533
|
+
wb.VBProject.References.Remove(ref)
|
|
247
534
|
|
|
248
535
|
def update(self, parameters: pd.DataFrame) -> None:
|
|
249
536
|
|
|
@@ -273,43 +560,77 @@ class ExcelInterface(FEMInterface):
|
|
|
273
560
|
except com_error as e:
|
|
274
561
|
raise SolveError(f'Failed to run macro {self.procedure_name}. The original message is: {e}')
|
|
275
562
|
|
|
276
|
-
|
|
277
563
|
def quit(self):
|
|
278
|
-
|
|
564
|
+
if self.terminate_excel_when_quit:
|
|
565
|
+
|
|
566
|
+
already_terminated = not hasattr(self, 'excel')
|
|
567
|
+
if already_terminated:
|
|
568
|
+
return
|
|
279
569
|
|
|
280
|
-
|
|
281
|
-
del self.sh_output
|
|
570
|
+
logger.info('Excel の終了処理を開始します。')
|
|
282
571
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
self.
|
|
286
|
-
|
|
287
|
-
|
|
572
|
+
# 参照設定解除の前に終了処理を必要なら実施する
|
|
573
|
+
# excel の setup 関数を必要なら実行する
|
|
574
|
+
if self.teardown_procedure_name is not None:
|
|
575
|
+
with lock_or_no_lock('excel_setup_procedure'):
|
|
576
|
+
try:
|
|
577
|
+
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout, restore_book=False):
|
|
578
|
+
self.excel.Run(
|
|
579
|
+
f'{self.teardown_procedure_name}',
|
|
580
|
+
*self.teardown_procedure_args
|
|
581
|
+
)
|
|
288
582
|
|
|
289
|
-
|
|
290
|
-
|
|
583
|
+
# 再計算
|
|
584
|
+
self.excel.CalculateFull()
|
|
291
585
|
|
|
292
|
-
|
|
293
|
-
|
|
586
|
+
except com_error as e:
|
|
587
|
+
raise RuntimeError(f'Failed to run macro {self.teardown_procedure_args}. The original message is: {e}')
|
|
294
588
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
589
|
+
# 参照設定を解除する(不要な処理かも)
|
|
590
|
+
self.remove_femtet_ref_xla(self.wb_input)
|
|
591
|
+
self.remove_femtet_ref_xla(self.wb_output)
|
|
592
|
+
self.remove_femtet_ref_xla(self.wb_setup)
|
|
593
|
+
self.remove_femtet_ref_xla(self.wb_teardown)
|
|
300
594
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
from multiprocessing import Process
|
|
305
|
-
p = Process(target=_terminate_femtet, args=(femtet_pid,))
|
|
306
|
-
p.start()
|
|
595
|
+
# シートの COM オブジェクト変数を削除する
|
|
596
|
+
del self.sh_input
|
|
597
|
+
del self.sh_output
|
|
307
598
|
|
|
308
|
-
|
|
309
|
-
|
|
599
|
+
# workbook を閉じる
|
|
600
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
601
|
+
self.wb_input.Close(SaveChanges := False)
|
|
310
602
|
|
|
311
|
-
|
|
312
|
-
|
|
603
|
+
if not is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
604
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
605
|
+
self.wb_output.Close(SaveChanges := False)
|
|
606
|
+
|
|
607
|
+
if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
608
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
609
|
+
self.wb_setup.Close(SaveChanges := False)
|
|
610
|
+
|
|
611
|
+
if not is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
|
|
612
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
613
|
+
self.wb_teardown.Close(SaveChanges := False)
|
|
614
|
+
|
|
615
|
+
del self.wb_input
|
|
616
|
+
del self.wb_output
|
|
617
|
+
del self.wb_setup
|
|
618
|
+
del self.wb_teardown
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
# excel の終了
|
|
622
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
623
|
+
self.excel.Quit()
|
|
624
|
+
del self.excel
|
|
625
|
+
|
|
626
|
+
# ここで Excel のプロセスが残らず落ちる
|
|
627
|
+
gc.collect()
|
|
628
|
+
|
|
629
|
+
if self._with_femtet_autosave_setting:
|
|
630
|
+
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled
|
|
631
|
+
logger.info('自動保存機能の設定を元に戻しています。')
|
|
632
|
+
_set_autosave_enabled(self._femtet_autosave_buffer)
|
|
633
|
+
logger.info('自動保存機能の設定を元に戻しました。')
|
|
313
634
|
|
|
314
635
|
# 直接アクセスしてもよいが、ユーザーに易しい名前にするためだけのプロパティ
|
|
315
636
|
@property
|
|
@@ -432,6 +753,7 @@ def _terminate_femtet(femtet_pid_):
|
|
|
432
753
|
Femtet, caught_pid = dispatch_specific_femtet(femtet_pid_)
|
|
433
754
|
_exit_or_force_terminate(timeout=3, Femtet=Femtet, force=True)
|
|
434
755
|
|
|
756
|
+
|
|
435
757
|
# main thread で作成した excel への参照を含む関数を
|
|
436
758
|
# 直接 thread や process に渡すと機能しない
|
|
437
759
|
class ScapeGoatObjective:
|
|
@@ -443,5 +765,12 @@ class ScapeGoatObjective:
|
|
|
443
765
|
return tuple()
|
|
444
766
|
|
|
445
767
|
|
|
768
|
+
def is_same_path(p1, p2):
|
|
769
|
+
_p1 = os.path.abspath(p1).lower()
|
|
770
|
+
_p2 = os.path.abspath(p2).lower()
|
|
771
|
+
return _p1 == _p2
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
|
|
446
775
|
if __name__ == '__main__':
|
|
447
776
|
ExcelInterface(..., ...)
|