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.

Files changed (33) hide show
  1. pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
  2. pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
  3. pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +69 -32
  4. pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
  5. pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +37 -25
  6. pyfemtet/FemtetPJTSample/her_ex40_parametric.py +57 -35
  7. pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +62 -0
  8. pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
  9. pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +61 -0
  10. pyfemtet/__init__.py +1 -1
  11. pyfemtet/opt/_FemtetWithNX/update_model.py +6 -2
  12. pyfemtet/opt/__init__.py +1 -1
  13. pyfemtet/opt/base.py +457 -86
  14. pyfemtet/opt/core.py +77 -17
  15. pyfemtet/opt/interface.py +217 -137
  16. pyfemtet/opt/monitor.py +181 -98
  17. pyfemtet/opt/{_optuna.py → optimizer.py} +70 -30
  18. pyfemtet/tools/DispatchUtils.py +46 -44
  19. {pyfemtet-0.1.12.dist-info → pyfemtet-0.2.1.dist-info}/LICENSE +1 -1
  20. pyfemtet-0.2.1.dist-info/METADATA +42 -0
  21. pyfemtet-0.2.1.dist-info/RECORD +31 -0
  22. pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01 - original.x_t +0 -359
  23. pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.x_t +0 -359
  24. pyfemtet/FemtetPJTSample/fem4 = Femtet(femprj_path=None, model_name=None, connect_method='catch').femprj +0 -0
  25. pyfemtet/FemtetPJTSample/gal_ex11_parametric.femprj +0 -0
  26. pyfemtet/FemtetPJTSample/gal_ex11_parametric.py +0 -54
  27. pyfemtet/FemtetPJTSample/pas_ex1_parametric.femprj +0 -0
  28. pyfemtet/FemtetPJTSample/pas_ex1_parametric.py +0 -66
  29. pyfemtet/FemtetPJTSample/pas_ex1_parametric2.py +0 -68
  30. pyfemtet/tools/FemtetClassConst.py +0 -9
  31. pyfemtet-0.1.12.dist-info/METADATA +0 -205
  32. pyfemtet-0.1.12.dist-info/RECORD +0 -37
  33. {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
- ipv: InterprocessVariables or None = None,
38
- pid: int or None = None,
38
+ subprocess_settings: 'Any' or None = None, # 具象クラスで引数に取ることが必須
39
39
  **kwargs
40
40
  ):
41
- """サブプロセスで FEM restore するときに必要な情報を保管する.
41
+ """Stores information necessary to restore FEMInterface instance in a subprocess.
42
42
 
43
- 具象クラスでは最低でも subprocess_idx, ipv, pid を引数にとること.
44
- 使わないなら **kwargs にしても構わない.
45
- 具象クラスではサブプロセス時に指定したい変数を引数にして
46
- super().__init__ を呼ぶこと.
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
- Parameters
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
- """提案された parameters に基づいて FEM 解析を更新する
74
+ """Updates the FEM analysis based on the proposed parameters.
74
75
 
75
- Parameters
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
- Raises
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
- Returns
86
- -------
87
- None
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 before_parallel_setup(self, femopt):
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, subprocess_idx):
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
- ipv=None,
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
- self.pid = pid
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
- # サブプロセスでなければ何も考えず Femtet と接続する
163
+ # サブプロセスでなければルールに従って Femtet と接続する
140
164
  if subprocess_idx is None:
141
165
  self.connect_and_open_femtet()
142
166
 
143
167
  # サブプロセスから呼ばれているならば
144
- # before_parallel_setup で起動されているはずの Femtet と接続する.
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'Allowed idx is {self.ipv.get_allowed_idx()}')
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 permitted.')
177
+ print(f'Wait to be allowed.')
154
178
  sleep(1)
155
- print(f'Permitted.')
156
- self.connect_femtet('existing', self.pid)
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
- Femtet プロセスとの接続を行い、メンバー変数 Femtet にインスタンスをセットします。
185
-
186
- Parameters
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
- 'auto' のとき、まず 'existing' を試行し、失敗した際には 'new' で接続します。
212
+ When connect_method is 'new', starts a new Femtet process and connects to it.
200
213
 
201
- pid : int or None, optional
202
- 接続したい既存の Femtet のプロセス ID. The default is None.
203
- connect_method existing 以外の時は無視されます。
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
- Raises
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 call_femtet_api(
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 マクロの API
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.call_femtet_api(
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.call_femtet_api(
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.pid)
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 update_model(self, parameters: 'pd.DataFrame') -> None:
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.call_femtet_api(
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
- self.call_femtet_api(
449
- fun=self.Femtet.UpdateVariable,
450
- ret_if_failed=False,
451
- if_error=ModelError, # 生きてるのに失敗した場合
452
- error_message=f'変数の更新に失敗しました:変数{name}, 値{value}',
453
- is_Gaudi_method=True,
454
- args=(name, value),
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.call_femtet_api(
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.call_femtet_api(
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.call_femtet_api(
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.call_femtet_api(
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.call_femtet_api(
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
- self.Femtet.SaveProject(self.Femtet.Project, True) # 動いてない??
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
- return pids
528
-
529
- def parallel_setup(self, subprocess_idx):
530
- # .Result の干渉を回避するため、
531
- # サブプロセスならばプロジェクトを別名保存する
532
- self.td = tempfile.mkdtemp()
533
- print(self.td)
534
- name = f'subprocess{subprocess_idx}'
535
- self.femprj_path = os.path.join(self.td, f'{name}.femprj')
536
- result = self.Femtet.SaveProject(self.femprj_path, True)
537
- if not result:
538
- self.Femtet.ShowLastError()
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
- util.close_femtet(self.Femtet.hWnd)
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
- ipv=None,
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
- ipv=ipv,
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.call_femtet_api(
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.call_femtet_api(
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.call_femtet_api(
727
+ self._call_femtet_api(
648
728
  self.Femtet.Redraw,
649
729
  False, # 戻り値は常に None なのでこの変数に意味はなく None 以外なら何でもいい
650
730
  ModelError, # 生きてるのに失敗した場合