pyfemtet 0.1.12__py3-none-any.whl → 0.2.1__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/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
- pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
- pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +69 -32
- pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +37 -25
- pyfemtet/FemtetPJTSample/her_ex40_parametric.py +57 -35
- pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +62 -0
- pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +61 -0
- pyfemtet/__init__.py +1 -1
- pyfemtet/opt/_FemtetWithNX/update_model.py +6 -2
- pyfemtet/opt/__init__.py +1 -1
- pyfemtet/opt/base.py +457 -86
- pyfemtet/opt/core.py +77 -17
- pyfemtet/opt/interface.py +217 -137
- pyfemtet/opt/monitor.py +181 -98
- pyfemtet/opt/{_optuna.py → optimizer.py} +70 -30
- pyfemtet/tools/DispatchUtils.py +46 -44
- {pyfemtet-0.1.12.dist-info → pyfemtet-0.2.1.dist-info}/LICENSE +1 -1
- pyfemtet-0.2.1.dist-info/METADATA +42 -0
- pyfemtet-0.2.1.dist-info/RECORD +31 -0
- pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01 - original.x_t +0 -359
- pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.x_t +0 -359
- pyfemtet/FemtetPJTSample/fem4 = Femtet(femprj_path=None, model_name=None, connect_method='catch').femprj +0 -0
- pyfemtet/FemtetPJTSample/gal_ex11_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/gal_ex11_parametric.py +0 -54
- pyfemtet/FemtetPJTSample/pas_ex1_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/pas_ex1_parametric.py +0 -66
- pyfemtet/FemtetPJTSample/pas_ex1_parametric2.py +0 -68
- pyfemtet/tools/FemtetClassConst.py +0 -9
- pyfemtet-0.1.12.dist-info/METADATA +0 -205
- pyfemtet-0.1.12.dist-info/RECORD +0 -37
- {pyfemtet-0.1.12.dist-info → pyfemtet-0.2.1.dist-info}/WHEEL +0 -0
pyfemtet/opt/interface.py
CHANGED
|
@@ -30,84 +30,93 @@ here, me = os.path.split(__file__)
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class FEMInterface(ABC):
|
|
33
|
+
"""Abstract base class for the interface with FEM software."""
|
|
33
34
|
|
|
34
35
|
def __init__(
|
|
35
36
|
self,
|
|
36
37
|
subprocess_idx: int or None = None,
|
|
37
|
-
|
|
38
|
-
pid: int or None = None,
|
|
38
|
+
subprocess_settings: 'Any' or None = None, # 具象クラスで引数に取ることが必須
|
|
39
39
|
**kwargs
|
|
40
40
|
):
|
|
41
|
-
"""
|
|
41
|
+
"""Stores information necessary to restore FEMInterface instance in a subprocess.
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
The concrete class should call super().__init__() with the desired arguments when restoring.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
subprocess_idx (int or None, optional): The index of the subprocess. Defaults to None.
|
|
47
|
+
subprocess_settings ('Any' or None, optional): Additional settings for the subprocess. Defaults to None.
|
|
48
|
+
**kwargs: keyword arguments for FEMInterface constructor.
|
|
47
49
|
|
|
48
50
|
"""
|
|
51
|
+
# restore のための情報保管
|
|
49
52
|
self.kwargs = kwargs
|
|
53
|
+
# base record 時にが呼ぶので attr があったほうがいい
|
|
54
|
+
if not hasattr(self, 'subprocess_idx'):
|
|
55
|
+
self.subprocess_idx = subprocess_idx
|
|
50
56
|
|
|
51
|
-
@abstractmethod
|
|
52
57
|
def check_param_value(self, param_name) -> float:
|
|
53
|
-
"""
|
|
58
|
+
"""Checks the value of a parameter in the FEM model.
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
param_name : str
|
|
60
|
+
Args:
|
|
61
|
+
param_name (str): The name of the parameter.
|
|
58
62
|
|
|
59
|
-
Raises
|
|
60
|
-
|
|
61
|
-
Exception
|
|
62
|
-
param_name が FEM に存在しない
|
|
63
|
+
Raises:
|
|
64
|
+
Exception: If param_name does not exist in the FEM model.
|
|
63
65
|
|
|
64
|
-
Returns
|
|
65
|
-
|
|
66
|
-
value : float
|
|
66
|
+
Returns:
|
|
67
|
+
float: The value of the parameter.
|
|
67
68
|
|
|
68
69
|
"""
|
|
69
70
|
pass
|
|
70
71
|
|
|
71
72
|
@abstractmethod
|
|
72
73
|
def update(self, parameters: 'pd.DataFrame') -> None:
|
|
73
|
-
"""
|
|
74
|
+
"""Updates the FEM analysis based on the proposed parameters.
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
parameters : pd.DataFrame
|
|
78
|
-
最低でも name, value を有する.
|
|
76
|
+
Args:
|
|
77
|
+
parameters (pd.DataFrame): The parameters to be updated. Must have at least 'name' and 'value'.
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
ModelError, MeshError, SolveError
|
|
83
|
-
モデルの更新, 解析に失敗した場合.
|
|
79
|
+
Raises:
|
|
80
|
+
ModelError, MeshError, SolveError: If there are errors in updating or analyzing the model.
|
|
84
81
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
Returns:
|
|
83
|
+
None
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
pass
|
|
88
87
|
|
|
88
|
+
def update_parameter(self, parameters: 'pd.DataFrame'):
|
|
89
|
+
"""Updates only FEM variables that can be obtained with Femtet.GetVariableValue and similar functions.
|
|
90
|
+
|
|
91
|
+
If users define a function to be passed to add_objective etc. to take OptimizerBase as an argument,
|
|
92
|
+
and access the variable by calling OptimizerBase.get_parameter() within the function,
|
|
93
|
+
there is no need to implement this method in the concrete class of FEMInterface.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
parameters (pd.DataFrame): The parameters to be updated.
|
|
89
97
|
|
|
90
98
|
"""
|
|
91
99
|
pass
|
|
92
100
|
|
|
93
101
|
def quit(self, *args, **kwargs):
|
|
94
|
-
"""
|
|
102
|
+
"""Destructor."""
|
|
95
103
|
pass
|
|
96
104
|
|
|
97
|
-
def
|
|
98
|
-
"""
|
|
99
|
-
pass
|
|
105
|
+
def settings_before_parallel(self, femopt) -> 'Any':
|
|
106
|
+
"""Preprocessing before launching a subprocess."""
|
|
107
|
+
pass # return subprocess_settings
|
|
100
108
|
|
|
101
|
-
def parallel_setup(self
|
|
102
|
-
"""
|
|
109
|
+
def parallel_setup(self):
|
|
110
|
+
"""Constructor called in a subprocess."""
|
|
103
111
|
pass
|
|
104
112
|
|
|
105
113
|
def parallel_terminate(self):
|
|
106
|
-
"""
|
|
114
|
+
"""Destructor called in a subprocess."""
|
|
107
115
|
pass
|
|
108
116
|
|
|
109
117
|
|
|
110
118
|
class FemtetInterface(FEMInterface):
|
|
119
|
+
"""Concrete class for the interface with Femtet software."""
|
|
111
120
|
|
|
112
121
|
def __init__(
|
|
113
122
|
self,
|
|
@@ -115,9 +124,22 @@ class FemtetInterface(FEMInterface):
|
|
|
115
124
|
model_name=None,
|
|
116
125
|
connect_method='auto',
|
|
117
126
|
subprocess_idx=None,
|
|
118
|
-
|
|
119
|
-
pid=None,
|
|
127
|
+
subprocess_settings=None,
|
|
120
128
|
):
|
|
129
|
+
"""Initializes the FemtetInterface.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
femprj_path (str or None, optional): The path to the .femprj file. Defaults to None.
|
|
133
|
+
model_name (str or None, optional): The name of the analysis model. Defaults to None.
|
|
134
|
+
connect_method (str, optional): The connection method to use. Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
|
|
135
|
+
subprocess_idx (int or None, optional): The index of the subprocess. Defaults to None.
|
|
136
|
+
subprocess_settings ('Any' or None, optional): Additional settings for the subprocess. Defaults to None.
|
|
137
|
+
|
|
138
|
+
Tip:
|
|
139
|
+
If you search for information about the method to connect python and Femtet, see :func:`connect_femtet`.
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
|
|
121
143
|
# 引数の処理
|
|
122
144
|
if femprj_path is None:
|
|
123
145
|
self.femprj_path = None
|
|
@@ -125,9 +147,11 @@ class FemtetInterface(FEMInterface):
|
|
|
125
147
|
self.femprj_path = os.path.abspath(femprj_path)
|
|
126
148
|
self.model_name = model_name
|
|
127
149
|
self.connect_method = connect_method
|
|
128
|
-
|
|
150
|
+
# 引数の処理(サブプロセス時)
|
|
129
151
|
self.subprocess_idx = subprocess_idx
|
|
130
|
-
self.ipv = ipv
|
|
152
|
+
self.ipv = None if subprocess_settings is None else subprocess_settings['ipv'] # 排他処理に使う
|
|
153
|
+
self.target_pid = None if subprocess_settings is None else subprocess_settings['pids'][subprocess_idx] # before_parallel で立てた Femtet への接続に使う
|
|
154
|
+
self.femprj_path = self.femprj_path if subprocess_settings is None else subprocess_settings['new_femprj_path'][subprocess_idx]
|
|
131
155
|
|
|
132
156
|
# その他の初期化
|
|
133
157
|
self.Femtet: 'IPyDispatch' = None
|
|
@@ -136,33 +160,33 @@ class FemtetInterface(FEMInterface):
|
|
|
136
160
|
self.max_api_retry = 3
|
|
137
161
|
self.pp = False
|
|
138
162
|
|
|
139
|
-
#
|
|
163
|
+
# サブプロセスでなければルールに従って Femtet と接続する
|
|
140
164
|
if subprocess_idx is None:
|
|
141
165
|
self.connect_and_open_femtet()
|
|
142
166
|
|
|
143
167
|
# サブプロセスから呼ばれているならば
|
|
144
|
-
#
|
|
168
|
+
# settings_before_parallel で起動されているはずの Femtet と接続する.
|
|
145
169
|
# connect_and_open_femtet は排他処理で行う.
|
|
146
170
|
else:
|
|
147
171
|
print('Start to connect femtet. This process is exclusive.')
|
|
148
172
|
while True:
|
|
149
|
-
print(f'My subprocess_idx is {subprocess_idx}')
|
|
150
|
-
print(f'
|
|
151
|
-
if subprocess_idx == self.ipv.get_allowed_idx():
|
|
173
|
+
print(f'My subprocess_idx is {self.subprocess_idx}')
|
|
174
|
+
print(f'Connection allowed idx is {self.ipv.get_allowed_idx()}')
|
|
175
|
+
if self.subprocess_idx == self.ipv.get_allowed_idx():
|
|
152
176
|
break
|
|
153
|
-
print(f'Wait to be
|
|
177
|
+
print(f'Wait to be allowed.')
|
|
154
178
|
sleep(1)
|
|
155
|
-
print(f'
|
|
156
|
-
self.connect_femtet('existing', self.
|
|
157
|
-
self.ipv.set_allowed_idx(subprocess_idx+1)
|
|
179
|
+
print(f'Allowed.')
|
|
180
|
+
self.connect_femtet('existing', self.target_pid)
|
|
181
|
+
self.ipv.set_allowed_idx(self.subprocess_idx + 1) # 次の idx に許可を出す
|
|
158
182
|
self.open(self.femprj_path, self.model_name)
|
|
159
183
|
|
|
160
|
-
# restore
|
|
161
|
-
#
|
|
184
|
+
# restore するための情報保管
|
|
185
|
+
# パスなどは connect_and_open_femtet での処理結果を反映し
|
|
186
|
+
# メインで開いた解析モデルが確実に開かれるようにする
|
|
162
187
|
super().__init__(
|
|
163
188
|
femprj_path=self.femprj_path,
|
|
164
189
|
model_name=self.model_name,
|
|
165
|
-
connect_method=self.connect_method,
|
|
166
190
|
)
|
|
167
191
|
|
|
168
192
|
def _connect_new_femtet(self):
|
|
@@ -180,34 +204,25 @@ class FemtetInterface(FEMInterface):
|
|
|
180
204
|
self.connected_method = 'existing'
|
|
181
205
|
|
|
182
206
|
def connect_femtet(self, connect_method: str = 'new', pid: int or None = None):
|
|
183
|
-
"""
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
----------
|
|
188
|
-
connect_method : str, optional
|
|
189
|
-
'new' or 'existing' or 'auto'. The default is 'new'.
|
|
190
|
-
'new' のとき、新しい Femtet プロセスを起動し、接続します。
|
|
191
|
-
|
|
192
|
-
'existing' のとき、既存の Femtet プロセスと接続します。
|
|
193
|
-
ただし、接続できる Femtet が存在しない場合、エラーを送出します。
|
|
194
|
-
この場合、既存 Femtet プロセスのどれに接続されるかは制御できません。
|
|
195
|
-
ただし、pid が既知の場合、pid を指定することができます。
|
|
196
|
-
また、すでに別の Python 又は Excel プロセスと接続されている
|
|
197
|
-
Femtet との接続を行うことはできません。
|
|
207
|
+
"""Connects to a Femtet process.
|
|
208
|
+
Args:
|
|
209
|
+
connect_method (str, optional): The connection method. Can be 'new', 'existing', or 'auto'. Defaults to 'new'.
|
|
210
|
+
pid (int or None, optional): The process ID of an existing Femtet process. Only used when `connect_method` is set as `'existing'`. Defaults to `None`.
|
|
198
211
|
|
|
199
|
-
|
|
212
|
+
When connect_method is 'new', starts a new Femtet process and connects to it.
|
|
200
213
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
214
|
+
When 'existing', connect to an existing Femtet process.
|
|
215
|
+
However, if there are no Femtets to which it can connect, it will throw an error.
|
|
216
|
+
In this case, you cannot control which of the existing Femtet processes is connected.
|
|
217
|
+
|
|
218
|
+
When 'existing', you can specify the pid if it is known.
|
|
219
|
+
Also, if it is already connected to another Python or Excel process.
|
|
220
|
+
Connections cannot be made with Femtet.
|
|
204
221
|
|
|
205
|
-
|
|
206
|
-
------
|
|
207
|
-
Exception
|
|
208
|
-
何らかの理由で Femtet との接続に失敗した際に例外を送出します。
|
|
222
|
+
When set to 'auto', first tries 'existing', and if that fails, connects with 'new'.
|
|
209
223
|
|
|
210
224
|
"""
|
|
225
|
+
|
|
211
226
|
if connect_method == 'new':
|
|
212
227
|
self._connect_new_femtet()
|
|
213
228
|
|
|
@@ -235,7 +250,7 @@ class FemtetInterface(FEMInterface):
|
|
|
235
250
|
if self.Femtet is None:
|
|
236
251
|
raise Exception('Femtet との接続に失敗しました.')
|
|
237
252
|
|
|
238
|
-
def
|
|
253
|
+
def _call_femtet_api(
|
|
239
254
|
self,
|
|
240
255
|
fun,
|
|
241
256
|
ret_if_failed,
|
|
@@ -248,12 +263,12 @@ class FemtetInterface(FEMInterface):
|
|
|
248
263
|
recourse_depth=0,
|
|
249
264
|
print_indent=0,
|
|
250
265
|
):
|
|
251
|
-
"""
|
|
266
|
+
"""Internal method. Call Femtet API with error handling.
|
|
252
267
|
|
|
253
268
|
Parameters
|
|
254
269
|
----------
|
|
255
270
|
fun : Callable
|
|
256
|
-
Femtet
|
|
271
|
+
Femtet API
|
|
257
272
|
ret_if_failed : Any
|
|
258
273
|
API が失敗した時の戻り値
|
|
259
274
|
if_error : Type
|
|
@@ -291,7 +306,7 @@ class FemtetInterface(FEMInterface):
|
|
|
291
306
|
if self.pp: print(' '*print_indent, 'コマンド', fun.__name__, args, kwargs)
|
|
292
307
|
if is_Gaudi_method: # Optimizer は Gogh に触らないので全部にこれをつけてもいい気がする
|
|
293
308
|
try:
|
|
294
|
-
self.
|
|
309
|
+
self._call_femtet_api(
|
|
295
310
|
self.Femtet.Gaudi.Activate,
|
|
296
311
|
False, # None 以外なら何でもいい
|
|
297
312
|
Exception,
|
|
@@ -354,7 +369,7 @@ class FemtetInterface(FEMInterface):
|
|
|
354
369
|
# 与えられた API の再帰的再試行
|
|
355
370
|
if self.pp: print(' '*print_indent, f'回復されました. コマンド{fun.__name__}の再試行を行います.')
|
|
356
371
|
print('回復されました.')
|
|
357
|
-
return self.
|
|
372
|
+
return self._call_femtet_api(
|
|
358
373
|
fun,
|
|
359
374
|
ret_if_failed,
|
|
360
375
|
if_error,
|
|
@@ -367,14 +382,12 @@ class FemtetInterface(FEMInterface):
|
|
|
367
382
|
print_indent+1
|
|
368
383
|
)
|
|
369
384
|
|
|
370
|
-
|
|
371
|
-
|
|
372
385
|
def femtet_is_alive(self) -> bool:
|
|
373
386
|
return _get_pid(self.Femtet.hWnd) > 0
|
|
374
387
|
|
|
375
|
-
|
|
376
|
-
|
|
377
388
|
def open(self, femprj_path: str, model_name: str or None = None) -> None:
|
|
389
|
+
"""Open specific analysis model with connected Femtet."""
|
|
390
|
+
|
|
378
391
|
# 引数の処理
|
|
379
392
|
self.femprj_path = os.path.abspath(femprj_path)
|
|
380
393
|
self.model_name = model_name
|
|
@@ -394,6 +407,17 @@ class FemtetInterface(FEMInterface):
|
|
|
394
407
|
self.Femtet.ShowLastError()
|
|
395
408
|
|
|
396
409
|
def connect_and_open_femtet(self):
|
|
410
|
+
"""Connects to a Femtet process and open the femprj.
|
|
411
|
+
|
|
412
|
+
This function is for establishing a connection with Femtet and opening the specified femprj file.
|
|
413
|
+
|
|
414
|
+
At the beginning of the function, we check if femprj_path is specified.
|
|
415
|
+
|
|
416
|
+
If femprj_path is specified, first connect to Femtet using the specified connection method. Then, if the project path of the connected Femtet is different from the specified femprj_path, open the project using the open function. Also, if model_name is specified, the project will be opened using the open function even if the Femtet analysis model name is different from the specified model_name.
|
|
417
|
+
|
|
418
|
+
On the other hand, if femprj_path is not specified, an error message will be displayed stating that femprj_path must be specified in order to use the "new" connection method. If the connection method is not "new", it will try to connect to an existing Femtet instance. If the connection is successful, the project path and analysis model name of the Femtet instance will be stored as femprj_path and model_name.
|
|
419
|
+
|
|
420
|
+
"""
|
|
397
421
|
|
|
398
422
|
print(f'try to open {self.model_name} of {self.femprj_path}')
|
|
399
423
|
|
|
@@ -414,12 +438,13 @@ class FemtetInterface(FEMInterface):
|
|
|
414
438
|
if self.connect_method == 'new':
|
|
415
439
|
Exception('Femtet の connect_method に "new" を用いる場合、femprj_path を指定してください。')
|
|
416
440
|
print('femprj_path が指定されていないため、開いている Femtet との接続を試行します。')
|
|
417
|
-
self.connect_femtet('existing', self.
|
|
441
|
+
self.connect_femtet('existing', self.target_pid)
|
|
418
442
|
# 接続した Femtet インスタンスのプロジェクト名などを保管する
|
|
419
443
|
self.femprj_path = self.Femtet.Project
|
|
420
444
|
self.model_name = self.Femtet.AnalysisModelName
|
|
421
445
|
|
|
422
446
|
def check_param_value(self, param_name):
|
|
447
|
+
"""See :func:`FEMInterface.check_param_value`"""
|
|
423
448
|
variable_names = self.Femtet.GetVariableNames()
|
|
424
449
|
if variable_names is not None:
|
|
425
450
|
if param_name in variable_names:
|
|
@@ -429,12 +454,13 @@ class FemtetInterface(FEMInterface):
|
|
|
429
454
|
message += '大文字・小文字の区別に注意してください.'
|
|
430
455
|
raise Exception(message)
|
|
431
456
|
|
|
432
|
-
def
|
|
457
|
+
def update_parameter(self, parameters: 'pd.DataFrame'):
|
|
458
|
+
"""See :func:`FEMInterface.update_parameter`"""
|
|
433
459
|
self.parameters = parameters.copy()
|
|
434
460
|
|
|
435
461
|
# 変数更新のための処理
|
|
436
462
|
sleep(0.1) # Gaudi がおかしくなる時がある対策
|
|
437
|
-
self.
|
|
463
|
+
self._call_femtet_api(
|
|
438
464
|
self.Femtet.Gaudi.Activate,
|
|
439
465
|
True, # 戻り値を持たないのでここは無意味で None 以外なら何でもいい
|
|
440
466
|
Exception, # 生きてるのに開けない場合
|
|
@@ -442,20 +468,46 @@ class FemtetInterface(FEMInterface):
|
|
|
442
468
|
)
|
|
443
469
|
|
|
444
470
|
# Femtet の設計変数の更新
|
|
471
|
+
existing_variable_names = self._call_femtet_api(
|
|
472
|
+
fun=self.Femtet.GetVariableNames_py,
|
|
473
|
+
ret_if_failed=False, # 意味がない
|
|
474
|
+
if_error=ModelError, # 生きてるのに失敗した場合
|
|
475
|
+
error_message=f'GetVariableNames_py に失敗しました。',
|
|
476
|
+
is_Gaudi_method=True,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
# 変数を含まないプロジェクトである場合
|
|
480
|
+
if existing_variable_names is None:
|
|
481
|
+
return
|
|
482
|
+
|
|
445
483
|
for i, row in parameters.iterrows():
|
|
446
484
|
name = row['name']
|
|
447
485
|
value = row['value']
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
486
|
+
if name in existing_variable_names:
|
|
487
|
+
self._call_femtet_api(
|
|
488
|
+
fun=self.Femtet.UpdateVariable,
|
|
489
|
+
ret_if_failed=False,
|
|
490
|
+
if_error=ModelError, # 生きてるのに失敗した場合
|
|
491
|
+
error_message=f'変数の更新に失敗しました:変数{name}, 値{value}',
|
|
492
|
+
is_Gaudi_method=True,
|
|
493
|
+
args=(name, value),
|
|
494
|
+
)
|
|
495
|
+
else:
|
|
496
|
+
print(f'変数 {name} は .femprj に含まれていません。無視されます。')
|
|
497
|
+
|
|
498
|
+
# ここでは ReExecute しない
|
|
499
|
+
pass
|
|
500
|
+
|
|
501
|
+
def update_model(self, parameters: 'pd.DataFrame') -> None:
|
|
502
|
+
"""Updates the analysis model only."""
|
|
503
|
+
|
|
504
|
+
self.parameters = parameters.copy()
|
|
505
|
+
|
|
506
|
+
# 変数の更新
|
|
507
|
+
self.update_parameter(parameters)
|
|
456
508
|
|
|
457
509
|
# 設計変数に従ってモデルを再構築
|
|
458
|
-
self.
|
|
510
|
+
self._call_femtet_api(
|
|
459
511
|
self.Femtet.Gaudi.ReExecute,
|
|
460
512
|
False,
|
|
461
513
|
ModelError, # 生きてるのに失敗した場合
|
|
@@ -464,7 +516,7 @@ class FemtetInterface(FEMInterface):
|
|
|
464
516
|
)
|
|
465
517
|
|
|
466
518
|
# 処理を確定
|
|
467
|
-
self.
|
|
519
|
+
self._call_femtet_api(
|
|
468
520
|
self.Femtet.Redraw,
|
|
469
521
|
False, # 戻り値は常に None なのでこの変数に意味はなく None 以外なら何でもいい
|
|
470
522
|
ModelError, # 生きてるのに失敗した場合
|
|
@@ -473,8 +525,9 @@ class FemtetInterface(FEMInterface):
|
|
|
473
525
|
)
|
|
474
526
|
|
|
475
527
|
def solve(self) -> None:
|
|
528
|
+
"""Execute FEM analysis (with updated model)."""
|
|
476
529
|
# # メッシュを切る
|
|
477
|
-
self.
|
|
530
|
+
self._call_femtet_api(
|
|
478
531
|
self.Femtet.Gaudi.Mesh,
|
|
479
532
|
0,
|
|
480
533
|
MeshError,
|
|
@@ -483,7 +536,7 @@ class FemtetInterface(FEMInterface):
|
|
|
483
536
|
)
|
|
484
537
|
|
|
485
538
|
# # ソルブする
|
|
486
|
-
self.
|
|
539
|
+
self._call_femtet_api(
|
|
487
540
|
self.Femtet.Solve,
|
|
488
541
|
False,
|
|
489
542
|
SolveError,
|
|
@@ -492,7 +545,7 @@ class FemtetInterface(FEMInterface):
|
|
|
492
545
|
)
|
|
493
546
|
|
|
494
547
|
# 次に呼ばれるはずのユーザー定義コスト関数の記述を簡単にするため先に解析結果を開いておく
|
|
495
|
-
self.
|
|
548
|
+
self._call_femtet_api(
|
|
496
549
|
self.Femtet.OpenCurrentResult,
|
|
497
550
|
False,
|
|
498
551
|
SolveError, # 生きてるのに開けない場合
|
|
@@ -502,17 +555,20 @@ class FemtetInterface(FEMInterface):
|
|
|
502
555
|
)
|
|
503
556
|
|
|
504
557
|
def update(self, parameters: 'pd.DataFrame') -> None:
|
|
558
|
+
"""See :func:`FEMInterface.update`"""
|
|
505
559
|
self.parameters = parameters.copy()
|
|
506
560
|
self.update_model(parameters)
|
|
507
561
|
self.solve()
|
|
508
562
|
|
|
509
|
-
def quit(self):
|
|
510
|
-
|
|
511
|
-
util.close_femtet(self.Femtet.hWnd)
|
|
563
|
+
def quit(self, timeout=1, force=True):
|
|
564
|
+
"""Force to terminate connected Femtet."""
|
|
565
|
+
util.close_femtet(self.Femtet.hWnd, timeout, force)
|
|
566
|
+
|
|
567
|
+
def settings_before_parallel(self, femopt) -> dict:
|
|
568
|
+
"""Before starting the subprocess, create the required number of Femtet and copy femprj to a temporary file."""
|
|
512
569
|
|
|
513
|
-
def before_parallel_setup(self, femopt) -> list:
|
|
514
|
-
"""サブプロセスを起動する前に必要数の Femtet を立てておく."""
|
|
515
570
|
pids = []
|
|
571
|
+
new_femprj_pathes = []
|
|
516
572
|
for i in range(femopt.n_parallel - 1):
|
|
517
573
|
|
|
518
574
|
# Femtet 起動
|
|
@@ -524,47 +580,50 @@ class FemtetInterface(FEMInterface):
|
|
|
524
580
|
if pid == 0:
|
|
525
581
|
raise Exception('起動された Femtet の認識に失敗しました')
|
|
526
582
|
pids.append(pid)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
583
|
+
|
|
584
|
+
# 一時フォルダ生成、ファイルをコピー
|
|
585
|
+
td = tempfile.mkdtemp()
|
|
586
|
+
name = 'subprocess'
|
|
587
|
+
new_femprj_path = os.path.join(td, f'{name}.femprj')
|
|
588
|
+
shutil.copy(self.femprj_path, new_femprj_path)
|
|
589
|
+
new_femprj_pathes.append(new_femprj_path)
|
|
590
|
+
|
|
591
|
+
subprocess_settings = dict(
|
|
592
|
+
ipv=femopt.ipv,
|
|
593
|
+
pids=pids,
|
|
594
|
+
new_femprj_path=new_femprj_pathes,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
return subprocess_settings
|
|
539
598
|
|
|
540
599
|
def parallel_terminate(self):
|
|
600
|
+
"""Terminate Femtet instances started by settings_before_parallel."""
|
|
601
|
+
# 何かがあってもあとは終わるだけなので
|
|
602
|
+
# プロセス終了 print が出るようにする。
|
|
541
603
|
try:
|
|
542
604
|
# Femtet プロセスを終了する(自動保存しないよう保存する)
|
|
543
605
|
print(f'try to close Femtet.exe')
|
|
544
606
|
self.Femtet.SaveProjectIgnoreHistory(self.Femtet.Project, True) # 動いてない??
|
|
545
|
-
|
|
607
|
+
sleep(3)
|
|
608
|
+
self.quit()
|
|
609
|
+
sleep(1)
|
|
546
610
|
|
|
547
611
|
# 一時ファイルを削除する
|
|
548
|
-
sleep(1)
|
|
549
612
|
try:
|
|
550
613
|
shutil.rmtree(self.td)
|
|
551
614
|
except PermissionError:
|
|
552
615
|
pass # 諦める
|
|
616
|
+
|
|
553
617
|
except:
|
|
554
|
-
pass
|
|
618
|
+
pass
|
|
555
619
|
|
|
556
620
|
|
|
557
621
|
class NoFEM(FEMInterface):
|
|
622
|
+
"""Interface with no FEM for debug."""
|
|
558
623
|
|
|
559
624
|
def update(self, parameters):
|
|
560
625
|
pass
|
|
561
626
|
|
|
562
|
-
def check_param_value(self, param_name):
|
|
563
|
-
pass
|
|
564
|
-
|
|
565
|
-
def quit(self):
|
|
566
|
-
pass
|
|
567
|
-
|
|
568
627
|
|
|
569
628
|
class FemtetWithNXInterface(FemtetInterface):
|
|
570
629
|
|
|
@@ -577,30 +636,48 @@ class FemtetWithNXInterface(FemtetInterface):
|
|
|
577
636
|
model_name=None,
|
|
578
637
|
connect_method='auto',
|
|
579
638
|
subprocess_idx=None,
|
|
580
|
-
|
|
581
|
-
pid=None,
|
|
582
|
-
|
|
639
|
+
subprocess_settings=None,
|
|
583
640
|
):
|
|
641
|
+
"""
|
|
642
|
+
Initializes the FemtetWithNXInterface class.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
prt_path: The path to the prt file.
|
|
646
|
+
femprj_path: The path to the femprj file (optional).
|
|
647
|
+
model_name: The name of the model (optional).
|
|
648
|
+
connect_method: The connection method (default 'auto').
|
|
649
|
+
subprocess_idx: The subprocess index (optional).
|
|
650
|
+
subprocess_settings: The subprocess settings (optional).
|
|
651
|
+
|
|
652
|
+
Returns:
|
|
653
|
+
None
|
|
654
|
+
"""
|
|
655
|
+
|
|
656
|
+
# check NX installation
|
|
657
|
+
exe = r'%UGII_BASE_DIR%\NXBIN\run_journal.exe'
|
|
658
|
+
if not os.path.isfile(exe):
|
|
659
|
+
raise Exception(r'"%UGII_BASE_DIR%\NXBIN\run_journal.exe" が見つかりませんでした。環境変数 UGII_BASE_DIR 又は NX のインストール状態を確認してください。')
|
|
660
|
+
|
|
584
661
|
self.prt_path = os.path.abspath(prt_path)
|
|
585
662
|
super().__init__(
|
|
586
663
|
femprj_path=femprj_path,
|
|
587
664
|
model_name=model_name,
|
|
588
665
|
connect_method=connect_method,
|
|
589
666
|
subprocess_idx=subprocess_idx,
|
|
590
|
-
|
|
591
|
-
pid=pid,
|
|
667
|
+
subprocess_settings=subprocess_settings,
|
|
592
668
|
)
|
|
593
669
|
|
|
594
670
|
def check_param_value(self, name):
|
|
671
|
+
# desable FemtetInterface.check_param_value()
|
|
672
|
+
# because the parameter can be registerd to not only .femprj but also .prt.
|
|
595
673
|
return None
|
|
596
674
|
|
|
597
|
-
|
|
598
675
|
def update_model(self, parameters: 'pd.DataFrame') -> None:
|
|
599
676
|
self.parameters = parameters.copy()
|
|
600
677
|
|
|
601
678
|
# 変数更新のための処理
|
|
602
679
|
sleep(0.1) # Gaudi がおかしくなる時がある対策
|
|
603
|
-
self.
|
|
680
|
+
self._call_femtet_api(
|
|
604
681
|
self.Femtet.Gaudi.Activate,
|
|
605
682
|
True, # 戻り値を持たないのでここは無意味で None 以外なら何でもいい
|
|
606
683
|
Exception, # 生きてるのに開けない場合
|
|
@@ -635,7 +712,7 @@ class FemtetWithNXInterface(FemtetInterface):
|
|
|
635
712
|
raise ModelError
|
|
636
713
|
|
|
637
714
|
# 設計変数に従ってモデルを再構築
|
|
638
|
-
self.
|
|
715
|
+
self._call_femtet_api(
|
|
639
716
|
self.Femtet.Gaudi.ReExecute,
|
|
640
717
|
False,
|
|
641
718
|
ModelError, # 生きてるのに失敗した場合
|
|
@@ -643,8 +720,11 @@ class FemtetWithNXInterface(FemtetInterface):
|
|
|
643
720
|
is_Gaudi_method=True,
|
|
644
721
|
)
|
|
645
722
|
|
|
723
|
+
# femprj モデルの変数も更新
|
|
724
|
+
super().update_model(parameters)
|
|
725
|
+
|
|
646
726
|
# 処理を確定
|
|
647
|
-
self.
|
|
727
|
+
self._call_femtet_api(
|
|
648
728
|
self.Femtet.Redraw,
|
|
649
729
|
False, # 戻り値は常に None なのでこの変数に意味はなく None 以外なら何でもいい
|
|
650
730
|
ModelError, # 生きてるのに失敗した場合
|