pyfemtet 0.7.1__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/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 +42 -14
- pyfemtet/opt/_femopt_core.py +93 -34
- 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 +296 -124
- pyfemtet/opt/interface/_femtet.py +19 -9
- 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 +28 -18
- 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.1.dist-info → pyfemtet-0.8.0.dist-info}/METADATA +2 -2
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/RECORD +44 -28
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/WHEEL +1 -1
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/LICENSE +0 -0
- {pyfemtet-0.7.1.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,7 +19,7 @@ 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
25
|
|
|
@@ -26,8 +27,10 @@ from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
|
|
|
26
27
|
|
|
27
28
|
from pyfemtet._util.excel_macro_util import watch_excel_macro_error
|
|
28
29
|
from pyfemtet._util.dask_util import lock_or_no_lock
|
|
30
|
+
from pyfemtet._util.excel_parse_util import *
|
|
29
31
|
|
|
30
32
|
from pyfemtet._warning import show_experimental_warning
|
|
33
|
+
from pyfemtet._message.messages import Message as Msg
|
|
31
34
|
|
|
32
35
|
from pyfemtet.opt.interface._base import logger
|
|
33
36
|
|
|
@@ -178,6 +181,8 @@ class ExcelInterface(FEMInterface):
|
|
|
178
181
|
input_sheet_name: str # 変数セルを定義しているシート名
|
|
179
182
|
output_xlsm_path: str # 操作対象の xlsm パス (指定しない場合、input と同一)
|
|
180
183
|
output_sheet_name: str # 計算結果セルを定義しているシート名 (指定しない場合、input と同一)
|
|
184
|
+
constraint_xlsm_path: str # 操作対象の xlsm パス (指定しない場合、input と同一)
|
|
185
|
+
constraint_sheet_name: str # 拘束関数セルを定義しているシート名 (指定しない場合、input と同一)
|
|
181
186
|
|
|
182
187
|
related_file_paths: list[str] # 並列時に個別に並列プロセスの space にアップロードする必要のあるパス
|
|
183
188
|
|
|
@@ -189,6 +194,8 @@ class ExcelInterface(FEMInterface):
|
|
|
189
194
|
sh_input: CDispatch # 変数の定義された WorkSheet
|
|
190
195
|
wb_output: CDispatch # システムを構成する Workbook
|
|
191
196
|
sh_output: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
|
|
197
|
+
wb_constraint: CDispatch # システムを構成する Workbook
|
|
198
|
+
sh_constraint: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
|
|
192
199
|
wb_setup: CDispatch # システムを構成する Workbook
|
|
193
200
|
wb_teardown: CDispatch # システムを構成する Workbook
|
|
194
201
|
|
|
@@ -210,12 +217,16 @@ class ExcelInterface(FEMInterface):
|
|
|
210
217
|
teardown_procedure_name: str
|
|
211
218
|
teardown_procedure_args: list or tuple
|
|
212
219
|
|
|
220
|
+
use_named_range: bool # input を定義したシートにおいて input の値を名前付き範囲で指定するかどうか。
|
|
221
|
+
|
|
213
222
|
def __init__(
|
|
214
223
|
self,
|
|
215
224
|
input_xlsm_path: str or Path,
|
|
216
225
|
input_sheet_name: str,
|
|
217
226
|
output_xlsm_path: str or Path = None,
|
|
218
227
|
output_sheet_name: str = None,
|
|
228
|
+
constraint_xlsm_path: str or Path = None,
|
|
229
|
+
constraint_sheet_name: str = None,
|
|
219
230
|
procedure_name: str = None,
|
|
220
231
|
procedure_args: list or tuple = None,
|
|
221
232
|
connect_method: str = 'auto', # or 'new'
|
|
@@ -231,6 +242,7 @@ class ExcelInterface(FEMInterface):
|
|
|
231
242
|
display_alerts: bool = False,
|
|
232
243
|
terminate_excel_when_quit: bool = None,
|
|
233
244
|
interactive: bool = True,
|
|
245
|
+
use_named_range: bool = True,
|
|
234
246
|
):
|
|
235
247
|
|
|
236
248
|
show_experimental_warning("ExcelInterface")
|
|
@@ -239,7 +251,9 @@ class ExcelInterface(FEMInterface):
|
|
|
239
251
|
self.input_xlsm_path = str(input_xlsm_path) # あとで再取得する
|
|
240
252
|
self.input_sheet_name = input_sheet_name
|
|
241
253
|
self.output_xlsm_path = str(input_xlsm_path) if output_xlsm_path is None else str(output_xlsm_path)
|
|
242
|
-
self.output_sheet_name = output_sheet_name
|
|
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
|
|
243
257
|
self.procedure_name = procedure_name or 'FemtetMacro.FemtetMain'
|
|
244
258
|
self.procedure_args = procedure_args or []
|
|
245
259
|
assert connect_method in ['new', 'auto']
|
|
@@ -264,12 +278,15 @@ class ExcelInterface(FEMInterface):
|
|
|
264
278
|
self.interactive = interactive
|
|
265
279
|
self.display_alerts = display_alerts
|
|
266
280
|
|
|
281
|
+
self.use_named_range = use_named_range
|
|
282
|
+
|
|
267
283
|
# dask サブプロセスのときは space 直下の input_xlsm_path を参照する
|
|
268
284
|
try:
|
|
269
285
|
worker = get_worker()
|
|
270
286
|
space = os.path.abspath(worker.local_directory)
|
|
271
287
|
self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
|
|
272
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))
|
|
273
290
|
self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
|
|
274
291
|
self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
|
|
275
292
|
self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
|
|
@@ -278,6 +295,7 @@ class ExcelInterface(FEMInterface):
|
|
|
278
295
|
except ValueError:
|
|
279
296
|
self.input_xlsm_path = os.path.abspath(self.input_xlsm_path)
|
|
280
297
|
self.output_xlsm_path = os.path.abspath(self.output_xlsm_path)
|
|
298
|
+
self.constraint_xlsm_path = os.path.abspath(self.constraint_xlsm_path)
|
|
281
299
|
self.setup_xlsm_path = os.path.abspath(self.setup_xlsm_path)
|
|
282
300
|
self.teardown_xlsm_path = os.path.abspath(self.teardown_xlsm_path)
|
|
283
301
|
self.related_file_paths = [os.path.abspath(p) for p in self.related_file_paths]
|
|
@@ -288,6 +306,8 @@ class ExcelInterface(FEMInterface):
|
|
|
288
306
|
input_sheet_name=self.input_sheet_name,
|
|
289
307
|
output_xlsm_path=self.output_xlsm_path,
|
|
290
308
|
output_sheet_name=self.output_sheet_name,
|
|
309
|
+
constraint_xlsm_path=self.constraint_xlsm_path,
|
|
310
|
+
constraint_sheet_name=self.constraint_sheet_name,
|
|
291
311
|
procedure_name=self.procedure_name,
|
|
292
312
|
procedure_args=self.procedure_args,
|
|
293
313
|
connect_method='new', # subprocess で connect する際は new を強制する
|
|
@@ -303,6 +323,7 @@ class ExcelInterface(FEMInterface):
|
|
|
303
323
|
visible=self.visible,
|
|
304
324
|
interactive=self.interactive,
|
|
305
325
|
display_alerts=self.display_alerts,
|
|
326
|
+
use_named_range=self.use_named_range,
|
|
306
327
|
)
|
|
307
328
|
FEMInterface.__init__(self, **kwargs)
|
|
308
329
|
|
|
@@ -317,6 +338,9 @@ class ExcelInterface(FEMInterface):
|
|
|
317
338
|
if not is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
318
339
|
client.upload_file(self.output_xlsm_path, False)
|
|
319
340
|
|
|
341
|
+
if not is_same_path(self.input_xlsm_path, self.constraint_xlsm_path):
|
|
342
|
+
client.upload_file(self.constraint_xlsm_path, False)
|
|
343
|
+
|
|
320
344
|
if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
321
345
|
client.upload_file(self.setup_xlsm_path, False)
|
|
322
346
|
|
|
@@ -336,6 +360,7 @@ class ExcelInterface(FEMInterface):
|
|
|
336
360
|
if space is not None:
|
|
337
361
|
self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
|
|
338
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))
|
|
339
364
|
self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
|
|
340
365
|
self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
|
|
341
366
|
self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
|
|
@@ -354,6 +379,7 @@ class ExcelInterface(FEMInterface):
|
|
|
354
379
|
|
|
355
380
|
self.input_xlsm_path = proc_path(self.input_xlsm_path, False)
|
|
356
381
|
self.output_xlsm_path = proc_path(self.output_xlsm_path, True)
|
|
382
|
+
self.constraint_xlsm_path = proc_path(self.constraint_xlsm_path, True)
|
|
357
383
|
self.setup_xlsm_path = proc_path(self.setup_xlsm_path, True)
|
|
358
384
|
self.teardown_xlsm_path = proc_path(self.teardown_xlsm_path, True)
|
|
359
385
|
|
|
@@ -367,18 +393,25 @@ class ExcelInterface(FEMInterface):
|
|
|
367
393
|
_set_autosave_enabled(False)
|
|
368
394
|
|
|
369
395
|
# excel に繋ぐ
|
|
370
|
-
|
|
396
|
+
with lock_or_no_lock('connect-excel'):
|
|
397
|
+
self.connect_excel(self.connect_method)
|
|
398
|
+
sleep(1)
|
|
371
399
|
|
|
372
400
|
# load_objective は 1 回目に呼ばれたのが main thread なので
|
|
373
401
|
# subprocess に入った後でもう一度 load objective を行う
|
|
374
402
|
from pyfemtet.opt.optimizer import AbstractOptimizer
|
|
375
|
-
from pyfemtet.opt._femopt_core import Objective
|
|
403
|
+
from pyfemtet.opt._femopt_core import Objective, Constraint
|
|
376
404
|
opt: AbstractOptimizer = kwargs['opt']
|
|
377
405
|
obj: Objective
|
|
378
406
|
for obj_name, obj in opt.objectives.items():
|
|
379
407
|
if isinstance(obj.fun, ScapeGoatObjective):
|
|
380
408
|
opt.objectives[obj_name].fun = self.objective_from_excel
|
|
381
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
|
+
|
|
382
415
|
# excel の setup 関数を必要なら実行する
|
|
383
416
|
if self.setup_procedure_name is not None:
|
|
384
417
|
with lock_or_no_lock('excel_setup_procedure'):
|
|
@@ -391,9 +424,10 @@ class ExcelInterface(FEMInterface):
|
|
|
391
424
|
|
|
392
425
|
# 再計算
|
|
393
426
|
self.excel.CalculateFull()
|
|
427
|
+
sleep(1)
|
|
394
428
|
|
|
395
429
|
except com_error as e:
|
|
396
|
-
raise RuntimeError(f'Failed to run macro {self.
|
|
430
|
+
raise RuntimeError(f'Failed to run macro {self.setup_procedure_name}. The original message is: {e}')
|
|
397
431
|
|
|
398
432
|
def connect_excel(self, connect_method):
|
|
399
433
|
|
|
@@ -404,6 +438,10 @@ class ExcelInterface(FEMInterface):
|
|
|
404
438
|
else:
|
|
405
439
|
self.excel = DispatchEx('Excel.Application')
|
|
406
440
|
|
|
441
|
+
# FemtetRef を追加する
|
|
442
|
+
self.open_femtet_ref_xla() # ここでエラーが発生しているかも?
|
|
443
|
+
sleep(0.5)
|
|
444
|
+
|
|
407
445
|
# 起動した excel の pid を記憶する
|
|
408
446
|
self._excel_hwnd = self.excel.hWnd
|
|
409
447
|
self._excel_pid = 0
|
|
@@ -415,7 +453,9 @@ class ExcelInterface(FEMInterface):
|
|
|
415
453
|
self.excel.Visible = self.visible
|
|
416
454
|
self.excel.DisplayAlerts = self.display_alerts
|
|
417
455
|
self.excel.Interactive = self.interactive
|
|
456
|
+
sleep(0.5)
|
|
418
457
|
|
|
458
|
+
# ===== input =====
|
|
419
459
|
# 開く
|
|
420
460
|
self.excel.Workbooks.Open(str(self.input_xlsm_path))
|
|
421
461
|
for wb in self.excel.Workbooks:
|
|
@@ -433,6 +473,7 @@ class ExcelInterface(FEMInterface):
|
|
|
433
473
|
else:
|
|
434
474
|
raise RuntimeError(f'Sheet {self.input_sheet_name} does not exist in the book {self.wb_input.Name}.')
|
|
435
475
|
|
|
476
|
+
# ===== output =====
|
|
436
477
|
# 開く (output)
|
|
437
478
|
if is_same_path(self.input_xlsm_path, self.output_xlsm_path):
|
|
438
479
|
self.wb_output = self.wb_input
|
|
@@ -453,6 +494,28 @@ class ExcelInterface(FEMInterface):
|
|
|
453
494
|
else:
|
|
454
495
|
raise RuntimeError(f'Sheet {self.output_sheet_name} does not exist in the book {self.wb_output.Name}.')
|
|
455
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 =====
|
|
456
519
|
# 開く (setup)
|
|
457
520
|
if is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
458
521
|
self.wb_setup = self.wb_input
|
|
@@ -465,6 +528,7 @@ class ExcelInterface(FEMInterface):
|
|
|
465
528
|
else:
|
|
466
529
|
raise RuntimeError(f'Cannot open {self.setup_xlsm_path}')
|
|
467
530
|
|
|
531
|
+
# ===== teardown =====
|
|
468
532
|
# 開く (teardown)
|
|
469
533
|
if is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
|
|
470
534
|
self.wb_teardown = self.wb_input
|
|
@@ -478,28 +542,29 @@ class ExcelInterface(FEMInterface):
|
|
|
478
542
|
raise RuntimeError(f'Cannot open {self.teardown_xlsm_path}')
|
|
479
543
|
|
|
480
544
|
# book に参照設定を追加する
|
|
481
|
-
self.
|
|
482
|
-
self.
|
|
483
|
-
self.
|
|
484
|
-
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)
|
|
485
550
|
|
|
486
|
-
def
|
|
551
|
+
def open_femtet_ref_xla(self):
|
|
487
552
|
|
|
488
|
-
#
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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):
|
|
503
568
|
|
|
504
569
|
# search
|
|
505
570
|
ref_file_2 = os.path.abspath(util._get_femtetmacro_dllpath())
|
|
@@ -508,44 +573,47 @@ class ExcelInterface(FEMInterface):
|
|
|
508
573
|
if ref.Description is not None:
|
|
509
574
|
if ref.Description == 'FemtetMacro': # FemtetMacro
|
|
510
575
|
contain_2 = True
|
|
576
|
+
break
|
|
511
577
|
# add
|
|
512
578
|
if not contain_2:
|
|
513
579
|
wb.VBProject.References.AddFromFile(ref_file_2)
|
|
514
580
|
|
|
515
581
|
def remove_femtet_ref_xla(self, wb):
|
|
516
|
-
|
|
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)
|
|
528
|
-
|
|
529
582
|
# search
|
|
530
583
|
for ref in wb.VBProject.References:
|
|
531
584
|
if ref.Description is not None:
|
|
532
585
|
if ref.Description == 'FemtetMacro': # FemtetMacro
|
|
533
586
|
wb.VBProject.References.Remove(ref)
|
|
534
587
|
|
|
535
|
-
def
|
|
536
|
-
|
|
588
|
+
def update_parameter(self, parameters: pd.DataFrame, with_warning=False) -> Optional[List[str]]:
|
|
537
589
|
# params を作成
|
|
538
590
|
params = dict()
|
|
539
591
|
for _, row in parameters.iterrows():
|
|
540
592
|
params[row['name']] = row['value']
|
|
541
593
|
|
|
542
594
|
# excel シートの変数更新
|
|
543
|
-
|
|
544
|
-
|
|
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
|
|
545
610
|
|
|
546
611
|
# 再計算
|
|
547
612
|
self.excel.CalculateFull()
|
|
548
613
|
|
|
614
|
+
def update(self, parameters: pd.DataFrame) -> None:
|
|
615
|
+
self.update_parameter(parameters)
|
|
616
|
+
|
|
549
617
|
# マクロ実行
|
|
550
618
|
try:
|
|
551
619
|
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout):
|
|
@@ -567,12 +635,12 @@ class ExcelInterface(FEMInterface):
|
|
|
567
635
|
if already_terminated:
|
|
568
636
|
return
|
|
569
637
|
|
|
570
|
-
logger.info(
|
|
638
|
+
logger.info(Msg.INFO_TERMINATING_EXCEL)
|
|
571
639
|
|
|
572
640
|
# 参照設定解除の前に終了処理を必要なら実施する
|
|
573
641
|
# excel の setup 関数を必要なら実行する
|
|
574
642
|
if self.teardown_procedure_name is not None:
|
|
575
|
-
with lock_or_no_lock('
|
|
643
|
+
with lock_or_no_lock('excel_teardown_procedure'):
|
|
576
644
|
try:
|
|
577
645
|
with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout, restore_book=False):
|
|
578
646
|
self.excel.Run(
|
|
@@ -586,15 +654,17 @@ class ExcelInterface(FEMInterface):
|
|
|
586
654
|
except com_error as e:
|
|
587
655
|
raise RuntimeError(f'Failed to run macro {self.teardown_procedure_args}. The original message is: {e}')
|
|
588
656
|
|
|
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.
|
|
593
|
-
self.remove_femtet_ref_xla(self.
|
|
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)
|
|
594
663
|
|
|
595
664
|
# シートの COM オブジェクト変数を削除する
|
|
596
665
|
del self.sh_input
|
|
597
666
|
del self.sh_output
|
|
667
|
+
del self.sh_constraint
|
|
598
668
|
|
|
599
669
|
# workbook を閉じる
|
|
600
670
|
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
@@ -604,6 +674,10 @@ class ExcelInterface(FEMInterface):
|
|
|
604
674
|
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
605
675
|
self.wb_output.Close(SaveChanges := False)
|
|
606
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
|
+
|
|
607
681
|
if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
|
|
608
682
|
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
609
683
|
self.wb_setup.Close(SaveChanges := False)
|
|
@@ -614,10 +688,10 @@ class ExcelInterface(FEMInterface):
|
|
|
614
688
|
|
|
615
689
|
del self.wb_input
|
|
616
690
|
del self.wb_output
|
|
691
|
+
del self.wb_constraint
|
|
617
692
|
del self.wb_setup
|
|
618
693
|
del self.wb_teardown
|
|
619
694
|
|
|
620
|
-
|
|
621
695
|
# excel の終了
|
|
622
696
|
with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
|
|
623
697
|
self.excel.Quit()
|
|
@@ -625,12 +699,12 @@ class ExcelInterface(FEMInterface):
|
|
|
625
699
|
|
|
626
700
|
# ここで Excel のプロセスが残らず落ちる
|
|
627
701
|
gc.collect()
|
|
702
|
+
logger.info(Msg.INFO_TERMINATED_EXCEL)
|
|
628
703
|
|
|
629
704
|
if self._with_femtet_autosave_setting:
|
|
630
705
|
from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled
|
|
631
|
-
logger.info(
|
|
706
|
+
logger.info(Msg.INFO_RESTORING_FEMTET_AUTOSAVE)
|
|
632
707
|
_set_autosave_enabled(self._femtet_autosave_buffer)
|
|
633
|
-
logger.info('自動保存機能の設定を元に戻しました。')
|
|
634
708
|
|
|
635
709
|
# 直接アクセスしてもよいが、ユーザーに易しい名前にするためだけのプロパティ
|
|
636
710
|
@property
|
|
@@ -653,93 +727,180 @@ class ExcelInterface(FEMInterface):
|
|
|
653
727
|
from pyfemtet.opt.optimizer import AbstractOptimizer, logger
|
|
654
728
|
opt: AbstractOptimizer
|
|
655
729
|
|
|
656
|
-
df =
|
|
657
|
-
self.input_xlsm_path,
|
|
658
|
-
self.input_sheet_name,
|
|
659
|
-
header=0,
|
|
660
|
-
index_col=None,
|
|
661
|
-
)
|
|
730
|
+
df = ParseAsParameter.parse(self.input_xlsm_path, self.input_sheet_name)
|
|
662
731
|
|
|
663
|
-
# TODO: 使い勝手を考える
|
|
664
732
|
for i, row in df.iterrows():
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
value = float(value)
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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)
|
|
691
789
|
|
|
692
790
|
def load_objective(self, opt):
|
|
693
791
|
from pyfemtet.opt.optimizer import AbstractOptimizer, logger
|
|
694
792
|
from pyfemtet.opt._femopt_core import Objective
|
|
695
793
|
opt: AbstractOptimizer
|
|
696
794
|
|
|
697
|
-
df =
|
|
698
|
-
self.output_xlsm_path,
|
|
699
|
-
self.output_sheet_name,
|
|
700
|
-
header=0,
|
|
701
|
-
index_col=None,
|
|
702
|
-
)
|
|
795
|
+
df = ParseAsObjective.parse(self.output_xlsm_path, self.output_sheet_name)
|
|
703
796
|
|
|
704
|
-
# TODO: 使い勝手を考える
|
|
705
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.'
|
|
706
811
|
try:
|
|
707
|
-
name = row['name']
|
|
708
|
-
_ = row['current']
|
|
709
|
-
direction = row['direction']
|
|
710
|
-
value_column_index = list(df.columns).index('current')
|
|
711
|
-
except KeyError:
|
|
712
|
-
logger.warn('列名が「name」「current」「direction」になっていません。この順に並んでいると仮定して処理を続けます。')
|
|
713
|
-
name, _, direction, *_residuals = row.iloc[0]
|
|
714
|
-
value_column_index = 1
|
|
715
|
-
|
|
716
|
-
name = str(name)
|
|
717
|
-
|
|
718
|
-
# direction は minimize or maximize or float
|
|
719
|
-
try:
|
|
720
|
-
# float or not
|
|
721
812
|
direction = float(direction)
|
|
722
|
-
|
|
723
813
|
except ValueError:
|
|
724
|
-
# 'minimize' or 'maximize
|
|
725
814
|
direction = str(direction).lower()
|
|
726
|
-
assert
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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)
|
|
740
895
|
v = self.sh_output.Cells(r, c).value
|
|
741
896
|
return float(v)
|
|
742
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
|
+
|
|
743
904
|
|
|
744
905
|
def wait_femtet():
|
|
745
906
|
Femtet = Dispatch('FemtetMacro.Femtet')
|
|
@@ -757,8 +918,8 @@ def _terminate_femtet(femtet_pid_):
|
|
|
757
918
|
# main thread で作成した excel への参照を含む関数を
|
|
758
919
|
# 直接 thread や process に渡すと機能しない
|
|
759
920
|
class ScapeGoatObjective:
|
|
760
|
-
def __call__(self, *args, fem: ExcelInterface or None = None, **kwargs):
|
|
761
|
-
|
|
921
|
+
# def __call__(self, *args, fem: ExcelInterface or None = None, **kwargs):
|
|
922
|
+
# fem.objective_from_excel(*args, **kwargs)
|
|
762
923
|
|
|
763
924
|
@property
|
|
764
925
|
def __globals__(self):
|
|
@@ -771,6 +932,17 @@ def is_same_path(p1, p2):
|
|
|
771
932
|
return _p1 == _p2
|
|
772
933
|
|
|
773
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
|
+
|
|
774
946
|
|
|
775
947
|
if __name__ == '__main__':
|
|
776
948
|
ExcelInterface(..., ...)
|