pyfemtet 0.4.7__tar.gz → 0.4.8__tar.gz
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-0.4.7 → pyfemtet-0.4.8}/PKG-INFO +1 -1
- pyfemtet-0.4.8/pyfemtet/__init__.py +1 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/_test_util.py +21 -1
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/_femopt_core.py +20 -3
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_NX.py +4 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py +4 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/_base.py +6 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/_femtet.py +159 -19
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -2
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/_femtet_with_nx/update_model.py +36 -28
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/opt/_base.py +2 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/visualization/_graphs.py +10 -13
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/visualization/_monitor.py +88 -10
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyproject.toml +1 -1
- pyfemtet-0.4.7/pyfemtet/__init__.py +0 -1
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/LICENSE +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/README.md +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.SLDPRT +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/_her_ex40_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/her_ex40_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/core.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/dispatch_extensions.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/logger.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/__init__.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/_femopt.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_NX.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_NX.prt +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_SW.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_SW.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gal_ex58_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gal_ex58_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gau_ex08_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gau_ex08_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/her_ex40_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/her_ex40_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/wat_ex14_parametric.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/wat_ex14_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.femprj +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/__init__.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/_femtet_parametric.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/interface/_femtet_with_sldworks.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/opt/__init__.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/opt/_optuna.py +0 -0
- {pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/visualization/__init__.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.8"
|
|
@@ -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% を超える相違があります。'
|
|
@@ -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(
|
|
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
|
# 目的関数の履歴を取り出してくる
|
|
@@ -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.
|
|
@@ -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との接続を行います)
|
|
@@ -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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
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
|
|
5
|
-
|
|
4
|
+
from xml.etree.ElementInclude import include
|
|
6
5
|
|
|
7
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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 更新が正常に終了しました。')
|
|
@@ -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=
|
|
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
|
-
|
|
67
|
+
fig = go.Figure()
|
|
71
68
|
|
|
72
69
|
elif len(obj_names) == 1:
|
|
73
|
-
|
|
70
|
+
fig = update_single_objective_plot(history, df)
|
|
74
71
|
|
|
75
72
|
elif len(obj_names) >= 2:
|
|
76
|
-
|
|
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=
|
|
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
|
-
|
|
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'][
|
|
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
|
-
|
|
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.
|
|
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
|
-
'-
|
|
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
|
-
'-
|
|
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] =
|
|
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:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pyfemtet"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.8" # ignored by versioning plugin
|
|
4
4
|
description = "Design parameter optimization using Femtet."
|
|
5
5
|
authors = ["kazuma.naito <kazuma.naito@murata.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.7"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.SLDPRT
RENAMED
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.femprj
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gal_ex58_parametric_test_result.reccsv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/gau_ex08_parametric_test_result.reccsv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/her_ex40_parametric_test_result.reccsv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample/wat_ex14_parametric_test_result.reccsv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.femprj
RENAMED
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.femprj
RENAMED
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.femprj
RENAMED
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.femprj
RENAMED
|
File without changes
|
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py
RENAMED
|
File without changes
|
{pyfemtet-0.4.7 → pyfemtet-0.4.8}/pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.femprj
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|