pyfemtet 0.6.1__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/_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(f"The function '{func.__name__}' is experimental and may change in the future.",
9
- category=UserWarning,
10
- stacklevel=2)
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
@@ -1,7 +1,7 @@
1
1
  # built-in
2
2
  import inspect
3
3
  import warnings
4
- from typing import Optional, Any, Callable, List
4
+ from typing import Optional, Any, Callable, List, Sequence, SupportsFloat
5
5
  import os
6
6
  import datetime
7
7
  from time import time, sleep
@@ -365,10 +365,10 @@ class FEMOpt:
365
365
 
366
366
  def add_objectives(
367
367
  self,
368
- fun: Callable[[Any], "Sequence"["SupportsFloat"]],
368
+ fun: Callable[[Any], Sequence[SupportsFloat]],
369
369
  n_return: int,
370
- names: str or "Sequence"[str] or None = None,
371
- directions: str or "Sequence"[str] or None = None,
370
+ names: str or Sequence[str] or None = None,
371
+ directions: str or Sequence[str] or None = None,
372
372
  args: tuple or None = None,
373
373
  kwargs: dict or None = None,
374
374
  ):
@@ -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
- # resolve expression dependencies
638
- self.opt.variables.resolve()
639
- self.opt.variables.evaluate()
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._main 相当の処理)
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
- skip_set_fem=True,
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
+
@@ -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."""