pyfemtet 0.6.2__py3-none-any.whl → 0.6.3__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 +1 -1
- pyfemtet/_femtet_config_util/__init__.py +0 -0
- pyfemtet/_femtet_config_util/autosave.py +38 -0
- pyfemtet/_femtet_config_util/exit.py +58 -0
- pyfemtet/_message/1. make_pot.bat +2 -3
- pyfemtet/_message/2. make_mo.bat +1 -1
- pyfemtet/_message/babel.cfg +1 -1
- pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +136 -124
- pyfemtet/_message/locales/messages.pot +136 -124
- pyfemtet/_message/messages.py +3 -0
- pyfemtet/_warning.py +59 -3
- pyfemtet/opt/_femopt.py +58 -38
- pyfemtet/opt/_test_utils/record_history.py +50 -0
- pyfemtet/opt/interface/_base.py +10 -1
- pyfemtet/opt/interface/_excel_interface.py +437 -0
- pyfemtet/opt/interface/_femtet.py +4 -75
- pyfemtet/opt/interface/_femtet_with_sldworks.py +1 -1
- pyfemtet/opt/optimizer/_base.py +10 -8
- pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +82 -21
- {pyfemtet-0.6.2.dist-info → pyfemtet-0.6.3.dist-info}/METADATA +1 -1
- {pyfemtet-0.6.2.dist-info → pyfemtet-0.6.3.dist-info}/RECORD +24 -20
- {pyfemtet-0.6.2.dist-info → pyfemtet-0.6.3.dist-info}/LICENSE +0 -0
- {pyfemtet-0.6.2.dist-info → pyfemtet-0.6.3.dist-info}/WHEEL +0 -0
- {pyfemtet-0.6.2.dist-info → pyfemtet-0.6.3.dist-info}/entry_points.txt +0 -0
pyfemtet/_warning.py
CHANGED
|
@@ -3,15 +3,57 @@ from functools import wraps
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def experimental_feature(func):
|
|
6
|
+
|
|
6
7
|
@wraps(func)
|
|
7
8
|
def wrapper(*args, **kwargs):
|
|
8
|
-
warnings.warn(
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
warnings.warn(
|
|
10
|
+
f"The function '{func.__name__}' is experimental and may change in the future.",
|
|
11
|
+
category=UserWarning,
|
|
12
|
+
stacklevel=2
|
|
13
|
+
)
|
|
11
14
|
return func(*args, **kwargs)
|
|
15
|
+
|
|
12
16
|
return wrapper
|
|
13
17
|
|
|
14
18
|
|
|
19
|
+
def experimental_class(cls):
|
|
20
|
+
|
|
21
|
+
class Wrapper(cls):
|
|
22
|
+
def __new__(cls, *args, **kwargs):
|
|
23
|
+
warnings.warn(
|
|
24
|
+
f"The class '{cls.__name__}' is experimental and may change in the future.",
|
|
25
|
+
category=UserWarning,
|
|
26
|
+
stacklevel=2
|
|
27
|
+
)
|
|
28
|
+
return super().__new__(cls)
|
|
29
|
+
|
|
30
|
+
Wrapper.__name__ = cls.__name__
|
|
31
|
+
Wrapper.__doc__ = cls.__doc__
|
|
32
|
+
return Wrapper
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def changed_feature(version, additional_message=None):
|
|
36
|
+
|
|
37
|
+
def changed_feature_wrapper(func):
|
|
38
|
+
|
|
39
|
+
@wraps(func)
|
|
40
|
+
def wrapper(*args, **kwargs):
|
|
41
|
+
|
|
42
|
+
version_string = f'in version {version}' if version is not None else 'in recent update'
|
|
43
|
+
additional_message_string = ' ' + additional_message if additional_message is not None else ''
|
|
44
|
+
|
|
45
|
+
warnings.warn(
|
|
46
|
+
f"The behavior of function '{func.__name__}' is changed {version_string}." + additional_message_string,
|
|
47
|
+
category=UserWarning,
|
|
48
|
+
stacklevel=2
|
|
49
|
+
)
|
|
50
|
+
return func(*args, **kwargs)
|
|
51
|
+
|
|
52
|
+
return wrapper
|
|
53
|
+
|
|
54
|
+
return changed_feature_wrapper
|
|
55
|
+
|
|
56
|
+
|
|
15
57
|
if __name__ == '__main__':
|
|
16
58
|
|
|
17
59
|
# 使用例
|
|
@@ -19,5 +61,19 @@ if __name__ == '__main__':
|
|
|
19
61
|
def my_experimental_function():
|
|
20
62
|
print("This is an experimental function.")
|
|
21
63
|
|
|
64
|
+
class Sample:
|
|
65
|
+
@experimental_feature
|
|
66
|
+
def add(self, a, b):
|
|
67
|
+
return a + b
|
|
68
|
+
|
|
22
69
|
# 実行すると、警告が表示されます。
|
|
23
70
|
my_experimental_function()
|
|
71
|
+
sample = Sample()
|
|
72
|
+
print(sample.add(1, 2))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@changed_feature('0.1.0', 'See documentation of `Optimizer` for more details.')
|
|
76
|
+
def product(a, b):
|
|
77
|
+
return a * b
|
|
78
|
+
|
|
79
|
+
print(product(1, 2))
|
pyfemtet/opt/_femopt.py
CHANGED
|
@@ -601,6 +601,25 @@ class FEMOpt:
|
|
|
601
601
|
|
|
602
602
|
"""
|
|
603
603
|
|
|
604
|
+
# ===== opt の設定 =====
|
|
605
|
+
|
|
606
|
+
# Interface から設定を読む場合の処理
|
|
607
|
+
# 設定ファイルから設定を読む場合は最適化問題が
|
|
608
|
+
# この時点で不定なので actor 等の設定をする前に
|
|
609
|
+
# ここで確定する必要がある。
|
|
610
|
+
if hasattr(self.fem, '_load_problem_from_me'):
|
|
611
|
+
if self.fem._load_problem_from_me:
|
|
612
|
+
self.fem.load_parameter(self.opt)
|
|
613
|
+
self.fem.load_objective(self.opt)
|
|
614
|
+
|
|
615
|
+
# resolve expression dependencies
|
|
616
|
+
self.opt.variables.resolve()
|
|
617
|
+
self.opt.variables.evaluate()
|
|
618
|
+
|
|
619
|
+
# opt の共通引数
|
|
620
|
+
self.opt.n_trials = n_trials
|
|
621
|
+
self.opt.timeout = timeout
|
|
622
|
+
|
|
604
623
|
# method checker
|
|
605
624
|
if n_parallel > 1:
|
|
606
625
|
self.opt.method_checker.check_parallel()
|
|
@@ -630,13 +649,37 @@ class FEMOpt:
|
|
|
630
649
|
if is_incomplete_bounds:
|
|
631
650
|
self.opt.method_checker.check_incomplete_bounds()
|
|
632
651
|
|
|
633
|
-
# 共通引数
|
|
634
|
-
self.opt.n_trials = n_trials
|
|
635
|
-
self.opt.timeout = timeout
|
|
636
652
|
|
|
637
|
-
#
|
|
638
|
-
|
|
639
|
-
|
|
653
|
+
# ===== fem の設定 ==--=
|
|
654
|
+
|
|
655
|
+
# Femtet 特有の処理
|
|
656
|
+
metadata = None
|
|
657
|
+
if isinstance(self.fem, FemtetInterface):
|
|
658
|
+
|
|
659
|
+
# 結果 csv に記載する femprj に関する情報の作成
|
|
660
|
+
metadata = json.dumps(
|
|
661
|
+
dict(
|
|
662
|
+
femprj_path=self.fem.original_femprj_path,
|
|
663
|
+
model_name=self.fem.model_name
|
|
664
|
+
)
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
# Femtet の parametric 設定を目的関数に用いるかどうか
|
|
668
|
+
if self.fem.parametric_output_indexes_use_as_objective is not None:
|
|
669
|
+
from pyfemtet.opt.interface._femtet_parametric import add_parametric_results_as_objectives
|
|
670
|
+
indexes = list(self.fem.parametric_output_indexes_use_as_objective.keys())
|
|
671
|
+
directions = list(self.fem.parametric_output_indexes_use_as_objective.values())
|
|
672
|
+
add_parametric_results_as_objectives(
|
|
673
|
+
self,
|
|
674
|
+
indexes,
|
|
675
|
+
directions,
|
|
676
|
+
)
|
|
677
|
+
|
|
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
|
+
logger.info('Femtet loaded successfully.')
|
|
640
683
|
|
|
641
684
|
# クラスターの設定
|
|
642
685
|
self.opt.is_cluster = self.scheduler_address is not None
|
|
@@ -696,35 +739,6 @@ class FEMOpt:
|
|
|
696
739
|
|
|
697
740
|
with self.client.cluster as _cluster, self.client as _client:
|
|
698
741
|
|
|
699
|
-
# Femtet 特有の処理
|
|
700
|
-
metadata = None
|
|
701
|
-
if isinstance(self.fem, FemtetInterface):
|
|
702
|
-
# 結果 csv に記載する femprj に関する情報
|
|
703
|
-
metadata = json.dumps(
|
|
704
|
-
dict(
|
|
705
|
-
femprj_path=self.fem.original_femprj_path,
|
|
706
|
-
model_name=self.fem.model_name
|
|
707
|
-
)
|
|
708
|
-
)
|
|
709
|
-
|
|
710
|
-
# Femtet の parametric 設定を目的関数に用いるかどうか
|
|
711
|
-
if self.fem.parametric_output_indexes_use_as_objective is not None:
|
|
712
|
-
from pyfemtet.opt.interface._femtet_parametric import add_parametric_results_as_objectives
|
|
713
|
-
indexes = list(self.fem.parametric_output_indexes_use_as_objective.keys())
|
|
714
|
-
directions = list(self.fem.parametric_output_indexes_use_as_objective.values())
|
|
715
|
-
add_parametric_results_as_objectives(
|
|
716
|
-
self,
|
|
717
|
-
indexes,
|
|
718
|
-
directions,
|
|
719
|
-
)
|
|
720
|
-
|
|
721
|
-
# Femtet の confirm_before_exit のセット
|
|
722
|
-
self.fem.confirm_before_exit = confirm_before_exit
|
|
723
|
-
self.fem.kwargs['confirm_before_exit'] = confirm_before_exit
|
|
724
|
-
|
|
725
|
-
logger.info('Femtet loaded successfully.')
|
|
726
|
-
|
|
727
|
-
|
|
728
742
|
# actor の設定
|
|
729
743
|
self.status = OptimizationStatus(_client)
|
|
730
744
|
self.worker_status_list = [OptimizationStatus(_client, name) for name in worker_addresses] # tqdm 検討
|
|
@@ -759,6 +773,7 @@ class FEMOpt:
|
|
|
759
773
|
logger.info('Process monitor initialized successfully.')
|
|
760
774
|
|
|
761
775
|
# fem
|
|
776
|
+
# TODO: n_parallel=1 のときもアップロードしている。これを使うべきか、アップロードしないべき。
|
|
762
777
|
self.fem._setup_before_parallel(_client)
|
|
763
778
|
|
|
764
779
|
# opt
|
|
@@ -768,6 +783,9 @@ class FEMOpt:
|
|
|
768
783
|
self.opt.history = self.history
|
|
769
784
|
self.opt._setup_before_parallel()
|
|
770
785
|
|
|
786
|
+
buff = self.opt.fem
|
|
787
|
+
del self.opt.fem
|
|
788
|
+
|
|
771
789
|
# クラスターでの計算開始
|
|
772
790
|
self.status.set(OptimizationStatus.LAUNCHING_FEM)
|
|
773
791
|
start = time()
|
|
@@ -780,15 +798,17 @@ class FEMOpt:
|
|
|
780
798
|
allow_other_workers=False,
|
|
781
799
|
)
|
|
782
800
|
|
|
801
|
+
self.opt.fem = buff
|
|
802
|
+
|
|
783
803
|
t_main = None
|
|
784
804
|
if not self.opt.is_cluster:
|
|
785
|
-
# ローカルプロセスでの計算(opt.
|
|
805
|
+
# ローカルプロセスでの計算(opt._run 相当の処理)
|
|
786
806
|
subprocess_idx = 0
|
|
787
807
|
|
|
788
808
|
# set_fem
|
|
789
809
|
self.opt.fem = self.fem
|
|
790
|
-
self.opt._reconstruct_fem(skip_reconstruct=True)
|
|
791
810
|
|
|
811
|
+
# fem の _setup_after_parallel はこの場合も呼ばれる
|
|
792
812
|
t_main = Thread(
|
|
793
813
|
target=self.opt._run,
|
|
794
814
|
args=(
|
|
@@ -797,7 +817,7 @@ class FEMOpt:
|
|
|
797
817
|
wait_setup,
|
|
798
818
|
),
|
|
799
819
|
kwargs=dict(
|
|
800
|
-
|
|
820
|
+
skip_reconstruct=True,
|
|
801
821
|
)
|
|
802
822
|
)
|
|
803
823
|
t_main.start()
|
|
@@ -10,6 +10,21 @@ from pyfemtet.opt import FEMOpt
|
|
|
10
10
|
from pyfemtet._message import encoding as ENCODING
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def remove_femprj_metadata_from_csv(csv_path, encoding=ENCODING):
|
|
14
|
+
|
|
15
|
+
with open(csv_path, mode="r", encoding=encoding, newline="\n") as f:
|
|
16
|
+
reader = csv.reader(f, delimiter=",")
|
|
17
|
+
data = [line for line in reader]
|
|
18
|
+
|
|
19
|
+
new_meta_data = data[0]
|
|
20
|
+
new_meta_data[0] = ""
|
|
21
|
+
data[0] = new_meta_data
|
|
22
|
+
|
|
23
|
+
with open(csv_path, mode="w", encoding=encoding, newline="\n") as f:
|
|
24
|
+
writer = csv.writer(f, delimiter=',')
|
|
25
|
+
writer.writerows(data)
|
|
26
|
+
|
|
27
|
+
|
|
13
28
|
def find_latest_csv(dir_path=None):
|
|
14
29
|
if dir_path is None:
|
|
15
30
|
dir_path = ""
|
|
@@ -70,3 +85,38 @@ def is_equal_result(ref_path, dif_path, log_path):
|
|
|
70
85
|
assert len(ref_columns) == len(dif_columns), "結果 csv の column 数が異なります。"
|
|
71
86
|
assert len(ref_df) == len(dif_df), "結果 csv の row 数が異なります。"
|
|
72
87
|
assert difference <= 0.05, "前回の結果との平均差異が 5% を超えています。"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _get_simplified_df_values(csv_path, exclude_columns=None):
|
|
91
|
+
exclude_columns = exclude_columns if exclude_columns is not None else []
|
|
92
|
+
|
|
93
|
+
with open(csv_path, 'r', encoding='cp932') as f:
|
|
94
|
+
meta_header = f.readline()
|
|
95
|
+
meta_header = 'removed' + meta_header.split('}"')[-1]
|
|
96
|
+
meta_header = meta_header.split(',')
|
|
97
|
+
|
|
98
|
+
df = pd.read_csv(csv_path, encoding='cp932', header=2)
|
|
99
|
+
|
|
100
|
+
prm_names = []
|
|
101
|
+
for meta_data, col in zip(meta_header, df.columns):
|
|
102
|
+
if meta_data == 'prm':
|
|
103
|
+
if col not in exclude_columns:
|
|
104
|
+
prm_names.append(col)
|
|
105
|
+
|
|
106
|
+
obj_names = []
|
|
107
|
+
for meta_data, col in zip(meta_header, df.columns):
|
|
108
|
+
if meta_data == 'obj':
|
|
109
|
+
if col not in exclude_columns:
|
|
110
|
+
obj_names.append(col)
|
|
111
|
+
|
|
112
|
+
pdf = pd.DataFrame()
|
|
113
|
+
|
|
114
|
+
for col in prm_names:
|
|
115
|
+
pdf[col] = df[col]
|
|
116
|
+
|
|
117
|
+
for col in obj_names:
|
|
118
|
+
pdf[col] = df[col]
|
|
119
|
+
|
|
120
|
+
return pdf.values.astype(float)
|
|
121
|
+
|
|
122
|
+
|
pyfemtet/opt/interface/_base.py
CHANGED
|
@@ -51,6 +51,12 @@ class FEMInterface(ABC):
|
|
|
51
51
|
"""
|
|
52
52
|
pass
|
|
53
53
|
|
|
54
|
+
def load_parameter(self, opt) -> None: # opt: AbstractOptimizer
|
|
55
|
+
raise NotImplementedError()
|
|
56
|
+
|
|
57
|
+
def load_objective(self, opt) -> None: # opt: AbstractOptimizer
|
|
58
|
+
raise NotImplementedError()
|
|
59
|
+
|
|
54
60
|
def _setup_before_parallel(self, client) -> None:
|
|
55
61
|
"""Preprocessing before launching a dask worker (if implemented in concrete class).
|
|
56
62
|
|
|
@@ -64,7 +70,7 @@ class FEMInterface(ABC):
|
|
|
64
70
|
"""
|
|
65
71
|
pass
|
|
66
72
|
|
|
67
|
-
def _setup_after_parallel(self):
|
|
73
|
+
def _setup_after_parallel(self, *args, **kwargs):
|
|
68
74
|
"""Preprocessing after launching a dask worker and before run optimization (if implemented in concrete class)."""
|
|
69
75
|
pass
|
|
70
76
|
|
|
@@ -74,6 +80,9 @@ class FEMInterface(ABC):
|
|
|
74
80
|
def _create_postprocess_args(self):
|
|
75
81
|
pass
|
|
76
82
|
|
|
83
|
+
def quit(self):
|
|
84
|
+
pass
|
|
85
|
+
|
|
77
86
|
|
|
78
87
|
class NoFEM(FEMInterface):
|
|
79
88
|
"""Dummy interface without FEM. Intended for debugging purposes."""
|