pyfemtet 0.9.6__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyfemtet might be problematic. Click here for more details.

Files changed (272) hide show
  1. pyfemtet/__init__.py +6 -1
  2. pyfemtet/_i18n/1. make_pot_and_update_po.bat +8 -0
  3. pyfemtet/_i18n/2. build_mo.bat +5 -0
  4. pyfemtet/_i18n/__init__.py +4 -0
  5. pyfemtet/_i18n/babel.cfg +2 -0
  6. pyfemtet/_i18n/i18n.py +37 -0
  7. pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
  8. pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.po +1020 -0
  9. pyfemtet/_i18n/locales/messages.pot +987 -0
  10. pyfemtet/{_message → _i18n}/messages.py +128 -41
  11. pyfemtet/_util/closing.py +19 -0
  12. pyfemtet/_util/dask_util.py +89 -7
  13. pyfemtet/_util/df_util.py +46 -0
  14. pyfemtet/_util/excel_macro_util.py +8 -3
  15. pyfemtet/_util/excel_parse_util.py +43 -23
  16. pyfemtet/_util/femtet_access_inspection.py +120 -0
  17. pyfemtet/{_femtet_config_util/autosave.py → _util/femtet_autosave.py} +7 -0
  18. pyfemtet/_util/femtet_exit.py +105 -0
  19. pyfemtet/_util/femtet_version.py +20 -0
  20. pyfemtet/_util/helper.py +103 -0
  21. pyfemtet/_util/process_util.py +107 -0
  22. pyfemtet/_util/str_enum.py +44 -0
  23. pyfemtet/core.py +15 -47
  24. pyfemtet/dispatch_extensions/__init__.py +8 -11
  25. pyfemtet/dispatch_extensions/_impl.py +42 -198
  26. pyfemtet/logger/__init__.py +8 -1
  27. pyfemtet/logger/_impl.py +5 -6
  28. pyfemtet/opt/__init__.py +3 -17
  29. pyfemtet/opt/exceptions.py +45 -0
  30. pyfemtet/opt/femopt.py +621 -0
  31. pyfemtet/opt/history/__init__.py +11 -0
  32. pyfemtet/opt/history/_history.py +1416 -0
  33. pyfemtet/opt/history/_hypervolume.py +169 -0
  34. pyfemtet/opt/history/_optimality.py +79 -0
  35. pyfemtet/opt/interface/__init__.py +17 -24
  36. pyfemtet/opt/interface/_base_interface.py +222 -0
  37. pyfemtet/opt/interface/_excel_interface/__init__.py +3 -0
  38. pyfemtet/opt/interface/_excel_interface/debug-excel-interface.xlsm +0 -0
  39. pyfemtet/opt/interface/_excel_interface/excel_interface.py +997 -0
  40. pyfemtet/opt/interface/_femtet_interface/__init__.py +3 -0
  41. pyfemtet/opt/interface/{_femtet_parametric.py → _femtet_interface/_femtet_parametric.py} +20 -12
  42. pyfemtet/opt/interface/{_femtet.py → _femtet_interface/femtet_interface.py} +508 -353
  43. pyfemtet/opt/interface/_femtet_with_nx_interface/__init__.py +5 -0
  44. pyfemtet/opt/interface/_femtet_with_nx_interface/femtet_with_nx_interface.py +230 -0
  45. pyfemtet/opt/interface/_femtet_with_nx_interface/model1.prt +0 -0
  46. pyfemtet/opt/interface/_femtet_with_nx_interface/model1.x_t +98 -0
  47. pyfemtet/opt/interface/{_femtet_with_nx → _femtet_with_nx_interface}/update_model.py +1 -3
  48. pyfemtet/opt/interface/_femtet_with_solidworks/__init__.py +5 -0
  49. pyfemtet/opt/interface/_femtet_with_solidworks/femtet_with_solidworks_interface.py +142 -0
  50. pyfemtet/opt/interface/_solidworks_interface/__init__.py +5 -0
  51. pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py +227 -0
  52. pyfemtet/opt/interface/_surrogate_model_interface/__init__.py +8 -0
  53. pyfemtet/opt/interface/_surrogate_model_interface/base_surrogate_interface.py +150 -0
  54. pyfemtet/opt/interface/_surrogate_model_interface/botorch_interface.py +298 -0
  55. pyfemtet/opt/interface/_surrogate_model_interface/debug-pof-botorch.reccsv +18 -0
  56. pyfemtet/opt/interface/_with_excel_settings/__init__.py +61 -0
  57. pyfemtet/opt/interface/_with_excel_settings/with_excel_settings.py +134 -0
  58. pyfemtet/opt/meta_script/YAML_Generator.xlsm +0 -0
  59. pyfemtet/opt/meta_script/__main__.py +58 -36
  60. pyfemtet/opt/optimizer/__init__.py +7 -9
  61. pyfemtet/opt/optimizer/_base_optimizer.py +911 -0
  62. pyfemtet/opt/optimizer/optuna_optimizer/__init__.py +9 -0
  63. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py +63 -0
  64. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py +796 -0
  65. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/__init__.py +7 -0
  66. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/debug-pof-botorch.reccsv +18 -0
  67. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/enable_nonlinear_constraint.py +244 -0
  68. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/pof_botorch_sampler.py +1249 -0
  69. pyfemtet/opt/optimizer/optuna_optimizer/wat_ex14_parametric_jp.femprj +0 -0
  70. pyfemtet/opt/optimizer/scipy_optimizer/__init__.py +1 -0
  71. pyfemtet/opt/optimizer/scipy_optimizer/_scipy_optimizer.py +383 -0
  72. pyfemtet/opt/prediction/__init__.py +7 -0
  73. pyfemtet/opt/prediction/_botorch_utils.py +133 -0
  74. pyfemtet/opt/prediction/_gpytorch_modules_extension.py +142 -0
  75. pyfemtet/opt/prediction/_helper.py +155 -0
  76. pyfemtet/opt/prediction/_model.py +118 -0
  77. pyfemtet/opt/problem/problem.py +304 -0
  78. pyfemtet/opt/problem/variable_manager/__init__.py +20 -0
  79. pyfemtet/opt/problem/variable_manager/_string_as_expression.py +115 -0
  80. pyfemtet/opt/problem/variable_manager/_variable_manager.py +295 -0
  81. pyfemtet/opt/visualization/history_viewer/__main__.py +5 -0
  82. pyfemtet/opt/visualization/{_base.py → history_viewer/_base_application.py} +18 -13
  83. pyfemtet/opt/visualization/history_viewer/_common_pages.py +150 -0
  84. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/alert_region.py +10 -5
  85. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/control_femtet.py +16 -13
  86. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/main_graph.py +117 -47
  87. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/pm_graph.py +159 -138
  88. pyfemtet/opt/visualization/history_viewer/_process_monitor/_application.py +173 -0
  89. pyfemtet/opt/visualization/history_viewer/_process_monitor/_pages.py +291 -0
  90. pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/dbc.py +1 -1
  91. pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/dcc.py +1 -1
  92. pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/html.py +1 -1
  93. pyfemtet/opt/visualization/history_viewer/result_viewer/__main__.py +5 -0
  94. pyfemtet/opt/visualization/{result_viewer/application.py → history_viewer/result_viewer/_application.py} +6 -6
  95. pyfemtet/opt/visualization/{result_viewer/pages.py → history_viewer/result_viewer/_pages.py} +106 -82
  96. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08.csv +18 -0
  97. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08.db +0 -0
  98. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.jpg +0 -0
  99. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.log +45 -0
  100. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.pdt +0 -0
  101. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_1.jpg +0 -0
  102. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_1.pdt +0 -0
  103. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_10.jpg +0 -0
  104. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_10.pdt +0 -0
  105. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_11.jpg +0 -0
  106. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_11.pdt +0 -0
  107. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_12.jpg +0 -0
  108. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_12.pdt +0 -0
  109. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_13.jpg +0 -0
  110. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_13.pdt +0 -0
  111. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_14.jpg +0 -0
  112. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_14.pdt +0 -0
  113. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_15.jpg +0 -0
  114. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_15.pdt +0 -0
  115. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_16.jpg +0 -0
  116. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_16.pdt +0 -0
  117. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_17.jpg +0 -0
  118. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_17.pdt +0 -0
  119. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_18.jpg +0 -0
  120. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_18.pdt +0 -0
  121. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_19.jpg +0 -0
  122. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_19.pdt +0 -0
  123. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_2.jpg +0 -0
  124. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_2.pdt +0 -0
  125. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_20.jpg +0 -0
  126. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_20.pdt +0 -0
  127. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_3.jpg +0 -0
  128. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_3.pdt +0 -0
  129. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.bgr +0 -0
  130. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.bnd +0 -0
  131. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.btr +0 -0
  132. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.jpg +0 -0
  133. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.mtl +0 -0
  134. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.pdt +0 -0
  135. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.prm +0 -0
  136. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_5.jpg +0 -0
  137. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_5.pdt +0 -0
  138. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_6.jpg +0 -0
  139. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_6.pdt +0 -0
  140. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_7.jpg +0 -0
  141. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_7.pdt +0 -0
  142. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_8.jpg +0 -0
  143. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_8.pdt +0 -0
  144. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_9.jpg +0 -0
  145. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_9.pdt +0 -0
  146. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.femprj +0 -0
  147. pyfemtet/opt/visualization/plotter/main_figure_creator.py +536 -0
  148. pyfemtet/opt/visualization/plotter/pm_graph_creator.py +359 -0
  149. pyfemtet/opt/worker_status.py +120 -0
  150. {pyfemtet-0.9.6.dist-info → pyfemtet-1.0.0.dist-info}/METADATA +23 -24
  151. pyfemtet-1.0.0.dist-info/RECORD +172 -0
  152. pyfemtet-1.0.0.dist-info/entry_points.txt +3 -0
  153. pyfemtet/_femtet_config_util/exit.py +0 -59
  154. pyfemtet/_message/1. make_pot.bat +0 -11
  155. pyfemtet/_message/2. make_mo.bat +0 -6
  156. pyfemtet/_message/__init__.py +0 -5
  157. pyfemtet/_message/babel.cfg +0 -2
  158. pyfemtet/_message/locales/ja/LC_MESSAGES/messages.mo +0 -0
  159. pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +0 -570
  160. pyfemtet/_message/locales/messages.pot +0 -551
  161. pyfemtet/_warning.py +0 -87
  162. pyfemtet/brep/_impl.py +0 -18
  163. pyfemtet/opt/_femopt.py +0 -1007
  164. pyfemtet/opt/_femopt_core.py +0 -1169
  165. pyfemtet/opt/_test_utils/control_femtet.py +0 -39
  166. pyfemtet/opt/_test_utils/hyper_sphere.py +0 -24
  167. pyfemtet/opt/_test_utils/record_history.py +0 -130
  168. pyfemtet/opt/advanced_samples/excel_ui/(ref) original_project.femprj +0 -0
  169. pyfemtet/opt/advanced_samples/excel_ui/femtet-macro.xlsm +0 -0
  170. pyfemtet/opt/advanced_samples/excel_ui/pyfemtet-core.py +0 -291
  171. pyfemtet/opt/advanced_samples/excel_ui/test-pyfemtet-core.cmd +0 -22
  172. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric.femprj +0 -0
  173. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart.py +0 -99
  174. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart_jp.py +0 -102
  175. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data.py +0 -60
  176. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data_jp.py +0 -57
  177. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate.py +0 -100
  178. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate_jp.py +0 -90
  179. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_parametric.femprj +0 -0
  180. pyfemtet/opt/interface/_base.py +0 -101
  181. pyfemtet/opt/interface/_excel_interface.py +0 -984
  182. pyfemtet/opt/interface/_femtet_excel.py +0 -141
  183. pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -3
  184. pyfemtet/opt/interface/_femtet_with_nx/_interface.py +0 -178
  185. pyfemtet/opt/interface/_femtet_with_sldworks.py +0 -298
  186. pyfemtet/opt/interface/_surrogate/__init__.py +0 -5
  187. pyfemtet/opt/interface/_surrogate/_base.py +0 -129
  188. pyfemtet/opt/interface/_surrogate/_chaospy.py +0 -71
  189. pyfemtet/opt/interface/_surrogate/_singletaskgp.py +0 -71
  190. pyfemtet/opt/interface/_surrogate_excel.py +0 -102
  191. pyfemtet/opt/optimizer/_base.py +0 -376
  192. pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py +0 -220
  193. pyfemtet/opt/optimizer/_optuna/_optuna.py +0 -434
  194. pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +0 -1914
  195. pyfemtet/opt/optimizer/_scipy.py +0 -159
  196. pyfemtet/opt/optimizer/_scipy_scalar.py +0 -127
  197. pyfemtet/opt/optimizer/parameter.py +0 -113
  198. pyfemtet/opt/prediction/_base.py +0 -61
  199. pyfemtet/opt/prediction/single_task_gp.py +0 -119
  200. pyfemtet/opt/samples/femprj_sample/ParametricIF.femprj +0 -0
  201. pyfemtet/opt/samples/femprj_sample/ParametricIF.py +0 -29
  202. pyfemtet/opt/samples/femprj_sample/ParametricIF_test_result.reccsv +0 -13
  203. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.femprj +0 -0
  204. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.prt +0 -0
  205. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.py +0 -135
  206. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -23
  207. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
  208. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.femprj +0 -0
  209. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.py +0 -131
  210. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -23
  211. pyfemtet/opt/samples/femprj_sample/constrained_pipe.femprj +0 -0
  212. pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +0 -96
  213. pyfemtet/opt/samples/femprj_sample/constrained_pipe_test_result.reccsv +0 -13
  214. pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.femprj +0 -0
  215. pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.py +0 -74
  216. pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -13
  217. pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.femprj +0 -0
  218. pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.py +0 -58
  219. pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -23
  220. pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.femprj +0 -0
  221. pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.py +0 -52
  222. pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.femprj +0 -0
  223. pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +0 -138
  224. pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -18
  225. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.femprj +0 -0
  226. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.py +0 -60
  227. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_parallel.py +0 -61
  228. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -18
  229. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.femprj +0 -0
  230. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.py +0 -58
  231. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_parallel.py +0 -58
  232. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -18
  233. pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.femprj +0 -0
  234. pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.py +0 -29
  235. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
  236. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.py +0 -129
  237. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
  238. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.py +0 -125
  239. pyfemtet/opt/samples/femprj_sample_jp/constrained_pipe_jp.py +0 -93
  240. pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
  241. pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -70
  242. pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
  243. pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -57
  244. pyfemtet/opt/samples/femprj_sample_jp/gau_ex12_parametric_jp.py +0 -52
  245. pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
  246. pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.py +0 -138
  247. pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
  248. pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -58
  249. pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -59
  250. pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -56
  251. pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +0 -56
  252. pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +0 -332
  253. pyfemtet/opt/visualization/_complex_components/pm_graph_creator.py +0 -201
  254. pyfemtet/opt/visualization/_process_monitor/application.py +0 -226
  255. pyfemtet/opt/visualization/_process_monitor/pages.py +0 -406
  256. pyfemtet/opt/visualization/_wrapped_components/__init__.py +0 -0
  257. pyfemtet/opt/visualization/result_viewer/__init__.py +0 -0
  258. pyfemtet-0.9.6.dist-info/RECORD +0 -158
  259. pyfemtet-0.9.6.dist-info/entry_points.txt +0 -3
  260. /pyfemtet/{_femtet_config_util → opt/problem}/__init__.py +0 -0
  261. /pyfemtet/{brep → opt/visualization/history_viewer}/__init__.py +0 -0
  262. /pyfemtet/opt/{_test_utils → visualization/history_viewer/_complex_components}/__init__.py +0 -0
  263. /pyfemtet/opt/{optimizer/_optuna → visualization/history_viewer/_process_monitor}/__init__.py +0 -0
  264. /pyfemtet/opt/{optimizer/_optuna/_botorch_patch → visualization/history_viewer/_wrapped_components}/__init__.py +0 -0
  265. /pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/str_enum.py +0 -0
  266. /pyfemtet/opt/visualization/{result_viewer → history_viewer/result_viewer}/.gitignore +0 -0
  267. /pyfemtet/opt/visualization/{_complex_components → history_viewer/result_viewer}/__init__.py +0 -0
  268. /pyfemtet/opt/visualization/{_process_monitor → plotter}/__init__.py +0 -0
  269. /pyfemtet/opt/{samples/femprj_sample_jp/wat_ex14_parametric_jp.femprj → wat_ex14_parametric_jp.femprj} +0 -0
  270. {pyfemtet-0.9.6.dist-info → pyfemtet-1.0.0.dist-info}/LICENSE +0 -0
  271. {pyfemtet-0.9.6.dist-info → pyfemtet-1.0.0.dist-info}/LICENSE_THIRD_PARTY.txt +0 -0
  272. {pyfemtet-0.9.6.dist-info → pyfemtet-1.0.0.dist-info}/WHEEL +0 -0
@@ -1,16 +1,12 @@
1
- import warnings
2
- from typing import Optional, List, Final
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  import os
5
6
  import sys
6
- from time import sleep, time
7
- import winreg
8
7
  import subprocess
9
-
10
- import pandas as pd
11
- import psutil
12
- import shutil
13
- from dask.distributed import get_worker
8
+ from time import sleep
9
+ from contextlib import nullcontext
14
10
 
15
11
  # noinspection PyUnresolvedReferences
16
12
  from pywintypes import com_error, error
@@ -20,32 +16,31 @@ from pythoncom import CoInitialize, CoUninitialize
20
16
  from win32com.client import constants
21
17
  import win32con
22
18
  import win32gui
23
- from femtetutils import util
24
-
25
- from pyfemtet.core import (
26
- ModelError,
27
- MeshError,
28
- SolveError,
29
- _version,
30
- )
31
- from pyfemtet.dispatch_extensions import (
32
- dispatch_femtet,
33
- dispatch_specific_femtet,
34
- launch_and_dispatch_femtet,
35
- _get_pid,
36
- _get_pids,
37
- DispatchExtensionException,
38
- )
39
- from pyfemtet.opt.interface._base import FEMInterface
40
- from pyfemtet._message import Msg
41
- from pyfemtet._femtet_config_util.autosave import _get_autosave_enabled, _set_autosave_enabled
42
- from pyfemtet._femtet_config_util.exit import _exit_or_force_terminate
43
-
44
- if __name__ == '__main__':
45
- from pyfemtet.logger import get_module_logger
46
- logger = get_module_logger('opt.interface.FemtetInterface', __name__)
47
- else:
48
- from pyfemtet.opt.interface._base import logger
19
+
20
+ import pandas as pd
21
+
22
+ from pyfemtet.logger import get_module_logger
23
+
24
+ from pyfemtet._i18n import Msg, _
25
+ from pyfemtet._util.helper import *
26
+ from pyfemtet._util.dask_util import *
27
+ from pyfemtet._util.femtet_exit import *
28
+ from pyfemtet._util.process_util import *
29
+ from pyfemtet._util.femtet_version import *
30
+ from pyfemtet._util.femtet_autosave import *
31
+ from pyfemtet._util.femtet_access_inspection import *
32
+
33
+ from pyfemtet.dispatch_extensions import *
34
+ from pyfemtet.opt.interface._base_interface import COMInterface
35
+ from pyfemtet.opt.exceptions import *
36
+ from pyfemtet.opt.problem.variable_manager import SupportedVariableTypes
37
+
38
+ from ._femtet_parametric import *
39
+
40
+ if TYPE_CHECKING:
41
+ from pyfemtet.opt.optimizer import AbstractOptimizer
42
+
43
+ logger = get_module_logger('opt.interface', False)
49
44
 
50
45
 
51
46
  def _post_activate_message(hwnd):
@@ -56,10 +51,7 @@ class FailedToPostProcess(Exception):
56
51
  pass
57
52
 
58
53
 
59
-
60
-
61
-
62
- class FemtetInterface(FEMInterface):
54
+ class FemtetInterface(COMInterface):
63
55
  """Control Femtet from optimizer.
64
56
 
65
57
  Args:
@@ -110,8 +102,6 @@ class FemtetInterface(FEMInterface):
110
102
  If you do not want to delete the swept table,
111
103
  make a copy of the original file.
112
104
 
113
- **kwargs: Additional arguments from inherited classes.
114
-
115
105
  Warning:
116
106
  Even if you specify ``strictly_pid_specify=True`` on the constructor,
117
107
  **the connection behavior is like** ``strictly_pid_specify=False`` **in parallel processing**
@@ -125,24 +115,32 @@ class FemtetInterface(FEMInterface):
125
115
 
126
116
  """
127
117
 
118
+ com_members = {'Femtet': 'FemtetMacro.Femtet'}
119
+ _show_parametric_index_warning = True # for GUI
120
+ _femtet_connection_timeout = 10
121
+
128
122
  def __init__(
129
123
  self,
130
124
  femprj_path: str = None,
131
125
  model_name: str = None,
132
- connect_method: str = 'auto', # dask worker では __init__ の中で 'new' にするので super() の引数にしない。(しても意味がない)
133
- save_pdt: str = 'all', # 'all', 'none' or 'optimal'
126
+ connect_method: str = "auto", # dask worker では __init__ の中で 'new' にする
127
+ save_pdt: str = "all", # 'all', 'none' or 'optimal'
134
128
  strictly_pid_specify: bool = True, # dask worker では True にしたいので super() の引数にしない。
135
129
  allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
136
130
  open_result_with_gui: bool = True,
137
- parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
138
- **kwargs # 継承されたクラスからの引数
131
+ always_open_copy=False,
132
+ # ユーザーはメソッドを使うことを推奨。GUI などで使用。
133
+ parametric_output_indexes_use_as_objective: dict[int, str | float] = None,
139
134
  ):
140
- # warning
141
135
  if parametric_output_indexes_use_as_objective is not None:
142
- warnings.warn('解析モデルに設定された既存のスイープテーブルは削除されます。')
143
-
144
- # win32com の初期化
145
- CoInitialize()
136
+ if FemtetInterface._show_parametric_index_warning:
137
+ logger.warning(_(
138
+ en_message='The argument `parametric_output_indexes_use_as_objective` is deprecated. '
139
+ 'Please use `FemtetInterface.use_parametric_output_as_objective()` instead.',
140
+ jp_message='`parametric_output_indexes_use_as_objective` は非推奨の引数です。'
141
+ '代わりに `FemtetInterface.use_parametric_output_as_objective()` '
142
+ 'を使ってください。',
143
+ ))
146
144
 
147
145
  # 引数の処理
148
146
  if femprj_path is None:
@@ -152,86 +150,135 @@ class FemtetInterface(FEMInterface):
152
150
  self.model_name = model_name
153
151
  self.connect_method = connect_method
154
152
  self.allow_without_project = allow_without_project
155
- self.original_femprj_path = self.femprj_path
153
+ self._original_femprj_path = self.femprj_path
156
154
  self.open_result_with_gui = open_result_with_gui
157
155
  self.save_pdt = save_pdt
156
+ self._always_open_copy = always_open_copy
158
157
 
159
158
  # その他のメンバーの宣言や初期化
160
159
  self.Femtet = None
161
160
  self.femtet_pid = 0
162
161
  self.quit_when_destruct = False
163
- self.connected_method = 'unconnected'
164
- self.parameters = None
162
+ self.connected_method = "unconnected"
165
163
  self.max_api_retry = 3
166
164
  self.strictly_pid_specify = strictly_pid_specify
167
165
  self.parametric_output_indexes_use_as_objective = parametric_output_indexes_use_as_objective
166
+ self._load_problem_from_fem = self.parametric_output_indexes_use_as_objective is not None
168
167
  self._original_autosave_enabled = _get_autosave_enabled()
169
168
  _set_autosave_enabled(False)
169
+ self._warn_if_undefined_variable = True
170
+ self.api_response_warning_time = 10 # mesh, solve, re-execute を除くマクロ実行警告時間
170
171
 
171
- # dask サブプロセスのときは femprj を更新し connect_method を new にする
172
- try:
173
- worker = get_worker()
174
- space = worker.local_directory
175
- # worker なら femprj_path が None でないはず
176
- self.femprj_path = os.path.join(space, os.path.basename(self.femprj_path))
177
- self.connect_method = 'new'
178
- self.strictly_pid_specify = False
179
- except ValueError: # get_worker に失敗した場合
180
- pass
181
-
182
- # femprj_path と model に基づいて Femtet を開き、
183
- # 開かれたモデルに応じて femprj_path と model を更新する
172
+ # connect to Femtet
184
173
  self._connect_and_open_femtet()
174
+ assert self.connected_method != 'unconnected'
175
+
176
+ if self._always_open_copy:
177
+ # 現時点でFemtet に model を close する機能がない
178
+ # + executor でも CAD 連携等でモデルに
179
+ # わかりにくい変更が入らないように
180
+ # _tmp_dir のファイルを開くようにしたため
181
+ # _tmp_dir 削除時の permission error を
182
+ # 避けるために Femtet を強制 close する
183
+ # Femtet で model が close できるようになれば
184
+ # この条件分岐は不要になる
185
+ self.quit_when_destruct = True
185
186
 
186
- # original_fem_prj が None なら必ず
187
- # dask worker でないプロセスがオリジナルファイルを開いている
188
- if self.original_femprj_path is None:
189
- # dask worker でなければ original のはず
190
- try:
191
- _ = get_worker()
192
- except ValueError:
193
- self.original_femprj_path = self.femprj_path
194
-
195
- # 接続した Femtet の種類に応じて del 時に quit するかどうか決める
196
- self.quit_when_destruct = self.connected_method == 'new'
187
+ else:
188
+ # 接続した Femtet の種類に応じて del 時に quit するかどうか決める
189
+ self.quit_when_destruct = self.connected_method == "new"
197
190
 
198
- # subprocess restore するための情報保管
199
- # パスなどは connect_and_open_femtet での処理結果を反映し
200
- # メインで開いた解析モデルが確実に開かれるようにする
201
- FEMInterface.__init__(
202
- self,
203
- femprj_path=self.femprj_path,
204
- model_name=self.model_name,
205
- open_result_with_gui=self.open_result_with_gui,
206
- parametric_output_indexes_use_as_objective=self.parametric_output_indexes_use_as_objective,
207
- save_pdt=self.save_pdt,
208
- **kwargs
209
- )
191
+ # ===== system =====
210
192
 
211
193
  @property
212
- def object_passed_to_functions(self):
194
+ def object_pass_to_fun(self):
195
+ """The object pass to the first argument of user-defined objective functions.
196
+
197
+ Returns:
198
+ Femtet (CDispatch): COM object of Femtet.
199
+ """
213
200
  return self.Femtet
214
201
 
215
- def use_parametric_output_as_objective(self, number: int, direction: str | float = 'minimize') -> None:
202
+ def _setup_before_parallel(self):
203
+ self._distribute_files([self.femprj_path])
204
+
205
+ def _setup_after_parallel(self, opt: AbstractOptimizer = None):
206
+
207
+ # main worker かつ always_open_copy でないときのみ
208
+ # femprj_path を切り替えない
209
+ if (get_worker() is None) and not self._always_open_copy:
210
+ pass
211
+
212
+ # worker space の femprj に切り替える
213
+ else:
214
+ suffix = self._get_worker_index_from_optimizer(opt)
215
+ self.femprj_path = self._rename_and_get_path_on_worker_space(self._original_femprj_path, suffix)
216
+
217
+ # dask process ならば Femtet を起動
218
+ worker = get_worker()
219
+ if worker is not None:
220
+ CoInitialize()
221
+ self.connect_femtet(connect_method='new')
222
+ self.quit_when_destruct = True
223
+
224
+ # femprj を開く
225
+ self.open(self.femprj_path, self.model_name)
226
+
227
+ def close(self, timeout=15, force=True): # 12 秒程度
228
+ """Force to terminate connected Femtet."""
229
+
230
+ _set_autosave_enabled(self._original_autosave_enabled)
231
+
232
+ if not hasattr(self, 'Femtet'):
233
+ return
234
+
235
+ if self.Femtet is None:
236
+ return
237
+
238
+ if self.quit_when_destruct:
239
+ logger.info(_('Closing Femtet (pid = {pid}) ...', pid=self.femtet_pid))
240
+ succeeded = _exit_or_force_terminate(
241
+ timeout=timeout, Femtet=self.Femtet, force=force)
242
+ if succeeded:
243
+ logger.info(_('Femtet is closed.'))
244
+ else:
245
+ logger.warning(_('Failed to close Femtet.'))
246
+
247
+ def use_parametric_output_as_objective(
248
+ self, number: int, direction: str | float = "minimize"
249
+ ) -> None:
216
250
  """Use output setting of Femtet parametric analysis as an objective function.
217
251
 
218
252
  Args:
219
253
  number (int): The index of output settings tab in parametric analysis dialog of Femtet. Starts at 1.
220
- direction (str | float): Objective direction. Valid input is one of 'minimize', 'maximize' or a specific value. Defaults to 'minimize'.
254
+ direction (str | float): Objective direction.
255
+ Valid input is one of 'minimize', 'maximize' or a specific value.
256
+ Defaults to 'minimize'.
221
257
 
222
258
  Returns:
223
259
  None
224
260
 
225
261
  """
262
+
263
+ # warning
264
+ logger.warning(_(
265
+ en_message='The existing sweep table in the project will be removed.',
266
+ jp_message='解析モデルに設定された既存のスイープテーブルは削除されます。'
267
+ ))
268
+
226
269
  # check
227
270
  if isinstance(direction, str):
228
- if direction not in ('minimize', 'maximize'):
229
- raise ValueError(f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}')
271
+ if direction not in ("minimize", "maximize"):
272
+ raise ValueError(
273
+ f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}'
274
+ )
230
275
  else:
231
276
  try:
232
277
  direction = float(direction)
233
278
  except (TypeError, ValueError):
234
- raise ValueError(f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}')
279
+ raise ValueError(
280
+ f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}'
281
+ )
235
282
 
236
283
  index = {number - 1: direction}
237
284
 
@@ -241,35 +288,44 @@ class FemtetInterface(FEMInterface):
241
288
  else:
242
289
  self.parametric_output_indexes_use_as_objective.update(index)
243
290
 
244
- # TODO: FEMInterface.__init__ の仕様を変えたらここも変える
245
- self.kwargs['parametric_output_indexes_use_as_objective'] = self.parametric_output_indexes_use_as_objective
291
+ self._load_problem_from_fem = True
292
+
293
+ def load_objectives(self, opt: AbstractOptimizer):
294
+ indexes = list(self.parametric_output_indexes_use_as_objective.keys())
295
+ directions = list(self.parametric_output_indexes_use_as_objective.values())
296
+ add_parametric_results_as_objectives(
297
+ opt, self.Femtet, indexes, directions
298
+ )
246
299
 
300
+ def _check_using_fem(self, fun: callable):
301
+ return _is_access_femtet(fun)
247
302
 
248
- def __del__(self):
249
- self.quit()
250
- # CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
303
+ # ===== connect_femtet =====
251
304
 
252
305
  def _connect_new_femtet(self):
253
- logger.info('└ Try to launch and connect new Femtet process.')
306
+ logger.info("└ Try to launch and connect new Femtet process.")
254
307
 
255
- self.Femtet, self.femtet_pid = launch_and_dispatch_femtet(strictly_pid_specify=self.strictly_pid_specify)
308
+ self.Femtet, self.femtet_pid = launch_and_dispatch_femtet(
309
+ strictly_pid_specify=self.strictly_pid_specify
310
+ )
256
311
 
257
- self.connected_method = 'new'
312
+ self.connected_method = "new"
258
313
 
259
314
  def _connect_existing_femtet(self, pid: int or None = None):
260
- logger.info('└ Try to connect existing Femtet process.')
315
+ logger.info("└ Try to connect existing Femtet process.")
261
316
  # 既存の Femtet を探して Dispatch する。
262
317
  if pid is None:
263
- self.Femtet, self.femtet_pid = dispatch_femtet(timeout=5)
318
+ self.Femtet, self.femtet_pid = dispatch_femtet(timeout=self._femtet_connection_timeout)
264
319
  else:
265
- self.Femtet, self.femtet_pid = dispatch_specific_femtet(pid, timeout=5)
266
- self.connected_method = 'existing'
320
+ self.Femtet, self.femtet_pid = dispatch_specific_femtet(pid, timeout=self._femtet_connection_timeout)
321
+ self.connected_method = "existing"
267
322
 
268
- def connect_femtet(self, connect_method: str = 'auto', pid: int or None = None):
323
+ def connect_femtet(self, connect_method: str = "auto", pid: int or None = None):
269
324
  """Connects to a Femtet process.
270
325
 
271
326
  Args:
272
- connect_method (str, optional): The connection method. Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
327
+ connect_method (str, optional): The connection method.
328
+ Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
273
329
  pid (int or None, optional): The process ID of an existing Femtet process and wanted to connect.
274
330
 
275
331
  Note:
@@ -289,33 +345,136 @@ class FemtetInterface(FEMInterface):
289
345
 
290
346
  """
291
347
 
292
- if connect_method == 'new':
348
+ if connect_method == "new":
293
349
  self._connect_new_femtet()
294
350
 
295
- elif connect_method == 'existing':
351
+ elif connect_method == "existing":
296
352
  self._connect_existing_femtet(pid)
297
353
 
298
- elif connect_method == 'auto':
354
+ elif connect_method == "auto":
299
355
  try:
300
356
  self._connect_existing_femtet(pid)
301
357
  except DispatchExtensionException:
302
358
  self._connect_new_femtet()
303
359
 
304
360
  else:
305
- raise Exception(f'{connect_method} は定義されていない接続方法です')
361
+ raise Exception(f"{connect_method} は定義されていない接続方法です")
306
362
 
307
363
  # ensure makepy
308
- if not hasattr(constants, 'STATIC_C'):
309
- cmd = f'{sys.executable} -m win32com.client.makepy FemtetMacro'
364
+ if not hasattr(constants, "STATIC_C"):
365
+ cmd = f"{sys.executable} -m win32com.client.makepy FemtetMacro"
310
366
  subprocess.run(cmd, shell=True)
311
- message = Msg.ERR_NO_MAKEPY
312
- logger.error('================')
367
+
368
+ message = _(
369
+ en_message='It was detected that the configuration of '
370
+ 'Femtet python macro constants has not been '
371
+ 'completed. The configuration was done '
372
+ 'automatically '
373
+ '(python -m win32com.client.makepy FemtetMacro). '
374
+ 'Please restart the program. '
375
+ 'If the error persists, please run '
376
+ '"py -m win32com.client.makepy FemtetMacro" '
377
+ 'or "python -m win32com.client.makepy FemtetMacro" '
378
+ 'on the command prompt.',
379
+ jp_message='Femtet Pythonマクロ定数の設定が完了していない'
380
+ 'ことが検出されました。設定は自動で行われました'
381
+ '(py -m win32com.client.makepy FemtetMacro)。 '
382
+ 'プログラムを再起動してください。'
383
+ 'エラーが解消されない場合は、'
384
+ '"py -m win32com.client.makepy FemtetMacro" か '
385
+ '"python -m win32com.client.makepy FemtetMacro" '
386
+ 'コマンドをコマンドプロンプトで実行してください。'
387
+ )
388
+
389
+ logger.error("================")
313
390
  logger.error(message)
314
- logger.error('================')
391
+ logger.error("================")
315
392
  raise RuntimeError(message)
316
393
 
317
394
  if self.Femtet is None:
318
- raise RuntimeError(Msg.ERR_FEMTET_CONNECTION_FAILED)
395
+ raise RuntimeError(_(
396
+ en_message='Failed to connect to Femtet.',
397
+ jp_message='Femtet への接続に失敗しました。',
398
+ ))
399
+
400
+ def open(self, femprj_path: str, model_name: str or None = None) -> None:
401
+ """Open specific analysis model with connected Femtet."""
402
+
403
+ # 引数の処理
404
+ self.femprj_path = os.path.abspath(femprj_path)
405
+ self.model_name = model_name
406
+ # 開く
407
+ if self.model_name is None:
408
+ result = self.Femtet.LoadProject(self.femprj_path, True)
409
+ else:
410
+ result = self.Femtet.LoadProjectAndAnalysisModel(
411
+ self.femprj_path, self.model_name, True
412
+ )
413
+ if not result:
414
+ self.Femtet.ShowLastError()
415
+
416
+ def _connect_and_open_femtet(self):
417
+ """Connects to a Femtet process and open the femprj.
418
+
419
+ This function is for establishing a connection with Femtet and opening the specified femprj file.
420
+
421
+ At the beginning of the function, we check if femprj_path is specified.
422
+
423
+ If femprj_path is specified, first connect to Femtet using the specified connection method. Then, if the project path of the connected Femtet is different from the specified femprj_path, open the project using the open function. Also, if model_name is specified, the project will be opened using the open function even if the Femtet analysis model name is different from the specified model_name.
424
+
425
+ On the other hand, if femprj_path is not specified, an error message will be displayed stating that femprj_path must be specified in order to use the "new" connection method. If the connection method is not "new", it will try to connect to an existing Femtet instance. If the connection is successful, the project path and analysis model name of the Femtet instance will be stored as femprj_path and model_name.
426
+
427
+ """
428
+
429
+ logger.info(f'Try to connect Femtet (method: "{self.connect_method}").')
430
+ logger.info(
431
+ f'│ femprj: {self.femprj_path if self.femprj_path is not None else "not specified."}'
432
+ )
433
+ logger.info(
434
+ f'│ model: {self.model_name if self.femprj_path is not None else "not specified."}'
435
+ )
436
+
437
+ # femprj が指定されている
438
+ if self.femprj_path is not None:
439
+ # 指定された方法で接続してみて
440
+ # 接続した Femtet と指定された femprj 及び model が異なれば
441
+ # 接続した Femtet で指定された femprj 及び model を開く
442
+ self.connect_femtet(self.connect_method)
443
+
444
+ # プロジェクトの相違をチェック
445
+ if self.Femtet.ProjectPath != self.femprj_path:
446
+ self.open(self.femprj_path, self.model_name)
447
+
448
+ # モデルが指定されていればその相違もチェック
449
+ if self.model_name is not None:
450
+ if self.Femtet.AnalysisModelName != self.model_name:
451
+ self.open(self.femprj_path, self.model_name)
452
+
453
+ # femprj が指定されていない
454
+ else:
455
+ # かつ new だと解析すべき femprj がわからないのでエラー
456
+ if (self.connect_method == "new") and (not self.allow_without_project):
457
+ raise RuntimeError(Msg.ERR_NEW_FEMTET_BUT_NO_FEMPRJ)
458
+
459
+ # さらに auto の場合は Femtet が存在しなければ new と同じ挙動になるので同様の処理
460
+ if (
461
+ (self.connect_method == "auto")
462
+ and (len(_get_pids(process_name="Femtet.exe")) == 0)
463
+ and (not self.allow_without_project)
464
+ ):
465
+ raise RuntimeError(Msg.ERR_NEW_FEMTET_BUT_NO_FEMPRJ)
466
+ self.connect_femtet(self.connect_method)
467
+
468
+ # 最終的に接続した Femtet の femprj_path と model を インスタンスに戻す
469
+ self.femprj_path = self.Femtet.Project
470
+ self.model_name = self.Femtet.AnalysisModelName
471
+
472
+ # femprj が指定されていなければこの時点でのパスをオリジナルとする
473
+ if self._original_femprj_path is None:
474
+ assert get_worker() is None
475
+ self._original_femprj_path = self.femprj_path
476
+
477
+ # ===== call femtet API =====
319
478
 
320
479
  def _check_gaudi_accessible(self) -> bool:
321
480
  try:
@@ -326,12 +485,12 @@ class FemtetInterface(FEMInterface):
326
485
  return True
327
486
 
328
487
  # noinspection PyMethodMayBeStatic
329
- def _construct_femtet_api(self, string): # static にしてはいけない
488
+ def _construct_femtet_api(self, string: str | callable): # static にしてはいけない
330
489
  if isinstance(string, str):
331
- if string.startswith('self.'):
490
+ if string.startswith("self."):
332
491
  return eval(string)
333
492
  else:
334
- return eval('self.' + string)
493
+ return eval("self." + string)
335
494
  else:
336
495
  return string # Callable
337
496
 
@@ -348,6 +507,74 @@ class FemtetInterface(FEMInterface):
348
507
  recourse_depth=0,
349
508
  print_indent=0,
350
509
  ):
510
+
511
+ context = None
512
+
513
+ # Solve, Mesh, ReExecute は時間計測しない
514
+ if callable(fun):
515
+ name = fun.__name__
516
+ if fun.__name__ == 'Solve':
517
+ context = nullcontext()
518
+ elif fun.__name__ == 'solve_via_parametric_dll':
519
+ context = nullcontext()
520
+
521
+ elif isinstance(fun, str):
522
+
523
+ if fun in ('self.Femtet.Gaudi.ReExecute', 'self.Femtet.Gaudi.Mesh'):
524
+ context = nullcontext()
525
+ name = fun.split('.')[-1]
526
+
527
+ else:
528
+ raise NotImplementedError
529
+
530
+ if context is None:
531
+ warning_time_sec = self.api_response_warning_time
532
+ context = time_counting(
533
+ name=name,
534
+ warning_time_sec=warning_time_sec,
535
+ warning_fun=lambda: logger.warning(
536
+ _(
537
+ '{name} does not finish in {warning_time_sec} seconds. '
538
+ 'If the optimization is hanging, the most reason is '
539
+ 'a dialog is opening in Femtet and it waits for your '
540
+ 'input. Please confirm there is no dialog in Femtet.',
541
+ '{name} の実行に {warning_time_sec} 秒以上かかっています。'
542
+ 'もし最適化がハングしているならば、考えられる理由として、'
543
+ 'Femtet で予期せずダイアログが開いてユーザーの入力待ちをしている場合があります。'
544
+ 'もし Femtet でダイアログが開いていれば、閉じてください。',
545
+ name=name,
546
+ warning_time_sec=warning_time_sec,
547
+ )
548
+ ),
549
+ )
550
+
551
+ with context:
552
+ return self._call_femtet_api_core(
553
+ fun,
554
+ return_value_if_failed,
555
+ if_error,
556
+ error_message,
557
+ is_Gaudi_method,
558
+ ret_for_check_idx,
559
+ args,
560
+ kwargs,
561
+ recourse_depth,
562
+ print_indent,
563
+ )
564
+
565
+ def _call_femtet_api_core(
566
+ self,
567
+ fun,
568
+ return_value_if_failed,
569
+ if_error,
570
+ error_message,
571
+ is_Gaudi_method=False,
572
+ ret_for_check_idx=None,
573
+ args=None,
574
+ kwargs=None,
575
+ recourse_depth=0,
576
+ print_indent=0,
577
+ ):
351
578
  """Internal method. Call Femtet API with error handling.
352
579
 
353
580
  Parameters
@@ -390,26 +617,33 @@ class FemtetInterface(FEMInterface):
390
617
 
391
618
  # 実行する API をデバッグ出力
392
619
  if isinstance(fun, str):
393
- logger.debug(' ' * print_indent + f'Femtet API:{fun}, args:{args}, kwargs:{kwargs}')
620
+ logger.debug(
621
+ " " * print_indent + f"Femtet API:{fun}, args:{args}, kwargs:{kwargs}"
622
+ )
394
623
  else:
395
- logger.debug(' ' * print_indent + f'Femtet API:{fun.__name__}, args:{args}, kwargs:{kwargs}')
624
+ logger.debug(
625
+ " " * print_indent
626
+ + f"Femtet API:{fun.__name__}, args:{args}, kwargs:{kwargs}"
627
+ )
396
628
 
397
629
  # Gaudi コマンドなら Gaudi.Activate する
398
- if is_Gaudi_method: # Optimizer は Gogh に触らないので全部にこれをつけてもいい気がする
630
+ if (
631
+ is_Gaudi_method
632
+ ): # Optimizer は Gogh に触らないので全部にこれをつけてもいい気がする
399
633
  try:
400
634
  # まず Gaudi にアクセスできるか
401
635
  gaudi_accessible = self._check_gaudi_accessible()
402
636
  if gaudi_accessible:
403
637
  # Gaudi にアクセスできるなら Gaudi を Activate する
404
- fun = self._construct_femtet_api(fun) # (str) -> Callable
405
- if fun.__name__ != 'Activate':
638
+ fun = self._construct_femtet_api(fun) # str | callable -> callable
639
+ if fun.__name__ != "Activate":
406
640
  # 再帰ループにならないように
407
641
  self._call_femtet_api(
408
642
  self.Femtet.Gaudi.Activate,
409
643
  False, # None 以外なら何でもいい
410
644
  Exception,
411
- 'Gaudi のオープンに失敗しました',
412
- print_indent=print_indent + 1
645
+ 'Failed to Femtet.Gaudi.Activate()',
646
+ print_indent=print_indent + 1,
413
647
  )
414
648
 
415
649
  else:
@@ -427,18 +661,25 @@ class FemtetInterface(FEMInterface):
427
661
 
428
662
  # gaudi_accessible なので関数が何であろうが安全にアクセスはできる
429
663
  if isinstance(fun, str):
430
- fun = self._construct_femtet_api(fun) # (str) -> Callable
664
+ fun = self._construct_femtet_api(fun) # str | callable -> callable
431
665
 
432
666
  # 解析結果を開いた状態で Gaudi.Activate して ReExecute する場合、ReExecute の前後にアクティブ化イベントが必要
433
667
  # さらに、プロジェクトツリーが開いていないとアクティブ化イベントも意味がないらしい。
434
- if fun.__name__ == 'ReExecute':
435
- if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
668
+ if fun.__name__ == "ReExecute":
669
+ if (
670
+ self.open_result_with_gui
671
+ or self.parametric_output_indexes_use_as_objective
672
+ ):
436
673
  _post_activate_message(self.Femtet.hWnd)
437
674
  # API を実行
438
675
  returns = fun(*args, **kwargs) # can raise pywintypes.error
439
- if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
676
+ if (
677
+ self.open_result_with_gui
678
+ or self.parametric_output_indexes_use_as_objective
679
+ ):
440
680
  _post_activate_message(self.Femtet.hWnd)
441
681
  else:
682
+ import sys
442
683
  returns = fun(*args, **kwargs)
443
684
 
444
685
  # API の実行に失敗
@@ -449,7 +690,7 @@ class FemtetInterface(FEMInterface):
449
690
  returns = return_value_if_failed
450
691
  else:
451
692
  returns = [return_value_if_failed] * (ret_for_check_idx + 1)
452
- logger.debug(' ' * print_indent + f'Femtet API result:{returns}')
693
+ logger.debug(" " * print_indent + f"Femtet API result:{returns}")
453
694
 
454
695
  # チェックすべき値の抽出
455
696
  if ret_for_check_idx is None:
@@ -468,25 +709,37 @@ class FemtetInterface(FEMInterface):
468
709
  if self.femtet_is_alive():
469
710
  # 生きていてもここにきているなら
470
711
  # 指定された Exception を送出する
471
- logger.debug(' ' * print_indent + error_message)
712
+ logger.debug(" " * print_indent + error_message)
472
713
  raise if_error(error_message)
473
714
 
474
715
  # 死んでいるなら再起動
475
716
  else:
476
717
  # 再起動試行回数の上限に達していたら諦める
477
- logger.debug(' ' * print_indent + f'現在の Femtet 再起動回数: {recourse_depth}')
718
+ logger.debug(
719
+ " " * print_indent + f"現在の Femtet 再起動回数: {recourse_depth}"
720
+ )
478
721
  if recourse_depth >= self.max_api_retry:
479
- raise Exception(Msg.ERR_FEMTET_CRASHED_AND_RESTART_FAILED)
722
+ raise Exception(
723
+ Msg.F_ERR_FEMTET_CRASHED_AND_RESTART_FAILED(
724
+ fun.__name__
725
+ )
726
+ )
480
727
 
481
728
  # 再起動
482
- logger.warn(' ' * print_indent + Msg.WARN_FEMTET_CRASHED_AND_TRY_RESTART)
729
+ logger.warning(
730
+ " " * print_indent + Msg.F_WARN_FEMTET_CRASHED_AND_TRY_RESTART(
731
+ fun.__name__
732
+ )
733
+ )
483
734
  CoInitialize()
484
- self.connect_femtet(connect_method='new')
735
+ self.connect_femtet(connect_method="new")
485
736
  self.open(self.femprj_path, self.model_name)
486
737
 
487
738
  # 状態を復元するために一度変数を渡して解析を行う(fun.__name__がSolveなら2度手間だが)
488
- logger.info(' ' * print_indent + Msg.INFO_FEMTET_CRASHED_AND_RESTARTED)
489
- self.update(self.parameters)
739
+ logger.info(" " * print_indent + Msg.INFO_FEMTET_CRASHED_AND_RESTARTED)
740
+
741
+ self.update_parameter(self.current_prm_values)
742
+ self.update()
490
743
 
491
744
  # 与えられた API の再帰的再試行
492
745
  return self._call_femtet_api(
@@ -499,90 +752,27 @@ class FemtetInterface(FEMInterface):
499
752
  args,
500
753
  kwargs,
501
754
  recourse_depth + 1,
502
- print_indent + 1
755
+ print_indent + 1,
503
756
  )
504
757
 
505
758
  def femtet_is_alive(self) -> bool:
506
759
  """Returns connected femtet process is existing or not."""
507
- return _get_pid(self.Femtet.hWnd) > 0 # hWnd の値はすでに Femtet が終了している場合は 0
508
-
509
- def open(self, femprj_path: str, model_name: str or None = None) -> None:
510
- """Open specific analysis model with connected Femtet."""
511
-
512
- # 引数の処理
513
- self.femprj_path = os.path.abspath(femprj_path)
514
- self.model_name = model_name
515
- # 開く
516
- if self.model_name is None:
517
- result = self.Femtet.LoadProject(
518
- self.femprj_path,
519
- True
520
- )
521
- else:
522
- result = self.Femtet.LoadProjectAndAnalysisModel(
523
- self.femprj_path,
524
- self.model_name,
525
- True
526
- )
527
- if not result:
528
- self.Femtet.ShowLastError()
529
-
530
- def _connect_and_open_femtet(self):
531
- """Connects to a Femtet process and open the femprj.
532
-
533
- This function is for establishing a connection with Femtet and opening the specified femprj file.
534
760
 
535
- At the beginning of the function, we check if femprj_path is specified.
536
-
537
- If femprj_path is specified, first connect to Femtet using the specified connection method. Then, if the project path of the connected Femtet is different from the specified femprj_path, open the project using the open function. Also, if model_name is specified, the project will be opened using the open function even if the Femtet analysis model name is different from the specified model_name.
538
-
539
- On the other hand, if femprj_path is not specified, an error message will be displayed stating that femprj_path must be specified in order to use the "new" connection method. If the connection method is not "new", it will try to connect to an existing Femtet instance. If the connection is successful, the project path and analysis model name of the Femtet instance will be stored as femprj_path and model_name.
540
-
541
- """
542
-
543
- logger.info(f'Try to connect Femtet (method: "{self.connect_method}").')
544
- logger.info(f'│ femprj: {self.femprj_path if self.femprj_path is not None else "not specified."}')
545
- logger.info(f'│ model: {self.model_name if self.femprj_path is not None else "not specified."}')
761
+ try:
762
+ hwnd = self.Femtet.hWnd
763
+ except (com_error, AttributeError):
764
+ return False
546
765
 
547
- # femprj が指定されている
548
- if self.femprj_path is not None:
549
- # 指定された方法で接続してみて
550
- # 接続した Femtet と指定された femprj 及び model が異なれば
551
- # 接続した Femtet で指定された femprj 及び model を開く
552
- self.connect_femtet(self.connect_method)
766
+ if hwnd == 0:
767
+ return False
553
768
 
554
- # プロジェクトの相違をチェック
555
- if self.Femtet.ProjectPath != self.femprj_path:
556
- self.open(self.femprj_path, self.model_name)
769
+ pid = _get_pid(hwnd)
557
770
 
558
- # モデルが指定されていればその相違もチェック
559
- if self.model_name is not None:
560
- if self.Femtet.AnalysisModelName != self.model_name:
561
- self.open(self.femprj_path, self.model_name)
771
+ return pid > 0
562
772
 
563
- # femprj が指定されていない
564
- else:
565
- # かつ new だと解析すべき femprj がわからないのでエラー
566
- if (
567
- (self.connect_method == 'new')
568
- and (not self.allow_without_project)
569
- ):
570
- raise RuntimeError(Msg.ERR_NEW_FEMTET_BUT_NO_FEMPRJ)
773
+ # ===== model check and solve =====
571
774
 
572
- # さらに auto の場合は Femtet が存在しなければ new と同じ挙動になるので同様の処理
573
- if (
574
- (self.connect_method == 'auto')
575
- and (len(_get_pids(process_name='Femtet.exe')) == 0)
576
- and (not self.allow_without_project)
577
- ):
578
- raise RuntimeError(Msg.ERR_NEW_FEMTET_BUT_NO_FEMPRJ)
579
- self.connect_femtet(self.connect_method)
580
-
581
- # 最終的に接続した Femtet の femprj_path と model を インスタンスに戻す
582
- self.femprj_path = self.Femtet.Project
583
- self.model_name = self.Femtet.AnalysisModelName
584
-
585
- def check_param_value(self, param_name):
775
+ def _check_param_and_raise(self, param_name) -> None:
586
776
  """Check param_name is set in femprj file or not.
587
777
 
588
778
  Note:
@@ -595,45 +785,53 @@ class FemtetInterface(FEMInterface):
595
785
  try:
596
786
  variable_names = self.Femtet.GetVariableNames_py()
597
787
  except AttributeError as e:
598
- logger.error('================')
599
- logger.error(Msg.ERR_CANNOT_ACCESS_API + 'GetVariableNames_py')
788
+ logger.error("================")
789
+ logger.error(Msg.ERR_CANNOT_ACCESS_API + "GetVariableNames_py")
600
790
  logger.error(Msg.CERTIFY_MACRO_VERSION)
601
- logger.error('================')
791
+ logger.error("================")
602
792
  raise e
603
-
793
+
604
794
  if variable_names is not None:
605
795
  if param_name in variable_names:
606
796
  return self.Femtet.GetVariableValue(param_name)
607
797
  message = Msg.ERR_NO_SUCH_PARAMETER_IN_FEMTET
608
- logger.error('================')
798
+ logger.error("================")
609
799
  logger.error(message)
610
- logger.error(f'`{param_name}` not in {variable_names}')
611
- logger.error('================')
800
+ logger.error(f"`{param_name}` not in {variable_names}")
801
+ logger.error("================")
612
802
  raise RuntimeError(message)
613
803
  else:
614
804
  return None
615
805
 
616
- def update_parameter(self, parameters: 'pd.DataFrame', with_warning=False):
806
+ def update_parameter(self, x: dict[str, SupportedVariableTypes], with_warning=False) -> None | list[str]:
617
807
  """Update parameter of femprj."""
618
- self.parameters = parameters.copy()
808
+ COMInterface.update_parameter(self, x)
619
809
 
620
- # 変数更新のための処理
810
+ # Gaudi.Activate()
621
811
  sleep(0.1) # Gaudi がおかしくなる時がある対策
622
812
  self._call_femtet_api(
623
- 'self.Femtet.Gaudi.Activate',
813
+ "self.Femtet.Gaudi.Activate",
624
814
  True, # 戻り値を持たないのでここは無意味で None 以外なら何でもいい
625
815
  Exception, # 生きてるのに開けない場合
626
816
  error_message=Msg.NO_ANALYSIS_MODEL_IS_OPEN,
627
817
  )
628
818
 
819
+ # Version check
629
820
  major, minor, bugfix = 2023, 1, 1
821
+
822
+ # 変数一覧を取得する関数がある
630
823
  if self._version() >= _version(major, minor, bugfix):
631
- # Femtet の設計変数の更新(2023.1.1 以降でマクロだけ古い場合はcheck_param_valueで引っかかっているはずなのでここはAttributeError をチェックしない)
824
+
825
+ # Femtet で定義されている変数の取得
826
+ # (2023.1.1 以降でマクロだけ古い場合は
827
+ # check_param_value で引っかかっている
828
+ # はずなのでここは AttributeError を
829
+ # チェックしない)
632
830
  existing_variable_names = self._call_femtet_api(
633
831
  fun=self.Femtet.GetVariableNames_py,
634
832
  return_value_if_failed=False, # 意味がない
635
833
  if_error=ModelError, # 生きてるのに失敗した場合
636
- error_message=f'GetVariableNames_py failed.',
834
+ error_message=f"GetVariableNames_py failed.",
637
835
  is_Gaudi_method=True,
638
836
  )
639
837
 
@@ -644,57 +842,62 @@ class FemtetInterface(FEMInterface):
644
842
  else:
645
843
  return None
646
844
 
647
- # update
648
- warnings = []
649
- for i, row in parameters.iterrows():
650
- name = row['name']
651
- value = row['value']
845
+ # 変数を更新
846
+ warning_messages = []
847
+ for name, value in self.current_prm_values.items():
848
+
849
+ # 渡された変数がちゃんと Femtet に定義されている
652
850
  if name in existing_variable_names:
851
+
852
+ # Femtet.UpdateVariable
653
853
  self._call_femtet_api(
654
854
  fun=self.Femtet.UpdateVariable,
655
855
  return_value_if_failed=False,
656
856
  if_error=ModelError, # 生きてるのに失敗した場合
657
- error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE + f'{value} -> {name}',
857
+ error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE
858
+ + f"{value} -> {name}",
658
859
  is_Gaudi_method=True,
659
860
  args=(name, value),
660
861
  )
862
+
863
+ # 渡された変数が Femtet で定義されていない
661
864
  else:
662
- msg = f'{name} not in {self.model_name}: ' + Msg.WARN_IGNORE_PARAMETER_NOT_CONTAINED
663
- warnings.append(msg)
664
- logger.warn(msg)
865
+ if self._warn_if_undefined_variable:
866
+ msg = (
867
+ f"{name} not in {self.model_name}: "
868
+ + Msg.WARN_IGNORE_PARAMETER_NOT_CONTAINED
869
+ )
870
+ warning_messages.append(msg)
871
+ logger.warning(msg)
665
872
 
873
+ # 変数一覧を取得する関数がない(チェックしない)
666
874
  else:
875
+
667
876
  # update without parameter check
668
- warnings = []
669
- for i, row in parameters.iterrows():
670
- name = row['name']
671
- value = row['value']
877
+ warning_messages = []
878
+ for name, value in self.current_prm_values.items():
672
879
  self._call_femtet_api(
673
880
  fun=self.Femtet.UpdateVariable,
674
881
  return_value_if_failed=False,
675
882
  if_error=ModelError, # 生きてるのに失敗した場合
676
- error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE + f'{value} -> {name}',
883
+ error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE
884
+ + f"{value} -> {name}",
677
885
  is_Gaudi_method=True,
678
886
  args=(name, value),
679
887
  )
680
888
 
681
889
  # ここでは ReExecute しない
682
890
  if with_warning:
683
- return warnings
891
+ return warning_messages
684
892
  else:
685
893
  return None
686
894
 
687
- def update_model(self, parameters: 'pd.DataFrame', with_warning=False) -> Optional[List[str]]:
895
+ def update_model(self) -> None:
688
896
  """Updates the analysis model only."""
689
897
 
690
- self.parameters = parameters.copy()
691
-
692
- # 変数の更新
693
- warnings = self.update_parameter(parameters, with_warning)
694
-
695
898
  # 設計変数に従ってモデルを再構築
696
899
  self._call_femtet_api(
697
- 'self.Femtet.Gaudi.ReExecute',
900
+ "self.Femtet.Gaudi.ReExecute",
698
901
  False,
699
902
  ModelError, # 生きてるのに失敗した場合
700
903
  error_message=Msg.ERR_RE_EXECUTE_MODEL_FAILED,
@@ -710,14 +913,12 @@ class FemtetInterface(FEMInterface):
710
913
  is_Gaudi_method=True,
711
914
  )
712
915
 
713
- if with_warning:
714
- return warnings or []
715
-
716
916
  def solve(self) -> None:
717
917
  """Execute FEM analysis."""
718
- # # メッシュを切る
918
+
919
+ # メッシュを切る
719
920
  self._call_femtet_api(
720
- 'self.Femtet.Gaudi.Mesh',
921
+ "self.Femtet.Gaudi.Mesh",
721
922
  0,
722
923
  MeshError,
723
924
  Msg.ERR_MODEL_MESH_FAILED,
@@ -725,9 +926,9 @@ class FemtetInterface(FEMInterface):
725
926
  )
726
927
 
727
928
  if self.parametric_output_indexes_use_as_objective is not None:
728
- from pyfemtet.opt.interface._femtet_parametric import solve_via_parametric_dll
729
929
 
730
- pdt_path = self.Femtet.ResultFilePath + '.pdt'
930
+ # PyFemtet で保存させる pdt パスを決定する
931
+ pdt_path = self.Femtet.ResultFilePath + ".pdt"
731
932
 
732
933
  # 前のものが残っているとややこしいので消しておく
733
934
  if os.path.exists(pdt_path):
@@ -767,7 +968,8 @@ class FemtetInterface(FEMInterface):
767
968
  is_Gaudi_method=True,
768
969
  )
769
970
 
770
- # 次に呼ばれるはずのユーザー定義コスト関数の記述を簡単にするため先に解析結果を開いておく
971
+ # 次に呼ばれるはずのユーザー定義コスト関数の
972
+ # 記述を簡単にするため先に解析結果を開いておく
771
973
  self._call_femtet_api(
772
974
  self.Femtet.OpenCurrentResult,
773
975
  False,
@@ -777,14 +979,6 @@ class FemtetInterface(FEMInterface):
777
979
  args=(self.open_result_with_gui,),
778
980
  )
779
981
 
780
- def update(self, parameters: 'pd.DataFrame') -> None:
781
- """See :func:`FEMInterface.update`"""
782
- self.parameters = parameters.copy()
783
- self.update_model(parameters)
784
- self.preprocess(self.Femtet)
785
- self.solve()
786
- self.postprocess(self.Femtet)
787
-
788
982
  def preprocess(self, Femtet):
789
983
  """A method called just before :func:`solve`.
790
984
 
@@ -807,22 +1001,14 @@ class FemtetInterface(FEMInterface):
807
1001
  """
808
1002
  pass
809
1003
 
810
- def quit(self, timeout=1, force=True):
811
- """Force to terminate connected Femtet."""
812
-
813
- _set_autosave_enabled(self._original_autosave_enabled)
814
-
815
- if self.quit_when_destruct:
816
- _exit_or_force_terminate(timeout=timeout, Femtet=self.Femtet, force=True)
817
-
818
- def _setup_before_parallel(self, client):
819
- client.upload_file(
820
- self.kwargs['femprj_path'],
821
- False
822
- )
1004
+ def update(self) -> None:
1005
+ """See :func:`FEMInterface.update`"""
1006
+ self.update_model()
1007
+ self.preprocess(self.Femtet)
1008
+ self.solve()
1009
+ self.postprocess(self.Femtet)
823
1010
 
824
- def _version(self):
825
- return _version(Femtet=self.Femtet)
1011
+ # ===== postprocess after recording =====
826
1012
 
827
1013
  def _create_postprocess_args(self):
828
1014
  try:
@@ -836,7 +1022,7 @@ class FemtetInterface(FEMInterface):
836
1022
  jpg_content = None
837
1023
 
838
1024
  out = dict(
839
- original_femprj_path=self.original_femprj_path,
1025
+ original_femprj_path=self._original_femprj_path,
840
1026
  model_name=self.model_name,
841
1027
  pdt_file_content=file_content,
842
1028
  jpg_file_content=jpg_content,
@@ -845,55 +1031,60 @@ class FemtetInterface(FEMInterface):
845
1031
  return out
846
1032
 
847
1033
  @staticmethod
848
- def _create_pdt_path(femprj_path, model_name, trial):
849
- result_dir = femprj_path.replace('.femprj', '.Results')
850
- pdt_path = os.path.join(result_dir, model_name + f'_trial{trial}.pdt')
1034
+ def _create_path(femprj_path, model_name, trial_name, ext):
1035
+ result_dir = femprj_path.replace(".femprj", ".Results")
1036
+ ext = ext.removeprefix('.')
1037
+ pdt_path = os.path.join(result_dir, model_name + f"_{trial_name}.{ext}")
851
1038
  return pdt_path
852
1039
 
853
1040
  # noinspection PyMethodOverriding
854
1041
  @staticmethod
855
- def _postprocess_func(
856
- trial: int,
857
- df: pd.DataFrame,
858
- original_femprj_path: str,
859
- save_results: str,
860
- model_name: str,
861
- pdt_file_content=None,
862
- jpg_file_content=None,
863
- dask_scheduler=None
1042
+ def _postprocess_after_recording(
1043
+ dask_scheduler, # must for run_on_scheduler
1044
+ trial_name: str,
1045
+ df: pd.DataFrame,
1046
+ *,
1047
+ original_femprj_path: str,
1048
+ save_results: str,
1049
+ model_name: str,
1050
+ pdt_file_content=None,
1051
+ jpg_file_content=None,
864
1052
  ):
1053
+ # FIXME: サブサンプリングの場合の処理
865
1054
 
866
1055
  # none なら何もしない
1056
+ # all or optimal ならいったん保存する
867
1057
  if save_results.lower() == 'none':
868
1058
  return
869
1059
 
870
- # all or optimal ならいったん保存する
871
- result_dir = original_femprj_path.replace('.femprj', '.Results')
872
1060
  if pdt_file_content is not None:
873
- pdt_path = FemtetInterface._create_pdt_path(original_femprj_path, model_name, trial)
874
- with open(pdt_path, 'wb') as f:
1061
+ pdt_path = FemtetInterface._create_path(
1062
+ original_femprj_path, model_name, trial_name, ext='pdt')
1063
+ with open(pdt_path, "wb") as f:
875
1064
  f.write(pdt_file_content)
876
1065
 
877
1066
  if jpg_file_content is not None:
878
- jpg_path = os.path.join(result_dir, model_name + f'_trial{trial}.jpg')
879
- with open(jpg_path, 'wb') as f:
1067
+ jpg_path = FemtetInterface._create_path(
1068
+ original_femprj_path, model_name, trial_name, ext='jpg')
1069
+ with open(jpg_path, "wb") as f:
880
1070
  f.write(jpg_file_content)
881
1071
 
882
1072
  # optimal なら不要ファイルの削除を実行する
883
1073
  if save_results.lower() == 'optimal':
884
1074
  for i, row in df.iterrows():
885
- if not bool(row['non_domi']):
1075
+ if not bool(row['optimality']):
886
1076
  trial_to_remove = int(row['trial'])
887
- pdt_path_to_remove = FemtetInterface._create_pdt_path(original_femprj_path, model_name, trial_to_remove)
1077
+ pdt_path_to_remove = FemtetInterface._create_path(
1078
+ original_femprj_path, model_name, trial_to_remove, ext='pdt')
888
1079
  if os.path.isfile(pdt_path_to_remove):
889
1080
  os.remove(pdt_path_to_remove)
890
1081
 
891
1082
  def _create_result_file_content(self):
892
1083
  """Called after solve"""
893
- if self.save_pdt == 'all':
1084
+ if self.save_pdt.lower() in ['all', 'optimal']:
894
1085
  # save to worker space
895
- result_dir = self.femprj_path.replace('.femprj', '.Results')
896
- pdt_path = os.path.join(result_dir, self.model_name + '.pdt')
1086
+ result_dir = self.femprj_path.replace(".femprj", ".Results")
1087
+ pdt_path = os.path.join(result_dir, self.model_name + ".pdt")
897
1088
 
898
1089
  self._call_femtet_api(
899
1090
  fun=self.Femtet.SavePDT,
@@ -905,7 +1096,7 @@ class FemtetInterface(FEMInterface):
905
1096
  )
906
1097
 
907
1098
  # convert .pdt to ByteIO and return it
908
- with open(pdt_path, 'rb') as f:
1099
+ with open(pdt_path, "rb") as f:
909
1100
  content = f.read()
910
1101
  return content
911
1102
 
@@ -913,8 +1104,8 @@ class FemtetInterface(FEMInterface):
913
1104
  return None
914
1105
 
915
1106
  def _create_jpg_content(self):
916
- result_dir = self.femprj_path.replace('.femprj', '.Results')
917
- jpg_path = os.path.join(result_dir, self.model_name + '.jpg')
1107
+ result_dir = self.femprj_path.replace(".femprj", ".Results")
1108
+ jpg_path = os.path.join(result_dir, self.model_name + ".jpg")
918
1109
 
919
1110
  # モデル表示画面の設定
920
1111
  self.Femtet.SetWindowSize(600, 600)
@@ -932,54 +1123,18 @@ class FemtetInterface(FEMInterface):
932
1123
  if not os.path.exists(jpg_path):
933
1124
  raise FailedToPostProcess(Msg.ERR_JPG_NOT_FOUND)
934
1125
 
935
- with open(jpg_path, 'rb') as f:
1126
+ with open(jpg_path, "rb") as f:
936
1127
  content = f.read()
937
1128
 
938
1129
  return content
939
1130
 
1131
+ # ===== others =====
940
1132
 
941
- from win32com.client import Dispatch, constants
942
-
943
-
944
- class _UnPicklableNoFEM(FemtetInterface):
945
-
946
-
947
- original_femprj_path = 'dummy'
948
- model_name = 'dummy'
949
- parametric_output_indexes_use_as_objective = None
950
- kwargs = dict()
951
- Femtet = None
952
- quit_when_destruct = False
953
-
954
- # noinspection PyMissingConstructor
955
- def __init__(self):
956
- CoInitialize()
957
- self.unpicklable_member = Dispatch('FemtetMacro.Femtet')
958
- self.cns = constants
959
-
960
- def _setup_before_parallel(self, *args, **kwargs):
961
- pass
962
-
963
- def check_param_value(self, *args, **kwargs):
964
- pass
965
-
966
- def update_parameter(self, *args, **kwargs):
967
- pass
968
-
969
- def update(self, *args, **kwargs):
970
- pass
971
-
972
- def create_result_file_content(self):
973
- """Called after solve"""
974
-
975
- # save to worker space
976
- with open(__file__, 'rb') as f:
977
- content = f.read()
978
-
979
- return content
1133
+ def _get_additional_data(self) -> dict:
1134
+ return dict(
1135
+ femprj_path=self._original_femprj_path,
1136
+ model_name=self.model_name,
1137
+ )
980
1138
 
981
- def create_file_path(self, trial: int):
982
- # return path of scheduler environment
983
- here = os.path.dirname(__file__)
984
- pdt_path = os.path.join(here, f'trial{trial}.pdt')
985
- return pdt_path
1139
+ def _version(self):
1140
+ return _version(Femtet=self.Femtet)