pyfemtet 0.9.5__py3-none-any.whl → 1.0.0b0__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 +29 -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 +94 -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 +608 -0
  31. pyfemtet/opt/history/__init__.py +11 -0
  32. pyfemtet/opt/history/_history.py +1404 -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 +999 -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} +505 -349
  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 +122 -0
  50. pyfemtet/opt/interface/_solidworks_interface/__init__.py +5 -0
  51. pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py +206 -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 +885 -0
  62. pyfemtet/opt/optimizer/optuna_optimizer/__init__.py +9 -0
  63. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py +73 -0
  64. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py +678 -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 +364 -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.5.dist-info → pyfemtet-1.0.0b0.dist-info}/METADATA +23 -24
  151. pyfemtet-1.0.0b0.dist-info/RECORD +172 -0
  152. pyfemtet-1.0.0b0.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.5.dist-info/RECORD +0 -158
  259. pyfemtet-0.9.5.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.5.dist-info → pyfemtet-1.0.0b0.dist-info}/LICENSE +0 -0
  271. {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/LICENSE_THIRD_PARTY.txt +0 -0
  272. {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.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:
@@ -125,24 +117,31 @@ class FemtetInterface(FEMInterface):
125
117
 
126
118
  """
127
119
 
120
+ com_members = {'Femtet': 'FemtetMacro.Femtet'}
121
+ _show_parametric_index_warning = True # for GUI
122
+
128
123
  def __init__(
129
124
  self,
130
125
  femprj_path: str = None,
131
126
  model_name: str = None,
132
- connect_method: str = 'auto', # dask worker では __init__ の中で 'new' にするので super() の引数にしない。(しても意味がない)
133
- save_pdt: str = 'all', # 'all', 'none' or 'optimal'
127
+ connect_method: str = "auto", # dask worker では __init__ の中で 'new' にする
128
+ save_pdt: str = "all", # 'all', 'none' or 'optimal'
134
129
  strictly_pid_specify: bool = True, # dask worker では True にしたいので super() の引数にしない。
135
130
  allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
136
131
  open_result_with_gui: bool = True,
137
- parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
138
- **kwargs # 継承されたクラスからの引数
132
+ always_open_copy=False,
133
+ # ユーザーはメソッドを使うことを推奨。GUI などで使用。
134
+ parametric_output_indexes_use_as_objective: dict[int, str | float] = None,
139
135
  ):
140
- # warning
141
136
  if parametric_output_indexes_use_as_objective is not None:
142
- warnings.warn('解析モデルに設定された既存のスイープテーブルは削除されます。')
143
-
144
- # win32com の初期化
145
- CoInitialize()
137
+ if FemtetInterface._show_parametric_index_warning:
138
+ logger.warning(_(
139
+ en_message='The argument `parametric_output_indexes_use_as_objective` is deprecated. '
140
+ 'Please use `FemtetInterface.use_parametric_output_as_objective()` instead.',
141
+ jp_message='`parametric_output_indexes_use_as_objective` は非推奨の引数です。'
142
+ '代わりに `FemtetInterface.use_parametric_output_as_objective()` '
143
+ 'を使ってください。',
144
+ ))
146
145
 
147
146
  # 引数の処理
148
147
  if femprj_path is None:
@@ -152,86 +151,135 @@ class FemtetInterface(FEMInterface):
152
151
  self.model_name = model_name
153
152
  self.connect_method = connect_method
154
153
  self.allow_without_project = allow_without_project
155
- self.original_femprj_path = self.femprj_path
154
+ self._original_femprj_path = self.femprj_path
156
155
  self.open_result_with_gui = open_result_with_gui
157
156
  self.save_pdt = save_pdt
157
+ self._always_open_copy = always_open_copy
158
158
 
159
159
  # その他のメンバーの宣言や初期化
160
160
  self.Femtet = None
161
161
  self.femtet_pid = 0
162
162
  self.quit_when_destruct = False
163
- self.connected_method = 'unconnected'
164
- self.parameters = None
163
+ self.connected_method = "unconnected"
165
164
  self.max_api_retry = 3
166
165
  self.strictly_pid_specify = strictly_pid_specify
167
166
  self.parametric_output_indexes_use_as_objective = parametric_output_indexes_use_as_objective
167
+ self._load_problem_from_fem = self.parametric_output_indexes_use_as_objective is not None
168
168
  self._original_autosave_enabled = _get_autosave_enabled()
169
169
  _set_autosave_enabled(False)
170
+ self._warn_if_undefined_variable = True
171
+ self.api_response_warning_time = 10 # mesh, solve, re-execute を除くマクロ実行警告時間
170
172
 
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 を更新する
173
+ # connect to Femtet
184
174
  self._connect_and_open_femtet()
175
+ assert self.connected_method != 'unconnected'
176
+
177
+ if self._always_open_copy:
178
+ # 現時点でFemtet に model を close する機能がない
179
+ # + executor でも CAD 連携等でモデルに
180
+ # わかりにくい変更が入らないように
181
+ # _tmp_dir のファイルを開くようにしたため
182
+ # _tmp_dir 削除時の permission error を
183
+ # 避けるために Femtet を強制 close する
184
+ # Femtet で model が close できるようになれば
185
+ # この条件分岐は不要になる
186
+ self.quit_when_destruct = True
185
187
 
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'
188
+ else:
189
+ # 接続した Femtet の種類に応じて del 時に quit するかどうか決める
190
+ self.quit_when_destruct = self.connected_method == "new"
197
191
 
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
- )
192
+ # ===== system =====
210
193
 
211
194
  @property
212
- def object_passed_to_functions(self):
195
+ def object_pass_to_fun(self):
196
+ """The object pass to the first argument of user-defined objective functions.
197
+
198
+ Returns:
199
+ Femtet (CDispatch): COM object of Femtet.
200
+ """
213
201
  return self.Femtet
214
202
 
215
- def use_parametric_output_as_objective(self, number: int, direction: str | float = 'minimize') -> None:
203
+ def _setup_before_parallel(self):
204
+ self._distribute_files([self.femprj_path])
205
+
206
+ def _setup_after_parallel(self, opt: AbstractOptimizer = None):
207
+
208
+ # main worker かつ always_open_copy でないときのみ
209
+ # femprj_path を切り替えない
210
+ if (get_worker() is None) and not self._always_open_copy:
211
+ pass
212
+
213
+ # worker space の femprj に切り替える
214
+ else:
215
+ suffix = self._get_worker_index_from_optimizer(opt)
216
+ self.femprj_path = self._rename_and_get_path_on_worker_space(self._original_femprj_path, suffix)
217
+
218
+ # dask process ならば Femtet を起動
219
+ worker = get_worker()
220
+ if worker is not None:
221
+ CoInitialize()
222
+ self.connect_femtet(connect_method='new')
223
+ self.quit_when_destruct = True
224
+
225
+ # femprj を開く
226
+ self.open(self.femprj_path, self.model_name)
227
+
228
+ def close(self, timeout=15, force=True): # 12 秒程度
229
+ """Force to terminate connected Femtet."""
230
+
231
+ _set_autosave_enabled(self._original_autosave_enabled)
232
+
233
+ if not hasattr(self, 'Femtet'):
234
+ return
235
+
236
+ if self.Femtet is None:
237
+ return
238
+
239
+ if self.quit_when_destruct:
240
+ logger.info(_('Closing Femtet (pid = {pid}) ...', pid=self.femtet_pid))
241
+ succeeded = _exit_or_force_terminate(
242
+ timeout=timeout, Femtet=self.Femtet, force=force)
243
+ if succeeded:
244
+ logger.info(_('Femtet is closed.'))
245
+ else:
246
+ logger.warning(_('Failed to close Femtet.'))
247
+
248
+ def use_parametric_output_as_objective(
249
+ self, number: int, direction: str | float = "minimize"
250
+ ) -> None:
216
251
  """Use output setting of Femtet parametric analysis as an objective function.
217
252
 
218
253
  Args:
219
254
  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'.
255
+ direction (str | float): Objective direction.
256
+ Valid input is one of 'minimize', 'maximize' or a specific value.
257
+ Defaults to 'minimize'.
221
258
 
222
259
  Returns:
223
260
  None
224
261
 
225
262
  """
263
+
264
+ # warning
265
+ logger.warning(_(
266
+ en_message='The existing sweep table in the project will be removed.',
267
+ jp_message='解析モデルに設定された既存のスイープテーブルは削除されます。'
268
+ ))
269
+
226
270
  # check
227
271
  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}')
272
+ if direction not in ("minimize", "maximize"):
273
+ raise ValueError(
274
+ f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}'
275
+ )
230
276
  else:
231
277
  try:
232
278
  direction = float(direction)
233
279
  except (TypeError, ValueError):
234
- raise ValueError(f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}')
280
+ raise ValueError(
281
+ f'direction must be one of "minimize", "maximize" or a specific value. Passed value is {direction}'
282
+ )
235
283
 
236
284
  index = {number - 1: direction}
237
285
 
@@ -241,35 +289,44 @@ class FemtetInterface(FEMInterface):
241
289
  else:
242
290
  self.parametric_output_indexes_use_as_objective.update(index)
243
291
 
244
- # TODO: FEMInterface.__init__ の仕様を変えたらここも変える
245
- self.kwargs['parametric_output_indexes_use_as_objective'] = self.parametric_output_indexes_use_as_objective
292
+ self._load_problem_from_fem = True
246
293
 
294
+ def load_objectives(self, opt: AbstractOptimizer):
295
+ indexes = list(self.parametric_output_indexes_use_as_objective.keys())
296
+ directions = list(self.parametric_output_indexes_use_as_objective.values())
297
+ add_parametric_results_as_objectives(
298
+ opt, self.Femtet, indexes, directions
299
+ )
300
+
301
+ def _check_using_fem(self, fun: callable):
302
+ return _is_access_femtet(fun)
247
303
 
248
- def __del__(self):
249
- self.quit()
250
- # CoUninitialize() # Win32 exception occurred releasing IUnknown at 0x0000022427692748
304
+ # ===== connect_femtet =====
251
305
 
252
306
  def _connect_new_femtet(self):
253
- logger.info('└ Try to launch and connect new Femtet process.')
307
+ logger.info("└ Try to launch and connect new Femtet process.")
254
308
 
255
- self.Femtet, self.femtet_pid = launch_and_dispatch_femtet(strictly_pid_specify=self.strictly_pid_specify)
309
+ self.Femtet, self.femtet_pid = launch_and_dispatch_femtet(
310
+ strictly_pid_specify=self.strictly_pid_specify
311
+ )
256
312
 
257
- self.connected_method = 'new'
313
+ self.connected_method = "new"
258
314
 
259
315
  def _connect_existing_femtet(self, pid: int or None = None):
260
- logger.info('└ Try to connect existing Femtet process.')
316
+ logger.info("└ Try to connect existing Femtet process.")
261
317
  # 既存の Femtet を探して Dispatch する。
262
318
  if pid is None:
263
319
  self.Femtet, self.femtet_pid = dispatch_femtet(timeout=5)
264
320
  else:
265
321
  self.Femtet, self.femtet_pid = dispatch_specific_femtet(pid, timeout=5)
266
- self.connected_method = 'existing'
322
+ self.connected_method = "existing"
267
323
 
268
- def connect_femtet(self, connect_method: str = 'auto', pid: int or None = None):
324
+ def connect_femtet(self, connect_method: str = "auto", pid: int or None = None):
269
325
  """Connects to a Femtet process.
270
326
 
271
327
  Args:
272
- connect_method (str, optional): The connection method. Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
328
+ connect_method (str, optional): The connection method.
329
+ Can be 'new', 'existing', or 'auto'. Defaults to 'auto'.
273
330
  pid (int or None, optional): The process ID of an existing Femtet process and wanted to connect.
274
331
 
275
332
  Note:
@@ -289,33 +346,136 @@ class FemtetInterface(FEMInterface):
289
346
 
290
347
  """
291
348
 
292
- if connect_method == 'new':
349
+ if connect_method == "new":
293
350
  self._connect_new_femtet()
294
351
 
295
- elif connect_method == 'existing':
352
+ elif connect_method == "existing":
296
353
  self._connect_existing_femtet(pid)
297
354
 
298
- elif connect_method == 'auto':
355
+ elif connect_method == "auto":
299
356
  try:
300
357
  self._connect_existing_femtet(pid)
301
358
  except DispatchExtensionException:
302
359
  self._connect_new_femtet()
303
360
 
304
361
  else:
305
- raise Exception(f'{connect_method} は定義されていない接続方法です')
362
+ raise Exception(f"{connect_method} は定義されていない接続方法です")
306
363
 
307
364
  # ensure makepy
308
- if not hasattr(constants, 'STATIC_C'):
309
- cmd = f'{sys.executable} -m win32com.client.makepy FemtetMacro'
365
+ if not hasattr(constants, "STATIC_C"):
366
+ cmd = f"{sys.executable} -m win32com.client.makepy FemtetMacro"
310
367
  subprocess.run(cmd, shell=True)
311
- message = Msg.ERR_NO_MAKEPY
312
- logger.error('================')
368
+
369
+ message = _(
370
+ en_message='It was detected that the configuration of '
371
+ 'Femtet python macro constants has not been '
372
+ 'completed. The configuration was done '
373
+ 'automatically '
374
+ '(python -m win32com.client.makepy FemtetMacro). '
375
+ 'Please restart the program. '
376
+ 'If the error persists, please run '
377
+ '"py -m win32com.client.makepy FemtetMacro" '
378
+ 'or "python -m win32com.client.makepy FemtetMacro" '
379
+ 'on the command prompt.',
380
+ jp_message='Femtet Pythonマクロ定数の設定が完了していない'
381
+ 'ことが検出されました。設定は自動で行われました'
382
+ '(py -m win32com.client.makepy FemtetMacro)。 '
383
+ 'プログラムを再起動してください。'
384
+ 'エラーが解消されない場合は、'
385
+ '"py -m win32com.client.makepy FemtetMacro" か '
386
+ '"python -m win32com.client.makepy FemtetMacro" '
387
+ 'コマンドをコマンドプロンプトで実行してください。'
388
+ )
389
+
390
+ logger.error("================")
313
391
  logger.error(message)
314
- logger.error('================')
392
+ logger.error("================")
315
393
  raise RuntimeError(message)
316
394
 
317
395
  if self.Femtet is None:
318
- raise RuntimeError(Msg.ERR_FEMTET_CONNECTION_FAILED)
396
+ raise RuntimeError(_(
397
+ en_message='Failed to connect to Femtet.',
398
+ jp_message='Femtet への接続に失敗しました。',
399
+ ))
400
+
401
+ def open(self, femprj_path: str, model_name: str or None = None) -> None:
402
+ """Open specific analysis model with connected Femtet."""
403
+
404
+ # 引数の処理
405
+ self.femprj_path = os.path.abspath(femprj_path)
406
+ self.model_name = model_name
407
+ # 開く
408
+ if self.model_name is None:
409
+ result = self.Femtet.LoadProject(self.femprj_path, True)
410
+ else:
411
+ result = self.Femtet.LoadProjectAndAnalysisModel(
412
+ self.femprj_path, self.model_name, True
413
+ )
414
+ if not result:
415
+ self.Femtet.ShowLastError()
416
+
417
+ def _connect_and_open_femtet(self):
418
+ """Connects to a Femtet process and open the femprj.
419
+
420
+ This function is for establishing a connection with Femtet and opening the specified femprj file.
421
+
422
+ At the beginning of the function, we check if femprj_path is specified.
423
+
424
+ 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.
425
+
426
+ 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.
427
+
428
+ """
429
+
430
+ logger.info(f'Try to connect Femtet (method: "{self.connect_method}").')
431
+ logger.info(
432
+ f'│ femprj: {self.femprj_path if self.femprj_path is not None else "not specified."}'
433
+ )
434
+ logger.info(
435
+ f'│ model: {self.model_name if self.femprj_path is not None else "not specified."}'
436
+ )
437
+
438
+ # femprj が指定されている
439
+ if self.femprj_path is not None:
440
+ # 指定された方法で接続してみて
441
+ # 接続した Femtet と指定された femprj 及び model が異なれば
442
+ # 接続した Femtet で指定された femprj 及び model を開く
443
+ self.connect_femtet(self.connect_method)
444
+
445
+ # プロジェクトの相違をチェック
446
+ if self.Femtet.ProjectPath != self.femprj_path:
447
+ self.open(self.femprj_path, self.model_name)
448
+
449
+ # モデルが指定されていればその相違もチェック
450
+ if self.model_name is not None:
451
+ if self.Femtet.AnalysisModelName != self.model_name:
452
+ self.open(self.femprj_path, self.model_name)
453
+
454
+ # femprj が指定されていない
455
+ else:
456
+ # かつ new だと解析すべき femprj がわからないのでエラー
457
+ if (self.connect_method == "new") and (not self.allow_without_project):
458
+ raise RuntimeError(Msg.ERR_NEW_FEMTET_BUT_NO_FEMPRJ)
459
+
460
+ # さらに auto の場合は Femtet が存在しなければ new と同じ挙動になるので同様の処理
461
+ if (
462
+ (self.connect_method == "auto")
463
+ and (len(_get_pids(process_name="Femtet.exe")) == 0)
464
+ and (not self.allow_without_project)
465
+ ):
466
+ raise RuntimeError(Msg.ERR_NEW_FEMTET_BUT_NO_FEMPRJ)
467
+ self.connect_femtet(self.connect_method)
468
+
469
+ # 最終的に接続した Femtet の femprj_path と model を インスタンスに戻す
470
+ self.femprj_path = self.Femtet.Project
471
+ self.model_name = self.Femtet.AnalysisModelName
472
+
473
+ # femprj が指定されていなければこの時点でのパスをオリジナルとする
474
+ if self._original_femprj_path is None:
475
+ assert get_worker() is None
476
+ self._original_femprj_path = self.femprj_path
477
+
478
+ # ===== call femtet API =====
319
479
 
320
480
  def _check_gaudi_accessible(self) -> bool:
321
481
  try:
@@ -326,12 +486,12 @@ class FemtetInterface(FEMInterface):
326
486
  return True
327
487
 
328
488
  # noinspection PyMethodMayBeStatic
329
- def _construct_femtet_api(self, string): # static にしてはいけない
489
+ def _construct_femtet_api(self, string: str | callable): # static にしてはいけない
330
490
  if isinstance(string, str):
331
- if string.startswith('self.'):
491
+ if string.startswith("self."):
332
492
  return eval(string)
333
493
  else:
334
- return eval('self.' + string)
494
+ return eval("self." + string)
335
495
  else:
336
496
  return string # Callable
337
497
 
@@ -348,6 +508,74 @@ class FemtetInterface(FEMInterface):
348
508
  recourse_depth=0,
349
509
  print_indent=0,
350
510
  ):
511
+
512
+ context = None
513
+
514
+ # Solve, Mesh, ReExecute は時間計測しない
515
+ if callable(fun):
516
+ name = fun.__name__
517
+ if fun.__name__ == 'Solve':
518
+ context = nullcontext()
519
+ elif fun.__name__ == 'solve_via_parametric_dll':
520
+ context = nullcontext()
521
+
522
+ elif isinstance(fun, str):
523
+
524
+ if fun in ('self.Femtet.Gaudi.ReExecute', 'self.Femtet.Gaudi.Mesh'):
525
+ context = nullcontext()
526
+ name = fun.split('.')[-1]
527
+
528
+ else:
529
+ raise NotImplementedError
530
+
531
+ if context is None:
532
+ warning_time_sec = self.api_response_warning_time
533
+ context = time_counting(
534
+ name=name,
535
+ warning_time_sec=warning_time_sec,
536
+ warning_fun=lambda: logger.warning(
537
+ _(
538
+ '{name} does not finish in {warning_time_sec} seconds. '
539
+ 'If the optimization is hanging, the most reason is '
540
+ 'a dialog is opening in Femtet and it waits for your '
541
+ 'input. Please confirm there is no dialog in Femtet.',
542
+ '{name} の実行に {warning_time_sec} 秒以上かかっています。'
543
+ 'もし最適化がハングしているならば、考えられる理由として、'
544
+ 'Femtet で予期せずダイアログが開いてユーザーの入力待ちをしている場合があります。'
545
+ 'もし Femtet でダイアログが開いていれば、閉じてください。',
546
+ name=name,
547
+ warning_time_sec=warning_time_sec,
548
+ )
549
+ ),
550
+ )
551
+
552
+ with context:
553
+ return self._call_femtet_api_core(
554
+ fun,
555
+ return_value_if_failed,
556
+ if_error,
557
+ error_message,
558
+ is_Gaudi_method,
559
+ ret_for_check_idx,
560
+ args,
561
+ kwargs,
562
+ recourse_depth,
563
+ print_indent,
564
+ )
565
+
566
+ def _call_femtet_api_core(
567
+ self,
568
+ fun,
569
+ return_value_if_failed,
570
+ if_error,
571
+ error_message,
572
+ is_Gaudi_method=False,
573
+ ret_for_check_idx=None,
574
+ args=None,
575
+ kwargs=None,
576
+ recourse_depth=0,
577
+ print_indent=0,
578
+ ):
351
579
  """Internal method. Call Femtet API with error handling.
352
580
 
353
581
  Parameters
@@ -390,26 +618,33 @@ class FemtetInterface(FEMInterface):
390
618
 
391
619
  # 実行する API をデバッグ出力
392
620
  if isinstance(fun, str):
393
- logger.debug(' ' * print_indent + f'Femtet API:{fun}, args:{args}, kwargs:{kwargs}')
621
+ logger.debug(
622
+ " " * print_indent + f"Femtet API:{fun}, args:{args}, kwargs:{kwargs}"
623
+ )
394
624
  else:
395
- logger.debug(' ' * print_indent + f'Femtet API:{fun.__name__}, args:{args}, kwargs:{kwargs}')
625
+ logger.debug(
626
+ " " * print_indent
627
+ + f"Femtet API:{fun.__name__}, args:{args}, kwargs:{kwargs}"
628
+ )
396
629
 
397
630
  # Gaudi コマンドなら Gaudi.Activate する
398
- if is_Gaudi_method: # Optimizer は Gogh に触らないので全部にこれをつけてもいい気がする
631
+ if (
632
+ is_Gaudi_method
633
+ ): # Optimizer は Gogh に触らないので全部にこれをつけてもいい気がする
399
634
  try:
400
635
  # まず Gaudi にアクセスできるか
401
636
  gaudi_accessible = self._check_gaudi_accessible()
402
637
  if gaudi_accessible:
403
638
  # Gaudi にアクセスできるなら Gaudi を Activate する
404
- fun = self._construct_femtet_api(fun) # (str) -> Callable
405
- if fun.__name__ != 'Activate':
639
+ fun = self._construct_femtet_api(fun) # str | callable -> callable
640
+ if fun.__name__ != "Activate":
406
641
  # 再帰ループにならないように
407
642
  self._call_femtet_api(
408
643
  self.Femtet.Gaudi.Activate,
409
644
  False, # None 以外なら何でもいい
410
645
  Exception,
411
- 'Gaudi のオープンに失敗しました',
412
- print_indent=print_indent + 1
646
+ 'Failed to Femtet.Gaudi.Activate()',
647
+ print_indent=print_indent + 1,
413
648
  )
414
649
 
415
650
  else:
@@ -427,18 +662,25 @@ class FemtetInterface(FEMInterface):
427
662
 
428
663
  # gaudi_accessible なので関数が何であろうが安全にアクセスはできる
429
664
  if isinstance(fun, str):
430
- fun = self._construct_femtet_api(fun) # (str) -> Callable
665
+ fun = self._construct_femtet_api(fun) # str | callable -> callable
431
666
 
432
667
  # 解析結果を開いた状態で Gaudi.Activate して ReExecute する場合、ReExecute の前後にアクティブ化イベントが必要
433
668
  # さらに、プロジェクトツリーが開いていないとアクティブ化イベントも意味がないらしい。
434
- if fun.__name__ == 'ReExecute':
435
- if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
669
+ if fun.__name__ == "ReExecute":
670
+ if (
671
+ self.open_result_with_gui
672
+ or self.parametric_output_indexes_use_as_objective
673
+ ):
436
674
  _post_activate_message(self.Femtet.hWnd)
437
675
  # API を実行
438
676
  returns = fun(*args, **kwargs) # can raise pywintypes.error
439
- if self.open_result_with_gui or self.parametric_output_indexes_use_as_objective:
677
+ if (
678
+ self.open_result_with_gui
679
+ or self.parametric_output_indexes_use_as_objective
680
+ ):
440
681
  _post_activate_message(self.Femtet.hWnd)
441
682
  else:
683
+ import sys
442
684
  returns = fun(*args, **kwargs)
443
685
 
444
686
  # API の実行に失敗
@@ -449,7 +691,7 @@ class FemtetInterface(FEMInterface):
449
691
  returns = return_value_if_failed
450
692
  else:
451
693
  returns = [return_value_if_failed] * (ret_for_check_idx + 1)
452
- logger.debug(' ' * print_indent + f'Femtet API result:{returns}')
694
+ logger.debug(" " * print_indent + f"Femtet API result:{returns}")
453
695
 
454
696
  # チェックすべき値の抽出
455
697
  if ret_for_check_idx is None:
@@ -468,25 +710,37 @@ class FemtetInterface(FEMInterface):
468
710
  if self.femtet_is_alive():
469
711
  # 生きていてもここにきているなら
470
712
  # 指定された Exception を送出する
471
- logger.debug(' ' * print_indent + error_message)
713
+ logger.debug(" " * print_indent + error_message)
472
714
  raise if_error(error_message)
473
715
 
474
716
  # 死んでいるなら再起動
475
717
  else:
476
718
  # 再起動試行回数の上限に達していたら諦める
477
- logger.debug(' ' * print_indent + f'現在の Femtet 再起動回数: {recourse_depth}')
719
+ logger.debug(
720
+ " " * print_indent + f"現在の Femtet 再起動回数: {recourse_depth}"
721
+ )
478
722
  if recourse_depth >= self.max_api_retry:
479
- raise Exception(Msg.ERR_FEMTET_CRASHED_AND_RESTART_FAILED)
723
+ raise Exception(
724
+ Msg.F_ERR_FEMTET_CRASHED_AND_RESTART_FAILED(
725
+ fun.__name__
726
+ )
727
+ )
480
728
 
481
729
  # 再起動
482
- logger.warn(' ' * print_indent + Msg.WARN_FEMTET_CRASHED_AND_TRY_RESTART)
730
+ logger.warning(
731
+ " " * print_indent + Msg.F_WARN_FEMTET_CRASHED_AND_TRY_RESTART(
732
+ fun.__name__
733
+ )
734
+ )
483
735
  CoInitialize()
484
- self.connect_femtet(connect_method='new')
736
+ self.connect_femtet(connect_method="new")
485
737
  self.open(self.femprj_path, self.model_name)
486
738
 
487
739
  # 状態を復元するために一度変数を渡して解析を行う(fun.__name__がSolveなら2度手間だが)
488
- logger.info(' ' * print_indent + Msg.INFO_FEMTET_CRASHED_AND_RESTARTED)
489
- self.update(self.parameters)
740
+ logger.info(" " * print_indent + Msg.INFO_FEMTET_CRASHED_AND_RESTARTED)
741
+
742
+ self.update_parameter(self.current_prm_values)
743
+ self.update()
490
744
 
491
745
  # 与えられた API の再帰的再試行
492
746
  return self._call_femtet_api(
@@ -499,90 +753,27 @@ class FemtetInterface(FEMInterface):
499
753
  args,
500
754
  kwargs,
501
755
  recourse_depth + 1,
502
- print_indent + 1
756
+ print_indent + 1,
503
757
  )
504
758
 
505
759
  def femtet_is_alive(self) -> bool:
506
760
  """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
761
 
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."}')
762
+ try:
763
+ hwnd = self.Femtet.hWnd
764
+ except (com_error, AttributeError):
765
+ return False
546
766
 
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)
767
+ if hwnd == 0:
768
+ return False
553
769
 
554
- # プロジェクトの相違をチェック
555
- if self.Femtet.ProjectPath != self.femprj_path:
556
- self.open(self.femprj_path, self.model_name)
770
+ pid = _get_pid(hwnd)
557
771
 
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)
772
+ return pid > 0
562
773
 
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)
774
+ # ===== model check and solve =====
571
775
 
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):
776
+ def _check_param_and_raise(self, param_name) -> None:
586
777
  """Check param_name is set in femprj file or not.
587
778
 
588
779
  Note:
@@ -595,45 +786,53 @@ class FemtetInterface(FEMInterface):
595
786
  try:
596
787
  variable_names = self.Femtet.GetVariableNames_py()
597
788
  except AttributeError as e:
598
- logger.error('================')
599
- logger.error(Msg.ERR_CANNOT_ACCESS_API + 'GetVariableNames_py')
789
+ logger.error("================")
790
+ logger.error(Msg.ERR_CANNOT_ACCESS_API + "GetVariableNames_py")
600
791
  logger.error(Msg.CERTIFY_MACRO_VERSION)
601
- logger.error('================')
792
+ logger.error("================")
602
793
  raise e
603
-
794
+
604
795
  if variable_names is not None:
605
796
  if param_name in variable_names:
606
797
  return self.Femtet.GetVariableValue(param_name)
607
798
  message = Msg.ERR_NO_SUCH_PARAMETER_IN_FEMTET
608
- logger.error('================')
799
+ logger.error("================")
609
800
  logger.error(message)
610
- logger.error(f'`{param_name}` not in {variable_names}')
611
- logger.error('================')
801
+ logger.error(f"`{param_name}` not in {variable_names}")
802
+ logger.error("================")
612
803
  raise RuntimeError(message)
613
804
  else:
614
805
  return None
615
806
 
616
- def update_parameter(self, parameters: 'pd.DataFrame', with_warning=False):
807
+ def update_parameter(self, x: dict[str, SupportedVariableTypes], with_warning=False) -> None | list[str]:
617
808
  """Update parameter of femprj."""
618
- self.parameters = parameters.copy()
809
+ COMInterface.update_parameter(self, x)
619
810
 
620
- # 変数更新のための処理
811
+ # Gaudi.Activate()
621
812
  sleep(0.1) # Gaudi がおかしくなる時がある対策
622
813
  self._call_femtet_api(
623
- 'self.Femtet.Gaudi.Activate',
814
+ "self.Femtet.Gaudi.Activate",
624
815
  True, # 戻り値を持たないのでここは無意味で None 以外なら何でもいい
625
816
  Exception, # 生きてるのに開けない場合
626
817
  error_message=Msg.NO_ANALYSIS_MODEL_IS_OPEN,
627
818
  )
628
819
 
820
+ # Version check
629
821
  major, minor, bugfix = 2023, 1, 1
822
+
823
+ # 変数一覧を取得する関数がある
630
824
  if self._version() >= _version(major, minor, bugfix):
631
- # Femtet の設計変数の更新(2023.1.1 以降でマクロだけ古い場合はcheck_param_valueで引っかかっているはずなのでここはAttributeError をチェックしない)
825
+
826
+ # Femtet で定義されている変数の取得
827
+ # (2023.1.1 以降でマクロだけ古い場合は
828
+ # check_param_value で引っかかっている
829
+ # はずなのでここは AttributeError を
830
+ # チェックしない)
632
831
  existing_variable_names = self._call_femtet_api(
633
832
  fun=self.Femtet.GetVariableNames_py,
634
833
  return_value_if_failed=False, # 意味がない
635
834
  if_error=ModelError, # 生きてるのに失敗した場合
636
- error_message=f'GetVariableNames_py failed.',
835
+ error_message=f"GetVariableNames_py failed.",
637
836
  is_Gaudi_method=True,
638
837
  )
639
838
 
@@ -644,57 +843,62 @@ class FemtetInterface(FEMInterface):
644
843
  else:
645
844
  return None
646
845
 
647
- # update
648
- warnings = []
649
- for i, row in parameters.iterrows():
650
- name = row['name']
651
- value = row['value']
846
+ # 変数を更新
847
+ warning_messages = []
848
+ for name, value in self.current_prm_values.items():
849
+
850
+ # 渡された変数がちゃんと Femtet に定義されている
652
851
  if name in existing_variable_names:
852
+
853
+ # Femtet.UpdateVariable
653
854
  self._call_femtet_api(
654
855
  fun=self.Femtet.UpdateVariable,
655
856
  return_value_if_failed=False,
656
857
  if_error=ModelError, # 生きてるのに失敗した場合
657
- error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE + f'{value} -> {name}',
858
+ error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE
859
+ + f"{value} -> {name}",
658
860
  is_Gaudi_method=True,
659
861
  args=(name, value),
660
862
  )
863
+
864
+ # 渡された変数が Femtet で定義されていない
661
865
  else:
662
- msg = f'{name} not in {self.model_name}: ' + Msg.WARN_IGNORE_PARAMETER_NOT_CONTAINED
663
- warnings.append(msg)
664
- logger.warn(msg)
866
+ if self._warn_if_undefined_variable:
867
+ msg = (
868
+ f"{name} not in {self.model_name}: "
869
+ + Msg.WARN_IGNORE_PARAMETER_NOT_CONTAINED
870
+ )
871
+ warning_messages.append(msg)
872
+ logger.warning(msg)
665
873
 
874
+ # 変数一覧を取得する関数がない(チェックしない)
666
875
  else:
876
+
667
877
  # update without parameter check
668
- warnings = []
669
- for i, row in parameters.iterrows():
670
- name = row['name']
671
- value = row['value']
878
+ warning_messages = []
879
+ for name, value in self.current_prm_values.items():
672
880
  self._call_femtet_api(
673
881
  fun=self.Femtet.UpdateVariable,
674
882
  return_value_if_failed=False,
675
883
  if_error=ModelError, # 生きてるのに失敗した場合
676
- error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE + f'{value} -> {name}',
884
+ error_message=Msg.ERR_FAILED_TO_UPDATE_VARIABLE
885
+ + f"{value} -> {name}",
677
886
  is_Gaudi_method=True,
678
887
  args=(name, value),
679
888
  )
680
889
 
681
890
  # ここでは ReExecute しない
682
891
  if with_warning:
683
- return warnings
892
+ return warning_messages
684
893
  else:
685
894
  return None
686
895
 
687
- def update_model(self, parameters: 'pd.DataFrame', with_warning=False) -> Optional[List[str]]:
896
+ def update_model(self) -> None:
688
897
  """Updates the analysis model only."""
689
898
 
690
- self.parameters = parameters.copy()
691
-
692
- # 変数の更新
693
- warnings = self.update_parameter(parameters, with_warning)
694
-
695
899
  # 設計変数に従ってモデルを再構築
696
900
  self._call_femtet_api(
697
- 'self.Femtet.Gaudi.ReExecute',
901
+ "self.Femtet.Gaudi.ReExecute",
698
902
  False,
699
903
  ModelError, # 生きてるのに失敗した場合
700
904
  error_message=Msg.ERR_RE_EXECUTE_MODEL_FAILED,
@@ -710,14 +914,12 @@ class FemtetInterface(FEMInterface):
710
914
  is_Gaudi_method=True,
711
915
  )
712
916
 
713
- if with_warning:
714
- return warnings or []
715
-
716
917
  def solve(self) -> None:
717
918
  """Execute FEM analysis."""
718
- # # メッシュを切る
919
+
920
+ # メッシュを切る
719
921
  self._call_femtet_api(
720
- 'self.Femtet.Gaudi.Mesh',
922
+ "self.Femtet.Gaudi.Mesh",
721
923
  0,
722
924
  MeshError,
723
925
  Msg.ERR_MODEL_MESH_FAILED,
@@ -725,9 +927,9 @@ class FemtetInterface(FEMInterface):
725
927
  )
726
928
 
727
929
  if self.parametric_output_indexes_use_as_objective is not None:
728
- from pyfemtet.opt.interface._femtet_parametric import solve_via_parametric_dll
729
930
 
730
- pdt_path = self.Femtet.ResultFilePath + '.pdt'
931
+ # PyFemtet で保存させる pdt パスを決定する
932
+ pdt_path = self.Femtet.ResultFilePath + ".pdt"
731
933
 
732
934
  # 前のものが残っているとややこしいので消しておく
733
935
  if os.path.exists(pdt_path):
@@ -767,7 +969,8 @@ class FemtetInterface(FEMInterface):
767
969
  is_Gaudi_method=True,
768
970
  )
769
971
 
770
- # 次に呼ばれるはずのユーザー定義コスト関数の記述を簡単にするため先に解析結果を開いておく
972
+ # 次に呼ばれるはずのユーザー定義コスト関数の
973
+ # 記述を簡単にするため先に解析結果を開いておく
771
974
  self._call_femtet_api(
772
975
  self.Femtet.OpenCurrentResult,
773
976
  False,
@@ -777,14 +980,6 @@ class FemtetInterface(FEMInterface):
777
980
  args=(self.open_result_with_gui,),
778
981
  )
779
982
 
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
983
  def preprocess(self, Femtet):
789
984
  """A method called just before :func:`solve`.
790
985
 
@@ -807,22 +1002,14 @@ class FemtetInterface(FEMInterface):
807
1002
  """
808
1003
  pass
809
1004
 
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
- )
1005
+ def update(self) -> None:
1006
+ """See :func:`FEMInterface.update`"""
1007
+ self.update_model()
1008
+ self.preprocess(self.Femtet)
1009
+ self.solve()
1010
+ self.postprocess(self.Femtet)
823
1011
 
824
- def _version(self):
825
- return _version(Femtet=self.Femtet)
1012
+ # ===== postprocess after recording =====
826
1013
 
827
1014
  def _create_postprocess_args(self):
828
1015
  try:
@@ -836,7 +1023,7 @@ class FemtetInterface(FEMInterface):
836
1023
  jpg_content = None
837
1024
 
838
1025
  out = dict(
839
- original_femprj_path=self.original_femprj_path,
1026
+ original_femprj_path=self._original_femprj_path,
840
1027
  model_name=self.model_name,
841
1028
  pdt_file_content=file_content,
842
1029
  jpg_file_content=jpg_content,
@@ -845,55 +1032,60 @@ class FemtetInterface(FEMInterface):
845
1032
  return out
846
1033
 
847
1034
  @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')
1035
+ def _create_path(femprj_path, model_name, trial_name, ext):
1036
+ result_dir = femprj_path.replace(".femprj", ".Results")
1037
+ ext = ext.removeprefix('.')
1038
+ pdt_path = os.path.join(result_dir, model_name + f"_{trial_name}.{ext}")
851
1039
  return pdt_path
852
1040
 
853
1041
  # noinspection PyMethodOverriding
854
1042
  @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
1043
+ def _postprocess_after_recording(
1044
+ dask_scheduler, # must for run_on_scheduler
1045
+ trial_name: str,
1046
+ df: pd.DataFrame,
1047
+ *,
1048
+ original_femprj_path: str,
1049
+ save_results: str,
1050
+ model_name: str,
1051
+ pdt_file_content=None,
1052
+ jpg_file_content=None,
864
1053
  ):
1054
+ # FIXME: サブサンプリングの場合の処理
865
1055
 
866
1056
  # none なら何もしない
1057
+ # all or optimal ならいったん保存する
867
1058
  if save_results.lower() == 'none':
868
1059
  return
869
1060
 
870
- # all or optimal ならいったん保存する
871
- result_dir = original_femprj_path.replace('.femprj', '.Results')
872
1061
  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:
1062
+ pdt_path = FemtetInterface._create_path(
1063
+ original_femprj_path, model_name, trial_name, ext='pdt')
1064
+ with open(pdt_path, "wb") as f:
875
1065
  f.write(pdt_file_content)
876
1066
 
877
1067
  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:
1068
+ jpg_path = FemtetInterface._create_path(
1069
+ original_femprj_path, model_name, trial_name, ext='jpg')
1070
+ with open(jpg_path, "wb") as f:
880
1071
  f.write(jpg_file_content)
881
1072
 
882
1073
  # optimal なら不要ファイルの削除を実行する
883
1074
  if save_results.lower() == 'optimal':
884
1075
  for i, row in df.iterrows():
885
- if not bool(row['non_domi']):
1076
+ if not bool(row['optimality']):
886
1077
  trial_to_remove = int(row['trial'])
887
- pdt_path_to_remove = FemtetInterface._create_pdt_path(original_femprj_path, model_name, trial_to_remove)
1078
+ pdt_path_to_remove = FemtetInterface._create_path(
1079
+ original_femprj_path, model_name, trial_to_remove, ext='pdt')
888
1080
  if os.path.isfile(pdt_path_to_remove):
889
1081
  os.remove(pdt_path_to_remove)
890
1082
 
891
1083
  def _create_result_file_content(self):
892
1084
  """Called after solve"""
893
- if self.save_pdt == 'all':
1085
+ if self.save_pdt.lower() in ['all', 'optimal']:
894
1086
  # 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')
1087
+ result_dir = self.femprj_path.replace(".femprj", ".Results")
1088
+ pdt_path = os.path.join(result_dir, self.model_name + ".pdt")
897
1089
 
898
1090
  self._call_femtet_api(
899
1091
  fun=self.Femtet.SavePDT,
@@ -905,7 +1097,7 @@ class FemtetInterface(FEMInterface):
905
1097
  )
906
1098
 
907
1099
  # convert .pdt to ByteIO and return it
908
- with open(pdt_path, 'rb') as f:
1100
+ with open(pdt_path, "rb") as f:
909
1101
  content = f.read()
910
1102
  return content
911
1103
 
@@ -913,8 +1105,8 @@ class FemtetInterface(FEMInterface):
913
1105
  return None
914
1106
 
915
1107
  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')
1108
+ result_dir = self.femprj_path.replace(".femprj", ".Results")
1109
+ jpg_path = os.path.join(result_dir, self.model_name + ".jpg")
918
1110
 
919
1111
  # モデル表示画面の設定
920
1112
  self.Femtet.SetWindowSize(600, 600)
@@ -932,54 +1124,18 @@ class FemtetInterface(FEMInterface):
932
1124
  if not os.path.exists(jpg_path):
933
1125
  raise FailedToPostProcess(Msg.ERR_JPG_NOT_FOUND)
934
1126
 
935
- with open(jpg_path, 'rb') as f:
1127
+ with open(jpg_path, "rb") as f:
936
1128
  content = f.read()
937
1129
 
938
1130
  return content
939
1131
 
1132
+ # ===== others =====
940
1133
 
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
1134
+ def _get_additional_data(self) -> dict:
1135
+ return dict(
1136
+ femprj_path=self._original_femprj_path,
1137
+ model_name=self.model_name,
1138
+ )
980
1139
 
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
1140
+ def _version(self):
1141
+ return _version(Femtet=self.Femtet)