pyfemtet 0.8.7__py3-none-any.whl → 0.8.9__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.8.7"
1
+ __version__ = "0.8.9"
pyfemtet/opt/_femopt.py CHANGED
@@ -131,7 +131,7 @@ class FEMOpt:
131
131
  # メンバーの宣言
132
132
  self.client = None
133
133
  self.status = None # actor
134
- self.history = None # actor
134
+ self.history: History = None
135
135
  self.worker_status_list = None # [actor]
136
136
  self.monitor_process_future = None
137
137
  self.monitor_server_kwargs = dict()
@@ -553,7 +553,7 @@ class History:
553
553
  hv_reference=None,
554
554
  ):
555
555
  # hypervolume 計算メソッド
556
- self._hv_reference = hv_reference or 'dynamic-pareto'
556
+ self._hv_reference = 'dynamic-pareto' if hv_reference is None else hv_reference
557
557
 
558
558
  # 引数の処理
559
559
  self.path = history_path # .csv
@@ -728,6 +728,14 @@ class History:
728
728
 
729
729
  return columns, meta_columns
730
730
 
731
+ def generate_hidden_infeasible_result(self):
732
+ y = np.full_like(np.zeros(len(self.obj_names)), np.nan)
733
+ c = np.full_like(np.zeros(len(self.cns_names)), np.nan)
734
+ return y, c
735
+
736
+ def is_hidden_infeasible_result(self, y):
737
+ return np.all(np.isnan(y))
738
+
731
739
  def record(
732
740
  self,
733
741
  parameters,
@@ -780,7 +788,7 @@ class History:
780
788
  feasible_list.append(is_feasible(cns_value, cns.lb, cns.ub))
781
789
 
782
790
  # feasibility
783
- row.append(all(feasible_list))
791
+ row.append(all(feasible_list) and not self.is_hidden_infeasible_result(obj_values))
784
792
 
785
793
  # the others
786
794
  row.append(-1.) # dummy hypervolume
@@ -892,7 +900,29 @@ class History:
892
900
  ret.append(row)
893
901
  return np.array(ret).max(axis=0)
894
902
 
895
- if self._hv_reference == 'dynamic-pareto':
903
+ if (
904
+ isinstance(self._hv_reference, np.ndarray)
905
+ or isinstance(self._hv_reference, list)
906
+ ):
907
+ _buff = np.array(self._hv_reference)
908
+ assert _buff.shape == (len(self.obj_names),)
909
+
910
+ ref_point = np.array(
911
+ [obj.convert(raw_value) for obj, raw_value in zip(objectives.values(), _buff)]
912
+ )
913
+
914
+ _buff = get_pareto(objective_values)
915
+
916
+ pareto_set = np.empty((0, len(objectives)))
917
+ for pareto_sol in _buff:
918
+ if all(pareto_sol < ref_point):
919
+ pareto_set = np.concatenate([pareto_set, [pareto_sol]], axis=0)
920
+
921
+ hv = compute_hypervolume(pareto_set, ref_point)
922
+ df.loc[len(df) - 1, 'hypervolume'] = hv
923
+ return
924
+
925
+ elif self._hv_reference == 'dynamic-pareto':
896
926
  pareto_set, pareto_set_list = get_pareto(objective_values, with_partial=True)
897
927
  for i, partial_pareto_set in enumerate(pareto_set_list):
898
928
  # 並列計算時など Valid な解がまだ一つもない場合は pareto_set が長さ 0 になる
@@ -943,28 +973,6 @@ class History:
943
973
  df.loc[len(df) - 1, 'hypervolume'] = hv
944
974
  return
945
975
 
946
- elif (
947
- isinstance(self._hv_reference, np.ndarray)
948
- or isinstance(self._hv_reference, list)
949
- ):
950
- _buff = np.array(self._hv_reference)
951
- assert _buff.shape == (len(self.obj_names),)
952
-
953
- ref_point = np.array(
954
- [obj.convert(raw_value) for obj, raw_value in zip(objectives.values(), _buff)]
955
- )
956
-
957
- _buff = get_pareto(objective_values)
958
-
959
- pareto_set = np.empty((0, len(objectives)))
960
- for pareto_sol in _buff:
961
- if all(pareto_sol < ref_point):
962
- pareto_set = np.concatenate([pareto_set, [pareto_sol]], axis=0)
963
-
964
- hv = compute_hypervolume(pareto_set, ref_point)
965
- df.loc[len(df) - 1, 'hypervolume'] = hv
966
- return
967
-
968
976
  else:
969
977
  raise NotImplementedError(f'Invalid Hypervolume reference point calculation method: {self._hv_reference}')
970
978
 
@@ -92,6 +92,7 @@ def is_equal_result(ref_path, dif_path, log_path=None, threashold=0.05):
92
92
  assert len(ref_columns) == len(dif_columns), "結果 csv の column 数が異なります。"
93
93
  assert len(ref_df) == len(dif_df), "結果 csv の row 数が異なります。"
94
94
  assert difference <= threashold*100, f"前回の結果との平均差異が {int(difference)}% で {int(threashold*100)}% を超えています。"
95
+ print(f"OK! 前回の結果との平均差異が {int(difference)}% で {int(threashold*100)}% 以下です。")
95
96
 
96
97
 
97
98
  def _get_simplified_df_values(csv_path, exclude_columns=None):
@@ -0,0 +1,99 @@
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
+ """Main function
20
+
21
+ length
22
+ using different algorithms for each restarting.
23
+
24
+ So this main function requires n_trials and sampler_class.
25
+
26
+ Args:
27
+
28
+ n_trials (int):
29
+ How many additional succeeded trials
30
+ to terminate optimization.
31
+
32
+ sampler_class (type[optuna.samplers.BaseSampler]):
33
+ The algorithm we use.
34
+
35
+ sampler_kwargs (dict):
36
+ The arguments of sampler.
37
+
38
+ """
39
+
40
+
41
+ # Connect to Femtet.
42
+ fem = FemtetInterface(
43
+ femprj_path='gal_ex13_parametric.femprj',
44
+ )
45
+
46
+ # Initialize the optimization object.
47
+ opt = OptunaOptimizer(
48
+ sampler_class=sampler_class,
49
+ sampler_kwargs=sampler_kwargs,
50
+ )
51
+
52
+ # To restart, it is necessary to inform the new optimization program
53
+ # about the history of the previous optimization.
54
+ # By specifying a csv for the `history_path` argument of FEMOpt,
55
+ # if it does not exist, a new csv file will be created,
56
+ # and if it exists, optimization will continue from that csv file.
57
+ #
58
+ # Note:
59
+ # When restarting, the number and names of variables,
60
+ # as well as the number and names of objective functions
61
+ # and constraints must be consistent.
62
+ # However, you can change the bounds of variables,
63
+ # direction of objective functions, and content of constraints.
64
+ #
65
+ # Note:
66
+ # When using OptunaOptimizer, the .db file with the same name
67
+ # (in this case restarting-sample.db) that is saved along with
68
+ # csv is required to be in the same folder as the csv file.
69
+ femopt = FEMOpt(
70
+ fem=fem,
71
+ opt=opt,
72
+ history_path='restarting-sample.csv'
73
+ )
74
+
75
+ # Set the design variables.
76
+ femopt.add_parameter('length', 0.1, 0.02, 0.2)
77
+ femopt.add_parameter('width', 0.01, 0.001, 0.02)
78
+ femopt.add_parameter('base_radius', 0.008, 0.006, 0.01)
79
+
80
+ # Set the objective function.
81
+ femopt.add_objective(fun=get_res_freq, name='First Resonant Frequency (Hz)', direction=800)
82
+
83
+ # Run optimization.
84
+ femopt.set_random_seed(42)
85
+ femopt.optimize(n_trials=n_trials, confirm_before_exit=False)
86
+
87
+
88
+ if __name__ == '__main__':
89
+ # First, we will perform 3 optimizations using the RandomSampler.
90
+ main(3, RandomSampler, {})
91
+
92
+ # Next, we will perform 3 optimizations using the NSGAIISampler.
93
+ main(3, NSGAIISampler, {})
94
+
95
+ # Finally, we will perform 3 optimizations using the GPSampler.
96
+ main(3, GPSampler, {'n_startup_trials': 0, 'deterministic_objective': True})
97
+
98
+ # After this program ends, you can continue further with
99
+ # restarting-sample.csv and the .db file.
@@ -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
+ # さらに続きの最適化を行うことができます。
@@ -201,6 +201,40 @@ class FemtetInterface(FEMInterface):
201
201
  **kwargs
202
202
  )
203
203
 
204
+ def use_parametric_output_as_objective(self, number: int, direction: str | float = 'minimize') -> None:
205
+ """Use output setting of Femtet parametric analysis as an objective function.
206
+
207
+ Args:
208
+ number (int): The index of output settings tab in parametric analysis dialog of Femtet. Starts at 1.
209
+ direction (str | float): Objective direction. Valid input is one of 'minimize', 'maximize' or a specific value. Defaults to 'minimize'.
210
+
211
+ Returns:
212
+ None
213
+
214
+ """
215
+ # check
216
+ if isinstance(direction, str):
217
+ if direction not in ('minimize', 'maximize'):
218
+ raise ValueError(f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}')
219
+ else:
220
+ try:
221
+ direction = float(direction)
222
+ except (TypeError, ValueError):
223
+ raise ValueError(f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}')
224
+
225
+ index = {number - 1: direction}
226
+
227
+ if self.parametric_output_indexes_use_as_objective is None:
228
+ self.parametric_output_indexes_use_as_objective = index
229
+
230
+ else:
231
+ self.parametric_output_indexes_use_as_objective.update(index)
232
+
233
+ # TODO: FEMInterface.__init__ の仕様を変えたらここも変える
234
+ self.kwargs['parametric_output_indexes_use_as_objective'] = self.parametric_output_indexes_use_as_objective
235
+
236
+
237
+
204
238
  def __del__(self):
205
239
  self.quit()
206
240
  # CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
@@ -682,6 +716,14 @@ class FemtetInterface(FEMInterface):
682
716
 
683
717
  if self.parametric_output_indexes_use_as_objective is not None:
684
718
  from pyfemtet.opt.interface._femtet_parametric import solve_via_parametric_dll
719
+
720
+ pdt_path = self.Femtet.ResultFilePath + '.pdt'
721
+
722
+ # 前のものが残っているとややこしいので消しておく
723
+ if os.path.exists(pdt_path):
724
+ os.remove(pdt_path)
725
+
726
+ # parametric analysis 経由で解析
685
727
  self._call_femtet_api(
686
728
  fun=solve_via_parametric_dll,
687
729
  return_value_if_failed=False,
@@ -690,8 +732,23 @@ class FemtetInterface(FEMInterface):
690
732
  is_Gaudi_method=True,
691
733
  args=(self.Femtet,),
692
734
  )
735
+
736
+ # parametric analysis の場合
737
+ # ダイアログで「解析結果を保存する」に
738
+ # チェックがついていないと次にすべき
739
+ # OpenCurrentResult に失敗するので
740
+ # parametric の場合も pdt を保存する
741
+ self._call_femtet_api(
742
+ fun=self.Femtet.SavePDT,
743
+ args=(pdt_path, True),
744
+ return_value_if_failed=False,
745
+ if_error=SolveError,
746
+ error_message=Msg.ERR_FAILED_TO_SAVE_PDT,
747
+ is_Gaudi_method=False,
748
+ )
749
+
693
750
  else:
694
- # # ソルブする
751
+ # ソルブする
695
752
  self._call_femtet_api(
696
753
  self.Femtet.Solve,
697
754
  False,
@@ -16,37 +16,35 @@ class SurrogateModelInterfaceBase(FEMInterface, ABC):
16
16
  def __init__(
17
17
  self,
18
18
  history_path: str = None,
19
- history: History = None,
20
- override_objective: bool = True,
19
+ train_history: History = None,
21
20
  ):
22
21
 
23
- self.history: History
22
+ self.train_history: History
24
23
  self.model: Any
25
24
  self.prm: dict[str, float] = dict()
26
25
  self.obj: dict[str, float] = dict()
27
26
  self.df_prm: pd.DataFrame
28
27
  self.df_obj: pd.DataFrame
29
- self.override_objective: bool = override_objective
30
28
 
31
- # history_path が与えられた場合、history をコンストラクトする
29
+ # history_path が与えられた場合、train_history をコンストラクトする
32
30
  if history_path is not None:
33
- history = History(history_path=history_path)
31
+ train_history = History(history_path=history_path)
34
32
 
35
- # history が与えられるかコンストラクトされている場合
36
- if history is not None:
33
+ # train_history が与えられるかコンストラクトされている場合
34
+ if train_history is not None:
37
35
  # 学習データを準備する
38
- df_prm = history.get_df()[history.prm_names]
39
- df_obj = history.get_df()[history.obj_names]
36
+ df_prm = train_history.get_df()[train_history.prm_names]
37
+ df_obj = train_history.get_df()[train_history.obj_names]
40
38
 
41
39
  # obj の名前を作る
42
- for obj_name in history.obj_names:
40
+ for obj_name in train_history.obj_names:
43
41
  self.obj[obj_name] = np.nan
44
42
 
45
43
  # prm の名前を作る
46
- for prm_name in history.prm_names:
44
+ for prm_name in train_history.prm_names:
47
45
  self.prm[prm_name] = np.nan
48
46
 
49
- self.history = history
47
+ self.train_history = train_history
50
48
 
51
49
  # history から作らない場合、引数チェック
52
50
  else:
@@ -58,8 +56,7 @@ class SurrogateModelInterfaceBase(FEMInterface, ABC):
58
56
 
59
57
  FEMInterface.__init__(
60
58
  self,
61
- history=history, # コンストラクト済み history を渡せば並列計算時も何もしなくてよい
62
- override_objective=self.override_objective
59
+ train_history=train_history, # コンストラクト済み train_history を渡せば並列計算時も何もしなくてよい
63
60
  )
64
61
 
65
62
  def filter_feasible(self, x: np.ndarray, y: np.ndarray, return_feasibility=False):
@@ -73,10 +70,14 @@ class SurrogateModelInterfaceBase(FEMInterface, ABC):
73
70
  return x[feasible_idx], y[feasible_idx]
74
71
 
75
72
  def _setup_after_parallel(self, *args, **kwargs):
76
- if self.override_objective:
77
- opt: AbstractOptimizer = kwargs['opt']
78
- obj: Objective
79
- for obj_name, obj in opt.objectives.items():
73
+ opt: AbstractOptimizer = kwargs['opt']
74
+ obj: Objective
75
+
76
+ # add_objective された目的のうち、
77
+ # training data に含まれる名前で
78
+ # あるものは fun を上書き
79
+ for obj_name, obj in opt.objectives.items():
80
+ if obj_name in self.train_history.obj_names:
80
81
  obj.fun = lambda obj_name_=obj_name: self.obj[obj_name_]
81
82
 
82
83
  def update_parameter(self, parameters: pd.DataFrame, with_warning=False) -> Optional[List[str]]:
@@ -27,7 +27,7 @@ class PolynomialChaosInterface(SurrogateModelInterfaceBase):
27
27
 
28
28
  # train model
29
29
  self.model = PolynomialExpansionModel()
30
- self.model.set_bounds_from_history(self.history)
30
+ self.model.set_bounds_from_history(self.train_history)
31
31
  self.train()
32
32
 
33
33
  def update(self, parameters: pd.DataFrame) -> None:
@@ -37,13 +37,13 @@ class PolynomialChaosInterface(SurrogateModelInterfaceBase):
37
37
  )
38
38
 
39
39
  # history.prm_name 順に並べ替え
40
- x = np.array([self.prm[k] for k in self.history.prm_names])
40
+ x = np.array([self.prm[k] for k in self.train_history.prm_names])
41
41
 
42
42
  # prediction
43
43
  dist_mean, _ = self.model.predict(x)
44
44
 
45
45
  # 目的関数の更新
46
- self.obj = {obj_name: value for obj_name, value in zip(self.history.obj_names, dist_mean)}
46
+ self.obj = {obj_name: value for obj_name, value in zip(self.train_history.obj_names, dist_mean)}
47
47
 
48
48
 
49
49
  if __name__ == '__main__':
@@ -40,12 +40,12 @@ class PoFBoTorchInterface(SurrogateModelInterfaceBase):
40
40
 
41
41
  # model training
42
42
  self.model = SingleTaskGPModel()
43
- self.model.set_bounds_from_history(self.history)
43
+ self.model.set_bounds_from_history(self.train_history)
44
44
  self.train()
45
45
 
46
46
  # model_f training
47
47
  self.model_f = SingleTaskGPModel(is_noise_free=False)
48
- self.model_f.set_bounds_from_history(self.history)
48
+ self.model_f.set_bounds_from_history(self.train_history)
49
49
  self.train_f()
50
50
 
51
51
  def update(self, parameters: pd.DataFrame) -> None:
@@ -54,8 +54,8 @@ class PoFBoTorchInterface(SurrogateModelInterfaceBase):
54
54
  self, parameters
55
55
  )
56
56
 
57
- # history.prm_name 順に並べ替え
58
- x = np.array([self.prm[k] for k in self.history.prm_names])
57
+ # train_history.prm_name 順に並べ替え
58
+ x = np.array([self.prm[k] for k in self.train_history.prm_names])
59
59
 
60
60
  # feasibility の計算
61
61
  mean_f, std_f = self.model_f.predict(np.array([x]))
@@ -63,9 +63,9 @@ class PoFBoTorchInterface(SurrogateModelInterfaceBase):
63
63
  if pof < self.threshold:
64
64
  raise SolveError(Msg.INFO_POF_IS_LESS_THAN_THRESHOLD)
65
65
 
66
- # 実際の計算(mean は history.obj_names 順)
66
+ # 実際の計算(現時点で mean は train_history.obj_names 順)
67
67
  _mean, _std = self.model.predict(np.array([x]))
68
68
  mean = _mean[0]
69
69
 
70
70
  # 目的関数の更新
71
- self.obj = {obj_name: value for obj_name, value in zip(self.history.obj_names, mean)}
71
+ self.obj = {obj_name: value for obj_name, value in zip(self.train_history.obj_names, mean)}
@@ -155,11 +155,6 @@ 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
-
163
158
  # ===== calc =====
164
159
  def f(self, x: np.ndarray, _record_infeasible=False) -> list[np.ndarray]:
165
160
  """Calculate objectives and constraints.
@@ -208,7 +203,8 @@ class AbstractOptimizer(ABC):
208
203
  c = [cns.calc(self.fem) for cns in self.constraints.values()]
209
204
 
210
205
  else:
211
- y, _y, c = self.generate_infeasible_result()
206
+ y, c = self.history.generate_hidden_infeasible_result()
207
+ _y = y
212
208
 
213
209
  # register to history
214
210
  df_to_opt = self.variables.get_variables(
@@ -206,10 +206,10 @@ class OptunaOptimizer(AbstractOptimizer):
206
206
  """Create storage, study and set initial parameter."""
207
207
 
208
208
  # create storage
209
- self.study_name = os.path.basename(self.history.path)
210
- storage_path = self.history.path.replace('.csv', '.db') # history と同じところに保存
209
+ self.study_name = 'pyfemtet-study'
210
+ storage_path = os.path.splitext(self.history.path)[0] + '.db' # history と同じところに保存
211
211
  if self.is_cluster: # remote cluster なら scheduler の working dir に保存
212
- storage_path = os.path.basename(self.history.path).replace('.csv', '.db')
212
+ storage_path = os.path.splitext(os.path.basename(self.history.path))[0] + '.db'
213
213
 
214
214
  # callback to terminate
215
215
  if self.n_trials is not None:
@@ -374,6 +374,17 @@ class OptunaOptimizer(AbstractOptimizer):
374
374
  sampler._pyfemtet_optimizer = self
375
375
 
376
376
  # load study
377
+ self.storage: optuna.storages.BaseStorage
378
+ studies = self.storage.get_all_studies()
379
+ if self.study_name in [s.study_name for s in studies]:
380
+ pass
381
+
382
+ elif len(studies) >= 1:
383
+ self.study_name = studies[-1].study_name
384
+
385
+ else:
386
+ raise ValueError('An empty db is passed.')
387
+
377
388
  study = optuna.load_study(
378
389
  study_name=self.study_name,
379
390
  storage=self.storage,
@@ -0,0 +1,52 @@
1
+ """Optimization using parametric analysis output settings as the objective function
2
+
3
+ This demo shows how to use the values outputted by Femtet's parametric
4
+ analysis output setting feature as the objective function for optimization.
5
+ This feature allows you to perform optimization without coding the objective function.
6
+
7
+
8
+ Note:
9
+
10
+ Please be aware of the following when using this feature.
11
+
12
+ - The sweep table from the parametric analysis will be deleted.
13
+ - Output settings that produce complex numbers or vectors will only use
14
+ the first value as the objective function. (For complex numbers, it will be
15
+ the real part, and for vector values, it will be components such as X.)
16
+
17
+
18
+ Corresponding project: gau_ex12_parametric.femprj
19
+
20
+ """
21
+
22
+ from pyfemtet.opt import FEMOpt, FemtetInterface
23
+
24
+
25
+ if __name__ == '__main__':
26
+
27
+ # Initialize an object to connect to
28
+ # Femtet for referencing Femtet settings.
29
+ fem = FemtetInterface()
30
+
31
+ # Set the output settings of the parametric analysis as the objective function.
32
+ # `number` is the index from the `results output settings` tab of the
33
+ # Femtet parametric analysis dialog, and `direction` is
34
+ # the goal of that objective function (similar to FEMOpt.add_objective).
35
+
36
+ # Mutual inductance
37
+ fem.use_parametric_output_as_objective(number=1, direction=1.5e-7)
38
+
39
+ # Strength of magnetic field at the center of the coil
40
+ fem.use_parametric_output_as_objective(number=2, direction='minimize')
41
+
42
+ # Initialize optimization object.
43
+ # Pass in the previously initialized fem object.
44
+ femopt = FEMOpt(fem=fem)
45
+
46
+ # Set parameters.
47
+ femopt.add_parameter('in_radius', 10, 5, 10)
48
+ femopt.add_parameter('out_radius', 20, 20, 25)
49
+
50
+ # Execute optimization.
51
+ femopt.set_random_seed(42) # Fix random seed
52
+ femopt.optimize(n_trials=20)
@@ -0,0 +1,52 @@
1
+ """パラメトリック解析出力設定を目的関数とする最適化
2
+
3
+ Femtet のパラメトリック解析の結果出力設定機能で出力される値を
4
+ 最適化の目的関数として使用する方法をデモします。
5
+ この機能により、目的関数をコーディングすることなく
6
+ 最適化を実施できます。
7
+
8
+
9
+ 注意:
10
+
11
+ この機能を使う際は、以下のことに注意してください。
12
+
13
+ - パラメトリック解析のスイープテーブルが削除されます。
14
+ - 複素数やベクトルを出力する出力設定は、第一の値のみが
15
+ 目的関数として使用されます。(複素数の場合は実数、
16
+ ベクトル値の場合は X 成分など)
17
+
18
+
19
+ 対応するプロジェクト: gau_ex12_parametric.femprj
20
+ """
21
+
22
+ from pyfemtet.opt import FEMOpt, FemtetInterface
23
+
24
+
25
+ if __name__ == '__main__':
26
+
27
+ # Femtet の設定を参照するため、Femtet と接続を
28
+ # 行うためのオブジェクトを初期化します。
29
+ fem = FemtetInterface()
30
+
31
+ # パラメトリック解析の結果出力設定を目的関数にします。
32
+ # number は Femtet パラメトリック解析ダイアログの
33
+ # 結果出力設定タブのテーブルの番号で、direction は
34
+ # その目的関数の目標です(FEMOpt.add_objective と同様)。
35
+
36
+ # 相互インダクタンス
37
+ fem.use_parametric_output_as_objective(number=1, direction=1.5e-7)
38
+
39
+ # コイル中央の磁界の強さ
40
+ fem.use_parametric_output_as_objective(number=2, direction='minimize')
41
+
42
+ # 最適化用オブジェクトを初期化します。
43
+ # さきほど初期化した fem を渡します。
44
+ femopt = FEMOpt(fem=fem)
45
+
46
+ # パラメータを設定します。
47
+ femopt.add_parameter('in_radius', 10, 5, 10)
48
+ femopt.add_parameter('out_radius', 20, 20, 25)
49
+
50
+ # 最適化を実行します。
51
+ femopt.set_random_seed(42) # 乱数シードの固定
52
+ femopt.optimize(n_trials=20)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyfemtet
3
- Version: 0.8.7
3
+ Version: 0.8.9
4
4
  Summary: Design parameter optimization using Femtet.
5
5
  License: BSD-3-Clause
6
6
  Author: kazuma.naito
@@ -1,4 +1,4 @@
1
- pyfemtet/__init__.py,sha256=04WO81p8xrQAHiTKqHaDkKWih6M2Y3Hy-UCNVwih4Zc,21
1
+ pyfemtet/__init__.py,sha256=jBrmFTf-nNFPcJTuyxzw1H9WEW7G7OpCejEb8xk5m-U,21
2
2
  pyfemtet/_femtet_config_util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  pyfemtet/_femtet_config_util/autosave.py,sha256=dNirA9XGuFehas8_Jkj2BW9GOzMbPyhnt1WHcH_ObSU,2070
4
4
  pyfemtet/_femtet_config_util/exit.py,sha256=0BWID-tjOkmZwmgPFkcJMkWW39voccz5ARIBWvZbHaw,1877
@@ -24,16 +24,19 @@ pyfemtet/dispatch_extensions/_impl.py,sha256=yH_yeAnQ-Xi9GfjX-FQt9u3yHnrLYIteRb6
24
24
  pyfemtet/logger/__init__.py,sha256=UOJ9n_U2xwdTrp0Xgg-N6geySxNzKqTBQlXsaH0kW_w,420
25
25
  pyfemtet/logger/_impl.py,sha256=rsAd0HpmveOaLS39ucp3U2OcDhQMWjC5fnVGhbJtWVw,6375
26
26
  pyfemtet/opt/__init__.py,sha256=wRR8LbEhb5I6MUgmnCgjB6-tqHlOVxDIo7yPkq0QbBs,758
27
- pyfemtet/opt/_femopt.py,sha256=ZhwOK1QYwo7Xk67qEOm4biKXzdIW2AUHYvgklBREDm8,40587
28
- pyfemtet/opt/_femopt_core.py,sha256=kxKh4_TdXR4LBgv8WibPiZ5Pe9apJd5ArBCfnhBwcCQ,38969
27
+ pyfemtet/opt/_femopt.py,sha256=9-naHBWBVCd2jkIldhO_whrOh6AmpQLXUsdzyYKZyco,40587
28
+ pyfemtet/opt/_femopt_core.py,sha256=hMnb5UD7R_FfF6VvNLakJedIdjv7B3i--nsvqhdue2M,39328
29
29
  pyfemtet/opt/_test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  pyfemtet/opt/_test_utils/control_femtet.py,sha256=8oAl9y5V2n8Nnsgx_ebcZVzwFt1eI3swkdiKg6pg3-M,1085
31
31
  pyfemtet/opt/_test_utils/hyper_sphere.py,sha256=nQhw8EIY0DwvcTqrbKhkxiITLZifr4-nG77E-_6ggmA,700
32
- pyfemtet/opt/_test_utils/record_history.py,sha256=zsa1w73K7NLBqbj7yuv0fWVJvZtWdiI0eCaUoAn5Bjg,4239
32
+ pyfemtet/opt/_test_utils/record_history.py,sha256=7V2LCZ8F985c_NNUVu-K7_2-p2mwG1lRMZhkYhSy_Dw,4356
33
33
  pyfemtet/opt/advanced_samples/excel_ui/(ref) original_project.femprj,sha256=5OqZfynTpVCrgEIOBOMYuDGaMvepi5lojVNFr1jAsEI,157489
34
34
  pyfemtet/opt/advanced_samples/excel_ui/femtet-macro.xlsm,sha256=ckF0SQ0f3IWSW6QoH1IPJdwUUlR7O_AiGC5fi8SI3jA,133137
35
35
  pyfemtet/opt/advanced_samples/excel_ui/pyfemtet-core.py,sha256=aF2TWXdbt7dnkeBqqVO6GvIExozjFp0mxx3BX8rpYNc,9879
36
36
  pyfemtet/opt/advanced_samples/excel_ui/test-pyfemtet-core.cmd,sha256=r-Pa1Ng9sa6wfDqIhTf2BUDrN9rePWFymz7pmtBbvcQ,895
37
+ pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric.femprj,sha256=iIHH1X-wWBqEYj4cFJXco73LCJXSrYBsSKOD0HxYu60,87599
38
+ pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart.py,sha256=YCqtLjazFDRKhEu7sGJlDJDDL7YPH6GmWEb6X0e30Og,3045
39
+ pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart_jp.py,sha256=Oaua2Eb8NGtlWjvYC9B1uYTKcEuWxWZxzJbLi_4IqxE,3580
37
40
  pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data.py,sha256=tgNH0z-mUZRq-3VLjR-BU09z2COmXFruyrc4T8WS5U8,1663
38
41
  pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data_jp.py,sha256=xtfJWrc353k1977wIf66MOPmgqLDDQpMCtX8QSDE5zQ,1813
39
42
  pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate.py,sha256=s0b31wuN3iXjb78dt0ro0ZjxHa8uLIH94jRfEuj1EVY,3090
@@ -42,22 +45,22 @@ pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_parametric.femprj,sha256=
42
45
  pyfemtet/opt/interface/__init__.py,sha256=na6-elI9-karOqoSxT9LfLQpjBPm1lrUWjow0NYYRP4,1349
43
46
  pyfemtet/opt/interface/_base.py,sha256=y0uQ5jdsWbgt5odyqPin7NXcK_IbUwPDcrrkV_JhpRw,2722
44
47
  pyfemtet/opt/interface/_excel_interface.py,sha256=s103vePTPXXYiPwGdAEUFgtpvGXtu1nSljDtP4HsmcY,40355
45
- pyfemtet/opt/interface/_femtet.py,sha256=Tn0qgVMJDv-6hBPDOWYBtlfvhsb4UH3MknX0PaXb8ro,35313
48
+ pyfemtet/opt/interface/_femtet.py,sha256=_T75D0GWEYc_5n9RRF6u-HZVxgFnG9PoGYF2-cDiqeM,37673
46
49
  pyfemtet/opt/interface/_femtet_parametric.py,sha256=rhvnpHdbjNJAKxiCkgnExnZdV5qOB6pBv6AaLeTkeF8,10155
47
50
  pyfemtet/opt/interface/_femtet_with_nx/__init__.py,sha256=-6W2g2FDEcKzGHmI5KAKQe-4U5jDpMj0CXuma-GZca0,83
48
51
  pyfemtet/opt/interface/_femtet_with_nx/_interface.py,sha256=LkaODUSpBLq05uz5Jf-JKuH6Evq8ElZoItXxFZopWeM,5994
49
52
  pyfemtet/opt/interface/_femtet_with_nx/update_model.py,sha256=P7VH0i_o-X9OUe6AGaLF1fACPeHNrMjcrOBCA3MMrI4,3092
50
53
  pyfemtet/opt/interface/_femtet_with_sldworks.py,sha256=rjEgebuP1w1eAFVWw4eRJUq3lsyBcmXlkMjZKIpD0kw,11019
51
54
  pyfemtet/opt/interface/_surrogate/__init__.py,sha256=2UT5NuBylyWQJNjg1zsBRCV-MzNCUswTUt6ZuSrYFUM,120
52
- pyfemtet/opt/interface/_surrogate/_base.py,sha256=bQMoztVq1b-3BW5Z1V-dSROplMHutrblDI289j0cC-E,3001
53
- pyfemtet/opt/interface/_surrogate/_chaospy.py,sha256=gL72bCgs1AY_EZdJtcifSC-apwsZzp4zsWYxcpVKvtw,1969
54
- pyfemtet/opt/interface/_surrogate/_singletaskgp.py,sha256=ojZHsxGxSc8ZJqJQ_uMHvpK98TPUsHzXP0q4tmM0YPQ,2471
55
+ pyfemtet/opt/interface/_surrogate/_base.py,sha256=_mVjoxrGEWL-PydjzEYXIgsOJ9zPmntoRHY3dXR2HGo,3098
56
+ pyfemtet/opt/interface/_surrogate/_chaospy.py,sha256=Bqej89Mo0zgdJq1OK7YKRqHOcuyN0wL4ZQUQXdJtYJ8,1987
57
+ pyfemtet/opt/interface/_surrogate/_singletaskgp.py,sha256=bHzY5QIjA9zhLxweexz259XQMZLgkHWfrIDW7f3q-2k,2520
55
58
  pyfemtet/opt/optimizer/__init__.py,sha256=Ia6viowECkG0IFXtFef0tJ4jDKsoDzJLqMJ9xLFH2LQ,543
56
- pyfemtet/opt/optimizer/_base.py,sha256=j8aQc3fGehZTJT9ETf9cr3VWYs2FYk1F8fO3f7QyKAU,13099
59
+ pyfemtet/opt/optimizer/_base.py,sha256=1TY1iVZdpgJcmF6g3-CcY-6u4REs8_gR_0y9cXUQe2s,12932
57
60
  pyfemtet/opt/optimizer/_optuna/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
61
  pyfemtet/opt/optimizer/_optuna/_botorch_patch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
62
  pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py,sha256=b2-PP2HM46kJS4cJkBWnxnW9AS9JfeVkEjmkoKK_ziE,8949
60
- pyfemtet/opt/optimizer/_optuna/_optuna.py,sha256=5Bjn8LrWpIFWH3lRq6Ke5XYzKQNwoGWNHz59HNMEALA,17093
63
+ pyfemtet/opt/optimizer/_optuna/_optuna.py,sha256=5W_Z9p6lPVeflttx0fpRryic_fUVKl0lz6tqkxUpCnA,17422
61
64
  pyfemtet/opt/optimizer/_optuna/_pof_botorch.py,sha256=FLx9p6IH8xcZl_SZYvs8grMqLEidj5YaBD8urDD88Pk,73768
62
65
  pyfemtet/opt/optimizer/_scipy.py,sha256=_2whhMNq6hC1lr5PlYhpZ8Zlh6-DkAjz8SVB5qHIpYg,4766
63
66
  pyfemtet/opt/optimizer/_scipy_scalar.py,sha256=rGvrLjrgfYzxK9GA0-r2Hhoaqt6A0TQsT_1M3moyklc,3615
@@ -85,6 +88,8 @@ pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric_test_result.reccsv,sha256
85
88
  pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.femprj,sha256=Yb9ILeTEKx5xfJGk8IZH_DVlgkpGB33Vy9-LGIEQboY,279251
86
89
  pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.py,sha256=5KazqJ5wRbGs0dBMJslZ1eRCUWq8j3k1mqlhyB8M0g8,1929
87
90
  pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric_test_result.reccsv,sha256=yZ9aHthiKIBY_NMOz94Jl2dyHIH-GWMvukgHk4ZeT_o,3474
91
+ pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.femprj,sha256=cmuUgKtM3qoJrSxkWMZHVNdEgUFo7wFNO0vMkGCxl9k,461055
92
+ pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.py,sha256=XYzMYQOq7O4wFgf9Diw_oKe7-kbGRFJBOWVg3e4O9io,1831
88
93
  pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.femprj,sha256=LLAUDlUo1dIpRzlKPs1lvACzJQxjnWW3xAGAodYEqRM,117221
89
94
  pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py,sha256=ImxVoNHDywW77DX8pkHi08wPW7AlRUiaDBpUD3fPIRk,4848
90
95
  pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv,sha256=0yWqTpmpAtFvYRRyk2zneAVnl_5qJDeVwG4aeIWxXv8,3679
@@ -107,6 +112,7 @@ pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.femprj,sha256=PzqtN
107
112
  pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.py,sha256=97np4uH-UQqpv4UDwJS0doFYA7TOkXnbhLdkZExdNek,2461
108
113
  pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.femprj,sha256=TTXw_8YT8pzHQlu4ufGzTq1IFYSwcWWt4GA6sIY1YPM,295600
109
114
  pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.py,sha256=8Op_zwz9SD0NfGg4TFlcNvs-ZlU0bxgs5oaaI9UtlRU,2087
115
+ pyfemtet/opt/samples/femprj_sample_jp/gau_ex12_parametric_jp.py,sha256=VDujrSbAPANqlizugVJX27uyWnnnOjclv4NJAZXDSXc,1997
110
116
  pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.femprj,sha256=OJ7f8iw0z1BZqanuNn71uEaoM2Kgb93ptUU8iYwYON0,129783
111
117
  pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.py,sha256=MlFk6KRCQeCX1J0DNOjph75qjCUHg5UQPNTcHxIEnoo,5279
112
118
  pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.femprj,sha256=y7eURFBdqh6PmD4zbelGuB458HmfihVht0K4wVI-mik,265368
@@ -137,8 +143,8 @@ pyfemtet/opt/visualization/result_viewer/.gitignore,sha256=ryvb4aqbbsHireHWlPQfx
137
143
  pyfemtet/opt/visualization/result_viewer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
138
144
  pyfemtet/opt/visualization/result_viewer/application.py,sha256=WcHBx_J5eNLKSaprpk9BGifwhO04oN8FiNGYTWorrXA,1691
139
145
  pyfemtet/opt/visualization/result_viewer/pages.py,sha256=MZAjzbuq0toZrR-iJhElM3A12_jHVCTt65gz1kdNPbw,32193
140
- pyfemtet-0.8.7.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
141
- pyfemtet-0.8.7.dist-info/METADATA,sha256=uy1fm6IKQ5OXU8kZ7bowgMIyCbyaQ9NTgaxz0r1DoV0,3509
142
- pyfemtet-0.8.7.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
143
- pyfemtet-0.8.7.dist-info/entry_points.txt,sha256=ZfYqRaoiPtuWqFi2_msccyrVF0LurMn-IHlYamAegZo,104
144
- pyfemtet-0.8.7.dist-info/RECORD,,
146
+ pyfemtet-0.8.9.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
147
+ pyfemtet-0.8.9.dist-info/METADATA,sha256=7KhkJFd-xgckrqllScrrmO-uAYns2wfa-G8PDR5KSd8,3509
148
+ pyfemtet-0.8.9.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
149
+ pyfemtet-0.8.9.dist-info/entry_points.txt,sha256=ZfYqRaoiPtuWqFi2_msccyrVF0LurMn-IHlYamAegZo,104
150
+ pyfemtet-0.8.9.dist-info/RECORD,,