pyfemtet 0.4.7__tar.gz → 0.4.9__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.

Files changed (81) hide show
  1. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/PKG-INFO +1 -1
  2. pyfemtet-0.4.9/pyfemtet/__init__.py +1 -0
  3. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/_test_util.py +21 -1
  4. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/_femopt.py +1 -4
  5. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/_femopt_core.py +21 -4
  6. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_NX.py +11 -0
  7. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_SW.py +7 -0
  8. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/gal_ex58_parametric.py +7 -0
  9. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/gau_ex08_parametric.py +7 -0
  10. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/her_ex40_parametric.py +7 -0
  11. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric.py +7 -0
  12. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py +7 -0
  13. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/wat_ex14_parametric.py +7 -0
  14. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py +11 -0
  15. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.py +7 -0
  16. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.py +7 -0
  17. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.py +7 -0
  18. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.py +7 -0
  19. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.py +7 -0
  20. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +7 -0
  21. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py +7 -0
  22. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/_base.py +6 -0
  23. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/_femtet.py +159 -19
  24. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -2
  25. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/_femtet_with_nx/update_model.py +36 -28
  26. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/opt/_base.py +2 -0
  27. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/visualization/_graphs.py +10 -13
  28. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/visualization/_monitor.py +89 -11
  29. pyfemtet-0.4.9/pyfemtet/opt/visualization/result_viewer.py +13 -0
  30. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyproject.toml +2 -2
  31. pyfemtet-0.4.7/pyfemtet/__init__.py +0 -1
  32. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/LICENSE +0 -0
  33. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/README.md +0 -0
  34. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
  35. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
  36. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +0 -0
  37. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.SLDPRT +0 -0
  38. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.femprj +0 -0
  39. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.py +0 -0
  40. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/_her_ex40_parametric.py +0 -0
  41. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
  42. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +0 -0
  43. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
  44. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/her_ex40_parametric.py +0 -0
  45. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +0 -0
  46. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
  47. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +0 -0
  48. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/core.py +0 -0
  49. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/dispatch_extensions.py +0 -0
  50. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/logger.py +0 -0
  51. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/__init__.py +0 -0
  52. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_NX.femprj +0 -0
  53. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_NX.prt +0 -0
  54. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -0
  55. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
  56. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_SW.femprj +0 -0
  57. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -0
  58. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/gal_ex58_parametric.femprj +0 -0
  59. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -0
  60. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/gau_ex08_parametric.femprj +0 -0
  61. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -0
  62. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/her_ex40_parametric.femprj +0 -0
  63. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -0
  64. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric.femprj +0 -0
  65. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -0
  66. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/wat_ex14_parametric.femprj +0 -0
  67. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -0
  68. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
  69. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
  70. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
  71. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
  72. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
  73. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
  74. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.femprj +0 -0
  75. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/__init__.py +0 -0
  76. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/_femtet_parametric.py +0 -0
  77. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -0
  78. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/interface/_femtet_with_sldworks.py +0 -0
  79. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/opt/__init__.py +0 -0
  80. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/opt/_optuna.py +0 -0
  81. {pyfemtet-0.4.7 → pyfemtet-0.4.9}/pyfemtet/opt/visualization/__init__.py +0 -0
@@ -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
@@ -0,0 +1 @@
1
+ __version__ = "0.4.9"
@@ -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% を超える相違があります。'
@@ -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)