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.

Files changed (46) 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 +112 -90
  4. pyfemtet/_message/locales/messages.pot +105 -89
  5. pyfemtet/_message/messages.py +6 -2
  6. pyfemtet/_util/dask_util.py +10 -0
  7. pyfemtet/_util/excel_macro_util.py +16 -4
  8. pyfemtet/_util/excel_parse_util.py +138 -0
  9. pyfemtet/_util/sample.xlsx +0 -0
  10. pyfemtet/brep/__init__.py +0 -3
  11. pyfemtet/brep/_impl.py +7 -3
  12. pyfemtet/opt/_femopt.py +69 -31
  13. pyfemtet/opt/_femopt_core.py +100 -36
  14. pyfemtet/opt/advanced_samples/excel_ui/(ref) original_project.femprj +0 -0
  15. pyfemtet/opt/advanced_samples/excel_ui/femtet-macro.xlsm +0 -0
  16. pyfemtet/opt/advanced_samples/excel_ui/pyfemtet-core.py +291 -0
  17. pyfemtet/opt/advanced_samples/excel_ui/test-pyfemtet-core.cmd +22 -0
  18. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data.py +60 -0
  19. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data_jp.py +57 -0
  20. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate.py +100 -0
  21. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate_jp.py +90 -0
  22. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_parametric.femprj +0 -0
  23. pyfemtet/opt/interface/__init__.py +2 -0
  24. pyfemtet/opt/interface/_base.py +3 -0
  25. pyfemtet/opt/interface/_excel_interface.py +565 -204
  26. pyfemtet/opt/interface/_femtet.py +26 -29
  27. pyfemtet/opt/interface/_surrogate/__init__.py +5 -0
  28. pyfemtet/opt/interface/_surrogate/_base.py +85 -0
  29. pyfemtet/opt/interface/_surrogate/_chaospy.py +71 -0
  30. pyfemtet/opt/interface/_surrogate/_singletaskgp.py +70 -0
  31. pyfemtet/opt/optimizer/_base.py +30 -19
  32. pyfemtet/opt/optimizer/_optuna/_optuna.py +20 -8
  33. pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +60 -18
  34. pyfemtet/opt/prediction/_base.py +8 -0
  35. pyfemtet/opt/prediction/single_task_gp.py +85 -62
  36. pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +5 -5
  37. pyfemtet/opt/visualization/_complex_components/main_graph.py +7 -1
  38. pyfemtet/opt/visualization/_complex_components/pm_graph.py +1 -1
  39. pyfemtet/opt/visualization/_process_monitor/application.py +2 -2
  40. pyfemtet/opt/visualization/_process_monitor/pages.py +1 -1
  41. pyfemtet/opt/visualization/result_viewer/pages.py +1 -1
  42. {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/METADATA +3 -2
  43. {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/RECORD +46 -29
  44. {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/WHEEL +1 -1
  45. {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/LICENSE +0 -0
  46. {pyfemtet-0.7.0.dist-info → pyfemtet-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -49,6 +49,10 @@ def _post_activate_message(hwnd):
49
49
  win32gui.PostMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
50
50
 
51
51
 
52
+ class FailedToPostProcess(Exception):
53
+ pass
54
+
55
+
52
56
  class FemtetInterface(FEMInterface):
53
57
  """Control Femtet from optimizer.
54
58
 
@@ -86,10 +90,6 @@ class FemtetInterface(FEMInterface):
86
90
  it will be None and no parametric outputs are used
87
91
  as objectives.
88
92
 
89
- confirm_before_exit (bool):
90
- Whether to confirm before (abnormal) termination.
91
- Default is True.
92
-
93
93
  **kwargs: Additional arguments from inherited classes.
94
94
 
95
95
  Warning:
@@ -115,7 +115,6 @@ class FemtetInterface(FEMInterface):
115
115
  allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
116
116
  open_result_with_gui: bool = True,
117
117
  parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
118
- confirm_before_exit: bool = True,
119
118
  **kwargs # 継承されたクラスからの引数
120
119
  ):
121
120
 
@@ -145,7 +144,6 @@ class FemtetInterface(FEMInterface):
145
144
  self.parametric_output_indexes_use_as_objective = parametric_output_indexes_use_as_objective
146
145
  self._original_autosave_enabled = _get_autosave_enabled()
147
146
  _set_autosave_enabled(False)
148
- self.confirm_before_exit = confirm_before_exit
149
147
 
150
148
  # dask サブプロセスのときは femprj を更新し connect_method を new にする
151
149
  try:
@@ -183,14 +181,11 @@ class FemtetInterface(FEMInterface):
183
181
  open_result_with_gui=self.open_result_with_gui,
184
182
  parametric_output_indexes_use_as_objective=self.parametric_output_indexes_use_as_objective,
185
183
  save_pdt=self.save_pdt,
186
- confirm_before_exit=self.confirm_before_exit,
187
184
  **kwargs
188
185
  )
189
186
 
190
187
  def __del__(self):
191
- _set_autosave_enabled(self._original_autosave_enabled)
192
- if self.quit_when_destruct:
193
- self.quit()
188
+ self.quit()
194
189
  # CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
195
190
 
196
191
  def _connect_new_femtet(self):
@@ -253,11 +248,9 @@ class FemtetInterface(FEMInterface):
253
248
  cmd = f'{sys.executable} -m win32com.client.makepy FemtetMacro'
254
249
  os.system(cmd)
255
250
  message = Msg.ERR_NO_MAKEPY
256
- print('================')
257
- print(message)
258
- print('================')
259
- if self.confirm_before_exit:
260
- input('Press enter to finish...')
251
+ logger.error('================')
252
+ logger.error(message)
253
+ logger.error('================')
261
254
  raise RuntimeError(message)
262
255
 
263
256
  if self.Femtet is None:
@@ -541,24 +534,20 @@ class FemtetInterface(FEMInterface):
541
534
  try:
542
535
  variable_names = self.Femtet.GetVariableNames_py()
543
536
  except AttributeError as e:
544
- print('================')
537
+ logger.error('================')
545
538
  logger.error(Msg.ERR_CANNOT_ACCESS_API + 'GetVariableNames_py')
546
539
  logger.error(Msg.CERTIFY_MACRO_VERSION)
547
- print('================')
548
- if self.confirm_before_exit:
549
- input(Msg.ENTER_TO_QUIT)
540
+ logger.error('================')
550
541
  raise e
551
542
 
552
543
  if variable_names is not None:
553
544
  if param_name in variable_names:
554
545
  return self.Femtet.GetVariableValue(param_name)
555
546
  message = Msg.ERR_NO_SUCH_PARAMETER_IN_FEMTET
556
- print('================')
547
+ logger.error('================')
557
548
  logger.error(message)
558
549
  logger.error(f'`{param_name}` not in {variable_names}')
559
- print('================')
560
- if self.confirm_before_exit:
561
- input(Msg.ENTER_TO_QUIT)
550
+ logger.error('================')
562
551
  raise RuntimeError(message)
563
552
  else:
564
553
  return None
@@ -739,7 +728,8 @@ class FemtetInterface(FEMInterface):
739
728
 
740
729
  _set_autosave_enabled(self._original_autosave_enabled)
741
730
 
742
- _exit_or_force_terminate(timeout=timeout, Femtet=self.Femtet, force=True)
731
+ if self.quit_when_destruct:
732
+ _exit_or_force_terminate(timeout=timeout, Femtet=self.Femtet, force=True)
743
733
 
744
734
  def _setup_before_parallel(self, client):
745
735
  client.upload_file(
@@ -751,8 +741,15 @@ class FemtetInterface(FEMInterface):
751
741
  return _version(Femtet=self.Femtet)
752
742
 
753
743
  def _create_postprocess_args(self):
754
- file_content = self._create_result_file_content()
755
- jpg_content = self._create_jpg_content()
744
+ try:
745
+ file_content = self._create_result_file_content()
746
+ except FailedToPostProcess:
747
+ file_content = None
748
+
749
+ try:
750
+ jpg_content = self._create_jpg_content()
751
+ except FailedToPostProcess:
752
+ jpg_content = None
756
753
 
757
754
  out = dict(
758
755
  original_femprj_path=self.original_femprj_path,
@@ -800,7 +797,7 @@ class FemtetInterface(FEMInterface):
800
797
  fun=self.Femtet.SavePDT,
801
798
  args=(pdt_path, True),
802
799
  return_value_if_failed=False,
803
- if_error=SolveError,
800
+ if_error=FailedToPostProcess,
804
801
  error_message=Msg.ERR_FAILED_TO_SAVE_PDT,
805
802
  is_Gaudi_method=False,
806
803
  )
@@ -828,10 +825,10 @@ class FemtetInterface(FEMInterface):
828
825
  self.Femtet.RedrawMode = True # 逐一の描画をオン
829
826
 
830
827
  if not succeed:
831
- raise Exception(Msg.ERR_FAILED_TO_SAVE_JPG)
828
+ raise FailedToPostProcess(Msg.ERR_FAILED_TO_SAVE_JPG)
832
829
 
833
830
  if not os.path.exists(jpg_path):
834
- raise Exception(Msg.ERR_JPG_NOT_FOUND)
831
+ raise FailedToPostProcess(Msg.ERR_JPG_NOT_FOUND)
835
832
 
836
833
  with open(jpg_path, 'rb') as f:
837
834
  content = f.read()
@@ -0,0 +1,5 @@
1
+ from pyfemtet.opt.interface._surrogate._singletaskgp import PoFBoTorchInterface
2
+
3
+ __all__ = [
4
+ PoFBoTorchInterface,
5
+ ]
@@ -0,0 +1,85 @@
1
+ from typing import Optional, List, Any
2
+ from abc import ABC
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+
7
+ from pyfemtet.logger import get_module_logger
8
+ from pyfemtet.opt._femopt_core import History, Objective
9
+ from pyfemtet.opt.interface._base import FEMInterface
10
+ from pyfemtet.opt.optimizer._base import AbstractOptimizer
11
+
12
+ logger = get_module_logger('opt.interface', __name__)
13
+
14
+
15
+ class SurrogateModelInterfaceBase(FEMInterface, ABC):
16
+ def __init__(
17
+ self,
18
+ history_path: str = None,
19
+ history: History = None,
20
+ ):
21
+
22
+ self.history: History
23
+ self.model: Any
24
+ self.prm: dict[str, float] = dict()
25
+ self.obj: dict[str, float] = dict()
26
+ self.df_prm: pd.DataFrame
27
+ self.df_obj: pd.DataFrame
28
+
29
+ # history_path が与えられた場合、history をコンストラクトする
30
+ if history_path is not None:
31
+ history = History(history_path=history_path)
32
+
33
+ # history が与えられるかコンストラクトされている場合
34
+ if history is not None:
35
+ # 学習データを準備する
36
+ df_prm = history.get_df()[history.prm_names]
37
+ df_obj = history.get_df()[history.obj_names]
38
+
39
+ # obj の名前を作る
40
+ for obj_name in history.obj_names:
41
+ self.obj[obj_name] = np.nan
42
+
43
+ # prm の名前を作る
44
+ for prm_name in history.prm_names:
45
+ self.prm[prm_name] = np.nan
46
+
47
+ self.history = history
48
+
49
+ # history から作らない場合、引数チェック
50
+ else:
51
+ # assert len(train_x) == len(train_y)
52
+ raise NotImplementedError
53
+
54
+ self.df_prm = df_prm
55
+ self.df_obj = df_obj
56
+
57
+ FEMInterface.__init__(
58
+ self,
59
+ history=history, # コンストラクト済み history を渡せば並列計算時も何もしなくてよい
60
+ )
61
+
62
+ def filter_feasible(self, x: np.ndarray, y: np.ndarray, return_feasibility=False):
63
+ feasible_idx = np.where(~np.isnan(y.sum(axis=1)))
64
+ if return_feasibility:
65
+ # calculated or not
66
+ y = np.zeros_like(y)
67
+ y[feasible_idx] = 1.
68
+ # satisfy weak feasibility or not
69
+ infeasible_idx = np.where(~self.history.get_df()['feasible'].values)
70
+ y[infeasible_idx] = .0
71
+ return x, y.reshape((-1, 1))
72
+ else:
73
+ return x[feasible_idx], y[feasible_idx]
74
+
75
+ def _setup_after_parallel(self, *args, **kwargs):
76
+
77
+ opt: AbstractOptimizer = kwargs['opt']
78
+ obj: Objective
79
+ for obj_name, obj in opt.objectives.items():
80
+ obj.fun = lambda: self.obj[obj_name]
81
+
82
+ def update_parameter(self, parameters: pd.DataFrame, with_warning=False) -> Optional[List[str]]:
83
+ for i, row in parameters.iterrows():
84
+ name, value = row['name'], row['value']
85
+ self.prm[name] = value
@@ -0,0 +1,71 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ from pyfemtet.logger import get_module_logger
5
+ from pyfemtet.opt.interface._surrogate._base import SurrogateModelInterfaceBase
6
+ from pyfemtet.opt.prediction.polynomial_expansion import PolynomialExpansionModel
7
+
8
+
9
+ logger = get_module_logger('opt.interface', __name__)
10
+
11
+
12
+ class PolynomialChaosInterface(SurrogateModelInterfaceBase):
13
+
14
+ model: PolynomialExpansionModel # surrogate_func_expansion
15
+
16
+ def train(self):
17
+ x, y = self.filter_feasible(self.df_prm.values, self.df_obj.values)
18
+ assert len(x) > 0, 'No feasible results in training data.'
19
+ self.model.fit(x, y)
20
+
21
+ def _setup_after_parallel(self, *args, **kwargs):
22
+
23
+ # # update objectives
24
+ # SurrogateModelInterfaceBase._setup_after_parallel(
25
+ # self, *args, **kwargs
26
+ # )
27
+
28
+ # train model
29
+ self.model = PolynomialExpansionModel()
30
+ self.model.set_bounds_from_history(self.history)
31
+ self.train()
32
+
33
+ def update(self, parameters: pd.DataFrame) -> None:
34
+ # self.prm 更新
35
+ SurrogateModelInterfaceBase.update_parameter(
36
+ self, parameters
37
+ )
38
+
39
+ # history.prm_name 順に並べ替え
40
+ x = np.array([self.prm[k] for k in self.history.prm_names])
41
+
42
+ # prediction
43
+ dist_mean, _ = self.model.predict(x)
44
+
45
+ # 目的関数の更新
46
+ self.obj = {obj_name: value for obj_name, value in zip(self.history.obj_names, dist_mean)}
47
+
48
+
49
+ if __name__ == '__main__':
50
+ import os
51
+ from pyfemtet.opt._femopt_core import History
52
+
53
+ os.chdir(os.path.dirname(__file__))
54
+
55
+ history = History(history_path='sample.csv')
56
+ fem = PolynomialChaosInterface(history=history)
57
+
58
+ import pandas as pd
59
+ df = history.get_df()
60
+ parameters = pd.DataFrame(
61
+ dict(
62
+ name=history.prm_names,
63
+ value=[1. for _ in history.prm_names]
64
+ )
65
+ )
66
+
67
+ fem._setup_after_parallel()
68
+
69
+ fem.update(
70
+ parameters
71
+ )
@@ -0,0 +1,70 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from scipy.stats.distributions import norm
4
+
5
+ from pyfemtet.core import SolveError
6
+ from pyfemtet.logger import get_module_logger
7
+ from pyfemtet.opt.interface._surrogate._base import SurrogateModelInterfaceBase
8
+ from pyfemtet.opt.prediction.single_task_gp import SingleTaskGPModel
9
+
10
+ from pyfemtet._message.messages import Message as Msg
11
+
12
+
13
+ logger = get_module_logger('opt.interface', __name__)
14
+
15
+
16
+ class PoFBoTorchInterface(SurrogateModelInterfaceBase):
17
+ model_f: SingleTaskGPModel
18
+ model: SingleTaskGPModel
19
+ threshold = 0.5
20
+
21
+ def train(self):
22
+ # df そのまま用いて training する
23
+ x, y = self.filter_feasible(self.df_prm.values, self.df_obj.values)
24
+ assert len(x) > 0, 'No feasible results in training data.'
25
+ self.model.fit(x, y)
26
+
27
+ def train_f(self):
28
+ # df そのまま用いて training する
29
+ x, y = self.filter_feasible(self.df_prm.values, self.df_obj.values, return_feasibility=True)
30
+ if y.min() == 1:
31
+ self.model_f.predict = lambda *args, **kwargs: (1., 0.001)
32
+ self.model_f.fit(x, y)
33
+
34
+ def _setup_after_parallel(self, *args, **kwargs):
35
+
36
+ # update objectives
37
+ SurrogateModelInterfaceBase._setup_after_parallel(
38
+ self, *args, **kwargs
39
+ )
40
+
41
+ # model training
42
+ self.model = SingleTaskGPModel()
43
+ self.model.set_bounds_from_history(self.history)
44
+ self.train()
45
+
46
+ # model_f training
47
+ self.model_f = SingleTaskGPModel(is_noise_free=False)
48
+ self.model_f.set_bounds_from_history(self.history)
49
+ self.train_f()
50
+
51
+ def update(self, parameters: pd.DataFrame) -> None:
52
+ # self.prm 更新
53
+ SurrogateModelInterfaceBase.update_parameter(
54
+ self, parameters
55
+ )
56
+
57
+ # history.prm_name 順に並べ替え
58
+ x = np.array([self.prm[k] for k in self.history.prm_names])
59
+
60
+ # feasibility の計算
61
+ mean_f, std_f = self.model_f.predict(np.array([x]))
62
+ pof = 1. - norm.cdf(self.threshold, loc=mean_f, scale=std_f)
63
+ if pof < self.threshold:
64
+ raise SolveError(Msg.INFO_POF_IS_LESS_THAN_THRESHOLD)
65
+
66
+ # 実際の計算(mean は history.obj_names 順)
67
+ mean, _ = self.model.predict(np.array([x]))
68
+
69
+ # 目的関数の更新
70
+ self.obj = {obj_name: value for obj_name, value in zip(self.history.obj_names, mean)}
@@ -155,12 +155,18 @@ class AbstractOptimizer(ABC):
155
155
  """Setup before parallel processes are launched."""
156
156
  pass
157
157
 
158
+ def generate_infeasible_result(self):
159
+ y = np.full_like(np.zeros(len(self.objectives)), np.nan)
160
+ c = np.full_like(np.zeros(len(self.constraints)), np.nan)
161
+ return y, y, c
162
+
158
163
  # ===== calc =====
159
- def f(self, x: np.ndarray) -> list[np.ndarray]:
164
+ def f(self, x: np.ndarray, _record_infeasible=False) -> list[np.ndarray]:
160
165
  """Calculate objectives and constraints.
161
166
 
162
167
  Args:
163
168
  x (np.ndarray): Optimization parameters.
169
+ _record_infeasible (bool): If True, skip fem.update() and record self.generate_invalid_results().
164
170
 
165
171
  Returns:
166
172
  list[np.ndarray]:
@@ -178,27 +184,31 @@ class AbstractOptimizer(ABC):
178
184
  self.set_parameter_values(x)
179
185
  logger.info(f'input: {x}')
180
186
 
181
- # FEM の更新
182
- try:
183
- logger.info(f'Solving FEM...')
184
- df_to_fem = self.variables.get_variables(
185
- format='df',
186
- filter_pass_to_fem=True
187
- )
188
- self.fem.update(df_to_fem)
187
+ if not _record_infeasible:
188
+ # FEM の更新
189
+ try:
190
+ logger.info(f'Solving FEM...')
191
+ df_to_fem = self.variables.get_variables(
192
+ format='df',
193
+ filter_pass_to_fem=True
194
+ )
195
+ self.fem.update(df_to_fem)
189
196
 
190
- except Exception as e:
191
- logger.info(f'{type(e).__name__} : {e}')
192
- logger.info(Msg.INFO_EXCEPTION_DURING_FEM_ANALYSIS)
193
- logger.info(x)
194
- raise e # may be just a ModelError, etc. Handling them in Concrete classes.
197
+ except Exception as e:
198
+ logger.info(f'{type(e).__name__} : {e}')
199
+ logger.info(Msg.INFO_EXCEPTION_DURING_FEM_ANALYSIS)
200
+ logger.info(x)
201
+ raise e # may be just a ModelError, etc. Handling them in Concrete classes.
195
202
 
196
- # y, _y, c の更新
197
- y = [obj.calc(self.fem) for obj in self.objectives.values()]
203
+ # y, _y, c の更新
204
+ y = [obj.calc(self.fem) for obj in self.objectives.values()]
198
205
 
199
- _y = [obj.convert(value) for obj, value in zip(self.objectives.values(), y)]
206
+ _y = [obj.convert(value) for obj, value in zip(self.objectives.values(), y)]
200
207
 
201
- c = [cns.calc(self.fem) for cns in self.constraints.values()]
208
+ c = [cns.calc(self.fem) for cns in self.constraints.values()]
209
+
210
+ else:
211
+ y, _y, c = self.generate_infeasible_result()
202
212
 
203
213
  # register to history
204
214
  df_to_opt = self.variables.get_variables(
@@ -302,6 +312,7 @@ class AbstractOptimizer(ABC):
302
312
  worker_status_list, # 他の worker の status オブジェクト
303
313
  wait_setup, # 他の worker の status が ready になるまで待つか
304
314
  skip_reconstruct=False, # reconstruct fem を行うかどうか
315
+ space_dir=None, # 特定の space_dir を使うかどうか
305
316
  ) -> Optional[Exception]:
306
317
 
307
318
  # 自分の worker_status の取得
@@ -314,7 +325,7 @@ class AbstractOptimizer(ABC):
314
325
 
315
326
  # set_fem をはじめ、終了したらそれを示す
316
327
  self._reconstruct_fem(skip_reconstruct)
317
- self.fem._setup_after_parallel(opt=self)
328
+ self.fem._setup_after_parallel(opt=self, space_dir=space_dir)
318
329
  self.worker_status.set(OptimizationStatus.WAIT_OTHER_WORKERS)
319
330
 
320
331
  # wait_setup or not
@@ -4,6 +4,7 @@ from typing import Iterable
4
4
  # built-in
5
5
  import os
6
6
  import inspect
7
+ import gc
7
8
 
8
9
  # 3rd-party
9
10
  import optuna
@@ -92,10 +93,7 @@ class OptunaOptimizer(AbstractOptimizer):
92
93
  def _objective(self, trial):
93
94
 
94
95
  logger.info('')
95
- if self._retry_counter == 0:
96
- logger.info(f'===== trial {1 + len(self.history.get_df())} start =====')
97
- else:
98
- logger.info(f'===== trial {1 + len(self.history.get_df())} (retry {self._retry_counter}) start =====')
96
+ logger.info(f'===== trial {1 + len(self.history.get_df())} ({len(self.history.get_df(valid_only=True))} succeeded trials) start =====')
99
97
 
100
98
  # 中断の確認 (FAIL loop に陥る対策)
101
99
  if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
@@ -124,6 +122,7 @@ class OptunaOptimizer(AbstractOptimizer):
124
122
  # fem 経由で変数を取得して constraint を計算する時のためにアップデート
125
123
  df_fem = self.variables.get_variables(format='df', filter_pass_to_fem=True)
126
124
  self.fem.update_parameter(df_fem)
125
+ x = self.variables.get_variables(format='values', filter_parameter=True)
127
126
 
128
127
  # strict 拘束
129
128
  strict_constraints = [cns for cns in self.constraints.values() if cns.strict]
@@ -140,10 +139,11 @@ class OptunaOptimizer(AbstractOptimizer):
140
139
  logger.info(f'Constraint: {cns.name}')
141
140
  logger.info(self.variables.get_variables('dict', filter_parameter=True))
142
141
  self._retry_counter += 1
142
+ self.message = f'Failed to calculate objectives because of the constraint violation: {cns.name}'
143
+ self.f(x, _record_infeasible=True)
143
144
  raise optuna.TrialPruned() # set TrialState PRUNED because FAIL causes similar candidate loop.
144
145
 
145
146
  # 計算
146
- x = self.variables.get_variables(format='values', filter_parameter=True)
147
147
  try:
148
148
  _, _y, c = self.f(x) # f の中で info は出している
149
149
  except (ModelError, MeshError, SolveError) as e:
@@ -162,6 +162,8 @@ class OptunaOptimizer(AbstractOptimizer):
162
162
  'or analysis.')
163
163
 
164
164
  self._retry_counter += 1
165
+ self.message = f'Failed to calculate objectives because of the parameter broke the FEM model.'
166
+ self.f(x, _record_infeasible=True)
165
167
  raise optuna.TrialPruned() # set TrialState PRUNED because FAIL causes similar candidate loop.
166
168
 
167
169
  # 拘束 attr の更新
@@ -284,8 +286,19 @@ class OptunaOptimizer(AbstractOptimizer):
284
286
  from optuna.samplers import TPESampler
285
287
  if issubclass(self.sampler_class, TPESampler):
286
288
 
287
- if os.path.exists(self._temporary_storage_path):
288
- os.remove(self._temporary_storage_path)
289
+ import re
290
+ pattern = r'_\d+$'
291
+
292
+ while os.path.exists(self._temporary_storage_path):
293
+ name, ext = os.path.splitext(self._temporary_storage_path)
294
+
295
+ if bool(re.search(pattern, name)):
296
+ base = '_'.join(name.split('_')[:-1])
297
+ n = int(name.split('_')[-1])
298
+ self._temporary_storage_path = name + '_' + str(n+1) + ext
299
+
300
+ else:
301
+ self._temporary_storage_path = name + '_2' + ext
289
302
 
290
303
  self._temporary_storage = optuna.integration.dask.DaskStorage(
291
304
  f'sqlite:///{self._temporary_storage_path}',
@@ -403,7 +416,6 @@ class OptunaOptimizer(AbstractOptimizer):
403
416
  self._temporary_storage.remove_session()
404
417
  del self._temporary_storage
405
418
  del _study
406
- import gc
407
419
  gc.collect()
408
420
  if os.path.exists(self._temporary_storage_path):
409
421
  os.remove(self._temporary_storage_path)