pyfemtet 0.5.2__py3-none-any.whl → 0.5.4__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/{message → _message}/locales/ja/LC_MESSAGES/messages.po +88 -76
- pyfemtet/{message → _message}/locales/messages.pot +88 -76
- pyfemtet/{message → _message}/messages.py +1 -1
- pyfemtet/_warning.py +23 -0
- pyfemtet/dispatch_extensions/__init__.py +12 -0
- pyfemtet/{dispatch_extensions.py → dispatch_extensions/_impl.py} +45 -43
- pyfemtet/logger/__init__.py +3 -0
- pyfemtet/{logger.py → logger/_impl.py} +12 -6
- pyfemtet/opt/_femopt.py +236 -58
- pyfemtet/opt/_femopt_core.py +21 -8
- pyfemtet/opt/_test_utils/record_history.py +1 -1
- pyfemtet/opt/interface/__init__.py +0 -1
- pyfemtet/opt/interface/_base.py +3 -3
- pyfemtet/opt/interface/_femtet.py +101 -55
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py +35 -12
- pyfemtet/opt/interface/_femtet_with_sldworks.py +22 -2
- pyfemtet/opt/optimizer/_base.py +76 -42
- pyfemtet/opt/optimizer/_optuna.py +33 -1
- pyfemtet/opt/optimizer/_optuna_botorchsampler_parameter_constraint_helper.py +1 -2
- pyfemtet/opt/optimizer/_scipy.py +20 -5
- pyfemtet/opt/optimizer/_scipy_scalar.py +20 -5
- pyfemtet/opt/prediction/{base.py → _base.py} +3 -2
- pyfemtet/opt/prediction/single_task_gp.py +10 -5
- pyfemtet/opt/visualization/{base.py → _base.py} +1 -1
- pyfemtet/opt/visualization/{complex_components → _complex_components}/alert_region.py +2 -2
- pyfemtet/opt/visualization/{complex_components → _complex_components}/control_femtet.py +3 -3
- pyfemtet/opt/visualization/{complex_components → _complex_components}/main_figure_creator.py +1 -1
- pyfemtet/opt/visualization/{complex_components → _complex_components}/main_graph.py +5 -5
- pyfemtet/opt/visualization/{complex_components → _complex_components}/pm_graph.py +5 -5
- pyfemtet/opt/visualization/{complex_components → _complex_components}/pm_graph_creator.py +2 -2
- pyfemtet/opt/visualization/_create_wrapped_components.py +2 -2
- pyfemtet/opt/visualization/{process_monitor → _process_monitor}/application.py +3 -3
- pyfemtet/opt/visualization/{process_monitor → _process_monitor}/pages.py +10 -10
- pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/dbc.py +1 -1
- pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/dcc.py +1 -1
- pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/html.py +1 -1
- pyfemtet/opt/visualization/result_viewer/application.py +4 -4
- pyfemtet/opt/visualization/result_viewer/pages.py +9 -9
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/METADATA +2 -2
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/RECORD +53 -51
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/WHEEL +1 -1
- pyfemtet/message/locales/ja/LC_MESSAGES/messages.mo +0 -0
- /pyfemtet/{message → _message}/1. make_pot.bat +0 -0
- /pyfemtet/{message → _message}/2. make_mo.bat +0 -0
- /pyfemtet/{message → _message}/__init__.py +0 -0
- /pyfemtet/{message → _message}/babel.cfg +0 -0
- /pyfemtet/opt/{parameter.py → optimizer/parameter.py} +0 -0
- /pyfemtet/opt/visualization/{complex_components → _complex_components}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{process_monitor → _process_monitor}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{wrapped_components → _wrapped_components}/str_enum.py +0 -0
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/LICENSE +0 -0
- {pyfemtet-0.5.2.dist-info → pyfemtet-0.5.4.dist-info}/entry_points.txt +0 -0
pyfemtet/opt/_femopt.py
CHANGED
|
@@ -17,7 +17,7 @@ from dask.distributed import LocalCluster, Client, Worker
|
|
|
17
17
|
# pyfemtet relative
|
|
18
18
|
from pyfemtet.opt.interface import FEMInterface, FemtetInterface
|
|
19
19
|
from pyfemtet.opt.optimizer import AbstractOptimizer, OptunaOptimizer
|
|
20
|
-
from pyfemtet.opt.visualization.
|
|
20
|
+
from pyfemtet.opt.visualization._process_monitor.application import main as process_monitor_main
|
|
21
21
|
from pyfemtet.opt._femopt_core import (
|
|
22
22
|
_check_bound,
|
|
23
23
|
_is_access_gogh,
|
|
@@ -28,8 +28,9 @@ from pyfemtet.opt._femopt_core import (
|
|
|
28
28
|
OptimizationStatus,
|
|
29
29
|
logger,
|
|
30
30
|
)
|
|
31
|
-
from pyfemtet.
|
|
32
|
-
from pyfemtet.opt.parameter import Parameter, Expression
|
|
31
|
+
from pyfemtet._message import Msg, encoding
|
|
32
|
+
from pyfemtet.opt.optimizer.parameter import Parameter, Expression
|
|
33
|
+
from pyfemtet._warning import experimental_feature
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def add_worker(client, worker_name):
|
|
@@ -54,27 +55,47 @@ def add_worker(client, worker_name):
|
|
|
54
55
|
client.wait_for_workers(n_workers=current_n_workers + 1)
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
|
|
58
58
|
class FEMOpt:
|
|
59
59
|
"""Class to control FEM interface and optimizer.
|
|
60
60
|
|
|
61
61
|
Args:
|
|
62
|
-
fem (FEMInterface, optional):
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
62
|
+
fem (FEMInterface, optional):
|
|
63
|
+
The FEM software interface.
|
|
64
|
+
Defaults to None (automatically set to :class:`FemtetInterface`).
|
|
65
|
+
|
|
66
|
+
opt (AbstractOptimizer, optional):
|
|
67
|
+
The numerical optimizer object.
|
|
68
|
+
Defaults to None (automatically set to :class:`OptunaOptimizer`
|
|
69
|
+
with :class:`optuna.samplers.TPESampler`).
|
|
70
|
+
|
|
71
|
+
history_path (str, optional):
|
|
72
|
+
The path to the history file.
|
|
73
|
+
Defaults to None.
|
|
74
|
+
If None, '%Y_%m_%d_%H_%M_%S.csv' is created in current directory.
|
|
75
|
+
|
|
76
|
+
scheduler_address (str, optional):
|
|
77
|
+
When performing cluster computing, please specify the address
|
|
78
|
+
of the scheduler computer here.
|
|
79
|
+
|
|
80
|
+
See Also:
|
|
81
|
+
https://pyfemtet.readthedocs.io/en/stable/pages/usage_pages/how_to_deploy_cluster.html
|
|
82
|
+
|
|
83
|
+
.. Example の中について、reST ではリストだと改行できないので、リストにしない
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
|
|
87
|
+
When specifying and opening a femprj and model, we write
|
|
88
|
+
|
|
89
|
+
>>> from pyfemtet.opt import FEMOpt, FemtetInterface # doctest: +SKIP
|
|
90
|
+
>>> fem = FemtetInterface(femprj_path='path/to/project.femprj', model_name='NewModel') # doctest: +SKIP
|
|
91
|
+
>>> femopt = FEMOpt(fem=fem) # doctest: +SKIP
|
|
92
|
+
|
|
93
|
+
When specifying optimization algorithm, we write
|
|
94
|
+
|
|
95
|
+
>>> from optuna.samplers import TPESampler # doctest: +SKIP
|
|
96
|
+
>>> from pyfemtet.opt import FEMOpt, OptunaOptimizer # doctest: +SKIP
|
|
97
|
+
>>> opt = OptunaOptimizer(sampler_class=TPESampler, sampler_kwargs=dict(n_startup_trials=10)) # doctest: +SKIP
|
|
98
|
+
>>> femopt = FEMOpt(opt=opt) # doctest: +SKIP
|
|
78
99
|
|
|
79
100
|
"""
|
|
80
101
|
|
|
@@ -133,25 +154,40 @@ class FEMOpt:
|
|
|
133
154
|
def add_parameter(
|
|
134
155
|
self,
|
|
135
156
|
name: str,
|
|
136
|
-
initial_value: float
|
|
137
|
-
lower_bound: float
|
|
138
|
-
upper_bound: float
|
|
139
|
-
step: float
|
|
140
|
-
properties:
|
|
141
|
-
pass_to_fem:
|
|
157
|
+
initial_value: float = None,
|
|
158
|
+
lower_bound: float = None,
|
|
159
|
+
upper_bound: float = None,
|
|
160
|
+
step: float = None,
|
|
161
|
+
properties: dict[str, str or float] = None,
|
|
162
|
+
pass_to_fem: bool = True,
|
|
142
163
|
):
|
|
164
|
+
# noinspection PyUnresolvedReferences
|
|
143
165
|
"""Adds a parameter to the optimization problem.
|
|
144
166
|
|
|
145
167
|
Args:
|
|
146
168
|
name (str): The name of the parameter.
|
|
147
|
-
initial_value (float
|
|
148
|
-
lower_bound (float
|
|
149
|
-
upper_bound (float or None, optional): The upper bound of the parameter. Defaults to None.
|
|
150
|
-
step (float
|
|
151
|
-
properties (dict, optional): Additional information about the parameter. Defaults to None.
|
|
169
|
+
initial_value (float, optional): The initial value of the parameter. Defaults to None. If None, try to get initial value from FEMInterface.
|
|
170
|
+
lower_bound (float, optional): The lower bound of the parameter. Defaults to None. Some optimization algorithms require this.
|
|
171
|
+
upper_bound (float or None, optional): The upper bound of the parameter. Defaults to None. Some optimization algorithms require this.
|
|
172
|
+
step (float, optional): The step of parameter. If specified, parameter is used as discrete. Defaults to None.
|
|
173
|
+
properties (dict[str, str or float], optional): Additional information about the parameter. Defaults to None.
|
|
152
174
|
pass_to_fem (bool, optional): If this variable is used directly in FEM model update or not. If False, this parameter can be just used as inpt of expressions. Defaults to True.
|
|
175
|
+
|
|
153
176
|
Raises:
|
|
154
|
-
ValueError: If initial_value is not specified and the value for the given name is also not specified.
|
|
177
|
+
ValueError: If initial_value is not specified and the value for the given name is also not specified in FEM.
|
|
178
|
+
|
|
179
|
+
Examples:
|
|
180
|
+
|
|
181
|
+
When adding parameter a (-1 <= a <= 1; initial value is 0), we write
|
|
182
|
+
|
|
183
|
+
>>> femopt.add_parameter('parameter_a', 0, -1, 1) # doctest: +SKIP
|
|
184
|
+
|
|
185
|
+
Note that the ```note``` argument can be set any name in this case.
|
|
186
|
+
|
|
187
|
+
When adding discrete parameter a (-1 <= a <= 1; initial value is 0,
|
|
188
|
+
step 0.5), we write
|
|
189
|
+
|
|
190
|
+
>>> femopt.add_parameter('parameter a', 0, -1, 1, 0.5) # doctest: +SKIP
|
|
155
191
|
|
|
156
192
|
"""
|
|
157
193
|
|
|
@@ -179,23 +215,73 @@ class FEMOpt:
|
|
|
179
215
|
)
|
|
180
216
|
self.opt.variables.add_parameter(prm)
|
|
181
217
|
|
|
218
|
+
@experimental_feature
|
|
182
219
|
def add_expression(
|
|
183
220
|
self,
|
|
184
221
|
name: str,
|
|
185
222
|
fun: Callable[[Any], float],
|
|
186
|
-
properties=
|
|
223
|
+
properties: dict[str, str or float] = None,
|
|
187
224
|
kwargs: Optional[dict] = None,
|
|
188
225
|
pass_to_fem=True,
|
|
189
226
|
):
|
|
227
|
+
# noinspection PyUnresolvedReferences
|
|
190
228
|
"""Add expression to the optimization problem.
|
|
191
229
|
|
|
230
|
+
Warnings:
|
|
231
|
+
This feature is highly experimental and
|
|
232
|
+
may change in the future.
|
|
233
|
+
|
|
192
234
|
Args:
|
|
193
235
|
name (str): The name of the variable.
|
|
194
236
|
fun (Callable[[Any], float]): An expression function. The arguments that you want to use as input variables must be the same with ``name`` of Variable objects added by ``add_parameter()`` or ``add_expression()``. If you use other objects as argument of the function, you must specify ``kwargs``.
|
|
195
|
-
properties ([
|
|
237
|
+
properties (dict[str, str or float], optional): Additional information about the parameter. Defaults to None.
|
|
196
238
|
kwargs (Optional[dict], optional): Remaining arguments of ``fun``. Defaults to None.
|
|
197
239
|
pass_to_fem (bool, optional): If this variable is used directly in FEM model update or not. If False, this variable can be just used as inpt of other expressions. Defaults to True.
|
|
240
|
+
|
|
241
|
+
Examples:
|
|
242
|
+
|
|
243
|
+
When adding variable a and b; a is not directly included as model
|
|
244
|
+
variables but an optimization parameter, b is not a parameter but
|
|
245
|
+
a model variable and the relationship of these 2 variables is
|
|
246
|
+
```b = a ** 2```, we write:
|
|
247
|
+
|
|
248
|
+
>>> def calc_b(parameter_a): # doctest: +SKIP
|
|
249
|
+
... return parameter_a ** 2 # doctest: +SKIP
|
|
250
|
+
...
|
|
251
|
+
>>> femopt.add_parameter('parameter_a', 0, -1, 1, pass_to_fem=False) # doctest: +SKIP
|
|
252
|
+
>>> femopt.add_expression('variable_b', calc_b) # doctest: +SKIP
|
|
253
|
+
|
|
254
|
+
Notes:
|
|
255
|
+
The argument names of function to calculate variable
|
|
256
|
+
must match the ```name``` of the parameter.
|
|
257
|
+
|
|
258
|
+
Notes:
|
|
259
|
+
In this case, only strings that can be used as Python variables are valid
|
|
260
|
+
for ```name``` argument of :func:`FEMOpt.add_parameter`.
|
|
261
|
+
|
|
262
|
+
When adding variable r, theta and x, y; r, theta is an optimization
|
|
263
|
+
parameter and x, y is an intermediate variable to calculate constraint
|
|
264
|
+
function, we write
|
|
265
|
+
|
|
266
|
+
>>> def calc_x(r, theta): # doctest: +SKIP
|
|
267
|
+
... return r * cos(theta) # doctest: +SKIP
|
|
268
|
+
...
|
|
269
|
+
>>> def calc_y(r, theta): # doctest: +SKIP
|
|
270
|
+
... return r * cos(theta) # doctest: +SKIP
|
|
271
|
+
...
|
|
272
|
+
>>> def constraint(Femtet, opt: AbstractOptimizer): # doctest: +SKIP
|
|
273
|
+
... d = opt.variables.get_variables() # doctest: +SKIP
|
|
274
|
+
... x, y = d['x'], d['y'] # doctest: +SKIP
|
|
275
|
+
... return min(x, y) # doctest: +SKIP
|
|
276
|
+
...
|
|
277
|
+
>>> femopt.add_parameter('r', 0, 0, 1) # doctest: +SKIP
|
|
278
|
+
>>> femopt.add_parameter('theta', 0, 0, 2*pi) # doctest: +SKIP
|
|
279
|
+
>>> femopt.add_expression('x', calc_x, pass_to_fem=False) # doctest: +SKIP
|
|
280
|
+
>>> femopt.add_expression('y', calc_y, pass_to_fem=False) # doctest: +SKIP
|
|
281
|
+
>>> femopt.add_constraint(constraint, lower_bound=0.5, args=(femopt.opt,)) # doctest: +SKIP
|
|
282
|
+
|
|
198
283
|
"""
|
|
284
|
+
|
|
199
285
|
exp = Expression(
|
|
200
286
|
name=name,
|
|
201
287
|
value=None,
|
|
@@ -214,18 +300,39 @@ class FEMOpt:
|
|
|
214
300
|
args: tuple or None = None,
|
|
215
301
|
kwargs: dict or None = None
|
|
216
302
|
):
|
|
303
|
+
# noinspection PyUnresolvedReferences
|
|
217
304
|
"""Adds an objective to the optimization problem.
|
|
218
305
|
|
|
219
306
|
Args:
|
|
220
307
|
fun (callable): The objective function.
|
|
221
308
|
name (str or None, optional): The name of the objective. Defaults to None.
|
|
222
|
-
direction (str or float, optional): The optimization direction. Defaults to 'minimize'.
|
|
309
|
+
direction (str or float, optional): The optimization direction. Varid values are 'maximize', 'minimize' or a float value. Defaults to 'minimize'.
|
|
223
310
|
args (tuple or None, optional): Additional arguments for the objective function. Defaults to None.
|
|
224
311
|
kwargs (dict or None, optional): Additional keyword arguments for the objective function. Defaults to None.
|
|
225
312
|
|
|
226
313
|
Note:
|
|
227
314
|
If the FEMInterface is FemtetInterface, the 1st argument of fun should be Femtet (IPyDispatch) object.
|
|
228
315
|
|
|
316
|
+
Examples:
|
|
317
|
+
|
|
318
|
+
When add a complex objective (for example, want to dynamically set body
|
|
319
|
+
to calculate temperature and use product of the temperature and one of
|
|
320
|
+
the parameters as objective) , we write
|
|
321
|
+
|
|
322
|
+
>>> class MyClass: # doctest: +SKIP
|
|
323
|
+
... def get_body_name(self): # doctest: +SKIP
|
|
324
|
+
... ... # process something and detect body name # doctest: +SKIP
|
|
325
|
+
... return body_name # doctest: +SKIP
|
|
326
|
+
...
|
|
327
|
+
>>> def complex_objective(Femtet, opt, some_object): # doctest: +SKIP
|
|
328
|
+
... body_name = some_object.get_body_name() # dynamically get body name to calculate temperature # doctest: +SKIP
|
|
329
|
+
... temp, _, _ = Femtet.Gogh.Watt.GetTemp(body_name) # calculate temperature # doctest: +SKIP
|
|
330
|
+
... some_param = opt.get_parameter()['some_param'] # get ome parameter # doctest: +SKIP
|
|
331
|
+
... return temp * some_param # calculate something and return it # doctest: +SKIP
|
|
332
|
+
...
|
|
333
|
+
>>> my_obj = MyClass() # doctest: +SKIP
|
|
334
|
+
>>> femopt.add_objective(complex_objective, args=(femopt.opt, my_obj,)) # doctest: +SKIP
|
|
335
|
+
|
|
229
336
|
Tip:
|
|
230
337
|
If name is None, name is a string with the prefix `"obj_"` followed by a sequential number.
|
|
231
338
|
|
|
@@ -263,6 +370,7 @@ class FEMOpt:
|
|
|
263
370
|
kwargs: dict or None = None,
|
|
264
371
|
using_fem: bool = None,
|
|
265
372
|
):
|
|
373
|
+
# noinspection PyUnresolvedReferences
|
|
266
374
|
"""Adds a constraint to the optimization problem.
|
|
267
375
|
|
|
268
376
|
Args:
|
|
@@ -275,6 +383,36 @@ class FEMOpt:
|
|
|
275
383
|
kwargs (dict): Additional arguments for the constraint function. Defaults to None.
|
|
276
384
|
using_fem (bool, optional): Using FEM or not in the constraint function. It may make the processing time in strict constraints in BoTorchSampler. Defaults to None. If None, PyFemtet checks the access to Femtet and estimate using Femtet or not automatically.
|
|
277
385
|
|
|
386
|
+
Warnings:
|
|
387
|
+
|
|
388
|
+
When ```strict``` == True and using OptunaOptimizer along with BoTorchSampler,
|
|
389
|
+
Pyfemtet will solve an optimization subproblem to propose new variables.
|
|
390
|
+
During this process, the constraint function fun will be executed at each
|
|
391
|
+
iteration of the subproblem, which may include time-consuming operations such
|
|
392
|
+
as retrieving 3D model information via FEMInterface.
|
|
393
|
+
As a result, it may not be feasible to complete the overall optimization within
|
|
394
|
+
a realistic timeframe.
|
|
395
|
+
|
|
396
|
+
Examples:
|
|
397
|
+
For example, in a case where the bottom area of the model is constrained,
|
|
398
|
+
the following approach may require a very long time.
|
|
399
|
+
|
|
400
|
+
>>> def bottom_area(Femtet, opt): # doctest: +SKIP
|
|
401
|
+
... w = Femtet.GetVariableValue('width') # doctest: +SKIP
|
|
402
|
+
... d = Femtet.GetVariableValue('depth') # doctest: +SKIP
|
|
403
|
+
... return w * d # doctest: +SKIP
|
|
404
|
+
...
|
|
405
|
+
>>> femopt.add_constraint(constraint, lower_bound=5) # doctest: +SKIP
|
|
406
|
+
|
|
407
|
+
Instead, please do the following.
|
|
408
|
+
|
|
409
|
+
>>> def bottom_area(_, opt): # 第一引数 Femtet にはアクセスしないようにします。 # doctest: +SKIP
|
|
410
|
+
... params = opt.get_parameter() # doctest: +SKIP
|
|
411
|
+
... w, d = params['width'], params['depth'] # doctest: +SKIP
|
|
412
|
+
... return w * d # doctest: +SKIP
|
|
413
|
+
...
|
|
414
|
+
>>> femopt.add_constraint(constraint, lower_bound=5, args=(femopt.opt,)) # doctest: +SKIP
|
|
415
|
+
|
|
278
416
|
Note:
|
|
279
417
|
If the FEMInterface is FemtetInterface, the 1st argument of fun should be Femtet (IPyDispatch) object.
|
|
280
418
|
|
|
@@ -330,22 +468,32 @@ class FEMOpt:
|
|
|
330
468
|
self.opt.constraints[name] = Constraint(fun, name, lower_bound, upper_bound, strict, args, kwargs, using_fem)
|
|
331
469
|
|
|
332
470
|
def get_parameter(self, format='dict'):
|
|
333
|
-
|
|
471
|
+
"""Deprecated method.
|
|
472
|
+
|
|
473
|
+
Planed to remove in future version. Use FEMOpt.opt.get_parameter() instead.
|
|
474
|
+
"""
|
|
475
|
+
raise DeprecationWarning('FEMOpt.get_parameter() was deprecated. Use FEMOpt.opt.get_parameter() instead.')
|
|
334
476
|
|
|
335
477
|
def set_monitor_host(self, host=None, port=None):
|
|
336
|
-
"""Sets
|
|
478
|
+
"""Sets the host IP address and the port of the process monitor.
|
|
337
479
|
|
|
338
480
|
Args:
|
|
339
|
-
host (str):
|
|
340
|
-
|
|
481
|
+
host (str):
|
|
482
|
+
The hostname or IP address of the monitor server.
|
|
483
|
+
port (int, optional):
|
|
484
|
+
The port number of the monitor server.
|
|
485
|
+
If None, ``8080`` will be used.
|
|
486
|
+
Defaults to None.
|
|
341
487
|
|
|
342
488
|
Tip:
|
|
343
|
-
Specifying host ``0.0.0.0`` allows viewing monitor
|
|
489
|
+
Specifying host ``0.0.0.0`` allows viewing monitor
|
|
490
|
+
from all computers on the local network.
|
|
344
491
|
|
|
345
|
-
|
|
346
|
-
|
|
492
|
+
If no hostname is specified, the monitor server
|
|
493
|
+
will be hosted on ``localhost``.
|
|
347
494
|
|
|
348
|
-
|
|
495
|
+
We can access process monitor by accessing
|
|
496
|
+
```localhost:8080``` on our browser by default.
|
|
349
497
|
|
|
350
498
|
"""
|
|
351
499
|
self.monitor_server_kwargs = dict(
|
|
@@ -355,33 +503,57 @@ class FEMOpt:
|
|
|
355
503
|
|
|
356
504
|
def optimize(
|
|
357
505
|
self,
|
|
358
|
-
n_trials=None,
|
|
359
|
-
n_parallel=1,
|
|
360
|
-
timeout=None,
|
|
361
|
-
wait_setup=True,
|
|
362
|
-
confirm_before_exit=True,
|
|
506
|
+
n_trials: int = None,
|
|
507
|
+
n_parallel: int = 1,
|
|
508
|
+
timeout: float = None,
|
|
509
|
+
wait_setup: bool = True,
|
|
510
|
+
confirm_before_exit: bool = True,
|
|
363
511
|
):
|
|
364
512
|
"""Runs the main optimization process.
|
|
365
513
|
|
|
366
514
|
Args:
|
|
367
|
-
n_trials (int
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
515
|
+
n_trials (int, optional):
|
|
516
|
+
The number of trials.
|
|
517
|
+
Defaults to None.
|
|
518
|
+
n_parallel (int, optional):
|
|
519
|
+
The number of parallel processes.
|
|
520
|
+
Defaults to 1.
|
|
521
|
+
Note that even if this argument is 1,
|
|
522
|
+
:class:`FEMOpt` makes some child processes
|
|
523
|
+
to run process monitor, status monitor, etc.
|
|
524
|
+
timeout (float, optional):
|
|
525
|
+
The maximum amount of time in seconds
|
|
526
|
+
that each trial can run.
|
|
527
|
+
Defaults to None.
|
|
528
|
+
wait_setup (bool, optional):
|
|
529
|
+
Wait for all workers launching FEM system.
|
|
530
|
+
Defaults to True.
|
|
531
|
+
confirm_before_exit (bool, optional):
|
|
532
|
+
Insert stop before exit to continue to
|
|
533
|
+
show process monitor.
|
|
372
534
|
|
|
373
535
|
Tip:
|
|
374
|
-
If set_monitor_host() is not executed,
|
|
536
|
+
If set_monitor_host() is not executed,
|
|
537
|
+
a local server for monitoring will be
|
|
538
|
+
started at localhost:8080.
|
|
539
|
+
|
|
540
|
+
See Also:
|
|
541
|
+
:func:`FEMOpt.set_monitor_host`
|
|
375
542
|
|
|
376
543
|
Note:
|
|
377
|
-
If ``n_trials`` and ``timeout`` are both None,
|
|
544
|
+
If ``n_trials`` and ``timeout`` are both None,
|
|
545
|
+
it runs forever until interrupting by the user.
|
|
378
546
|
|
|
379
547
|
Note:
|
|
380
|
-
If ``n_parallel`` >= 2, depending on the end timing,
|
|
548
|
+
If ``n_parallel`` >= 2, depending on the end timing,
|
|
549
|
+
``n_trials`` may be exceeded by up to ``n_parallel-1`` times.
|
|
381
550
|
|
|
382
551
|
Warning:
|
|
383
|
-
If ``n_parallel`` >= 2 and ``fem`` is a subclass of
|
|
384
|
-
|
|
552
|
+
If ``n_parallel`` >= 2 and ``fem`` is a subclass of
|
|
553
|
+
``FemtetInterface``, the ``strictly_pid_specify`` of
|
|
554
|
+
subprocess is set to ``False``.
|
|
555
|
+
So **it is recommended to close all other Femtet processes
|
|
556
|
+
before running.**
|
|
385
557
|
|
|
386
558
|
"""
|
|
387
559
|
|
|
@@ -510,6 +682,7 @@ class FEMOpt:
|
|
|
510
682
|
logger.info('Status Actor initialized successfully.')
|
|
511
683
|
|
|
512
684
|
# launch monitor
|
|
685
|
+
# noinspection PyTypeChecker
|
|
513
686
|
self.monitor_process_future = _client.submit(
|
|
514
687
|
# func
|
|
515
688
|
_start_monitor_server,
|
|
@@ -634,6 +807,11 @@ class FEMOpt:
|
|
|
634
807
|
|
|
635
808
|
@staticmethod
|
|
636
809
|
def terminate_all():
|
|
810
|
+
"""Deprecated method. We plan to remove this in future version.
|
|
811
|
+
|
|
812
|
+
In current version, the termination processes are
|
|
813
|
+
automatically execute in the last of :func:`FEMOpt.optimize`.
|
|
814
|
+
"""
|
|
637
815
|
warnings.warn(
|
|
638
816
|
"terminate_all() is deprecated and will be removed in a future version. "
|
|
639
817
|
"In current and later versions, the equivalent of terminate_all() will be executed when optimize() finishes. "
|
pyfemtet/opt/_femopt_core.py
CHANGED
|
@@ -8,13 +8,20 @@ import inspect
|
|
|
8
8
|
import ast
|
|
9
9
|
import csv
|
|
10
10
|
import ctypes
|
|
11
|
+
from packaging import version
|
|
11
12
|
|
|
12
13
|
# 3rd-party
|
|
13
14
|
import numpy as np
|
|
14
15
|
import pandas as pd
|
|
15
16
|
from scipy.stats.qmc import LatinHypercube
|
|
16
17
|
import optuna
|
|
17
|
-
|
|
18
|
+
if version.parse(optuna.version.__version__) < version.parse('4.0.0'):
|
|
19
|
+
from optuna._hypervolume import WFG
|
|
20
|
+
wfg = WFG()
|
|
21
|
+
compute_hypervolume = wfg.compute
|
|
22
|
+
else:
|
|
23
|
+
from optuna._hypervolume import wfg
|
|
24
|
+
compute_hypervolume = wfg.compute_hypervolume
|
|
18
25
|
from dask.distributed import Lock, get_client
|
|
19
26
|
|
|
20
27
|
# win32com
|
|
@@ -22,7 +29,7 @@ from win32com.client import constants, Constants
|
|
|
22
29
|
|
|
23
30
|
# pyfemtet relative
|
|
24
31
|
from pyfemtet.opt.interface import FEMInterface, FemtetInterface
|
|
25
|
-
from pyfemtet.
|
|
32
|
+
from pyfemtet._message import encoding, Msg
|
|
26
33
|
|
|
27
34
|
# logger
|
|
28
35
|
import logging
|
|
@@ -708,20 +715,27 @@ class History:
|
|
|
708
715
|
|
|
709
716
|
def _calc_non_domi(self, objectives, df):
|
|
710
717
|
|
|
718
|
+
# feasible のもののみ取り出してくる
|
|
719
|
+
idx = df['feasible'].values
|
|
720
|
+
pdf = df[idx]
|
|
721
|
+
|
|
711
722
|
# 目的関数の履歴を取り出してくる
|
|
712
|
-
solution_set =
|
|
723
|
+
solution_set = pdf[self.obj_names]
|
|
713
724
|
|
|
714
725
|
# 最小化問題の座標空間に変換する
|
|
715
726
|
for obj_column, (_, objective) in zip(self.obj_names, objectives.items()):
|
|
716
727
|
solution_set.loc[:, obj_column] = solution_set[obj_column].map(objective.convert)
|
|
717
728
|
|
|
718
729
|
# 非劣解の計算
|
|
719
|
-
non_domi = []
|
|
730
|
+
non_domi: list[bool] = []
|
|
720
731
|
for i, row in solution_set.iterrows():
|
|
721
732
|
non_domi.append((row > solution_set).product(axis=1).sum(axis=0) == 0)
|
|
722
733
|
|
|
723
|
-
#
|
|
724
|
-
df['non_domi'] =
|
|
734
|
+
# feasible も infeasible も一旦劣解にする
|
|
735
|
+
df['non_domi'] = False
|
|
736
|
+
|
|
737
|
+
# feasible のものに non_domi の評価結果を代入する
|
|
738
|
+
df.loc[idx, 'non_domi'] = non_domi
|
|
725
739
|
|
|
726
740
|
def _calc_hypervolume(self, objectives, df):
|
|
727
741
|
|
|
@@ -753,10 +767,9 @@ class History:
|
|
|
753
767
|
reference_point = r * (maximum - minimum) + minimum
|
|
754
768
|
|
|
755
769
|
#### hv 履歴の計算
|
|
756
|
-
wfg = WFG()
|
|
757
770
|
hvs = []
|
|
758
771
|
for i in range(n):
|
|
759
|
-
hv =
|
|
772
|
+
hv = compute_hypervolume(pareto_set[:i], reference_point)
|
|
760
773
|
if np.isnan(hv):
|
|
761
774
|
hv = 0
|
|
762
775
|
hvs.append(hv)
|
pyfemtet/opt/interface/_base.py
CHANGED
|
@@ -68,15 +68,15 @@ class FEMInterface(ABC):
|
|
|
68
68
|
"""Preprocessing after launching a dask worker and before run optimization (if implemented in concrete class)."""
|
|
69
69
|
pass
|
|
70
70
|
|
|
71
|
-
def
|
|
71
|
+
def _postprocess_func(self, trial: int, *args, dask_scheduler=None, **kwargs):
|
|
72
72
|
pass
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def _create_postprocess_args(self):
|
|
75
75
|
pass
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
class NoFEM(FEMInterface):
|
|
79
|
-
"""
|
|
79
|
+
"""Dummy interface without FEM. Intended for debugging purposes."""
|
|
80
80
|
|
|
81
81
|
def update(self, parameters: pd.DataFrame) -> None:
|
|
82
82
|
pass
|