pyfemtet 0.8.8__py3-none-any.whl → 0.8.10__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.

Files changed (35) hide show
  1. pyfemtet/__init__.py +1 -1
  2. pyfemtet/_message/locales/ja/LC_MESSAGES/messages.mo +0 -0
  3. pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +10 -2
  4. pyfemtet/_message/locales/messages.pot +10 -2
  5. pyfemtet/_message/messages.py +2 -1
  6. pyfemtet/_util/excel_parse_util.py +33 -15
  7. pyfemtet/opt/_femopt.py +3 -1
  8. pyfemtet/opt/_femopt_core.py +34 -26
  9. pyfemtet/opt/_test_utils/record_history.py +1 -0
  10. pyfemtet/opt/advanced_samples/excel_ui/femtet-macro.xlsm +0 -0
  11. pyfemtet/opt/advanced_samples/meta_script/meta_script.py +163 -0
  12. pyfemtet/opt/advanced_samples/meta_script/sample.yaml +14 -0
  13. pyfemtet/opt/advanced_samples/meta_script/yaml_generator.txt +0 -0
  14. pyfemtet/opt/advanced_samples/meta_script/yaml_generator.xlsm +0 -0
  15. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric.femprj +0 -0
  16. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart.py +99 -0
  17. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart_jp.py +102 -0
  18. pyfemtet/opt/interface/__init__.py +1 -1
  19. pyfemtet/opt/interface/_base.py +13 -4
  20. pyfemtet/opt/interface/_excel_interface.py +45 -39
  21. pyfemtet/opt/interface/_femtet.py +63 -2
  22. pyfemtet/opt/interface/_femtet_excel.py +138 -0
  23. pyfemtet/opt/interface/_surrogate/_base.py +43 -0
  24. pyfemtet/opt/interface/_surrogate_excel.py +102 -0
  25. pyfemtet/opt/optimizer/_base.py +2 -6
  26. pyfemtet/opt/optimizer/_optuna/_optuna.py +2 -2
  27. pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.femprj +0 -0
  28. pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.py +52 -0
  29. pyfemtet/opt/samples/femprj_sample_jp/gau_ex12_parametric_jp.py +52 -0
  30. pyfemtet/opt/visualization/_process_monitor/pages.py +13 -0
  31. {pyfemtet-0.8.8.dist-info → pyfemtet-0.8.10.dist-info}/METADATA +2 -1
  32. {pyfemtet-0.8.8.dist-info → pyfemtet-0.8.10.dist-info}/RECORD +35 -23
  33. {pyfemtet-0.8.8.dist-info → pyfemtet-0.8.10.dist-info}/WHEEL +1 -1
  34. {pyfemtet-0.8.8.dist-info → pyfemtet-0.8.10.dist-info}/LICENSE +0 -0
  35. {pyfemtet-0.8.8.dist-info → pyfemtet-0.8.10.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,102 @@
1
+ import os
2
+ from time import sleep
3
+
4
+ from optuna.samplers import RandomSampler, NSGAIISampler, GPSampler, BaseSampler
5
+
6
+ from pyfemtet.opt import FEMOpt, FemtetInterface, OptunaOptimizer
7
+
8
+ os.chdir(os.path.dirname(__file__))
9
+
10
+
11
+ def get_res_freq(Femtet):
12
+ Galileo = Femtet.Gogh.Galileo
13
+ Galileo.Mode = 0
14
+ sleep(0.01)
15
+ return Galileo.GetFreq().Real
16
+
17
+
18
+ def main(n_trials, sampler_class: type[BaseSampler], sampler_kwargs: dict):
19
+ """メイン関数
20
+
21
+ このサンプルは最適化を途中で中断して続きから行う場合で、
22
+ 各リスタートで異なるアルゴリズムを使用して
23
+ 最適化を再開する方法を示しています。
24
+
25
+ このメイン関数は n_trials と sampler_class を与えると
26
+ その回数、アルゴリズムに応じて最適化を行います。
27
+
28
+ Args:
29
+
30
+ n_trials (int):
31
+ 最適化を終了するために必要な追加の成功した試行回数。
32
+
33
+ sampler_class (type[optuna.samplers.BaseSampler]):
34
+ 使用するアルゴリズム。
35
+
36
+ sampler_kwargs (dict):
37
+ アルゴリズムの引数。
38
+
39
+ """
40
+
41
+
42
+ # Femtet に接続します。
43
+ fem = FemtetInterface(
44
+ femprj_path='gal_ex13_parametric.femprj',
45
+ )
46
+
47
+ # 最適化オブジェクトを初期化します。
48
+ opt = OptunaOptimizer(
49
+ sampler_class=sampler_class,
50
+ sampler_kwargs=sampler_kwargs,
51
+ )
52
+
53
+ # リスタートするためには以前の最適化の履歴を
54
+ # 新しい最適化プログラムに知らせる必要があります。
55
+ # FEMOpt の `history_path` 引数に csv を指定すると、
56
+ # それが存在しない場合、新しい csv ファイルを作り、
57
+ # それが存在する場合、その csv ファイルの続きから
58
+ # 最適化を実行します。
59
+ #
60
+ # 注意:
61
+ # リスタートする場合、変数の数と名前、目的関数の数と
62
+ # 名前、および拘束関数の数と名前が一貫している必要が
63
+ # あります。
64
+ # ただし、変数の上下限や目的関数の方向、拘束関数の内容
65
+ # などは変更できます。
66
+ #
67
+ # 注意:
68
+ # OptunaOptimizer を使用する場合、csv と同名の
69
+ # .db ファイル (ここでは restarting-sample.db) が
70
+ # csv ファイルと同じフォルダにある必要があります。
71
+ femopt = FEMOpt(
72
+ fem=fem,
73
+ opt=opt,
74
+ history_path='restarting-sample.csv'
75
+ )
76
+
77
+ # 設計パラメータを指定します。
78
+ femopt.add_parameter('length', 0.1, 0.02, 0.2)
79
+ femopt.add_parameter('width', 0.01, 0.001, 0.02)
80
+ femopt.add_parameter('base_radius', 0.008, 0.006, 0.01)
81
+
82
+ # 目的関数を指定します。
83
+ femopt.add_objective(fun=get_res_freq, name='First Resonant Frequency (Hz)', direction=800)
84
+
85
+ # 最適化を実行します。
86
+ femopt.set_random_seed(42)
87
+ femopt.optimize(n_trials=n_trials, confirm_before_exit=False)
88
+
89
+
90
+ if __name__ == '__main__':
91
+ # 最初に、RandomSampler を使用して 3 回計算を行います。
92
+ main(3, RandomSampler, {})
93
+
94
+ # 次に、NSGAIISampler を使用して 3 回計算を行います。
95
+ main(3, NSGAIISampler, {})
96
+
97
+ # 最後に、GPSampler を使用して 3 回計算を行います。
98
+ main(3, GPSampler, {'n_startup_trials': 0, 'deterministic_objective': True})
99
+
100
+ # このプログラム終了後、
101
+ # restarting-sample.csv と同名の .db ファイルを用いて
102
+ # さらに続きの最適化を行うことができます。
@@ -25,7 +25,7 @@ from pyfemtet.opt.interface._surrogate._base import SurrogateModelInterfaceBase
25
25
  from pyfemtet.opt.interface._surrogate._singletaskgp import PoFBoTorchInterface
26
26
 
27
27
 
28
- __all__ =[
28
+ __all__ = [
29
29
  'FEMInterface',
30
30
  'NoFEM',
31
31
  'FemtetInterface',
@@ -24,18 +24,27 @@ class FEMInterface(ABC):
24
24
 
25
25
  """
26
26
 
27
+ kwargs = None
28
+
27
29
  def __init__(
28
30
  self,
29
31
  **kwargs
30
32
  ):
31
33
  # restore のための情報保管
32
- self.kwargs = kwargs
34
+ if self.kwargs is None:
35
+ self.kwargs = kwargs
36
+ else:
37
+ self.kwargs.update(kwargs)
33
38
 
34
39
  @abstractmethod
35
40
  def update(self, parameters: pd.DataFrame) -> None:
36
41
  """Updates the FEM analysis based on the proposed parameters."""
37
42
  raise NotImplementedError('update() must be implemented.')
38
43
 
44
+ @property
45
+ def object_passed_to_functions(self):
46
+ return self
47
+
39
48
  def check_param_value(self, param_name) -> float or None:
40
49
  """Checks the value of a parameter in the FEM model (if implemented in concrete class)."""
41
50
  pass
@@ -50,13 +59,13 @@ class FEMInterface(ABC):
50
59
  pass
51
60
 
52
61
  def load_parameter(self, opt) -> None: # opt: AbstractOptimizer
53
- raise NotImplementedError()
62
+ pass
54
63
 
55
64
  def load_objective(self, opt) -> None: # opt: AbstractOptimizer
56
- raise NotImplementedError()
65
+ pass
57
66
 
58
67
  def load_constraint(self, opt) -> None: # opt: AbstractOptimizer
59
- raise NotImplementedError()
68
+ pass
60
69
 
61
70
  def _setup_before_parallel(self, client) -> None:
62
71
  """Preprocessing before launching a dask worker (if implemented in concrete class).
@@ -55,36 +55,36 @@ class ExcelInterface(FEMInterface):
55
55
  input_xlsm_path (str or Path):
56
56
  設計変数の定義を含む Excel ファイルのパスを指定
57
57
  します。
58
-
58
+
59
59
  input_sheet_name (str):
60
60
  設計変数の定義を含むシートの名前を指定します。
61
-
61
+
62
62
  output_xlsm_path (str or Path, optional):
63
63
  目的関数の定義を含む Excel ファイルのパスを指定
64
64
  します。指定しない場合は ``input_xlsm_path`` と
65
65
  同じと見做します。
66
-
66
+
67
67
  output_sheet_name (str, optional):
68
68
  目的関数の定義を含む含むシートの名前を指定します。
69
69
  指定しない場合は ``input_sheet_name`` と同じと見
70
70
  做します。
71
-
71
+
72
72
  procedure_name (str, optional):
73
73
  Excel マクロ関数名を指定します。指定しない場合は
74
74
  ``FemtetMacro.FemtetMain`` と見做します。
75
-
75
+
76
76
  procedure_args (list or tuple, optional):
77
77
  Excel マクロ関数に渡す引数をリストまたはタプルで
78
78
  指定します。
79
-
79
+
80
80
  connect_method (str, optional):
81
81
  Excel との接続方法を指定します。 'auto' または
82
82
  'new' が利用可能です。デフォルトは 'auto' です。
83
-
83
+
84
84
  procedure_timeout (float or None, optional):
85
85
  Excel マクロ関数のタイムアウト時間を秒単位で指定
86
86
  します。 None の場合はタイムアウトなしとなります。
87
-
87
+
88
88
  setup_xlsm_path (str or Path, optional):
89
89
  セットアップ時に呼ぶ関数を含む xlsm のパスです。
90
90
  指定しない場合は ``input_xlsm_path`` と
@@ -135,7 +135,7 @@ class ExcelInterface(FEMInterface):
135
135
  Attributes:
136
136
  input_xlsm_path (Path):
137
137
  設計変数の定義を含む Excel ファイルのパス。
138
-
138
+
139
139
  input_sheet_name (str):
140
140
  設計変数の定義を含むシートの名前。
141
141
 
@@ -257,7 +257,7 @@ class ExcelInterface(FEMInterface):
257
257
  self.output_sheet_name = output_sheet_name if output_sheet_name is not None else input_sheet_name
258
258
  self.constraint_xlsm_path = str(input_xlsm_path) if constraint_xlsm_path is None else str(constraint_xlsm_path)
259
259
  self.constraint_sheet_name = constraint_sheet_name or self.input_sheet_name
260
- self.procedure_name = procedure_name or 'FemtetMacro.FemtetMain'
260
+ self.procedure_name = procedure_name
261
261
  self.procedure_args = procedure_args or []
262
262
  assert connect_method in ['new', 'auto']
263
263
  self.connect_method = connect_method
@@ -271,7 +271,8 @@ class ExcelInterface(FEMInterface):
271
271
  self.setup_procedure_name = setup_procedure_name
272
272
  self.setup_procedure_args = setup_procedure_args or []
273
273
 
274
- self.teardown_xlsm_path = str(input_xlsm_path) if teardown_xlsm_path is None else str(teardown_xlsm_path) # あとで取得する
274
+ self.teardown_xlsm_path = str(input_xlsm_path) if teardown_xlsm_path is None else str(
275
+ teardown_xlsm_path) # あとで取得する
275
276
  self.teardown_procedure_name = teardown_procedure_name
276
277
  self.teardown_procedure_args = teardown_procedure_args or []
277
278
 
@@ -516,7 +517,8 @@ class ExcelInterface(FEMInterface):
516
517
  self.sh_constraint = sh
517
518
  break
518
519
  else:
519
- raise RuntimeError(f'Sheet {self.constraint_sheet_name} does not exist in the book {self.wb_constraint.Name}.')
520
+ raise RuntimeError(
521
+ f'Sheet {self.constraint_sheet_name} does not exist in the book {self.wb_constraint.Name}.')
520
522
 
521
523
  # ===== setup =====
522
524
  # 開く (setup)
@@ -618,18 +620,19 @@ class ExcelInterface(FEMInterface):
618
620
  self.update_parameter(parameters)
619
621
 
620
622
  # マクロ実行
621
- try:
622
- with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout):
623
- self.excel.Run(
624
- f'{self.procedure_name}',
625
- *self.procedure_args
626
- )
623
+ if self.procedure_name is not None:
624
+ try:
625
+ with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout):
626
+ self.excel.Run(
627
+ f'{self.procedure_name}',
628
+ *self.procedure_args
629
+ )
627
630
 
628
- # 再計算
629
- self.excel.CalculateFull()
631
+ # 再計算
632
+ self.excel.CalculateFull()
630
633
 
631
- except com_error as e:
632
- raise SolveError(f'Failed to run macro {self.procedure_name}. The original message is: {e}')
634
+ except com_error as e:
635
+ raise SolveError(f'Failed to run macro {self.procedure_name}. The original message is: {e}')
633
636
 
634
637
  def quit(self):
635
638
  if self.terminate_excel_when_quit:
@@ -655,7 +658,8 @@ class ExcelInterface(FEMInterface):
655
658
  self.excel.CalculateFull()
656
659
 
657
660
  except com_error as e:
658
- raise RuntimeError(f'Failed to run macro {self.teardown_procedure_args}. The original message is: {e}')
661
+ raise RuntimeError(
662
+ f'Failed to run macro {self.teardown_procedure_args}. The original message is: {e}')
659
663
 
660
664
  # 不具合の原因になる場合があるので参照設定は解除しないこと
661
665
  # self.remove_femtet_ref_xla(self.wb_input)
@@ -791,11 +795,14 @@ class ExcelInterface(FEMInterface):
791
795
  opt.variables.add_expression(fixed_prm)
792
796
 
793
797
  def load_objective(self, opt):
794
- from pyfemtet.opt.optimizer import AbstractOptimizer, logger
798
+ from pyfemtet.opt.optimizer import AbstractOptimizer
795
799
  from pyfemtet.opt._femopt_core import Objective
796
800
  opt: AbstractOptimizer
797
801
 
798
- df = ParseAsObjective.parse(self.output_xlsm_path, self.output_sheet_name)
802
+ df = ParseAsObjective.parse(
803
+ self.output_xlsm_path,
804
+ self.output_sheet_name,
805
+ )
799
806
 
800
807
  for i, row in df.iterrows():
801
808
 
@@ -827,22 +834,19 @@ class ExcelInterface(FEMInterface):
827
834
  kwargs=dict(),
828
835
  )
829
836
 
830
- def load_constraint(self, opt):
831
- from pyfemtet.opt.optimizer import AbstractOptimizer, logger
837
+ def load_constraint(self, opt, raise_if_no_keyword=False):
838
+ from pyfemtet.opt.optimizer import AbstractOptimizer
832
839
  from pyfemtet.opt._femopt_core import Constraint
833
840
  opt: AbstractOptimizer
834
841
 
835
- # TODO:
836
- # constraint optional である。
837
- # 現在は実装していないが、シートから問題を取得するよりよいロジックができたら
838
- # (つまり、同じシートからパラメータと拘束を取得出来るようになったら)__init__ 内で
839
- # constraint に None が与えられたのか故意に input_sheet_name と同じシート名を
840
- # 与えられたのか分別できる実装に変えてそのチェック処理をここに反映する。
841
- # constraint_sheet_name が指定されていない場合何もしない
842
- if (self.constraint_sheet_name == self.input_sheet_name) and is_same_path(self.input_xlsm_path, self.constraint_xlsm_path):
843
- return
844
-
845
- df = ParseAsConstraint.parse(self.constraint_xlsm_path, self.constraint_sheet_name)
842
+ # constraint は optional であるが
843
+ # __init__ input_sheet_name を入れられるので
844
+ # ここで constraint が実際に与えられているか判断する
845
+ df = ParseAsConstraint.parse(
846
+ self.constraint_xlsm_path,
847
+ self.constraint_sheet_name,
848
+ raise_if_no_keyword=raise_if_no_keyword,
849
+ )
846
850
 
847
851
  for i, row in df.iterrows():
848
852
 
@@ -877,7 +881,8 @@ class ExcelInterface(FEMInterface):
877
881
  calc_before_solve = True
878
882
  if ParseAsConstraint.calc_before_solve in df.columns:
879
883
  _calc_before_solve = row[ParseAsConstraint.calc_before_solve]
880
- calc_before_solve = True if is_cell_value_empty(_calc_before_solve) else bool(_calc_before_solve) # bool or NaN
884
+ calc_before_solve = True if is_cell_value_empty(_calc_before_solve) else bool(
885
+ _calc_before_solve) # bool or NaN
881
886
 
882
887
  if use:
883
888
  # constraint を作る
@@ -892,6 +897,7 @@ class ExcelInterface(FEMInterface):
892
897
  using_fem=not calc_before_solve,
893
898
  )
894
899
 
900
+ # TODO: femopt_core.Function の仕様を変えたらここも変える
895
901
  def objective_from_excel(self, name: str):
896
902
  r = 1 + search_r(self.output_xlsm_path, self.output_sheet_name, name)
897
903
  c = 1 + search_c(self.output_xlsm_path, self.output_sheet_name, ParseAsObjective.value)
@@ -192,7 +192,8 @@ class FemtetInterface(FEMInterface):
192
192
  # subprocess で restore するための情報保管
193
193
  # パスなどは connect_and_open_femtet での処理結果を反映し
194
194
  # メインで開いた解析モデルが確実に開かれるようにする
195
- super().__init__(
195
+ FEMInterface.__init__(
196
+ self,
196
197
  femprj_path=self.femprj_path,
197
198
  model_name=self.model_name,
198
199
  open_result_with_gui=self.open_result_with_gui,
@@ -201,6 +202,43 @@ class FemtetInterface(FEMInterface):
201
202
  **kwargs
202
203
  )
203
204
 
205
+ @property
206
+ def object_passed_to_functions(self):
207
+ return self.Femtet
208
+
209
+ def use_parametric_output_as_objective(self, number: int, direction: str | float = 'minimize') -> None:
210
+ """Use output setting of Femtet parametric analysis as an objective function.
211
+
212
+ Args:
213
+ number (int): The index of output settings tab in parametric analysis dialog of Femtet. Starts at 1.
214
+ direction (str | float): Objective direction. Valid input is one of 'minimize', 'maximize' or a specific value. Defaults to 'minimize'.
215
+
216
+ Returns:
217
+ None
218
+
219
+ """
220
+ # check
221
+ if isinstance(direction, str):
222
+ if direction not in ('minimize', 'maximize'):
223
+ raise ValueError(f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}')
224
+ else:
225
+ try:
226
+ direction = float(direction)
227
+ except (TypeError, ValueError):
228
+ raise ValueError(f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}')
229
+
230
+ index = {number - 1: direction}
231
+
232
+ if self.parametric_output_indexes_use_as_objective is None:
233
+ self.parametric_output_indexes_use_as_objective = index
234
+
235
+ else:
236
+ self.parametric_output_indexes_use_as_objective.update(index)
237
+
238
+ # TODO: FEMInterface.__init__ の仕様を変えたらここも変える
239
+ self.kwargs['parametric_output_indexes_use_as_objective'] = self.parametric_output_indexes_use_as_objective
240
+
241
+
204
242
  def __del__(self):
205
243
  self.quit()
206
244
  # CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
@@ -682,6 +720,14 @@ class FemtetInterface(FEMInterface):
682
720
 
683
721
  if self.parametric_output_indexes_use_as_objective is not None:
684
722
  from pyfemtet.opt.interface._femtet_parametric import solve_via_parametric_dll
723
+
724
+ pdt_path = self.Femtet.ResultFilePath + '.pdt'
725
+
726
+ # 前のものが残っているとややこしいので消しておく
727
+ if os.path.exists(pdt_path):
728
+ os.remove(pdt_path)
729
+
730
+ # parametric analysis 経由で解析
685
731
  self._call_femtet_api(
686
732
  fun=solve_via_parametric_dll,
687
733
  return_value_if_failed=False,
@@ -690,8 +736,23 @@ class FemtetInterface(FEMInterface):
690
736
  is_Gaudi_method=True,
691
737
  args=(self.Femtet,),
692
738
  )
739
+
740
+ # parametric analysis の場合
741
+ # ダイアログで「解析結果を保存する」に
742
+ # チェックがついていないと次にすべき
743
+ # OpenCurrentResult に失敗するので
744
+ # parametric の場合も pdt を保存する
745
+ self._call_femtet_api(
746
+ fun=self.Femtet.SavePDT,
747
+ args=(pdt_path, True),
748
+ return_value_if_failed=False,
749
+ if_error=SolveError,
750
+ error_message=Msg.ERR_FAILED_TO_SAVE_PDT,
751
+ is_Gaudi_method=False,
752
+ )
753
+
693
754
  else:
694
- # # ソルブする
755
+ # ソルブする
695
756
  self._call_femtet_api(
696
757
  self.Femtet.Solve,
697
758
  False,
@@ -0,0 +1,138 @@
1
+ import re
2
+ from pathlib import Path
3
+
4
+ from pyfemtet.opt.interface._base import FEMInterface
5
+ from pyfemtet.opt.interface._femtet import FemtetInterface
6
+ from pyfemtet.opt.interface._excel_interface import (
7
+ ExcelInterface, is_cell_value_empty, ParseAsObjective, ScapeGoatObjective,
8
+ ParseAsConstraint, search_c, search_r
9
+ )
10
+
11
+
12
+ PARAMETRIC_PREFIX = 'パラメトリック'
13
+
14
+
15
+ def get_number(name):
16
+ numbers = re.findall(r'\d+', name)
17
+ if len(numbers) == 0:
18
+ raise ValueError('パラメトリック結果出力の番号指定が検出できませんでした。')
19
+ else:
20
+ return int(numbers[0])
21
+
22
+
23
+
24
+ class FemtetWithExcelSettingsInterface(FemtetInterface, ExcelInterface, FEMInterface):
25
+
26
+ def __init__(
27
+ self,
28
+
29
+ # FemtetInterface arguments
30
+ femprj_path: str = None, model_name: str = None, connect_method: str = 'auto',
31
+ save_pdt: str = 'all', strictly_pid_specify: bool = True, allow_without_project: bool = False,
32
+ open_result_with_gui: bool = True,
33
+ parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
34
+
35
+ # ExcelInterface arguments
36
+ input_xlsm_path: str or Path = None, input_sheet_name: str = None, output_xlsm_path: str or Path = None,
37
+ output_sheet_name: str = None, constraint_xlsm_path: str or Path = None,
38
+ constraint_sheet_name: str = None, procedure_name: str = None, procedure_args: list or tuple = None,
39
+ procedure_timeout: float or None = None,
40
+ setup_xlsm_path: str or Path = None, setup_procedure_name: str = None,
41
+ setup_procedure_args: list or tuple = None, teardown_xlsm_path: str or Path = None,
42
+ teardown_procedure_name: str = None, teardown_procedure_args: list or tuple = None,
43
+ related_file_paths: list[str or Path] = None, visible: bool = False, display_alerts: bool = False,
44
+ terminate_excel_when_quit: bool = None, interactive: bool = True, use_named_range: bool = True,
45
+
46
+ ):
47
+ ExcelInterface.__init__(
48
+ self, input_xlsm_path, input_sheet_name, output_xlsm_path, output_sheet_name, constraint_xlsm_path,
49
+ constraint_sheet_name, procedure_name, procedure_args, connect_method, procedure_timeout,
50
+ setup_xlsm_path, setup_procedure_name, setup_procedure_args, teardown_xlsm_path,
51
+ teardown_procedure_name, teardown_procedure_args, related_file_paths, visible, display_alerts,
52
+ terminate_excel_when_quit, interactive, use_named_range)
53
+
54
+ FemtetInterface.__init__(
55
+ self,
56
+ femprj_path, model_name, connect_method, save_pdt, strictly_pid_specify, allow_without_project,
57
+ open_result_with_gui, parametric_output_indexes_use_as_objective
58
+ )
59
+
60
+
61
+ def load_objective(self, opt):
62
+ from pyfemtet.opt.optimizer import AbstractOptimizer
63
+ from pyfemtet.opt._femopt_core import Objective
64
+ opt: AbstractOptimizer
65
+
66
+ df = ParseAsObjective.parse(
67
+ self.output_xlsm_path,
68
+ self.output_sheet_name,
69
+ False
70
+ )
71
+
72
+ for i, row in df.iterrows():
73
+
74
+ # use(optional)
75
+ use = True
76
+ if ParseAsObjective.use in df.columns:
77
+ _use = row[ParseAsObjective.use]
78
+ use = False if is_cell_value_empty(_use) else bool(_use) # bool or NaN
79
+
80
+ # name
81
+ name = str(row[ParseAsObjective.name])
82
+
83
+ # direction
84
+ direction = row[ParseAsObjective.direction]
85
+ assert not is_cell_value_empty(direction), 'direction is empty.'
86
+ try:
87
+ direction = float(direction)
88
+ except ValueError:
89
+ direction = str(direction).lower()
90
+ assert direction in ['minimize', 'maximize']
91
+
92
+ if use:
93
+
94
+ # name が「パラメトリック」から始まっていたら
95
+ # パラメトリック解析の結果を目的関数にする
96
+ if name.startswith(PARAMETRIC_PREFIX):
97
+ number = get_number(name)
98
+ self.use_parametric_output_as_objective(number, direction)
99
+
100
+ # そうでなければ通常の Excel objective を作る
101
+ else:
102
+ opt.objectives[name] = Objective(
103
+ fun=ScapeGoatObjective(),
104
+ name=name,
105
+ direction=direction,
106
+ args=(name,),
107
+ kwargs=dict(),
108
+ )
109
+
110
+ def _setup_before_parallel(self, client):
111
+ FemtetInterface._setup_before_parallel(self, client)
112
+ ExcelInterface._setup_before_parallel(self, client)
113
+
114
+ def _setup_after_parallel(self, *args, **kwargs):
115
+ FemtetInterface._setup_after_parallel(self, *args, **kwargs)
116
+ ExcelInterface._setup_after_parallel(self, *args, **kwargs)
117
+
118
+ def update(self, parameters) -> None:
119
+ FemtetInterface.update(self, parameters)
120
+ ExcelInterface.update(self, parameters)
121
+
122
+ def quit(self, timeout=1, force=True):
123
+ FemtetInterface.quit(self, timeout, force)
124
+ ExcelInterface.quit(self)
125
+
126
+ # noinspection PyMethodOverriding
127
+ def objective_from_excel(self, _, name: str):
128
+ r = 1 + search_r(self.output_xlsm_path, self.output_sheet_name, name)
129
+ c = 1 + search_c(self.output_xlsm_path, self.output_sheet_name, ParseAsObjective.value)
130
+ v = self.sh_output.Cells(r, c).value
131
+ return float(v)
132
+
133
+ # noinspection PyMethodOverriding
134
+ def constraint_from_excel(self, _, name: str):
135
+ r = 1 + search_r(self.constraint_xlsm_path, self.constraint_sheet_name, name)
136
+ c = 1 + search_c(self.constraint_xlsm_path, self.constraint_sheet_name, ParseAsConstraint.value)
137
+ v = self.sh_constraint.Cells(r, c).value
138
+ return float(v)
@@ -17,6 +17,7 @@ class SurrogateModelInterfaceBase(FEMInterface, ABC):
17
17
  self,
18
18
  history_path: str = None,
19
19
  train_history: History = None,
20
+ _output_directions: dict[int, str | float] | list[str | float] = None,
20
21
  ):
21
22
 
22
23
  self.train_history: History
@@ -25,6 +26,7 @@ class SurrogateModelInterfaceBase(FEMInterface, ABC):
25
26
  self.obj: dict[str, float] = dict()
26
27
  self.df_prm: pd.DataFrame
27
28
  self.df_obj: pd.DataFrame
29
+ self._output_directions = _output_directions
28
30
 
29
31
  # history_path が与えられた場合、train_history をコンストラクトする
30
32
  if history_path is not None:
@@ -54,11 +56,52 @@ class SurrogateModelInterfaceBase(FEMInterface, ABC):
54
56
  self.df_prm = df_prm
55
57
  self.df_obj = df_obj
56
58
 
59
+ # _output_directions が与えられている場合、
60
+ # history から objective の設定を読み込む
61
+ if self._output_directions is not None:
62
+ self._load_problem_from_me: bool = True
63
+
57
64
  FEMInterface.__init__(
58
65
  self,
59
66
  train_history=train_history, # コンストラクト済み train_history を渡せば並列計算時も何もしなくてよい
60
67
  )
61
68
 
69
+
70
+ def load_objective(self, opt) -> None:
71
+ from pyfemtet.opt._femopt_core import Objective
72
+
73
+ assert self._output_directions is not None
74
+
75
+ if isinstance(self._output_directions, dict):
76
+
77
+ for index, direction in self._output_directions:
78
+ obj_name = self.train_history.obj_names[index]
79
+ opt.objectives[obj_name] = Objective(
80
+ lambda obj_name_=obj_name: self.obj[obj_name_],
81
+ name=obj_name,
82
+ direction=direction,
83
+ args=(),
84
+ kwargs={},
85
+ )
86
+
87
+ elif isinstance(self._output_directions, list) \
88
+ or isinstance(self._output_directions, tuple):
89
+
90
+ obj_names = self.train_history.obj_names
91
+ assert len(self._output_directions) == len(obj_names)
92
+
93
+ for obj_name, direction in zip(obj_names, self._output_directions):
94
+ opt.objectives[obj_name] = Objective(
95
+ lambda obj_name_=obj_name: self.obj[obj_name_],
96
+ name=obj_name,
97
+ direction=direction,
98
+ args=(),
99
+ kwargs={},
100
+ )
101
+
102
+ else:
103
+ raise ValueError('Invalid _output_directions')
104
+
62
105
  def filter_feasible(self, x: np.ndarray, y: np.ndarray, return_feasibility=False):
63
106
  feasible_idx = np.where(~np.isnan(y.sum(axis=1)))
64
107
  if return_feasibility: