pyfemtet 0.7.0__py3-none-any.whl → 0.8.0__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/_message/locales/ja/LC_MESSAGES/messages.mo +0 -0
- pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +112 -90
- pyfemtet/_message/locales/messages.pot +105 -89
- pyfemtet/_message/messages.py +6 -2
- pyfemtet/_util/dask_util.py +10 -0
- pyfemtet/_util/excel_macro_util.py +16 -4
- pyfemtet/_util/excel_parse_util.py +138 -0
- pyfemtet/_util/sample.xlsx +0 -0
- pyfemtet/brep/__init__.py +0 -3
- pyfemtet/brep/_impl.py +7 -3
- pyfemtet/opt/_femopt.py +69 -31
- pyfemtet/opt/_femopt_core.py +100 -36
- pyfemtet/opt/advanced_samples/excel_ui/(ref) original_project.femprj +0 -0
- pyfemtet/opt/advanced_samples/excel_ui/femtet-macro.xlsm +0 -0
- pyfemtet/opt/advanced_samples/excel_ui/pyfemtet-core.py +291 -0
- pyfemtet/opt/advanced_samples/excel_ui/test-pyfemtet-core.cmd +22 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data.py +60 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data_jp.py +57 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate.py +100 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate_jp.py +90 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_parametric.femprj +0 -0
- pyfemtet/opt/interface/__init__.py +2 -0
- pyfemtet/opt/interface/_base.py +3 -0
- pyfemtet/opt/interface/_excel_interface.py +565 -204
- pyfemtet/opt/interface/_femtet.py +26 -29
- pyfemtet/opt/interface/_surrogate/__init__.py +5 -0
- pyfemtet/opt/interface/_surrogate/_base.py +85 -0
- pyfemtet/opt/interface/_surrogate/_chaospy.py +71 -0
- pyfemtet/opt/interface/_surrogate/_singletaskgp.py +70 -0
- pyfemtet/opt/optimizer/_base.py +30 -19
- pyfemtet/opt/optimizer/_optuna/_optuna.py +20 -8
- pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +60 -18
- pyfemtet/opt/prediction/_base.py +8 -0
- pyfemtet/opt/prediction/single_task_gp.py +85 -62
- pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +5 -5
- pyfemtet/opt/visualization/_complex_components/main_graph.py +7 -1
- pyfemtet/opt/visualization/_complex_components/pm_graph.py +1 -1
- pyfemtet/opt/visualization/_process_monitor/application.py +2 -2
- pyfemtet/opt/visualization/_process_monitor/pages.py +1 -1
- pyfemtet/opt/visualization/result_viewer/pages.py +1 -1
- {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/METADATA +3 -2
- {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/RECORD +46 -29
- {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/WHEEL +1 -1
- {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/LICENSE +0 -0
- {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from time import sleep
|
|
4
4
|
import gc
|
|
5
|
+
from typing import Optional, List
|
|
5
6
|
|
|
6
7
|
import pandas as pd
|
|
7
8
|
import numpy as np
|
|
@@ -18,17 +19,18 @@ from pywintypes import com_error
|
|
|
18
19
|
|
|
19
20
|
from pyfemtet.opt import FEMInterface
|
|
20
21
|
from pyfemtet.core import SolveError
|
|
21
|
-
from pyfemtet.opt.optimizer.parameter import Parameter
|
|
22
|
+
from pyfemtet.opt.optimizer.parameter import Parameter, Expression
|
|
22
23
|
|
|
23
24
|
from pyfemtet.dispatch_extensions import _get_pid, dispatch_specific_femtet
|
|
24
|
-
from pyfemtet.dispatch_extensions._impl import _NestableSpawnProcess
|
|
25
25
|
|
|
26
26
|
from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
|
|
27
|
-
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
|
|
28
27
|
|
|
29
28
|
from pyfemtet._util.excel_macro_util import watch_excel_macro_error
|
|
29
|
+
from pyfemtet._util.dask_util import lock_or_no_lock
|
|
30
|
+
from pyfemtet._util.excel_parse_util import *
|
|
30
31
|
|
|
31
32
|
from pyfemtet._warning import show_experimental_warning
|
|
33
|
+
from pyfemtet._message.messages import Message as Msg
|
|
32
34
|
|
|
33
35
|
from pyfemtet.opt.interface._base import logger
|
|
34
36
|
|
|
@@ -80,11 +82,53 @@ class ExcelInterface(FEMInterface):
|
|
|
80
82
|
Excel マクロ関数のタイムアウト時間を秒単位で指定
|
|
81
83
|
します。 None の場合はタイムアウトなしとなります。
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
setup_xlsm_path (str or Path, optional):
|
|
86
|
+
セットアップ時に呼ぶ関数を含む xlsm のパスです。
|
|
87
|
+
指定しない場合は ``input_xlsm_path`` と
|
|
88
|
+
同じと見做します。
|
|
89
|
+
|
|
90
|
+
setup_procedure_name (str, optional):
|
|
91
|
+
セットアップ時に呼ぶマクロ関数名です。
|
|
92
|
+
指定しない場合、セットアップ時に何もしません。
|
|
93
|
+
|
|
94
|
+
setup_procedure_args (list or tuple, optional):
|
|
95
|
+
セットアップ時に呼ぶマクロ関数の引数です。
|
|
96
|
+
|
|
97
|
+
teardown_xlsm_path (str or Path, optional):
|
|
98
|
+
終了時に呼ぶ関数を含む xlsm のパスです。
|
|
99
|
+
指定しない場合は ``input_xlsm_path`` と
|
|
100
|
+
同じと見做します。
|
|
101
|
+
|
|
102
|
+
teardown_procedure_name (str, optional):
|
|
103
|
+
終了時に呼ぶマクロ関数名です。
|
|
104
|
+
指定しない場合、終了時に何もしません。
|
|
105
|
+
|
|
106
|
+
teardown_procedure_args (list or tuple, optional):
|
|
107
|
+
終了時に呼ぶマクロ関数の引数です。
|
|
108
|
+
|
|
109
|
+
visible (bool):
|
|
110
|
+
excel を可視化するかどうかです。
|
|
111
|
+
ただし、 True を指定した場合でもマクロの実行中は
|
|
112
|
+
不可視になります。
|
|
113
|
+
デフォルトは False です。
|
|
114
|
+
|
|
115
|
+
display_alerts (bool):
|
|
116
|
+
excel ダイアログを表示するかどうかです。
|
|
117
|
+
デバッグ目的の場合以外は True にしないでください。
|
|
85
118
|
デフォルトは False です。
|
|
86
119
|
|
|
87
|
-
|
|
120
|
+
terminate_excel_when_quit (bool):
|
|
121
|
+
終了時に Excel を終了するかどうかです。
|
|
122
|
+
指定しない場合、 connect_method が 'new' の場合
|
|
123
|
+
True とふるまい 'auto' の場合 False と振舞います。
|
|
124
|
+
|
|
125
|
+
interactive (bool):
|
|
126
|
+
excel を対話モードにするかどうかです。
|
|
127
|
+
False にすると、 visible == True であっても
|
|
128
|
+
自動化プロセス中にユーザーが誤って
|
|
129
|
+
Excel 本体を操作できないようにします。
|
|
130
|
+
デフォルトは True です。
|
|
131
|
+
|
|
88
132
|
Attributes:
|
|
89
133
|
input_xlsm_path (Path):
|
|
90
134
|
設計変数の定義を含む Excel ファイルのパス。
|
|
@@ -133,12 +177,14 @@ class ExcelInterface(FEMInterface):
|
|
|
133
177
|
|
|
134
178
|
"""
|
|
135
179
|
|
|
136
|
-
input_xlsm_path:
|
|
180
|
+
input_xlsm_path: str # 操作対象の xlsm パス
|
|
137
181
|
input_sheet_name: str # 変数セルを定義しているシート名
|
|
138
|
-
output_xlsm_path:
|
|
182
|
+
output_xlsm_path: str # 操作対象の xlsm パス (指定しない場合、input と同一)
|
|
139
183
|
output_sheet_name: str # 計算結果セルを定義しているシート名 (指定しない場合、input と同一)
|
|
184
|
+
constraint_xlsm_path: str # 操作対象の xlsm パス (指定しない場合、input と同一)
|
|
185
|
+
constraint_sheet_name: str # 拘束関数セルを定義しているシート名 (指定しない場合、input と同一)
|
|
140
186
|
|
|
141
|
-
|
|
187
|
+
related_file_paths: list[str] # 並列時に個別に並列プロセスの space にアップロードする必要のあるパス
|
|
142
188
|
|
|
143
189
|
procedure_name: str # マクロ関数名(or モジュール名.関数名)
|
|
144
190
|
procedure_args: list # マクロ関数の引数
|
|
@@ -148,17 +194,30 @@ class ExcelInterface(FEMInterface):
|
|
|
148
194
|
sh_input: CDispatch # 変数の定義された WorkSheet
|
|
149
195
|
wb_output: CDispatch # システムを構成する Workbook
|
|
150
196
|
sh_output: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
|
|
197
|
+
wb_constraint: CDispatch # システムを構成する Workbook
|
|
198
|
+
sh_constraint: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
|
|
199
|
+
wb_setup: CDispatch # システムを構成する Workbook
|
|
200
|
+
wb_teardown: CDispatch # システムを構成する Workbook
|
|
151
201
|
|
|
152
|
-
visible: bool
|
|
153
|
-
display_alerts: bool
|
|
202
|
+
visible: bool # excel を可視化するかどうか
|
|
203
|
+
display_alerts: bool # ダイアログを表示するかどうか
|
|
154
204
|
terminate_excel_when_quit: bool # 終了時に Excel を終了するかどうか
|
|
205
|
+
interactive: bool # excel を対話モードにするかどうか
|
|
155
206
|
|
|
156
207
|
_load_problem_from_me: bool = True
|
|
157
208
|
_excel_pid: int
|
|
158
209
|
_excel_hwnd: int
|
|
210
|
+
_with_femtet_autosave_setting: bool = True # Femtet の自動保存機能の自動設定を行うかどうか。Femtet がインストールされていない場合はオフにする。クラス変数なので、インスタンス化前に設定する。
|
|
159
211
|
_femtet_autosave_buffer: bool # Femtet の自動保存機能の一時退避場所。最適化中はオフにする。
|
|
160
|
-
_with_call_femtet: bool # Femtet を Python から起動するかどうか。Excel から起動できる場合は False でよい。
|
|
161
212
|
|
|
213
|
+
setup_xlsm_path: str
|
|
214
|
+
setup_procedure_name: str
|
|
215
|
+
setup_procedure_args: list or tuple
|
|
216
|
+
teardown_xlsm_path: str
|
|
217
|
+
teardown_procedure_name: str
|
|
218
|
+
teardown_procedure_args: list or tuple
|
|
219
|
+
|
|
220
|
+
use_named_range: bool # input を定義したシートにおいて input の値を名前付き範囲で指定するかどうか。
|
|
162
221
|
|
|
163
222
|
def __init__(
|
|
164
223
|
self,
|
|
@@ -166,43 +225,80 @@ class ExcelInterface(FEMInterface):
|
|
|
166
225
|
input_sheet_name: str,
|
|
167
226
|
output_xlsm_path: str or Path = None,
|
|
168
227
|
output_sheet_name: str = None,
|
|
228
|
+
constraint_xlsm_path: str or Path = None,
|
|
229
|
+
constraint_sheet_name: str = None,
|
|
169
230
|
procedure_name: str = None,
|
|
170
231
|
procedure_args: list or tuple = None,
|
|
171
232
|
connect_method: str = 'auto', # or 'new'
|
|
172
233
|
procedure_timeout: float or None = None,
|
|
173
|
-
|
|
234
|
+
setup_xlsm_path: str or Path = None,
|
|
235
|
+
setup_procedure_name: str = None,
|
|
236
|
+
setup_procedure_args: list or tuple = None,
|
|
237
|
+
teardown_xlsm_path: str or Path = None,
|
|
238
|
+
teardown_procedure_name: str = None,
|
|
239
|
+
teardown_procedure_args: list or tuple = None,
|
|
240
|
+
related_file_paths: list[str or Path] = None,
|
|
241
|
+
visible: bool = False,
|
|
242
|
+
display_alerts: bool = False,
|
|
243
|
+
terminate_excel_when_quit: bool = None,
|
|
244
|
+
interactive: bool = True,
|
|
245
|
+
use_named_range: bool = True,
|
|
174
246
|
):
|
|
175
247
|
|
|
176
248
|
show_experimental_warning("ExcelInterface")
|
|
177
249
|
|
|
178
250
|
# 初期化
|
|
179
|
-
self.input_xlsm_path =
|
|
251
|
+
self.input_xlsm_path = str(input_xlsm_path) # あとで再取得する
|
|
180
252
|
self.input_sheet_name = input_sheet_name
|
|
181
|
-
self.output_xlsm_path = None
|
|
182
|
-
self.output_sheet_name = output_sheet_name
|
|
253
|
+
self.output_xlsm_path = str(input_xlsm_path) if output_xlsm_path is None else str(output_xlsm_path)
|
|
254
|
+
self.output_sheet_name = output_sheet_name if output_sheet_name is not None else input_sheet_name
|
|
255
|
+
self.constraint_xlsm_path = str(input_xlsm_path) if constraint_xlsm_path is None else str(constraint_xlsm_path)
|
|
256
|
+
self.constraint_sheet_name = constraint_sheet_name or self.input_sheet_name
|
|
183
257
|
self.procedure_name = procedure_name or 'FemtetMacro.FemtetMain'
|
|
184
258
|
self.procedure_args = procedure_args or []
|
|
185
259
|
assert connect_method in ['new', 'auto']
|
|
186
260
|
self.connect_method = connect_method
|
|
187
|
-
self._femtet_autosave_buffer = _get_autosave_enabled()
|
|
188
261
|
self.procedure_timeout = procedure_timeout
|
|
189
|
-
|
|
190
|
-
|
|
262
|
+
if terminate_excel_when_quit is None:
|
|
263
|
+
self.terminate_excel_when_quit = self.connect_method == 'new'
|
|
264
|
+
else:
|
|
265
|
+
self.terminate_excel_when_quit = terminate_excel_when_quit
|
|
266
|
+
|
|
267
|
+
self.setup_xlsm_path = str(input_xlsm_path) if setup_xlsm_path is None else str(setup_xlsm_path) # あとで取得する
|
|
268
|
+
self.setup_procedure_name = setup_procedure_name
|
|
269
|
+
self.setup_procedure_args = setup_procedure_args or []
|
|
270
|
+
|
|
271
|
+
self.teardown_xlsm_path = str(input_xlsm_path) if teardown_xlsm_path is None else str(teardown_xlsm_path) # あとで取得する
|
|
272
|
+
self.teardown_procedure_name = teardown_procedure_name
|
|
273
|
+
self.teardown_procedure_args = teardown_procedure_args or []
|
|
274
|
+
|
|
275
|
+
self.related_file_paths = [str(p) for p in related_file_paths] if related_file_paths is not None else []
|
|
276
|
+
|
|
277
|
+
self.visible = visible
|
|
278
|
+
self.interactive = interactive
|
|
279
|
+
self.display_alerts = display_alerts
|
|
280
|
+
|
|
281
|
+
self.use_named_range = use_named_range
|
|
191
282
|
|
|
192
283
|
# dask サブプロセスのときは space 直下の input_xlsm_path を参照する
|
|
193
284
|
try:
|
|
194
285
|
worker = get_worker()
|
|
195
|
-
space = worker.local_directory
|
|
196
|
-
self.input_xlsm_path =
|
|
197
|
-
self.output_xlsm_path =
|
|
286
|
+
space = os.path.abspath(worker.local_directory)
|
|
287
|
+
self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
|
|
288
|
+
self.output_xlsm_path = os.path.join(space, os.path.basename(self.output_xlsm_path))
|
|
289
|
+
self.constraint_xlsm_path = os.path.join(space, os.path.basename(self.constraint_xlsm_path))
|
|
290
|
+
self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
|
|
291
|
+
self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
|
|
292
|
+
self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
|
|
198
293
|
|
|
199
294
|
# main プロセスの場合は絶対パスを参照する
|
|
200
295
|
except ValueError:
|
|
201
|
-
self.input_xlsm_path =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
296
|
+
self.input_xlsm_path = os.path.abspath(self.input_xlsm_path)
|
|
297
|
+
self.output_xlsm_path = os.path.abspath(self.output_xlsm_path)
|
|
298
|
+
self.constraint_xlsm_path = os.path.abspath(self.constraint_xlsm_path)
|
|
299
|
+
self.setup_xlsm_path = os.path.abspath(self.setup_xlsm_path)
|
|
300
|
+
self.teardown_xlsm_path = os.path.abspath(self.teardown_xlsm_path)
|
|
301
|
+
self.related_file_paths = [os.path.abspath(p) for p in self.related_file_paths]
|
|
206
302
|
|
|
207
303
|
# サブプロセスでの restore のための情報保管
|
|
208
304
|
kwargs = dict(
|
|
@@ -210,29 +306,128 @@ class ExcelInterface(FEMInterface):
|
|
|
210
306
|
input_sheet_name=self.input_sheet_name,
|
|
211
307
|
output_xlsm_path=self.output_xlsm_path,
|
|
212
308
|
output_sheet_name=self.output_sheet_name,
|
|
309
|
+
constraint_xlsm_path=self.constraint_xlsm_path,
|
|
310
|
+
constraint_sheet_name=self.constraint_sheet_name,
|
|
213
311
|
procedure_name=self.procedure_name,
|
|
214
312
|
procedure_args=self.procedure_args,
|
|
215
313
|
connect_method='new', # subprocess で connect する際は new を強制する
|
|
314
|
+
terminate_excel_when_quit=True, # なので終了時は破棄する
|
|
216
315
|
procedure_timeout=self.procedure_timeout,
|
|
217
|
-
|
|
316
|
+
setup_xlsm_path=self.setup_xlsm_path,
|
|
317
|
+
setup_procedure_name=self.setup_procedure_name,
|
|
318
|
+
setup_procedure_args=self.setup_procedure_args,
|
|
319
|
+
teardown_xlsm_path=self.teardown_xlsm_path,
|
|
320
|
+
teardown_procedure_name=self.teardown_procedure_name,
|
|
321
|
+
teardown_procedure_args=self.teardown_procedure_args,
|
|
322
|
+
related_file_paths=self.related_file_paths,
|
|
323
|
+
visible=self.visible,
|
|
324
|
+
interactive=self.interactive,
|
|
325
|
+
display_alerts=self.display_alerts,
|
|
326
|
+
use_named_range=self.use_named_range,
|
|
218
327
|
)
|
|
219
328
|
FEMInterface.__init__(self, **kwargs)
|
|
220
329
|
|
|
221
330
|
def __del__(self):
|
|
222
|
-
|
|
223
|
-
_set_autosave_enabled(self._femtet_autosave_buffer)
|
|
224
|
-
finally:
|
|
225
|
-
pass
|
|
331
|
+
pass
|
|
226
332
|
|
|
227
333
|
def _setup_before_parallel(self, client) -> None:
|
|
228
334
|
# メインプロセスで、並列プロセスを開始する前に行う前処理
|
|
229
335
|
|
|
230
|
-
|
|
231
|
-
|
|
336
|
+
client.upload_file(self.input_xlsm_path, False)
|
|
337
|
+
|
|
338
|
+
if not is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
339
|
+
client.upload_file(self.output_xlsm_path, False)
|
|
340
|
+
|
|
341
|
+
if not is_same_path(self.input_xlsm_path, self.constraint_xlsm_path):
|
|
342
|
+
client.upload_file(self.constraint_xlsm_path, False)
|
|
232
343
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
344
|
+
if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
345
|
+
client.upload_file(self.setup_xlsm_path, False)
|
|
346
|
+
|
|
347
|
+
if not is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
|
|
348
|
+
client.upload_file(self.setup_xlsm_path, False)
|
|
349
|
+
|
|
350
|
+
for path in self.related_file_paths:
|
|
351
|
+
client.upload_file(path, False)
|
|
352
|
+
|
|
353
|
+
def _setup_after_parallel(self, *args, **kwargs):
|
|
354
|
+
"""サブプロセス又はメインプロセスのサブスレッドで、最適化を開始する前の前処理"""
|
|
355
|
+
|
|
356
|
+
# kwargs で space_dir が与えられている場合、そちらを使用する
|
|
357
|
+
# メインプロセスで呼ばれることを想定
|
|
358
|
+
if 'space_dir' in kwargs.keys():
|
|
359
|
+
space = kwargs['space_dir']
|
|
360
|
+
if space is not None:
|
|
361
|
+
self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
|
|
362
|
+
self.output_xlsm_path = os.path.join(space, os.path.basename(self.output_xlsm_path))
|
|
363
|
+
self.constraint_xlsm_path = os.path.join(space, os.path.basename(self.constraint_xlsm_path))
|
|
364
|
+
self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
|
|
365
|
+
self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
|
|
366
|
+
self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
|
|
367
|
+
|
|
368
|
+
# connect_method が auto でかつ使用中のファイルを開こうとする場合に備えて excel のファイル名を変更
|
|
369
|
+
subprocess_idx = kwargs['opt'].subprocess_idx
|
|
370
|
+
|
|
371
|
+
def proc_path(path, ignore_no_exists):
|
|
372
|
+
exclude_ext, ext = os.path.splitext(path)
|
|
373
|
+
new_path = exclude_ext + f'{subprocess_idx}' + ext
|
|
374
|
+
if os.path.exists(path): # input と output が同じの場合など。input がないのはおかしい
|
|
375
|
+
os.rename(path, new_path)
|
|
376
|
+
elif not ignore_no_exists:
|
|
377
|
+
raise FileNotFoundError(f'{path} が見つかりません。')
|
|
378
|
+
return new_path
|
|
379
|
+
|
|
380
|
+
self.input_xlsm_path = proc_path(self.input_xlsm_path, False)
|
|
381
|
+
self.output_xlsm_path = proc_path(self.output_xlsm_path, True)
|
|
382
|
+
self.constraint_xlsm_path = proc_path(self.constraint_xlsm_path, True)
|
|
383
|
+
self.setup_xlsm_path = proc_path(self.setup_xlsm_path, True)
|
|
384
|
+
self.teardown_xlsm_path = proc_path(self.teardown_xlsm_path, True)
|
|
385
|
+
|
|
386
|
+
# スレッドが変わっているかもしれないので win32com の初期化
|
|
387
|
+
CoInitialize()
|
|
388
|
+
|
|
389
|
+
# 最適化中は femtet の autosave を無効にする
|
|
390
|
+
if self._with_femtet_autosave_setting:
|
|
391
|
+
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
|
|
392
|
+
self._femtet_autosave_buffer = _get_autosave_enabled()
|
|
393
|
+
_set_autosave_enabled(False)
|
|
394
|
+
|
|
395
|
+
# excel に繋ぐ
|
|
396
|
+
with lock_or_no_lock('connect-excel'):
|
|
397
|
+
self.connect_excel(self.connect_method)
|
|
398
|
+
sleep(1)
|
|
399
|
+
|
|
400
|
+
# load_objective は 1 回目に呼ばれたのが main thread なので
|
|
401
|
+
# subprocess に入った後でもう一度 load objective を行う
|
|
402
|
+
from pyfemtet.opt.optimizer import AbstractOptimizer
|
|
403
|
+
from pyfemtet.opt._femopt_core import Objective, Constraint
|
|
404
|
+
opt: AbstractOptimizer = kwargs['opt']
|
|
405
|
+
obj: Objective
|
|
406
|
+
for obj_name, obj in opt.objectives.items():
|
|
407
|
+
if isinstance(obj.fun, ScapeGoatObjective):
|
|
408
|
+
opt.objectives[obj_name].fun = self.objective_from_excel
|
|
409
|
+
|
|
410
|
+
cns: Constraint
|
|
411
|
+
for cns_name, cns in opt.constraints.items():
|
|
412
|
+
if isinstance(cns.fun, ScapeGoatObjective):
|
|
413
|
+
opt.constraints[cns_name].fun = self.constraint_from_excel
|
|
414
|
+
|
|
415
|
+
# excel の setup 関数を必要なら実行する
|
|
416
|
+
if self.setup_procedure_name is not None:
|
|
417
|
+
with lock_or_no_lock('excel_setup_procedure'):
|
|
418
|
+
try:
|
|
419
|
+
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout, restore_book=False):
|
|
420
|
+
self.excel.Run(
|
|
421
|
+
f'{self.setup_procedure_name}',
|
|
422
|
+
*self.setup_procedure_args
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
# 再計算
|
|
426
|
+
self.excel.CalculateFull()
|
|
427
|
+
sleep(1)
|
|
428
|
+
|
|
429
|
+
except com_error as e:
|
|
430
|
+
raise RuntimeError(f'Failed to run macro {self.setup_procedure_name}. The original message is: {e}')
|
|
236
431
|
|
|
237
432
|
def connect_excel(self, connect_method):
|
|
238
433
|
|
|
@@ -243,6 +438,10 @@ class ExcelInterface(FEMInterface):
|
|
|
243
438
|
else:
|
|
244
439
|
self.excel = DispatchEx('Excel.Application')
|
|
245
440
|
|
|
441
|
+
# FemtetRef を追加する
|
|
442
|
+
self.open_femtet_ref_xla() # ここでエラーが発生しているかも?
|
|
443
|
+
sleep(0.5)
|
|
444
|
+
|
|
246
445
|
# 起動した excel の pid を記憶する
|
|
247
446
|
self._excel_hwnd = self.excel.hWnd
|
|
248
447
|
self._excel_pid = 0
|
|
@@ -253,7 +452,10 @@ class ExcelInterface(FEMInterface):
|
|
|
253
452
|
# 可視性の設定
|
|
254
453
|
self.excel.Visible = self.visible
|
|
255
454
|
self.excel.DisplayAlerts = self.display_alerts
|
|
455
|
+
self.excel.Interactive = self.interactive
|
|
456
|
+
sleep(0.5)
|
|
256
457
|
|
|
458
|
+
# ===== input =====
|
|
257
459
|
# 開く
|
|
258
460
|
self.excel.Workbooks.Open(str(self.input_xlsm_path))
|
|
259
461
|
for wb in self.excel.Workbooks:
|
|
@@ -271,11 +473,11 @@ class ExcelInterface(FEMInterface):
|
|
|
271
473
|
else:
|
|
272
474
|
raise RuntimeError(f'Sheet {self.input_sheet_name} does not exist in the book {self.wb_input.Name}.')
|
|
273
475
|
|
|
274
|
-
|
|
476
|
+
# ===== output =====
|
|
477
|
+
# 開く (output)
|
|
478
|
+
if is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
275
479
|
self.wb_output = self.wb_input
|
|
276
|
-
|
|
277
480
|
else:
|
|
278
|
-
# 開く (output)
|
|
279
481
|
self.excel.Workbooks.Open(str(self.output_xlsm_path))
|
|
280
482
|
for wb in self.excel.Workbooks:
|
|
281
483
|
if wb.Name == os.path.basename(self.output_xlsm_path):
|
|
@@ -292,27 +494,77 @@ class ExcelInterface(FEMInterface):
|
|
|
292
494
|
else:
|
|
293
495
|
raise RuntimeError(f'Sheet {self.output_sheet_name} does not exist in the book {self.wb_output.Name}.')
|
|
294
496
|
|
|
497
|
+
# ===== constraint =====
|
|
498
|
+
# 開く (constraint)
|
|
499
|
+
if is_same_path(self.input_xlsm_path, self.constraint_xlsm_path):
|
|
500
|
+
self.wb_constraint = self.wb_input
|
|
501
|
+
else:
|
|
502
|
+
self.excel.Workbooks.Open(str(self.constraint_xlsm_path))
|
|
503
|
+
for wb in self.excel.Workbooks:
|
|
504
|
+
if wb.Name == os.path.basename(self.constraint_xlsm_path):
|
|
505
|
+
self.wb_constraint = wb
|
|
506
|
+
break
|
|
507
|
+
else:
|
|
508
|
+
raise RuntimeError(f'Cannot open {self.constraint_xlsm_path}')
|
|
509
|
+
|
|
510
|
+
# シートを特定する (constraint)
|
|
511
|
+
for sh in self.wb_constraint.WorkSheets:
|
|
512
|
+
if sh.Name == self.constraint_sheet_name:
|
|
513
|
+
self.sh_constraint = sh
|
|
514
|
+
break
|
|
515
|
+
else:
|
|
516
|
+
raise RuntimeError(f'Sheet {self.constraint_sheet_name} does not exist in the book {self.wb_constraint.Name}.')
|
|
517
|
+
|
|
518
|
+
# ===== setup =====
|
|
519
|
+
# 開く (setup)
|
|
520
|
+
if is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
521
|
+
self.wb_setup = self.wb_input
|
|
522
|
+
else:
|
|
523
|
+
self.excel.Workbooks.Open(self.setup_xlsm_path)
|
|
524
|
+
for wb in self.excel.Workbooks:
|
|
525
|
+
if wb.Name == os.path.basename(self.setup_xlsm_path):
|
|
526
|
+
self.wb_setup = wb
|
|
527
|
+
break
|
|
528
|
+
else:
|
|
529
|
+
raise RuntimeError(f'Cannot open {self.setup_xlsm_path}')
|
|
530
|
+
|
|
531
|
+
# ===== teardown =====
|
|
532
|
+
# 開く (teardown)
|
|
533
|
+
if is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
|
|
534
|
+
self.wb_teardown = self.wb_input
|
|
535
|
+
else:
|
|
536
|
+
self.excel.Workbooks.Open(self.teardown_xlsm_path)
|
|
537
|
+
for wb in self.excel.Workbooks:
|
|
538
|
+
if wb.Name == os.path.basename(self.teardown_xlsm_path):
|
|
539
|
+
self.wb_teardown = wb
|
|
540
|
+
break
|
|
541
|
+
else:
|
|
542
|
+
raise RuntimeError(f'Cannot open {self.teardown_xlsm_path}')
|
|
543
|
+
|
|
295
544
|
# book に参照設定を追加する
|
|
296
|
-
self.
|
|
297
|
-
self.
|
|
545
|
+
self.add_femtet_macro_reference(self.wb_input)
|
|
546
|
+
self.add_femtet_macro_reference(self.wb_output)
|
|
547
|
+
self.add_femtet_macro_reference(self.wb_setup)
|
|
548
|
+
self.add_femtet_macro_reference(self.wb_teardown)
|
|
549
|
+
self.add_femtet_macro_reference(self.wb_constraint)
|
|
298
550
|
|
|
299
|
-
def
|
|
551
|
+
def open_femtet_ref_xla(self):
|
|
300
552
|
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
553
|
+
# get 64 bit
|
|
554
|
+
xla_file_path = r'C:\Program Files\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
555
|
+
|
|
556
|
+
# if not exist, get 32bit
|
|
557
|
+
if not os.path.exists(xla_file_path):
|
|
558
|
+
xla_file_path = r'C:\Program Files (x86)\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
559
|
+
|
|
560
|
+
# certify
|
|
561
|
+
if not os.path.exists(xla_file_path):
|
|
562
|
+
raise FileNotFoundError(f'{xla_file_path} not found. Please check the "Enable Macros" command was fired.')
|
|
563
|
+
|
|
564
|
+
# self.excel.Workbooks.Add(xla_file_path)
|
|
565
|
+
self.excel.Workbooks.Open(xla_file_path, ReadOnly=True)
|
|
566
|
+
|
|
567
|
+
def add_femtet_macro_reference(self, wb):
|
|
316
568
|
|
|
317
569
|
# search
|
|
318
570
|
ref_file_2 = os.path.abspath(util._get_femtetmacro_dllpath())
|
|
@@ -321,76 +573,47 @@ class ExcelInterface(FEMInterface):
|
|
|
321
573
|
if ref.Description is not None:
|
|
322
574
|
if ref.Description == 'FemtetMacro': # FemtetMacro
|
|
323
575
|
contain_2 = True
|
|
576
|
+
break
|
|
324
577
|
# add
|
|
325
578
|
if not contain_2:
|
|
326
579
|
wb.VBProject.References.AddFromFile(ref_file_2)
|
|
327
580
|
|
|
328
581
|
def remove_femtet_ref_xla(self, wb):
|
|
329
|
-
|
|
330
|
-
# search
|
|
331
|
-
ref_file_1 = r'C:\Program Files\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
332
|
-
if not os.path.exists(ref_file_1):
|
|
333
|
-
# 32bit
|
|
334
|
-
ref_file_1 = r'C:\Program Files (x86)\Microsoft Office\root\Office16\XLSTART\FemtetRef.xla'
|
|
335
|
-
if not os.path.exists(ref_file_1):
|
|
336
|
-
raise FileNotFoundError(f'{ref_file_1} not found. Please check the "Enable Macros" command was fired.')
|
|
337
|
-
for ref in wb.VBProject.References:
|
|
338
|
-
if ref.FullPath is not None:
|
|
339
|
-
if ref.FullPath == ref_file_1: # or ``FemtetMacroを使用するための参照設定を自動で行ないます。``
|
|
340
|
-
wb.VBProject.References.Remove(ref)
|
|
341
|
-
|
|
342
582
|
# search
|
|
343
583
|
for ref in wb.VBProject.References:
|
|
344
584
|
if ref.Description is not None:
|
|
345
585
|
if ref.Description == 'FemtetMacro': # FemtetMacro
|
|
346
586
|
wb.VBProject.References.Remove(ref)
|
|
347
587
|
|
|
348
|
-
def
|
|
349
|
-
# サブプロセス又はメインプロセスのサブスレッドで、最適化を開始する前の前処理
|
|
350
|
-
|
|
351
|
-
# スレッドが変わっているかもしれないので win32com の初期化
|
|
352
|
-
CoInitialize()
|
|
353
|
-
|
|
354
|
-
# 最適化中は femtet の autosave を無効にする
|
|
355
|
-
_set_autosave_enabled(False)
|
|
356
|
-
|
|
357
|
-
# 必要なら Femtet を起動する
|
|
358
|
-
if self._with_call_femtet:
|
|
359
|
-
util.execute_femtet()
|
|
360
|
-
|
|
361
|
-
# 直後の Excel 起動に間に合わない場合があるため
|
|
362
|
-
# Femtet が Dispatch 可能になるまで捨てプロセスで待つ
|
|
363
|
-
p = _NestableSpawnProcess(target=wait_femtet)
|
|
364
|
-
p.start()
|
|
365
|
-
p.join()
|
|
366
|
-
|
|
367
|
-
# excel に繋ぐ
|
|
368
|
-
self.connect_excel(self.connect_method)
|
|
369
|
-
|
|
370
|
-
# load_objective は 1 回目に呼ばれたのが main thread なので
|
|
371
|
-
# subprocess に入った後でもう一度 load objective を行う
|
|
372
|
-
from pyfemtet.opt.optimizer import AbstractOptimizer
|
|
373
|
-
from pyfemtet.opt._femopt_core import Objective
|
|
374
|
-
opt: AbstractOptimizer = kwargs['opt']
|
|
375
|
-
obj: Objective
|
|
376
|
-
for obj_name, obj in opt.objectives.items():
|
|
377
|
-
if isinstance(obj.fun, ScapeGoatObjective):
|
|
378
|
-
opt.objectives[obj_name].fun = self.objective_from_excel
|
|
379
|
-
|
|
380
|
-
def update(self, parameters: pd.DataFrame) -> None:
|
|
381
|
-
|
|
588
|
+
def update_parameter(self, parameters: pd.DataFrame, with_warning=False) -> Optional[List[str]]:
|
|
382
589
|
# params を作成
|
|
383
590
|
params = dict()
|
|
384
591
|
for _, row in parameters.iterrows():
|
|
385
592
|
params[row['name']] = row['value']
|
|
386
593
|
|
|
387
594
|
# excel シートの変数更新
|
|
388
|
-
|
|
389
|
-
|
|
595
|
+
if self.use_named_range:
|
|
596
|
+
for key, value in params.items():
|
|
597
|
+
try:
|
|
598
|
+
self.sh_input.Range(key).value = value
|
|
599
|
+
except com_error:
|
|
600
|
+
logger.warn('The cell address specification by named range is failed. '
|
|
601
|
+
'The process changes the specification way to table based method.')
|
|
602
|
+
self.use_named_range = False
|
|
603
|
+
break
|
|
604
|
+
|
|
605
|
+
if not self.use_named_range: # else にしないこと
|
|
606
|
+
for name, value in params.items():
|
|
607
|
+
r = 1 + search_r(self.input_xlsm_path, self.input_sheet_name, name)
|
|
608
|
+
c = 1 + search_c(self.input_xlsm_path, self.input_sheet_name, ParseAsParameter.value)
|
|
609
|
+
self.sh_input.Cells(r, c).value = value
|
|
390
610
|
|
|
391
611
|
# 再計算
|
|
392
612
|
self.excel.CalculateFull()
|
|
393
613
|
|
|
614
|
+
def update(self, parameters: pd.DataFrame) -> None:
|
|
615
|
+
self.update_parameter(parameters)
|
|
616
|
+
|
|
394
617
|
# マクロ実行
|
|
395
618
|
try:
|
|
396
619
|
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout):
|
|
@@ -407,48 +630,81 @@ class ExcelInterface(FEMInterface):
|
|
|
407
630
|
|
|
408
631
|
def quit(self):
|
|
409
632
|
if self.terminate_excel_when_quit:
|
|
410
|
-
logger.info('Excel の終了処理を開始します。')
|
|
411
|
-
|
|
412
|
-
self.remove_femtet_ref_xla(self.wb_input)
|
|
413
|
-
self.remove_femtet_ref_xla(self.wb_output)
|
|
414
633
|
|
|
634
|
+
already_terminated = not hasattr(self, 'excel')
|
|
635
|
+
if already_terminated:
|
|
636
|
+
return
|
|
637
|
+
|
|
638
|
+
logger.info(Msg.INFO_TERMINATING_EXCEL)
|
|
639
|
+
|
|
640
|
+
# 参照設定解除の前に終了処理を必要なら実施する
|
|
641
|
+
# excel の setup 関数を必要なら実行する
|
|
642
|
+
if self.teardown_procedure_name is not None:
|
|
643
|
+
with lock_or_no_lock('excel_teardown_procedure'):
|
|
644
|
+
try:
|
|
645
|
+
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout, restore_book=False):
|
|
646
|
+
self.excel.Run(
|
|
647
|
+
f'{self.teardown_procedure_name}',
|
|
648
|
+
*self.teardown_procedure_args
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
# 再計算
|
|
652
|
+
self.excel.CalculateFull()
|
|
653
|
+
|
|
654
|
+
except com_error as e:
|
|
655
|
+
raise RuntimeError(f'Failed to run macro {self.teardown_procedure_args}. The original message is: {e}')
|
|
656
|
+
|
|
657
|
+
# 不具合の原因になる場合があるので参照設定は解除しないこと
|
|
658
|
+
# self.remove_femtet_ref_xla(self.wb_input)
|
|
659
|
+
# self.remove_femtet_ref_xla(self.wb_output)
|
|
660
|
+
# self.remove_femtet_ref_xla(self.wb_constraint)
|
|
661
|
+
# self.remove_femtet_ref_xla(self.wb_setup)
|
|
662
|
+
# self.remove_femtet_ref_xla(self.wb_teardown)
|
|
663
|
+
|
|
664
|
+
# シートの COM オブジェクト変数を削除する
|
|
415
665
|
del self.sh_input
|
|
416
666
|
del self.sh_output
|
|
667
|
+
del self.sh_constraint
|
|
417
668
|
|
|
669
|
+
# workbook を閉じる
|
|
418
670
|
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
419
671
|
self.wb_input.Close(SaveChanges := False)
|
|
420
|
-
|
|
672
|
+
|
|
673
|
+
if not is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
421
674
|
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
422
675
|
self.wb_output.Close(SaveChanges := False)
|
|
423
676
|
|
|
677
|
+
if not is_same_path(self.input_xlsm_path, self.constraint_xlsm_path):
|
|
678
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
679
|
+
self.wb_constraint.Close(SaveChanges := False)
|
|
680
|
+
|
|
681
|
+
if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
682
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
683
|
+
self.wb_setup.Close(SaveChanges := False)
|
|
684
|
+
|
|
685
|
+
if not is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
|
|
686
|
+
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
687
|
+
self.wb_teardown.Close(SaveChanges := False)
|
|
688
|
+
|
|
424
689
|
del self.wb_input
|
|
425
690
|
del self.wb_output
|
|
691
|
+
del self.wb_constraint
|
|
692
|
+
del self.wb_setup
|
|
693
|
+
del self.wb_teardown
|
|
426
694
|
|
|
695
|
+
# excel の終了
|
|
427
696
|
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
428
697
|
self.excel.Quit()
|
|
429
698
|
del self.excel
|
|
430
699
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
# quit した後ならば femtet を終了できる
|
|
436
|
-
# excel の process の消滅を待つ
|
|
437
|
-
logger.info('Excel の終了を待っています。')
|
|
438
|
-
while self._excel_pid == _get_pid(self._excel_hwnd):
|
|
439
|
-
sleep(1)
|
|
440
|
-
|
|
441
|
-
# TODO: 正確だが時間がかかる。選択できるようにしたほうがいいかもしれない。
|
|
442
|
-
logger.info('終了する Femtet を特定しています。')
|
|
443
|
-
femtet_pid = util.get_last_executed_femtet_process_id()
|
|
444
|
-
from multiprocessing import Process
|
|
445
|
-
p = Process(target=_terminate_femtet, args=(femtet_pid,))
|
|
446
|
-
p.start()
|
|
447
|
-
p.join()
|
|
448
|
-
logger.info('Excel-Femtet を終了しました。')
|
|
700
|
+
# ここで Excel のプロセスが残らず落ちる
|
|
701
|
+
gc.collect()
|
|
702
|
+
logger.info(Msg.INFO_TERMINATED_EXCEL)
|
|
449
703
|
|
|
450
|
-
|
|
451
|
-
|
|
704
|
+
if self._with_femtet_autosave_setting:
|
|
705
|
+
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled
|
|
706
|
+
logger.info(Msg.INFO_RESTORING_FEMTET_AUTOSAVE)
|
|
707
|
+
_set_autosave_enabled(self._femtet_autosave_buffer)
|
|
452
708
|
|
|
453
709
|
# 直接アクセスしてもよいが、ユーザーに易しい名前にするためだけのプロパティ
|
|
454
710
|
@property
|
|
@@ -471,93 +727,180 @@ class ExcelInterface(FEMInterface):
|
|
|
471
727
|
from pyfemtet.opt.optimizer import AbstractOptimizer, logger
|
|
472
728
|
opt: AbstractOptimizer
|
|
473
729
|
|
|
474
|
-
df =
|
|
475
|
-
self.input_xlsm_path,
|
|
476
|
-
self.input_sheet_name,
|
|
477
|
-
header=0,
|
|
478
|
-
index_col=None,
|
|
479
|
-
)
|
|
730
|
+
df = ParseAsParameter.parse(self.input_xlsm_path, self.input_sheet_name)
|
|
480
731
|
|
|
481
|
-
# TODO: 使い勝手を考える
|
|
482
732
|
for i, row in df.iterrows():
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
value = float(value)
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
733
|
+
|
|
734
|
+
# use(optional)
|
|
735
|
+
use = True
|
|
736
|
+
if ParseAsParameter.use in df.columns:
|
|
737
|
+
_use = row[ParseAsParameter.use]
|
|
738
|
+
use = False if is_cell_value_empty(_use) else bool(_use) # bool or NaN
|
|
739
|
+
|
|
740
|
+
# name
|
|
741
|
+
name = str(row[ParseAsParameter.name])
|
|
742
|
+
|
|
743
|
+
# value
|
|
744
|
+
value = float(row[ParseAsParameter.value])
|
|
745
|
+
|
|
746
|
+
# lb (optional)
|
|
747
|
+
lb = None
|
|
748
|
+
if ParseAsParameter.lb in df.columns:
|
|
749
|
+
lb = row[ParseAsParameter.lb]
|
|
750
|
+
lb = None if is_cell_value_empty(lb) else float(lb)
|
|
751
|
+
|
|
752
|
+
# ub (optional)
|
|
753
|
+
ub = None
|
|
754
|
+
if ParseAsParameter.ub in df.columns:
|
|
755
|
+
ub = row[ParseAsParameter.ub]
|
|
756
|
+
ub = None if is_cell_value_empty(ub) else float(ub)
|
|
757
|
+
|
|
758
|
+
# step (optional)
|
|
759
|
+
step = None
|
|
760
|
+
if ParseAsParameter.step in df.columns:
|
|
761
|
+
step = row[ParseAsParameter.step]
|
|
762
|
+
step = None if is_cell_value_empty(step) else float(step)
|
|
763
|
+
|
|
764
|
+
if use:
|
|
765
|
+
prm = Parameter(
|
|
766
|
+
name=name,
|
|
767
|
+
value=value,
|
|
768
|
+
lower_bound=lb,
|
|
769
|
+
upper_bound=ub,
|
|
770
|
+
step=step,
|
|
771
|
+
pass_to_fem=True,
|
|
772
|
+
properties=None,
|
|
773
|
+
)
|
|
774
|
+
opt.variables.add_parameter(prm)
|
|
775
|
+
|
|
776
|
+
else:
|
|
777
|
+
fixed_prm = Expression(
|
|
778
|
+
name=name,
|
|
779
|
+
fun=lambda: value,
|
|
780
|
+
value=None,
|
|
781
|
+
pass_to_fem=True,
|
|
782
|
+
properties=dict(
|
|
783
|
+
lower_bound=lb,
|
|
784
|
+
upper_bound=ub,
|
|
785
|
+
),
|
|
786
|
+
kwargs=dict(),
|
|
787
|
+
)
|
|
788
|
+
opt.variables.add_expression(fixed_prm)
|
|
509
789
|
|
|
510
790
|
def load_objective(self, opt):
|
|
511
791
|
from pyfemtet.opt.optimizer import AbstractOptimizer, logger
|
|
512
792
|
from pyfemtet.opt._femopt_core import Objective
|
|
513
793
|
opt: AbstractOptimizer
|
|
514
794
|
|
|
515
|
-
df =
|
|
516
|
-
self.output_xlsm_path,
|
|
517
|
-
self.output_sheet_name,
|
|
518
|
-
header=0,
|
|
519
|
-
index_col=None,
|
|
520
|
-
)
|
|
795
|
+
df = ParseAsObjective.parse(self.output_xlsm_path, self.output_sheet_name)
|
|
521
796
|
|
|
522
|
-
# TODO: 使い勝手を考える
|
|
523
797
|
for i, row in df.iterrows():
|
|
798
|
+
|
|
799
|
+
# use(optional)
|
|
800
|
+
use = True
|
|
801
|
+
if ParseAsObjective.use in df.columns:
|
|
802
|
+
_use = row[ParseAsObjective.use]
|
|
803
|
+
use = False if is_cell_value_empty(_use) else bool(_use) # bool or NaN
|
|
804
|
+
|
|
805
|
+
# name
|
|
806
|
+
name = str(row[ParseAsObjective.name])
|
|
807
|
+
|
|
808
|
+
# direction
|
|
809
|
+
direction = row[ParseAsObjective.direction]
|
|
810
|
+
assert not is_cell_value_empty(direction), 'direction is empty.'
|
|
524
811
|
try:
|
|
525
|
-
name = row['name']
|
|
526
|
-
_ = row['current']
|
|
527
|
-
direction = row['direction']
|
|
528
|
-
value_column_index = list(df.columns).index('current')
|
|
529
|
-
except KeyError:
|
|
530
|
-
logger.warn('列名が「name」「current」「direction」になっていません。この順に並んでいると仮定して処理を続けます。')
|
|
531
|
-
name, _, direction, *_residuals = row.iloc[0]
|
|
532
|
-
value_column_index = 1
|
|
533
|
-
|
|
534
|
-
name = str(name)
|
|
535
|
-
|
|
536
|
-
# direction は minimize or maximize or float
|
|
537
|
-
try:
|
|
538
|
-
# float or not
|
|
539
812
|
direction = float(direction)
|
|
540
|
-
|
|
541
813
|
except ValueError:
|
|
542
|
-
# 'minimize' or 'maximize
|
|
543
814
|
direction = str(direction).lower()
|
|
544
|
-
assert
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
815
|
+
assert direction in ['minimize', 'maximize']
|
|
816
|
+
|
|
817
|
+
if use:
|
|
818
|
+
# objective を作る
|
|
819
|
+
opt.objectives[name] = Objective(
|
|
820
|
+
fun=ScapeGoatObjective(),
|
|
821
|
+
name=name,
|
|
822
|
+
direction=direction,
|
|
823
|
+
args=(name,),
|
|
824
|
+
kwargs=dict(),
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
def load_constraint(self, opt):
|
|
828
|
+
from pyfemtet.opt.optimizer import AbstractOptimizer, logger
|
|
829
|
+
from pyfemtet.opt._femopt_core import Constraint
|
|
830
|
+
opt: AbstractOptimizer
|
|
831
|
+
|
|
832
|
+
# TODO:
|
|
833
|
+
# constraint は optional である。
|
|
834
|
+
# 現在は実装していないが、シートから問題を取得するよりよいロジックができたら
|
|
835
|
+
# (つまり、同じシートからパラメータと拘束を取得出来るようになったら)__init__ 内で
|
|
836
|
+
# constraint に None が与えられたのか故意に input_sheet_name と同じシート名を
|
|
837
|
+
# 与えられたのか分別できる実装に変えてそのチェック処理をここに反映する。
|
|
838
|
+
# constraint_sheet_name が指定されていない場合何もしない
|
|
839
|
+
if (self.constraint_sheet_name == self.input_sheet_name) and is_same_path(self.input_xlsm_path, self.constraint_xlsm_path):
|
|
840
|
+
return
|
|
841
|
+
|
|
842
|
+
df = ParseAsConstraint.parse(self.constraint_xlsm_path, self.constraint_sheet_name)
|
|
843
|
+
|
|
844
|
+
for i, row in df.iterrows():
|
|
845
|
+
|
|
846
|
+
# use(optional)
|
|
847
|
+
use = True
|
|
848
|
+
if ParseAsConstraint.use in df.columns:
|
|
849
|
+
_use = row[ParseAsConstraint.use]
|
|
850
|
+
use = False if is_cell_value_empty(_use) else bool(_use) # bool or NaN
|
|
851
|
+
|
|
852
|
+
# name
|
|
853
|
+
name = str(row[ParseAsConstraint.name])
|
|
854
|
+
|
|
855
|
+
# lb (optional)
|
|
856
|
+
lb = None
|
|
857
|
+
if ParseAsConstraint.lb in df.columns:
|
|
858
|
+
lb = row[ParseAsConstraint.lb]
|
|
859
|
+
lb = None if is_cell_value_empty(lb) else float(lb)
|
|
860
|
+
|
|
861
|
+
# ub (optional)
|
|
862
|
+
ub = None
|
|
863
|
+
if ParseAsConstraint.ub in df.columns:
|
|
864
|
+
ub = row[ParseAsConstraint.ub]
|
|
865
|
+
ub = None if is_cell_value_empty(ub) else float(ub)
|
|
866
|
+
|
|
867
|
+
# strict (optional)
|
|
868
|
+
strict = True
|
|
869
|
+
if ParseAsConstraint.strict in df.columns:
|
|
870
|
+
_strict = row[ParseAsConstraint.strict]
|
|
871
|
+
strict = True if is_cell_value_empty(_strict) else bool(_strict) # bool or NaN
|
|
872
|
+
|
|
873
|
+
# using_fem (optional)
|
|
874
|
+
calc_before_solve = True
|
|
875
|
+
if ParseAsConstraint.calc_before_solve in df.columns:
|
|
876
|
+
_calc_before_solve = row[ParseAsConstraint.calc_before_solve]
|
|
877
|
+
calc_before_solve = True if is_cell_value_empty(_calc_before_solve) else bool(_calc_before_solve) # bool or NaN
|
|
878
|
+
|
|
879
|
+
if use:
|
|
880
|
+
# constraint を作る
|
|
881
|
+
opt.constraints[name] = Constraint(
|
|
882
|
+
fun=ScapeGoatObjective(),
|
|
883
|
+
name=name,
|
|
884
|
+
lb=lb,
|
|
885
|
+
ub=ub,
|
|
886
|
+
strict=strict,
|
|
887
|
+
args=(name,),
|
|
888
|
+
kwargs=dict(),
|
|
889
|
+
using_fem=not calc_before_solve,
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
def objective_from_excel(self, name: str):
|
|
893
|
+
r = 1 + search_r(self.output_xlsm_path, self.output_sheet_name, name)
|
|
894
|
+
c = 1 + search_c(self.output_xlsm_path, self.output_sheet_name, ParseAsObjective.value)
|
|
558
895
|
v = self.sh_output.Cells(r, c).value
|
|
559
896
|
return float(v)
|
|
560
897
|
|
|
898
|
+
def constraint_from_excel(self, name: str):
|
|
899
|
+
r = 1 + search_r(self.constraint_xlsm_path, self.constraint_sheet_name, name)
|
|
900
|
+
c = 1 + search_c(self.constraint_xlsm_path, self.constraint_sheet_name, ParseAsConstraint.value)
|
|
901
|
+
v = self.sh_constraint.Cells(r, c).value
|
|
902
|
+
return float(v)
|
|
903
|
+
|
|
561
904
|
|
|
562
905
|
def wait_femtet():
|
|
563
906
|
Femtet = Dispatch('FemtetMacro.Femtet')
|
|
@@ -575,13 +918,31 @@ def _terminate_femtet(femtet_pid_):
|
|
|
575
918
|
# main thread で作成した excel への参照を含む関数を
|
|
576
919
|
# 直接 thread や process に渡すと機能しない
|
|
577
920
|
class ScapeGoatObjective:
|
|
578
|
-
def __call__(self, *args, fem: ExcelInterface or None = None, **kwargs):
|
|
579
|
-
|
|
921
|
+
# def __call__(self, *args, fem: ExcelInterface or None = None, **kwargs):
|
|
922
|
+
# fem.objective_from_excel(*args, **kwargs)
|
|
580
923
|
|
|
581
924
|
@property
|
|
582
925
|
def __globals__(self):
|
|
583
926
|
return tuple()
|
|
584
927
|
|
|
585
928
|
|
|
929
|
+
def is_same_path(p1, p2):
|
|
930
|
+
_p1 = os.path.abspath(p1).lower()
|
|
931
|
+
_p2 = os.path.abspath(p2).lower()
|
|
932
|
+
return _p1 == _p2
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
def is_cell_value_empty(cell_value):
|
|
936
|
+
if isinstance(cell_value, str):
|
|
937
|
+
return cell_value == ''
|
|
938
|
+
elif isinstance(cell_value, int) \
|
|
939
|
+
or isinstance(cell_value, float):
|
|
940
|
+
return np.isnan(cell_value)
|
|
941
|
+
elif cell_value is None:
|
|
942
|
+
return True
|
|
943
|
+
else:
|
|
944
|
+
return False
|
|
945
|
+
|
|
946
|
+
|
|
586
947
|
if __name__ == '__main__':
|
|
587
948
|
ExcelInterface(..., ...)
|