pyfemtet 0.3.12__py3-none-any.whl → 0.4.1__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/FemtetPJTSample/NX_ex01/NX_ex01.py +1 -1
- pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.py +1 -1
- pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +1 -1
- pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/her_ex40_parametric.py +1 -1
- pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +1 -1
- pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +1 -1
- pyfemtet/__init__.py +1 -1
- pyfemtet/core.py +14 -0
- pyfemtet/dispatch_extensions.py +5 -0
- pyfemtet/opt/__init__.py +22 -2
- pyfemtet/opt/_femopt.py +544 -0
- pyfemtet/opt/_femopt_core.py +730 -0
- pyfemtet/opt/interface/__init__.py +15 -0
- pyfemtet/opt/interface/_base.py +71 -0
- pyfemtet/opt/{interface.py → interface/_femtet.py} +120 -407
- pyfemtet/opt/interface/_femtet_with_nx/__init__.py +3 -0
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py +128 -0
- pyfemtet/opt/interface/_femtet_with_sldworks.py +174 -0
- pyfemtet/opt/opt/__init__.py +8 -0
- pyfemtet/opt/opt/_base.py +202 -0
- pyfemtet/opt/opt/_optuna.py +240 -0
- pyfemtet/opt/visualization/__init__.py +7 -0
- pyfemtet/opt/visualization/_graphs.py +222 -0
- pyfemtet/opt/visualization/_monitor.py +1149 -0
- {pyfemtet-0.3.12.dist-info → pyfemtet-0.4.1.dist-info}/METADATA +4 -4
- pyfemtet-0.4.1.dist-info/RECORD +38 -0
- {pyfemtet-0.3.12.dist-info → pyfemtet-0.4.1.dist-info}/WHEEL +1 -1
- pyfemtet-0.4.1.dist-info/entry_points.txt +3 -0
- pyfemtet/opt/base.py +0 -1490
- pyfemtet/opt/monitor.py +0 -474
- pyfemtet-0.3.12.dist-info/RECORD +0 -26
- /pyfemtet/opt/{_FemtetWithNX → interface/_femtet_with_nx}/update_model.py +0 -0
- {pyfemtet-0.3.12.dist-info → pyfemtet-0.4.1.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# typing
|
|
2
|
+
from typing import Iterable
|
|
3
|
+
|
|
4
|
+
# built-in
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
# 3rd-party
|
|
8
|
+
import numpy as np
|
|
9
|
+
import optuna
|
|
10
|
+
from optuna.trial import TrialState
|
|
11
|
+
from optuna.study import MaxTrialsCallback
|
|
12
|
+
|
|
13
|
+
# pyfemtet relative
|
|
14
|
+
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
15
|
+
from pyfemtet.opt.opt import AbstractOptimizer, logger
|
|
16
|
+
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
17
|
+
|
|
18
|
+
# filter warnings
|
|
19
|
+
import warnings
|
|
20
|
+
from optuna.exceptions import ExperimentalWarning
|
|
21
|
+
warnings.filterwarnings('ignore', category=ExperimentalWarning)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class OptunaOptimizer(AbstractOptimizer):
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
sampler_class: optuna.samplers.BaseSampler or None = None,
|
|
29
|
+
sampler_kwargs: dict or None = None,
|
|
30
|
+
add_init_method: str or Iterable[str] or None = None
|
|
31
|
+
):
|
|
32
|
+
super().__init__()
|
|
33
|
+
self.study_name = None
|
|
34
|
+
self.storage = None
|
|
35
|
+
self.study = None
|
|
36
|
+
self.optimize_callbacks = []
|
|
37
|
+
self.sampler_class = optuna.samplers.TPESampler if sampler_class is None else sampler_class
|
|
38
|
+
self.sampler_kwargs = dict() if sampler_kwargs is None else sampler_kwargs
|
|
39
|
+
self.additional_initial_parameter = []
|
|
40
|
+
self.additional_initial_methods = add_init_method if hasattr(add_init_method, '__iter__') else [add_init_method]
|
|
41
|
+
|
|
42
|
+
def _objective(self, trial):
|
|
43
|
+
|
|
44
|
+
# 中断の確認 (FAIL loop に陥る対策)
|
|
45
|
+
if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
46
|
+
self.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
47
|
+
trial.study.stop() # 現在実行中の trial を最後にする
|
|
48
|
+
return None # set TrialState FAIL
|
|
49
|
+
|
|
50
|
+
# candidate x
|
|
51
|
+
x = []
|
|
52
|
+
for i, row in self.parameters.iterrows():
|
|
53
|
+
v = trial.suggest_float(row['name'], row['lb'], row['ub'])
|
|
54
|
+
x.append(v)
|
|
55
|
+
x = np.array(x).astype(float)
|
|
56
|
+
|
|
57
|
+
# message の設定
|
|
58
|
+
self.message = trial.user_attrs['message'] if 'message' in trial.user_attrs.keys() else ''
|
|
59
|
+
|
|
60
|
+
# fem や opt 経由で変数を取得して constraint を計算する時のためにアップデート
|
|
61
|
+
self.parameters['value'] = x
|
|
62
|
+
self.fem.update_parameter(self.parameters)
|
|
63
|
+
|
|
64
|
+
# strict 拘束
|
|
65
|
+
strict_constraints = [cns for cns in self.constraints.values() if cns.strict]
|
|
66
|
+
for cns in strict_constraints:
|
|
67
|
+
feasible = True
|
|
68
|
+
cns_value = cns.calc(self.fem)
|
|
69
|
+
if cns.lb is not None:
|
|
70
|
+
feasible = feasible and (cns_value >= cns.lb)
|
|
71
|
+
if cns.ub is not None:
|
|
72
|
+
feasible = feasible and (cns.ub >= cns_value)
|
|
73
|
+
if not feasible:
|
|
74
|
+
logger.info(f'以下の変数で拘束 {cns.name} が満たされませんでした。')
|
|
75
|
+
print(self.get_parameter('dict'))
|
|
76
|
+
raise optuna.TrialPruned() # set TrialState PRUNED because FAIL causes similar candidate loop.
|
|
77
|
+
|
|
78
|
+
# 計算
|
|
79
|
+
try:
|
|
80
|
+
_, _y, c = self.f(x)
|
|
81
|
+
except (ModelError, MeshError, SolveError) as e:
|
|
82
|
+
logger.info(e)
|
|
83
|
+
logger.info('以下の変数で FEM 解析に失敗しました。')
|
|
84
|
+
print(self.get_parameter('dict'))
|
|
85
|
+
|
|
86
|
+
# 中断の確認 (解析中に interrupt されている場合対策)
|
|
87
|
+
if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
88
|
+
self.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
89
|
+
trial.study.stop() # 現在実行中の trial を最後にする
|
|
90
|
+
return None # set TrialState FAIL
|
|
91
|
+
|
|
92
|
+
raise optuna.TrialPruned() # set TrialState PRUNED because FAIL causes similar candidate loop.
|
|
93
|
+
|
|
94
|
+
# 拘束 attr の更新
|
|
95
|
+
_c = [] # 非正なら OK
|
|
96
|
+
for (name, cns), c_value in zip(self.constraints.items(), c):
|
|
97
|
+
lb, ub = cns.lb, cns.ub
|
|
98
|
+
if lb is not None: # fun >= lb <=> lb - fun <= 0
|
|
99
|
+
_c.append(lb - c_value)
|
|
100
|
+
if ub is not None: # ub >= fun <=> fun - ub <= 0
|
|
101
|
+
_c.append(c_value - ub)
|
|
102
|
+
trial.set_user_attr('constraint', _c)
|
|
103
|
+
|
|
104
|
+
# 中断の確認 (解析中に interrupt されている場合対策)
|
|
105
|
+
if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
106
|
+
self.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
107
|
+
trial.study.stop() # 現在実行中の trial を最後にする
|
|
108
|
+
return None # set TrialState FAIL
|
|
109
|
+
|
|
110
|
+
# 結果
|
|
111
|
+
return tuple(_y)
|
|
112
|
+
|
|
113
|
+
def _constraint(self, trial):
|
|
114
|
+
return trial.user_attrs['constraint'] if 'constraint' in trial.user_attrs.keys() else (1,) # infeasible
|
|
115
|
+
|
|
116
|
+
def _setup_before_parallel(self):
|
|
117
|
+
"""Create storage, study and set initial parameter."""
|
|
118
|
+
|
|
119
|
+
# create storage
|
|
120
|
+
self.study_name = os.path.basename(self.history.path)
|
|
121
|
+
storage_path = self.history.path.replace('.csv', '.db') # history と同じところに保存
|
|
122
|
+
if self.is_cluster: # remote cluster なら scheduler の working dir に保存
|
|
123
|
+
storage_path = os.path.basename(self.history.path).replace('.csv', '.db')
|
|
124
|
+
|
|
125
|
+
# callback to terminate
|
|
126
|
+
if self.n_trials is not None:
|
|
127
|
+
n_trials = self.n_trials
|
|
128
|
+
|
|
129
|
+
# restart である場合、追加 N 回と見做す
|
|
130
|
+
if self.history.is_restart:
|
|
131
|
+
n_existing_trials = len(self.history.actor_data)
|
|
132
|
+
n_trials += n_existing_trials
|
|
133
|
+
|
|
134
|
+
self.optimize_callbacks.append(MaxTrialsCallback(n_trials, states=(TrialState.COMPLETE,)))
|
|
135
|
+
|
|
136
|
+
# if not restart, create study if storage is not exists
|
|
137
|
+
if not self.history.is_restart:
|
|
138
|
+
|
|
139
|
+
self.storage = optuna.integration.dask.DaskStorage(
|
|
140
|
+
f'sqlite:///{storage_path}',
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
self.study = optuna.create_study(
|
|
144
|
+
study_name=self.study_name,
|
|
145
|
+
storage=self.storage,
|
|
146
|
+
load_if_exists=True,
|
|
147
|
+
directions=['minimize'] * len(self.objectives),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# 初期値の設定
|
|
151
|
+
if len(self.study.trials) == 0: # リスタートでなければ
|
|
152
|
+
# ユーザーの指定した初期値
|
|
153
|
+
params = self.get_parameter('dict')
|
|
154
|
+
self.study.enqueue_trial(params, user_attrs={"message": "initial"})
|
|
155
|
+
|
|
156
|
+
# add_initial_parameter で追加された初期値
|
|
157
|
+
for prm, prm_set_name in self.additional_initial_parameter:
|
|
158
|
+
self.study.enqueue_trial(
|
|
159
|
+
prm,
|
|
160
|
+
user_attrs={"message": prm_set_name}
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# add_init で指定された方法による初期値
|
|
164
|
+
if 'LHS' in self.additional_initial_methods:
|
|
165
|
+
names = []
|
|
166
|
+
bounds = []
|
|
167
|
+
for i, row in self.parameters.iterrows():
|
|
168
|
+
names.append(row['name'])
|
|
169
|
+
lb = row['lb']
|
|
170
|
+
ub = row['ub']
|
|
171
|
+
bounds.append([lb, ub])
|
|
172
|
+
data = generate_lhs(bounds, seed=self.seed)
|
|
173
|
+
for datum in data:
|
|
174
|
+
d = {}
|
|
175
|
+
for name, v in zip(names, datum):
|
|
176
|
+
d[name] = v
|
|
177
|
+
self.study.enqueue_trial(
|
|
178
|
+
d, user_attrs={"message": "additional initial (Latin Hypercube Sampling)"}
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# if is_restart, load study
|
|
182
|
+
else:
|
|
183
|
+
if not os.path.exists(storage_path):
|
|
184
|
+
msg = f'{storage_path} が見つかりません。'
|
|
185
|
+
msg += '.db ファイルは .csv ファイルと同じフォルダに生成されます。'
|
|
186
|
+
msg += 'クラスター解析の場合は、スケジューラを起動したフォルダに生成されます。'
|
|
187
|
+
raise FileNotFoundError(msg)
|
|
188
|
+
self.storage = optuna.integration.dask.DaskStorage(
|
|
189
|
+
f'sqlite:///{storage_path}',
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def add_init_parameter(
|
|
193
|
+
self,
|
|
194
|
+
parameter: dict or Iterable,
|
|
195
|
+
name: str or None = None,
|
|
196
|
+
):
|
|
197
|
+
"""Add additional initial parameter for evaluate.
|
|
198
|
+
|
|
199
|
+
The parameter set is ignored if the main() is continued.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
parameter (dict or Iterable): Parameter to evaluate before run optimization algorithm.
|
|
203
|
+
name (str or None): Optional. If specified, the name is saved in the history row. Default to None.
|
|
204
|
+
|
|
205
|
+
"""
|
|
206
|
+
if name is None:
|
|
207
|
+
name = 'additional initial'
|
|
208
|
+
else:
|
|
209
|
+
name = f'additional initial ({name})'
|
|
210
|
+
self.additional_initial_parameter.append([parameter, name])
|
|
211
|
+
|
|
212
|
+
def run(self):
|
|
213
|
+
"""Set random seed, sampler, study and run study.optimize()."""
|
|
214
|
+
|
|
215
|
+
# (re)set random seed
|
|
216
|
+
seed = self.seed
|
|
217
|
+
if seed is not None:
|
|
218
|
+
if self.subprocess_idx is not None:
|
|
219
|
+
seed += self.subprocess_idx
|
|
220
|
+
|
|
221
|
+
# restore sampler
|
|
222
|
+
sampler = self.sampler_class(
|
|
223
|
+
seed=seed,
|
|
224
|
+
constraints_func=self._constraint,
|
|
225
|
+
**self.sampler_kwargs
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# load study
|
|
229
|
+
study = optuna.load_study(
|
|
230
|
+
study_name=self.study_name,
|
|
231
|
+
storage=self.storage,
|
|
232
|
+
sampler=sampler,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# run
|
|
236
|
+
study.optimize(
|
|
237
|
+
self._objective,
|
|
238
|
+
timeout=self.timeout,
|
|
239
|
+
callbacks=self.optimize_callbacks,
|
|
240
|
+
)
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import plotly.graph_objs as go
|
|
2
|
+
import plotly.express as px
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
_CUSTOM_DATA_DICT = {'trial': 0} # 連番
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class _ColorSet:
|
|
9
|
+
non_domi = {True: '#007bff', False: '#6c757d'} # color
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _SymbolSet:
|
|
13
|
+
feasible = {True: 'circle', False: 'circle-open'} # style
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _LanguageSet:
|
|
17
|
+
|
|
18
|
+
feasible = {'label': 'feasible', True: True, False: False}
|
|
19
|
+
non_domi = {'label': 'non_domi', True: True, False: False}
|
|
20
|
+
|
|
21
|
+
def __init__(self, language: str = 'ja'):
|
|
22
|
+
self.lang = language
|
|
23
|
+
if self.lang.lower() == 'ja':
|
|
24
|
+
self.feasible = {'label': '拘束条件', True: '満足', False: '違反'}
|
|
25
|
+
self.non_domi = {'label': '最適性', True: '非劣解', False: '劣解'}
|
|
26
|
+
|
|
27
|
+
def localize(self, df):
|
|
28
|
+
# 元のオブジェクトを変更しないようにコピー
|
|
29
|
+
cdf = df.copy()
|
|
30
|
+
|
|
31
|
+
# feasible, non_domi の localize
|
|
32
|
+
cdf[self.feasible['label']] = [self.feasible[v] for v in cdf['feasible']]
|
|
33
|
+
cdf[self.non_domi['label']] = [self.non_domi[v] for v in cdf['non_domi']]
|
|
34
|
+
|
|
35
|
+
return cdf
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
_ls = _LanguageSet('ja')
|
|
39
|
+
_cs = _ColorSet()
|
|
40
|
+
_ss = _SymbolSet()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def update_hypervolume_plot(history, df):
|
|
44
|
+
df = _ls.localize(df)
|
|
45
|
+
|
|
46
|
+
# create figure
|
|
47
|
+
fig = px.line(
|
|
48
|
+
df,
|
|
49
|
+
x="trial",
|
|
50
|
+
y="hypervolume",
|
|
51
|
+
markers=True,
|
|
52
|
+
custom_data=_CUSTOM_DATA_DICT.keys(),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
fig.update_layout(
|
|
56
|
+
dict(
|
|
57
|
+
title_text="ハイパーボリュームプロット",
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return fig
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def update_default_figure(history, df):
|
|
65
|
+
|
|
66
|
+
# data setting
|
|
67
|
+
obj_names = history.obj_names
|
|
68
|
+
|
|
69
|
+
if len(obj_names) == 0:
|
|
70
|
+
return go.Figure()
|
|
71
|
+
|
|
72
|
+
elif len(obj_names) == 1:
|
|
73
|
+
return update_single_objective_plot(history, df)
|
|
74
|
+
|
|
75
|
+
elif len(obj_names) >= 2:
|
|
76
|
+
return update_multi_objective_pairplot(history, df)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def update_single_objective_plot(history, df):
|
|
80
|
+
|
|
81
|
+
df = _ls.localize(df)
|
|
82
|
+
obj_name = history.obj_names[0]
|
|
83
|
+
|
|
84
|
+
fig = px.scatter(
|
|
85
|
+
df,
|
|
86
|
+
x='trial',
|
|
87
|
+
y=obj_name,
|
|
88
|
+
symbol=_ls.feasible['label'],
|
|
89
|
+
symbol_map={
|
|
90
|
+
_ls.feasible[True]: _ss.feasible[True],
|
|
91
|
+
_ls.feasible[False]: _ss.feasible[False],
|
|
92
|
+
},
|
|
93
|
+
hover_data={
|
|
94
|
+
_ls.feasible['label']: False,
|
|
95
|
+
'trial': True,
|
|
96
|
+
},
|
|
97
|
+
custom_data=_CUSTOM_DATA_DICT.keys(),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
fig.add_trace(
|
|
101
|
+
go.Scatter(
|
|
102
|
+
x=df['trial'],
|
|
103
|
+
y=df[obj_name],
|
|
104
|
+
mode="lines",
|
|
105
|
+
line=go.scatter.Line(
|
|
106
|
+
width=0.5,
|
|
107
|
+
color='#6c757d',
|
|
108
|
+
),
|
|
109
|
+
showlegend=False
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
fig.update_layout(
|
|
114
|
+
dict(
|
|
115
|
+
title_text="目的プロット",
|
|
116
|
+
xaxis_title="解析実行回数(回)",
|
|
117
|
+
yaxis_title=obj_name,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return fig
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def update_multi_objective_pairplot(history, df):
|
|
125
|
+
df = _ls.localize(df)
|
|
126
|
+
|
|
127
|
+
obj_names = history.obj_names
|
|
128
|
+
|
|
129
|
+
common_kwargs = dict(
|
|
130
|
+
color=_ls.non_domi['label'],
|
|
131
|
+
color_discrete_map={
|
|
132
|
+
_ls.non_domi[True]: _cs.non_domi[True],
|
|
133
|
+
_ls.non_domi[False]: _cs.non_domi[False],
|
|
134
|
+
},
|
|
135
|
+
symbol=_ls.feasible['label'],
|
|
136
|
+
symbol_map={
|
|
137
|
+
_ls.feasible[True]: _ss.feasible[True],
|
|
138
|
+
_ls.feasible[False]: _ss.feasible[False],
|
|
139
|
+
},
|
|
140
|
+
hover_data={
|
|
141
|
+
_ls.feasible['label']: False,
|
|
142
|
+
'trial': True,
|
|
143
|
+
},
|
|
144
|
+
custom_data=_CUSTOM_DATA_DICT.keys(),
|
|
145
|
+
category_orders={
|
|
146
|
+
_ls.feasible['label']: (_ls.feasible[False], _ls.feasible[True]),
|
|
147
|
+
_ls.non_domi['label']: (_ls.non_domi[False], _ls.non_domi[True]),
|
|
148
|
+
},
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if len(obj_names) == 2:
|
|
152
|
+
fig = px.scatter(
|
|
153
|
+
data_frame=df,
|
|
154
|
+
x=obj_names[0],
|
|
155
|
+
y=obj_names[1],
|
|
156
|
+
**common_kwargs,
|
|
157
|
+
)
|
|
158
|
+
fig.update_layout(
|
|
159
|
+
dict(
|
|
160
|
+
xaxis_title=obj_names[0],
|
|
161
|
+
yaxis_title=obj_names[1],
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
else:
|
|
166
|
+
fig = px.scatter_matrix(
|
|
167
|
+
data_frame=df,
|
|
168
|
+
dimensions=obj_names,
|
|
169
|
+
**common_kwargs,
|
|
170
|
+
)
|
|
171
|
+
fig.update_traces(
|
|
172
|
+
patch={'diagonal.visible': False},
|
|
173
|
+
showupperhalf=False,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
fig.update_layout(
|
|
177
|
+
dict(
|
|
178
|
+
title_text="多目的ペアプロット",
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return fig
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _debug():
|
|
186
|
+
import os
|
|
187
|
+
|
|
188
|
+
os.chdir(os.path.dirname(__file__))
|
|
189
|
+
csv_path = 'sample.csv'
|
|
190
|
+
|
|
191
|
+
show_static_monitor(csv_path)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def show_static_monitor(csv_path):
|
|
195
|
+
from pyfemtet.opt._femopt_core import History
|
|
196
|
+
from pyfemtet.opt.visualization._monitor import ResultViewerApp
|
|
197
|
+
_h = History(history_path=csv_path)
|
|
198
|
+
_monitor = ResultViewerApp(history=_h)
|
|
199
|
+
_monitor.run()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def entry_point():
|
|
203
|
+
import argparse
|
|
204
|
+
parser = argparse.ArgumentParser()
|
|
205
|
+
|
|
206
|
+
parser.add_argument('csv_path', help='pyfemtet を実行した結果の csv ファイルのパスを指定してください。', type=str)
|
|
207
|
+
|
|
208
|
+
# parser.add_argument(
|
|
209
|
+
# "-c",
|
|
210
|
+
# "--csv-path",
|
|
211
|
+
# help="pyfemtet.opt による最適化結果 csv ファイルパス",
|
|
212
|
+
# type=str,
|
|
213
|
+
# )
|
|
214
|
+
|
|
215
|
+
args = parser.parse_args()
|
|
216
|
+
|
|
217
|
+
if args.csv_path:
|
|
218
|
+
show_static_monitor(args.csv_path)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if __name__ == '__main__':
|
|
222
|
+
_debug()
|