pyfemtet 0.6.2__py3-none-any.whl → 0.6.3__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.

@@ -0,0 +1,437 @@
1
+ import os
2
+ from pathlib import Path
3
+ from time import sleep
4
+
5
+ import pandas as pd
6
+ import numpy as np
7
+ from dask.distributed import get_worker
8
+
9
+ from win32com.client import DispatchEx, Dispatch
10
+ from win32com.client.dynamic import CDispatch
11
+ from femtetutils import util
12
+
13
+ # noinspection PyUnresolvedReferences
14
+ from pythoncom import CoInitialize, CoUninitialize
15
+ # noinspection PyUnresolvedReferences
16
+ from pywintypes import com_error
17
+
18
+ from pyfemtet.opt import FEMInterface
19
+ from pyfemtet.core import SolveError
20
+ from pyfemtet.opt.optimizer.parameter import Parameter
21
+
22
+ from pyfemtet.dispatch_extensions import _get_pid, dispatch_specific_femtet
23
+ from pyfemtet.dispatch_extensions._impl import _NestableSpawnProcess
24
+
25
+ from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
26
+ from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
27
+
28
+ from pyfemtet._warning import experimental_class
29
+
30
+ import logging
31
+
32
+ logger = logging.getLogger(__name__)
33
+ logger.setLevel(logging.INFO)
34
+
35
+
36
+ @experimental_class
37
+ class ExcelInterface(FEMInterface):
38
+
39
+ input_xlsm_path: Path # 操作対象の xlsm パス
40
+ input_sheet_name: str # 変数セルを定義しているシート名
41
+ output_xlsm_path: Path # 操作対象の xlsm パス (指定しない場合、input と同一)
42
+ output_sheet_name: str # 計算結果セルを定義しているシート名 (指定しない場合、input と同一)
43
+
44
+ # TODO: related_file_paths: dict[Path] を必要なら実装 # 並列時に個別に並列プロセスの space にアップロードする必要のあるパス
45
+
46
+ procedure_name: str # マクロ関数名(or モジュール名.関数名)
47
+ procedure_args: list # マクロ関数の引数
48
+
49
+ excel: CDispatch # Excel Application
50
+ wb_input: CDispatch # システムを構成する Workbook
51
+ sh_input: CDispatch # 変数の定義された WorkSheet
52
+ wb_output: CDispatch # システムを構成する Workbook
53
+ sh_output: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
54
+
55
+ visible: bool = True # excel を可視化するかどうか
56
+ display_alerts: bool = True # ダイアログを表示するかどうか
57
+
58
+ _load_problem_from_me: bool = True # TODO: add_parameter() 等を省略するかどうか。定義するだけでフラグとして機能する。
59
+ _excel_pid: int
60
+ _excel_hwnd: int
61
+ _femtet_autosave_buffer: bool # Femtet の自動保存機能の一時退避場所。最適化中はオフにする。
62
+
63
+ def __init__(
64
+ self,
65
+ input_xlsm_path: str or Path,
66
+ input_sheet_name: str,
67
+ output_xlsm_path: str or Path = None,
68
+ output_sheet_name: str = None,
69
+ procedure_name: str = None,
70
+ procedure_args: list or tuple = None,
71
+ connect_method: str = 'auto', # or 'new'
72
+ ):
73
+
74
+ # 初期化
75
+ self.input_xlsm_path = None # あとで取得する
76
+ self.input_sheet_name = input_sheet_name
77
+ self.output_xlsm_path = None # あとで取得する
78
+ self.output_sheet_name = output_sheet_name or self.input_sheet_name
79
+ self.procedure_name = procedure_name or 'FemtetMacro.FemtetMain'
80
+ self.procedure_args = procedure_args or []
81
+ assert connect_method in ['new', 'auto']
82
+ self.connect_method = connect_method
83
+ self._femtet_autosave_buffer = _get_autosave_enabled()
84
+
85
+ # dask サブプロセスのときは space 直下の input_xlsm_path を参照する
86
+ try:
87
+ worker = get_worker()
88
+ space = worker.local_directory
89
+ self.input_xlsm_path = Path(os.path.join(space, os.path.basename(input_xlsm_path))).resolve()
90
+ self.output_xlsm_path = Path(os.path.join(space, os.path.basename(output_xlsm_path))).resolve()
91
+
92
+ # main プロセスの場合は絶対パスを参照する
93
+ except ValueError:
94
+ self.input_xlsm_path = Path(os.path.abspath(input_xlsm_path)).resolve()
95
+ if output_xlsm_path is None:
96
+ self.output_xlsm_path = self.input_xlsm_path
97
+ else:
98
+ self.output_xlsm_path = Path(os.path.abspath(output_xlsm_path)).resolve()
99
+
100
+ # FIXME:
101
+ # そもそも ExcelInterface なので Femtet 関連のことをやらなくていいと思う。
102
+ # FemtetRef が文句なく動けばいいのだが、手元環境ではなぜか動いたり動かなかったりするため
103
+ # 仕方なく Femtet を Python 側から動かしている仮実装。
104
+ # 先に femtet を起動
105
+ util.execute_femtet()
106
+
107
+ # 直後の Excel 起動に間に合わない場合があったため
108
+ # Femtet が Dispatch 可能になるまで捨てプロセスで待つ
109
+ p = _NestableSpawnProcess(target=wait_femtet)
110
+ p.start()
111
+ p.join()
112
+
113
+ # サブプロセスでの restore のための情報保管
114
+ kwargs = dict(
115
+ input_xlsm_path=self.input_xlsm_path,
116
+ input_sheet_name=self.input_sheet_name,
117
+ output_xlsm_path=self.output_xlsm_path,
118
+ output_sheet_name=self.output_sheet_name,
119
+ procedure_name=self.procedure_name,
120
+ procedure_args=self.procedure_args,
121
+ connect_method='new', # subprocess で connect する際は new を強制する
122
+ )
123
+ super().__init__(**kwargs)
124
+
125
+ def __del__(self):
126
+ try:
127
+ _set_autosave_enabled(self._femtet_autosave_buffer)
128
+ finally:
129
+ pass
130
+
131
+ def _setup_before_parallel(self, client) -> None:
132
+ # メインプロセスで、並列プロセスを開始する前に行う前処理
133
+
134
+ input_xlsm_path: Path = self.kwargs['input_xlsm_path']
135
+ output_xlsm_path: Path = self.kwargs['output_xlsm_path']
136
+
137
+ client.upload_file(str(input_xlsm_path), False)
138
+ if input_xlsm_path.resolve() != output_xlsm_path.resolve():
139
+ client.upload_file(str(output_xlsm_path), False)
140
+
141
+ def connect_excel(self, connect_method):
142
+
143
+ # ===== 新しい excel instance を起動 =====
144
+ # 起動
145
+ if connect_method == 'auto':
146
+ self.excel = Dispatch('Excel.Application')
147
+ else:
148
+ self.excel = DispatchEx('Excel.Application')
149
+
150
+ # 起動した excel の pid を記憶する
151
+ self._excel_hwnd = self.excel.hWnd
152
+ self._excel_pid = 0
153
+ while self._excel_pid == 0:
154
+ sleep(0.5)
155
+ self._excel_pid = _get_pid(self.excel.hWnd)
156
+
157
+ # 可視性の設定
158
+ self.excel.Visible = self.visible
159
+ self.excel.DisplayAlerts = self.display_alerts
160
+
161
+ # 開く
162
+ self.excel.Workbooks.Open(str(self.input_xlsm_path))
163
+ for wb in self.excel.Workbooks:
164
+ if wb.Name == os.path.basename(self.input_xlsm_path):
165
+ self.wb_input = wb
166
+ break
167
+ else:
168
+ raise RuntimeError(f'Cannot open {self.input_xlsm_path}')
169
+
170
+ # シートを特定する
171
+ for sh in self.wb_input.WorkSheets:
172
+ if sh.Name == self.input_sheet_name:
173
+ self.sh_input = sh
174
+ break
175
+ else:
176
+ raise RuntimeError(f'Sheet {self.input_sheet_name} does not exist in the book {self.wb_input.Name}.')
177
+
178
+ if self.input_xlsm_path.resolve() == self.output_xlsm_path.resolve():
179
+ self.wb_output = self.wb_input
180
+
181
+ else:
182
+ # 開く (output)
183
+ self.excel.Workbooks.Open(str(self.output_xlsm_path))
184
+ for wb in self.excel.Workbooks:
185
+ if wb.Name == os.path.basename(self.output_xlsm_path):
186
+ self.wb_output = wb
187
+ break
188
+ else:
189
+ raise RuntimeError(f'Cannot open {self.output_xlsm_path}')
190
+
191
+ # シートを特定する (output)
192
+ for sh in self.wb_output.WorkSheets:
193
+ if sh.Name == self.output_sheet_name:
194
+ self.sh_output = sh
195
+ break
196
+ else:
197
+ raise RuntimeError(f'Sheet {self.output_sheet_name} does not exist in the book {self.wb_output.Name}.')
198
+
199
+ # book に参照設定を追加する
200
+ self.add_femtet_ref_xla(self.wb_input)
201
+ self.add_femtet_ref_xla(self.wb_output)
202
+
203
+ def add_femtet_ref_xla(self, wb):
204
+
205
+ # search
206
+ ref_file_2 = os.path.abspath(util._get_femtetmacro_dllpath())
207
+ contain = False
208
+ for ref in wb.VBProject.References:
209
+ if ref.Description is not None:
210
+ if ref.Description == 'FemtetMacro': # FemtetMacro
211
+ contain = True
212
+ # add
213
+ if not contain:
214
+ wb.VBProject.References.AddFromFile(ref_file_2)
215
+
216
+ def _setup_after_parallel(self, *args, **kwargs):
217
+ # サブプロセス又はメインプロセスのサブスレッドで、最適化を開始する前の前処理
218
+
219
+ # スレッドが変わっているかもしれないので win32com の初期化
220
+ CoInitialize()
221
+
222
+ # 最適化中は femtet の autosave を無効にする
223
+ _set_autosave_enabled(False)
224
+
225
+ # excel に繋ぎなおす
226
+ self.connect_excel(self.connect_method)
227
+
228
+ # load_objective は 1 回目に呼ばれたのが main thread なので
229
+ # subprocess に入った後でもう一度 load objective を行う
230
+ from pyfemtet.opt.optimizer import AbstractOptimizer
231
+ from pyfemtet.opt._femopt_core import Objective
232
+ opt: AbstractOptimizer = kwargs['opt']
233
+ obj: Objective
234
+ for obj_name, obj in opt.objectives.items():
235
+ if isinstance(obj.fun, ScapeGoatObjective):
236
+ opt.objectives[obj_name].fun = self.objective_from_excel
237
+
238
+ # TODO:
239
+ # __init__ が作った Femtet 以外を排他処理して
240
+ # Excel がそれを使うことを保証するようにする(遅すぎるか?)
241
+ # そもそも、excel から Femtet を起動できればそれで済む(?)。
242
+
243
+ def update(self, parameters: pd.DataFrame) -> None:
244
+
245
+ # params を作成
246
+ params = dict()
247
+ for _, row in parameters.iterrows():
248
+ params[row['name']] = row['value']
249
+
250
+ # excel シートの変数更新
251
+ for key, value in params.items():
252
+ self.sh_input.Range(key).value = value
253
+
254
+ # 再計算
255
+ self.excel.CalculateFull()
256
+
257
+ # マクロ実行
258
+ try:
259
+ self.excel.Run(
260
+ f'{self.wb_input.Name}!{self.procedure_name}',
261
+ *self.procedure_args
262
+ )
263
+
264
+ # FIXME: エラーハンドリング
265
+ # com_error をキャッチする(solveerror)か、
266
+ # sh_out に解析結果を書く(拘束違反)ようにして、
267
+ # それが FALSE なら SolveError を raise する。
268
+ except ...:
269
+ raise SolveError('Excelアップデートに失敗しました')
270
+
271
+ # 再計算
272
+ self.excel.CalculateFull()
273
+
274
+ def quit(self):
275
+ logger.info('Excel-Femtet の終了処理を開始します。') # FIXME: message にする
276
+
277
+ del self.sh_input
278
+ del self.sh_output
279
+
280
+ self.wb_input.Close(SaveChanges := False)
281
+ if self.input_xlsm_path.name != self.output_xlsm_path.name:
282
+ self.wb_output.Close(SaveChanges := False)
283
+ del self.wb_input
284
+ del self.wb_output
285
+
286
+ self.excel.Quit()
287
+ del self.excel
288
+
289
+ import gc
290
+ gc.collect()
291
+
292
+ # quit した後ならば femtet を終了できる
293
+ # excel の process の完全消滅を待つ
294
+ logger.info('Excel の終了を待っています。')
295
+ while self._excel_pid == _get_pid(self._excel_hwnd):
296
+ sleep(1)
297
+
298
+ # 正確だが時間がかかるかも
299
+ logger.info('終了する Femtet を特定しています。')
300
+ femtet_pid = util.get_last_executed_femtet_process_id()
301
+ Femtet, caught_pid = dispatch_specific_femtet(femtet_pid)
302
+ _exit_or_force_terminate(timeout=3, Femtet=Femtet, force=True)
303
+
304
+ logger.info('自動保存機能の設定を元に戻しています。')
305
+ _set_autosave_enabled(self._femtet_autosave_buffer)
306
+
307
+ logger.info('Excel-Femtet を終了しました。')
308
+
309
+ # 直接アクセスしてもよいが、ユーザーに易しい名前にするためだけのプロパティ
310
+ @property
311
+ def output_sheet(self) -> CDispatch:
312
+ return self.sh_output
313
+
314
+ @property
315
+ def input_sheet(self) -> CDispatch:
316
+ return self.sh_input
317
+
318
+ @property
319
+ def output_workbook(self) -> CDispatch:
320
+ return self.wb_output
321
+
322
+ @property
323
+ def input_workbook(self) -> CDispatch:
324
+ return self.wb_input
325
+
326
+ def load_parameter(self, opt) -> None:
327
+ from pyfemtet.opt.optimizer import AbstractOptimizer, logger
328
+ opt: AbstractOptimizer
329
+
330
+ df = pd.read_excel(
331
+ self.input_xlsm_path,
332
+ self.input_sheet_name,
333
+ header=0,
334
+ index_col=None,
335
+ )
336
+
337
+ # TODO: 使い勝手を考える
338
+ for i, row in df.iterrows():
339
+ try:
340
+ name = row['name']
341
+ value = row['current']
342
+ lb = row['lower']
343
+ ub = row['upper']
344
+ step = row['step']
345
+ except KeyError:
346
+ logger.warn('列名が「name」「current」「lower」「upper」「step」になっていません。この順に並んでいると仮定して処理を続けます。')
347
+ name, value, lb, ub, step, *_residuals = row.iloc[0]
348
+
349
+ name = str(name)
350
+ value = float(value)
351
+ lb = float(lb) if not np.isnan(lb) else None
352
+ ub = float(ub) if not np.isnan(ub) else None
353
+ step = float(step) if not np.isnan(step) else None
354
+
355
+ prm = Parameter(
356
+ name=name,
357
+ value=value,
358
+ lower_bound=lb,
359
+ upper_bound=ub,
360
+ step=step,
361
+ pass_to_fem=True,
362
+ properties=None,
363
+ )
364
+ opt.variables.add_parameter(prm)
365
+
366
+ def load_objective(self, opt):
367
+ from pyfemtet.opt.optimizer import AbstractOptimizer, logger
368
+ from pyfemtet.opt._femopt_core import Objective
369
+ opt: AbstractOptimizer
370
+
371
+ df = pd.read_excel(
372
+ self.output_xlsm_path,
373
+ self.output_sheet_name,
374
+ header=0,
375
+ index_col=None,
376
+ )
377
+
378
+ # TODO: 使い勝手を考える
379
+ for i, row in df.iterrows():
380
+ try:
381
+ name = row['name']
382
+ _ = row['current']
383
+ direction = row['direction']
384
+ value_column_index = list(df.columns).index('current')
385
+ except KeyError:
386
+ logger.warn('列名が「name」「current」「direction」になっていません。この順に並んでいると仮定して処理を続けます。')
387
+ name, _, direction, *_residuals = row.iloc[0]
388
+ value_column_index = 1
389
+
390
+ name = str(name)
391
+
392
+ # direction は minimize or maximize or float
393
+ try:
394
+ # float or not
395
+ direction = float(direction)
396
+
397
+ except ValueError:
398
+ # 'minimize' or 'maximize
399
+ direction = str(direction).lower()
400
+ assert (direction == 'minimize') or (direction == 'maximize')
401
+
402
+ # objective を作る
403
+ opt.objectives[name] = Objective(
404
+ fun=ScapeGoatObjective(),
405
+ name=name,
406
+ direction=direction,
407
+ args=(i, value_column_index, ),
408
+ kwargs=dict(),
409
+ )
410
+
411
+ def objective_from_excel(self, i: int, value_column_index: int):
412
+ r = i + 2 # header が 1
413
+ c = value_column_index + 1
414
+ v = self.sh_output.Cells(r, c).value
415
+ return float(v)
416
+
417
+
418
+ def wait_femtet():
419
+ Femtet = Dispatch('FemtetMacro.Femtet')
420
+ while Femtet.hWnd <= 0:
421
+ sleep(1)
422
+ Femtet = Dispatch('FemtetMacro.Femtet')
423
+
424
+
425
+ # main thread で作成した excel への参照を含む関数を
426
+ # 直接 thread や process に渡すと機能しない
427
+ class ScapeGoatObjective:
428
+ def __call__(self, *args, fem: ExcelInterface or None = None, **kwargs):
429
+ fem.objective_from_excel(*args, **kwargs)
430
+
431
+ @property
432
+ def __globals__(self):
433
+ return tuple()
434
+
435
+
436
+ if __name__ == '__main__':
437
+ ExcelInterface(..., ...)
@@ -35,6 +35,8 @@ from pyfemtet.dispatch_extensions import (
35
35
  )
36
36
  from pyfemtet.opt.interface import FEMInterface, logger
37
37
  from pyfemtet._message import Msg
38
+ from pyfemtet._femtet_config_util.autosave import _get_autosave_enabled, _set_autosave_enabled
39
+ from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
38
40
 
39
41
 
40
42
  def _post_activate_message(hwnd):
@@ -317,9 +319,6 @@ class FemtetInterface(FEMInterface):
317
319
 
318
320
  """
319
321
 
320
- # FIXME: Gaudi へのアクセスなど、self.Femtet.Gaudi.SomeFunc() のような場合、この関数を呼び出す前に Gaudi へのアクセスの時点で com_error が起こる
321
- # FIXME: => 文字列で渡して eval() すればよい。
322
-
323
322
  if args is None:
324
323
  args = tuple()
325
324
  if kwargs is None:
@@ -731,43 +730,10 @@ class FemtetInterface(FEMInterface):
731
730
 
732
731
  def quit(self, timeout=1, force=True):
733
732
  """Force to terminate connected Femtet."""
734
- major, minor, bugfix = 2024, 0, 1
735
-
736
- # すでに終了しているならば何もしない
737
- if not self.femtet_is_alive():
738
- return
739
733
 
740
- if self._version() >= _version(major, minor, bugfix):
741
- # gracefully termination method without save project available from 2024.0.1
742
- try:
743
- self.Femtet.Exit(True)
744
- except AttributeError as e:
745
- print('================')
746
- logger.error(Msg.ERR_CANNOT_ACCESS_API + 'Femtet.Exit()')
747
- logger.error(Msg.CERTIFY_MACRO_VERSION)
748
- print('================')
749
- if self.confirm_before_exit:
750
- input(Msg.ENTER_TO_QUIT)
751
- raise e
752
-
753
- else:
754
- hwnd = self.Femtet.hWnd
755
-
756
- # terminate
757
- util.close_femtet(hwnd, timeout, force)
734
+ _set_autosave_enabled(self._original_autosave_enabled)
758
735
 
759
- try:
760
- pid = _get_pid(hwnd)
761
- start = time()
762
- while psutil.pid_exists(pid):
763
- if time() - start > 30: # 30 秒経っても存在するのは何かおかしい
764
- logger.error(Msg.ERR_CLOSE_FEMTET_FAILED)
765
- break
766
- sleep(1)
767
- sleep(1)
768
-
769
- except (AttributeError, OSError): # already dead
770
- pass
736
+ _exit_or_force_terminate(timeout=timeout, Femtet=self.Femtet, force=True)
771
737
 
772
738
  def _setup_before_parallel(self, client):
773
739
  client.upload_file(
@@ -912,40 +878,3 @@ class _UnPicklableNoFEM(FemtetInterface):
912
878
  here = os.path.dirname(__file__)
913
879
  pdt_path = os.path.join(here, f'trial{trial}.pdt')
914
880
  return pdt_path
915
-
916
-
917
- # レジストリのパスと値の名前
918
- _REGISTRY_PATH: Final[str] = r"SOFTWARE\Murata Software\Femtet2014\Femtet"
919
- _VALUE_NAME: Final[str] = "AutoSave"
920
-
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)
925
- if regtype == winreg.REG_DWORD:
926
- return bool(value)
927
- else:
928
- raise ValueError("Unexpected registry value type.")
929
-
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))
934
-
935
-
936
- def _test_autosave_setting():
937
-
938
- # 使用例
939
- current_setting = _get_autosave_enabled()
940
- print(f"Current AutoSave setting is {'enabled' if current_setting else 'disabled'}.")
941
-
942
- # 設定を変更する例
943
- new_setting = not current_setting
944
- _set_autosave_enabled(new_setting)
945
- print(f"AutoSave setting has been {'enabled' if new_setting else 'disabled'}.")
946
-
947
- # 再度設定を確認する
948
- after_setting = _get_autosave_enabled()
949
- print(f"Current AutoSave setting is {'enabled' if after_setting else 'disabled'}.")
950
-
951
- assert new_setting == after_setting, "レジストリ編集に失敗しました。"
@@ -92,7 +92,7 @@ class FemtetWithSolidworksInterface(FemtetInterface):
92
92
  )
93
93
  super()._setup_before_parallel(client)
94
94
 
95
- def _setup_after_parallel(self):
95
+ def _setup_after_parallel(self, *args, **kwargs):
96
96
  CoInitialize()
97
97
  self.initialize_sldworks_connection()
98
98
 
@@ -292,17 +292,17 @@ class AbstractOptimizer(ABC):
292
292
 
293
293
  def _finalize(self):
294
294
  """Destruct fem and set worker status."""
295
- del self.fem
295
+ self.fem.quit()
296
296
  if not self.worker_status.get() == OptimizationStatus.CRASHED:
297
297
  self.worker_status.set(OptimizationStatus.TERMINATED)
298
298
 
299
299
  # run via FEMOpt (considering parallel processing)
300
300
  def _run(
301
301
  self,
302
- subprocess_idx,
303
- worker_status_list,
304
- wait_setup,
305
- skip_set_fem=False,
302
+ subprocess_idx, # 自身が何番目の並列プロセスであるかを示す連番
303
+ worker_status_list, # 他の worker の status オブジェクト
304
+ wait_setup, # 他の worker の status が ready になるまで待つか
305
+ skip_reconstruct=False, # reconstruct fem を行うかどうか
306
306
  ) -> Optional[Exception]:
307
307
 
308
308
  # 自分の worker_status の取得
@@ -314,9 +314,8 @@ class AbstractOptimizer(ABC):
314
314
  return None
315
315
 
316
316
  # set_fem をはじめ、終了したらそれを示す
317
- if not skip_set_fem: # なくても動く??
318
- self._reconstruct_fem()
319
- self.fem._setup_after_parallel()
317
+ self._reconstruct_fem(skip_reconstruct)
318
+ self.fem._setup_after_parallel(opt=self)
320
319
  self.worker_status.set(OptimizationStatus.WAIT_OTHER_WORKERS)
321
320
 
322
321
  # wait_setup or not
@@ -326,6 +325,9 @@ class AbstractOptimizer(ABC):
326
325
  return None
327
326
  # 他のすべての worker_status が wait 以上になったら break
328
327
  if all([ws.get() >= OptimizationStatus.WAIT_OTHER_WORKERS for ws in worker_status_list]):
328
+ # リソースの競合等を避けるため
329
+ # break する前に index 秒待つ
330
+ sleep(int(subprocess_idx))
329
331
  break
330
332
  sleep(1)
331
333
  else: