pyfemtet 0.5.2__py3-none-any.whl → 0.5.4__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 +88 -76
- 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/_femopt.py +236 -58
- pyfemtet/opt/_femopt_core.py +21 -8
- 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 +101 -55
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -12
- pyfemtet/opt/interface/_femtet_with_sldworks.py +22 -2
- pyfemtet/opt/optimizer/_base.py +76 -42
- pyfemtet/opt/optimizer/_optuna.py +33 -1
- pyfemtet/opt/optimizer/_optuna_botorchsampler_parameter_constraint_helper.py +1 -2
- 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/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 → _process_monitor}/application.py +3 -3
- pyfemtet/opt/visualization/{process_monitor → _process_monitor}/pages.py +10 -10
- 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.2.dist-info → pyfemtet-0.5.4.dist-info}/METADATA +2 -2
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/RECORD +53 -51
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/WHEEL +1 -1
- pyfemtet/message/locales/ja/LC_MESSAGES/messages.mo +0 -0
- /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/{parameter.py → optimizer/parameter.py} +0 -0
- /pyfemtet/opt/visualization/{complex_components → _complex_components}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{process_monitor → _process_monitor}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/str_enum.py +0 -0
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/LICENSE +0 -0
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/entry_points.txt +0 -0
|
@@ -34,47 +34,75 @@ 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
|
+
**kwargs: Additional arguments from inherited classes.
|
|
82
|
+
|
|
83
|
+
Warning:
|
|
84
|
+
Even if you specify ``strictly_pid_specify=True`` on the constructor,
|
|
85
|
+
**the connection behavior is like** ``strictly_pid_specify=False`` **in parallel processing**
|
|
86
|
+
because of its large overhead.
|
|
87
|
+
So you should close all Femtet processes before running FEMOpt.optimize()
|
|
88
|
+
if ``n_parallel`` >= 2.
|
|
89
|
+
|
|
90
|
+
Tip:
|
|
91
|
+
If you search for information about the method to
|
|
92
|
+
connect python and Femtet, see :func:`connect_femtet`.
|
|
65
93
|
|
|
66
94
|
"""
|
|
67
95
|
|
|
68
96
|
def __init__(
|
|
69
97
|
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,
|
|
98
|
+
femprj_path: str = None,
|
|
99
|
+
model_name: str = None,
|
|
100
|
+
connect_method: str = 'auto', # dask worker では __init__ の中で 'new' にするので super() の引数にしない。(しても意味がない)
|
|
101
|
+
save_pdt: str = 'all', # 'all' or None
|
|
102
|
+
strictly_pid_specify: bool = True, # dask worker では True にしたいので super() の引数にしない。
|
|
103
|
+
allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
|
|
104
|
+
open_result_with_gui: bool = True,
|
|
105
|
+
parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
|
|
78
106
|
**kwargs # 継承されたクラスからの引数
|
|
79
107
|
):
|
|
80
108
|
|
|
@@ -102,8 +130,8 @@ class FemtetInterface(FEMInterface):
|
|
|
102
130
|
self.max_api_retry = 3
|
|
103
131
|
self.strictly_pid_specify = strictly_pid_specify
|
|
104
132
|
self.parametric_output_indexes_use_as_objective = parametric_output_indexes_use_as_objective
|
|
105
|
-
self._original_autosave_enabled =
|
|
106
|
-
|
|
133
|
+
self._original_autosave_enabled = _get_autosave_enabled()
|
|
134
|
+
_set_autosave_enabled(False)
|
|
107
135
|
|
|
108
136
|
# dask サブプロセスのときは femprj を更新し connect_method を new にする
|
|
109
137
|
try:
|
|
@@ -145,7 +173,7 @@ class FemtetInterface(FEMInterface):
|
|
|
145
173
|
)
|
|
146
174
|
|
|
147
175
|
def __del__(self):
|
|
148
|
-
|
|
176
|
+
_set_autosave_enabled(self._original_autosave_enabled)
|
|
149
177
|
if self.quit_when_destruct:
|
|
150
178
|
self.quit()
|
|
151
179
|
# CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
|
|
@@ -170,12 +198,12 @@ class FemtetInterface(FEMInterface):
|
|
|
170
198
|
"""Connects to a Femtet process.
|
|
171
199
|
|
|
172
200
|
Args:
|
|
173
|
-
connect_method (str, optional): The connection method. Can be 'new', 'existing', or 'auto'. Defaults to '
|
|
201
|
+
connect_method (str, optional): The connection method. Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
|
|
174
202
|
pid (int or None, optional): The process ID of an existing Femtet process and wanted to connect.
|
|
175
203
|
|
|
176
204
|
Note:
|
|
177
205
|
When connect_method is 'new', starts a new Femtet process and connects to it.
|
|
178
|
-
|
|
206
|
+
`pid` will be ignored.
|
|
179
207
|
|
|
180
208
|
Note:
|
|
181
209
|
When 'existing', connect to an existing Femtet process.
|
|
@@ -338,11 +366,11 @@ class FemtetInterface(FEMInterface):
|
|
|
338
366
|
# さらに、プロジェクトツリーが開いていないとアクティブ化イベントも意味がないらしい。
|
|
339
367
|
if fun.__name__ == 'ReExecute':
|
|
340
368
|
if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
|
|
341
|
-
|
|
369
|
+
_post_activate_message(self.Femtet.hWnd)
|
|
342
370
|
# API を実行
|
|
343
371
|
returns = fun(*args, **kwargs) # can raise pywintypes.error
|
|
344
372
|
if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
|
|
345
|
-
|
|
373
|
+
_post_activate_message(self.Femtet.hWnd)
|
|
346
374
|
else:
|
|
347
375
|
returns = fun(*args, **kwargs)
|
|
348
376
|
|
|
@@ -670,9 +698,25 @@ class FemtetInterface(FEMInterface):
|
|
|
670
698
|
self.postprocess(self.Femtet)
|
|
671
699
|
|
|
672
700
|
def preprocess(self, Femtet):
|
|
701
|
+
"""A method called just before :func:`solve`.
|
|
702
|
+
|
|
703
|
+
This method is called just before solve.
|
|
704
|
+
By inheriting from the Interface class
|
|
705
|
+
and overriding this method, it is possible
|
|
706
|
+
to perform any desired preprocessing after
|
|
707
|
+
the model update and before solving.
|
|
708
|
+
"""
|
|
673
709
|
pass
|
|
674
710
|
|
|
675
711
|
def postprocess(self, Femtet):
|
|
712
|
+
"""A method called just after :func:`solve`.
|
|
713
|
+
|
|
714
|
+
This method is called just after solve.
|
|
715
|
+
By inheriting from the Interface class
|
|
716
|
+
and overriding this method, it is possible
|
|
717
|
+
to perform any desired postprocessing after
|
|
718
|
+
the solve and before evaluating objectives.
|
|
719
|
+
"""
|
|
676
720
|
pass
|
|
677
721
|
|
|
678
722
|
def quit(self, timeout=1, force=True):
|
|
@@ -723,9 +767,9 @@ class FemtetInterface(FEMInterface):
|
|
|
723
767
|
def _version(self):
|
|
724
768
|
return _version(Femtet=self.Femtet)
|
|
725
769
|
|
|
726
|
-
def
|
|
727
|
-
file_content = self.
|
|
728
|
-
jpg_content = self.
|
|
770
|
+
def _create_postprocess_args(self):
|
|
771
|
+
file_content = self._create_result_file_content()
|
|
772
|
+
jpg_content = self._create_jpg_content()
|
|
729
773
|
|
|
730
774
|
out = dict(
|
|
731
775
|
original_femprj_path=self.original_femprj_path,
|
|
@@ -736,14 +780,14 @@ class FemtetInterface(FEMInterface):
|
|
|
736
780
|
return out
|
|
737
781
|
|
|
738
782
|
@staticmethod
|
|
739
|
-
def
|
|
783
|
+
def _create_pdt_path(femprj_path, model_name, trial):
|
|
740
784
|
result_dir = femprj_path.replace('.femprj', '.Results')
|
|
741
785
|
pdt_path = os.path.join(result_dir, model_name + f'_trial{trial}.pdt')
|
|
742
786
|
return pdt_path
|
|
743
787
|
|
|
744
788
|
# noinspection PyMethodOverriding
|
|
745
789
|
@staticmethod
|
|
746
|
-
def
|
|
790
|
+
def _postprocess_func(
|
|
747
791
|
trial: int,
|
|
748
792
|
original_femprj_path: str,
|
|
749
793
|
model_name: str,
|
|
@@ -753,7 +797,7 @@ class FemtetInterface(FEMInterface):
|
|
|
753
797
|
):
|
|
754
798
|
result_dir = original_femprj_path.replace('.femprj', '.Results')
|
|
755
799
|
if pdt_file_content is not None:
|
|
756
|
-
pdt_path = FemtetInterface.
|
|
800
|
+
pdt_path = FemtetInterface._create_pdt_path(original_femprj_path, model_name, trial)
|
|
757
801
|
with open(pdt_path, 'wb') as f:
|
|
758
802
|
f.write(pdt_file_content)
|
|
759
803
|
|
|
@@ -762,7 +806,7 @@ class FemtetInterface(FEMInterface):
|
|
|
762
806
|
with open(jpg_path, 'wb') as f:
|
|
763
807
|
f.write(jpg_file_content)
|
|
764
808
|
|
|
765
|
-
def
|
|
809
|
+
def _create_result_file_content(self):
|
|
766
810
|
"""Called after solve"""
|
|
767
811
|
if self.save_pdt == 'all':
|
|
768
812
|
# save to worker space
|
|
@@ -786,7 +830,7 @@ class FemtetInterface(FEMInterface):
|
|
|
786
830
|
else:
|
|
787
831
|
return None
|
|
788
832
|
|
|
789
|
-
def
|
|
833
|
+
def _create_jpg_content(self):
|
|
790
834
|
result_dir = self.femprj_path.replace('.femprj', '.Results')
|
|
791
835
|
jpg_path = os.path.join(result_dir, self.model_name + '.jpg')
|
|
792
836
|
|
|
@@ -860,35 +904,37 @@ class _UnPicklableNoFEM(FemtetInterface):
|
|
|
860
904
|
|
|
861
905
|
|
|
862
906
|
# レジストリのパスと値の名前
|
|
863
|
-
|
|
864
|
-
|
|
907
|
+
_REGISTRY_PATH: Final[str] = r"SOFTWARE\Murata Software\Femtet2014\Femtet"
|
|
908
|
+
_VALUE_NAME: Final[str] = "AutoSave"
|
|
865
909
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
910
|
+
|
|
911
|
+
def _get_autosave_enabled() -> bool:
|
|
912
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, _REGISTRY_PATH) as key:
|
|
913
|
+
value, regtype = winreg.QueryValueEx(key, _VALUE_NAME)
|
|
869
914
|
if regtype == winreg.REG_DWORD:
|
|
870
915
|
return bool(value)
|
|
871
916
|
else:
|
|
872
917
|
raise ValueError("Unexpected registry value type.")
|
|
873
918
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
919
|
+
|
|
920
|
+
def _set_autosave_enabled(enable: bool) -> None:
|
|
921
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, _REGISTRY_PATH, 0, winreg.KEY_SET_VALUE) as key:
|
|
922
|
+
winreg.SetValueEx(key, _VALUE_NAME, 0, winreg.REG_DWORD, int(enable))
|
|
877
923
|
|
|
878
924
|
|
|
879
925
|
def _test_autosave_setting():
|
|
880
926
|
|
|
881
927
|
# 使用例
|
|
882
|
-
current_setting =
|
|
928
|
+
current_setting = _get_autosave_enabled()
|
|
883
929
|
print(f"Current AutoSave setting is {'enabled' if current_setting else 'disabled'}.")
|
|
884
930
|
|
|
885
931
|
# 設定を変更する例
|
|
886
932
|
new_setting = not current_setting
|
|
887
|
-
|
|
933
|
+
_set_autosave_enabled(new_setting)
|
|
888
934
|
print(f"AutoSave setting has been {'enabled' if new_setting else 'disabled'}.")
|
|
889
935
|
|
|
890
936
|
# 再度設定を確認する
|
|
891
|
-
after_setting =
|
|
937
|
+
after_setting = _get_autosave_enabled()
|
|
892
938
|
print(f"Current AutoSave setting is {'enabled' if after_setting else 'disabled'}.")
|
|
893
939
|
|
|
894
940
|
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)
|
|
@@ -10,10 +10,30 @@ from pythoncom import CoInitialize, CoUninitialize
|
|
|
10
10
|
|
|
11
11
|
from pyfemtet.core import ModelError
|
|
12
12
|
from pyfemtet.opt.interface import FemtetInterface, logger
|
|
13
|
-
from pyfemtet.
|
|
13
|
+
from pyfemtet._message import Msg
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class FemtetWithSolidworksInterface(FemtetInterface):
|
|
17
|
+
"""Control Femtet and Solidworks.
|
|
18
|
+
|
|
19
|
+
Using this class, you can import CAD files created
|
|
20
|
+
in Solidworks through the Parasolid format into a
|
|
21
|
+
Femtet project. It allows you to pass design
|
|
22
|
+
variables to Solidworks, update the model, and
|
|
23
|
+
perform analysis using the updated model in Femtet.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
sldprt_path (str):
|
|
28
|
+
The path to .sldprt file containing the
|
|
29
|
+
CAD data from which the import is made.
|
|
30
|
+
**kwargs:
|
|
31
|
+
For other arguments, please refer to the
|
|
32
|
+
:class:`FemtetInterface` class.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
17
37
|
# 定数の宣言
|
|
18
38
|
swThisConfiguration = 1 # https://help.solidworks.com/2023/english/api/swconst/SOLIDWORKS.Interop.swconst~SOLIDWORKS.Interop.swconst.swInConfigurationOpts_e.html
|
|
19
39
|
swAllConfiguration = 2
|
|
@@ -76,7 +96,7 @@ class FemtetWithSolidworksInterface(FemtetInterface):
|
|
|
76
96
|
CoInitialize()
|
|
77
97
|
self.initialize_sldworks_connection()
|
|
78
98
|
|
|
79
|
-
def update_model(self, parameters: pd.DataFrame):
|
|
99
|
+
def update_model(self, parameters: pd.DataFrame, with_warning=False):
|
|
80
100
|
"""Update .x_t"""
|
|
81
101
|
|
|
82
102
|
self.parameters = parameters.copy()
|
pyfemtet/opt/optimizer/_base.py
CHANGED
|
@@ -8,13 +8,12 @@ from time import sleep
|
|
|
8
8
|
|
|
9
9
|
# 3rd-party
|
|
10
10
|
import numpy as np
|
|
11
|
-
import pandas as pd
|
|
12
11
|
|
|
13
12
|
# pyfemtet relative
|
|
14
|
-
from pyfemtet.opt.interface import
|
|
13
|
+
from pyfemtet.opt.interface import FEMInterface
|
|
15
14
|
from pyfemtet.opt._femopt_core import OptimizationStatus, Objective, Constraint
|
|
16
|
-
from pyfemtet.
|
|
17
|
-
from pyfemtet.opt.parameter import ExpressionEvaluator
|
|
15
|
+
from pyfemtet._message import Msg
|
|
16
|
+
from pyfemtet.opt.optimizer.parameter import ExpressionEvaluator, Parameter
|
|
18
17
|
|
|
19
18
|
# logger
|
|
20
19
|
import logging
|
|
@@ -117,17 +116,11 @@ class AbstractOptimizer(ABC):
|
|
|
117
116
|
fem (FEMInterface): The finite element method object.
|
|
118
117
|
fem_class (type): The class of the finite element method object.
|
|
119
118
|
fem_kwargs (dict): The keyword arguments used to instantiate the finite element method object.
|
|
120
|
-
|
|
121
|
-
objectives (dict): A dictionary containing the objective functions used in the optimization.
|
|
122
|
-
constraints (dict): A dictionary containing the constraint functions used in the optimization.
|
|
123
|
-
entire_status (OptimizationStatus): The status of the entire optimization process.
|
|
119
|
+
variables (ExpressionEvaluator): The variables using optimization process including parameters.
|
|
120
|
+
objectives (dict[str, Objective]): A dictionary containing the objective functions used in the optimization.
|
|
121
|
+
constraints (dict[str, Constraint]): A dictionary containing the constraint functions used in the optimization.
|
|
124
122
|
history (History): An actor object that records the history of each iteration in the optimization process.
|
|
125
|
-
worker_status (OptimizationStatus): The status of each worker in a distributed computing environment.
|
|
126
|
-
message (str): A message associated with the current state of the optimization process.
|
|
127
123
|
seed (int or None): The random seed used for random number generation during the optimization process.
|
|
128
|
-
timeout (float or int or None): The maximum time allowed for each iteration of the optimization process. If exceeded, it will be interrupted and terminated early.
|
|
129
|
-
n_trials (int or None): The maximum number of trials allowed for each iteration of the optimization process. If exceeded, it will be interrupted and terminated early.
|
|
130
|
-
is_cluster (bool): Flag indicating if running on a distributed computing cluster.
|
|
131
124
|
|
|
132
125
|
"""
|
|
133
126
|
|
|
@@ -150,9 +143,33 @@ class AbstractOptimizer(ABC):
|
|
|
150
143
|
self._exception = None
|
|
151
144
|
self.method_checker: OptimizationMethodChecker = OptimizationMethodChecker(self)
|
|
152
145
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
# ===== algorithm specific methods =====
|
|
147
|
+
@abstractmethod
|
|
148
|
+
def run(self) -> None:
|
|
149
|
+
"""Start optimization."""
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
# ----- FEMOpt interfaces -----
|
|
153
|
+
@abstractmethod
|
|
154
|
+
def _setup_before_parallel(self, *args, **kwargs):
|
|
155
|
+
"""Setup before parallel processes are launched."""
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
# ===== calc =====
|
|
159
|
+
def f(self, x: np.ndarray) -> list[np.ndarray]:
|
|
160
|
+
"""Calculate objectives and constraints.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
x (np.ndarray): Optimization parameters.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
list[np.ndarray]:
|
|
167
|
+
The list of internal objective values,
|
|
168
|
+
un-normalized objective values and
|
|
169
|
+
constraint values.
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
|
|
156
173
|
|
|
157
174
|
if isinstance(x, np.float64):
|
|
158
175
|
x = np.array([x])
|
|
@@ -202,8 +219,8 @@ class AbstractOptimizer(ABC):
|
|
|
202
219
|
y,
|
|
203
220
|
c,
|
|
204
221
|
self.message,
|
|
205
|
-
postprocess_func=self.fem.
|
|
206
|
-
postprocess_args=self.fem.
|
|
222
|
+
postprocess_func=self.fem._postprocess_func,
|
|
223
|
+
postprocess_args=self.fem._create_postprocess_args(),
|
|
207
224
|
)
|
|
208
225
|
|
|
209
226
|
logger.debug('history.record end')
|
|
@@ -212,26 +229,21 @@ class AbstractOptimizer(ABC):
|
|
|
212
229
|
|
|
213
230
|
return np.array(y), np.array(_y), np.array(c)
|
|
214
231
|
|
|
215
|
-
|
|
216
|
-
"""Reconstruct FEMInterface in a subprocess."""
|
|
217
|
-
# reconstruct fem
|
|
218
|
-
if not skip_reconstruct:
|
|
219
|
-
self.fem = self.fem_class(**self.fem_kwargs)
|
|
220
|
-
|
|
221
|
-
# COM 定数の restore
|
|
222
|
-
for obj in self.objectives.values():
|
|
223
|
-
obj._restore_constants()
|
|
224
|
-
for cns in self.constraints.values():
|
|
225
|
-
cns._restore_constants()
|
|
226
|
-
|
|
232
|
+
# ===== parameter processing =====
|
|
227
233
|
def get_parameter(self, format='dict'):
|
|
228
234
|
"""Returns the parameters in the specified format.
|
|
229
235
|
|
|
230
236
|
Args:
|
|
231
|
-
format (str, optional):
|
|
237
|
+
format (str, optional):
|
|
238
|
+
The desired format of the parameters.
|
|
239
|
+
Can be 'df' (DataFrame),
|
|
240
|
+
'values' (np.ndarray),
|
|
241
|
+
'dict' or
|
|
242
|
+
'raw' (list of Variable object).
|
|
243
|
+
Defaults to 'dict'.
|
|
232
244
|
|
|
233
245
|
Returns:
|
|
234
|
-
|
|
246
|
+
The parameters in the specified format.
|
|
235
247
|
|
|
236
248
|
Raises:
|
|
237
249
|
ValueError: If an invalid format is provided.
|
|
@@ -239,11 +251,14 @@ class AbstractOptimizer(ABC):
|
|
|
239
251
|
"""
|
|
240
252
|
return self.variables.get_variables(format=format, filter_parameter=True)
|
|
241
253
|
|
|
242
|
-
def set_parameter(self, params: dict) -> None:
|
|
254
|
+
def set_parameter(self, params: dict[str, float]) -> None:
|
|
243
255
|
"""Update parameter.
|
|
244
256
|
|
|
245
257
|
Args:
|
|
246
|
-
params (dict):
|
|
258
|
+
params (dict):
|
|
259
|
+
Key is the name of parameter and
|
|
260
|
+
the value is the value of it.
|
|
261
|
+
The partial set is available.
|
|
247
262
|
|
|
248
263
|
"""
|
|
249
264
|
for name, value in params.items():
|
|
@@ -255,11 +270,25 @@ class AbstractOptimizer(ABC):
|
|
|
255
270
|
|
|
256
271
|
Args:
|
|
257
272
|
values (np.ndarray): Values of all parameters.
|
|
273
|
+
|
|
258
274
|
"""
|
|
259
275
|
prm_names = self.variables.get_parameter_names()
|
|
260
276
|
assert len(values) == len(prm_names)
|
|
261
277
|
self.set_parameter({k: v for k, v in zip(prm_names, values)})
|
|
262
278
|
|
|
279
|
+
# ===== FEMOpt interfaces =====
|
|
280
|
+
def _reconstruct_fem(self, skip_reconstruct=False):
|
|
281
|
+
"""Reconstruct FEMInterface in a subprocess."""
|
|
282
|
+
# reconstruct fem
|
|
283
|
+
if not skip_reconstruct:
|
|
284
|
+
self.fem = self.fem_class(**self.fem_kwargs)
|
|
285
|
+
|
|
286
|
+
# COM 定数の restore
|
|
287
|
+
for obj in self.objectives.values():
|
|
288
|
+
obj._restore_constants()
|
|
289
|
+
for cns in self.constraints.values():
|
|
290
|
+
cns._restore_constants()
|
|
291
|
+
|
|
263
292
|
def _check_interruption(self):
|
|
264
293
|
""""""
|
|
265
294
|
if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
@@ -275,6 +304,7 @@ class AbstractOptimizer(ABC):
|
|
|
275
304
|
if not self.worker_status.get() == OptimizationStatus.CRASHED:
|
|
276
305
|
self.worker_status.set(OptimizationStatus.TERMINATED)
|
|
277
306
|
|
|
307
|
+
# run via FEMOpt (considering parallel processing)
|
|
278
308
|
def _run(
|
|
279
309
|
self,
|
|
280
310
|
subprocess_idx,
|
|
@@ -331,12 +361,16 @@ class AbstractOptimizer(ABC):
|
|
|
331
361
|
|
|
332
362
|
return self._exception
|
|
333
363
|
|
|
334
|
-
@abstractmethod
|
|
335
|
-
def run(self) -> None:
|
|
336
|
-
"""Start calculation using optimization library."""
|
|
337
|
-
pass
|
|
338
364
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
pass
|
|
365
|
+
if __name__ == '__main__':
|
|
366
|
+
class Optimizer(AbstractOptimizer):
|
|
367
|
+
def run(self): pass
|
|
368
|
+
def _setup_before_parallel(self, *args, **kwargs): pass
|
|
369
|
+
|
|
370
|
+
opt = Optimizer()
|
|
371
|
+
opt.set_parameter(
|
|
372
|
+
dict(
|
|
373
|
+
prm1=0.,
|
|
374
|
+
prm2=1.,
|
|
375
|
+
)
|
|
376
|
+
)
|
|
@@ -14,7 +14,7 @@ from optuna.study import MaxTrialsCallback
|
|
|
14
14
|
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs, Constraint
|
|
15
15
|
from pyfemtet.opt.optimizer import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
16
16
|
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
17
|
-
from pyfemtet.
|
|
17
|
+
from pyfemtet._message import Msg
|
|
18
18
|
|
|
19
19
|
# filter warnings
|
|
20
20
|
import warnings
|
|
@@ -37,6 +37,38 @@ class OptunaMethodChecker(OptimizationMethodChecker):
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class OptunaOptimizer(AbstractOptimizer):
|
|
40
|
+
"""Optimizer using ```optuna```.
|
|
41
|
+
|
|
42
|
+
This class provides an interface for the optimization
|
|
43
|
+
engine using Optuna. For more details, please refer to
|
|
44
|
+
the Optuna documentation.
|
|
45
|
+
|
|
46
|
+
See Also:
|
|
47
|
+
https://optuna.readthedocs.io/en/stable/reference/index.html
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
sampler_class (optuna.samplers.BaseSampler, optional):
|
|
51
|
+
A sampler class from Optuna. If not specified,
|
|
52
|
+
```optuna.samplers.TPESampler``` is specified.
|
|
53
|
+
This defines the sampling strategy used during
|
|
54
|
+
optimization. Defaults to None.
|
|
55
|
+
sampler_kwargs (dict, optional):
|
|
56
|
+
A dictionary of keyword arguments to be passed to
|
|
57
|
+
the sampler class. This allows for customization
|
|
58
|
+
of the sampling process. Defaults to None.
|
|
59
|
+
add_init_method (str or Iterable[str], optional):
|
|
60
|
+
A method or a collection of methods to be added
|
|
61
|
+
during initialization. This can be used to specify
|
|
62
|
+
additional setup procedures.
|
|
63
|
+
Currently, the only valid value is 'LHS'
|
|
64
|
+
(using Latin Hypercube Sampling).
|
|
65
|
+
Defaults to None.
|
|
66
|
+
|
|
67
|
+
Warnings:
|
|
68
|
+
Do not include ```constraints_func``` in ```sampler_kwargs```.
|
|
69
|
+
It is generated and provided by :func:`FEMOpt.add_constraint`.
|
|
70
|
+
|
|
71
|
+
"""
|
|
40
72
|
|
|
41
73
|
def __init__(
|
|
42
74
|
self,
|
|
@@ -15,7 +15,7 @@ from botorch.optim.initializers import gen_batch_initial_conditions
|
|
|
15
15
|
|
|
16
16
|
from pyfemtet.opt._femopt_core import Constraint
|
|
17
17
|
from pyfemtet.opt.optimizer import OptunaOptimizer, logger
|
|
18
|
-
from pyfemtet.
|
|
18
|
+
from pyfemtet._message import Msg
|
|
19
19
|
|
|
20
20
|
from time import time
|
|
21
21
|
|
|
@@ -183,7 +183,6 @@ class NonlinearInequalityConstraints:
|
|
|
183
183
|
item = (lambda x: GeneralFunctionWithForwardDifference.apply(cns_botorch, x), True)
|
|
184
184
|
self._nonlinear_inequality_constraints.append(item)
|
|
185
185
|
|
|
186
|
-
|
|
187
186
|
def _filter_feasible_conditions(self, ic_batch):
|
|
188
187
|
# List to store feasible initial conditions
|
|
189
188
|
feasible_ic_list = []
|