pyfemtet 0.4.20__py3-none-any.whl → 0.4.23__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 +0 -2
- pyfemtet/message/locales/ja/LC_MESSAGES/messages.mo +0 -0
- pyfemtet/message/locales/ja/LC_MESSAGES/messages.po +107 -96
- pyfemtet/message/locales/messages.pot +104 -96
- pyfemtet/message/messages.py +15 -1
- pyfemtet/opt/_femopt.py +289 -230
- pyfemtet/opt/_femopt_core.py +118 -49
- pyfemtet/opt/femprj_sample/ParametricIF.py +0 -2
- pyfemtet/opt/femprj_sample/cad_ex01_NX.py +0 -8
- pyfemtet/opt/femprj_sample/cad_ex01_SW.py +0 -8
- pyfemtet/opt/femprj_sample/gal_ex58_parametric.py +0 -8
- pyfemtet/opt/femprj_sample/gau_ex08_parametric.py +0 -8
- pyfemtet/opt/femprj_sample/her_ex40_parametric.py +0 -8
- pyfemtet/opt/femprj_sample/paswat_ex1_parametric.py +0 -8
- pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py +0 -8
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.py +0 -8
- pyfemtet/opt/femprj_sample/wat_ex14_parametric_parallel.py +0 -8
- pyfemtet/opt/femprj_sample_jp/ParametricIF_jp.py +0 -2
- pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -8
- pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +0 -8
- pyfemtet/opt/interface/_femtet.py +77 -24
- pyfemtet/opt/opt/_base.py +25 -18
- pyfemtet/opt/opt/_optuna.py +53 -14
- pyfemtet/opt/opt/_optuna_botorch_helper.py +209 -0
- pyfemtet/opt/opt/_scipy.py +1 -1
- pyfemtet/opt/opt/_scipy_scalar.py +1 -1
- pyfemtet/opt/parameter.py +113 -0
- pyfemtet/opt/visualization/complex_components/main_graph.py +22 -5
- pyfemtet/opt/visualization/complex_components/pm_graph.py +77 -25
- pyfemtet/opt/visualization/complex_components/pm_graph_creator.py +7 -0
- pyfemtet/opt/visualization/process_monitor/application.py +10 -6
- pyfemtet/opt/visualization/process_monitor/pages.py +102 -0
- pyfemtet/opt/visualization/result_viewer/application.py +6 -0
- pyfemtet/opt/visualization/result_viewer/pages.py +1 -1
- {pyfemtet-0.4.20.dist-info → pyfemtet-0.4.23.dist-info}/METADATA +3 -4
- {pyfemtet-0.4.20.dist-info → pyfemtet-0.4.23.dist-info}/RECORD +47 -59
- pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
- pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
- pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +0 -118
- pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.SLDPRT +0 -0
- pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.femprj +0 -0
- pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.py +0 -121
- pyfemtet/FemtetPJTSample/_her_ex40_parametric.py +0 -148
- pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +0 -58
- pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/her_ex40_parametric.py +0 -148
- pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +0 -65
- pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
- pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +0 -64
- {pyfemtet-0.4.20.dist-info → pyfemtet-0.4.23.dist-info}/LICENSE +0 -0
- {pyfemtet-0.4.20.dist-info → pyfemtet-0.4.23.dist-info}/WHEEL +0 -0
- {pyfemtet-0.4.20.dist-info → pyfemtet-0.4.23.dist-info}/entry_points.txt +0 -0
pyfemtet/opt/_femopt.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# built-in
|
|
2
|
+
import inspect
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import Optional, Any, Callable, List
|
|
2
5
|
import os
|
|
3
6
|
import datetime
|
|
4
7
|
from time import time, sleep
|
|
5
8
|
from threading import Thread
|
|
6
9
|
import json
|
|
10
|
+
from traceback import print_exception
|
|
7
11
|
|
|
8
12
|
# 3rd-party
|
|
9
13
|
import numpy as np
|
|
@@ -24,6 +28,30 @@ from pyfemtet.opt._femopt_core import (
|
|
|
24
28
|
logger,
|
|
25
29
|
)
|
|
26
30
|
from pyfemtet.message import Msg, encoding
|
|
31
|
+
from pyfemtet.opt.parameter import Parameter, Expression
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def add_worker(client, worker_name):
|
|
35
|
+
import sys
|
|
36
|
+
from subprocess import Popen, DEVNULL
|
|
37
|
+
|
|
38
|
+
current_n_workers = len(client.nthreads().keys())
|
|
39
|
+
|
|
40
|
+
Popen(
|
|
41
|
+
f'{sys.executable} -m dask worker '
|
|
42
|
+
f'{client.scheduler.address} '
|
|
43
|
+
f'--nthreads 1 '
|
|
44
|
+
f'--nworkers 1 '
|
|
45
|
+
f'--name {worker_name} '
|
|
46
|
+
f'--no-nanny',
|
|
47
|
+
shell=True,
|
|
48
|
+
stderr=DEVNULL,
|
|
49
|
+
stdout=DEVNULL,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# worker が増えるまで待つ
|
|
53
|
+
client.wait_for_workers(n_workers=current_n_workers + 1)
|
|
54
|
+
|
|
27
55
|
|
|
28
56
|
|
|
29
57
|
class FEMOpt:
|
|
@@ -82,7 +110,6 @@ class FEMOpt:
|
|
|
82
110
|
self.monitor_process_future = None
|
|
83
111
|
self.monitor_server_kwargs = dict()
|
|
84
112
|
self.monitor_process_worker_name = None
|
|
85
|
-
self._is_error_exit = False
|
|
86
113
|
|
|
87
114
|
# multiprocess 時に pickle できないオブジェクト参照の削除
|
|
88
115
|
def __getstate__(self):
|
|
@@ -109,7 +136,8 @@ class FEMOpt:
|
|
|
109
136
|
lower_bound: float or None = None,
|
|
110
137
|
upper_bound: float or None = None,
|
|
111
138
|
step: float or None = None,
|
|
112
|
-
|
|
139
|
+
properties: Optional[dict] = None,
|
|
140
|
+
pass_to_fem: Optional[bool] = True,
|
|
113
141
|
):
|
|
114
142
|
"""Adds a parameter to the optimization problem.
|
|
115
143
|
|
|
@@ -119,34 +147,63 @@ class FEMOpt:
|
|
|
119
147
|
lower_bound (float or None, optional): The lower bound of the parameter. Defaults to None. However, this argument is required for some algorithms.
|
|
120
148
|
upper_bound (float or None, optional): The upper bound of the parameter. Defaults to None. However, this argument is required for some algorithms.
|
|
121
149
|
step (float or None, optional): The step of parameter. Defaults to None.
|
|
122
|
-
|
|
150
|
+
properties (dict, optional): Additional information about the parameter. Defaults to None.
|
|
151
|
+
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.
|
|
123
152
|
Raises:
|
|
124
153
|
ValueError: If initial_value is not specified and the value for the given name is also not specified.
|
|
125
154
|
|
|
126
155
|
"""
|
|
127
156
|
|
|
128
157
|
_check_bound(lower_bound, upper_bound, name)
|
|
129
|
-
value = self.fem.check_param_value(name)
|
|
130
|
-
if initial_value is None:
|
|
131
|
-
if value is not None:
|
|
132
|
-
initial_value = value
|
|
133
|
-
else:
|
|
134
|
-
raise ValueError('initial_value を指定してください.')
|
|
135
158
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
pdf = pd.DataFrame(d, index=[0], dtype=object)
|
|
145
|
-
|
|
146
|
-
if len(self.opt.parameters) == 0:
|
|
147
|
-
self.opt.parameters = pdf
|
|
159
|
+
if pass_to_fem:
|
|
160
|
+
value = self.fem.check_param_value(name)
|
|
161
|
+
if initial_value is None:
|
|
162
|
+
if value is not None:
|
|
163
|
+
initial_value = value
|
|
164
|
+
else:
|
|
165
|
+
raise ValueError('initial_value を指定してください.')
|
|
148
166
|
else:
|
|
149
|
-
|
|
167
|
+
if initial_value is None:
|
|
168
|
+
raise ValueError('initial_value を指定してください.')
|
|
169
|
+
|
|
170
|
+
prm = Parameter(
|
|
171
|
+
name=name,
|
|
172
|
+
value=float(initial_value),
|
|
173
|
+
lower_bound=float(lower_bound) if lower_bound is not None else None,
|
|
174
|
+
upper_bound=float(upper_bound) if upper_bound is not None else None,
|
|
175
|
+
step=float(step) if step is not None else None,
|
|
176
|
+
pass_to_fem=pass_to_fem,
|
|
177
|
+
properties=properties,
|
|
178
|
+
)
|
|
179
|
+
self.opt.variables.add_parameter(prm)
|
|
180
|
+
|
|
181
|
+
def add_expression(
|
|
182
|
+
self,
|
|
183
|
+
name: str,
|
|
184
|
+
fun: Callable[[Any], float],
|
|
185
|
+
properties=property,
|
|
186
|
+
kwargs: Optional[dict] = None,
|
|
187
|
+
pass_to_fem=True,
|
|
188
|
+
):
|
|
189
|
+
"""Add expression to the optimization problem.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
name (str): The name of the variable.
|
|
193
|
+
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``.
|
|
194
|
+
properties ([type], optional): Property names and their values of the variable. Defaults to property.
|
|
195
|
+
kwargs (Optional[dict], optional): Remaining arguments of ``fun``. Defaults to None.
|
|
196
|
+
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.
|
|
197
|
+
"""
|
|
198
|
+
exp = Expression(
|
|
199
|
+
name=name,
|
|
200
|
+
value=None,
|
|
201
|
+
properties=properties,
|
|
202
|
+
fun=fun,
|
|
203
|
+
kwargs=kwargs if kwargs else {},
|
|
204
|
+
pass_to_fem=pass_to_fem,
|
|
205
|
+
)
|
|
206
|
+
self.opt.variables.add_expression(exp)
|
|
150
207
|
|
|
151
208
|
def add_objective(
|
|
152
209
|
self,
|
|
@@ -250,20 +307,49 @@ class FEMOpt:
|
|
|
250
307
|
|
|
251
308
|
self.opt.constraints[name] = Constraint(fun, name, lower_bound, upper_bound, strict, args, kwargs)
|
|
252
309
|
|
|
253
|
-
def
|
|
254
|
-
|
|
310
|
+
def add_parameter_constraint(
|
|
311
|
+
self,
|
|
312
|
+
fun: Callable[[List[float], Any], float],
|
|
313
|
+
name: Optional[str] = None,
|
|
314
|
+
lower_bound: Optional[float] = None,
|
|
315
|
+
upper_bound: Optional[float] = None,
|
|
316
|
+
):
|
|
317
|
+
"""Add constraint in case of parameter-only.
|
|
255
318
|
|
|
256
319
|
Args:
|
|
257
|
-
|
|
320
|
+
fun (Callable[[List[float], Any], float]): Function to constraint. The name of arguments must be one of ones of parameter or variables.
|
|
321
|
+
name (Optional[str], optional): Name of constraint. Defaults to None and then name is set to 'cns_{i}'.
|
|
322
|
+
lower_bound (Optional[float], optional): Lower bound of return value. Defaults to None.
|
|
323
|
+
upper_bound (Optional[float], optional): Upper bound of return value. Defaults to None.
|
|
324
|
+
"""
|
|
258
325
|
|
|
259
|
-
|
|
260
|
-
|
|
326
|
+
# candidate default name
|
|
327
|
+
if name is None:
|
|
328
|
+
prefix = Constraint.default_name
|
|
329
|
+
i = 0
|
|
330
|
+
while True:
|
|
331
|
+
candidate = f'{prefix}_{str(int(i))}'
|
|
332
|
+
is_existing = candidate in list(self.opt.constraints.keys())
|
|
333
|
+
if not is_existing:
|
|
334
|
+
break
|
|
335
|
+
else:
|
|
336
|
+
i += 1
|
|
337
|
+
name = candidate
|
|
261
338
|
|
|
262
|
-
|
|
263
|
-
|
|
339
|
+
# assert at least 1 bound exist
|
|
340
|
+
assert lower_bound is not None or upper_bound is not None, Msg.ERR_NO_BOUNDS
|
|
264
341
|
|
|
265
|
-
|
|
266
|
-
|
|
342
|
+
from pyfemtet.opt._femopt_core import ParameterConstraint
|
|
343
|
+
self.opt.constraints[name] = ParameterConstraint(fun, name, lower_bound, upper_bound, self.opt)
|
|
344
|
+
if hasattr(self.opt, 'add_parameter_constraints'):
|
|
345
|
+
prm_args = [p.name for p in inspect.signature(fun).parameters.values()]
|
|
346
|
+
if lower_bound is not None:
|
|
347
|
+
self.opt.add_parameter_constraints(lambda *args, **kwargs: fun(*args, **kwargs) - lower_bound, prm_args=prm_args)
|
|
348
|
+
if upper_bound is not None:
|
|
349
|
+
self.opt.add_parameter_constraints(lambda *args, **kwargs: upper_bound - fun(*args, **kwargs), prm_args=prm_args)
|
|
350
|
+
|
|
351
|
+
def get_parameter(self, format='dict'):
|
|
352
|
+
raise DeprecationWarning('FEMOpt.get_parameter() was deprecated. Use Femopt.opt.get_parameter() instead.')
|
|
267
353
|
|
|
268
354
|
def set_monitor_host(self, host=None, port=None):
|
|
269
355
|
"""Sets up the monitor server with the specified host and port.
|
|
@@ -292,6 +378,7 @@ class FEMOpt:
|
|
|
292
378
|
n_parallel=1,
|
|
293
379
|
timeout=None,
|
|
294
380
|
wait_setup=True,
|
|
381
|
+
confirm_before_exit=True,
|
|
295
382
|
):
|
|
296
383
|
"""Runs the main optimization process.
|
|
297
384
|
|
|
@@ -300,6 +387,7 @@ class FEMOpt:
|
|
|
300
387
|
n_parallel (int, optional): The number of parallel processes. Defaults to 1.
|
|
301
388
|
timeout (float or None, optional): The maximum amount of time in seconds that each trial can run. Defaults to None.
|
|
302
389
|
wait_setup (bool, optional): Wait for all workers launching FEM system. Defaults to True.
|
|
390
|
+
confirm_before_exit (bool, optional): Insert stop before exit to continue to show process monitor.
|
|
303
391
|
|
|
304
392
|
Tip:
|
|
305
393
|
If set_monitor_host() is not executed, a local server for monitoring will be started at localhost:8080.
|
|
@@ -338,8 +426,9 @@ class FEMOpt:
|
|
|
338
426
|
self.opt.method_checker.check_seed()
|
|
339
427
|
|
|
340
428
|
is_incomplete_bounds = False
|
|
341
|
-
|
|
342
|
-
|
|
429
|
+
prm: Parameter = None
|
|
430
|
+
for prm in self.opt.variables.parameters.values():
|
|
431
|
+
lb, ub = prm.lower_bound, prm.upper_bound
|
|
343
432
|
is_incomplete_bounds = is_incomplete_bounds + (lb is None) + (ub is None)
|
|
344
433
|
if is_incomplete_bounds:
|
|
345
434
|
self.opt.method_checker.check_incomplete_bounds()
|
|
@@ -348,6 +437,10 @@ class FEMOpt:
|
|
|
348
437
|
self.opt.n_trials = n_trials
|
|
349
438
|
self.opt.timeout = timeout
|
|
350
439
|
|
|
440
|
+
# resolve expression dependencies
|
|
441
|
+
self.opt.variables.resolve()
|
|
442
|
+
self.opt.variables.evaluate()
|
|
443
|
+
|
|
351
444
|
# クラスターの設定
|
|
352
445
|
self.opt.is_cluster = self.scheduler_address is not None
|
|
353
446
|
if self.opt.is_cluster:
|
|
@@ -373,16 +466,7 @@ class FEMOpt:
|
|
|
373
466
|
# monitor worker の設定
|
|
374
467
|
logger.info('Launching monitor server. This may take a few seconds.')
|
|
375
468
|
self.monitor_process_worker_name = datetime.datetime.now().strftime("Monitor%Y%m%d%H%M%S")
|
|
376
|
-
|
|
377
|
-
from subprocess import Popen
|
|
378
|
-
import sys
|
|
379
|
-
Popen(
|
|
380
|
-
f'{sys.executable} -m dask worker {self.client.scheduler.address} --nthreads 1 --nworkers 1 --name {self.monitor_process_worker_name} --no-nanny',
|
|
381
|
-
shell=True
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
# monitor 用 worker が増えるまで待つ
|
|
385
|
-
self.client.wait_for_workers(n_workers=current_n_workers + 1)
|
|
469
|
+
add_worker(self.client, self.monitor_process_worker_name)
|
|
386
470
|
|
|
387
471
|
else:
|
|
388
472
|
# ローカルクラスターを構築
|
|
@@ -400,200 +484,175 @@ class FEMOpt:
|
|
|
400
484
|
self.monitor_process_worker_name = worker_addresses[0]
|
|
401
485
|
worker_addresses[0] = 'Main'
|
|
402
486
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
487
|
+
with self.client.cluster as _cluster, self.client as _client:
|
|
488
|
+
|
|
489
|
+
# Femtet 特有の処理
|
|
490
|
+
metadata = None
|
|
491
|
+
if isinstance(self.fem, FemtetInterface):
|
|
492
|
+
# 結果 csv に記載する femprj に関する情報
|
|
493
|
+
metadata = json.dumps(
|
|
494
|
+
dict(
|
|
495
|
+
femprj_path=self.fem.original_femprj_path,
|
|
496
|
+
model_name=self.fem.model_name
|
|
497
|
+
)
|
|
411
498
|
)
|
|
499
|
+
# Femtet の parametric 設定を目的関数に用いるかどうか
|
|
500
|
+
if self.fem.parametric_output_indexes_use_as_objective is not None:
|
|
501
|
+
from pyfemtet.opt.interface._femtet_parametric import add_parametric_results_as_objectives
|
|
502
|
+
indexes = list(self.fem.parametric_output_indexes_use_as_objective.keys())
|
|
503
|
+
directions = list(self.fem.parametric_output_indexes_use_as_objective.values())
|
|
504
|
+
add_parametric_results_as_objectives(
|
|
505
|
+
self,
|
|
506
|
+
indexes,
|
|
507
|
+
directions,
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
# actor の設定
|
|
511
|
+
self.status = OptimizationStatus(_client)
|
|
512
|
+
self.worker_status_list = [OptimizationStatus(_client, name) for name in worker_addresses] # tqdm 検討
|
|
513
|
+
self.status.set(OptimizationStatus.SETTING_UP)
|
|
514
|
+
self.history = History(
|
|
515
|
+
self.history_path,
|
|
516
|
+
self.opt.variables.get_parameter_names(),
|
|
517
|
+
list(self.opt.objectives.keys()),
|
|
518
|
+
list(self.opt.constraints.keys()),
|
|
519
|
+
_client,
|
|
520
|
+
metadata,
|
|
412
521
|
)
|
|
413
|
-
# Femtet の parametric 設定を目的関数に用いるかどうか
|
|
414
|
-
if self.fem.parametric_output_indexes_use_as_objective is not None:
|
|
415
|
-
from pyfemtet.opt.interface._femtet_parametric import add_parametric_results_as_objectives
|
|
416
|
-
indexes = list(self.fem.parametric_output_indexes_use_as_objective.keys())
|
|
417
|
-
directions = list(self.fem.parametric_output_indexes_use_as_objective.values())
|
|
418
|
-
add_parametric_results_as_objectives(
|
|
419
|
-
self,
|
|
420
|
-
indexes,
|
|
421
|
-
directions,
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
# actor の設定
|
|
425
|
-
self.status = OptimizationStatus(self.client)
|
|
426
|
-
self.worker_status_list = [OptimizationStatus(self.client, name) for name in worker_addresses] # tqdm 検討
|
|
427
|
-
self.status.set(OptimizationStatus.SETTING_UP)
|
|
428
|
-
self.history = History(
|
|
429
|
-
self.history_path,
|
|
430
|
-
self.opt.parameters['name'].to_list(),
|
|
431
|
-
list(self.opt.objectives.keys()),
|
|
432
|
-
list(self.opt.constraints.keys()),
|
|
433
|
-
self.client,
|
|
434
|
-
metadata,
|
|
435
|
-
)
|
|
436
522
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
# fem
|
|
454
|
-
self.fem._setup_before_parallel(self.client)
|
|
455
|
-
|
|
456
|
-
# opt
|
|
457
|
-
self.opt.fem_class = type(self.fem)
|
|
458
|
-
self.opt.fem_kwargs = self.fem.kwargs
|
|
459
|
-
self.opt.entire_status = self.status
|
|
460
|
-
self.opt.history = self.history
|
|
461
|
-
self.opt._setup_before_parallel()
|
|
462
|
-
|
|
463
|
-
# クラスターでの計算開始
|
|
464
|
-
self.status.set(OptimizationStatus.LAUNCHING_FEM)
|
|
465
|
-
start = time()
|
|
466
|
-
calc_futures = self.client.map(
|
|
467
|
-
self.opt._run,
|
|
468
|
-
subprocess_indices,
|
|
469
|
-
[self.worker_status_list] * len(subprocess_indices),
|
|
470
|
-
[wait_setup] * len(subprocess_indices),
|
|
471
|
-
workers=worker_addresses,
|
|
472
|
-
allow_other_workers=False,
|
|
473
|
-
)
|
|
474
|
-
|
|
475
|
-
t_main = None
|
|
476
|
-
if not self.opt.is_cluster:
|
|
477
|
-
# ローカルプロセスでの計算(opt._main 相当の処理)
|
|
478
|
-
subprocess_idx = 0
|
|
479
|
-
|
|
480
|
-
# set_fem
|
|
481
|
-
self.opt.fem = self.fem
|
|
482
|
-
self.opt._reconstruct_fem(skip_reconstruct=True)
|
|
483
|
-
|
|
484
|
-
t_main = Thread(
|
|
485
|
-
target=self.opt._run,
|
|
486
|
-
args=(
|
|
487
|
-
subprocess_idx,
|
|
488
|
-
self.worker_status_list,
|
|
489
|
-
wait_setup,
|
|
490
|
-
),
|
|
491
|
-
kwargs=dict(
|
|
492
|
-
skip_set_fem=True,
|
|
493
|
-
)
|
|
523
|
+
# launch monitor
|
|
524
|
+
self.monitor_process_future = _client.submit(
|
|
525
|
+
# func
|
|
526
|
+
_start_monitor_server,
|
|
527
|
+
# args
|
|
528
|
+
self.history,
|
|
529
|
+
self.status,
|
|
530
|
+
worker_addresses,
|
|
531
|
+
self.worker_status_list,
|
|
532
|
+
# kwargs
|
|
533
|
+
**self.monitor_server_kwargs,
|
|
534
|
+
# kwargs of submit
|
|
535
|
+
workers=self.monitor_process_worker_name,
|
|
536
|
+
allow_other_workers=False
|
|
494
537
|
)
|
|
495
|
-
t_main.start()
|
|
496
|
-
|
|
497
|
-
# save history
|
|
498
|
-
def save_history():
|
|
499
|
-
while True:
|
|
500
|
-
sleep(2)
|
|
501
|
-
try:
|
|
502
|
-
self.history.save()
|
|
503
|
-
except PermissionError:
|
|
504
|
-
logger.warning(Msg.WARN_HISTORY_CSV_NOT_ACCESSIBLE)
|
|
505
|
-
if self.status.get() >= OptimizationStatus.TERMINATED:
|
|
506
|
-
break
|
|
507
|
-
|
|
508
|
-
t_save_history = Thread(target=save_history)
|
|
509
|
-
t_save_history.start()
|
|
510
538
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if any(opt_crashed_list):
|
|
532
|
-
self._is_error_exit = True
|
|
533
|
-
|
|
534
|
-
return self.history.local_data
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
def terminate_all(self):
|
|
538
|
-
"""Try to terminate all launched processes.
|
|
539
|
-
|
|
540
|
-
If distributed computing, Scheduler and Workers will NOT be terminated.
|
|
541
|
-
|
|
542
|
-
"""
|
|
543
|
-
|
|
544
|
-
# monitor が terminated 状態で少なくとも一度更新されなければ running のまま固まる
|
|
545
|
-
sleep(1)
|
|
546
|
-
|
|
547
|
-
# terminate monitor process
|
|
548
|
-
self.status.set(OptimizationStatus.TERMINATE_ALL)
|
|
549
|
-
logger.info(self.monitor_process_future.result())
|
|
550
|
-
sleep(1)
|
|
551
|
-
|
|
552
|
-
# terminate actors
|
|
553
|
-
self.client.cancel(self.history._future, force=True)
|
|
554
|
-
self.client.cancel(self.status._future, force=True)
|
|
555
|
-
for worker_status in self.worker_status_list:
|
|
556
|
-
self.client.cancel(worker_status._future, force=True)
|
|
557
|
-
logger.info('Terminate actors.')
|
|
558
|
-
sleep(1)
|
|
559
|
-
|
|
560
|
-
# terminate monitor worker
|
|
561
|
-
n_workers = len(self.client.nthreads())
|
|
562
|
-
|
|
563
|
-
found_worker_dict = self.client.retire_workers(
|
|
564
|
-
names=[self.monitor_process_worker_name], # name
|
|
565
|
-
close_workers=True,
|
|
566
|
-
remove=True,
|
|
567
|
-
)
|
|
568
|
-
|
|
569
|
-
if len(found_worker_dict) == 0:
|
|
570
|
-
found_worker_dict = self.client.retire_workers(
|
|
571
|
-
workers=[self.monitor_process_worker_name], # address
|
|
572
|
-
close_workers=True,
|
|
573
|
-
remove=True,
|
|
539
|
+
# fem
|
|
540
|
+
self.fem._setup_before_parallel(_client)
|
|
541
|
+
|
|
542
|
+
# opt
|
|
543
|
+
self.opt.fem_class = type(self.fem)
|
|
544
|
+
self.opt.fem_kwargs = self.fem.kwargs
|
|
545
|
+
self.opt.entire_status = self.status
|
|
546
|
+
self.opt.history = self.history
|
|
547
|
+
self.opt._setup_before_parallel()
|
|
548
|
+
|
|
549
|
+
# クラスターでの計算開始
|
|
550
|
+
self.status.set(OptimizationStatus.LAUNCHING_FEM)
|
|
551
|
+
start = time()
|
|
552
|
+
calc_futures = _client.map(
|
|
553
|
+
self.opt._run,
|
|
554
|
+
subprocess_indices,
|
|
555
|
+
[self.worker_status_list] * len(subprocess_indices),
|
|
556
|
+
[wait_setup] * len(subprocess_indices),
|
|
557
|
+
workers=worker_addresses,
|
|
558
|
+
allow_other_workers=False,
|
|
574
559
|
)
|
|
575
560
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
561
|
+
t_main = None
|
|
562
|
+
if not self.opt.is_cluster:
|
|
563
|
+
# ローカルプロセスでの計算(opt._main 相当の処理)
|
|
564
|
+
subprocess_idx = 0
|
|
565
|
+
|
|
566
|
+
# set_fem
|
|
567
|
+
self.opt.fem = self.fem
|
|
568
|
+
self.opt._reconstruct_fem(skip_reconstruct=True)
|
|
569
|
+
|
|
570
|
+
t_main = Thread(
|
|
571
|
+
target=self.opt._run,
|
|
572
|
+
args=(
|
|
573
|
+
subprocess_idx,
|
|
574
|
+
self.worker_status_list,
|
|
575
|
+
wait_setup,
|
|
576
|
+
),
|
|
577
|
+
kwargs=dict(
|
|
578
|
+
skip_set_fem=True,
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
t_main.start()
|
|
582
|
+
|
|
583
|
+
# save history
|
|
584
|
+
def save_history():
|
|
585
|
+
while True:
|
|
586
|
+
sleep(2)
|
|
587
|
+
try:
|
|
588
|
+
self.history.save()
|
|
589
|
+
except PermissionError:
|
|
590
|
+
logger.warning(Msg.WARN_HISTORY_CSV_NOT_ACCESSIBLE)
|
|
591
|
+
if self.status.get() >= OptimizationStatus.TERMINATED:
|
|
592
|
+
break
|
|
593
|
+
|
|
594
|
+
t_save_history = Thread(target=save_history)
|
|
595
|
+
t_save_history.start()
|
|
596
|
+
|
|
597
|
+
# ===== 終了 =====
|
|
598
|
+
|
|
599
|
+
# クラスターの Unexpected Exception のリストを取得
|
|
600
|
+
opt_exceptions: list[Exception or None] = _client.gather(calc_futures) # gather() で終了待ちも兼ねる
|
|
601
|
+
|
|
602
|
+
# ローカルの opt で計算している場合、その Exception も取得
|
|
603
|
+
local_opt_exception: Exception or None = None
|
|
604
|
+
if not self.opt.is_cluster:
|
|
605
|
+
if t_main is not None:
|
|
606
|
+
t_main.join() # 終了待ち
|
|
607
|
+
local_opt_exception = self.opt._exception # Exception を取得
|
|
608
|
+
opt_exceptions.append(local_opt_exception)
|
|
609
|
+
|
|
610
|
+
# 終了
|
|
611
|
+
self.status.set(OptimizationStatus.TERMINATED)
|
|
612
|
+
end = time()
|
|
613
|
+
|
|
614
|
+
# 一応
|
|
615
|
+
t_save_history.join()
|
|
616
|
+
|
|
617
|
+
# 結果通知
|
|
618
|
+
logger.info(Msg.OPTIMIZATION_FINISHED)
|
|
619
|
+
logger.info(self.history.path)
|
|
620
|
+
|
|
621
|
+
# monitor worker を終了する準備
|
|
622
|
+
# 実際の終了は monitor worker の終了時
|
|
623
|
+
self.status.set(OptimizationStatus.TERMINATE_ALL)
|
|
624
|
+
logger.info(self.monitor_process_future.result())
|
|
625
|
+
sleep(1) # monitor が terminated 状態で少なくとも一度更新されなければ running のまま固まる
|
|
626
|
+
|
|
627
|
+
# 全ての Exception を再表示
|
|
628
|
+
for i, opt_exception in enumerate(opt_exceptions):
|
|
629
|
+
if opt_exception is not None:
|
|
630
|
+
print()
|
|
631
|
+
print(f'===== unexpected exception raised on worker {i} =====')
|
|
632
|
+
print_exception(opt_exception)
|
|
633
|
+
print()
|
|
634
|
+
|
|
635
|
+
# monitor worker を残してユーザーが結果を確認できるようにする
|
|
636
|
+
if confirm_before_exit:
|
|
637
|
+
print()
|
|
638
|
+
print('='*len(Msg.CONFIRM_BEFORE_EXIT))
|
|
639
|
+
print(Msg.CONFIRM_BEFORE_EXIT)
|
|
640
|
+
print('='*len(Msg.CONFIRM_BEFORE_EXIT))
|
|
641
|
+
input()
|
|
642
|
+
|
|
643
|
+
return self.history.get_df() # with 文を抜けると actor は消えるが .copy() はこの段階では不要
|
|
644
|
+
|
|
645
|
+
@staticmethod
|
|
646
|
+
def terminate_all():
|
|
647
|
+
warnings.warn(
|
|
648
|
+
"terminate_all() is deprecated and will be removed in a future version. "
|
|
649
|
+
"In current and later versions, the equivalent of terminate_all() will be executed when optimize() finishes. "
|
|
650
|
+
"Therefore, you can simply remove terminate_all() from your code. "
|
|
651
|
+
"If you want to stop program before terminating monitor process, "
|
|
652
|
+
"use ``confirm_before_exit`` argument like ``FEMOpt.optimize(confirm_before_exit=True)``",
|
|
653
|
+
DeprecationWarning,
|
|
654
|
+
stacklevel=2
|
|
655
|
+
)
|
|
597
656
|
|
|
598
657
|
|
|
599
658
|
def _start_monitor_server(
|