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