pyfemtet 0.4.2__tar.gz → 0.4.4__tar.gz
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-0.4.4/PKG-INFO +97 -0
- pyfemtet-0.4.4/README.md +61 -0
- pyfemtet-0.4.4/pyfemtet/FemtetPJTSample/her_ex40_parametric.py +148 -0
- pyfemtet-0.4.4/pyfemtet/__init__.py +1 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/_femopt.py +48 -16
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/_femopt_core.py +14 -7
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/cad_ex01_NX.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/cad_ex01_NX.prt +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/cad_ex01_NX.py +132 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/cad_ex01_SW.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/cad_ex01_SW.py +132 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/gal_ex58_parametric.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/gal_ex58_parametric.py +75 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/gau_ex08_parametric.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/gau_ex08_parametric.py +59 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/her_ex40_parametric.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/her_ex40_parametric.py +137 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/paswat_ex1_parametric.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/paswat_ex1_parametric.py +61 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/paswat_ex1_parametric_parallel.py +62 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/wat_ex14_parametric.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample/wat_ex14_parametric.py +59 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/cad_ex01_NX_jp.py +126 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/cad_ex01_SW_jp.py +126 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/gal_ex58_parametric_jp.py +71 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/gau_ex08_parametric_jp.py +58 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/her_ex40_parametric_jp.py +137 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_jp.py +59 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +60 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.femprj +0 -0
- pyfemtet-0.4.4/pyfemtet/opt/femprj_sample_jp/wat_ex14_parametric_jp.py +57 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/interface/_femtet.py +36 -18
- pyfemtet-0.4.4/pyfemtet/opt/interface/_femtet_parametric.py +72 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/interface/_femtet_with_nx/_interface.py +2 -8
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/interface/_femtet_with_sldworks.py +2 -8
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/opt/_base.py +12 -3
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/visualization/_monitor.py +2 -2
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyproject.toml +5 -2
- pyfemtet-0.4.2/PKG-INFO +0 -48
- pyfemtet-0.4.2/README.md +0 -13
- pyfemtet-0.4.2/pyfemtet/__init__.py +0 -1
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/LICENSE +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.femprj +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.prt +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/NX_ex01/NX_ex01.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.SLDPRT +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.femprj +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/Sldworks_ex01/Sldworks_ex01.py +0 -0
- /pyfemtet-0.4.2/pyfemtet/FemtetPJTSample/her_ex40_parametric.py → /pyfemtet-0.4.4/pyfemtet/FemtetPJTSample/_her_ex40_parametric.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.femprj +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/gau_ex08_parametric.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/her_ex40_parametric.femprj +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/wat_ex14_parallel_parametric.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.femprj +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/FemtetPJTSample/wat_ex14_parametric.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/core.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/dispatch_extensions.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/logger.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/__init__.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/interface/__init__.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/interface/_base.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/interface/_femtet_with_nx/update_model.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/opt/__init__.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/opt/_optuna.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/visualization/__init__.py +0 -0
- {pyfemtet-0.4.2 → pyfemtet-0.4.4}/pyfemtet/opt/visualization/_graphs.py +0 -0
pyfemtet-0.4.4/PKG-INFO
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyfemtet
|
|
3
|
+
Version: 0.4.4
|
|
4
|
+
Summary: Design parameter optimization using Femtet.
|
|
5
|
+
Home-page: https://github.com/pyfemtet/pyfemtet
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Author: kazuma.naito
|
|
8
|
+
Author-email: kazuma.naito@murata.com
|
|
9
|
+
Requires-Python: >=3.9.3,<3.13
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Dist: botorch (>=0.9.5) ; python_version >= "3.12" and python_version < "3.13"
|
|
16
|
+
Requires-Dist: botorch (>=0.9.5,<0.10.0) ; python_version < "3.12"
|
|
17
|
+
Requires-Dist: colorlog (>=6.8.0,<7.0.0)
|
|
18
|
+
Requires-Dist: dash (>=2.14.2,<3.0.0)
|
|
19
|
+
Requires-Dist: dash-bootstrap-components (>=1.5.0,<2.0.0)
|
|
20
|
+
Requires-Dist: dask (>=2023.12.1,<2024.0.0)
|
|
21
|
+
Requires-Dist: distributed (>=2023.12.1,<2024.0.0)
|
|
22
|
+
Requires-Dist: femtetutils (>=1.0.0,<2.0.0)
|
|
23
|
+
Requires-Dist: numpy (>=1.26.2,<2.0.0)
|
|
24
|
+
Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
|
|
25
|
+
Requires-Dist: optuna (>=3.4.0,<4.0.0)
|
|
26
|
+
Requires-Dist: optuna-integration (>=3.6.0,<4.0.0)
|
|
27
|
+
Requires-Dist: pandas (>=2.1.3,<3.0.0)
|
|
28
|
+
Requires-Dist: plotly (>=5.18.0,<6.0.0)
|
|
29
|
+
Requires-Dist: psutil (>=5.9.6,<6.0.0)
|
|
30
|
+
Requires-Dist: pywin32 (>=306,<307)
|
|
31
|
+
Requires-Dist: scipy (>=1.11.4,<2.0.0)
|
|
32
|
+
Requires-Dist: tqdm (>=4.66.1,<5.0.0)
|
|
33
|
+
Project-URL: Repository, https://github.com/pyfemtet/pyfemtet
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# Welcome! PyFemtet is the extension package for Femtet.
|
|
37
|
+
|
|
38
|
+
## Main Features
|
|
39
|
+
|
|
40
|
+
<img src="img/pyfemtet_conceptual_diagram.png" width="450">
|
|
41
|
+
|
|
42
|
+
> [!NOTE]
|
|
43
|
+
> For details, see the documentation of "Related Pages" section.
|
|
44
|
+
|
|
45
|
+
### Design Parameter Optimization
|
|
46
|
+
- Single or multiple objective optimization
|
|
47
|
+
- Progress monitor GUI
|
|
48
|
+
- Parallel computation with controling multiple Femtet processes
|
|
49
|
+
- Result output in easy-to-analyze csv format for Excel and other tools
|
|
50
|
+
|
|
51
|
+
### Dispatch Extensions
|
|
52
|
+
- Create `CFemtet` object with process ID specification
|
|
53
|
+
|
|
54
|
+
## Related Pages
|
|
55
|
+
|
|
56
|
+
- Femtet Website: https://www.muratasoftware.com/
|
|
57
|
+
- Documentation (English): https://pyfemtet.readthedocs.io/en/stable/
|
|
58
|
+
- ドキュメント(日本語): https://pyfemtet.readthedocs.io/jp/stable/
|
|
59
|
+
- Source code: https://github.com/pyfemtet/pyfemtet
|
|
60
|
+
- Bug reports: https://github.com/pyfemtet/pyfemtet/issues
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Getting Started
|
|
64
|
+
|
|
65
|
+
> [!NOTE]
|
|
66
|
+
> Currently Femtet and `pyfemtet` supports **Windows only**.
|
|
67
|
+
|
|
68
|
+
### 1. Install Femtet
|
|
69
|
+
|
|
70
|
+
- Access [Murata Software Website](https://www.muratasoftware.com/) and get **Femtet trial version** or purchase a lisence.
|
|
71
|
+
- Get installer and launch it.
|
|
72
|
+
- **Run 'EnableMacros'** from the start menu.
|
|
73
|
+
> [!NOTE]
|
|
74
|
+
> This procedure requires administrator privileges.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### 2. Install PyFemtet
|
|
78
|
+
|
|
79
|
+
> [!NOTE]
|
|
80
|
+
> The commands on this section are for `CMD` or `PowerShell` on with `py launcher`.
|
|
81
|
+
> For a virtual environment, replace `py` to `python`.
|
|
82
|
+
> If you do not have Python, please install it first.
|
|
83
|
+
|
|
84
|
+
- Get `pyfemtet` via following command:
|
|
85
|
+
|
|
86
|
+
```py -m pip install pyfemtet```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### 3. Setting `win32com` Constants
|
|
90
|
+
|
|
91
|
+
- Run following command to enable COM constants:
|
|
92
|
+
|
|
93
|
+
```py -m win32com.client.makepy FemtetMacro```
|
|
94
|
+
|
|
95
|
+
That's all! Now you can use Femtet's extention features via `pyfemtet`.
|
|
96
|
+
For more information including sample code and FEM projects, see the documentation of "Related Pages" section.
|
|
97
|
+
|
pyfemtet-0.4.4/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Welcome! PyFemtet is the extension package for Femtet.
|
|
2
|
+
|
|
3
|
+
## Main Features
|
|
4
|
+
|
|
5
|
+
<img src="img/pyfemtet_conceptual_diagram.png" width="450">
|
|
6
|
+
|
|
7
|
+
> [!NOTE]
|
|
8
|
+
> For details, see the documentation of "Related Pages" section.
|
|
9
|
+
|
|
10
|
+
### Design Parameter Optimization
|
|
11
|
+
- Single or multiple objective optimization
|
|
12
|
+
- Progress monitor GUI
|
|
13
|
+
- Parallel computation with controling multiple Femtet processes
|
|
14
|
+
- Result output in easy-to-analyze csv format for Excel and other tools
|
|
15
|
+
|
|
16
|
+
### Dispatch Extensions
|
|
17
|
+
- Create `CFemtet` object with process ID specification
|
|
18
|
+
|
|
19
|
+
## Related Pages
|
|
20
|
+
|
|
21
|
+
- Femtet Website: https://www.muratasoftware.com/
|
|
22
|
+
- Documentation (English): https://pyfemtet.readthedocs.io/en/stable/
|
|
23
|
+
- ドキュメント(日本語): https://pyfemtet.readthedocs.io/jp/stable/
|
|
24
|
+
- Source code: https://github.com/pyfemtet/pyfemtet
|
|
25
|
+
- Bug reports: https://github.com/pyfemtet/pyfemtet/issues
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Getting Started
|
|
29
|
+
|
|
30
|
+
> [!NOTE]
|
|
31
|
+
> Currently Femtet and `pyfemtet` supports **Windows only**.
|
|
32
|
+
|
|
33
|
+
### 1. Install Femtet
|
|
34
|
+
|
|
35
|
+
- Access [Murata Software Website](https://www.muratasoftware.com/) and get **Femtet trial version** or purchase a lisence.
|
|
36
|
+
- Get installer and launch it.
|
|
37
|
+
- **Run 'EnableMacros'** from the start menu.
|
|
38
|
+
> [!NOTE]
|
|
39
|
+
> This procedure requires administrator privileges.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### 2. Install PyFemtet
|
|
43
|
+
|
|
44
|
+
> [!NOTE]
|
|
45
|
+
> The commands on this section are for `CMD` or `PowerShell` on with `py launcher`.
|
|
46
|
+
> For a virtual environment, replace `py` to `python`.
|
|
47
|
+
> If you do not have Python, please install it first.
|
|
48
|
+
|
|
49
|
+
- Get `pyfemtet` via following command:
|
|
50
|
+
|
|
51
|
+
```py -m pip install pyfemtet```
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### 3. Setting `win32com` Constants
|
|
55
|
+
|
|
56
|
+
- Run following command to enable COM constants:
|
|
57
|
+
|
|
58
|
+
```py -m win32com.client.makepy FemtetMacro```
|
|
59
|
+
|
|
60
|
+
That's all! Now you can use Femtet's extention features via `pyfemtet`.
|
|
61
|
+
For more information including sample code and FEM projects, see the documentation of "Related Pages" section.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Single-objective optimization: Resonant frequency of a circular patch antenna
|
|
2
|
+
|
|
3
|
+
Using Femtet’s electromagnetic wave analysis solver,
|
|
4
|
+
we explain an example of setting the resonant frequency
|
|
5
|
+
of a circular patch antenna to a specific value.
|
|
6
|
+
"""
|
|
7
|
+
from time import sleep
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from scipy.signal import find_peaks
|
|
11
|
+
from tqdm import tqdm
|
|
12
|
+
from optuna.integration.botorch import BoTorchSampler
|
|
13
|
+
|
|
14
|
+
from pyfemtet.opt import OptunaOptimizer, FEMOpt
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SParameterCalculator:
|
|
18
|
+
"""This class is for calculating S-parameters and resonance frequencies."""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.freq = []
|
|
22
|
+
self.S = []
|
|
23
|
+
self.interpolated_function = None
|
|
24
|
+
self.resonance_frequency = None
|
|
25
|
+
self.minimum_S = None
|
|
26
|
+
|
|
27
|
+
def get_result_from_Femtet(self, Femtet):
|
|
28
|
+
"""Obtain the relationship between frequency and S-parameter from the Femtet analysis results."""
|
|
29
|
+
|
|
30
|
+
# Preparation
|
|
31
|
+
Femtet.OpenCurrentResult(True)
|
|
32
|
+
Gogh = Femtet.Gogh
|
|
33
|
+
|
|
34
|
+
# Obtain the frequency and S(1,1) for each mode
|
|
35
|
+
mode = 0
|
|
36
|
+
freq_list = []
|
|
37
|
+
dB_S_list = []
|
|
38
|
+
for mode in tqdm(range(Gogh.Hertz.nMode), 'Obtaining frequency and S-parameter'):
|
|
39
|
+
# Femtet result screen mode settings
|
|
40
|
+
Gogh.Hertz.Mode = mode
|
|
41
|
+
sleep(0.01)
|
|
42
|
+
# Get frequency
|
|
43
|
+
freq = Gogh.Hertz.GetFreq().Real
|
|
44
|
+
# Get S-parameters
|
|
45
|
+
comp_S = Gogh.Hertz.GetSMatrix(0, 0)
|
|
46
|
+
norm = np.linalg.norm((comp_S.Real, comp_S.Imag))
|
|
47
|
+
dB_S = 20 * np.log10(norm)
|
|
48
|
+
# Get results
|
|
49
|
+
freq_list.append(freq)
|
|
50
|
+
dB_S_list.append(dB_S)
|
|
51
|
+
self.freq = freq_list
|
|
52
|
+
self.S = dB_S_list
|
|
53
|
+
|
|
54
|
+
def calc_resonance_frequency(self):
|
|
55
|
+
"""Compute the frequency that gives the first peak for S-parameter."""
|
|
56
|
+
x = -np.array(self.S)
|
|
57
|
+
peaks, _ = find_peaks(x, height=None, threshold=None, distance=None, prominence=0.5, width=None, wlen=None, rel_height=0.5, plateau_size=None)
|
|
58
|
+
from pyfemtet.core import SolveError
|
|
59
|
+
if len(peaks) == 0:
|
|
60
|
+
raise SolveError('No peaks detected.')
|
|
61
|
+
self.resonance_frequency = self.freq[peaks[0]]
|
|
62
|
+
self.minimum_S = self.S[peaks[0]]
|
|
63
|
+
|
|
64
|
+
def get_resonance_frequency(self, Femtet):
|
|
65
|
+
"""Calculate the resonant frequency.
|
|
66
|
+
|
|
67
|
+
Note:
|
|
68
|
+
The objective or constraint function
|
|
69
|
+
must take a Femtet as its first argument
|
|
70
|
+
and must return a single float.
|
|
71
|
+
|
|
72
|
+
Params:
|
|
73
|
+
Femtet: An instance for using Femtet macros. For more information, see "Femtet Macro Help / CFemtet Class".
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
float: A resonance frequency.
|
|
77
|
+
"""
|
|
78
|
+
self.get_result_from_Femtet(Femtet)
|
|
79
|
+
self.calc_resonance_frequency()
|
|
80
|
+
f = self.resonance_frequency * 1e-9
|
|
81
|
+
return f # GHz
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def antenna_is_smaller_than_substrate(Femtet):
|
|
85
|
+
"""Calculate the relationship between antenna size and board size.
|
|
86
|
+
|
|
87
|
+
This function is used to constrain the model
|
|
88
|
+
from breaking down while changing parameters.
|
|
89
|
+
|
|
90
|
+
Params:
|
|
91
|
+
Femtet: An instance for using Femtet macros.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
float: Difference between the board size and antenna size. Must be equal to or grater than 1 mm.
|
|
95
|
+
"""
|
|
96
|
+
ant_r = Femtet.GetVariableValue('ant_r')
|
|
97
|
+
Sx = Femtet.GetVariableValue('sx')
|
|
98
|
+
return Sx/2 - ant_r
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def port_is_inside_antenna(Femtet):
|
|
102
|
+
"""Calculate the relationship between the feed port location and antenna size.
|
|
103
|
+
|
|
104
|
+
This function is used to constrain the model
|
|
105
|
+
from breaking down while changing parameters.
|
|
106
|
+
|
|
107
|
+
Params:
|
|
108
|
+
Femtet: An instance for using Femtet macros.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
float: Difference between the antenna edge and the position of the feed port. Must be equal to or grater than 1 mm.
|
|
112
|
+
"""
|
|
113
|
+
ant_r = Femtet.GetVariableValue('ant_r')
|
|
114
|
+
xf = Femtet.GetVariableValue('xf')
|
|
115
|
+
return ant_r - xf
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == '__main__':
|
|
119
|
+
# Define the object for calculating S-parameters and resonance frequencies.
|
|
120
|
+
s = SParameterCalculator()
|
|
121
|
+
|
|
122
|
+
# Define mathematical optimization object.
|
|
123
|
+
opt = OptunaOptimizer(
|
|
124
|
+
sampler_class=BoTorchSampler,
|
|
125
|
+
sampler_kwargs=dict(
|
|
126
|
+
n_startup_trials=10,
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Define FEMOpt object (This process integrates mathematical optimization and FEM.).
|
|
131
|
+
femopt = FEMOpt(opt=opt)
|
|
132
|
+
|
|
133
|
+
# Add design variables (Use variable names set in Femtet) to the optimization problem.
|
|
134
|
+
femopt.add_parameter('ant_r', 10, 5, 20)
|
|
135
|
+
femopt.add_parameter('sx', 50, 40, 60)
|
|
136
|
+
femopt.add_parameter('xf', 5, 1, 20)
|
|
137
|
+
|
|
138
|
+
# Add constraint to the optimization problem.
|
|
139
|
+
femopt.add_constraint(antenna_is_smaller_than_substrate, 'board_antenna_clearance', lower_bound=1)
|
|
140
|
+
femopt.add_constraint(port_is_inside_antenna, 'antenna_port_clearance', lower_bound=1)
|
|
141
|
+
|
|
142
|
+
# Add objective to the optimization problem.
|
|
143
|
+
# The target frequency is 3 GHz.
|
|
144
|
+
femopt.add_objective(s.get_resonance_frequency, 'First_resonant_frequency(GHz)', direction=3.0)
|
|
145
|
+
|
|
146
|
+
femopt.set_random_seed(42)
|
|
147
|
+
femopt.optimize(n_trials=20)
|
|
148
|
+
femopt.terminate_all()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.4"
|
|
@@ -81,6 +81,7 @@ class FEMOpt:
|
|
|
81
81
|
self.monitor_process_future = None
|
|
82
82
|
self.monitor_server_kwargs = dict()
|
|
83
83
|
self.monitor_process_worker_name = None
|
|
84
|
+
self._is_error_exit = False
|
|
84
85
|
|
|
85
86
|
# multiprocess 時に pickle できないオブジェクト参照の削除
|
|
86
87
|
def __getstate__(self):
|
|
@@ -328,12 +329,27 @@ class FEMOpt:
|
|
|
328
329
|
subprocess_indices = list(range(n_parallel))
|
|
329
330
|
worker_addresses = list(self.client.nthreads().keys())
|
|
330
331
|
|
|
332
|
+
# worker が足りない場合はエラー
|
|
333
|
+
if n_parallel > len(worker_addresses):
|
|
334
|
+
raise RuntimeError(f'n_parallel({n_parallel}) > n_workers({len(worker_addresses)}). Worker 数が不足しています。')
|
|
335
|
+
|
|
336
|
+
# worker が多い場合は閉じる
|
|
337
|
+
if n_parallel < len(worker_addresses):
|
|
338
|
+
used_worker_addresses = worker_addresses[:n_parallel] # 前から順番に選ぶ:CPU の早い / メモリの多い順に並べることが望ましい
|
|
339
|
+
unused_worker_addresses = worker_addresses[n_parallel:]
|
|
340
|
+
self.client.retire_workers(unused_worker_addresses, close_workers=True)
|
|
341
|
+
worker_addresses = used_worker_addresses
|
|
342
|
+
|
|
331
343
|
# monitor worker の設定
|
|
332
344
|
logger.info('Launching monitor server. This may take a few seconds.')
|
|
333
|
-
self.monitor_process_worker_name = datetime.datetime.now().strftime("Monitor
|
|
345
|
+
self.monitor_process_worker_name = datetime.datetime.now().strftime("Monitor%Y%m%d%H%M%S")
|
|
334
346
|
current_n_workers = len(self.client.nthreads().keys())
|
|
335
|
-
from
|
|
336
|
-
|
|
347
|
+
from subprocess import Popen
|
|
348
|
+
import sys
|
|
349
|
+
Popen(
|
|
350
|
+
f'{sys.executable} -m dask worker {self.client.scheduler.address} --nthreads 1 --nworkers 1 --name {self.monitor_process_worker_name} --no-nanny',
|
|
351
|
+
shell=True
|
|
352
|
+
)
|
|
337
353
|
|
|
338
354
|
# monitor 用 worker が増えるまで待つ
|
|
339
355
|
self.client.wait_for_workers(n_workers=current_n_workers + 1)
|
|
@@ -357,12 +373,17 @@ class FEMOpt:
|
|
|
357
373
|
# Femtet 特有の処理
|
|
358
374
|
metadata = None
|
|
359
375
|
if isinstance(self.fem, FemtetInterface):
|
|
376
|
+
# 結果 csv に記載する femprj に関する情報
|
|
360
377
|
metadata = json.dumps(
|
|
361
378
|
dict(
|
|
362
379
|
femprj_path=self.fem.original_femprj_path,
|
|
363
380
|
model_name=self.fem.model_name
|
|
364
381
|
)
|
|
365
382
|
)
|
|
383
|
+
# Femtet の parametric 設定を目的関数に用いるかどうか
|
|
384
|
+
if self.fem.use_parametric_as_objective:
|
|
385
|
+
from pyfemtet.opt.interface._femtet_parametric import add_parametric_results_as_objectives
|
|
386
|
+
add_parametric_results_as_objectives(self)
|
|
366
387
|
|
|
367
388
|
# actor の設定
|
|
368
389
|
self.status = OptimizationStatus(self.client)
|
|
@@ -389,7 +410,7 @@ class FEMOpt:
|
|
|
389
410
|
# kwargs
|
|
390
411
|
**self.monitor_server_kwargs,
|
|
391
412
|
# kwargs of submit
|
|
392
|
-
workers=self.monitor_process_worker_name,
|
|
413
|
+
workers=self.monitor_process_worker_name,
|
|
393
414
|
allow_other_workers=False
|
|
394
415
|
)
|
|
395
416
|
|
|
@@ -452,10 +473,13 @@ class FEMOpt:
|
|
|
452
473
|
t_save_history.start()
|
|
453
474
|
|
|
454
475
|
# 終了を待つ
|
|
455
|
-
|
|
476
|
+
local_opt_crashed = False
|
|
477
|
+
opt_crashed_list = self.client.gather(calc_futures)
|
|
456
478
|
if not self.opt.is_cluster: # 既存の fem を使っているならそれも待つ
|
|
457
479
|
if t_main is not None:
|
|
458
480
|
t_main.join()
|
|
481
|
+
local_opt_crashed = self.opt._is_error_exit
|
|
482
|
+
opt_crashed_list.append(local_opt_crashed)
|
|
459
483
|
self.status.set(OptimizationStatus.TERMINATED)
|
|
460
484
|
end = time()
|
|
461
485
|
|
|
@@ -465,6 +489,13 @@ class FEMOpt:
|
|
|
465
489
|
logger.info(f'計算が終了しました. 実行時間は {int(end - start)} 秒でした。ウィンドウを閉じると終了します.')
|
|
466
490
|
logger.info(f'結果は{self.history.path}を確認してください.')
|
|
467
491
|
|
|
492
|
+
# ひとつでも crashed ならばフラグを立てる
|
|
493
|
+
if any(opt_crashed_list):
|
|
494
|
+
self._is_error_exit = True
|
|
495
|
+
|
|
496
|
+
return self.history.local_data
|
|
497
|
+
|
|
498
|
+
|
|
468
499
|
def terminate_all(self):
|
|
469
500
|
"""Try to terminate all launched processes.
|
|
470
501
|
|
|
@@ -476,7 +507,10 @@ class FEMOpt:
|
|
|
476
507
|
sleep(1)
|
|
477
508
|
|
|
478
509
|
# terminate monitor process
|
|
479
|
-
self.
|
|
510
|
+
if self._is_error_exit:
|
|
511
|
+
self.status.set(OptimizationStatus.CRASHED)
|
|
512
|
+
else:
|
|
513
|
+
self.status.set(OptimizationStatus.TERMINATE_ALL)
|
|
480
514
|
logger.info(self.monitor_process_future.result())
|
|
481
515
|
sleep(1)
|
|
482
516
|
|
|
@@ -514,22 +548,20 @@ class FEMOpt:
|
|
|
514
548
|
else:
|
|
515
549
|
logger.warn('Monitor process worker not found.')
|
|
516
550
|
|
|
517
|
-
# close scheduler, other workers(, cluster)
|
|
518
|
-
self.client.close()
|
|
519
|
-
while self.client.scheduler is not None:
|
|
520
|
-
sleep(1)
|
|
521
|
-
logger.info('Terminate client.')
|
|
522
|
-
|
|
523
551
|
# close FEM (if specified to quit when deconstruct)
|
|
524
552
|
del self.fem
|
|
525
553
|
logger.info('Terminate FEM.')
|
|
554
|
+
sleep(1)
|
|
526
555
|
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
logger.info('Terminate all relative processes.')
|
|
556
|
+
# close scheduler, other workers(, cluster)
|
|
557
|
+
self.client.shutdown()
|
|
558
|
+
logger.info('Terminate all relative processes.')
|
|
531
559
|
sleep(3)
|
|
532
560
|
|
|
561
|
+
# if optimization was crashed, raise Exception
|
|
562
|
+
if self._is_error_exit:
|
|
563
|
+
raise RuntimeError('At least 1 of optimization processes have been crashed. See console log.')
|
|
564
|
+
|
|
533
565
|
|
|
534
566
|
def _start_monitor_server(
|
|
535
567
|
history,
|
|
@@ -7,6 +7,7 @@ import datetime
|
|
|
7
7
|
import inspect
|
|
8
8
|
import ast
|
|
9
9
|
import csv
|
|
10
|
+
import ctypes
|
|
10
11
|
|
|
11
12
|
# 3rd-party
|
|
12
13
|
import numpy as np
|
|
@@ -197,9 +198,12 @@ class Function:
|
|
|
197
198
|
|
|
198
199
|
# serializable でない COM 定数を parallelize するため
|
|
199
200
|
# COM 定数を一度 _Scapegoat 型のオブジェクトにする
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
# ParametricIF で使う dll 関数は _FuncPtr 型であって __globals__ を持たないが、
|
|
202
|
+
# これは絶対に constants を持たないので単に無視すればよい。
|
|
203
|
+
if not isinstance(fun, ctypes._CFuncPtr):
|
|
204
|
+
for varname in fun.__globals__:
|
|
205
|
+
if isinstance(fun.__globals__[varname], Constants):
|
|
206
|
+
fun.__globals__[varname] = _Scapegoat()
|
|
203
207
|
|
|
204
208
|
self.fun = fun
|
|
205
209
|
self.name = name
|
|
@@ -224,10 +228,11 @@ class Function:
|
|
|
224
228
|
def _restore_constants(self):
|
|
225
229
|
"""Helper function for parallelize Femtet."""
|
|
226
230
|
fun = self.fun
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if
|
|
230
|
-
fun.__globals__[varname]
|
|
231
|
+
if not isinstance(fun, ctypes._CFuncPtr):
|
|
232
|
+
for varname in fun.__globals__:
|
|
233
|
+
if isinstance(fun.__globals__[varname], _Scapegoat):
|
|
234
|
+
if not fun.__globals__[varname]._ignore_when_restore_constants:
|
|
235
|
+
fun.__globals__[varname] = constants
|
|
231
236
|
|
|
232
237
|
|
|
233
238
|
class Objective(Function):
|
|
@@ -691,6 +696,7 @@ class OptimizationStatus:
|
|
|
691
696
|
RUNNING = 30
|
|
692
697
|
INTERRUPTING = 40
|
|
693
698
|
TERMINATED = 50
|
|
699
|
+
CRASHED = 55
|
|
694
700
|
TERMINATE_ALL = 60
|
|
695
701
|
|
|
696
702
|
def __init__(self, client, name='entire'):
|
|
@@ -712,6 +718,7 @@ class OptimizationStatus:
|
|
|
712
718
|
if status_const == cls.INTERRUPTING: return 'Interrupting'
|
|
713
719
|
if status_const == cls.TERMINATED: return 'Terminated'
|
|
714
720
|
if status_const == cls.TERMINATE_ALL: return 'Terminate_all'
|
|
721
|
+
if status_const == cls.CRASHED: return 'Crashed'
|
|
715
722
|
|
|
716
723
|
def set(self, status_const):
|
|
717
724
|
"""Set optimization status."""
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""External CAD (NX) Integration
|
|
2
|
+
|
|
3
|
+
Using Femtet's stress analysis solver and Siemens' CAD software NX,
|
|
4
|
+
design a lightweight and high-strength H-shaped beam.
|
|
5
|
+
|
|
6
|
+
As a preliminary step, please perform the following procedures:
|
|
7
|
+
- Install NX
|
|
8
|
+
- Create a C:\temp folder
|
|
9
|
+
- Note: NX will save a .x_t file in this folder.
|
|
10
|
+
- Place the following files in the same folder:
|
|
11
|
+
- cad_ex01_NX.py (this file)
|
|
12
|
+
- cad_ex01_NX.prt
|
|
13
|
+
- cad_ex01_NX.femprj
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from win32com.client import constants
|
|
19
|
+
|
|
20
|
+
from pyfemtet.opt import FEMOpt
|
|
21
|
+
from pyfemtet.opt.interface import FemtetWithNXInterface
|
|
22
|
+
from pyfemtet.core import ModelError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
here, me = os.path.split(__file__)
|
|
26
|
+
os.chdir(here)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def von_mises(Femtet):
|
|
30
|
+
"""Obtain the maximum von Mises stress of the model.
|
|
31
|
+
|
|
32
|
+
Note:
|
|
33
|
+
The objective or constraint function should take Femtet
|
|
34
|
+
as its first argument and return a float as the output.
|
|
35
|
+
|
|
36
|
+
Warning:
|
|
37
|
+
CAD integration may assign boundary conditions to unintended locations.
|
|
38
|
+
|
|
39
|
+
In this example, if the boundary conditions are assigned as intended,
|
|
40
|
+
the maximum z displacement is always negative.
|
|
41
|
+
If the maximum displacement is not negative, it is assumed that
|
|
42
|
+
boundary condition assignment has failed.
|
|
43
|
+
Then this function raises a ModelError.
|
|
44
|
+
|
|
45
|
+
If a ModelError, MeshError, or SolveError occurs during optimization,
|
|
46
|
+
the optimization process considers the attempt a failure and skips to
|
|
47
|
+
the next trial.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# Simple check for the correctness of boundary conditions.
|
|
51
|
+
dx, dy, dz = Femtet.Gogh.Galileo.GetMaxDisplacement_py()
|
|
52
|
+
if dz >= 0:
|
|
53
|
+
raise ModelError('Assigning unintended boundary conditions.')
|
|
54
|
+
|
|
55
|
+
# Von Mises stress calculation.
|
|
56
|
+
Gogh = Femtet.Gogh
|
|
57
|
+
Gogh.Galileo.Potential = constants.GALILEO_VON_MISES_C
|
|
58
|
+
succeed, (x, y, z), mises = Gogh.Galileo.GetMAXPotentialPoint_py(constants.CMPX_REAL_C)
|
|
59
|
+
|
|
60
|
+
return mises
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def mass(Femtet):
|
|
64
|
+
"""Obtain model mass."""
|
|
65
|
+
return Femtet.Gogh.Galileo.GetMass('H_beam')
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def C_minus_B(Femtet, opt):
|
|
69
|
+
"""Calculate the difference between C and B dimensions.
|
|
70
|
+
|
|
71
|
+
Another example uses the following snippet to access design variables:
|
|
72
|
+
|
|
73
|
+
A = Femtet.GetVariableValue('A')
|
|
74
|
+
|
|
75
|
+
However, when performing CAD integration, this method does not work
|
|
76
|
+
because the variables are not set in the .femprj file.
|
|
77
|
+
|
|
78
|
+
In CAD integration, design variables are obtained in the following way.
|
|
79
|
+
|
|
80
|
+
# How to obtain a dictionary with the variable names of parameters
|
|
81
|
+
# added by add_parameter() as keys.
|
|
82
|
+
params: dict = opt.get_parameter()
|
|
83
|
+
A = params['A']
|
|
84
|
+
|
|
85
|
+
Or
|
|
86
|
+
|
|
87
|
+
# How to obtain an array of values of parameters added in the order
|
|
88
|
+
# by add_parameter().
|
|
89
|
+
values: np.ndarray = opt.get_parameter('values')
|
|
90
|
+
A, B, C = values
|
|
91
|
+
|
|
92
|
+
Objective functions and constraint functions can take arbitrary variables
|
|
93
|
+
after the first argument.
|
|
94
|
+
The FEMOpt member variable `opt` has a method called get_parameter().
|
|
95
|
+
This method allows you to retrieve design variables added by add_parameter().
|
|
96
|
+
By taking `opt` as the second argument, you can execute get_parameter()
|
|
97
|
+
within the objective or constraint function to retrieve design variables.
|
|
98
|
+
"""
|
|
99
|
+
A, B, C = opt.get_parameter('values')
|
|
100
|
+
return C - B
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == '__main__':
|
|
104
|
+
|
|
105
|
+
# Initialize NX-Femtet integration object.
|
|
106
|
+
# At this point, Python is connected to the Femtet.
|
|
107
|
+
fem = FemtetWithNXInterface(
|
|
108
|
+
prt_path='cad_ex01_NX.prt',
|
|
109
|
+
open_result_with_gui=False, # To calculate von Mises stress, set this argument to False. See Femtet Macro Help.
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Initialize the FEMOpt object.
|
|
113
|
+
# (establish connection between the optimization problem and Femtet)
|
|
114
|
+
femopt = FEMOpt(fem=fem)
|
|
115
|
+
|
|
116
|
+
# Add design variables to the optimization problem.
|
|
117
|
+
# (Specify the variables registered in the femprj file.)
|
|
118
|
+
femopt.add_parameter('A', 10, lower_bound=1, upper_bound=59)
|
|
119
|
+
femopt.add_parameter('B', 10, lower_bound=1, upper_bound=40)
|
|
120
|
+
femopt.add_parameter('C', 20, lower_bound=5, upper_bound=59)
|
|
121
|
+
|
|
122
|
+
# Add the constraint function to the optimization problem.
|
|
123
|
+
femopt.add_constraint(C_minus_B, 'C>B', lower_bound=1, args=femopt.opt)
|
|
124
|
+
|
|
125
|
+
# Add the objective function to the optimization problem.
|
|
126
|
+
femopt.add_objective(von_mises, name='von Mises (Pa)')
|
|
127
|
+
femopt.add_objective(mass, name='mass (kg)')
|
|
128
|
+
|
|
129
|
+
# Run optimization.
|
|
130
|
+
femopt.set_random_seed(42)
|
|
131
|
+
femopt.optimize(n_trials=20)
|
|
132
|
+
femopt.terminate_all()
|
|
Binary file
|
|
Binary file
|