pyfemtet 0.7.0__py3-none-any.whl → 0.7.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/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.7.0"
1
+ __version__ = "0.7.1"
@@ -0,0 +1,10 @@
1
+ from contextlib import nullcontext
2
+ from dask.distributed import Lock
3
+
4
+
5
+ def lock_or_no_lock(name: str, client=None):
6
+ lock = Lock(name, client)
7
+ if lock.client is None:
8
+ return nullcontext()
9
+ else:
10
+ return lock
@@ -5,16 +5,27 @@ import asyncio # for timeout
5
5
  import win32gui
6
6
  import win32con
7
7
  import win32api
8
+ import win32process
8
9
 
9
10
  from pyfemtet.logger import get_module_logger
10
11
 
11
12
  logger = get_module_logger('util.excel', __name__)
12
13
 
13
14
 
15
+ def _get_pid(hwnd):
16
+ """Window handle から process ID を取得します."""
17
+ if hwnd > 0:
18
+ _, pid = win32process.GetWindowThreadProcessId(hwnd)
19
+ else:
20
+ pid = 0
21
+ return pid
22
+
23
+
14
24
  class _ExcelDialogProcessor:
15
25
 
16
26
  def __init__(self, excel_, timeout, restore_book=True):
17
27
  self.excel = excel_
28
+ self.excel_pid = _get_pid(excel_.hWnd)
18
29
  self.__excel_window_title = f' - Excel' # {basename} - Excel
19
30
  self.__error_dialog_title = 'Microsoft Visual Basic'
20
31
  self.__vbe_window_title = f'Microsoft Visual Basic for Applications - ' # Microsoft Visual Basic for Applications - {basename}
@@ -42,6 +53,7 @@ class _ExcelDialogProcessor:
42
53
  win32gui.EnumWindows(self.enum_callback_to_close_dialog, found)
43
54
  await asyncio.sleep(0.5)
44
55
  if any(found):
56
+ await asyncio.sleep(1.)
45
57
  break
46
58
 
47
59
  logger.debug('ブックを閉じます。')
@@ -56,7 +68,7 @@ class _ExcelDialogProcessor:
56
68
  def enum_callback_to_activate(self, hwnd, _):
57
69
  title = win32gui.GetWindowText(hwnd)
58
70
  # Excel 本体
59
- if self.__excel_window_title in title:
71
+ if (self.excel_pid == _get_pid(hwnd)) and (self.__excel_window_title in title):
60
72
  # Visible == True の際、エラーが発生した際、
61
73
  # 一度 Excel ウィンドウをアクティブ化しないと dialog が出てこない
62
74
  # が、これだけではダメかも。
@@ -65,7 +77,7 @@ class _ExcelDialogProcessor:
65
77
  def enum_callback_to_close_dialog(self, hwnd, found):
66
78
  title = win32gui.GetWindowText(hwnd)
67
79
  # エラーダイアログ
68
- if self.__error_dialog_title == title:
80
+ if (self.excel_pid == _get_pid(hwnd)) and (self.__error_dialog_title == title):
69
81
  # 何故かこのコマンド以外受け付けず、
70
82
  # このコマンドで問答無用でデバッグモードに入る
71
83
  logger.debug('エラーダイアログを見つけました。')
@@ -77,14 +89,14 @@ class _ExcelDialogProcessor:
77
89
  def enum_callback_to_close_confirm_dialog(self, hwnd, _):
78
90
  title = win32gui.GetWindowText(hwnd)
79
91
  # 確認ダイアログ
80
- if "Microsoft Excel" in title:
92
+ if (self.excel_pid == _get_pid(hwnd)) and ("Microsoft Excel" in title):
81
93
  # DisplayAlerts が False の場合は不要
82
94
  win32gui.SendMessage(hwnd, win32con.WM_SYSCOMMAND, win32con.SC_CLOSE, 0)
83
95
 
84
96
  def enum_callback_to_close_book(self, hwnd, _):
85
97
  title = win32gui.GetWindowText(hwnd)
86
98
  # VBE
87
- if self.__vbe_window_title in title:
99
+ if (self.excel_pid == _get_pid(hwnd)) and (self.__vbe_window_title in title):
88
100
  # 何故かこれで book 本体が閉じる
89
101
  win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0)
90
102
 
pyfemtet/opt/_femopt.py CHANGED
@@ -12,7 +12,7 @@ from traceback import print_exception
12
12
  # 3rd-party
13
13
  import numpy as np
14
14
  import pandas as pd
15
- from dask.distributed import LocalCluster, Client
15
+ from dask.distributed import LocalCluster, Client, get_worker, Nanny
16
16
 
17
17
  # pyfemtet relative
18
18
  from pyfemtet.opt.interface import FEMInterface, FemtetInterface
@@ -136,6 +136,7 @@ class FEMOpt:
136
136
  self.monitor_server_kwargs = dict()
137
137
  self.monitor_process_worker_name = None
138
138
  self._hv_reference = None
139
+ self._extra_space_dir = None
139
140
 
140
141
  # multiprocess 時に pickle できないオブジェクト参照の削除
141
142
  def __getstate__(self):
@@ -675,10 +676,6 @@ class FEMOpt:
675
676
  directions,
676
677
  )
677
678
 
678
- # Femtet の confirm_before_exit のセット
679
- self.fem.confirm_before_exit = confirm_before_exit
680
- self.fem.kwargs['confirm_before_exit'] = confirm_before_exit
681
-
682
679
  logger.info('Femtet loaded successfully.')
683
680
 
684
681
  # クラスターの設定
@@ -718,30 +715,43 @@ class FEMOpt:
718
715
  # これは CLI の --no-nanny オプションも同様らしい。
719
716
 
720
717
  # クラスターの構築
718
+ # noinspection PyTypeChecker
721
719
  cluster = LocalCluster(
722
720
  processes=True,
723
721
  n_workers=n_parallel,
724
722
  threads_per_worker=1,
723
+ worker_class=Nanny,
725
724
  )
726
725
  logger.info('LocalCluster launched successfully.')
727
726
 
728
- self.client = Client(cluster, direct_to_workers=False)
729
- self.scheduler_address = self.client.scheduler.address
727
+ self.client = Client(
728
+ cluster,
729
+ direct_to_workers=False,
730
+ )
730
731
  logger.info('Client launched successfully.')
731
732
 
732
- # 最適化タスクを振り分ける worker を指定
733
- subprocess_indices = list(range(n_parallel))[1:]
734
- worker_addresses = list(self.client.nthreads().keys())
733
+ self.scheduler_address = self.client.scheduler.address
735
734
 
736
- # monitor worker の設定
737
- self.monitor_process_worker_name = worker_addresses[0]
738
- worker_addresses[0] = 'Main'
735
+ # worker address を取得
736
+ nannies_dict: dict[Any, Nanny] = self.client.cluster.workers
737
+ nannies = tuple(nannies_dict.values())
738
+
739
+ # ひとつの Nanny を選んで monitor 用にしつつ
740
+ # その space は main process に使わせるために記憶する
741
+ self.monitor_process_worker_name = nannies[0].worker_address
742
+ self._extra_space_dir = nannies[0].worker_dir
743
+
744
+ # 名前と address がごちゃごちゃになっていて可読性が悪いが
745
+ # 選んだ以外の Nanny は計算を割り当てる用にする
746
+ worker_addresses = ['Main']
747
+ worker_addresses.extend([n.worker_address for n in nannies[1:]])
748
+ subprocess_indices = list(range(n_parallel))[1:]
739
749
 
740
750
  with self.client.cluster as _cluster, self.client as _client:
741
751
 
742
752
  # actor の設定
743
- self.status = OptimizationStatus(_client)
744
- self.worker_status_list = [OptimizationStatus(_client, name) for name in worker_addresses] # tqdm 検討
753
+ self.status = OptimizationStatus(_client, worker_address=self.monitor_process_worker_name)
754
+ self.worker_status_list = [OptimizationStatus(_client, worker_address=self.monitor_process_worker_name, name=name) for name in worker_addresses] # tqdm 検討
745
755
  self.status.set(OptimizationStatus.SETTING_UP)
746
756
  self.history = History(
747
757
  self.history_path,
@@ -773,7 +783,6 @@ class FEMOpt:
773
783
  logger.info('Process monitor initialized successfully.')
774
784
 
775
785
  # fem
776
- # TODO: n_parallel=1 のときもアップロードしている。これを使うべきか、アップロードしないべき。
777
786
  self.fem._setup_before_parallel(_client)
778
787
 
779
788
  # opt
@@ -794,7 +803,7 @@ class FEMOpt:
794
803
  subprocess_indices,
795
804
  [self.worker_status_list] * len(subprocess_indices),
796
805
  [wait_setup] * len(subprocess_indices),
797
- workers=worker_addresses,
806
+ workers=worker_addresses if self.opt.is_cluster else worker_addresses[1:],
798
807
  allow_other_workers=False,
799
808
  )
800
809
 
@@ -818,6 +827,7 @@ class FEMOpt:
818
827
  ),
819
828
  kwargs=dict(
820
829
  skip_reconstruct=True,
830
+ space_dir=self._extra_space_dir,
821
831
  )
822
832
  )
823
833
  t_main.start()
@@ -980,8 +980,13 @@ class OptimizationStatus:
980
980
  TERMINATE_ALL = 60
981
981
  CRASHED = 70
982
982
 
983
- def __init__(self, client, name='entire'):
984
- self._future = client.submit(_OptimizationStatusActor, actor=True)
983
+ def __init__(self, client, worker_address, name='entire'):
984
+ self._future = client.submit(
985
+ _OptimizationStatusActor,
986
+ actor=True,
987
+ workers=[worker_address],
988
+ allow_other_workers=False,
989
+ )
985
990
  self._actor = self._future.result()
986
991
  self.name = name
987
992
  self.set(self.INITIALIZING)
@@ -21,12 +21,11 @@ from pyfemtet.core import SolveError
21
21
  from pyfemtet.opt.optimizer.parameter import Parameter
22
22
 
23
23
  from pyfemtet.dispatch_extensions import _get_pid, dispatch_specific_femtet
24
- from pyfemtet.dispatch_extensions._impl import _NestableSpawnProcess
25
24
 
26
25
  from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
27
- from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
28
26
 
29
27
  from pyfemtet._util.excel_macro_util import watch_excel_macro_error
28
+ from pyfemtet._util.dask_util import lock_or_no_lock
30
29
 
31
30
  from pyfemtet._warning import show_experimental_warning
32
31
 
@@ -80,11 +79,53 @@ class ExcelInterface(FEMInterface):
80
79
  Excel マクロ関数のタイムアウト時間を秒単位で指定
81
80
  します。 None の場合はタイムアウトなしとなります。
82
81
 
83
- with_call_femtet (bool, optional):
84
- Femtet を呼び出すかどうかを指定します。
82
+ setup_xlsm_path (str or Path, optional):
83
+ セットアップ時に呼ぶ関数を含む xlsm のパスです。
84
+ 指定しない場合は ``input_xlsm_path`` と
85
+ 同じと見做します。
86
+
87
+ setup_procedure_name (str, optional):
88
+ セットアップ時に呼ぶマクロ関数名です。
89
+ 指定しない場合、セットアップ時に何もしません。
90
+
91
+ setup_procedure_args (list or tuple, optional):
92
+ セットアップ時に呼ぶマクロ関数の引数です。
93
+
94
+ teardown_xlsm_path (str or Path, optional):
95
+ 終了時に呼ぶ関数を含む xlsm のパスです。
96
+ 指定しない場合は ``input_xlsm_path`` と
97
+ 同じと見做します。
98
+
99
+ teardown_procedure_name (str, optional):
100
+ 終了時に呼ぶマクロ関数名です。
101
+ 指定しない場合、終了時に何もしません。
102
+
103
+ teardown_procedure_args (list or tuple, optional):
104
+ 終了時に呼ぶマクロ関数の引数です。
105
+
106
+ visible (bool):
107
+ excel を可視化するかどうかです。
108
+ ただし、 True を指定した場合でもマクロの実行中は
109
+ 不可視になります。
110
+ デフォルトは False です。
111
+
112
+ display_alerts (bool):
113
+ excel ダイアログを表示するかどうかです。
114
+ デバッグ目的の場合以外は True にしないでください。
85
115
  デフォルトは False です。
86
116
 
87
-
117
+ terminate_excel_when_quit (bool):
118
+ 終了時に Excel を終了するかどうかです。
119
+ 指定しない場合、 connect_method が 'new' の場合
120
+ True とふるまい 'auto' の場合 False と振舞います。
121
+
122
+ interactive (bool):
123
+ excel を対話モードにするかどうかです。
124
+ False にすると、 visible == True であっても
125
+ 自動化プロセス中にユーザーが誤って
126
+ Excel 本体を操作できないようにします。
127
+ デフォルトは True です。
128
+
88
129
  Attributes:
89
130
  input_xlsm_path (Path):
90
131
  設計変数の定義を含む Excel ファイルのパス。
@@ -133,12 +174,12 @@ class ExcelInterface(FEMInterface):
133
174
 
134
175
  """
135
176
 
136
- input_xlsm_path: Path # 操作対象の xlsm パス
177
+ input_xlsm_path: str # 操作対象の xlsm パス
137
178
  input_sheet_name: str # 変数セルを定義しているシート名
138
- output_xlsm_path: Path # 操作対象の xlsm パス (指定しない場合、input と同一)
179
+ output_xlsm_path: str # 操作対象の xlsm パス (指定しない場合、input と同一)
139
180
  output_sheet_name: str # 計算結果セルを定義しているシート名 (指定しない場合、input と同一)
140
181
 
141
- # TODO: related_file_paths: dict[Path] を必要なら実装 # 並列時に個別に並列プロセスの space にアップロードする必要のあるパス
182
+ related_file_paths: list[str] # 並列時に個別に並列プロセスの space にアップロードする必要のあるパス
142
183
 
143
184
  procedure_name: str # マクロ関数名(or モジュール名.関数名)
144
185
  procedure_args: list # マクロ関数の引数
@@ -148,17 +189,26 @@ class ExcelInterface(FEMInterface):
148
189
  sh_input: CDispatch # 変数の定義された WorkSheet
149
190
  wb_output: CDispatch # システムを構成する Workbook
150
191
  sh_output: CDispatch # 計算結果の定義された WorkSheet (sh_input と同じでもよい)
192
+ wb_setup: CDispatch # システムを構成する Workbook
193
+ wb_teardown: CDispatch # システムを構成する Workbook
151
194
 
152
- visible: bool = False # excel を可視化するかどうか
153
- display_alerts: bool = False # ダイアログを表示するかどうか
195
+ visible: bool # excel を可視化するかどうか
196
+ display_alerts: bool # ダイアログを表示するかどうか
154
197
  terminate_excel_when_quit: bool # 終了時に Excel を終了するかどうか
198
+ interactive: bool # excel を対話モードにするかどうか
155
199
 
156
200
  _load_problem_from_me: bool = True
157
201
  _excel_pid: int
158
202
  _excel_hwnd: int
203
+ _with_femtet_autosave_setting: bool = True # Femtet の自動保存機能の自動設定を行うかどうか。Femtet がインストールされていない場合はオフにする。クラス変数なので、インスタンス化前に設定する。
159
204
  _femtet_autosave_buffer: bool # Femtet の自動保存機能の一時退避場所。最適化中はオフにする。
160
- _with_call_femtet: bool # Femtet を Python から起動するかどうか。Excel から起動できる場合は False でよい。
161
205
 
206
+ setup_xlsm_path: str
207
+ setup_procedure_name: str
208
+ setup_procedure_args: list or tuple
209
+ teardown_xlsm_path: str
210
+ teardown_procedure_name: str
211
+ teardown_procedure_args: list or tuple
162
212
 
163
213
  def __init__(
164
214
  self,
@@ -170,39 +220,67 @@ class ExcelInterface(FEMInterface):
170
220
  procedure_args: list or tuple = None,
171
221
  connect_method: str = 'auto', # or 'new'
172
222
  procedure_timeout: float or None = None,
173
- with_call_femtet: bool = False
223
+ setup_xlsm_path: str or Path = None,
224
+ setup_procedure_name: str = None,
225
+ setup_procedure_args: list or tuple = None,
226
+ teardown_xlsm_path: str or Path = None,
227
+ teardown_procedure_name: str = None,
228
+ teardown_procedure_args: list or tuple = None,
229
+ related_file_paths: list[str or Path] = None,
230
+ visible: bool = False,
231
+ display_alerts: bool = False,
232
+ terminate_excel_when_quit: bool = None,
233
+ interactive: bool = True,
174
234
  ):
175
235
 
176
236
  show_experimental_warning("ExcelInterface")
177
237
 
178
238
  # 初期化
179
- self.input_xlsm_path = None # あとで取得する
239
+ self.input_xlsm_path = str(input_xlsm_path) # あとで再取得する
180
240
  self.input_sheet_name = input_sheet_name
181
- self.output_xlsm_path = None # あとで取得する
241
+ self.output_xlsm_path = str(input_xlsm_path) if output_xlsm_path is None else str(output_xlsm_path)
182
242
  self.output_sheet_name = output_sheet_name or self.input_sheet_name
183
243
  self.procedure_name = procedure_name or 'FemtetMacro.FemtetMain'
184
244
  self.procedure_args = procedure_args or []
185
245
  assert connect_method in ['new', 'auto']
186
246
  self.connect_method = connect_method
187
- self._femtet_autosave_buffer = _get_autosave_enabled()
188
247
  self.procedure_timeout = procedure_timeout
189
- self._with_call_femtet = with_call_femtet
190
- self.terminate_excel_when_quit = self.connect_method == 'new'
248
+ if terminate_excel_when_quit is None:
249
+ self.terminate_excel_when_quit = self.connect_method == 'new'
250
+ else:
251
+ self.terminate_excel_when_quit = terminate_excel_when_quit
252
+
253
+ self.setup_xlsm_path = str(input_xlsm_path) if setup_xlsm_path is None else str(setup_xlsm_path) # あとで取得する
254
+ self.setup_procedure_name = setup_procedure_name
255
+ self.setup_procedure_args = setup_procedure_args or []
256
+
257
+ self.teardown_xlsm_path = str(input_xlsm_path) if teardown_xlsm_path is None else str(teardown_xlsm_path) # あとで取得する
258
+ self.teardown_procedure_name = teardown_procedure_name
259
+ self.teardown_procedure_args = teardown_procedure_args or []
260
+
261
+ self.related_file_paths = [str(p) for p in related_file_paths] if related_file_paths is not None else []
262
+
263
+ self.visible = visible
264
+ self.interactive = interactive
265
+ self.display_alerts = display_alerts
191
266
 
192
267
  # dask サブプロセスのときは space 直下の input_xlsm_path を参照する
193
268
  try:
194
269
  worker = get_worker()
195
- space = worker.local_directory
196
- self.input_xlsm_path = Path(os.path.join(space, os.path.basename(input_xlsm_path))).resolve()
197
- self.output_xlsm_path = Path(os.path.join(space, os.path.basename(output_xlsm_path))).resolve()
270
+ space = os.path.abspath(worker.local_directory)
271
+ self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
272
+ self.output_xlsm_path = os.path.join(space, os.path.basename(self.output_xlsm_path))
273
+ self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
274
+ self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
275
+ self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
198
276
 
199
277
  # main プロセスの場合は絶対パスを参照する
200
278
  except ValueError:
201
- self.input_xlsm_path = Path(os.path.abspath(input_xlsm_path)).resolve()
202
- if output_xlsm_path is None:
203
- self.output_xlsm_path = self.input_xlsm_path
204
- else:
205
- self.output_xlsm_path = Path(os.path.abspath(output_xlsm_path)).resolve()
279
+ self.input_xlsm_path = os.path.abspath(self.input_xlsm_path)
280
+ self.output_xlsm_path = os.path.abspath(self.output_xlsm_path)
281
+ self.setup_xlsm_path = os.path.abspath(self.setup_xlsm_path)
282
+ self.teardown_xlsm_path = os.path.abspath(self.teardown_xlsm_path)
283
+ self.related_file_paths = [os.path.abspath(p) for p in self.related_file_paths]
206
284
 
207
285
  # サブプロセスでの restore のための情報保管
208
286
  kwargs = dict(
@@ -213,26 +291,109 @@ class ExcelInterface(FEMInterface):
213
291
  procedure_name=self.procedure_name,
214
292
  procedure_args=self.procedure_args,
215
293
  connect_method='new', # subprocess で connect する際は new を強制する
294
+ terminate_excel_when_quit=True, # なので終了時は破棄する
216
295
  procedure_timeout=self.procedure_timeout,
217
- with_call_femtet=self._with_call_femtet,
296
+ setup_xlsm_path=self.setup_xlsm_path,
297
+ setup_procedure_name=self.setup_procedure_name,
298
+ setup_procedure_args=self.setup_procedure_args,
299
+ teardown_xlsm_path=self.teardown_xlsm_path,
300
+ teardown_procedure_name=self.teardown_procedure_name,
301
+ teardown_procedure_args=self.teardown_procedure_args,
302
+ related_file_paths=self.related_file_paths,
303
+ visible=self.visible,
304
+ interactive=self.interactive,
305
+ display_alerts=self.display_alerts,
218
306
  )
219
307
  FEMInterface.__init__(self, **kwargs)
220
308
 
221
309
  def __del__(self):
222
- try:
223
- _set_autosave_enabled(self._femtet_autosave_buffer)
224
- finally:
225
- pass
310
+ pass
226
311
 
227
312
  def _setup_before_parallel(self, client) -> None:
228
313
  # メインプロセスで、並列プロセスを開始する前に行う前処理
229
314
 
230
- input_xlsm_path: Path = self.kwargs['input_xlsm_path']
231
- output_xlsm_path: Path = self.kwargs['output_xlsm_path']
315
+ client.upload_file(self.input_xlsm_path, False)
316
+
317
+ if not is_same_path(self.input_xlsm_path, self.output_xlsm_path):
318
+ client.upload_file(self.output_xlsm_path, False)
319
+
320
+ if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
321
+ client.upload_file(self.setup_xlsm_path, False)
322
+
323
+ if not is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
324
+ client.upload_file(self.setup_xlsm_path, False)
325
+
326
+ for path in self.related_file_paths:
327
+ client.upload_file(path, False)
232
328
 
233
- client.upload_file(str(input_xlsm_path), False)
234
- if input_xlsm_path.resolve() != output_xlsm_path.resolve():
235
- client.upload_file(str(output_xlsm_path), False)
329
+ def _setup_after_parallel(self, *args, **kwargs):
330
+ """サブプロセス又はメインプロセスのサブスレッドで、最適化を開始する前の前処理"""
331
+
332
+ # kwargs で space_dir が与えられている場合、そちらを使用する
333
+ # メインプロセスで呼ばれることを想定
334
+ if 'space_dir' in kwargs.keys():
335
+ space = kwargs['space_dir']
336
+ if space is not None:
337
+ self.input_xlsm_path = os.path.join(space, os.path.basename(self.input_xlsm_path))
338
+ self.output_xlsm_path = os.path.join(space, os.path.basename(self.output_xlsm_path))
339
+ self.setup_xlsm_path = os.path.join(space, os.path.basename(self.setup_xlsm_path))
340
+ self.teardown_xlsm_path = os.path.join(space, os.path.basename(self.teardown_xlsm_path))
341
+ self.related_file_paths = [os.path.join(space, os.path.basename(p)) for p in self.related_file_paths]
342
+
343
+ # connect_method が auto でかつ使用中のファイルを開こうとする場合に備えて excel のファイル名を変更
344
+ subprocess_idx = kwargs['opt'].subprocess_idx
345
+
346
+ def proc_path(path, ignore_no_exists):
347
+ exclude_ext, ext = os.path.splitext(path)
348
+ new_path = exclude_ext + f'{subprocess_idx}' + ext
349
+ if os.path.exists(path): # input と output が同じの場合など。input がないのはおかしい
350
+ os.rename(path, new_path)
351
+ elif not ignore_no_exists:
352
+ raise FileNotFoundError(f'{path} が見つかりません。')
353
+ return new_path
354
+
355
+ self.input_xlsm_path = proc_path(self.input_xlsm_path, False)
356
+ self.output_xlsm_path = proc_path(self.output_xlsm_path, True)
357
+ self.setup_xlsm_path = proc_path(self.setup_xlsm_path, True)
358
+ self.teardown_xlsm_path = proc_path(self.teardown_xlsm_path, True)
359
+
360
+ # スレッドが変わっているかもしれないので win32com の初期化
361
+ CoInitialize()
362
+
363
+ # 最適化中は femtet の autosave を無効にする
364
+ if self._with_femtet_autosave_setting:
365
+ from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled, _get_autosave_enabled
366
+ self._femtet_autosave_buffer = _get_autosave_enabled()
367
+ _set_autosave_enabled(False)
368
+
369
+ # excel に繋ぐ
370
+ self.connect_excel(self.connect_method)
371
+
372
+ # load_objective は 1 回目に呼ばれたのが main thread なので
373
+ # subprocess に入った後でもう一度 load objective を行う
374
+ from pyfemtet.opt.optimizer import AbstractOptimizer
375
+ from pyfemtet.opt._femopt_core import Objective
376
+ opt: AbstractOptimizer = kwargs['opt']
377
+ obj: Objective
378
+ for obj_name, obj in opt.objectives.items():
379
+ if isinstance(obj.fun, ScapeGoatObjective):
380
+ opt.objectives[obj_name].fun = self.objective_from_excel
381
+
382
+ # excel の setup 関数を必要なら実行する
383
+ if self.setup_procedure_name is not None:
384
+ with lock_or_no_lock('excel_setup_procedure'):
385
+ try:
386
+ with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout, restore_book=False):
387
+ self.excel.Run(
388
+ f'{self.setup_procedure_name}',
389
+ *self.setup_procedure_args
390
+ )
391
+
392
+ # 再計算
393
+ self.excel.CalculateFull()
394
+
395
+ except com_error as e:
396
+ raise RuntimeError(f'Failed to run macro {self.setup_procedure_args}. The original message is: {e}')
236
397
 
237
398
  def connect_excel(self, connect_method):
238
399
 
@@ -253,6 +414,7 @@ class ExcelInterface(FEMInterface):
253
414
  # 可視性の設定
254
415
  self.excel.Visible = self.visible
255
416
  self.excel.DisplayAlerts = self.display_alerts
417
+ self.excel.Interactive = self.interactive
256
418
 
257
419
  # 開く
258
420
  self.excel.Workbooks.Open(str(self.input_xlsm_path))
@@ -271,11 +433,10 @@ class ExcelInterface(FEMInterface):
271
433
  else:
272
434
  raise RuntimeError(f'Sheet {self.input_sheet_name} does not exist in the book {self.wb_input.Name}.')
273
435
 
274
- if self.input_xlsm_path.resolve() == self.output_xlsm_path.resolve():
436
+ # 開く (output)
437
+ if is_same_path(self.input_xlsm_path, self.output_xlsm_path):
275
438
  self.wb_output = self.wb_input
276
-
277
439
  else:
278
- # 開く (output)
279
440
  self.excel.Workbooks.Open(str(self.output_xlsm_path))
280
441
  for wb in self.excel.Workbooks:
281
442
  if wb.Name == os.path.basename(self.output_xlsm_path):
@@ -292,9 +453,35 @@ class ExcelInterface(FEMInterface):
292
453
  else:
293
454
  raise RuntimeError(f'Sheet {self.output_sheet_name} does not exist in the book {self.wb_output.Name}.')
294
455
 
456
+ # 開く (setup)
457
+ if is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
458
+ self.wb_setup = self.wb_input
459
+ else:
460
+ self.excel.Workbooks.Open(self.setup_xlsm_path)
461
+ for wb in self.excel.Workbooks:
462
+ if wb.Name == os.path.basename(self.setup_xlsm_path):
463
+ self.wb_setup = wb
464
+ break
465
+ else:
466
+ raise RuntimeError(f'Cannot open {self.setup_xlsm_path}')
467
+
468
+ # 開く (teardown)
469
+ if is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
470
+ self.wb_teardown = self.wb_input
471
+ else:
472
+ self.excel.Workbooks.Open(self.teardown_xlsm_path)
473
+ for wb in self.excel.Workbooks:
474
+ if wb.Name == os.path.basename(self.teardown_xlsm_path):
475
+ self.wb_teardown = wb
476
+ break
477
+ else:
478
+ raise RuntimeError(f'Cannot open {self.teardown_xlsm_path}')
479
+
295
480
  # book に参照設定を追加する
296
481
  self.add_femtet_ref_xla(self.wb_input)
297
482
  self.add_femtet_ref_xla(self.wb_output)
483
+ self.add_femtet_ref_xla(self.wb_setup)
484
+ self.add_femtet_ref_xla(self.wb_teardown)
298
485
 
299
486
  def add_femtet_ref_xla(self, wb):
300
487
 
@@ -345,38 +532,6 @@ class ExcelInterface(FEMInterface):
345
532
  if ref.Description == 'FemtetMacro': # FemtetMacro
346
533
  wb.VBProject.References.Remove(ref)
347
534
 
348
- def _setup_after_parallel(self, *args, **kwargs):
349
- # サブプロセス又はメインプロセスのサブスレッドで、最適化を開始する前の前処理
350
-
351
- # スレッドが変わっているかもしれないので win32com の初期化
352
- CoInitialize()
353
-
354
- # 最適化中は femtet の autosave を無効にする
355
- _set_autosave_enabled(False)
356
-
357
- # 必要なら Femtet を起動する
358
- if self._with_call_femtet:
359
- util.execute_femtet()
360
-
361
- # 直後の Excel 起動に間に合わない場合があるため
362
- # Femtet が Dispatch 可能になるまで捨てプロセスで待つ
363
- p = _NestableSpawnProcess(target=wait_femtet)
364
- p.start()
365
- p.join()
366
-
367
- # excel に繋ぐ
368
- self.connect_excel(self.connect_method)
369
-
370
- # load_objective は 1 回目に呼ばれたのが main thread なので
371
- # subprocess に入った後でもう一度 load objective を行う
372
- from pyfemtet.opt.optimizer import AbstractOptimizer
373
- from pyfemtet.opt._femopt_core import Objective
374
- opt: AbstractOptimizer = kwargs['opt']
375
- obj: Objective
376
- for obj_name, obj in opt.objectives.items():
377
- if isinstance(obj.fun, ScapeGoatObjective):
378
- opt.objectives[obj_name].fun = self.objective_from_excel
379
-
380
535
  def update(self, parameters: pd.DataFrame) -> None:
381
536
 
382
537
  # params を作成
@@ -407,48 +562,75 @@ class ExcelInterface(FEMInterface):
407
562
 
408
563
  def quit(self):
409
564
  if self.terminate_excel_when_quit:
565
+
566
+ already_terminated = not hasattr(self, 'excel')
567
+ if already_terminated:
568
+ return
569
+
410
570
  logger.info('Excel の終了処理を開始します。')
411
571
 
572
+ # 参照設定解除の前に終了処理を必要なら実施する
573
+ # excel の setup 関数を必要なら実行する
574
+ if self.teardown_procedure_name is not None:
575
+ with lock_or_no_lock('excel_setup_procedure'):
576
+ try:
577
+ with watch_excel_macro_error(self.excel, timeout=self.procedure_timeout, restore_book=False):
578
+ self.excel.Run(
579
+ f'{self.teardown_procedure_name}',
580
+ *self.teardown_procedure_args
581
+ )
582
+
583
+ # 再計算
584
+ self.excel.CalculateFull()
585
+
586
+ except com_error as e:
587
+ raise RuntimeError(f'Failed to run macro {self.teardown_procedure_args}. The original message is: {e}')
588
+
589
+ # 参照設定を解除する(不要な処理かも)
412
590
  self.remove_femtet_ref_xla(self.wb_input)
413
591
  self.remove_femtet_ref_xla(self.wb_output)
592
+ self.remove_femtet_ref_xla(self.wb_setup)
593
+ self.remove_femtet_ref_xla(self.wb_teardown)
414
594
 
595
+ # シートの COM オブジェクト変数を削除する
415
596
  del self.sh_input
416
597
  del self.sh_output
417
598
 
599
+ # workbook を閉じる
418
600
  with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
419
601
  self.wb_input.Close(SaveChanges := False)
420
- if self.input_xlsm_path.name != self.output_xlsm_path.name:
602
+
603
+ if not is_same_path(self.input_xlsm_path, self.output_xlsm_path):
421
604
  with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
422
605
  self.wb_output.Close(SaveChanges := False)
423
606
 
607
+ if not is_same_path(self.input_xlsm_path, self.setup_xlsm_path):
608
+ with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
609
+ self.wb_setup.Close(SaveChanges := False)
610
+
611
+ if not is_same_path(self.input_xlsm_path, self.teardown_xlsm_path):
612
+ with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
613
+ self.wb_teardown.Close(SaveChanges := False)
614
+
424
615
  del self.wb_input
425
616
  del self.wb_output
617
+ del self.wb_setup
618
+ del self.wb_teardown
619
+
426
620
 
621
+ # excel の終了
427
622
  with watch_excel_macro_error(self.excel, timeout=10, restore_book=False):
428
623
  self.excel.Quit()
429
624
  del self.excel
430
625
 
431
- gc.collect() # ここで Excel のプロセスが残らず落ちる
626
+ # ここで Excel のプロセスが残らず落ちる
627
+ gc.collect()
432
628
 
433
- if self._with_call_femtet:
434
-
435
- # quit した後ならば femtet を終了できる
436
- # excel の process の消滅を待つ
437
- logger.info('Excel の終了を待っています。')
438
- while self._excel_pid == _get_pid(self._excel_hwnd):
439
- sleep(1)
440
-
441
- # TODO: 正確だが時間がかかる。選択できるようにしたほうがいいかもしれない。
442
- logger.info('終了する Femtet を特定しています。')
443
- femtet_pid = util.get_last_executed_femtet_process_id()
444
- from multiprocessing import Process
445
- p = Process(target=_terminate_femtet, args=(femtet_pid,))
446
- p.start()
447
- p.join()
448
- logger.info('Excel-Femtet を終了しました。')
449
-
450
- logger.info('自動保存機能の設定を元に戻しています。')
451
- _set_autosave_enabled(self._femtet_autosave_buffer)
629
+ if self._with_femtet_autosave_setting:
630
+ from pyfemtet._femtet_config_util.autosave import _set_autosave_enabled
631
+ logger.info('自動保存機能の設定を元に戻しています。')
632
+ _set_autosave_enabled(self._femtet_autosave_buffer)
633
+ logger.info('自動保存機能の設定を元に戻しました。')
452
634
 
453
635
  # 直接アクセスしてもよいが、ユーザーに易しい名前にするためだけのプロパティ
454
636
  @property
@@ -583,5 +765,12 @@ class ScapeGoatObjective:
583
765
  return tuple()
584
766
 
585
767
 
768
+ def is_same_path(p1, p2):
769
+ _p1 = os.path.abspath(p1).lower()
770
+ _p2 = os.path.abspath(p2).lower()
771
+ return _p1 == _p2
772
+
773
+
774
+
586
775
  if __name__ == '__main__':
587
776
  ExcelInterface(..., ...)
@@ -86,10 +86,6 @@ class FemtetInterface(FEMInterface):
86
86
  it will be None and no parametric outputs are used
87
87
  as objectives.
88
88
 
89
- confirm_before_exit (bool):
90
- Whether to confirm before (abnormal) termination.
91
- Default is True.
92
-
93
89
  **kwargs: Additional arguments from inherited classes.
94
90
 
95
91
  Warning:
@@ -115,7 +111,6 @@ class FemtetInterface(FEMInterface):
115
111
  allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
116
112
  open_result_with_gui: bool = True,
117
113
  parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
118
- confirm_before_exit: bool = True,
119
114
  **kwargs # 継承されたクラスからの引数
120
115
  ):
121
116
 
@@ -145,7 +140,6 @@ class FemtetInterface(FEMInterface):
145
140
  self.parametric_output_indexes_use_as_objective = parametric_output_indexes_use_as_objective
146
141
  self._original_autosave_enabled = _get_autosave_enabled()
147
142
  _set_autosave_enabled(False)
148
- self.confirm_before_exit = confirm_before_exit
149
143
 
150
144
  # dask サブプロセスのときは femprj を更新し connect_method を new にする
151
145
  try:
@@ -183,7 +177,6 @@ class FemtetInterface(FEMInterface):
183
177
  open_result_with_gui=self.open_result_with_gui,
184
178
  parametric_output_indexes_use_as_objective=self.parametric_output_indexes_use_as_objective,
185
179
  save_pdt=self.save_pdt,
186
- confirm_before_exit=self.confirm_before_exit,
187
180
  **kwargs
188
181
  )
189
182
 
@@ -253,11 +246,9 @@ class FemtetInterface(FEMInterface):
253
246
  cmd = f'{sys.executable} -m win32com.client.makepy FemtetMacro'
254
247
  os.system(cmd)
255
248
  message = Msg.ERR_NO_MAKEPY
256
- print('================')
257
- print(message)
258
- print('================')
259
- if self.confirm_before_exit:
260
- input('Press enter to finish...')
249
+ logger.error('================')
250
+ logger.error(message)
251
+ logger.error('================')
261
252
  raise RuntimeError(message)
262
253
 
263
254
  if self.Femtet is None:
@@ -541,24 +532,20 @@ class FemtetInterface(FEMInterface):
541
532
  try:
542
533
  variable_names = self.Femtet.GetVariableNames_py()
543
534
  except AttributeError as e:
544
- print('================')
535
+ logger.error('================')
545
536
  logger.error(Msg.ERR_CANNOT_ACCESS_API + 'GetVariableNames_py')
546
537
  logger.error(Msg.CERTIFY_MACRO_VERSION)
547
- print('================')
548
- if self.confirm_before_exit:
549
- input(Msg.ENTER_TO_QUIT)
538
+ logger.error('================')
550
539
  raise e
551
540
 
552
541
  if variable_names is not None:
553
542
  if param_name in variable_names:
554
543
  return self.Femtet.GetVariableValue(param_name)
555
544
  message = Msg.ERR_NO_SUCH_PARAMETER_IN_FEMTET
556
- print('================')
545
+ logger.error('================')
557
546
  logger.error(message)
558
547
  logger.error(f'`{param_name}` not in {variable_names}')
559
- print('================')
560
- if self.confirm_before_exit:
561
- input(Msg.ENTER_TO_QUIT)
548
+ logger.error('================')
562
549
  raise RuntimeError(message)
563
550
  else:
564
551
  return None
@@ -302,6 +302,7 @@ class AbstractOptimizer(ABC):
302
302
  worker_status_list, # 他の worker の status オブジェクト
303
303
  wait_setup, # 他の worker の status が ready になるまで待つか
304
304
  skip_reconstruct=False, # reconstruct fem を行うかどうか
305
+ space_dir=None, # 特定の space_dir を使うかどうか
305
306
  ) -> Optional[Exception]:
306
307
 
307
308
  # 自分の worker_status の取得
@@ -314,7 +315,7 @@ class AbstractOptimizer(ABC):
314
315
 
315
316
  # set_fem をはじめ、終了したらそれを示す
316
317
  self._reconstruct_fem(skip_reconstruct)
317
- self.fem._setup_after_parallel(opt=self)
318
+ self.fem._setup_after_parallel(opt=self, space_dir=space_dir)
318
319
  self.worker_status.set(OptimizationStatus.WAIT_OTHER_WORKERS)
319
320
 
320
321
  # wait_setup or not
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyfemtet
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Design parameter optimization using Femtet.
5
5
  Home-page: https://github.com/pyfemtet/pyfemtet
6
6
  License: BSD-3-Clause
@@ -22,6 +22,7 @@ Requires-Dist: dash-bootstrap-components (>=1.5.0,<2.0.0)
22
22
  Requires-Dist: dask (>=2023.12.1,<2024.0.0)
23
23
  Requires-Dist: distributed (>=2023.12.1,<2024.0.0)
24
24
  Requires-Dist: femtetutils (>=1.0.0,<2.0.0)
25
+ Requires-Dist: fire (>=0.6.0,<0.7.0)
25
26
  Requires-Dist: numpy (>=1.26.2,<2.0.0)
26
27
  Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
27
28
  Requires-Dist: optuna (>=3.4.0,<5.0.0)
@@ -1,4 +1,4 @@
1
- pyfemtet/__init__.py,sha256=P1kRuZW02tF-ekLuBqSjV_tIKKqcbfRPDdpp9C8ftUY,21
1
+ pyfemtet/__init__.py,sha256=tUukPDbH9wVBvn9_DYqm0p_Q7TagQGM_2ZX042hSuUs,21
2
2
  pyfemtet/_femtet_config_util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  pyfemtet/_femtet_config_util/autosave.py,sha256=dNirA9XGuFehas8_Jkj2BW9GOzMbPyhnt1WHcH_ObSU,2070
4
4
  pyfemtet/_femtet_config_util/exit.py,sha256=0BWID-tjOkmZwmgPFkcJMkWW39voccz5ARIBWvZbHaw,1877
@@ -10,7 +10,8 @@ pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po,sha256=F2bJGHVMtk086pekjVwY
10
10
  pyfemtet/_message/locales/messages.pot,sha256=8Yjf462pJdEtxBLySKT34zMG5CH5uLB_8VaJQll_QsY,14493
11
11
  pyfemtet/_message/messages.py,sha256=F8ENLZKoHq5irn-Ag7rqA3aSDsTmRWDyNHvOLY76ROI,13368
12
12
  pyfemtet/_util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- pyfemtet/_util/excel_macro_util.py,sha256=wja_yE79xXHnr8YuesDypXRXkC6mI4ZYaUU-1wunZCE,7470
13
+ pyfemtet/_util/dask_util.py,sha256=ufgr4m8slvyWP97lWBwolysQpJ1PmAO_-OI8IlEyvU8,233
14
+ pyfemtet/_util/excel_macro_util.py,sha256=cF1Z3yl9FMM0J7dpMRTsle8uYxYcfHhQC0QffnVovdY,7944
14
15
  pyfemtet/_warning.py,sha256=TSOj8mOhuyfOUJB24LsW6GNhTA3IzIEevJw_hLKTrq8,2205
15
16
  pyfemtet/brep/__init__.py,sha256=V1IQ2s-8eWjXOVlTp2jMav9u-NBiSkmyAX1vmtHDEso,73
16
17
  pyfemtet/brep/_impl.py,sha256=Amf_wsUxUosQB3XXhErJ5RGKXBxRnaaPpavb_0Xx6Ek,404
@@ -20,23 +21,23 @@ pyfemtet/dispatch_extensions/_impl.py,sha256=yH_yeAnQ-Xi9GfjX-FQt9u3yHnrLYIteRb6
20
21
  pyfemtet/logger/__init__.py,sha256=UOJ9n_U2xwdTrp0Xgg-N6geySxNzKqTBQlXsaH0kW_w,420
21
22
  pyfemtet/logger/_impl.py,sha256=rsAd0HpmveOaLS39ucp3U2OcDhQMWjC5fnVGhbJtWVw,6375
22
23
  pyfemtet/opt/__init__.py,sha256=wRR8LbEhb5I6MUgmnCgjB6-tqHlOVxDIo7yPkq0QbBs,758
23
- pyfemtet/opt/_femopt.py,sha256=h3DQpwdNyPDU9jxc52DEsHaDWO1mVnA4hOT5_omXglo,37377
24
- pyfemtet/opt/_femopt_core.py,sha256=rKuZcv3Dn9zzaXrcXXOWLyFFpL_6yva4-29lFTWikXQ,35288
24
+ pyfemtet/opt/_femopt.py,sha256=3mh68QrVeP3EAhy3xEaNyB97uNJYAUZvpaPv6ZRrMN4,37883
25
+ pyfemtet/opt/_femopt_core.py,sha256=7z9xS8oQ8wW7dMoPaLL5PUizdJJIhoIM0EsvTu6LZ2Q,35416
25
26
  pyfemtet/opt/_test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
27
  pyfemtet/opt/_test_utils/control_femtet.py,sha256=8oAl9y5V2n8Nnsgx_ebcZVzwFt1eI3swkdiKg6pg3-M,1085
27
28
  pyfemtet/opt/_test_utils/hyper_sphere.py,sha256=nQhw8EIY0DwvcTqrbKhkxiITLZifr4-nG77E-_6ggmA,700
28
29
  pyfemtet/opt/_test_utils/record_history.py,sha256=JCNJLZMCNTpJ6VT7iwEt2DIbwmsuQmgC0ClQSfcatj4,3915
29
30
  pyfemtet/opt/interface/__init__.py,sha256=P5Ij-xjB4628qdgacIXLu_WBaWCoBkAk4nEMUCAQzWs,458
30
31
  pyfemtet/opt/interface/_base.py,sha256=NVrvHVL7npgZbAQdMziA5TbTBghgi31JwrFH57edBKE,2615
31
- pyfemtet/opt/interface/_excel_interface.py,sha256=C9Ix1oUm2dHAHrKQPpb4gJo86iGJBcpXjXLa4c_VGSA,22853
32
- pyfemtet/opt/interface/_femtet.py,sha256=7FFwGJ4TFIhvjCCRIuEsJpJ-Cmo2OIO5cWKosLqcs-U,34944
32
+ pyfemtet/opt/interface/_excel_interface.py,sha256=mdPOCeEVrozcDBGIkprb8qSiCUYN2g07gz4KJRZdWSw,32479
33
+ pyfemtet/opt/interface/_femtet.py,sha256=REbi7-DdV0Keq-IgjDCTWHP9gjweayoORz3S04RfmkA,34443
33
34
  pyfemtet/opt/interface/_femtet_parametric.py,sha256=0pAEhHflp0wIxWBVMXI8nCC02oAyRKLinH3Y6O8bq3M,2224
34
35
  pyfemtet/opt/interface/_femtet_with_nx/__init__.py,sha256=-6W2g2FDEcKzGHmI5KAKQe-4U5jDpMj0CXuma-GZca0,83
35
36
  pyfemtet/opt/interface/_femtet_with_nx/_interface.py,sha256=oefISc6c6RPPyhPnWuzCb60tgsrzGiqoIWk1DsiKzTk,5986
36
37
  pyfemtet/opt/interface/_femtet_with_nx/update_model.py,sha256=P7VH0i_o-X9OUe6AGaLF1fACPeHNrMjcrOBCA3MMrI4,3092
37
38
  pyfemtet/opt/interface/_femtet_with_sldworks.py,sha256=qqo2P4qZN0d89uNQyohKxq-Yhdql5vC0QHg4bpy7Ky8,11011
38
39
  pyfemtet/opt/optimizer/__init__.py,sha256=Ia6viowECkG0IFXtFef0tJ4jDKsoDzJLqMJ9xLFH2LQ,543
39
- pyfemtet/opt/optimizer/_base.py,sha256=GsTOs3ZHLqCglYYRBk4XSJ5h1lR_W9eA2STiyQig6dM,12497
40
+ pyfemtet/opt/optimizer/_base.py,sha256=0cramby4lHPjNt6WoSn8XFhfx54xXMYucjD0O1uYz7w,12591
40
41
  pyfemtet/opt/optimizer/_optuna/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
42
  pyfemtet/opt/optimizer/_optuna/_botorch_patch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
43
  pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py,sha256=b2-PP2HM46kJS4cJkBWnxnW9AS9JfeVkEjmkoKK_ziE,8949
@@ -120,8 +121,8 @@ pyfemtet/opt/visualization/result_viewer/.gitignore,sha256=ryvb4aqbbsHireHWlPQfx
120
121
  pyfemtet/opt/visualization/result_viewer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
121
122
  pyfemtet/opt/visualization/result_viewer/application.py,sha256=WcHBx_J5eNLKSaprpk9BGifwhO04oN8FiNGYTWorrXA,1691
122
123
  pyfemtet/opt/visualization/result_viewer/pages.py,sha256=laEAKHAtdshCAHxgXo-zMNg3RP6lCxfszO3XwLnF1dU,32156
123
- pyfemtet-0.7.0.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
124
- pyfemtet-0.7.0.dist-info/METADATA,sha256=T0IJPwww5DvoZN1Z2ucdGJhBsThjPn05lVU27zfC_go,3371
125
- pyfemtet-0.7.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
126
- pyfemtet-0.7.0.dist-info/entry_points.txt,sha256=ZfYqRaoiPtuWqFi2_msccyrVF0LurMn-IHlYamAegZo,104
127
- pyfemtet-0.7.0.dist-info/RECORD,,
124
+ pyfemtet-0.7.1.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
125
+ pyfemtet-0.7.1.dist-info/METADATA,sha256=NQPiiH6Lfk_1zkuMKNVtOnegaWhJXp_hN6afJMmaWts,3408
126
+ pyfemtet-0.7.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
127
+ pyfemtet-0.7.1.dist-info/entry_points.txt,sha256=ZfYqRaoiPtuWqFi2_msccyrVF0LurMn-IHlYamAegZo,104
128
+ pyfemtet-0.7.1.dist-info/RECORD,,