pyfemtet 0.5.3__py3-none-any.whl → 0.6.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 (62) hide show
  1. pyfemtet/__init__.py +1 -1
  2. pyfemtet/{message → _message}/locales/ja/LC_MESSAGES/messages.po +89 -77
  3. pyfemtet/{message → _message}/locales/messages.pot +88 -76
  4. pyfemtet/{message → _message}/messages.py +1 -1
  5. pyfemtet/_warning.py +23 -0
  6. pyfemtet/dispatch_extensions/__init__.py +12 -0
  7. pyfemtet/{dispatch_extensions.py → dispatch_extensions/_impl.py} +45 -43
  8. pyfemtet/logger/__init__.py +3 -0
  9. pyfemtet/{logger.py → logger/_impl.py} +12 -6
  10. pyfemtet/opt/__init__.py +3 -0
  11. pyfemtet/opt/_femopt.py +265 -68
  12. pyfemtet/opt/_femopt_core.py +111 -68
  13. pyfemtet/opt/_test_utils/record_history.py +1 -1
  14. pyfemtet/opt/interface/__init__.py +0 -1
  15. pyfemtet/opt/interface/_base.py +3 -3
  16. pyfemtet/opt/interface/_femtet.py +116 -59
  17. pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -12
  18. pyfemtet/opt/interface/_femtet_with_sldworks.py +22 -2
  19. pyfemtet/opt/optimizer/__init__.py +5 -1
  20. pyfemtet/opt/optimizer/_base.py +81 -55
  21. pyfemtet/opt/optimizer/{_optuna_botorchsampler_parameter_constraint_helper.py → _optuna/_botorch_patch/enable_nonlinear_constraint.py} +10 -127
  22. pyfemtet/opt/optimizer/{_optuna.py → _optuna/_optuna.py} +122 -19
  23. pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +1833 -0
  24. pyfemtet/opt/optimizer/_scipy.py +20 -5
  25. pyfemtet/opt/optimizer/_scipy_scalar.py +20 -5
  26. pyfemtet/opt/prediction/{base.py → _base.py} +3 -2
  27. pyfemtet/opt/prediction/single_task_gp.py +10 -5
  28. pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +2 -2
  29. pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +2 -2
  30. pyfemtet/opt/visualization/{base.py → _base.py} +1 -1
  31. pyfemtet/opt/visualization/{complex_components → _complex_components}/alert_region.py +2 -2
  32. pyfemtet/opt/visualization/{complex_components → _complex_components}/control_femtet.py +3 -3
  33. pyfemtet/opt/visualization/{complex_components → _complex_components}/main_figure_creator.py +1 -1
  34. pyfemtet/opt/visualization/{complex_components → _complex_components}/main_graph.py +5 -5
  35. pyfemtet/opt/visualization/{complex_components → _complex_components}/pm_graph.py +5 -5
  36. pyfemtet/opt/visualization/{complex_components → _complex_components}/pm_graph_creator.py +2 -2
  37. pyfemtet/opt/visualization/_create_wrapped_components.py +2 -2
  38. pyfemtet/opt/visualization/_process_monitor/__init__.py +0 -0
  39. pyfemtet/opt/visualization/{process_monitor → _process_monitor}/application.py +3 -3
  40. pyfemtet/opt/visualization/{process_monitor → _process_monitor}/pages.py +10 -10
  41. pyfemtet/opt/visualization/_wrapped_components/__init__.py +0 -0
  42. pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/dbc.py +1 -1
  43. pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/dcc.py +1 -1
  44. pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/html.py +1 -1
  45. pyfemtet/opt/visualization/result_viewer/application.py +4 -4
  46. pyfemtet/opt/visualization/result_viewer/pages.py +9 -9
  47. {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/METADATA +2 -2
  48. {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/RECORD +60 -56
  49. {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/WHEEL +1 -1
  50. pyfemtet/message/locales/ja/LC_MESSAGES/messages.mo +0 -0
  51. pyfemtet/opt/samples/femprj_sample/.gitignore +0 -2
  52. /pyfemtet/{message → _message}/1. make_pot.bat +0 -0
  53. /pyfemtet/{message → _message}/2. make_mo.bat +0 -0
  54. /pyfemtet/{message → _message}/__init__.py +0 -0
  55. /pyfemtet/{message → _message}/babel.cfg +0 -0
  56. /pyfemtet/opt/{visualization/complex_components → optimizer/_optuna}/__init__.py +0 -0
  57. /pyfemtet/opt/{visualization/process_monitor → optimizer/_optuna/_botorch_patch}/__init__.py +0 -0
  58. /pyfemtet/opt/{parameter.py → optimizer/parameter.py} +0 -0
  59. /pyfemtet/opt/visualization/{wrapped_components → _complex_components}/__init__.py +0 -0
  60. /pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/str_enum.py +0 -0
  61. {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/LICENSE +0 -0
  62. {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/entry_points.txt +0 -0
@@ -29,7 +29,7 @@ from win32com.client import constants, Constants
29
29
 
30
30
  # pyfemtet relative
31
31
  from pyfemtet.opt.interface import FEMInterface, FemtetInterface
32
- from pyfemtet.message import encoding, Msg
32
+ from pyfemtet._message import encoding, Msg
33
33
 
34
34
  # logger
35
35
  import logging
@@ -453,18 +453,12 @@ class History:
453
453
  cns_names (List[str], optional): The names of constraints. Defaults to None.
454
454
  client (dask.distributed.Client): Dask client.
455
455
  additional_metadata (str, optional): metadata of optimization process.
456
-
457
- Raises:
458
- FileNotFoundError: If the csv file is not found.
459
-
460
- Attributes:
461
- HEADER_ROW (int): Header row number of csv file. Must be grater than 0. Default to 2.
462
- ENCODING (str): Encoding of csv file. Default to 'cp932'.
463
- prm_names (str): User defined names of parameters.
464
- obj_names (str): User defined names of objectives.
465
- cns_names (str): User defined names of constraints.
466
- is_restart (bool): If the optimization process is a continuation of another process or not.
467
- is_processing (bool): The optimization is running or not.
456
+ hv_reference (str or list[float or np.ndarray, optional):
457
+ The method to calculate hypervolume or
458
+ the reference point itself.
459
+ Valid values are 'dynamic-pareto' or
460
+ 'dynamic-nadir' or 'nadir' or 'pareto'
461
+ or fixed point (in objective function space).
468
462
 
469
463
  """
470
464
 
@@ -487,7 +481,10 @@ class History:
487
481
  cns_names=None,
488
482
  client=None,
489
483
  additional_metadata=None,
484
+ hv_reference=None,
490
485
  ):
486
+ # hypervolume 計算メソッド
487
+ self._hv_reference = hv_reference or 'dynamic-pareto'
491
488
 
492
489
  # 引数の処理
493
490
  self.path = history_path # .csv
@@ -715,76 +712,122 @@ class History:
715
712
 
716
713
  def _calc_non_domi(self, objectives, df):
717
714
 
715
+ # feasible のもののみ取り出してくる
716
+ idx = df['feasible'].values
717
+ pdf = df[idx]
718
+
718
719
  # 目的関数の履歴を取り出してくる
719
- solution_set = df[self.obj_names]
720
+ solution_set = pdf[self.obj_names]
720
721
 
721
722
  # 最小化問題の座標空間に変換する
722
723
  for obj_column, (_, objective) in zip(self.obj_names, objectives.items()):
723
724
  solution_set.loc[:, obj_column] = solution_set[obj_column].map(objective.convert)
724
725
 
725
726
  # 非劣解の計算
726
- non_domi = []
727
+ non_domi: list[bool] = []
727
728
  for i, row in solution_set.iterrows():
728
729
  non_domi.append((row > solution_set).product(axis=1).sum(axis=0) == 0)
729
730
 
730
- # 非劣解の登録
731
- df['non_domi'] = non_domi
731
+ # feasible も infeasible も一旦劣解にする
732
+ df['non_domi'] = False
733
+
734
+ # feasible のものに non_domi の評価結果を代入する
735
+ df.loc[idx, 'non_domi'] = non_domi
732
736
 
733
737
  def _calc_hypervolume(self, objectives, df):
734
738
 
735
- # filter non-dominated and feasible solutions
736
- idx = df['non_domi'].values
737
- idx = idx * df['feasible'].values # *= を使うと non_domi 列の値が変わる
738
- pdf = df[idx]
739
- pareto_set = pdf[self.obj_names].values
740
- n = len(pareto_set) # 集合の要素数
741
- m = len(pareto_set.T) # 目的変数数
742
- # 多目的でないと計算できない
743
- if m <= 1:
744
- return None
745
- # 長さが 2 以上でないと計算できない
746
- if n <= 1:
747
- return None
748
- # 最小化問題に convert
749
- for i, (_, objective) in enumerate(objectives.items()):
750
- for j in range(n):
751
- pareto_set[j, i] = objective.convert(pareto_set[j, i])
752
- #### reference point の計算[1]
753
- # 逆正規化のための範囲計算
754
- maximum = pareto_set.max(axis=0)
755
- minimum = pareto_set.min(axis=0)
756
-
757
- r = 1.01
758
-
759
- # r を逆正規化
760
- reference_point = r * (maximum - minimum) + minimum
761
-
762
- #### hv 履歴の計算
763
- hvs = []
764
- for i in range(n):
765
- hv = compute_hypervolume(pareto_set[:i], reference_point)
766
- if np.isnan(hv):
767
- hv = 0
768
- hvs.append(hv)
769
-
770
- # 計算結果を履歴の一部に割り当て
771
- # idx: [False, True, False, True, True, False, ...]
772
- # hvs: [ 1, 2, 3, ...]
773
- # want:[ 0, 1, 1, 2, 3, 3, ...]
774
- hvs_index = -1
775
- for i in range(len(df)):
776
-
777
- # process hvs index
778
- if idx[i]:
779
- hvs_index += 1
780
-
781
- # get hv
782
- if hvs_index < 0:
783
- hypervolume = 0.
739
+ if len(objectives) < 2:
740
+ df.loc[len(df) - 1, 'hypervolume'] = 0.
741
+ return
742
+
743
+ # 最小化問題に変換された objective values を取得
744
+ raw_objective_values = df[self.obj_names].values
745
+ objective_values = np.empty_like(raw_objective_values)
746
+ for n_trial in range(len(raw_objective_values)):
747
+ for obj_idx, (_, objective) in enumerate(objectives.items()):
748
+ objective_values[n_trial, obj_idx] = objective.convert(raw_objective_values[n_trial, obj_idx])
749
+
750
+ # pareto front を取得
751
+ def get_pareto(objective_values_, with_partial=False):
752
+ ret = None
753
+ if with_partial:
754
+ ret = []
755
+
756
+ pareto_set_ = np.empty((0, len(self.obj_names)))
757
+ for i in range(len(objective_values_)):
758
+ target = objective_values_[i]
759
+ dominated = False
760
+ # TODO: Array の計算に直して高速化する
761
+ for j in range(len(objective_values_)):
762
+ compare = objective_values_[j]
763
+ if all(target > compare):
764
+ dominated = True
765
+ break
766
+ if not dominated:
767
+ pareto_set_ = np.concatenate([pareto_set_, [target]], axis=0)
768
+
769
+ if ret is not None:
770
+ ret.append(np.array(pareto_set_))
771
+
772
+ if ret is not None:
773
+ return pareto_set_, ret
784
774
  else:
785
- hypervolume = hvs[hvs_index]
775
+ return pareto_set_
776
+
777
+ if self._hv_reference == 'dynamic-pareto':
778
+ pareto_set, pareto_set_list = get_pareto(objective_values, with_partial=True)
779
+ for i, partial_pareto_set in enumerate(pareto_set_list):
780
+ ref_point = pareto_set.max(axis=0) + 1e-8
781
+ hv = compute_hypervolume(partial_pareto_set, ref_point)
782
+ df.loc[i, 'hypervolume'] = hv
783
+ return
784
+
785
+ elif self._hv_reference == 'dynamic-nadir':
786
+ _, pareto_set_list = get_pareto(objective_values, with_partial=True)
787
+ for i, partial_pareto_set in enumerate(pareto_set_list):
788
+ ref_point = objective_values.max(axis=0) + 1e-8
789
+ hv = compute_hypervolume(partial_pareto_set, ref_point)
790
+ df.loc[i, 'hypervolume'] = hv
791
+ return
792
+
793
+ elif self._hv_reference == 'nadir':
794
+ pareto_set = get_pareto(objective_values)
795
+ ref_point = objective_values.max(axis=0) + 1e-8
796
+ hv = compute_hypervolume(pareto_set, ref_point)
797
+ df.loc[len(df) - 1, 'hypervolume'] = hv
798
+ return
799
+
800
+ elif self._hv_reference == 'pareto':
801
+ pareto_set = get_pareto(objective_values)
802
+ ref_point = pareto_set.max(axis=0) + 1e-8
803
+ hv = compute_hypervolume(pareto_set, ref_point)
804
+ df.loc[len(df) - 1, 'hypervolume'] = hv
805
+ return
806
+
807
+ elif (
808
+ isinstance(self._hv_reference, np.ndarray)
809
+ or isinstance(self._hv_reference, list)
810
+ ):
811
+ _buff = np.array(self._hv_reference)
812
+ assert _buff.shape == (len(self.obj_names),)
813
+
814
+ ref_point = np.array(
815
+ [obj.convert(raw_value) for obj, raw_value in zip(objectives.values(), _buff)]
816
+ )
817
+
818
+ _buff = get_pareto(objective_values)
819
+
820
+ pareto_set = np.empty((0, len(objectives)))
821
+ for pareto_sol in _buff:
822
+ if all(pareto_sol < ref_point):
823
+ pareto_set = np.concatenate([pareto_set, [pareto_sol]], axis=0)
786
824
 
787
- df.loc[i, 'hypervolume'] = hypervolume
825
+ hv = compute_hypervolume(pareto_set, ref_point)
826
+ df.loc[len(df) - 1, 'hypervolume'] = hv
827
+ return
828
+
829
+ else:
830
+ raise NotImplementedError(f'Invalid Hypervolume reference point calculation method: {self._hv_reference}')
788
831
 
789
832
  def save(self, _f=None):
790
833
  """Save csv file."""
@@ -7,7 +7,7 @@ import numpy as np
7
7
  import pandas as pd
8
8
 
9
9
  from pyfemtet.opt import FEMOpt
10
- from pyfemtet.message import encoding as ENCODING
10
+ from pyfemtet._message import encoding as ENCODING
11
11
 
12
12
 
13
13
  def find_latest_csv(dir_path=None):
@@ -11,5 +11,4 @@ __all__ = [
11
11
  'FemtetInterface',
12
12
  'FemtetWithNXInterface',
13
13
  'FemtetWithSolidworksInterface',
14
- 'logger',
15
14
  ]
@@ -68,15 +68,15 @@ class FEMInterface(ABC):
68
68
  """Preprocessing after launching a dask worker and before run optimization (if implemented in concrete class)."""
69
69
  pass
70
70
 
71
- def postprocess_func(self, trial: int, *args, dask_scheduler=None, **kwargs):
71
+ def _postprocess_func(self, trial: int, *args, dask_scheduler=None, **kwargs):
72
72
  pass
73
73
 
74
- def create_postprocess_args(self):
74
+ def _create_postprocess_args(self):
75
75
  pass
76
76
 
77
77
 
78
78
  class NoFEM(FEMInterface):
79
- """Interface with no FEM for debug."""
79
+ """Dummy interface without FEM. Intended for debugging purposes."""
80
80
 
81
81
  def update(self, parameters: pd.DataFrame) -> None:
82
82
  pass
@@ -34,47 +34,80 @@ from pyfemtet.dispatch_extensions import (
34
34
  DispatchExtensionException,
35
35
  )
36
36
  from pyfemtet.opt.interface import FEMInterface, logger
37
- from pyfemtet.message import Msg
37
+ from pyfemtet._message import Msg
38
38
 
39
39
 
40
- def post_activate_message(hwnd):
40
+ def _post_activate_message(hwnd):
41
41
  win32gui.PostMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
42
42
 
43
43
 
44
44
  class FemtetInterface(FEMInterface):
45
- """Concrete class for the interface with Femtet.
45
+ """Control Femtet from optimizer.
46
46
 
47
- Args:
48
- femprj_path (str or None, optional): The path to the .femprj file. Defaults to None.
49
- model_name (str or None, optional): The name of the analysis model. Defaults to None.
50
- connect_method (str, optional): The connection method to use. Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
51
- strictly_pid_specify (bool, optional): If True and connect_method=='new', search launched Femtet process strictly based on its process id.
52
- allow_without_project (bool, optional): Allow to launch Femtet with no project file. Default to False.
53
- open_result_with_gui (bool, optional): Open analysis result with Femtet GUI. Default to True.
54
- parametric_output_indexes_use_as_objective (dict, optional): Parametric output indexes and their directions which will be used as objective functions. Parametric output should be set on Femtet parametric analysis dialog. Note that output 'No.' in dialog is starts with 1, but this 'index' is starts with 0. The key is the 'index', the value is direction ('maximize', 'minimize' or float). Default to None.
55
-
56
- Warning:
57
- Even if you specify ``strictly_pid_specify=True`` on the constructor,
58
- **the connection behavior is like** ``strictly_pid_specify=False`` **in parallel processing**
59
- because of its large overhead.
60
- So you should close all Femtet processes before running FEMOpt.optimize()
61
- if ``n_parallel`` >= 2.
62
-
63
- Tip:
64
- If you search for information about the method to connect python and Femtet, see :func:`connect_femtet`.
47
+ Args:
48
+ femprj_path (str, optional):
49
+ The path to the project file.
50
+ If not specified, get it from connected Femtet.
51
+
52
+ model_name (str, optional):
53
+ The name of the model.
54
+ If not specified, get it from connected Femtet or the
55
+ first model when Femtet open the project file.
56
+
57
+ connect_method (str, optional):
58
+ The connection method.
59
+ Default is 'auto'. Other valid values are 'new' or 'existing'.
60
+
61
+ save_pdt (str, optional):
62
+ The type to save result file.
63
+ Can specify 'all' or None. Default is 'all'.
64
+
65
+ strictly_pid_specify (bool, optional):
66
+ Whether to strictly specify the PID in Femtet connection.
67
+ Default is True.
68
+
69
+ allow_without_project (bool, optional):
70
+ Whether to allow without a project. Default is False.
71
+
72
+ open_result_with_gui (bool, optional):
73
+ Whether to open the result with GUI. Default is True.
74
+
75
+ parametric_output_indexes_use_as_objective (dict[int, str or float], optional):
76
+ A list of parametric output indexes and its direction
77
+ to use as the objective function. If not specified,
78
+ it will be None and no parametric outputs are used
79
+ as objectives.
80
+
81
+ confirm_before_exit (bool):
82
+ Whether to confirm before (abnormal) termination.
83
+ Default is True.
84
+
85
+ **kwargs: Additional arguments from inherited classes.
86
+
87
+ Warning:
88
+ Even if you specify ``strictly_pid_specify=True`` on the constructor,
89
+ **the connection behavior is like** ``strictly_pid_specify=False`` **in parallel processing**
90
+ because of its large overhead.
91
+ So you should close all Femtet processes before running FEMOpt.optimize()
92
+ if ``n_parallel`` >= 2.
93
+
94
+ Tip:
95
+ If you search for information about the method to
96
+ connect python and Femtet, see :func:`connect_femtet`.
65
97
 
66
98
  """
67
99
 
68
100
  def __init__(
69
101
  self,
70
- femprj_path=None,
71
- model_name=None,
72
- connect_method='auto', # dask worker では __init__ の中で 'new' にするので super() の引数にしない。(しても意味がない)
73
- save_pdt='all', # 'all' or None
74
- strictly_pid_specify=True, # dask worker では True にしたいので super() の引数にしない。
75
- allow_without_project=False, # main でのみ True を許容したいので super() の引数にしない。
76
- open_result_with_gui=True,
77
- parametric_output_indexes_use_as_objective=None,
102
+ femprj_path: str = None,
103
+ model_name: str = None,
104
+ connect_method: str = 'auto', # dask worker では __init__ の中で 'new' にするので super() の引数にしない。(しても意味がない)
105
+ save_pdt: str = 'all', # 'all' or None
106
+ strictly_pid_specify: bool = True, # dask worker では True にしたいので super() の引数にしない。
107
+ allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
108
+ open_result_with_gui: bool = True,
109
+ parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
110
+ confirm_before_exit: bool = True,
78
111
  **kwargs # 継承されたクラスからの引数
79
112
  ):
80
113
 
@@ -102,8 +135,9 @@ class FemtetInterface(FEMInterface):
102
135
  self.max_api_retry = 3
103
136
  self.strictly_pid_specify = strictly_pid_specify
104
137
  self.parametric_output_indexes_use_as_objective = parametric_output_indexes_use_as_objective
105
- self._original_autosave_enabled = get_autosave_enabled()
106
- set_autosave_enabled(False)
138
+ self._original_autosave_enabled = _get_autosave_enabled()
139
+ _set_autosave_enabled(False)
140
+ self.confirm_before_exit = confirm_before_exit
107
141
 
108
142
  # dask サブプロセスのときは femprj を更新し connect_method を new にする
109
143
  try:
@@ -141,11 +175,12 @@ class FemtetInterface(FEMInterface):
141
175
  open_result_with_gui=self.open_result_with_gui,
142
176
  parametric_output_indexes_use_as_objective=self.parametric_output_indexes_use_as_objective,
143
177
  save_pdt=self.save_pdt,
178
+ confirm_before_exit=self.confirm_before_exit,
144
179
  **kwargs
145
180
  )
146
181
 
147
182
  def __del__(self):
148
- set_autosave_enabled(self._original_autosave_enabled)
183
+ _set_autosave_enabled(self._original_autosave_enabled)
149
184
  if self.quit_when_destruct:
150
185
  self.quit()
151
186
  # CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
@@ -170,12 +205,12 @@ class FemtetInterface(FEMInterface):
170
205
  """Connects to a Femtet process.
171
206
 
172
207
  Args:
173
- connect_method (str, optional): The connection method. Can be 'new', 'existing', or 'auto'. Defaults to 'new'.
208
+ connect_method (str, optional): The connection method. Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
174
209
  pid (int or None, optional): The process ID of an existing Femtet process and wanted to connect.
175
210
 
176
211
  Note:
177
212
  When connect_method is 'new', starts a new Femtet process and connects to it.
178
- **`pid` will be ignored.**
213
+ `pid` will be ignored.
179
214
 
180
215
  Note:
181
216
  When 'existing', connect to an existing Femtet process.
@@ -213,7 +248,8 @@ class FemtetInterface(FEMInterface):
213
248
  print('================')
214
249
  print(message)
215
250
  print('================')
216
- input('Press enter to finish...')
251
+ if self.confirm_before_exit:
252
+ input('Press enter to finish...')
217
253
  raise RuntimeError(message)
218
254
 
219
255
  if self.Femtet is None:
@@ -338,11 +374,11 @@ class FemtetInterface(FEMInterface):
338
374
  # さらに、プロジェクトツリーが開いていないとアクティブ化イベントも意味がないらしい。
339
375
  if fun.__name__ == 'ReExecute':
340
376
  if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
341
- post_activate_message(self.Femtet.hWnd)
377
+ _post_activate_message(self.Femtet.hWnd)
342
378
  # API を実行
343
379
  returns = fun(*args, **kwargs) # can raise pywintypes.error
344
380
  if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
345
- post_activate_message(self.Femtet.hWnd)
381
+ _post_activate_message(self.Femtet.hWnd)
346
382
  else:
347
383
  returns = fun(*args, **kwargs)
348
384
 
@@ -504,7 +540,8 @@ class FemtetInterface(FEMInterface):
504
540
  logger.error(Msg.ERR_CANNOT_ACCESS_API + 'GetVariableNames_py')
505
541
  logger.error(Msg.CERTIFY_MACRO_VERSION)
506
542
  print('================')
507
- input(Msg.ENTER_TO_QUIT)
543
+ if self.confirm_before_exit:
544
+ input(Msg.ENTER_TO_QUIT)
508
545
  raise e
509
546
 
510
547
  if variable_names is not None:
@@ -515,7 +552,8 @@ class FemtetInterface(FEMInterface):
515
552
  logger.error(message)
516
553
  logger.error(f'`{param_name}` not in {variable_names}')
517
554
  print('================')
518
- input(Msg.ENTER_TO_QUIT)
555
+ if self.confirm_before_exit:
556
+ input(Msg.ENTER_TO_QUIT)
519
557
  raise RuntimeError(message)
520
558
  else:
521
559
  return None
@@ -670,9 +708,25 @@ class FemtetInterface(FEMInterface):
670
708
  self.postprocess(self.Femtet)
671
709
 
672
710
  def preprocess(self, Femtet):
711
+ """A method called just before :func:`solve`.
712
+
713
+ This method is called just before solve.
714
+ By inheriting from the Interface class
715
+ and overriding this method, it is possible
716
+ to perform any desired preprocessing after
717
+ the model update and before solving.
718
+ """
673
719
  pass
674
720
 
675
721
  def postprocess(self, Femtet):
722
+ """A method called just after :func:`solve`.
723
+
724
+ This method is called just after solve.
725
+ By inheriting from the Interface class
726
+ and overriding this method, it is possible
727
+ to perform any desired postprocessing after
728
+ the solve and before evaluating objectives.
729
+ """
676
730
  pass
677
731
 
678
732
  def quit(self, timeout=1, force=True):
@@ -692,7 +746,8 @@ class FemtetInterface(FEMInterface):
692
746
  logger.error(Msg.ERR_CANNOT_ACCESS_API + 'Femtet.Exit()')
693
747
  logger.error(Msg.CERTIFY_MACRO_VERSION)
694
748
  print('================')
695
- input(Msg.ENTER_TO_QUIT)
749
+ if self.confirm_before_exit:
750
+ input(Msg.ENTER_TO_QUIT)
696
751
  raise e
697
752
 
698
753
  else:
@@ -723,9 +778,9 @@ class FemtetInterface(FEMInterface):
723
778
  def _version(self):
724
779
  return _version(Femtet=self.Femtet)
725
780
 
726
- def create_postprocess_args(self):
727
- file_content = self.create_result_file_content()
728
- jpg_content = self.create_jpg_content()
781
+ def _create_postprocess_args(self):
782
+ file_content = self._create_result_file_content()
783
+ jpg_content = self._create_jpg_content()
729
784
 
730
785
  out = dict(
731
786
  original_femprj_path=self.original_femprj_path,
@@ -736,14 +791,14 @@ class FemtetInterface(FEMInterface):
736
791
  return out
737
792
 
738
793
  @staticmethod
739
- def create_pdt_path(femprj_path, model_name, trial):
794
+ def _create_pdt_path(femprj_path, model_name, trial):
740
795
  result_dir = femprj_path.replace('.femprj', '.Results')
741
796
  pdt_path = os.path.join(result_dir, model_name + f'_trial{trial}.pdt')
742
797
  return pdt_path
743
798
 
744
799
  # noinspection PyMethodOverriding
745
800
  @staticmethod
746
- def postprocess_func(
801
+ def _postprocess_func(
747
802
  trial: int,
748
803
  original_femprj_path: str,
749
804
  model_name: str,
@@ -753,7 +808,7 @@ class FemtetInterface(FEMInterface):
753
808
  ):
754
809
  result_dir = original_femprj_path.replace('.femprj', '.Results')
755
810
  if pdt_file_content is not None:
756
- pdt_path = FemtetInterface.create_pdt_path(original_femprj_path, model_name, trial)
811
+ pdt_path = FemtetInterface._create_pdt_path(original_femprj_path, model_name, trial)
757
812
  with open(pdt_path, 'wb') as f:
758
813
  f.write(pdt_file_content)
759
814
 
@@ -762,7 +817,7 @@ class FemtetInterface(FEMInterface):
762
817
  with open(jpg_path, 'wb') as f:
763
818
  f.write(jpg_file_content)
764
819
 
765
- def create_result_file_content(self):
820
+ def _create_result_file_content(self):
766
821
  """Called after solve"""
767
822
  if self.save_pdt == 'all':
768
823
  # save to worker space
@@ -786,7 +841,7 @@ class FemtetInterface(FEMInterface):
786
841
  else:
787
842
  return None
788
843
 
789
- def create_jpg_content(self):
844
+ def _create_jpg_content(self):
790
845
  result_dir = self.femprj_path.replace('.femprj', '.Results')
791
846
  jpg_path = os.path.join(result_dir, self.model_name + '.jpg')
792
847
 
@@ -860,35 +915,37 @@ class _UnPicklableNoFEM(FemtetInterface):
860
915
 
861
916
 
862
917
  # レジストリのパスと値の名前
863
- REGISTRY_PATH: Final[str] = r"SOFTWARE\Murata Software\Femtet2014\Femtet"
864
- VALUE_NAME: Final[str] = "AutoSave"
918
+ _REGISTRY_PATH: Final[str] = r"SOFTWARE\Murata Software\Femtet2014\Femtet"
919
+ _VALUE_NAME: Final[str] = "AutoSave"
865
920
 
866
- def get_autosave_enabled() -> bool:
867
- with winreg.OpenKey(winreg.HKEY_CURRENT_USER, REGISTRY_PATH) as key:
868
- value, regtype = winreg.QueryValueEx(key, VALUE_NAME)
921
+
922
+ def _get_autosave_enabled() -> bool:
923
+ with winreg.OpenKey(winreg.HKEY_CURRENT_USER, _REGISTRY_PATH) as key:
924
+ value, regtype = winreg.QueryValueEx(key, _VALUE_NAME)
869
925
  if regtype == winreg.REG_DWORD:
870
926
  return bool(value)
871
927
  else:
872
928
  raise ValueError("Unexpected registry value type.")
873
929
 
874
- def set_autosave_enabled(enable: bool) -> None:
875
- with winreg.OpenKey(winreg.HKEY_CURRENT_USER, REGISTRY_PATH, 0, winreg.KEY_SET_VALUE) as key:
876
- winreg.SetValueEx(key, VALUE_NAME, 0, winreg.REG_DWORD, int(enable))
930
+
931
+ def _set_autosave_enabled(enable: bool) -> None:
932
+ with winreg.OpenKey(winreg.HKEY_CURRENT_USER, _REGISTRY_PATH, 0, winreg.KEY_SET_VALUE) as key:
933
+ winreg.SetValueEx(key, _VALUE_NAME, 0, winreg.REG_DWORD, int(enable))
877
934
 
878
935
 
879
936
  def _test_autosave_setting():
880
937
 
881
938
  # 使用例
882
- current_setting = get_autosave_enabled()
939
+ current_setting = _get_autosave_enabled()
883
940
  print(f"Current AutoSave setting is {'enabled' if current_setting else 'disabled'}.")
884
941
 
885
942
  # 設定を変更する例
886
943
  new_setting = not current_setting
887
- set_autosave_enabled(new_setting)
944
+ _set_autosave_enabled(new_setting)
888
945
  print(f"AutoSave setting has been {'enabled' if new_setting else 'disabled'}.")
889
946
 
890
947
  # 再度設定を確認する
891
- after_setting = get_autosave_enabled()
948
+ after_setting = _get_autosave_enabled()
892
949
  print(f"Current AutoSave setting is {'enabled' if after_setting else 'disabled'}.")
893
950
 
894
951
  assert new_setting == after_setting, "レジストリ編集に失敗しました。"
@@ -7,23 +7,47 @@ from dask.distributed import get_worker
7
7
 
8
8
  from pyfemtet.core import ModelError
9
9
  from pyfemtet.opt.interface import FemtetInterface, logger
10
- from pyfemtet.message import Msg
10
+ from pyfemtet._message import Msg
11
11
 
12
12
 
13
13
  here, me = os.path.split(__file__)
14
14
 
15
15
 
16
16
  class FemtetWithNXInterface(FemtetInterface):
17
- """Femtet with NX interface class.
17
+ """Control Femtet and NX.
18
18
 
19
- Args:
20
- prt_path: The path to the prt file.
21
- export_curves(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
22
- export_surfaces(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
23
- export_solids(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
24
- export_flattened_assembly(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
19
+ Using this class, you can import CAD files created
20
+ in NX through the Parasolid format into a
21
+ Femtet project. It allows you to pass design
22
+ variables to NX, update the model, and
23
+ perform analysis using the updated model in Femtet.
25
24
 
26
- For details of The other arguments, see ``FemtetInterface``.
25
+ Args:
26
+ prt_path (str):
27
+ The path to .prt file containing the
28
+ CAD data from which the import is made.
29
+
30
+ export_curves(bool or None, optional):
31
+ Defaults to None.
32
+ export_surfaces(bool or None, optional):
33
+ Defaults to None.
34
+ export_solids(bool or None, optional):
35
+ Defaults to None.
36
+ export_flattened_assembly(bool or None, optional):
37
+ Defaults to None.
38
+ **kwargs:
39
+ For other arguments, please refer to the
40
+ :class:`FemtetInterface` class.
41
+
42
+ Notes:
43
+ ```export_*``` arguments sets
44
+ parasolid export setting of NX.
45
+ If None, PyFemtet does not change
46
+ the current setting of NX.
47
+
48
+ It is recommended not to change these values
49
+ from the settings used when exporting the
50
+ Parasolid that was imported into Femtet.
27
51
 
28
52
  """
29
53
 
@@ -84,7 +108,7 @@ class FemtetWithNXInterface(FemtetInterface):
84
108
  )
85
109
  super()._setup_before_parallel(client)
86
110
 
87
- def update_model(self, parameters: 'pd.DataFrame') -> None:
111
+ def update_model(self, parameters: 'pd.DataFrame', with_warning=False) -> None:
88
112
  """Update .x_t"""
89
113
 
90
114
  self.parameters = parameters.copy()
@@ -151,5 +175,4 @@ class FemtetWithNXInterface(FemtetInterface):
151
175
  )
152
176
 
153
177
  # femprj モデルの変数も更新
154
- super().update_model(parameters)
155
-
178
+ super().update_model(parameters, with_warning=False)