pyfemtet 0.4.7__py3-none-any.whl → 0.4.9__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 (34) hide show
  1. pyfemtet/__init__.py +1 -1
  2. pyfemtet/_test_util.py +21 -1
  3. pyfemtet/opt/_femopt.py +1 -4
  4. pyfemtet/opt/_femopt_core.py +21 -4
  5. pyfemtet/opt/femprj_sample/cad_ex01_NX.py +11 -0
  6. pyfemtet/opt/femprj_sample/cad_ex01_SW.py +7 -0
  7. pyfemtet/opt/femprj_sample/gal_ex58_parametric.py +7 -0
  8. pyfemtet/opt/femprj_sample/gau_ex08_parametric.py +7 -0
  9. pyfemtet/opt/femprj_sample/her_ex40_parametric.py +7 -0
  10. pyfemtet/opt/femprj_sample/paswat_ex1_parametric.py +7 -0
  11. pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py +7 -0
  12. pyfemtet/opt/femprj_sample/wat_ex14_parametric.py +7 -0
  13. pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py +11 -0
  14. pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.py +7 -0
  15. pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.py +7 -0
  16. pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.py +7 -0
  17. pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.py +7 -0
  18. pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.py +7 -0
  19. pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +7 -0
  20. pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py +7 -0
  21. pyfemtet/opt/interface/_base.py +6 -0
  22. pyfemtet/opt/interface/_femtet.py +159 -19
  23. pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -2
  24. pyfemtet/opt/interface/_femtet_with_nx/update_model.py +36 -28
  25. pyfemtet/opt/opt/_base.py +2 -0
  26. pyfemtet/opt/visualization/_graphs.py +10 -13
  27. pyfemtet/opt/visualization/_monitor.py +89 -11
  28. pyfemtet/opt/visualization/result_viewer.py +13 -0
  29. {pyfemtet-0.4.7.dist-info → pyfemtet-0.4.9.dist-info}/METADATA +1 -1
  30. {pyfemtet-0.4.7.dist-info → pyfemtet-0.4.9.dist-info}/RECORD +33 -32
  31. pyfemtet-0.4.9.dist-info/entry_points.txt +3 -0
  32. pyfemtet-0.4.7.dist-info/entry_points.txt +0 -3
  33. {pyfemtet-0.4.7.dist-info → pyfemtet-0.4.9.dist-info}/LICENSE +0 -0
  34. {pyfemtet-0.4.7.dist-info → pyfemtet-0.4.9.dist-info}/WHEEL +0 -0
pyfemtet/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.4.7"
1
+ __version__ = "0.4.9"
pyfemtet/_test_util.py CHANGED
@@ -86,10 +86,30 @@ def _get_obj_from_csv(csv_path):
86
86
  return out, columns
87
87
 
88
88
 
89
- def is_equal_result(csv1, csv2):
89
+ def is_equal_result(csv1, csv2, result_save_to=None):
90
90
  """Check the equality of two result csv files."""
91
91
  df1, columns1 = _get_obj_from_csv(csv1)
92
92
  df2, columns2 = _get_obj_from_csv(csv2)
93
+
94
+ if result_save_to is not None:
95
+ import datetime
96
+ with open(result_save_to, 'a', encoding='utf-8', newline='\n') as f:
97
+ name = os.path.basename(csv1)
98
+ content = [
99
+ f'===== result of {name} =====\n',
100
+ f'{datetime.datetime.now()}\n',
101
+ f'----- column numbers -----\n',
102
+ f'csv1: {len(columns1)} columns\n',
103
+ f'csv2: {len(columns2)} columns\n',
104
+ f'----- row numbers -----\n',
105
+ f'csv1: {len(df1)} columns\n',
106
+ f'csv2: {len(df2)} columns\n',
107
+ f'----- difference -----\n',
108
+ f'max difference ratio: {(np.abs(df1.values - df2.values) / np.abs(df2.values)).max() * 100}%\n',
109
+ '\n'
110
+ ]
111
+ f.writelines(content)
112
+
93
113
  assert len(columns1) == len(columns2), '結果 csv の column 数が異なります。'
94
114
  assert len(df1) == len(df2), '結果 csv の row 数が異なります。'
95
115
  assert (np.abs(df1.values - df2.values) / np.abs(df2.values)).max() <= 0.01, '前回の結果と 1% を超える相違があります。'
pyfemtet/opt/_femopt.py CHANGED
@@ -513,10 +513,7 @@ class FEMOpt:
513
513
  sleep(1)
514
514
 
515
515
  # terminate monitor process
516
- if self._is_error_exit:
517
- self.status.set(OptimizationStatus.CRASHED)
518
- else:
519
- self.status.set(OptimizationStatus.TERMINATE_ALL)
516
+ self.status.set(OptimizationStatus.TERMINATE_ALL)
520
517
  logger.info(self.monitor_process_future.result())
521
518
  sleep(1)
522
519
 
@@ -14,7 +14,7 @@ import numpy as np
14
14
  import pandas as pd
15
15
  from scipy.stats.qmc import LatinHypercube
16
16
  from optuna._hypervolume import WFG
17
- from dask.distributed import Lock
17
+ from dask.distributed import Lock, get_client
18
18
 
19
19
  # win32com
20
20
  from win32com.client import constants, Constants
@@ -532,7 +532,17 @@ class History:
532
532
 
533
533
  return columns, metadata
534
534
 
535
- def record(self, parameters, objectives, constraints, obj_values, cns_values, message):
535
+ def record(
536
+ self,
537
+ parameters,
538
+ objectives,
539
+ constraints,
540
+ obj_values,
541
+ cns_values,
542
+ message,
543
+ postprocess_func,
544
+ postprocess_args,
545
+ ):
536
546
  """Records the optimization results in the history.
537
547
 
538
548
  Record only. NOT save.
@@ -544,7 +554,8 @@ class History:
544
554
  obj_values (list): The objective values.
545
555
  cns_values (list): The constraint values.
546
556
  message (str): Additional information or messages related to the optimization results.
547
-
557
+ postprocess_func (Callable): fem method to call after solving. i.e. save result file. Must take trial(int) for 1st argument.
558
+ postprocess_args (dict): arguments for `postprocess_func`. i.e. create binary data of result file in the worker process.
548
559
  """
549
560
 
550
561
  # create row
@@ -591,6 +602,12 @@ class History:
591
602
  self._calc_hypervolume(objectives) # update self.local_data
592
603
  self.actor_data = self.local_data
593
604
 
605
+ # save file
606
+ if postprocess_args is not None:
607
+ trial = self.local_data['trial'].values[-1]
608
+ client = get_client() # always returns valid client
609
+ client.run_on_scheduler(postprocess_func, trial, **postprocess_args)
610
+
594
611
  def _calc_non_domi(self, objectives):
595
612
 
596
613
  # 目的関数の履歴を取り出してくる
@@ -696,8 +713,8 @@ class OptimizationStatus:
696
713
  RUNNING = 30
697
714
  INTERRUPTING = 40
698
715
  TERMINATED = 50
699
- CRASHED = 55
700
716
  TERMINATE_ALL = 60
717
+ CRASHED = 70
701
718
 
702
719
  def __init__(self, client, name='entire'):
703
720
  self._future = client.submit(_OptimizationStatusActor, actor=True)
@@ -107,6 +107,10 @@ if __name__ == '__main__':
107
107
  fem = FemtetWithNXInterface(
108
108
  prt_path='cad_ex01_NX.prt',
109
109
  open_result_with_gui=False, # To calculate von Mises stress, set this argument to False. See Femtet Macro Help.
110
+ export_curves=False,
111
+ export_surfaces=False,
112
+ export_solids=True,
113
+ export_flattened_assembly=False,
110
114
  )
111
115
 
112
116
  # Initialize the FEMOpt object.
@@ -129,4 +133,11 @@ if __name__ == '__main__':
129
133
  # Run optimization.
130
134
  femopt.set_random_seed(42)
131
135
  femopt.optimize(n_trials=20)
136
+
137
+ # Stop script to keep process alive
138
+ # while you check the result in process monitor.
139
+ print('================================')
140
+ print('Finished. Press Enter to quit...')
141
+ print('================================')
142
+ input()
132
143
  femopt.terminate_all()
@@ -129,4 +129,11 @@ if __name__ == '__main__':
129
129
  # Run optimization.
130
130
  femopt.set_random_seed(42)
131
131
  femopt.optimize(n_trials=20)
132
+
133
+ # Stop script to keep process alive
134
+ # while you check the result in process monitor.
135
+ print('================================')
136
+ print('Finished. Press Enter to quit...')
137
+ print('================================')
138
+ input()
132
139
  femopt.terminate_all()
@@ -72,4 +72,11 @@ if __name__ == '__main__':
72
72
  # Run optimization.
73
73
  femopt.set_random_seed(42)
74
74
  femopt.optimize(n_trials=10)
75
+
76
+ # Stop script to keep process alive
77
+ # while you check the result in process monitor.
78
+ print('================================')
79
+ print('Finished. Press Enter to quit...')
80
+ print('================================')
81
+ input()
75
82
  femopt.terminate_all()
@@ -56,4 +56,11 @@ if __name__ == '__main__':
56
56
  # Run optimization.
57
57
  femopt.set_random_seed(42)
58
58
  femopt.optimize(n_trials=20)
59
+
60
+ # Stop script to keep process alive
61
+ # while you check the result in process monitor.
62
+ print('================================')
63
+ print('Finished. Press Enter to quit...')
64
+ print('================================')
65
+ input()
59
66
  femopt.terminate_all()
@@ -134,4 +134,11 @@ if __name__ == '__main__':
134
134
 
135
135
  femopt.set_random_seed(42)
136
136
  femopt.optimize(n_trials=15)
137
+
138
+ # Stop script to keep process alive
139
+ # while you check the result in process monitor.
140
+ print('================================')
141
+ print('Finished. Press Enter to quit...')
142
+ print('================================')
143
+ input()
137
144
  femopt.terminate_all()
@@ -58,4 +58,11 @@ if __name__ == '__main__':
58
58
  # Run optimization.
59
59
  femopt.set_random_seed(42)
60
60
  femopt.optimize(n_trials=15)
61
+
62
+ # Stop script to keep process alive
63
+ # while you check the result in process monitor.
64
+ print('================================')
65
+ print('Finished. Press Enter to quit...')
66
+ print('================================')
67
+ input()
61
68
  femopt.terminate_all()
@@ -59,4 +59,11 @@ if __name__ == '__main__':
59
59
  femopt.set_random_seed(42)
60
60
  # femopt.optimize(n_trials=15)
61
61
  femopt.optimize(n_trials=30, n_parallel=3) # This line is the only difference with no parallel pattern.
62
+
63
+ # Stop script to keep process alive
64
+ # while you check the result in process monitor.
65
+ print('================================')
66
+ print('Finished. Press Enter to quit...')
67
+ print('================================')
68
+ input()
62
69
  femopt.terminate_all()
@@ -56,4 +56,11 @@ if __name__ == '__main__':
56
56
  # Run optimization.
57
57
  femopt.set_random_seed(42)
58
58
  femopt.optimize(n_trials=15)
59
+
60
+ # Stop script to keep process alive
61
+ # while you check the result in process monitor.
62
+ print('================================')
63
+ print('Finished. Press Enter to quit...')
64
+ print('================================')
65
+ input()
59
66
  femopt.terminate_all()
@@ -103,6 +103,10 @@ if __name__ == '__main__':
103
103
  fem = FemtetWithNXInterface(
104
104
  prt_path='cad_ex01_NX.prt',
105
105
  open_result_with_gui=False,
106
+ export_curves=False,
107
+ export_surfaces=False,
108
+ export_solids=True,
109
+ export_flattened_assembly=False,
106
110
  )
107
111
 
108
112
  # FEMOpt オブジェクトの初期化 (最適化問題とFemtetとの接続を行います)
@@ -123,4 +127,11 @@ if __name__ == '__main__':
123
127
  # 最適化を実行
124
128
  femopt.set_random_seed(42)
125
129
  femopt.optimize(n_trials=20)
130
+
131
+ # プロセスモニタで結果を確認するために
132
+ # Enter キーが押されるまで処理を停止します。
133
+ print('================================')
134
+ print('Finished. Press Enter to quit...')
135
+ print('================================')
136
+ input()
126
137
  femopt.terminate_all()
@@ -123,4 +123,11 @@ if __name__ == '__main__':
123
123
  # 最適化を実行
124
124
  femopt.set_random_seed(42)
125
125
  femopt.optimize(n_trials=20)
126
+
127
+ # プロセスモニタで結果を確認するために
128
+ # Enter キーが押されるまで処理を停止します。
129
+ print('================================')
130
+ print('Finished. Press Enter to quit...')
131
+ print('================================')
132
+ input()
126
133
  femopt.terminate_all()
@@ -68,4 +68,11 @@ if __name__ == '__main__':
68
68
  # 最適化を実行
69
69
  femopt.set_random_seed(42)
70
70
  femopt.optimize(n_trials=10)
71
+
72
+ # プロセスモニタで結果を確認するために
73
+ # Enter キーが押されるまで処理を停止します。
74
+ print('================================')
75
+ print('Finished. Press Enter to quit...')
76
+ print('================================')
77
+ input()
71
78
  femopt.terminate_all()
@@ -55,4 +55,11 @@ if __name__ == '__main__':
55
55
  # 最適化を実行
56
56
  femopt.set_random_seed(42)
57
57
  femopt.optimize(n_trials=20)
58
+
59
+ # プロセスモニタで結果を確認するために
60
+ # Enter キーが押されるまで処理を停止します。
61
+ print('================================')
62
+ print('Finished. Press Enter to quit...')
63
+ print('================================')
64
+ input()
58
65
  femopt.terminate_all()
@@ -134,4 +134,11 @@ if __name__ == '__main__':
134
134
 
135
135
  femopt.set_random_seed(42)
136
136
  femopt.optimize(n_trials=15)
137
+
138
+ # プロセスモニタで結果を確認するために
139
+ # Enter キーが押されるまで処理を停止します。
140
+ print('================================')
141
+ print('Finished. Press Enter to quit...')
142
+ print('================================')
143
+ input()
137
144
  femopt.terminate_all()
@@ -56,4 +56,11 @@ if __name__ == '__main__':
56
56
  # 最適化を実行
57
57
  femopt.set_random_seed(42)
58
58
  femopt.optimize(n_trials=15)
59
+
60
+ # プロセスモニタで結果を確認するために
61
+ # Enter キーが押されるまで処理を停止します。
62
+ print('================================')
63
+ print('Finished. Press Enter to quit...')
64
+ print('================================')
65
+ input()
59
66
  femopt.terminate_all()
@@ -57,4 +57,11 @@ if __name__ == '__main__':
57
57
  femopt.set_random_seed(42)
58
58
  # femopt.optimize(n_trials=15)
59
59
  femopt.optimize(n_trials=30, n_parallel=3) # 並列計算しない場合との差はこの行のみです。
60
+
61
+ # プロセスモニタで結果を確認するために
62
+ # Enter キーが押されるまで処理を停止します。
63
+ print('================================')
64
+ print('Finished. Press Enter to quit...')
65
+ print('================================')
66
+ input()
60
67
  femopt.terminate_all()
@@ -54,4 +54,11 @@ if __name__ == '__main__':
54
54
  # 最適化を実行
55
55
  femopt.set_random_seed(42)
56
56
  femopt.optimize(n_trials=15)
57
+
58
+ # プロセスモニタで結果を確認するために
59
+ # Enter キーが押されるまで処理を停止します。
60
+ print('================================')
61
+ print('Finished. Press Enter to quit...')
62
+ print('================================')
63
+ input()
57
64
  femopt.terminate_all()
@@ -63,6 +63,12 @@ class FEMInterface(ABC):
63
63
  """Preprocessing after launching a dask worker and before run optimization (if implemented in concrete class)."""
64
64
  pass
65
65
 
66
+ def postprocess_func(self, trial: int, *args, dask_scheduler=None, **kwargs):
67
+ pass
68
+
69
+ def create_postprocess_args(self):
70
+ pass
71
+
66
72
 
67
73
  class NoFEM(FEMInterface):
68
74
  """Interface with no FEM for debug."""
@@ -59,9 +59,10 @@ class FemtetInterface(FEMInterface):
59
59
  self,
60
60
  femprj_path=None,
61
61
  model_name=None,
62
- connect_method='auto',
63
- strictly_pid_specify=True,
64
- allow_without_project=False,
62
+ connect_method='auto', # dask worker では __init__ の中で 'new' にするので super() の引数にしない。(しても意味がない)
63
+ save_pdt='all', # 'all' or None
64
+ strictly_pid_specify=True, # dask worker では True にしたいので super() の引数にしない。
65
+ allow_without_project=False, # main でのみ True を許容したいので super() の引数にしない。
65
66
  open_result_with_gui=True,
66
67
  parametric_output_indexes_use_as_objective=None,
67
68
  **kwargs # 継承されたクラスからの引数
@@ -80,6 +81,7 @@ class FemtetInterface(FEMInterface):
80
81
  self.allow_without_project = allow_without_project
81
82
  self.original_femprj_path = self.femprj_path
82
83
  self.open_result_with_gui = open_result_with_gui
84
+ self.save_pdt = save_pdt
83
85
 
84
86
  # その他のメンバーの宣言や初期化
85
87
  self.Femtet = None
@@ -106,6 +108,15 @@ class FemtetInterface(FEMInterface):
106
108
  # 開かれたモデルに応じて femprj_path と model を更新する
107
109
  self._connect_and_open_femtet()
108
110
 
111
+ # original_fem_prj が None なら必ず
112
+ # dask worker でないプロセスがオリジナルファイルを開いている
113
+ if self.original_femprj_path is None:
114
+ # dask worker でなければ original のはず
115
+ try:
116
+ worker = get_worker()
117
+ except ValueError:
118
+ self.original_femprj_path = self.femprj_path
119
+
109
120
  # 接続した Femtet の種類に応じて del 時に quit するかどうか決める
110
121
  self.quit_when_destruct = self.connected_method == 'new'
111
122
 
@@ -117,26 +128,13 @@ class FemtetInterface(FEMInterface):
117
128
  model_name=self.model_name,
118
129
  open_result_with_gui=self.open_result_with_gui,
119
130
  parametric_output_indexes_use_as_objective=self.parametric_output_indexes_use_as_objective,
131
+ save_pdt=self.save_pdt,
120
132
  **kwargs
121
133
  )
122
134
 
123
135
  def __del__(self):
124
136
  if self.quit_when_destruct:
125
- try:
126
- # 強制終了 TODO: 自動保存を回避する
127
- hwnd = self.Femtet.hWnd
128
- pid = _get_pid(hwnd)
129
- util.close_femtet(hwnd, 1, True)
130
- start = time()
131
- while psutil.pid_exists(pid):
132
- if time() - start > 30: # 30 秒経っても存在するのは何かおかしい
133
- os.kill(pid, signal.SIGKILL)
134
- break
135
- sleep(1)
136
- sleep(1)
137
-
138
- except (AttributeError, OSError): # already dead
139
- pass
137
+ self.quit()
140
138
  # CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
141
139
 
142
140
  def _connect_new_femtet(self):
@@ -626,8 +624,25 @@ class FemtetInterface(FEMInterface):
626
624
  print('================')
627
625
  input('終了するには Enter を押してください。')
628
626
  raise e
627
+
629
628
  else:
630
- util.close_femtet(self.Femtet.hWnd, timeout, force)
629
+ hwnd = self.Femtet.hWnd
630
+
631
+ # terminate
632
+ util.close_femtet(hwnd, timeout, force)
633
+
634
+ try:
635
+ pid = _get_pid(hwnd)
636
+ start = time()
637
+ while psutil.pid_exists(pid):
638
+ if time() - start > 30: # 30 秒経っても存在するのは何かおかしい
639
+ logger.error('Femtet の終了に失敗しました。')
640
+ break
641
+ sleep(1)
642
+ sleep(1)
643
+
644
+ except (AttributeError, OSError): # already dead
645
+ pass
631
646
 
632
647
  def _setup_before_parallel(self, client):
633
648
  client.upload_file(
@@ -637,3 +652,128 @@ class FemtetInterface(FEMInterface):
637
652
 
638
653
  def _version(self):
639
654
  return _version(Femtet=self.Femtet)
655
+
656
+ def create_postprocess_args(self):
657
+ file_content = self.create_result_file_content()
658
+ jpg_content = self.create_jpg_content()
659
+
660
+ out = dict(
661
+ original_femprj_path=self.original_femprj_path,
662
+ model_name=self.model_name,
663
+ pdt_file_content=file_content,
664
+ jpg_file_content=jpg_content,
665
+ )
666
+ return out
667
+
668
+ @staticmethod
669
+ def postprocess_func(
670
+ trial: int,
671
+ original_femprj_path: str,
672
+ model_name: str,
673
+ pdt_file_content=None,
674
+ jpg_file_content=None,
675
+ dask_scheduler=None
676
+ ):
677
+ result_dir = original_femprj_path.replace('.femprj', '.Results')
678
+ if pdt_file_content is not None:
679
+ pdt_path = os.path.join(result_dir, model_name + f'_trial{trial}.pdt')
680
+ with open(pdt_path, 'wb') as f:
681
+ f.write(pdt_file_content)
682
+
683
+ if jpg_file_content is not None:
684
+ jpg_path = os.path.join(result_dir, model_name + f'_trial{trial}.jpg')
685
+ with open(jpg_path, 'wb') as f:
686
+ f.write(jpg_file_content)
687
+
688
+ def create_result_file_content(self):
689
+ """Called after solve"""
690
+ if self.save_pdt == 'all':
691
+ # save to worker space
692
+ result_dir = self.femprj_path.replace('.femprj', '.Results')
693
+ pdt_path = os.path.join(result_dir, self.model_name + '.pdt')
694
+ succeed = self.Femtet.SavePDT(pdt_path, True)
695
+
696
+ # convert .pdt to ByteIO
697
+ if succeed:
698
+ with open(pdt_path, 'rb') as f:
699
+ content = f.read()
700
+ return content
701
+
702
+ else:
703
+ raise Exception('pdt ファイルの保存でエラーが発生しました。')
704
+
705
+ else:
706
+ return None
707
+
708
+ def create_jpg_content(self):
709
+ result_dir = self.femprj_path.replace('.femprj', '.Results')
710
+ jpg_path = os.path.join(result_dir, self.model_name + '.jpg')
711
+
712
+ # モデル表示画面の設定
713
+ self.Femtet.SetWindowSize(200, 200)
714
+ self.Femtet.Fit()
715
+ self.Femtet.ViewNumeric.SetCoord(1, 1, 1)
716
+
717
+ # ---モデルの画面を保存---
718
+ self.Femtet.Redraw() # 再描画
719
+ succeed = self.Femtet.SavePicture(jpg_path, 200, 200, 80)
720
+
721
+ self.Femtet.RedrawMode = True # 逐一の描画をオン
722
+
723
+ if not succeed:
724
+ raise Exception('jpg ファイルの保存でエラーが発生しました。')
725
+
726
+ if not os.path.exists(jpg_path):
727
+ raise Exception('保存した jpg ファイルが見つかりませんでした。')
728
+
729
+ with open(jpg_path, 'rb') as f:
730
+ content = f.read()
731
+
732
+ return content
733
+
734
+
735
+ from win32com.client import Dispatch, constants
736
+
737
+
738
+ class _UnPicklableNoFEM(FemtetInterface):
739
+
740
+
741
+ original_femprj_path = 'dummy'
742
+ model_name = 'dummy'
743
+ parametric_output_indexes_use_as_objective = None
744
+ kwargs = dict()
745
+ Femtet = None
746
+ quit_when_destruct = False
747
+
748
+ def __init__(self):
749
+ CoInitialize()
750
+ self.unpicklable_member = Dispatch('FemtetMacro.Femtet')
751
+ self.cns = constants
752
+
753
+ def _setup_before_parallel(self, *args, **kwargs):
754
+ pass
755
+
756
+ def check_param_value(self, *args, **kwargs):
757
+ pass
758
+
759
+ def update_parameter(self, *args, **kwargs):
760
+ pass
761
+
762
+ def update(self, *args, **kwargs):
763
+ pass
764
+
765
+ def create_result_file_content(self):
766
+ """Called after solve"""
767
+
768
+ # save to worker space
769
+ with open(__file__, 'rb') as f:
770
+ content = f.read()
771
+
772
+ return content
773
+
774
+ def create_file_path(self, trial: int):
775
+ # return path of scheduler environment
776
+ here = os.path.dirname(__file__)
777
+ pdt_path = os.path.join(here, f'trial{trial}.pdt')
778
+ return pdt_path
779
+
@@ -17,6 +17,10 @@ class FemtetWithNXInterface(FemtetInterface):
17
17
 
18
18
  Args:
19
19
  prt_path: The path to the prt file.
20
+ export_curves(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
21
+ export_surfaces(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
22
+ export_solids(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
23
+ export_flattened_assembly(bool or None): Parasolid export setting of NX. If None, PyFemtet does not change the setting of NX. Defaults to None.
20
24
 
21
25
  For details of The other arguments, see ``FemtetInterface``.
22
26
 
@@ -27,9 +31,12 @@ class FemtetWithNXInterface(FemtetInterface):
27
31
  def __init__(
28
32
  self,
29
33
  prt_path,
34
+ export_curves: bool or None = None,
35
+ export_surfaces: bool or None = None,
36
+ export_solids: bool or None = None,
37
+ export_flattened_assembly: bool or None = None,
30
38
  **kwargs
31
39
  ):
32
-
33
40
  # check NX installation
34
41
  self.run_journal_path = os.path.join(os.environ.get('UGII_BASE_DIR'), 'NXBIN', 'run_journal.exe')
35
42
  if not os.path.isfile(self.run_journal_path):
@@ -45,10 +52,19 @@ class FemtetWithNXInterface(FemtetInterface):
45
52
  except ValueError: # get_worker に失敗した場合
46
53
  self.prt_path = os.path.abspath(prt_path)
47
54
 
55
+ self.export_curves = export_curves
56
+ self.export_surfaces = export_surfaces
57
+ self.export_solids = export_solids
58
+ self.export_flattened_assembly = export_flattened_assembly
59
+
48
60
  # FemtetInterface の設定 (femprj_path, model_name の更新など)
49
61
  # + restore 情報の上書き
50
62
  super().__init__(
51
63
  prt_path=self.prt_path,
64
+ export_curves=self.export_curves,
65
+ export_surfaces=self.export_surfaces,
66
+ export_solids=self.export_solids,
67
+ export_flattened_assembly=self.export_flattened_assembly,
52
68
  **kwargs
53
69
  )
54
70
 
@@ -86,10 +102,27 @@ class FemtetWithNXInterface(FemtetInterface):
86
102
  tmp_dict[row['name']] = row['value']
87
103
  str_json = json.dumps(tmp_dict)
88
104
 
105
+ # create dumped json of export settings
106
+ tmp_dict = dict(
107
+ include_curves=self.export_curves,
108
+ include_surfaces=self.export_surfaces,
109
+ include_solids=self.export_solids,
110
+ flatten_assembly=self.export_flattened_assembly,
111
+ )
112
+ dumped_json_export_settings = json.dumps(tmp_dict)
113
+
89
114
  # NX journal を使ってモデルを編集する
90
115
  env = os.environ.copy()
91
116
  subprocess.run(
92
- [self.run_journal_path, self._JOURNAL_PATH, '-args', self.prt_path, str_json, x_t_path],
117
+ [
118
+ self.run_journal_path, # run_journal.exe
119
+ self._JOURNAL_PATH, # update_model.py
120
+ '-args',
121
+ self.prt_path,
122
+ str_json,
123
+ x_t_path,
124
+ dumped_json_export_settings,
125
+ ],
93
126
  env=env,
94
127
  shell=True,
95
128
  cwd=os.path.dirname(self.prt_path)
@@ -1,35 +1,34 @@
1
1
  import os
2
2
  import sys
3
3
  import json
4
- import NXOpen
5
-
4
+ from xml.etree.ElementInclude import include
6
5
 
7
- def main(prtPath:str, parameters:'dict as str', x_tPath:str = None):
8
- '''
9
- .prt ファイルのパスを受け取り、parameters に指定された変数を更新し、
10
- x_tPath が None のときは.prt と同じディレクトリに
11
- .x_t ファイルをエクスポートする
6
+ import NXOpen
12
7
 
13
- Parameters
14
- ----------
15
- prtPath : str
16
- DESCRIPTION.
17
- parameters : 'dict as str'
18
- DESCRIPTION.
19
8
 
20
- Returns
21
- -------
22
- None.
9
+ def main(
10
+ prtPath: str,
11
+ parameters: str, # dumped json
12
+ x_tPath: str,
13
+ dumped_json_export_settings: str,
14
+ ):
15
+ """Update the parameter of .prt file and export to .x_t file."""
23
16
 
24
- '''
25
17
  # 保存先の設定
26
- prtPath = os.path.abspath(prtPath) # 一応
18
+ prtPath = os.path.abspath(prtPath)
27
19
  if x_tPath is None:
28
20
  x_tPath = os.path.splitext(prtPath)[0] + '.x_t'
29
21
 
30
22
  # 辞書の作成
31
23
  parameters = json.loads(parameters)
32
-
24
+
25
+ # export 設定
26
+ settings = json.loads(dumped_json_export_settings)
27
+ include_curves = settings['include_curves']
28
+ include_surfaces = settings['include_surfaces']
29
+ include_solids = settings['include_solids']
30
+ flatten_assembly = settings['flatten_assembly']
31
+
33
32
  # session の取得とパートを開く
34
33
  theSession = NXOpen.Session.GetSession()
35
34
  theSession.Parts.OpenActiveDisplay(prtPath, NXOpen.DisplayPartOption.AllowAdditional)
@@ -40,7 +39,6 @@ def main(prtPath:str, parameters:'dict as str', x_tPath:str = None):
40
39
  displayPart = theSession.Parts.Display
41
40
 
42
41
  # 式を更新
43
- unit_mm = workPart.UnitCollection.FindObject("MilliMeter")
44
42
  for k, v in parameters.items():
45
43
  try:
46
44
  exp = workPart.Expressions.FindObject(k)
@@ -48,14 +46,15 @@ def main(prtPath:str, parameters:'dict as str', x_tPath:str = None):
48
46
  print(f'├ 変数{k}は .prt ファイルに含まれていません。無視されます。')
49
47
  continue
50
48
 
51
- workPart.Expressions.EditWithUnits(exp, unit_mm, str(v))
49
+ workPart.Expressions.Edit(exp, str(v))
52
50
  # 式の更新を適用
53
51
  id1 = theSession.NewestVisibleUndoMark
54
52
  try:
55
53
  nErrs1 = theSession.UpdateManager.DoUpdate(id1)
56
54
  # 更新に失敗
57
55
  except NXOpen.NXException as e:
58
- print(' 形状が破綻しました。操作を取り消します。')
56
+ print(f' ERROR! {e}')
57
+ print(f'└ 形状が破綻しました。操作を取り消します。')
59
58
  return None
60
59
 
61
60
  print('│ model 更新に成功しました。')
@@ -64,19 +63,28 @@ def main(prtPath:str, parameters:'dict as str', x_tPath:str = None):
64
63
  # parasolid のエクスポート
65
64
  parasolidExporter1 = theSession.DexManager.CreateParasolidExporter()
66
65
 
67
- parasolidExporter1.ObjectTypes.Curves = False
68
- parasolidExporter1.ObjectTypes.Surfaces = False
69
- parasolidExporter1.ObjectTypes.Solids = True
66
+ if include_curves is not None:
67
+ parasolidExporter1.ObjectTypes.Curves = include_curves
68
+
69
+ if include_surfaces is not None:
70
+ parasolidExporter1.ObjectTypes.Surfaces = include_surfaces
71
+
72
+ if include_solids is not None:
73
+ parasolidExporter1.ObjectTypes.Solids = include_solids
74
+
75
+ if flatten_assembly is not None:
76
+ parasolidExporter1.FlattenAssembly = flatten_assembly
70
77
 
71
78
  parasolidExporter1.InputFile = prtPath
72
79
  parasolidExporter1.ParasolidVersion = NXOpen.ParasolidExporter.ParasolidVersionOption.Current
73
80
  parasolidExporter1.OutputFile = x_tPath
74
81
 
75
82
  parasolidExporter1.Commit()
76
-
77
83
  parasolidExporter1.Destroy()
78
- except:
79
- print('└ parasolid 更新に失敗しました。')
84
+
85
+ except Exception as e:
86
+ print(f'├ ERROR! {e}')
87
+ print(f'└ parasolid 更新に失敗しました。')
80
88
  return None
81
89
 
82
90
  print('└ parasolid 更新が正常に終了しました。')
pyfemtet/opt/opt/_base.py CHANGED
@@ -88,6 +88,8 @@ class AbstractOptimizer(ABC):
88
88
  y,
89
89
  c,
90
90
  self.message,
91
+ postprocess_func=self.fem.postprocess_func,
92
+ postprocess_args=self.fem.create_postprocess_args(),
91
93
  )
92
94
 
93
95
  logger.debug('history.record end')
@@ -2,9 +2,6 @@ import plotly.graph_objs as go
2
2
  import plotly.express as px
3
3
 
4
4
 
5
- _CUSTOM_DATA_DICT = {'trial': 0} # 連番
6
-
7
-
8
5
  class _ColorSet:
9
6
  non_domi = {True: '#007bff', False: '#6c757d'} # color
10
7
 
@@ -49,7 +46,7 @@ def update_hypervolume_plot(history, df):
49
46
  x="trial",
50
47
  y="hypervolume",
51
48
  markers=True,
52
- custom_data=_CUSTOM_DATA_DICT.keys(),
49
+ custom_data=['trial'],
53
50
  )
54
51
 
55
52
  fig.update_layout(
@@ -67,13 +64,17 @@ def update_default_figure(history, df):
67
64
  obj_names = history.obj_names
68
65
 
69
66
  if len(obj_names) == 0:
70
- return go.Figure()
67
+ fig = go.Figure()
71
68
 
72
69
  elif len(obj_names) == 1:
73
- return update_single_objective_plot(history, df)
70
+ fig = update_single_objective_plot(history, df)
74
71
 
75
72
  elif len(obj_names) >= 2:
76
- return update_multi_objective_pairplot(history, df)
73
+ fig = update_multi_objective_pairplot(history, df)
74
+
75
+ fig.update_traces(hoverinfo="none", hovertemplate=None)
76
+
77
+ return fig
77
78
 
78
79
 
79
80
  def update_single_objective_plot(history, df):
@@ -97,7 +98,7 @@ def update_single_objective_plot(history, df):
97
98
  _ls.feasible['label']: False,
98
99
  'trial': True,
99
100
  },
100
- custom_data=_CUSTOM_DATA_DICT.keys(),
101
+ custom_data=['trial'],
101
102
  )
102
103
 
103
104
  fig.add_trace(
@@ -143,11 +144,7 @@ def update_multi_objective_pairplot(history, df):
143
144
  _ls.feasible[True]: _ss.feasible[True],
144
145
  _ls.feasible[False]: _ss.feasible[False],
145
146
  },
146
- hover_data={
147
- _ls.feasible['label']: False,
148
- 'trial': True,
149
- },
150
- custom_data=_CUSTOM_DATA_DICT.keys(),
147
+ custom_data=['trial'],
151
148
  category_orders={
152
149
  _ls.feasible['label']: (_ls.feasible[False], _ls.feasible[True]),
153
150
  _ls.non_domi['label']: (_ls.non_domi[False], _ls.non_domi[True]),
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import base64
2
3
  from typing import Optional, List
3
4
 
4
5
  import json
@@ -6,10 +7,11 @@ import webbrowser
6
7
  from time import sleep
7
8
  from threading import Thread
8
9
 
10
+ import numpy as np
9
11
  import pandas as pd
10
12
  import psutil
11
13
  from plotly.graph_objects import Figure
12
- from dash import Dash, html, dcc, Output, Input, State, callback_context, no_update
14
+ from dash import Dash, html, dcc, Output, Input, State, callback_context, no_update, dash_table
13
15
  from dash.exceptions import PreventUpdate
14
16
  import dash_bootstrap_components as dbc
15
17
 
@@ -18,7 +20,6 @@ from pyfemtet.opt.interface import FemtetInterface
18
20
  from pyfemtet.opt.visualization._graphs import (
19
21
  update_default_figure,
20
22
  update_hypervolume_plot,
21
- _CUSTOM_DATA_DICT
22
23
  )
23
24
 
24
25
 
@@ -28,6 +29,8 @@ logger = get_logger('viz')
28
29
  logger.setLevel(logging.INFO)
29
30
 
30
31
 
32
+ here = os.path.dirname(__file__)
33
+
31
34
  DBC_COLUMN_STYLE_CENTER = {
32
35
  'display': 'flex',
33
36
  'justify-content': 'center',
@@ -458,7 +461,7 @@ class FemtetControl:
458
461
  points_dicts = selection_data['points']
459
462
  for points_dict in points_dicts:
460
463
  logger.debug(points_dict)
461
- trial = points_dict['customdata'][_CUSTOM_DATA_DICT['trial']]
464
+ trial = points_dict['customdata'][0]
462
465
  logger.debug(trial)
463
466
  index = trial - 1
464
467
  names = [name for name in home.monitor.local_df.columns if name.startswith('prm_')]
@@ -556,6 +559,7 @@ class HomePageBase:
556
559
  ID_GRAPH_TABS = 'home-graph-tabs'
557
560
  ID_GRAPH_CARD_BODY = 'home-graph-card-body'
558
561
  ID_GRAPH = 'home-graph'
562
+ ID_GRAPH_TOOLTIP = 'home-graph-hover-tooltip'
559
563
  ID_SELECTION_DATA = 'home-selection-data'
560
564
 
561
565
  # selection data attribute
@@ -577,7 +581,6 @@ class HomePageBase:
577
581
  self.monitor = monitor
578
582
  self.app: Dash = monitor.app
579
583
  self.history = monitor.history
580
- self.df = monitor.local_df
581
584
  self.setup_graph_card()
582
585
  self.setup_contents()
583
586
  self.setup_layout()
@@ -616,7 +619,12 @@ class HomePageBase:
616
619
  children=[
617
620
  # Loading : child が Output である callback について、
618
621
  # それが発火してから return するまでの間 Spinner が出てくる
619
- dcc.Loading(dcc.Graph(id=self.ID_GRAPH)),
622
+ html.Div([
623
+ dcc.Loading(
624
+ dcc.Graph(id=self.ID_GRAPH, clear_on_unhover=True, figure=self.get_fig_by_tab_id(default_tab)),
625
+ ),
626
+ dcc.Tooltip(id=self.ID_GRAPH_TOOLTIP),
627
+ ]),
620
628
  ],
621
629
  id=self.ID_GRAPH_CARD_BODY,
622
630
  ),
@@ -633,7 +641,7 @@ class HomePageBase:
633
641
  Output(self.ID_GRAPH, 'figure'),
634
642
  ],
635
643
  [
636
- Input(self.ID_GRAPH_CARD_BODY, 'children'),
644
+ Input(self.ID_DUMMY, 'children'),
637
645
  ],
638
646
  [
639
647
  State(self.ID_GRAPH_TABS, 'active_tab'),
@@ -678,6 +686,72 @@ class HomePageBase:
678
686
  logger.debug(f'on_select: {selected_data}')
679
687
  return [selected_data]
680
688
 
689
+ # ホバーに画像を表示する callback
690
+ @self.monitor.app.callback(
691
+ Output(self.ID_GRAPH_TOOLTIP, "show"),
692
+ Output(self.ID_GRAPH_TOOLTIP, "bbox"),
693
+ Output(self.ID_GRAPH_TOOLTIP, "children"),
694
+ Input(self.ID_GRAPH, "hoverData"),
695
+ )
696
+ def display_hover(hoverData):
697
+ if hoverData is None:
698
+ return False, no_update, no_update
699
+
700
+ pt = hoverData["points"][0]
701
+ bbox = pt["bbox"]
702
+
703
+ # get row of the history
704
+ trial = pt['customdata'][0]
705
+ row = self.monitor.local_df[self.monitor.local_df['trial'] == trial]
706
+
707
+ # === create hovered data ===
708
+ # get encoded image from history.additional_metadata
709
+ img_url = None
710
+
711
+ # Femtet specified processing
712
+ metadata = self.history.metadata
713
+ if metadata[0] != '':
714
+ # get img path
715
+ d = json.loads(metadata[0])
716
+ femprj_path = d['femprj_path']
717
+ model_name = d['model_name']
718
+ femprj_result_dir = femprj_path.replace('.femprj', '.Results')
719
+ img_path = os.path.join(femprj_result_dir, f'{model_name}_trial{trial}.jpg')
720
+ if os.path.exists(img_path):
721
+ # create encoded image
722
+ with open(img_path, 'rb') as f:
723
+ content = f.read()
724
+ encoded_image = base64.b64encode(content).decode('utf-8')
725
+ img_url = 'data:image/jpeg;base64, ' + encoded_image
726
+ html_img = html.Img(src=img_url, style={"width": "200px"}) if img_url is not None else html.Div()
727
+
728
+ # parameters
729
+ pd.options.display.float_format = '{:.4e}'.format
730
+ parameters = row.iloc[:, np.where(np.array(metadata) == 'prm')[0]]
731
+ names = parameters.columns
732
+ values = [f'{value:.3e}' for value in parameters.values.ravel()]
733
+ data = pd.DataFrame(dict(
734
+ name=names, value=values
735
+ ))
736
+
737
+ # descript result
738
+ desc = html.Div([
739
+ html.H3(f"trial{trial}", style={"color": "darkblue"}),
740
+ dash_table.DataTable(
741
+ columns=[{'name': col, 'id': col} for col in data.columns],
742
+ data=data.to_dict('records')
743
+ ),
744
+ ])
745
+
746
+ # make output
747
+ children = html.Div([
748
+ html.Div(html_img, style={'display': 'inline-block', 'margin-right': '10px', 'vertical-align': 'top'}),
749
+ html.Div(desc, style={'display': 'inline-block', 'margin-right': '10px'})
750
+ ])
751
+
752
+ return True, bbox, children
753
+
754
+
681
755
  def get_fig_by_tab_id(self, tab_id):
682
756
  if tab_id in self.graphs.keys():
683
757
  fig_func = self.graphs[tab_id]['fig_func']
@@ -712,8 +786,8 @@ class ResultViewerAppHomePage(HomePageBase):
712
786
  '---\n'
713
787
  '- 最適化の結果分析画面です。\n'
714
788
  '- 凡例をクリックすると、対応する要素の表示/非表示を切り替えます。\n'
715
- '- ブラウザを使用しますが、ネットワーク通信は行いません。\n'
716
- '- ブラウザを閉じてもプログラムは終了しません。'
789
+ '- ブラウザを使用しますが、解析結果のインターネット通信は行いません。\n'
790
+ '- ブラウザを閉じてもプログラムは終了しません。\n'
717
791
  ' - コマンドプロンプトを閉じるかコマンドプロンプトに `CTRL+C` を入力してプログラムを終了してください。\n'
718
792
  )
719
793
  self.contents.children = [
@@ -753,9 +827,10 @@ class ProcessMonitorAppHomePage(HomePageBase):
753
827
  note = dcc.Markdown(
754
828
  '---\n'
755
829
  '- 最適化の結果分析画面です。\n'
756
- '- ブラウザを使用しますが、ネットワーク通信は行いません。\n'
830
+ '- ブラウザを使用しますが、解析結果のインターネット通信は行いません。\n'
757
831
  '- この画面を閉じても最適化は中断されません。\n'
758
832
  f'- この画面を再び開くにはブラウザのアドレスバーに「localhost:{self.monitor.DEFAULT_PORT}」と入力して下さい。\n'
833
+ '- __マウスオーバーで表示されるデータを見る場合は、「自動更新を一時停止する」ボタンを押してください。__\n'
759
834
  )
760
835
 
761
836
  self.contents.children = [
@@ -832,7 +907,10 @@ class ProcessMonitorAppHomePage(HomePageBase):
832
907
  # 1. interval => figure を更新する
833
908
  if (len(self.monitor.local_df) > 0) and (active_tab_id is not None):
834
909
  fig = self.get_fig_by_tab_id(active_tab_id)
835
- ret[card_body] = dcc.Graph(figure=fig, id=self.ID_GRAPH) # list にせんとダメかも
910
+ ret[card_body] = [
911
+ dcc.Graph(figure=fig, id=self.ID_GRAPH, clear_on_unhover=True),
912
+ dcc.Tooltip(id=self.ID_GRAPH_TOOLTIP),
913
+ ]
836
914
 
837
915
  # 3. btn toggle => (toggle の children を切替) and (interval を切替)
838
916
  if toggle_n_clicks % 2 == 1:
@@ -1142,7 +1220,7 @@ class ProcessMonitorApp(AppBase):
1142
1220
  self.local_worker_status_int_list = [s.get() for s in self.worker_status_list]
1143
1221
 
1144
1222
  # terminate_all 指令があれば monitor server をホストするプロセスごと終了する
1145
- if self.status.get() == OptimizationStatus.TERMINATE_ALL:
1223
+ if self.status.get() >= OptimizationStatus.TERMINATE_ALL:
1146
1224
  return 0 # take server down with me
1147
1225
 
1148
1226
  # interval
@@ -0,0 +1,13 @@
1
+ import argparse
2
+ from pyfemtet.opt.visualization._graphs import show_static_monitor
3
+
4
+
5
+ def main():
6
+ parser = argparse.ArgumentParser()
7
+
8
+ parser.add_argument('csv_path', help='pyfemtet を実行した結果の csv ファイルのパスを指定してください。', type=str)
9
+
10
+ args = parser.parse_args()
11
+
12
+ if args.csv_path:
13
+ show_static_monitor(args.csv_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyfemtet
3
- Version: 0.4.7
3
+ Version: 0.4.9
4
4
  Summary: Design parameter optimization using Femtet.
5
5
  Home-page: https://github.com/pyfemtet/pyfemtet
6
6
  License: BSD-3-Clause
@@ -12,69 +12,70 @@ pyfemtet/FemtetPJTSample/her_ex40_parametric.py,sha256=B5PQoh71Q3KN2CyLU1gP_Yh9g
12
12
  pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py,sha256=UfhJffuXyhzdIWNpOrpV6xLTK1fuVvgyhlyg4Rp-628,2148
13
13
  pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj,sha256=pxacKe0NPNUPAcxqo2cATFApsMKiVt2g2e_FOk4fpjA,172895
14
14
  pyfemtet/FemtetPJTSample/wat_ex14_parametric.py,sha256=LGbWxCek0Ad2YrDCKykiQkE3aIypM4g8P3mLd_2anEE,2052
15
- pyfemtet/__init__.py,sha256=H-XEMfwNdLYsHizQIA9HjmQyc0e1i8XfavXH5Y7e3bU,21
16
- pyfemtet/_test_util.py,sha256=Qcf_g3Rz1yJMkTWmEW8Scl2U3h-Vj1h5u0Ejs3JgMYQ,2882
15
+ pyfemtet/__init__.py,sha256=Awjzp7R_3igiNhiHUO_Jzt5iqhYXj8NZfpCn857chhM,21
16
+ pyfemtet/_test_util.py,sha256=cxSPWvYbvAMAEoY_H3XpWlhhdS0MijwZEQgZ7aa4Fy8,3727
17
17
  pyfemtet/core.py,sha256=3lqfBGJ5IuKz2Nqj5pRo7YQqKwx_0ZDL72u95Ur_1p0,1386
18
18
  pyfemtet/dispatch_extensions.py,sha256=MhWiUXVt2Cq8vDeajMK4SrajjiAmb4m2fK8gXwHLrWA,16177
19
19
  pyfemtet/logger.py,sha256=JYD0FvzijMS2NvZN7VT7vZA5hqtHEkvS93AHlIMDePw,2507
20
20
  pyfemtet/opt/__init__.py,sha256=_P8unESvqCRD5pmuo6-5yo7BbrPi7c0aE6UZpqUD-_I,596
21
- pyfemtet/opt/_femopt.py,sha256=o2TLbmWwiOVdDsVHSh6j3k7fWktHJQxpTpeDmDkg5XE,22601
22
- pyfemtet/opt/_femopt_core.py,sha256=n_m-GIW82uvKNAn_N1ypBZQ4sOjngfNly7-SEkOXfuU,24774
21
+ pyfemtet/opt/_femopt.py,sha256=nP9ApYS3dSi7ytSqMApmDFp1KKlqPX-KvVqBnXauQzA,22495
22
+ pyfemtet/opt/_femopt_core.py,sha256=zB12rNBnWN-CuqGijgKL8RuBEdoyzfbNWQBTcBiTvcU,25490
23
23
  pyfemtet/opt/femprj_sample/cad_ex01_NX.femprj,sha256=KC8JlHqHzqgyKriK911QSnQByQpRlw-SX5OSQ_GNe5M,149193
24
24
  pyfemtet/opt/femprj_sample/cad_ex01_NX.prt,sha256=3okHLeMdslrRA_wkhppZtxIe-2-ZPMfNqWCdQwUV31o,226626
25
- pyfemtet/opt/femprj_sample/cad_ex01_NX.py,sha256=qpfbU41qsJSMWnROzTOU1mpmHngGoa0cjnLHqNkt0EY,4519
25
+ pyfemtet/opt/femprj_sample/cad_ex01_NX.py,sha256=TmM8wQ5CuesS8-QWlQNwykoAJWaPxUfNjqjx7xhDqz8,4892
26
26
  pyfemtet/opt/femprj_sample/cad_ex01_NX_test_result.reccsv,sha256=hcOiI1Ct5o7k_tN8nB82kpVJpAtGvdLBjLwqEAFz49c,1029
27
27
  pyfemtet/opt/femprj_sample/cad_ex01_SW.SLDPRT,sha256=jjBi4aRRwZPK-4-YRKDC4eO_Ch2jwd7M7chvJlnBbZU,97158
28
28
  pyfemtet/opt/femprj_sample/cad_ex01_SW.femprj,sha256=knN0bBTHm5CqExLdmxdJvPldJ6ahnQesKt974qRjWh4,126837
29
- pyfemtet/opt/femprj_sample/cad_ex01_SW.py,sha256=HOpxWC_jxnKmabO4u5JrHNHPR8b7WFm-PauJSoTxRrQ,4586
29
+ pyfemtet/opt/femprj_sample/cad_ex01_SW.py,sha256=AtKGRMMweXjIriYe5kxEecrNK5KPdf2XMNm54nVK-1o,4830
30
30
  pyfemtet/opt/femprj_sample/cad_ex01_SW_test_result.reccsv,sha256=hcOiI1Ct5o7k_tN8nB82kpVJpAtGvdLBjLwqEAFz49c,1029
31
31
  pyfemtet/opt/femprj_sample/gal_ex58_parametric.femprj,sha256=dbanN3W2eX_ciZ0wZGqK60mc4edSVh5G2OqbbMKyFng,74898
32
- pyfemtet/opt/femprj_sample/gal_ex58_parametric.py,sha256=yNAgkHknRY6CPnYR6ETl6ZNOElepvQNwwnofUGpFhVw,2485
32
+ pyfemtet/opt/femprj_sample/gal_ex58_parametric.py,sha256=imD84lKenIbkxY9La37SWbPvJlhXJRnV_IFtE0tQ5ac,2729
33
33
  pyfemtet/opt/femprj_sample/gal_ex58_parametric_test_result.reccsv,sha256=hcOiI1Ct5o7k_tN8nB82kpVJpAtGvdLBjLwqEAFz49c,1029
34
34
  pyfemtet/opt/femprj_sample/gau_ex08_parametric.femprj,sha256=Yb9ILeTEKx5xfJGk8IZH_DVlgkpGB33Vy9-LGIEQboY,279251
35
- pyfemtet/opt/femprj_sample/gau_ex08_parametric.py,sha256=7SLe6Qbi2AGm1Li7kotyy2qSYynX2YitoW255XRxOOI,1956
35
+ pyfemtet/opt/femprj_sample/gau_ex08_parametric.py,sha256=SzHKPrK05Muv5edvrMr91YYN6Zm51qHbWWLCWd94Ww8,2200
36
36
  pyfemtet/opt/femprj_sample/gau_ex08_parametric_test_result.reccsv,sha256=usCllUrcaDihii8rSyCcZDhXp1wJI01P-yzm-BHfhb4,2779
37
37
  pyfemtet/opt/femprj_sample/her_ex40_parametric.femprj,sha256=LLAUDlUo1dIpRzlKPs1lvACzJQxjnWW3xAGAodYEqRM,117221
38
- pyfemtet/opt/femprj_sample/her_ex40_parametric.py,sha256=9ubwnEavFpjbTAbH5RmYwYoGxATl-ehW_oH_kDLTlXs,4839
38
+ pyfemtet/opt/femprj_sample/her_ex40_parametric.py,sha256=iEIkQLozBwQOBzv04nVH0JRUByPvfwPX50c1P9IRHfY,5083
39
39
  pyfemtet/opt/femprj_sample/her_ex40_parametric_test_result.reccsv,sha256=_vTN2GdxPcHZByNw14izdYB190Vn6WROp7-qOHW7QU8,2845
40
40
  pyfemtet/opt/femprj_sample/paswat_ex1_parametric.femprj,sha256=z2NHFJWiuiLv_zhxjFpLpmRbYVvQ43bAMj_NLioQGsk,262283
41
- pyfemtet/opt/femprj_sample/paswat_ex1_parametric.py,sha256=nFBQPr2gaCoLVxZDsKVUU_aZ3I9ZwbQCwz1_qJTSgOU,2388
42
- pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py,sha256=2IPq3SMrd2TCQMKL0_NA7ELBAj9yqwNe2RiMWpopw_M,2499
41
+ pyfemtet/opt/femprj_sample/paswat_ex1_parametric.py,sha256=HXbkWYUtGB5p1IyCfrg-CQ6Q5HXTEtgAtKwa75iGXBo,2632
42
+ pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py,sha256=Px8fS_WrQ0N3xaD5eHyYvx2-kcx8Mf84CIC2STS1kKw,2743
43
43
  pyfemtet/opt/femprj_sample/paswat_ex1_parametric_test_result.reccsv,sha256=RjENlloyx71dkZJ_fIY6FVWKnSBCT_RmV1dO98eJxyY,3303
44
44
  pyfemtet/opt/femprj_sample/wat_ex14_parametric.femprj,sha256=F-yu2dGrsbrIA1Lhizu2aHTjQFTohyBmOuJv-Iyl8jk,179596
45
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.py,sha256=sGj0khYO7_yGyXGHbdWJ883XvzO1HUbrgBLjiS2d7TM,2240
45
+ pyfemtet/opt/femprj_sample/wat_ex14_parametric.py,sha256=o_LeUpo779a8vCBjxqS6NNF5zS7ID4mBfFjHsYvI1vs,2484
46
46
  pyfemtet/opt/femprj_sample/wat_ex14_parametric_test_result.reccsv,sha256=92IkN7HdHBatyacO6aU3MVhOG6rkXlodlw34oF91BSM,3056
47
47
  pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.femprj,sha256=0RBhOGhtiFAp0QSCTBYEaDY9EZymn9hJYchAOJ6PaBA,143533
48
- pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py,sha256=2LE-3d0XEChqCSwgUi45V8eAsF_YUGUgO9jLx5u5bjQ,4797
48
+ pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py,sha256=sqhOskvRn84e1xET5rTfvbu5WG0kXvLoNfdAJ3e_XqA,5205
49
49
  pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.femprj,sha256=ZZhT9XjB9Xu9YwHWv4gbvKBiUWlOFKEoHjAcGWb3vvQ,128026
50
- pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.py,sha256=o8P6o7Q0d0Jj02Qeij8kxbduyyKf_fNHSlXU_1H65S8,4864
50
+ pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.py,sha256=qI_mU_0ype_6706CQphaLD_IF2ILawgNqPUCWsfbLsc,5143
51
51
  pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.femprj,sha256=PzqtNVde6VnMHFpedRBsOq9JVhCY0ymQPVs54EKsNLw,75668
52
- pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.py,sha256=gnCPiaxCPITAP-972DylFQ0BNpDJLeal9HPf5cfbhRI,2488
52
+ pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.py,sha256=MLLlWxpDo1JWWiglNc7XNoms3_IXPCj65aEPy_83tLs,2767
53
53
  pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.femprj,sha256=TTXw_8YT8pzHQlu4ufGzTq1IFYSwcWWt4GA6sIY1YPM,295600
54
- pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.py,sha256=GxfAb5_0F9tK_i9almLdQh7OjzdNFo_leoyIPtPCqvM,2114
54
+ pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.py,sha256=g8VYoibnKwnNpK7KPZ321jrlSL28xJ3dKzP3FacovNw,2393
55
55
  pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.femprj,sha256=OJ7f8iw0z1BZqanuNn71uEaoM2Kgb93ptUU8iYwYON0,129783
56
- pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.py,sha256=FFXO4GblaCHXrU7yUapBcescscESrVv-dutVYs4Fp4s,5272
56
+ pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.py,sha256=eMrUZhyqIQ2jDl9kdHrTnpvmiw-6GkPJQBf7kaBilaI,5551
57
57
  pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.femprj,sha256=y7eURFBdqh6PmD4zbelGuB458HmfihVht0K4wVI-mik,265368
58
- pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.py,sha256=cm_P-qXX0p4GBzst6IUuarkSczrDhitCNodzlcbyRPM,2516
59
- pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py,sha256=zpkGyKBhPJhEoc_pfYX6m27FgGcEV8D8HIBUg6EL_ps,2632
58
+ pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.py,sha256=HwLhd1xIq_wdQchjttKmcT4EU1lpNkcaAZDcK-L8C_w,2795
59
+ pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py,sha256=utOjKYFSXqvrgPtphHMeYUQpDT20K7Iw2GBLG80ga5M,2911
60
60
  pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.femprj,sha256=dMwQMt6yok_PbZLyxPYdmg5wJQwgQDZ4RhS76zdGLGk,177944
61
- pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py,sha256=vnfI4WuCvyjQN1MhnzyB-jcAPtTwz3yNiwtTm2yQZHU,2311
61
+ pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py,sha256=W30jobW1eEYuAsJJR14-EifQ1fxeXlS-gRSFqgCP4BM,2590
62
62
  pyfemtet/opt/interface/__init__.py,sha256=qz5BszPuU3jZIoDnPjkPDAgvgHLlx1sYhuqh5ID798k,480
63
- pyfemtet/opt/interface/_base.py,sha256=lDjdvKYmBMxSc7SfoWSu91qVijGaQ2Ng-0C7z_f4eFM,2079
64
- pyfemtet/opt/interface/_femtet.py,sha256=ofX76BSWEnoMAvSbOIdkVA2CUPcJYJuG4UHWFQtFBg0,28601
63
+ pyfemtet/opt/interface/_base.py,sha256=Ti2wCicyeofAZ0DzQLYoj9PBj1lyM5_lbLa0LBoypLM,2228
64
+ pyfemtet/opt/interface/_femtet.py,sha256=PSy1AldNK7Yg7tmxzqEmwCb5oYXouh_Y6p2vrxtwnDY,33151
65
65
  pyfemtet/opt/interface/_femtet_parametric.py,sha256=l3Ka-e6WPTM4cerIFT4_qK6o80Lk9oHPsOnXUaJufm4,2883
66
66
  pyfemtet/opt/interface/_femtet_with_nx/__init__.py,sha256=-6W2g2FDEcKzGHmI5KAKQe-4U5jDpMj0CXuma-GZca0,83
67
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py,sha256=kTEmfAyLMEZvYTYjGXgYnoyhi-wDzhKK7opyVp7rOcY,3931
68
- pyfemtet/opt/interface/_femtet_with_nx/update_model.py,sha256=t0AB7mKY7rmrI_9stP1-5qhzmugEQ19DnZ4CCrCdTSw,2856
67
+ pyfemtet/opt/interface/_femtet_with_nx/_interface.py,sha256=8ZvD6ajNmKUqXPkDsGJ4tOHrQOYwWBlO9OSPaCO3ml0,5666
68
+ pyfemtet/opt/interface/_femtet_with_nx/update_model.py,sha256=uFf_SU96_BYUmLBBR5w3NS3WqXvEvSbOoxVnz9FA3Ug,3187
69
69
  pyfemtet/opt/interface/_femtet_with_sldworks.py,sha256=400FidHp7mBAVLyxzLlLtGYNzK_TtK61ycd4vFjHES0,6254
70
70
  pyfemtet/opt/opt/__init__.py,sha256=eQh-7PJN2YEUbHZnjinbeIyb0bk1wSh76TaEAa2l8SU,191
71
- pyfemtet/opt/opt/_base.py,sha256=yEnPWAxFUIRNmUV-nbfCW4N3naDkkzQGME3qsDgEBFQ,7571
71
+ pyfemtet/opt/opt/_base.py,sha256=PIEoahy7VdnrfR4jknGiB0ZzJTPzM7gEpErb6Zsm2l4,7692
72
72
  pyfemtet/opt/opt/_optuna.py,sha256=TWtp9auU6GBWtEJVOuo8aWSvLcCHCUNUyOQRnqyln4I,9996
73
73
  pyfemtet/opt/visualization/__init__.py,sha256=PUCHoZnuZrHjTd0QQQBgzWkCpKY2noBPTvi-lyvxQyw,193
74
- pyfemtet/opt/visualization/_graphs.py,sha256=34NPlvz7RkoBndvaVCNhlV37SLjpHTe7X3rnOZSmpYs,5553
75
- pyfemtet/opt/visualization/_monitor.py,sha256=X2KR6fd8qf1U58Qr_o0jn0FTm1w9ARMPhs4eMYgGipI,42100
76
- pyfemtet-0.4.7.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
77
- pyfemtet-0.4.7.dist-info/METADATA,sha256=e3obY8HzDscP_r-DC4qvF_CvAQ2go-XKYcZQLZQ4ZAs,3381
78
- pyfemtet-0.4.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
79
- pyfemtet-0.4.7.dist-info/entry_points.txt,sha256=E_NUq8BEuKLM3z6Ou0sh6HyvaKE5O6NBDlmO-wgEGaQ,67
80
- pyfemtet-0.4.7.dist-info/RECORD,,
74
+ pyfemtet/opt/visualization/_graphs.py,sha256=eovw8ShTGGWaDNr-nZuQZd3aa_gcYr-zziB0ZSI8JPg,5436
75
+ pyfemtet/opt/visualization/_monitor.py,sha256=VxR1RutF_bfV37IETsUSIqjPdOPnm4OwrGVJi4eA9ag,45431
76
+ pyfemtet/opt/visualization/result_viewer.py,sha256=n3yToETPO4eDrqBtbmkB1rg0BjRQjCLbdgR84PVLAjk,379
77
+ pyfemtet-0.4.9.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
78
+ pyfemtet-0.4.9.dist-info/METADATA,sha256=py3VvCY5zKdeATTFUj6rQ10lZUczWBjepZUqk_d4pTo,3381
79
+ pyfemtet-0.4.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
80
+ pyfemtet-0.4.9.dist-info/entry_points.txt,sha256=gnfw44Hr_FcEOXVCJDSlvFzmJtmmV59gnPYChcKXem0,92
81
+ pyfemtet-0.4.9.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ pyfemtet-opt-result-viewer=pyfemtet.opt.visualization.result_viewer:main
3
+
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- opt-show=pyfemtet.opt.visualization:entry_point
3
-