pyfemtet 0.4.11__py3-none-any.whl → 0.4.12__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/_test_util.py +26 -0
- pyfemtet/opt/__init__.py +1 -1
- pyfemtet/opt/_femopt.py +32 -2
- pyfemtet/opt/interface/_base.py +6 -1
- pyfemtet/opt/opt/__init__.py +3 -1
- pyfemtet/opt/opt/_base.py +79 -7
- pyfemtet/opt/opt/_optuna.py +13 -2
- pyfemtet/opt/opt/_scipy.py +144 -0
- pyfemtet/opt/opt/_scipy_scalar.py +104 -0
- {pyfemtet-0.4.11.dist-info → pyfemtet-0.4.12.dist-info}/METADATA +1 -1
- {pyfemtet-0.4.11.dist-info → pyfemtet-0.4.12.dist-info}/RECORD +15 -13
- {pyfemtet-0.4.11.dist-info → pyfemtet-0.4.12.dist-info}/LICENSE +0 -0
- {pyfemtet-0.4.11.dist-info → pyfemtet-0.4.12.dist-info}/WHEEL +0 -0
- {pyfemtet-0.4.11.dist-info → pyfemtet-0.4.12.dist-info}/entry_points.txt +0 -0
pyfemtet/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.4.
|
|
1
|
+
__version__ = "0.4.12"
|
pyfemtet/_test_util.py
CHANGED
|
@@ -15,6 +15,32 @@ from femtetutils import util
|
|
|
15
15
|
from pyfemtet.opt import FEMOpt
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
class SuperSphere:
|
|
19
|
+
def __init__(self, n):
|
|
20
|
+
self.n = n
|
|
21
|
+
|
|
22
|
+
def x(self, radius, *angles):
|
|
23
|
+
assert len(angles) == self.n - 1, 'invalid angles length'
|
|
24
|
+
|
|
25
|
+
out = []
|
|
26
|
+
|
|
27
|
+
for i in range(self.n):
|
|
28
|
+
if i == 0:
|
|
29
|
+
out.append(radius * np.cos(angles[0]))
|
|
30
|
+
elif i < self.n - 1:
|
|
31
|
+
product = radius
|
|
32
|
+
for j in range(i):
|
|
33
|
+
product *= np.sin(angles[j])
|
|
34
|
+
product *= np.cos(angles[i])
|
|
35
|
+
out.append(product)
|
|
36
|
+
else:
|
|
37
|
+
product = radius
|
|
38
|
+
for j in range(i):
|
|
39
|
+
product *= np.sin(angles[j])
|
|
40
|
+
out.append(product)
|
|
41
|
+
return out
|
|
42
|
+
|
|
43
|
+
|
|
18
44
|
def _open_femprj(femprj_path):
|
|
19
45
|
Femtet = Dispatch('FemtetMacro.Femtet')
|
|
20
46
|
for _ in tqdm(range(5), 'wait for dispatch Femtet'):
|
pyfemtet/opt/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@ from pyfemtet.opt.interface import FemtetInterface
|
|
|
4
4
|
from pyfemtet.opt.interface import FemtetWithNXInterface
|
|
5
5
|
from pyfemtet.opt.interface import FemtetWithSolidworksInterface
|
|
6
6
|
|
|
7
|
-
from pyfemtet.opt.opt import OptunaOptimizer
|
|
7
|
+
from pyfemtet.opt.opt import OptunaOptimizer, ScipyOptimizer
|
|
8
8
|
from pyfemtet.opt.opt import AbstractOptimizer
|
|
9
9
|
|
|
10
10
|
from pyfemtet.opt._femopt import FEMOpt
|
pyfemtet/opt/_femopt.py
CHANGED
|
@@ -107,6 +107,7 @@ class FEMOpt:
|
|
|
107
107
|
initial_value: float or None = None,
|
|
108
108
|
lower_bound: float or None = None,
|
|
109
109
|
upper_bound: float or None = None,
|
|
110
|
+
step: float or None = None,
|
|
110
111
|
memo: str = ''
|
|
111
112
|
):
|
|
112
113
|
"""Adds a parameter to the optimization problem.
|
|
@@ -133,8 +134,9 @@ class FEMOpt:
|
|
|
133
134
|
d = {
|
|
134
135
|
'name': name,
|
|
135
136
|
'value': float(initial_value),
|
|
136
|
-
'lb': float(lower_bound),
|
|
137
|
-
'ub': float(upper_bound),
|
|
137
|
+
'lb': float(lower_bound) if lower_bound is not None else None,
|
|
138
|
+
'ub': float(upper_bound) if upper_bound is not None else None,
|
|
139
|
+
'step': float(step) if step is not None else None,
|
|
138
140
|
'memo': memo,
|
|
139
141
|
}
|
|
140
142
|
pdf = pd.DataFrame(d, index=[0], dtype=object)
|
|
@@ -314,6 +316,34 @@ class FEMOpt:
|
|
|
314
316
|
|
|
315
317
|
"""
|
|
316
318
|
|
|
319
|
+
# method checker
|
|
320
|
+
if n_parallel > 1:
|
|
321
|
+
self.opt.method_checker.check_parallel()
|
|
322
|
+
|
|
323
|
+
if timeout is not None:
|
|
324
|
+
self.opt.method_checker.check_timeout()
|
|
325
|
+
|
|
326
|
+
if len(self.opt.objectives) > 1:
|
|
327
|
+
self.opt.method_checker.check_multi_objective()
|
|
328
|
+
|
|
329
|
+
if len(self.opt.constraints) > 0:
|
|
330
|
+
self.opt.method_checker.check_constraint()
|
|
331
|
+
|
|
332
|
+
for key, value in self.opt.constraints.items():
|
|
333
|
+
if value.strict:
|
|
334
|
+
self.opt.method_checker.check_strict_constraint()
|
|
335
|
+
break
|
|
336
|
+
|
|
337
|
+
if self.opt.seed is not None:
|
|
338
|
+
self.opt.method_checker.check_seed()
|
|
339
|
+
|
|
340
|
+
is_incomplete_bounds = False
|
|
341
|
+
for _, row in self.opt.parameters.iterrows():
|
|
342
|
+
lb, ub = row['lb'], row['ub']
|
|
343
|
+
is_incomplete_bounds = is_incomplete_bounds + (lb is None) + (ub is None)
|
|
344
|
+
if is_incomplete_bounds:
|
|
345
|
+
self.opt.method_checker.check_incomplete_bounds()
|
|
346
|
+
|
|
317
347
|
# 共通引数
|
|
318
348
|
self.opt.n_trials = n_trials
|
|
319
349
|
self.opt.timeout = timeout
|
pyfemtet/opt/interface/_base.py
CHANGED
|
@@ -43,7 +43,12 @@ class FEMInterface(ABC):
|
|
|
43
43
|
pass
|
|
44
44
|
|
|
45
45
|
def update_parameter(self, parameters: pd.DataFrame, with_warning=False) -> Optional[List[str]]:
|
|
46
|
-
"""Updates only FEM variables (if implemented in concrete class).
|
|
46
|
+
"""Updates only FEM variables (if implemented in concrete class).
|
|
47
|
+
|
|
48
|
+
If this method is implemented,
|
|
49
|
+
it is able to get parameter via FEMInterface.
|
|
50
|
+
|
|
51
|
+
"""
|
|
47
52
|
pass
|
|
48
53
|
|
|
49
54
|
def _setup_before_parallel(self, client) -> None:
|
pyfemtet/opt/opt/__init__.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from pyfemtet.opt.opt._base import AbstractOptimizer, logger
|
|
1
|
+
from pyfemtet.opt.opt._base import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
2
2
|
from pyfemtet.opt.opt._optuna import OptunaOptimizer
|
|
3
|
+
from pyfemtet.opt.opt._scipy import ScipyOptimizer
|
|
3
4
|
|
|
4
5
|
__all__ = [
|
|
6
|
+
'ScipyOptimizer',
|
|
5
7
|
'OptunaOptimizer',
|
|
6
8
|
'AbstractOptimizer',
|
|
7
9
|
'logger',
|
pyfemtet/opt/opt/_base.py
CHANGED
|
@@ -20,6 +20,77 @@ logger = get_logger('opt')
|
|
|
20
20
|
logger.setLevel(logging.INFO)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
class OptimizationMethodChecker:
|
|
24
|
+
"""Check implementation of PyFemtet functions."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, opt):
|
|
27
|
+
self.opt = opt
|
|
28
|
+
|
|
29
|
+
def check_parallel(self, raise_error=True):
|
|
30
|
+
function = 'parallel computing'
|
|
31
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
32
|
+
if raise_error:
|
|
33
|
+
raise NotImplementedError(message)
|
|
34
|
+
else:
|
|
35
|
+
logger.warning(message)
|
|
36
|
+
|
|
37
|
+
def check_timeout(self, raise_error=True):
|
|
38
|
+
function = 'timeout'
|
|
39
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
40
|
+
if raise_error:
|
|
41
|
+
raise NotImplementedError(message)
|
|
42
|
+
else:
|
|
43
|
+
logger.warning(message)
|
|
44
|
+
|
|
45
|
+
def check_multi_objective(self, raise_error=True):
|
|
46
|
+
function = 'multi-objective'
|
|
47
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
48
|
+
if raise_error:
|
|
49
|
+
raise NotImplementedError(message)
|
|
50
|
+
else:
|
|
51
|
+
logger.warning(message)
|
|
52
|
+
|
|
53
|
+
def check_strict_constraint(self, raise_error=True):
|
|
54
|
+
function = 'strict_constraint'
|
|
55
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
56
|
+
if raise_error:
|
|
57
|
+
raise NotImplementedError(message)
|
|
58
|
+
else:
|
|
59
|
+
logger.warning(message)
|
|
60
|
+
|
|
61
|
+
def check_constraint(self, raise_error=True):
|
|
62
|
+
function = 'strict_constraint'
|
|
63
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
64
|
+
if raise_error:
|
|
65
|
+
raise NotImplementedError(message)
|
|
66
|
+
else:
|
|
67
|
+
logger.warning(message)
|
|
68
|
+
|
|
69
|
+
def check_skip(self, raise_error=True):
|
|
70
|
+
function = 'skip'
|
|
71
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
72
|
+
if raise_error:
|
|
73
|
+
raise NotImplementedError(message)
|
|
74
|
+
else:
|
|
75
|
+
logger.warning(message)
|
|
76
|
+
|
|
77
|
+
def check_seed(self, raise_error=True):
|
|
78
|
+
function = 'random seed setting'
|
|
79
|
+
message = f'{type(self.opt)} is not implement {function}'
|
|
80
|
+
if raise_error:
|
|
81
|
+
raise NotImplementedError(message)
|
|
82
|
+
else:
|
|
83
|
+
logger.warning(message)
|
|
84
|
+
|
|
85
|
+
def check_incomplete_bounds(self, raise_error=True):
|
|
86
|
+
function = 'optimize with no or incomplete bounds'
|
|
87
|
+
message = f'{type(self.opt)} is not implement "{function}"'
|
|
88
|
+
if raise_error:
|
|
89
|
+
raise NotImplementedError(message)
|
|
90
|
+
else:
|
|
91
|
+
logger.warning(message)
|
|
92
|
+
|
|
93
|
+
|
|
23
94
|
class AbstractOptimizer(ABC):
|
|
24
95
|
"""Abstract base class for an interface of optimization library.
|
|
25
96
|
|
|
@@ -45,9 +116,9 @@ class AbstractOptimizer(ABC):
|
|
|
45
116
|
self.fem = None
|
|
46
117
|
self.fem_class = None
|
|
47
118
|
self.fem_kwargs = dict()
|
|
48
|
-
self.parameters = pd.DataFrame()
|
|
49
|
-
self.objectives = dict()
|
|
50
|
-
self.constraints = dict()
|
|
119
|
+
self.parameters: pd.DataFrame = pd.DataFrame()
|
|
120
|
+
self.objectives: dict = dict()
|
|
121
|
+
self.constraints: dict = dict()
|
|
51
122
|
self.entire_status = None # actor
|
|
52
123
|
self.history = None # actor
|
|
53
124
|
self.worker_status = None # actor
|
|
@@ -58,6 +129,7 @@ class AbstractOptimizer(ABC):
|
|
|
58
129
|
self.is_cluster = False
|
|
59
130
|
self.subprocess_idx = None
|
|
60
131
|
self._is_error_exit = False
|
|
132
|
+
self.method_checker: OptimizationMethodChecker = OptimizationMethodChecker(self)
|
|
61
133
|
|
|
62
134
|
def f(self, x):
|
|
63
135
|
"""Get x, update fem analysis, return objectives (and constraints)."""
|
|
@@ -199,10 +271,10 @@ class AbstractOptimizer(ABC):
|
|
|
199
271
|
try:
|
|
200
272
|
self.run()
|
|
201
273
|
except Exception as e:
|
|
202
|
-
logger.error("
|
|
203
|
-
logger.error("An unexpected error has
|
|
204
|
-
logger.error("
|
|
205
|
-
logger.error(e)
|
|
274
|
+
logger.error("=================================")
|
|
275
|
+
logger.error("An unexpected error has occurred!")
|
|
276
|
+
logger.error("=================================")
|
|
277
|
+
logger.error(f'{type(e)}: {e}')
|
|
206
278
|
self._is_error_exit = True
|
|
207
279
|
self.worker_status.set(OptimizationStatus.CRASHED)
|
|
208
280
|
finally:
|
pyfemtet/opt/opt/_optuna.py
CHANGED
|
@@ -12,7 +12,7 @@ from optuna.study import MaxTrialsCallback
|
|
|
12
12
|
|
|
13
13
|
# pyfemtet relative
|
|
14
14
|
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
15
|
-
from pyfemtet.opt.opt import AbstractOptimizer, logger
|
|
15
|
+
from pyfemtet.opt.opt import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
16
16
|
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
17
17
|
|
|
18
18
|
# filter warnings
|
|
@@ -25,6 +25,16 @@ optuna.logging.set_verbosity(optuna.logging.ERROR)
|
|
|
25
25
|
warnings.filterwarnings('ignore', category=ExperimentalWarning)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class OptunaMethodChecker(OptimizationMethodChecker):
|
|
29
|
+
def check_multi_objective(self, raise_error=True): return True
|
|
30
|
+
def check_timeout(self, raise_error=True): return True
|
|
31
|
+
def check_parallel(self, raise_error=True): return True
|
|
32
|
+
def check_constraint(self, raise_error=True): return True
|
|
33
|
+
def check_strict_constraint(self, raise_error=True): return True
|
|
34
|
+
def check_skip(self, raise_error=True): return True
|
|
35
|
+
def check_seed(self, raise_error=True): return True
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
class OptunaOptimizer(AbstractOptimizer):
|
|
29
39
|
|
|
30
40
|
def __init__(
|
|
@@ -42,6 +52,7 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
42
52
|
self.sampler_kwargs = dict() if sampler_kwargs is None else sampler_kwargs
|
|
43
53
|
self.additional_initial_parameter = []
|
|
44
54
|
self.additional_initial_methods = add_init_method if hasattr(add_init_method, '__iter__') else [add_init_method]
|
|
55
|
+
self.method_checker = OptunaMethodChecker(self)
|
|
45
56
|
|
|
46
57
|
def _objective(self, trial):
|
|
47
58
|
|
|
@@ -54,7 +65,7 @@ class OptunaOptimizer(AbstractOptimizer):
|
|
|
54
65
|
# candidate x
|
|
55
66
|
x = []
|
|
56
67
|
for i, row in self.parameters.iterrows():
|
|
57
|
-
v = trial.suggest_float(row['name'], row['lb'], row['ub'])
|
|
68
|
+
v = trial.suggest_float(row['name'], row['lb'], row['ub'], step=row['step'])
|
|
58
69
|
x.append(v)
|
|
59
70
|
x = np.array(x).astype(float)
|
|
60
71
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# typing
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
|
|
5
|
+
# built-in
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
# 3rd-party
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
import scipy.optimize
|
|
12
|
+
from scipy.optimize import minimize, OptimizeResult
|
|
13
|
+
|
|
14
|
+
# pyfemtet relative
|
|
15
|
+
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
16
|
+
from pyfemtet.opt.opt import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
17
|
+
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StopIteration2(Exception):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class StopIterationCallback:
|
|
25
|
+
def __init__(self, opt):
|
|
26
|
+
self.opt: ScipyOptimizer = opt
|
|
27
|
+
self.res: OptimizeResult = None
|
|
28
|
+
|
|
29
|
+
def stop_iteration(self):
|
|
30
|
+
# stop iteration gimmick
|
|
31
|
+
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
|
|
32
|
+
if self.opt.minimize_kwargs['method'] == "trust-constr":
|
|
33
|
+
raise StopIteration2 # supports nothing
|
|
34
|
+
elif (
|
|
35
|
+
self.opt.minimize_kwargs['method'] == 'TNC'
|
|
36
|
+
or self.opt.minimize_kwargs['method'] == 'SLSQP'
|
|
37
|
+
or self.opt.minimize_kwargs['method'] == 'COBYLA'
|
|
38
|
+
):
|
|
39
|
+
raise StopIteration2 # supports xk
|
|
40
|
+
else:
|
|
41
|
+
raise StopIteration # supports xk , intermediate_result and StopIteration
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __call__(self, xk=None, intermediate_result=None):
|
|
45
|
+
self.res = intermediate_result
|
|
46
|
+
if self.opt.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
47
|
+
self.opt.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
48
|
+
self.stop_iteration()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ScipyMethodChecker(OptimizationMethodChecker):
|
|
52
|
+
def check_incomplete_bounds(self, raise_error=True): return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ScipyOptimizer(AbstractOptimizer):
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
**minimize_kwargs,
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
Args:
|
|
63
|
+
**minimize_kwargs: Kwargs of `scipy.optimize.minimize`.
|
|
64
|
+
"""
|
|
65
|
+
super().__init__()
|
|
66
|
+
|
|
67
|
+
# define members
|
|
68
|
+
self.minimize_kwargs: dict = dict(
|
|
69
|
+
method='L-BFGS-B',
|
|
70
|
+
)
|
|
71
|
+
self.minimize_kwargs.update(minimize_kwargs)
|
|
72
|
+
self.res: OptimizeResult = None
|
|
73
|
+
self.method_checker: OptimizationMethodChecker = ScipyMethodChecker(self)
|
|
74
|
+
self.stop_iteration_callback = StopIterationCallback(self)
|
|
75
|
+
|
|
76
|
+
def _objective(self, x: np.ndarray): # x: candidate parameter
|
|
77
|
+
# update parameter
|
|
78
|
+
self.parameters['value'] = x
|
|
79
|
+
self.fem.update_parameter(self.parameters)
|
|
80
|
+
|
|
81
|
+
# strict constraints
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
# fem
|
|
85
|
+
try:
|
|
86
|
+
_, obj_values, cns_values = self.f(x)
|
|
87
|
+
except (ModelError, MeshError, SolveError) as e:
|
|
88
|
+
logger.info(e)
|
|
89
|
+
logger.info('以下の変数で FEM 解析に失敗しました。')
|
|
90
|
+
print(self.get_parameter('dict'))
|
|
91
|
+
|
|
92
|
+
# 現状、エラーが起きたらスキップできない
|
|
93
|
+
raise StopIteration2
|
|
94
|
+
|
|
95
|
+
# constraints
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
# # check interruption command
|
|
99
|
+
# if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
100
|
+
# self.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
101
|
+
# raise StopOptimize
|
|
102
|
+
|
|
103
|
+
# objectives to objective
|
|
104
|
+
|
|
105
|
+
return obj_values[0]
|
|
106
|
+
|
|
107
|
+
def _setup_before_parallel(self):
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
def run(self):
|
|
111
|
+
|
|
112
|
+
# create init
|
|
113
|
+
x0 = self.parameters['value'].values
|
|
114
|
+
|
|
115
|
+
# create bounds
|
|
116
|
+
if 'bounds' not in self.minimize_kwargs.keys():
|
|
117
|
+
bounds = []
|
|
118
|
+
for i, row in self.parameters.iterrows():
|
|
119
|
+
lb, ub = row['lb'], row['ub']
|
|
120
|
+
if lb is None: lb = -np.inf
|
|
121
|
+
if ub is None: ub = np.inf
|
|
122
|
+
bounds.append([lb, ub])
|
|
123
|
+
self.minimize_kwargs.update(
|
|
124
|
+
{'bounds': bounds}
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# run optimize
|
|
128
|
+
try:
|
|
129
|
+
res = minimize(
|
|
130
|
+
fun=self._objective,
|
|
131
|
+
x0=x0,
|
|
132
|
+
**self.minimize_kwargs,
|
|
133
|
+
callback=self.stop_iteration_callback,
|
|
134
|
+
)
|
|
135
|
+
except StopIteration2:
|
|
136
|
+
res = None
|
|
137
|
+
logger.warn('Optimization has been interrupted. '
|
|
138
|
+
'Note that you cannot acquire the OptimizationResult '
|
|
139
|
+
'in case of `trust-constr`, `TNC`, `SLSQP` or `COBYLA`.')
|
|
140
|
+
|
|
141
|
+
if res is None:
|
|
142
|
+
self.res = self.stop_iteration_callback.res
|
|
143
|
+
else:
|
|
144
|
+
self.res = res
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# typing
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
|
|
5
|
+
# built-in
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
# 3rd-party
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
import scipy.optimize
|
|
12
|
+
from scipy.optimize import minimize_scalar, OptimizeResult
|
|
13
|
+
|
|
14
|
+
# pyfemtet relative
|
|
15
|
+
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
16
|
+
from pyfemtet.opt.opt import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
17
|
+
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ScipyScalarMethodChecker(OptimizationMethodChecker):
|
|
21
|
+
def check_incomplete_bounds(self, raise_error=True): return True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ScipyOptimizer(AbstractOptimizer):
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
**minimize_kwargs,
|
|
29
|
+
):
|
|
30
|
+
"""
|
|
31
|
+
Args:
|
|
32
|
+
**minimize_kwargs: Kwargs of `scipy.optimize.minimize_scalar` __except ``fun``.__.
|
|
33
|
+
"""
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
# define members
|
|
37
|
+
self.minimize_kwargs: dict = dict()
|
|
38
|
+
self.minimize_kwargs.update(minimize_kwargs)
|
|
39
|
+
self.res: OptimizeResult = None
|
|
40
|
+
self.method_checker: OptimizationMethodChecker = ScipyScalarMethodChecker(self)
|
|
41
|
+
|
|
42
|
+
def _objective(self, x: float): # x: candidate parameter
|
|
43
|
+
# update parameter
|
|
44
|
+
self.parameters['value'] = x
|
|
45
|
+
self.fem.update_parameter(self.parameters)
|
|
46
|
+
|
|
47
|
+
# strict constraints
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
# fem
|
|
51
|
+
try:
|
|
52
|
+
_, obj_values, cns_values = self.f(x)
|
|
53
|
+
except (ModelError, MeshError, SolveError) as e:
|
|
54
|
+
logger.info(e)
|
|
55
|
+
logger.info('以下の変数で FEM 解析に失敗しました。')
|
|
56
|
+
print(self.get_parameter('dict'))
|
|
57
|
+
|
|
58
|
+
# 現状、エラーが起きたらスキップできない
|
|
59
|
+
raise StopIteration
|
|
60
|
+
|
|
61
|
+
# constraints
|
|
62
|
+
...
|
|
63
|
+
|
|
64
|
+
# check interruption command
|
|
65
|
+
if self.entire_status.get() == OptimizationStatus.INTERRUPTING:
|
|
66
|
+
self.worker_status.set(OptimizationStatus.INTERRUPTING)
|
|
67
|
+
raise StopIteration
|
|
68
|
+
|
|
69
|
+
# objectives to objective
|
|
70
|
+
|
|
71
|
+
return obj_values[0]
|
|
72
|
+
|
|
73
|
+
def _setup_before_parallel(self):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
def run(self):
|
|
77
|
+
|
|
78
|
+
# create init
|
|
79
|
+
assert len(self.parameters) == 1
|
|
80
|
+
|
|
81
|
+
# create bounds
|
|
82
|
+
if 'bounds' not in self.minimize_kwargs.keys():
|
|
83
|
+
bounds = []
|
|
84
|
+
for i, row in self.parameters.iterrows():
|
|
85
|
+
lb, ub = row['lb'], row['ub']
|
|
86
|
+
if lb is None: lb = -np.inf
|
|
87
|
+
if ub is None: ub = np.inf
|
|
88
|
+
bounds.append([lb, ub])
|
|
89
|
+
self.minimize_kwargs.update(
|
|
90
|
+
{'bounds': bounds}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# run optimize
|
|
94
|
+
try:
|
|
95
|
+
res = minimize_scalar(
|
|
96
|
+
fun=self._objective,
|
|
97
|
+
**self.minimize_kwargs,
|
|
98
|
+
)
|
|
99
|
+
except StopIteration:
|
|
100
|
+
res = None
|
|
101
|
+
logger.warn('Optimization has been interrupted. '
|
|
102
|
+
'Note that you cannot acquire the OptimizationResult.')
|
|
103
|
+
|
|
104
|
+
self.res = res
|
|
@@ -12,13 +12,13 @@ pyfemtet/FemtetPJTSample/her_ex40_parametric.py,sha256=B5PQoh71Q3KN2CyLU1gP_Yh9g
|
|
|
12
12
|
pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py,sha256=UfhJffuXyhzdIWNpOrpV6xLTK1fuVvgyhlyg4Rp-628,2148
|
|
13
13
|
pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj,sha256=pxacKe0NPNUPAcxqo2cATFApsMKiVt2g2e_FOk4fpjA,172895
|
|
14
14
|
pyfemtet/FemtetPJTSample/wat_ex14_parametric.py,sha256=LGbWxCek0Ad2YrDCKykiQkE3aIypM4g8P3mLd_2anEE,2052
|
|
15
|
-
pyfemtet/__init__.py,sha256=
|
|
16
|
-
pyfemtet/_test_util.py,sha256=
|
|
15
|
+
pyfemtet/__init__.py,sha256=tZkJ0uJk05Wp3JKUzODcgphEkYtK6avu9HolSmeKZbk,22
|
|
16
|
+
pyfemtet/_test_util.py,sha256=VLRyeLrBbaZ-tBoncyiR9-1BgT5N91SPR8u1j0AI6dU,4445
|
|
17
17
|
pyfemtet/core.py,sha256=3lqfBGJ5IuKz2Nqj5pRo7YQqKwx_0ZDL72u95Ur_1p0,1386
|
|
18
18
|
pyfemtet/dispatch_extensions.py,sha256=MhWiUXVt2Cq8vDeajMK4SrajjiAmb4m2fK8gXwHLrWA,16177
|
|
19
19
|
pyfemtet/logger.py,sha256=JYD0FvzijMS2NvZN7VT7vZA5hqtHEkvS93AHlIMDePw,2507
|
|
20
|
-
pyfemtet/opt/__init__.py,sha256=
|
|
21
|
-
pyfemtet/opt/_femopt.py,sha256=
|
|
20
|
+
pyfemtet/opt/__init__.py,sha256=AJc1_d0sQ2_X4h_8FOcdmHvSPrFV_vfxlRrZTsqDZuE,612
|
|
21
|
+
pyfemtet/opt/_femopt.py,sha256=NdqXbeH81AHnMpuq1PFKD3Bo1FTfYVji5H2kyUQR6xI,23693
|
|
22
22
|
pyfemtet/opt/_femopt_core.py,sha256=zB12rNBnWN-CuqGijgKL8RuBEdoyzfbNWQBTcBiTvcU,25490
|
|
23
23
|
pyfemtet/opt/femprj_sample/cad_ex01_NX.femprj,sha256=KC8JlHqHzqgyKriK911QSnQByQpRlw-SX5OSQ_GNe5M,149193
|
|
24
24
|
pyfemtet/opt/femprj_sample/cad_ex01_NX.prt,sha256=3okHLeMdslrRA_wkhppZtxIe-2-ZPMfNqWCdQwUV31o,226626
|
|
@@ -62,16 +62,18 @@ pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.femprj,sha256=dMwQMt6yok_Pb
|
|
|
62
62
|
pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py,sha256=W30jobW1eEYuAsJJR14-EifQ1fxeXlS-gRSFqgCP4BM,2590
|
|
63
63
|
pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py,sha256=ul2JUwlM5jfjNRELHkT7ZDuyMOq5RAHO32vAweEhCwg,2641
|
|
64
64
|
pyfemtet/opt/interface/__init__.py,sha256=qz5BszPuU3jZIoDnPjkPDAgvgHLlx1sYhuqh5ID798k,480
|
|
65
|
-
pyfemtet/opt/interface/_base.py,sha256=
|
|
65
|
+
pyfemtet/opt/interface/_base.py,sha256=I4pJttLeRW-6WWMuCNynwxWPwriiGZk20vHLVcfixZY,2332
|
|
66
66
|
pyfemtet/opt/interface/_femtet.py,sha256=Of-O8ufbD1cjvXrJ3K7yyRTlNZYsBJGiq6okCx72B8I,34195
|
|
67
67
|
pyfemtet/opt/interface/_femtet_parametric.py,sha256=X6D5KfQE5_vxOTY8gZcHXWgWAhzaUs5QfmR4TwDyAHk,2124
|
|
68
68
|
pyfemtet/opt/interface/_femtet_with_nx/__init__.py,sha256=-6W2g2FDEcKzGHmI5KAKQe-4U5jDpMj0CXuma-GZca0,83
|
|
69
69
|
pyfemtet/opt/interface/_femtet_with_nx/_interface.py,sha256=8ZvD6ajNmKUqXPkDsGJ4tOHrQOYwWBlO9OSPaCO3ml0,5666
|
|
70
70
|
pyfemtet/opt/interface/_femtet_with_nx/update_model.py,sha256=uFf_SU96_BYUmLBBR5w3NS3WqXvEvSbOoxVnz9FA3Ug,3187
|
|
71
71
|
pyfemtet/opt/interface/_femtet_with_sldworks.py,sha256=400FidHp7mBAVLyxzLlLtGYNzK_TtK61ycd4vFjHES0,6254
|
|
72
|
-
pyfemtet/opt/opt/__init__.py,sha256=
|
|
73
|
-
pyfemtet/opt/opt/_base.py,sha256=
|
|
74
|
-
pyfemtet/opt/opt/_optuna.py,sha256=
|
|
72
|
+
pyfemtet/opt/opt/__init__.py,sha256=UrfJ0oEr_ikUKfzrsBEsBC3L3mQO_hV9dejr5UfuWT4,291
|
|
73
|
+
pyfemtet/opt/opt/_base.py,sha256=IBnapgEM34b9tFkwdbJMrfMWy7h-nIwby4GoLKsE6bc,10447
|
|
74
|
+
pyfemtet/opt/opt/_optuna.py,sha256=IjUsAPsAp3-sc3U8OYpDs137heW0c4b3Y_bExaXdRRs,10636
|
|
75
|
+
pyfemtet/opt/opt/_scipy.py,sha256=XArCRPTTqE1Cf-O_XZcsErsvP1_nXJh7YCQwL9jCeFo,4398
|
|
76
|
+
pyfemtet/opt/opt/_scipy_scalar.py,sha256=bhBfAfiep8t170QDFBxGJb_wAE0viTtJHz-q8iubJM0,2964
|
|
75
77
|
pyfemtet/opt/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
78
|
pyfemtet/opt/visualization/_create_wrapped_components.py,sha256=XpOv_iEEB3lFT3vYuCnCCbwUwRFbCYgvsZDpaiSzmZY,2511
|
|
77
79
|
pyfemtet/opt/visualization/base.py,sha256=XhZodLEr5PPHmtNai6uZcPJobKurENepuGCUaqDOI7I,7588
|
|
@@ -129,8 +131,8 @@ pyfemtet/opt/visualization/wrapped_components/dbc.py,sha256=wzR1ZMOb4uwPNTMFn5up
|
|
|
129
131
|
pyfemtet/opt/visualization/wrapped_components/dcc.py,sha256=hcW7SR6VIMn4S4-JMyohvOzdc0Aw8A4chIeHqQEUbFU,17499
|
|
130
132
|
pyfemtet/opt/visualization/wrapped_components/html.py,sha256=sE2XHTDY1GvA1NW7y6SKWf-WglVXFKKvXhU9h3z53_g,95652
|
|
131
133
|
pyfemtet/opt/visualization/wrapped_components/str_enum.py,sha256=NZqbh2jNEAckvJyZv__MWeRs2F2Q-dkJCWo30rU2rrM,1383
|
|
132
|
-
pyfemtet-0.4.
|
|
133
|
-
pyfemtet-0.4.
|
|
134
|
-
pyfemtet-0.4.
|
|
135
|
-
pyfemtet-0.4.
|
|
136
|
-
pyfemtet-0.4.
|
|
134
|
+
pyfemtet-0.4.12.dist-info/LICENSE,sha256=sVQBhyoglGJUu65-BP3iR6ujORI6YgEU2Qm-V4fGlOA,1485
|
|
135
|
+
pyfemtet-0.4.12.dist-info/METADATA,sha256=4xS2zTozQkGcgzt53ZYgme4StBaIRVftfzVkSzotHAA,3382
|
|
136
|
+
pyfemtet-0.4.12.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
137
|
+
pyfemtet-0.4.12.dist-info/entry_points.txt,sha256=ZfYqRaoiPtuWqFi2_msccyrVF0LurMn-IHlYamAegZo,104
|
|
138
|
+
pyfemtet-0.4.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|