pyfemtet 0.4.6__py3-none-any.whl → 0.4.8__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 +1 -1
- pyfemtet/_test_util.py +115 -0
- pyfemtet/opt/_femopt.py +7 -1
- pyfemtet/opt/_femopt_core.py +20 -3
- pyfemtet/opt/femprj_sample/cad_ex01_NX.py +4 -0
- pyfemtet/opt/femprj_sample/cad_ex01_NX_test_result.reccsv +13 -0
- pyfemtet/opt/femprj_sample/cad_ex01_SW_test_result.reccsv +13 -0
- pyfemtet/opt/femprj_sample/gal_ex58_parametric.femprj +0 -0
- pyfemtet/opt/femprj_sample/gal_ex58_parametric_test_result.reccsv +13 -0
- pyfemtet/opt/femprj_sample/gau_ex08_parametric_test_result.reccsv +23 -0
- pyfemtet/opt/femprj_sample/her_ex40_parametric_test_result.reccsv +18 -0
- pyfemtet/opt/femprj_sample/paswat_ex1_parametric_test_result.reccsv +18 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric_test_result.reccsv +18 -0
- pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py +4 -0
- pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
- pyfemtet/opt/interface/_base.py +6 -0
- pyfemtet/opt/interface/_femtet.py +203 -27
- pyfemtet/opt/interface/_femtet_parametric.py +3 -3
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -2
- pyfemtet/opt/interface/_femtet_with_nx/update_model.py +36 -28
- pyfemtet/opt/opt/_base.py +2 -0
- pyfemtet/opt/visualization/_graphs.py +16 -13
- pyfemtet/opt/visualization/_monitor.py +88 -10
- {pyfemtet-0.4.6.dist-info → pyfemtet-0.4.8.dist-info}/METADATA +2 -1
- {pyfemtet-0.4.6.dist-info → pyfemtet-0.4.8.dist-info}/RECORD +28 -20
- {pyfemtet-0.4.6.dist-info → pyfemtet-0.4.8.dist-info}/LICENSE +0 -0
- {pyfemtet-0.4.6.dist-info → pyfemtet-0.4.8.dist-info}/WHEEL +0 -0
- {pyfemtet-0.4.6.dist-info → pyfemtet-0.4.8.dist-info}/entry_points.txt +0 -0
|
@@ -41,7 +41,7 @@ class FemtetInterface(FEMInterface):
|
|
|
41
41
|
strictly_pid_specify (bool, optional): If True and connect_method=='new', search launched Femtet process strictly based on its process id.
|
|
42
42
|
allow_without_project (bool, optional): Allow to launch Femtet with no project file. Default to False.
|
|
43
43
|
open_result_with_gui (bool, optional): Open analysis result with Femtet GUI. Default to True.
|
|
44
|
-
parametric_output_indexes_use_as_objective (
|
|
44
|
+
parametric_output_indexes_use_as_objective (dict, optional): Parametric output indexes and their directions which will be used as objective functions. Parametric output should be set on Femtet parametric analysis dialog. Note that output 'No.' in dialog is starts with 1, but this 'index' is starts with 0. The key is the 'index', the value is direction ('maximize', 'minimize' or float). Default to None.
|
|
45
45
|
|
|
46
46
|
Warning:
|
|
47
47
|
Even if you specify ``strictly_pid_specify=True`` on the constructor,
|
|
@@ -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):
|
|
@@ -199,8 +197,12 @@ class FemtetInterface(FEMInterface):
|
|
|
199
197
|
cmd = f'{sys.executable} -m win32com.client.makepy FemtetMacro'
|
|
200
198
|
os.system(cmd)
|
|
201
199
|
message = 'Femtet python マクロ定数の設定が完了してないことを検出しました.'
|
|
202
|
-
message += '
|
|
203
|
-
message += '
|
|
200
|
+
message += '設定は自動で行われました(python -m win32com.client.makepy FemtetMacro).'
|
|
201
|
+
message += 'プログラムを再起動してください.'
|
|
202
|
+
print('================')
|
|
203
|
+
print(message)
|
|
204
|
+
print('================')
|
|
205
|
+
input('終了するには Enter を押してください。')
|
|
204
206
|
raise RuntimeError(message)
|
|
205
207
|
|
|
206
208
|
if self.Femtet is None:
|
|
@@ -270,7 +272,7 @@ class FemtetInterface(FEMInterface):
|
|
|
270
272
|
self.Femtet.Gaudi.Activate,
|
|
271
273
|
False, # None 以外なら何でもいい
|
|
272
274
|
Exception,
|
|
273
|
-
'
|
|
275
|
+
'Gaudi のオープンに失敗しました',
|
|
274
276
|
print_indent=print_indent + 1
|
|
275
277
|
)
|
|
276
278
|
except com_error:
|
|
@@ -430,14 +432,30 @@ class FemtetInterface(FEMInterface):
|
|
|
430
432
|
Otherwise, no check is performed.
|
|
431
433
|
|
|
432
434
|
"""
|
|
433
|
-
|
|
434
|
-
|
|
435
|
+
major, minor, bugfix = 2023, 1, 1
|
|
436
|
+
if self._version() >= _version(major, minor, bugfix):
|
|
437
|
+
try:
|
|
438
|
+
variable_names = self.Femtet.GetVariableNames_py()
|
|
439
|
+
except AttributeError as e:
|
|
440
|
+
message = 'GetVariableNames_py' + 'にアクセスできません。'
|
|
441
|
+
f'Femtet {major}.{minor}.{bugfix} 以降で「マクロの有効化」が行われていない可能性があります。'
|
|
442
|
+
'スタートメニューから、インストールされいてる Femtet と同一バージョンの「マクロ機能を有効化する」コマンドを管理者権限で実行してください。'
|
|
443
|
+
print('================')
|
|
444
|
+
logger.error(message)
|
|
445
|
+
print('================')
|
|
446
|
+
input('終了するには Enter を押してください。')
|
|
447
|
+
raise e
|
|
448
|
+
|
|
435
449
|
if variable_names is not None:
|
|
436
450
|
if param_name in variable_names:
|
|
437
451
|
return self.Femtet.GetVariableValue(param_name)
|
|
438
452
|
message = f'Femtet 解析モデルに変数 {param_name} がありません.'
|
|
439
453
|
message += f'現在のモデルに設定されている変数は {variable_names} です.'
|
|
440
454
|
message += '大文字・小文字の区別に注意してください.'
|
|
455
|
+
print('================')
|
|
456
|
+
logger.error(message)
|
|
457
|
+
print('================')
|
|
458
|
+
input('終了するには Enter を押してください。')
|
|
441
459
|
raise RuntimeError(message)
|
|
442
460
|
else:
|
|
443
461
|
return None
|
|
@@ -455,8 +473,9 @@ class FemtetInterface(FEMInterface):
|
|
|
455
473
|
error_message='解析モデルが開かれていません',
|
|
456
474
|
)
|
|
457
475
|
|
|
458
|
-
|
|
459
|
-
|
|
476
|
+
major, minor, bugfix = 2023, 1, 1
|
|
477
|
+
if self._version() >= _version(major, minor, bugfix):
|
|
478
|
+
# Femtet の設計変数の更新(2023.1.1 以降でマクロだけ古い場合はcheck_param_valueで引っかかっているはずなのでここはAttributeError をチェックしない)
|
|
460
479
|
existing_variable_names = self._call_femtet_api(
|
|
461
480
|
fun=self.Femtet.GetVariableNames_py,
|
|
462
481
|
return_value_if_failed=False, # 意味がない
|
|
@@ -591,7 +610,39 @@ class FemtetInterface(FEMInterface):
|
|
|
591
610
|
|
|
592
611
|
def quit(self, timeout=1, force=True):
|
|
593
612
|
"""Force to terminate connected Femtet."""
|
|
594
|
-
|
|
613
|
+
major, minor, bugfix = 2024, 0, 1
|
|
614
|
+
if self._version() >= _version(major, minor, bugfix):
|
|
615
|
+
# gracefully termination method without save project available from 2024.0.1
|
|
616
|
+
try:
|
|
617
|
+
self.Femtet.Exit(True)
|
|
618
|
+
except AttributeError as e:
|
|
619
|
+
message = 'Femtet.Exit()' + 'にアクセスできません。'
|
|
620
|
+
f'Femtet {major}.{minor}.{bugfix} 以降で「マクロの有効化」が行われていない可能性があります。'
|
|
621
|
+
'スタートメニューから、インストールされいてる Femtet と同一バージョンの「マクロ機能を有効化する」コマンドを管理者権限で実行してください。'
|
|
622
|
+
print('================')
|
|
623
|
+
logger.error(message)
|
|
624
|
+
print('================')
|
|
625
|
+
input('終了するには Enter を押してください。')
|
|
626
|
+
raise e
|
|
627
|
+
|
|
628
|
+
else:
|
|
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
|
|
595
646
|
|
|
596
647
|
def _setup_before_parallel(self, client):
|
|
597
648
|
client.upload_file(
|
|
@@ -601,3 +652,128 @@ class FemtetInterface(FEMInterface):
|
|
|
601
652
|
|
|
602
653
|
def _version(self):
|
|
603
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
|
+
|
|
@@ -55,20 +55,20 @@ def _get_prm_result_names(Femtet):
|
|
|
55
55
|
return out
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def add_parametric_results_as_objectives(femopt, indexes) -> bool:
|
|
58
|
+
def add_parametric_results_as_objectives(femopt, indexes, directions) -> bool:
|
|
59
59
|
# load dll and set target femtet
|
|
60
60
|
dll = _get_dll_with_set_femtet(femopt.fem.Femtet)
|
|
61
61
|
|
|
62
62
|
# get objective names
|
|
63
63
|
dll.GetPrmnResult.restype = ctypes.c_int
|
|
64
64
|
n = dll.GetPrmnResult()
|
|
65
|
-
for i in indexes:
|
|
65
|
+
for i, direction in zip(indexes, directions):
|
|
66
66
|
# objective name
|
|
67
67
|
dll.GetPrmResultName.restype = ctypes.c_char_p
|
|
68
68
|
result = dll.GetPrmResultName(i)
|
|
69
69
|
name = result.decode('mbcs')
|
|
70
70
|
# objective value function
|
|
71
|
-
femopt.add_objective(_parametric_objective, name, args=(i,))
|
|
71
|
+
femopt.add_objective(_parametric_objective, name, direction=direction, args=(i,))
|
|
72
72
|
return True # ここまで来たら成功
|
|
73
73
|
|
|
74
74
|
|
|
@@ -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 更新が正常に終了しました。')
|
pyfemtet/opt/opt/_base.py
CHANGED
|
@@ -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):
|
|
@@ -81,6 +82,9 @@ def update_single_objective_plot(history, df):
|
|
|
81
82
|
df = _ls.localize(df)
|
|
82
83
|
obj_name = history.obj_names[0]
|
|
83
84
|
|
|
85
|
+
df.columns = [c.replace(' / ', '<BR>/ ') for c in df.columns]
|
|
86
|
+
obj_name = obj_name.replace(' / ', '<BR>/ ')
|
|
87
|
+
|
|
84
88
|
fig = px.scatter(
|
|
85
89
|
df,
|
|
86
90
|
x='trial',
|
|
@@ -94,7 +98,7 @@ def update_single_objective_plot(history, df):
|
|
|
94
98
|
_ls.feasible['label']: False,
|
|
95
99
|
'trial': True,
|
|
96
100
|
},
|
|
97
|
-
custom_data=
|
|
101
|
+
custom_data=['trial'],
|
|
98
102
|
)
|
|
99
103
|
|
|
100
104
|
fig.add_trace(
|
|
@@ -126,6 +130,9 @@ def update_multi_objective_pairplot(history, df):
|
|
|
126
130
|
|
|
127
131
|
obj_names = history.obj_names
|
|
128
132
|
|
|
133
|
+
df.columns = [c.replace(' / ', '<BR>/ ') for c in df.columns]
|
|
134
|
+
obj_names = [o.replace(' / ', '<BR>/ ') for o in obj_names]
|
|
135
|
+
|
|
129
136
|
common_kwargs = dict(
|
|
130
137
|
color=_ls.non_domi['label'],
|
|
131
138
|
color_discrete_map={
|
|
@@ -137,11 +144,7 @@ def update_multi_objective_pairplot(history, df):
|
|
|
137
144
|
_ls.feasible[True]: _ss.feasible[True],
|
|
138
145
|
_ls.feasible[False]: _ss.feasible[False],
|
|
139
146
|
},
|
|
140
|
-
|
|
141
|
-
_ls.feasible['label']: False,
|
|
142
|
-
'trial': True,
|
|
143
|
-
},
|
|
144
|
-
custom_data=_CUSTOM_DATA_DICT.keys(),
|
|
147
|
+
custom_data=['trial'],
|
|
145
148
|
category_orders={
|
|
146
149
|
_ls.feasible['label']: (_ls.feasible[False], _ls.feasible[True]),
|
|
147
150
|
_ls.non_domi['label']: (_ls.non_domi[False], _ls.non_domi[True]),
|