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.
- pyfemtet/__init__.py +1 -1
- pyfemtet/{message → _message}/locales/ja/LC_MESSAGES/messages.po +89 -77
- pyfemtet/{message → _message}/locales/messages.pot +88 -76
- pyfemtet/{message → _message}/messages.py +1 -1
- pyfemtet/_warning.py +23 -0
- pyfemtet/dispatch_extensions/__init__.py +12 -0
- pyfemtet/{dispatch_extensions.py → dispatch_extensions/_impl.py} +45 -43
- pyfemtet/logger/__init__.py +3 -0
- pyfemtet/{logger.py → logger/_impl.py} +12 -6
- pyfemtet/opt/__init__.py +3 -0
- pyfemtet/opt/_femopt.py +265 -68
- pyfemtet/opt/_femopt_core.py +111 -68
- pyfemtet/opt/_test_utils/record_history.py +1 -1
- pyfemtet/opt/interface/__init__.py +0 -1
- pyfemtet/opt/interface/_base.py +3 -3
- pyfemtet/opt/interface/_femtet.py +116 -59
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -12
- pyfemtet/opt/interface/_femtet_with_sldworks.py +22 -2
- pyfemtet/opt/optimizer/__init__.py +5 -1
- pyfemtet/opt/optimizer/_base.py +81 -55
- pyfemtet/opt/optimizer/{_optuna_botorchsampler_parameter_constraint_helper.py → _optuna/_botorch_patch/enable_nonlinear_constraint.py} +10 -127
- pyfemtet/opt/optimizer/{_optuna.py → _optuna/_optuna.py} +122 -19
- pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +1833 -0
- pyfemtet/opt/optimizer/_scipy.py +20 -5
- pyfemtet/opt/optimizer/_scipy_scalar.py +20 -5
- pyfemtet/opt/prediction/{base.py → _base.py} +3 -2
- pyfemtet/opt/prediction/single_task_gp.py +10 -5
- pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +2 -2
- pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +2 -2
- pyfemtet/opt/visualization/{base.py → _base.py} +1 -1
- pyfemtet/opt/visualization/{complex_components → _complex_components}/alert_region.py +2 -2
- pyfemtet/opt/visualization/{complex_components → _complex_components}/control_femtet.py +3 -3
- pyfemtet/opt/visualization/{complex_components → _complex_components}/main_figure_creator.py +1 -1
- pyfemtet/opt/visualization/{complex_components → _complex_components}/main_graph.py +5 -5
- pyfemtet/opt/visualization/{complex_components → _complex_components}/pm_graph.py +5 -5
- pyfemtet/opt/visualization/{complex_components → _complex_components}/pm_graph_creator.py +2 -2
- pyfemtet/opt/visualization/_create_wrapped_components.py +2 -2
- pyfemtet/opt/visualization/_process_monitor/__init__.py +0 -0
- pyfemtet/opt/visualization/{process_monitor → _process_monitor}/application.py +3 -3
- pyfemtet/opt/visualization/{process_monitor → _process_monitor}/pages.py +10 -10
- pyfemtet/opt/visualization/_wrapped_components/__init__.py +0 -0
- pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/dbc.py +1 -1
- pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/dcc.py +1 -1
- pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/html.py +1 -1
- pyfemtet/opt/visualization/result_viewer/application.py +4 -4
- pyfemtet/opt/visualization/result_viewer/pages.py +9 -9
- {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/METADATA +2 -2
- {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/RECORD +60 -56
- {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/WHEEL +1 -1
- pyfemtet/message/locales/ja/LC_MESSAGES/messages.mo +0 -0
- pyfemtet/opt/samples/femprj_sample/.gitignore +0 -2
- /pyfemtet/{message → _message}/1. make_pot.bat +0 -0
- /pyfemtet/{message → _message}/2. make_mo.bat +0 -0
- /pyfemtet/{message → _message}/__init__.py +0 -0
- /pyfemtet/{message → _message}/babel.cfg +0 -0
- /pyfemtet/opt/{visualization/complex_components → optimizer/_optuna}/__init__.py +0 -0
- /pyfemtet/opt/{visualization/process_monitor → optimizer/_optuna/_botorch_patch}/__init__.py +0 -0
- /pyfemtet/opt/{parameter.py → optimizer/parameter.py} +0 -0
- /pyfemtet/opt/visualization/{wrapped_components → _complex_components}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/str_enum.py +0 -0
- {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/LICENSE +0 -0
- {pyfemtet-0.5.3.dist-info → pyfemtet-0.6.0.dist-info}/entry_points.txt +0 -0
pyfemtet/opt/_femopt_core.py
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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 =
|
|
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'] =
|
|
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
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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."""
|
pyfemtet/opt/interface/_base.py
CHANGED
|
@@ -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
|
|
71
|
+
def _postprocess_func(self, trial: int, *args, dask_scheduler=None, **kwargs):
|
|
72
72
|
pass
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def _create_postprocess_args(self):
|
|
75
75
|
pass
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
class NoFEM(FEMInterface):
|
|
79
|
-
"""
|
|
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.
|
|
37
|
+
from pyfemtet._message import Msg
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def
|
|
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
|
-
"""
|
|
45
|
+
"""Control Femtet from optimizer.
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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 =
|
|
106
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
727
|
-
file_content = self.
|
|
728
|
-
jpg_content = self.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
864
|
-
|
|
918
|
+
_REGISTRY_PATH: Final[str] = r"SOFTWARE\Murata Software\Femtet2014\Femtet"
|
|
919
|
+
_VALUE_NAME: Final[str] = "AutoSave"
|
|
865
920
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
-
|
|
875
|
-
|
|
876
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
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
|
|
17
|
+
"""Control Femtet and NX.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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)
|