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