pyfemtet 0.6.6__tar.gz → 0.7.1__tar.gz

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

Potentially problematic release.


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

Files changed (131) hide show
  1. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/PKG-INFO +2 -1
  2. pyfemtet-0.7.1/pyfemtet/__init__.py +1 -0
  3. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_femtet_config_util/exit.py +3 -2
  4. pyfemtet-0.7.1/pyfemtet/_util/dask_util.py +10 -0
  5. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_util/excel_macro_util.py +25 -16
  6. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/dispatch_extensions/__init__.py +0 -1
  7. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/dispatch_extensions/_impl.py +2 -5
  8. pyfemtet-0.7.1/pyfemtet/logger/__init__.py +22 -0
  9. pyfemtet-0.7.1/pyfemtet/logger/_impl.py +231 -0
  10. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/_femopt.py +27 -17
  11. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/_femopt_core.py +36 -30
  12. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/_test_utils/control_femtet.py +0 -6
  13. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/__init__.py +1 -1
  14. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/_base.py +2 -4
  15. pyfemtet-0.7.1/pyfemtet/opt/interface/_excel_interface.py +776 -0
  16. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/_femtet.py +14 -21
  17. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/_femtet_parametric.py +1 -1
  18. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/_femtet_with_nx/_interface.py +1 -1
  19. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/_femtet_with_sldworks.py +1 -1
  20. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_base.py +5 -5
  21. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py +4 -1
  22. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +0 -1
  23. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_base.py +4 -4
  24. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyproject.toml +3 -1
  25. pyfemtet-0.6.6/pyfemtet/__init__.py +0 -1
  26. pyfemtet-0.6.6/pyfemtet/logger/__init__.py +0 -3
  27. pyfemtet-0.6.6/pyfemtet/logger/_impl.py +0 -107
  28. pyfemtet-0.6.6/pyfemtet/opt/interface/_excel_interface.py +0 -447
  29. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/LICENSE +0 -0
  30. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/README.md +0 -0
  31. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_femtet_config_util/__init__.py +0 -0
  32. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_femtet_config_util/autosave.py +0 -0
  33. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_message/1. make_pot.bat +0 -0
  34. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_message/2. make_mo.bat +0 -0
  35. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_message/__init__.py +0 -0
  36. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_message/babel.cfg +0 -0
  37. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +0 -0
  38. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_message/locales/messages.pot +0 -0
  39. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_message/messages.py +0 -0
  40. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_util/__init__.py +0 -0
  41. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/_warning.py +0 -0
  42. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/brep/__init__.py +0 -0
  43. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/brep/_impl.py +0 -0
  44. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/core.py +0 -0
  45. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/__init__.py +0 -0
  46. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/_test_utils/__init__.py +0 -0
  47. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/_test_utils/hyper_sphere.py +0 -0
  48. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/_test_utils/record_history.py +0 -0
  49. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -0
  50. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/interface/_femtet_with_nx/update_model.py +0 -0
  51. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/__init__.py +0 -0
  52. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_optuna/__init__.py +0 -0
  53. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_optuna/_botorch_patch/__init__.py +0 -0
  54. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_optuna/_optuna.py +0 -0
  55. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +0 -0
  56. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_scipy.py +0 -0
  57. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/_scipy_scalar.py +0 -0
  58. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/optimizer/parameter.py +0 -0
  59. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/prediction/__init__.py +0 -0
  60. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/prediction/_base.py +0 -0
  61. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/prediction/single_task_gp.py +0 -0
  62. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/ParametricIF.femprj +0 -0
  63. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/ParametricIF.py +0 -0
  64. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/ParametricIF_test_result.reccsv +0 -0
  65. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.femprj +0 -0
  66. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.prt +0 -0
  67. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.py +0 -0
  68. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -0
  69. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
  70. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.femprj +0 -0
  71. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.py +0 -0
  72. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -0
  73. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/constrained_pipe.femprj +0 -0
  74. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/constrained_pipe_test_result.reccsv +0 -0
  75. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.femprj +0 -0
  76. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.py +0 -0
  77. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -0
  78. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.femprj +0 -0
  79. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.py +0 -0
  80. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -0
  81. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.femprj +0 -0
  82. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +0 -0
  83. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -0
  84. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.femprj +0 -0
  85. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.py +0 -0
  86. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_parallel.py +0 -0
  87. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -0
  88. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.femprj +0 -0
  89. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.py +0 -0
  90. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_parallel.py +0 -0
  91. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -0
  92. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.femprj +0 -0
  93. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.py +0 -0
  94. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
  95. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.py +0 -0
  96. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
  97. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.py +0 -0
  98. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/constrained_pipe_jp.py +0 -0
  99. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
  100. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -0
  101. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
  102. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -0
  103. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
  104. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.py +0 -0
  105. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
  106. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -0
  107. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -0
  108. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.femprj +0 -0
  109. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -0
  110. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +0 -0
  111. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/__init__.py +0 -0
  112. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_complex_components/__init__.py +0 -0
  113. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_complex_components/alert_region.py +0 -0
  114. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_complex_components/control_femtet.py +0 -0
  115. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +0 -0
  116. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_complex_components/main_graph.py +0 -0
  117. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_complex_components/pm_graph.py +0 -0
  118. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_complex_components/pm_graph_creator.py +0 -0
  119. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_create_wrapped_components.py +0 -0
  120. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_process_monitor/__init__.py +0 -0
  121. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_process_monitor/application.py +0 -0
  122. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_process_monitor/pages.py +0 -0
  123. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_wrapped_components/__init__.py +0 -0
  124. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_wrapped_components/dbc.py +0 -0
  125. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_wrapped_components/dcc.py +0 -0
  126. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_wrapped_components/html.py +0 -0
  127. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/_wrapped_components/str_enum.py +0 -0
  128. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/result_viewer/.gitignore +0 -0
  129. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/result_viewer/__init__.py +0 -0
  130. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/result_viewer/application.py +0 -0
  131. {pyfemtet-0.6.6 → pyfemtet-0.7.1}/pyfemtet/opt/visualization/result_viewer/pages.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyfemtet
3
- Version: 0.6.6
3
+ Version: 0.7.1
4
4
  Summary: Design parameter optimization using Femtet.
5
5
  Home-page: https://github.com/pyfemtet/pyfemtet
6
6
  License: BSD-3-Clause
@@ -22,6 +22,7 @@ Requires-Dist: dash-bootstrap-components (>=1.5.0,<2.0.0)
22
22
  Requires-Dist: dask (>=2023.12.1,<2024.0.0)
23
23
  Requires-Dist: distributed (>=2023.12.1,<2024.0.0)
24
24
  Requires-Dist: femtetutils (>=1.0.0,<2.0.0)
25
+ Requires-Dist: fire (>=0.6.0,<0.7.0)
25
26
  Requires-Dist: numpy (>=1.26.2,<2.0.0)
26
27
  Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
27
28
  Requires-Dist: optuna (>=3.4.0,<5.0.0)
@@ -0,0 +1 @@
1
+ __version__ = "0.7.1"
@@ -1,5 +1,4 @@
1
1
  from time import time, sleep
2
- import logging
3
2
 
4
3
  from win32com.client import CDispatch
5
4
  from femtetutils import util
@@ -9,7 +8,9 @@ from pyfemtet.dispatch_extensions._impl import _get_pid
9
8
  from pyfemtet.core import _version
10
9
  from pyfemtet._message import Msg
11
10
 
12
- logger = logging.getLogger('fem')
11
+ from pyfemtet.logger import get_module_logger
12
+
13
+ logger = get_module_logger('util.femtet.exit', __name__)
13
14
 
14
15
 
15
16
  def _exit_or_force_terminate(timeout, Femtet: CDispatch, force=True):
@@ -0,0 +1,10 @@
1
+ from contextlib import nullcontext
2
+ from dask.distributed import Lock
3
+
4
+
5
+ def lock_or_no_lock(name: str, client=None):
6
+ lock = Lock(name, client)
7
+ if lock.client is None:
8
+ return nullcontext()
9
+ else:
10
+ return lock
@@ -1,26 +1,31 @@
1
1
  """Excel のエラーダイアログを補足します。"""
2
- import sys
3
2
  from time import sleep
4
3
  from threading import Thread
5
- import logging
6
4
  import asyncio # for timeout
7
5
  import win32gui
8
6
  import win32con
9
7
  import win32api
8
+ import win32process
9
+
10
+ from pyfemtet.logger import get_module_logger
11
+
12
+ logger = get_module_logger('util.excel', __name__)
10
13
 
11
- logger = logging.getLogger(__name__)
12
- if __name__ == '__main__':
13
- formatter = logging.Formatter(logging.BASIC_FORMAT)
14
- handler = logging.StreamHandler(sys.stdout)
15
- handler.setFormatter(formatter)
16
- logger.addHandler(handler)
17
- logger.setLevel(logging.DEBUG)
14
+
15
+ def _get_pid(hwnd):
16
+ """Window handle から process ID を取得します."""
17
+ if hwnd > 0:
18
+ _, pid = win32process.GetWindowThreadProcessId(hwnd)
19
+ else:
20
+ pid = 0
21
+ return pid
18
22
 
19
23
 
20
24
  class _ExcelDialogProcessor:
21
25
 
22
- def __init__(self, excel_, timeout):
26
+ def __init__(self, excel_, timeout, restore_book=True):
23
27
  self.excel = excel_
28
+ self.excel_pid = _get_pid(excel_.hWnd)
24
29
  self.__excel_window_title = f' - Excel' # {basename} - Excel
25
30
  self.__error_dialog_title = 'Microsoft Visual Basic'
26
31
  self.__vbe_window_title = f'Microsoft Visual Basic for Applications - ' # Microsoft Visual Basic for Applications - {basename}
@@ -31,6 +36,7 @@ class _ExcelDialogProcessor:
31
36
  self.__error_raised = False
32
37
  self.__excel_state_stash = dict()
33
38
  self.__watch_thread = None
39
+ self.restore_book = restore_book
34
40
 
35
41
  async def watch(self):
36
42
 
@@ -47,6 +53,7 @@ class _ExcelDialogProcessor:
47
53
  win32gui.EnumWindows(self.enum_callback_to_close_dialog, found)
48
54
  await asyncio.sleep(0.5)
49
55
  if any(found):
56
+ await asyncio.sleep(1.)
50
57
  break
51
58
 
52
59
  logger.debug('ブックを閉じます。')
@@ -61,7 +68,7 @@ class _ExcelDialogProcessor:
61
68
  def enum_callback_to_activate(self, hwnd, _):
62
69
  title = win32gui.GetWindowText(hwnd)
63
70
  # Excel 本体
64
- if self.__excel_window_title in title:
71
+ if (self.excel_pid == _get_pid(hwnd)) and (self.__excel_window_title in title):
65
72
  # Visible == True の際、エラーが発生した際、
66
73
  # 一度 Excel ウィンドウをアクティブ化しないと dialog が出てこない
67
74
  # が、これだけではダメかも。
@@ -70,7 +77,7 @@ class _ExcelDialogProcessor:
70
77
  def enum_callback_to_close_dialog(self, hwnd, found):
71
78
  title = win32gui.GetWindowText(hwnd)
72
79
  # エラーダイアログ
73
- if self.__error_dialog_title == title:
80
+ if (self.excel_pid == _get_pid(hwnd)) and (self.__error_dialog_title == title):
74
81
  # 何故かこのコマンド以外受け付けず、
75
82
  # このコマンドで問答無用でデバッグモードに入る
76
83
  logger.debug('エラーダイアログを見つけました。')
@@ -82,14 +89,14 @@ class _ExcelDialogProcessor:
82
89
  def enum_callback_to_close_confirm_dialog(self, hwnd, _):
83
90
  title = win32gui.GetWindowText(hwnd)
84
91
  # 確認ダイアログ
85
- if "Microsoft Excel" in title:
92
+ if (self.excel_pid == _get_pid(hwnd)) and ("Microsoft Excel" in title):
86
93
  # DisplayAlerts が False の場合は不要
87
94
  win32gui.SendMessage(hwnd, win32con.WM_SYSCOMMAND, win32con.SC_CLOSE, 0)
88
95
 
89
96
  def enum_callback_to_close_book(self, hwnd, _):
90
97
  title = win32gui.GetWindowText(hwnd)
91
98
  # VBE
92
- if self.__vbe_window_title in title:
99
+ if (self.excel_pid == _get_pid(hwnd)) and (self.__vbe_window_title in title):
93
100
  # 何故かこれで book 本体が閉じる
94
101
  win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0)
95
102
 
@@ -133,15 +140,17 @@ class _ExcelDialogProcessor:
133
140
  # if exc_type is not None:
134
141
  # if issubclass(exc_type, com_error) and self.__error_raised:
135
142
  if self.__error_raised:
143
+ if self.restore_book:
136
144
  logger.debug('エラーハンドリングの副作用でブックを閉じているので'
137
145
  'Excel のブックを開きなおします。')
138
146
  for wb_path in self.__workbook_paths:
139
147
  self.excel.Workbooks.Open(wb_path)
140
148
 
141
149
 
142
- def watch_excel_macro_error(excel_, timeout):
150
+ def watch_excel_macro_error(excel_, timeout, restore_book=True):
143
151
  """Excel のエラーダイアログの出現を監視し、検出されればブックを閉じます。"""
144
- return _ExcelDialogProcessor(excel_, timeout)
152
+ return _ExcelDialogProcessor(excel_, timeout, restore_book)
153
+
145
154
 
146
155
 
147
156
  if __name__ == '__main__':
@@ -9,4 +9,3 @@ from pyfemtet.dispatch_extensions._impl import (
9
9
  _get_pid,
10
10
  _get_pids,
11
11
  )
12
-
@@ -18,14 +18,11 @@ from multiprocessing.context import BaseContext, SpawnProcess, _concrete_context
18
18
  from multiprocessing.process import _children, _cleanup
19
19
  from multiprocessing.managers import SyncManager
20
20
 
21
- import logging
22
- from pyfemtet.logger import get_logger
23
-
24
21
  from pyfemtet._message import Msg
25
22
 
23
+ from pyfemtet.logger import get_module_logger
26
24
 
27
- logger = get_logger('dispatch')
28
- logger.setLevel(logging.INFO)
25
+ logger = get_module_logger('dispatch', __name__)
29
26
 
30
27
 
31
28
  DISPATCH_TIMEOUT = 120
@@ -0,0 +1,22 @@
1
+ import logging
2
+ from pyfemtet.logger._impl import (
3
+ get_module_logger,
4
+ add_file_output,
5
+ set_stdout_output,
6
+ remove_file_output,
7
+ remove_stdout_output,
8
+ remove_all_output,
9
+ )
10
+
11
+
12
+ def get_dask_logger():
13
+ return logging.getLogger('distributed')
14
+
15
+
16
+ def get_optuna_logger():
17
+ import optuna
18
+ return optuna.logging.get_logger('optuna')
19
+
20
+
21
+ def get_dash_logger():
22
+ return logging.getLogger('werkzeug')
@@ -0,0 +1,231 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ import datetime
5
+ import locale
6
+ from threading import Lock
7
+ from pathlib import Path
8
+ import platform
9
+
10
+ from colorlog import ColoredFormatter
11
+ from dask.distributed import get_worker
12
+
13
+ LOCALE, LOCALE_ENCODING = locale.getlocale()
14
+ if platform.system() == 'Windows':
15
+ DATEFMT = '%#m/%#d %#H:%M'
16
+ else:
17
+ DATEFMT = '%-m/%-d %-H:%M'
18
+
19
+ __lock = Lock() # thread 並列されたタスクがアクセスする場合に備えて
20
+
21
+ __initialized_root_packages: list[str] = list()
22
+
23
+
24
+ # ===== set dask worker prefix to ``ROOT`` logger =====
25
+
26
+ def _get_dask_worker_name():
27
+ name = '(Main)'
28
+ try:
29
+ worker = get_worker()
30
+ if isinstance(worker.name, str): # local なら index, cluster なら tcp address
31
+ name = f'({worker.name})'
32
+ else:
33
+ name = f'(Sub{worker.name})'
34
+ except ValueError:
35
+ pass
36
+ return name
37
+
38
+
39
+ class _DaskLogRecord(logging.LogRecord):
40
+ def getMessage(self):
41
+ msg = str(self.msg)
42
+ if self.args:
43
+ msg = msg % self.args
44
+ msg = _get_dask_worker_name() + ' ' + msg
45
+ return msg
46
+
47
+
48
+ logging.setLogRecordFactory(_DaskLogRecord)
49
+
50
+
51
+ # ===== format config =====
52
+
53
+ def __create_formatter(colored=True):
54
+
55
+ if colored:
56
+ # colorized
57
+ header = "%(log_color)s" + "[%(name)s %(levelname).4s]" + " %(asctime)s" + "%(reset)s"
58
+
59
+ formatter = ColoredFormatter(
60
+ f"{header} %(message)s",
61
+ datefmt=DATEFMT,
62
+ reset=True,
63
+ log_colors={
64
+ "DEBUG": "purple",
65
+ "INFO": "cyan",
66
+ "WARNING": "yellow",
67
+ "ERROR": "light_red",
68
+ "CRITICAL": "red",
69
+ },
70
+ )
71
+
72
+ else:
73
+ header = "[%(name)s %(levelname).4s]"
74
+ formatter = logging.Formatter(
75
+ f"{header} %(message)s",
76
+ datefmt=DATEFMT,
77
+ )
78
+
79
+ return formatter
80
+
81
+
82
+ # ===== handler config =====
83
+
84
+ STDOUT_HANDLER_NAME = 'stdout-handler'
85
+
86
+
87
+ def __get_stdout_handler():
88
+ stdout_handler = logging.StreamHandler(sys.stdout)
89
+ stdout_handler.set_name(STDOUT_HANDLER_NAME)
90
+ stdout_handler.setFormatter(__create_formatter(colored=True))
91
+ return stdout_handler
92
+
93
+
94
+ def __has_stdout_handler(logger):
95
+ return any([handler.get_name() != STDOUT_HANDLER_NAME for handler in logger.handlers])
96
+
97
+
98
+ def set_stdout_output(logger, level=logging.INFO):
99
+
100
+ if not __has_stdout_handler(logger):
101
+ logger.addHandler(__get_stdout_handler())
102
+
103
+ logger.setLevel(level)
104
+
105
+
106
+ def remove_stdout_output(logger):
107
+ if __has_stdout_handler(logger):
108
+ logger.removeHandler(__get_stdout_handler())
109
+
110
+
111
+ def add_file_output(logger, filepath=None, level=logging.INFO) -> str:
112
+ """Add FileHandler to the logger.
113
+
114
+ Returns:
115
+ str: THe name of the added handler.
116
+ Its format is 'filehandler-{os.path.basename(filepath)}'
117
+
118
+ """
119
+
120
+ # certify filepath
121
+ if filepath is None:
122
+ filepath = datetime.datetime.now().strftime('%Y%m%d-%H%M%S') + f'_{logger.name}.log'
123
+
124
+ # add file handler
125
+ file_handler = logging.FileHandler(filename=filepath, encoding=LOCALE_ENCODING)
126
+ file_handler.set_name(f'filehandler-{os.path.basename(filepath)}')
127
+ file_handler.setFormatter(__create_formatter(colored=False))
128
+ logger.addHandler(file_handler)
129
+
130
+ # set (default) log level
131
+ logger.setLevel(level)
132
+
133
+ return file_handler.get_name()
134
+
135
+
136
+ def remove_file_output(logger, filepath=None):
137
+ """Removes FileHandler from the logger.
138
+
139
+ If filepath is None, remove all FileHandler.
140
+ """
141
+
142
+ if filepath is None:
143
+ for handler in logger.handlers:
144
+ if 'filehandler-' in handler.name:
145
+ logger.removeHandler(handler)
146
+
147
+ else:
148
+ handler_name = f'filehandler-{os.path.basename(filepath)}'
149
+ for handler in logger.handlers:
150
+ if handler_name == handler.name:
151
+ logger.removeHandler(handler)
152
+
153
+
154
+ def remove_all_output(logger):
155
+ for handler in logger.handlers:
156
+ logger.removeHandler(handler)
157
+
158
+ logger.addHandler(logging.NullHandler())
159
+
160
+
161
+ # ===== root-package logger =====
162
+
163
+ def setup_package_root_logger(package_name):
164
+ global __initialized_root_packages
165
+ if package_name not in __initialized_root_packages:
166
+ with __lock:
167
+ logger = logging.getLogger(package_name)
168
+ logger.propagate = True
169
+ set_stdout_output(logger)
170
+ logger.setLevel(logging.INFO)
171
+ __initialized_root_packages.append(package_name)
172
+ else:
173
+ logger = logging.getLogger(package_name)
174
+ return logger
175
+
176
+
177
+ # ===== module logger =====
178
+
179
+ def get_module_logger(name: str, __module_name__: str, ) -> logging.Logger:
180
+ """Return the module-level logger.
181
+
182
+ The format is defined in the package_root_logger.
183
+
184
+ Args:
185
+ name (str): The logger name to want.
186
+ __module_name__ (str): __name__ of the module.
187
+
188
+ Returns:
189
+ logging.Logger:
190
+ The logger its name is ``root_package.subpackage.module``.
191
+ child level logger's signal propagates to the parent logger
192
+ and is shown in the parent(s)'s handler(s).
193
+
194
+ """
195
+
196
+ # check root logger initialized
197
+ name_arr = name.split('.')
198
+ if name_arr[0] not in __initialized_root_packages:
199
+ setup_package_root_logger(name_arr[0])
200
+
201
+ # get logger
202
+ logger = logging.getLogger(name)
203
+
204
+ # If not root logger, ensure propagate is True.
205
+ if len(name_arr) > 1:
206
+ logger.propagate = True
207
+
208
+ # If debug mode, set specific level.
209
+ if __module_name__ == '__main__':
210
+ logger.setLevel(logging.DEBUG)
211
+
212
+ return logger
213
+
214
+
215
+ if __name__ == '__main__':
216
+
217
+ root_logger = setup_package_root_logger('logger')
218
+ optimizer_logger = get_module_logger('logger.optimizer', __name__); optimizer_logger.setLevel(logging.INFO)
219
+ interface_logger = get_module_logger('logger.interface', __name__)
220
+
221
+ root_logger.info("This is root logger's info.")
222
+ optimizer_logger.info("This is optimizer logger's info.")
223
+
224
+ add_file_output(interface_logger, 'test-module-log.log', level=logging.DEBUG)
225
+ interface_logger.debug('debugging...')
226
+ remove_file_output(interface_logger, 'test-module-log.log')
227
+
228
+ interface_logger.debug('debug is finished.')
229
+ root_logger.debug("This message will not be shown "
230
+ "even if the module_logger's level "
231
+ "is logging.DEBUG.")
@@ -12,7 +12,7 @@ from traceback import print_exception
12
12
  # 3rd-party
13
13
  import numpy as np
14
14
  import pandas as pd
15
- from dask.distributed import LocalCluster, Client
15
+ from dask.distributed import LocalCluster, Client, get_worker, Nanny
16
16
 
17
17
  # pyfemtet relative
18
18
  from pyfemtet.opt.interface import FEMInterface, FemtetInterface
@@ -136,6 +136,7 @@ class FEMOpt:
136
136
  self.monitor_server_kwargs = dict()
137
137
  self.monitor_process_worker_name = None
138
138
  self._hv_reference = None
139
+ self._extra_space_dir = None
139
140
 
140
141
  # multiprocess 時に pickle できないオブジェクト参照の削除
141
142
  def __getstate__(self):
@@ -675,10 +676,6 @@ class FEMOpt:
675
676
  directions,
676
677
  )
677
678
 
678
- # Femtet の confirm_before_exit のセット
679
- self.fem.confirm_before_exit = confirm_before_exit
680
- self.fem.kwargs['confirm_before_exit'] = confirm_before_exit
681
-
682
679
  logger.info('Femtet loaded successfully.')
683
680
 
684
681
  # クラスターの設定
@@ -718,30 +715,43 @@ class FEMOpt:
718
715
  # これは CLI の --no-nanny オプションも同様らしい。
719
716
 
720
717
  # クラスターの構築
718
+ # noinspection PyTypeChecker
721
719
  cluster = LocalCluster(
722
720
  processes=True,
723
721
  n_workers=n_parallel,
724
722
  threads_per_worker=1,
723
+ worker_class=Nanny,
725
724
  )
726
725
  logger.info('LocalCluster launched successfully.')
727
726
 
728
- self.client = Client(cluster, direct_to_workers=False)
729
- self.scheduler_address = self.client.scheduler.address
727
+ self.client = Client(
728
+ cluster,
729
+ direct_to_workers=False,
730
+ )
730
731
  logger.info('Client launched successfully.')
731
732
 
732
- # 最適化タスクを振り分ける worker を指定
733
- subprocess_indices = list(range(n_parallel))[1:]
734
- worker_addresses = list(self.client.nthreads().keys())
733
+ self.scheduler_address = self.client.scheduler.address
735
734
 
736
- # monitor worker の設定
737
- self.monitor_process_worker_name = worker_addresses[0]
738
- worker_addresses[0] = 'Main'
735
+ # worker address を取得
736
+ nannies_dict: dict[Any, Nanny] = self.client.cluster.workers
737
+ nannies = tuple(nannies_dict.values())
738
+
739
+ # ひとつの Nanny を選んで monitor 用にしつつ
740
+ # その space は main process に使わせるために記憶する
741
+ self.monitor_process_worker_name = nannies[0].worker_address
742
+ self._extra_space_dir = nannies[0].worker_dir
743
+
744
+ # 名前と address がごちゃごちゃになっていて可読性が悪いが
745
+ # 選んだ以外の Nanny は計算を割り当てる用にする
746
+ worker_addresses = ['Main']
747
+ worker_addresses.extend([n.worker_address for n in nannies[1:]])
748
+ subprocess_indices = list(range(n_parallel))[1:]
739
749
 
740
750
  with self.client.cluster as _cluster, self.client as _client:
741
751
 
742
752
  # actor の設定
743
- self.status = OptimizationStatus(_client)
744
- self.worker_status_list = [OptimizationStatus(_client, name) for name in worker_addresses] # tqdm 検討
753
+ self.status = OptimizationStatus(_client, worker_address=self.monitor_process_worker_name)
754
+ self.worker_status_list = [OptimizationStatus(_client, worker_address=self.monitor_process_worker_name, name=name) for name in worker_addresses] # tqdm 検討
745
755
  self.status.set(OptimizationStatus.SETTING_UP)
746
756
  self.history = History(
747
757
  self.history_path,
@@ -773,7 +783,6 @@ class FEMOpt:
773
783
  logger.info('Process monitor initialized successfully.')
774
784
 
775
785
  # fem
776
- # TODO: n_parallel=1 のときもアップロードしている。これを使うべきか、アップロードしないべき。
777
786
  self.fem._setup_before_parallel(_client)
778
787
 
779
788
  # opt
@@ -794,7 +803,7 @@ class FEMOpt:
794
803
  subprocess_indices,
795
804
  [self.worker_status_list] * len(subprocess_indices),
796
805
  [wait_setup] * len(subprocess_indices),
797
- workers=worker_addresses,
806
+ workers=worker_addresses if self.opt.is_cluster else worker_addresses[1:],
798
807
  allow_other_workers=False,
799
808
  )
800
809
 
@@ -818,6 +827,7 @@ class FEMOpt:
818
827
  ),
819
828
  kwargs=dict(
820
829
  skip_reconstruct=True,
830
+ space_dir=self._extra_space_dir,
821
831
  )
822
832
  )
823
833
  t_main.start()
@@ -22,7 +22,7 @@ if version.parse(optuna.version.__version__) < version.parse('4.0.0'):
22
22
  else:
23
23
  from optuna._hypervolume import wfg
24
24
  compute_hypervolume = wfg.compute_hypervolume
25
- from dask.distributed import Lock, get_client
25
+ from dask.distributed import Lock, get_client, Client
26
26
 
27
27
  # win32com
28
28
  from win32com.client import constants, Constants
@@ -32,10 +32,9 @@ from pyfemtet.opt.interface import FEMInterface, FemtetInterface
32
32
  from pyfemtet._message import encoding, Msg
33
33
 
34
34
  # logger
35
- import logging
36
- from pyfemtet.logger import get_logger
37
- logger = get_logger('femopt')
38
- logger.setLevel(logging.INFO)
35
+ from pyfemtet.logger import get_module_logger
36
+
37
+ logger = get_module_logger('opt.core', __name__)
39
38
 
40
39
 
41
40
  __all__ = [
@@ -503,19 +502,6 @@ class ObjectivesFunc:
503
502
  return f
504
503
 
505
504
 
506
- class _HistoryDfCore:
507
- """Class for managing a DataFrame object in a distributed manner."""
508
-
509
- def __init__(self):
510
- self.df = pd.DataFrame()
511
-
512
- def set_df(self, df):
513
- self.df = df
514
-
515
- def get_df(self):
516
- return self.df
517
-
518
-
519
505
  class History:
520
506
  """Class for managing the history of optimization results.
521
507
 
@@ -543,8 +529,6 @@ class History:
543
529
  is_restart = False
544
530
  is_processing = False
545
531
  _df = None # in case without client
546
- _future = None # in case with client
547
- _actor_data = None # in case with client
548
532
 
549
533
  def __init__(
550
534
  self,
@@ -565,6 +549,7 @@ class History:
565
549
  self.obj_names = obj_names
566
550
  self.cns_names = cns_names
567
551
  self.additional_metadata = additional_metadata or ''
552
+ self.__scheduler_address = client.scheduler.address if client is not None else None
568
553
 
569
554
  # 最適化実行中かどうか
570
555
  self.is_processing = client is not None
@@ -572,10 +557,6 @@ class History:
572
557
  # 最適化実行中の process monitor である場合
573
558
  if self.is_processing:
574
559
 
575
- # actor の生成
576
- self._future = client.submit(_HistoryDfCore, actor=True)
577
- self._actor_data = self._future.result()
578
-
579
560
  # csv が存在すれば続きからモード
580
561
  self.is_restart = os.path.isfile(self.path)
581
562
 
@@ -641,16 +622,36 @@ class History:
641
622
  self.set_df(df)
642
623
 
643
624
  def get_df(self) -> pd.DataFrame:
644
- if self._actor_data is None:
625
+ if self.__scheduler_address is None:
645
626
  return self._df
646
627
  else:
647
- return self._actor_data.get_df().result()
628
+ # scheduler がまだ存命か確認する
629
+ try:
630
+ with Lock('access-df'):
631
+ client_: 'Client' = get_client(self.__scheduler_address)
632
+ if 'df' in client_.list_datasets():
633
+ return client_.get_dataset('df')
634
+ else:
635
+ logger.debug('Access df of History before it is initialized.')
636
+ return pd.DataFrame()
637
+ except OSError:
638
+ logger.error('Scheduler is already dead. Most frequent reasen to show this message is that the pyfemtet monitor UI is not refreshed even if the main optimization process is terminated.')
639
+ return pd.DataFrame()
648
640
 
649
641
  def set_df(self, df: pd.DataFrame):
650
- if self._actor_data is None:
642
+ if self.__scheduler_address is None:
651
643
  self._df = df
652
644
  else:
653
- self._actor_data.set_df(df).result()
645
+ try:
646
+ with Lock('access-df'):
647
+ client_: 'Client' = get_client(self.__scheduler_address)
648
+ if 'df' in client_.list_datasets():
649
+ client_.unpublish_dataset('df') # 更新する場合は前もって削除が必要、本来は dask collection をここに入れる使い方をする。
650
+ client_.publish_dataset(**dict(
651
+ df=df
652
+ ))
653
+ except OSError:
654
+ logger.error('Scheduler is already dead. Most frequent reasen to show this message is that the pyfemtet monitor UI is not refreshed even if the main optimization process is terminated.')
654
655
 
655
656
  def create_df_columns(self):
656
657
  """Create columns of history."""
@@ -979,8 +980,13 @@ class OptimizationStatus:
979
980
  TERMINATE_ALL = 60
980
981
  CRASHED = 70
981
982
 
982
- def __init__(self, client, name='entire'):
983
- self._future = client.submit(_OptimizationStatusActor, actor=True)
983
+ def __init__(self, client, worker_address, name='entire'):
984
+ self._future = client.submit(
985
+ _OptimizationStatusActor,
986
+ actor=True,
987
+ workers=[worker_address],
988
+ allow_other_workers=False,
989
+ )
984
990
  self._actor = self._future.result()
985
991
  self.name = name
986
992
  self.set(self.INITIALIZING)
@@ -5,12 +5,6 @@ from tqdm import tqdm
5
5
  from win32com.client import Dispatch
6
6
  from femtetutils import util
7
7
 
8
- import logging
9
-
10
-
11
- logger = logging.getLogger("test")
12
- logger.setLevel(logging.DEBUG)
13
-
14
8
 
15
9
  def _open_femprj(femprj_path):
16
10
  Femtet = Dispatch("FemtetMacro.Femtet")
@@ -1,4 +1,4 @@
1
- from pyfemtet.opt.interface._base import FEMInterface, logger
1
+ from pyfemtet.opt.interface._base import FEMInterface
2
2
  from pyfemtet.opt.interface._base import NoFEM
3
3
  from pyfemtet.opt.interface._femtet import FemtetInterface
4
4
  from pyfemtet.opt.interface._femtet_with_sldworks import FemtetWithSolidworksInterface