PyOpenMagnetics 1.2.2__tar.gz → 1.3.0__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.
- pyopenmagnetics-1.3.0/AGENTS.md +2443 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/CMakeLists.txt +4 -1
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/PKG-INFO +33 -1
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/README.md +32 -0
- pyopenmagnetics-1.3.0/examples/complete_simulation_example.py +539 -0
- pyopenmagnetics-1.3.0/examples/converter_design_example.py +336 -0
- pyopenmagnetics-1.3.0/examples/debug_bobbin.py +47 -0
- pyopenmagnetics-1.3.0/examples/debug_coil.py +64 -0
- pyopenmagnetics-1.3.0/examples/debug_core.py +49 -0
- pyopenmagnetics-1.3.0/examples/debug_plotting.py +29 -0
- pyopenmagnetics-1.3.0/examples/flyback_220v_12v_1a.py +640 -0
- pyopenmagnetics-1.3.0/examples/flyback_220v_12v_2a_complete.py +588 -0
- pyopenmagnetics-1.3.0/examples/flyback_bh_curve.png +0 -0
- pyopenmagnetics-1.3.0/examples/flyback_core.png +0 -0
- pyopenmagnetics-1.3.0/examples/flyback_summary.png +0 -0
- pyopenmagnetics-1.3.0/examples/flyback_waveforms.png +0 -0
- pyopenmagnetics-1.3.0/examples/list_plot_funcs.py +14 -0
- pyopenmagnetics-1.3.0/examples/plot_flyback_design.py +486 -0
- pyopenmagnetics-1.3.0/examples/plot_flyback_pyom.py +238 -0
- pyopenmagnetics-1.3.0/examples/test_field_calc.py +76 -0
- pyopenmagnetics-1.3.0/examples/test_field_plot.py +57 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/pyproject.toml +6 -1
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/.github/workflows/publish.yml +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/.gitignore +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/LICENSE +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/PyOpenMagnetics.pyi +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/api/MAS.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/api/validation.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/docs/errors.md +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/docs/performance.md +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/examples/README.md +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/llms.txt +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/requirements.txt +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/advisers.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/common.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/converter.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/converter.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/core.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/core.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/database.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/database.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/logging.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/losses.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/losses.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/module.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/plotting.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/settings.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/settings.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/simulation.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/simulation.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/utils.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/winding.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/wire.h +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/test.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_core.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_winding.py +0 -0
|
@@ -0,0 +1,2443 @@
|
|
|
1
|
+
# PyOpenMagnetics - Complete AI Assistant Guide
|
|
2
|
+
|
|
3
|
+
> **📍 Location**: This file is bundled with PyOpenMagnetics and can be found at:
|
|
4
|
+
> - In the package: `PyOpenMagnetics/AGENTS.md`
|
|
5
|
+
> - GitHub: https://github.com/OpenMagnetics/PyOpenMagnetics/blob/main/AGENTS.md
|
|
6
|
+
|
|
7
|
+
## ⚠️ IMPORTANT: Avoid Manual Mode
|
|
8
|
+
|
|
9
|
+
**ALWAYS use the PyOpenMagnetics API methods rather than manual calculations.**
|
|
10
|
+
|
|
11
|
+
❌ **Don't do this:**
|
|
12
|
+
- Calculate turns ratios, inductance, or gaps manually using textbook formulas
|
|
13
|
+
- Hardcode core selections based on power ratings alone
|
|
14
|
+
- Assume standard formulas apply without validation
|
|
15
|
+
|
|
16
|
+
✅ **Do this instead:**
|
|
17
|
+
- Use `process_flyback()`, `process_buck()`, etc. to calculate specifications automatically
|
|
18
|
+
- Use `calculate_advised_magnetics()` to get ranked design recommendations
|
|
19
|
+
- Use `design_magnetics_from_converter()` for complete converter-to-magnetic design
|
|
20
|
+
|
|
21
|
+
**Why:** The MKF engine accounts for:
|
|
22
|
+
- Complex magnetic field distributions
|
|
23
|
+
- Temperature-dependent material properties
|
|
24
|
+
- Geometrical fringing effects
|
|
25
|
+
- Core database constraints
|
|
26
|
+
- Optimal gap calculations for target inductance
|
|
27
|
+
|
|
28
|
+
If API calls fail, **check the Troubleshooting section below** and fix the API usage rather than falling back to manual mode.
|
|
29
|
+
|
|
30
|
+
## Overview
|
|
31
|
+
|
|
32
|
+
PyOpenMagnetics is a Python wrapper for the OpenMagnetics MKF (Magnetics Knowledge Foundation) C++ engine. It provides comprehensive tools for designing, simulating, and optimizing magnetic components (transformers, inductors, chokes) for power electronics.
|
|
33
|
+
|
|
34
|
+
**Architecture:**
|
|
35
|
+
```
|
|
36
|
+
User Application
|
|
37
|
+
↓
|
|
38
|
+
PyOpenMagnetics (Python API)
|
|
39
|
+
↓
|
|
40
|
+
MKF C++ Engine (via pybind11)
|
|
41
|
+
↓
|
|
42
|
+
MAS JSON Schema (data structures)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install PyOpenMagnetics
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### ⚠️ CRITICAL: Module Import Instructions
|
|
54
|
+
|
|
55
|
+
**The standard `import PyOpenMagnetics` may not work** due to the compiled extension module (.so/.pyd file) structure. You have three options:
|
|
56
|
+
|
|
57
|
+
**Option 1: Direct importlib loading (Recommended)**
|
|
58
|
+
```python
|
|
59
|
+
import importlib.util
|
|
60
|
+
|
|
61
|
+
# Find the .so file path (adjust for your Python version and platform)
|
|
62
|
+
so_path = '/path/to/site-packages/PyOpenMagnetics/PyOpenMagnetics.cpython-311-x86_64-linux-gnu.so'
|
|
63
|
+
|
|
64
|
+
spec = importlib.util.spec_from_file_location('PyOpenMagnetics', so_path)
|
|
65
|
+
PyOpenMagnetics = importlib.util.module_from_spec(spec)
|
|
66
|
+
spec.loader.exec_module(PyOpenMagnetics)
|
|
67
|
+
|
|
68
|
+
# Now use normally
|
|
69
|
+
PyOpenMagnetics.load_databases({})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Option 2: Create __init__.py in package directory**
|
|
73
|
+
Create this file in `site-packages/PyOpenMagnetics/__init__.py`:
|
|
74
|
+
```python
|
|
75
|
+
import os
|
|
76
|
+
import importlib.util
|
|
77
|
+
|
|
78
|
+
so_file = os.path.join(os.path.dirname(__file__),
|
|
79
|
+
'PyOpenMagnetics.cpython-311-x86_64-linux-gnu.so')
|
|
80
|
+
spec = importlib.util.spec_from_file_location('PyOpenMagnetics', so_file)
|
|
81
|
+
module = importlib.util.module_from_spec(spec)
|
|
82
|
+
spec.loader.exec_module(module)
|
|
83
|
+
|
|
84
|
+
for attr_name in dir(module):
|
|
85
|
+
if not attr_name.startswith('_'):
|
|
86
|
+
globals()[attr_name] = getattr(module, attr_name)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Option 3: Load databases explicitly**
|
|
90
|
+
```python
|
|
91
|
+
import PyOpenMagnetics
|
|
92
|
+
PyOpenMagnetics.load_databases({}) # Required if databases not auto-loaded
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Verifying installation:**
|
|
96
|
+
```python
|
|
97
|
+
PyOpenMagnetics.load_databases({})
|
|
98
|
+
print(f"Materials: {len(PyOpenMagnetics.get_core_materials())}")
|
|
99
|
+
print(f"Shapes: {len(PyOpenMagnetics.get_core_shapes())}")
|
|
100
|
+
# Should show: Materials: 400+, Shapes: 1300+
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Accessing this guide after installation:**
|
|
104
|
+
```python
|
|
105
|
+
import PyOpenMagnetics
|
|
106
|
+
import inspect
|
|
107
|
+
import os
|
|
108
|
+
|
|
109
|
+
# Get the path to AGENTS.md
|
|
110
|
+
module_path = os.path.dirname(inspect.getfile(PyOpenMagnetics))
|
|
111
|
+
agents_md_path = os.path.join(module_path, "AGENTS.md")
|
|
112
|
+
print(f"AGENTS.md location: {agents_md_path}")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Complete API Reference by Category
|
|
118
|
+
|
|
119
|
+
### 1. DATABASE ACCESS FUNCTIONS
|
|
120
|
+
|
|
121
|
+
#### Core Materials
|
|
122
|
+
```python
|
|
123
|
+
# List all materials
|
|
124
|
+
materials = PyOpenMagnetics.get_core_materials() # Returns list of full material objects
|
|
125
|
+
names = PyOpenMagnetics.get_core_material_names() # Returns list of names only
|
|
126
|
+
|
|
127
|
+
# Filter by manufacturer
|
|
128
|
+
ferroxcube = PyOpenMagnetics.get_core_material_names_by_manufacturer("Ferroxcube")
|
|
129
|
+
tdk = PyOpenMagnetics.get_core_material_names_by_manufacturer("TDK")
|
|
130
|
+
magnetics = PyOpenMagnetics.get_core_material_names_by_manufacturer("Magnetics")
|
|
131
|
+
|
|
132
|
+
# Find specific material
|
|
133
|
+
material = PyOpenMagnetics.find_core_material_by_name("3C95")
|
|
134
|
+
|
|
135
|
+
# Get material properties
|
|
136
|
+
mu = PyOpenMagnetics.get_material_permeability("3C95", temperature=25, dc_bias=0, frequency=100000)
|
|
137
|
+
rho = PyOpenMagnetics.get_material_resistivity("3C95", temperature=25)
|
|
138
|
+
steinmetz = PyOpenMagnetics.get_core_material_steinmetz_coefficients(material, frequency=100000)
|
|
139
|
+
# Returns: {k, alpha, beta, minimumFrequency, maximumFrequency, ct0, ct1, ct2}
|
|
140
|
+
|
|
141
|
+
# Temperature-dependent parameters
|
|
142
|
+
params = PyOpenMagnetics.get_core_temperature_dependant_parameters(core, temperature=80)
|
|
143
|
+
# Returns: {magneticFluxDensitySaturation, initialPermeability, effectivePermeability,
|
|
144
|
+
# reluctance, permeance, resistivity}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Core Shapes
|
|
148
|
+
```python
|
|
149
|
+
# List all shapes
|
|
150
|
+
shapes = PyOpenMagnetics.get_core_shapes() # Full shape objects
|
|
151
|
+
names = PyOpenMagnetics.get_core_shape_names(include_toroidal=True) # Names only
|
|
152
|
+
families = PyOpenMagnetics.get_core_shape_families() # ['E', 'ETD', 'PQ', 'RM', 'T', ...]
|
|
153
|
+
|
|
154
|
+
# Find specific shape
|
|
155
|
+
shape = PyOpenMagnetics.find_core_shape_by_name("E 42/21/15")
|
|
156
|
+
shape = PyOpenMagnetics.find_core_shape_by_name("ETD 34")
|
|
157
|
+
shape = PyOpenMagnetics.find_core_shape_by_name("PQ 26/20")
|
|
158
|
+
|
|
159
|
+
# Get shape data
|
|
160
|
+
shape_data = PyOpenMagnetics.get_shape_data(shape)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Wires
|
|
164
|
+
```python
|
|
165
|
+
# List all wires
|
|
166
|
+
wires = PyOpenMagnetics.get_wires() # Full wire objects
|
|
167
|
+
names = PyOpenMagnetics.get_wire_names() # Names only
|
|
168
|
+
|
|
169
|
+
# Wire types and standards
|
|
170
|
+
types = PyOpenMagnetics.get_available_wire_types() # ['round', 'litz', 'rectangular', 'foil']
|
|
171
|
+
standards = PyOpenMagnetics.get_available_wire_standards() # ['IEC 60317', 'NEMA MW 1000', ...]
|
|
172
|
+
|
|
173
|
+
# Find wires
|
|
174
|
+
wire = PyOpenMagnetics.find_wire_by_name("Round 0.5 - Grade 1")
|
|
175
|
+
wire = PyOpenMagnetics.find_wire_by_dimension(0.0005, "round", "IEC 60317")
|
|
176
|
+
|
|
177
|
+
# Get wire data
|
|
178
|
+
wire_data = PyOpenMagnetics.get_wire_data(wire)
|
|
179
|
+
wire_data_by_name = PyOpenMagnetics.get_wire_data_by_name("Round 0.5 - Grade 1")
|
|
180
|
+
wire_data_by_standard = PyOpenMagnetics.get_wire_data_by_standard_name("some_standard_name")
|
|
181
|
+
|
|
182
|
+
# Wire dimensions
|
|
183
|
+
outer_d = PyOpenMagnetics.get_wire_outer_diameter_enamelled_round(wire)
|
|
184
|
+
outer_dims = PyOpenMagnetics.get_outer_dimensions(wire) # Works for any type
|
|
185
|
+
outer_d_litz = PyOpenMagnetics.get_wire_outer_diameter_bare_litz(wire)
|
|
186
|
+
outer_d_insulated_litz = PyOpenMagnetics.get_wire_outer_diameter_insulated_litz(wire)
|
|
187
|
+
outer_d_rect_h = PyOpenMagnetics.get_wire_outer_height_rectangular(wire)
|
|
188
|
+
outer_d_rect_w = PyOpenMagnetics.get_wire_outer_width_rectangular(wire)
|
|
189
|
+
|
|
190
|
+
# Get equivalent wire (different grade/size)
|
|
191
|
+
equiv_wire = PyOpenMagnetics.get_equivalent_wire(wire, target_grade="Grade 2")
|
|
192
|
+
|
|
193
|
+
# Get strand by standard (for litz)
|
|
194
|
+
strand = PyOpenMagnetics.get_strand_by_standard_name("IEC 60317 Grade 1 0.1mm")
|
|
195
|
+
conducting_d = PyOpenMagnetics.get_wire_conducting_diameter_by_standard_name("IEC 60317 Grade 1 0.1mm")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Wire Materials
|
|
199
|
+
```python
|
|
200
|
+
materials = PyOpenMagnetics.get_wire_materials()
|
|
201
|
+
names = PyOpenMagnetics.get_wire_material_names()
|
|
202
|
+
material = PyOpenMagnetics.find_wire_material_by_name("Copper")
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Bobbins
|
|
206
|
+
```python
|
|
207
|
+
bobbins = PyOpenMagnetics.get_bobbins()
|
|
208
|
+
names = PyOpenMagnetics.get_bobbin_names()
|
|
209
|
+
bobbin = PyOpenMagnetics.find_bobbin_by_name("E 42/21/15 Bobbin")
|
|
210
|
+
bobbin_data = PyOpenMagnetics.calculate_bobbin_data(bobbin)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### Insulation Materials
|
|
214
|
+
```python
|
|
215
|
+
materials = PyOpenMagnetics.get_insulation_materials()
|
|
216
|
+
names = PyOpenMagnetics.get_insulation_material_names()
|
|
217
|
+
material = PyOpenMagnetics.find_insulation_material_by_name("Kapton")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
### 2. CORE CALCULATIONS
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
# Calculate complete core data (adds processedDescription, geometricalDescription)
|
|
226
|
+
core = PyOpenMagnetics.calculate_core_data(core_functional_description, include_material_data=False)
|
|
227
|
+
|
|
228
|
+
# Get effective parameters
|
|
229
|
+
Ae = core["processedDescription"]["effectiveParameters"]["effectiveArea"] # m²
|
|
230
|
+
le = core["processedDescription"]["effectiveParameters"]["effectiveLength"] # m
|
|
231
|
+
Ve = core["processedDescription"]["effectiveParameters"]["effectiveVolume"] # m³
|
|
232
|
+
|
|
233
|
+
# Geometrical description
|
|
234
|
+
geom = PyOpenMagnetics.calculate_core_geometrical_description(core)
|
|
235
|
+
|
|
236
|
+
# Processed description
|
|
237
|
+
processed = PyOpenMagnetics.calculate_core_processed_description(core)
|
|
238
|
+
|
|
239
|
+
# Maximum magnetic energy
|
|
240
|
+
E_max = PyOpenMagnetics.calculate_core_maximum_magnetic_energy(core, operating_point)
|
|
241
|
+
|
|
242
|
+
# Saturation current (requires complete magnetic)
|
|
243
|
+
I_sat = PyOpenMagnetics.calculate_saturation_current(magnetic, temperature=25)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
### 3. INDUCTANCE CALCULATIONS
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
# Available models: "ZHANG", "MUEHLETHALER", "PARTRIDGE", "EFFECTIVE_AREA",
|
|
252
|
+
# "EFFECTIVE_LENGTH", "STENGLEIN", "BALAKRISHNAN", "CLASSIC"
|
|
253
|
+
models = {"reluctance": "ZHANG"}
|
|
254
|
+
|
|
255
|
+
# Calculate inductance from turns and gap
|
|
256
|
+
L = PyOpenMagnetics.calculate_inductance_from_number_turns_and_gapping(
|
|
257
|
+
core, coil, operating_point, models
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Calculate turns from inductance and gap
|
|
261
|
+
N = PyOpenMagnetics.calculate_number_turns_from_gapping_and_inductance(
|
|
262
|
+
core, inputs, models
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# Calculate gap from turns and inductance
|
|
266
|
+
core_with_gap = PyOpenMagnetics.calculate_gapping_from_number_turns_and_inductance(
|
|
267
|
+
core, coil, inputs,
|
|
268
|
+
gapping_type="SUBTRACTIVE", # or "ADDITIVE", "DISTRIBUTED"
|
|
269
|
+
decimals=4
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Calculate number of turns from voltage and frequency
|
|
273
|
+
N = PyOpenMagnetics.calculate_number_turns(core, operating_point, models)
|
|
274
|
+
|
|
275
|
+
# Gap reluctance
|
|
276
|
+
gap_result = PyOpenMagnetics.calculate_gap_reluctance(gap_data, "ZHANG")
|
|
277
|
+
# Returns: {reluctance, fringingFactor}
|
|
278
|
+
|
|
279
|
+
# Inductance matrix (for multi-winding)
|
|
280
|
+
L_matrix = PyOpenMagnetics.calculate_inductance_matrix(magnetic, operating_point)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
### 4. LOSS CALCULATIONS
|
|
286
|
+
|
|
287
|
+
#### Core Losses
|
|
288
|
+
```python
|
|
289
|
+
# Available models: "STEINMETZ", "IGSE", "MSE", "BARG", "ROSHEN", "ALBACH", "PROPRIETARY"
|
|
290
|
+
models = {
|
|
291
|
+
"coreLosses": "IGSE",
|
|
292
|
+
"reluctance": "ZHANG",
|
|
293
|
+
"coreTemperature": "MANIKTALA"
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
losses = PyOpenMagnetics.calculate_core_losses(core, coil, inputs, models)
|
|
297
|
+
# Returns:
|
|
298
|
+
# {
|
|
299
|
+
# coreLosses: float (Watts),
|
|
300
|
+
# magneticFluxDensityPeak: float (Tesla),
|
|
301
|
+
# magneticFluxDensityAcPeak: float (Tesla),
|
|
302
|
+
# voltageRms: float (Volts),
|
|
303
|
+
# currentRms: float (Amperes),
|
|
304
|
+
# apparentPower: float (VA),
|
|
305
|
+
# maximumCoreTemperature: float (Celsius),
|
|
306
|
+
# maximumCoreTemperatureRise: float (Kelvin)
|
|
307
|
+
# }
|
|
308
|
+
|
|
309
|
+
# Get model documentation
|
|
310
|
+
model_info = PyOpenMagnetics.get_core_losses_model_information(material)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### Winding Losses
|
|
314
|
+
```python
|
|
315
|
+
# Complete winding losses (DC + skin + proximity)
|
|
316
|
+
winding_losses = PyOpenMagnetics.calculate_winding_losses(magnetic, operating_point, temperature=25)
|
|
317
|
+
# Returns:
|
|
318
|
+
# {
|
|
319
|
+
# windingLosses: float (total Watts),
|
|
320
|
+
# windingLossesPerWinding: [float, ...],
|
|
321
|
+
# ohmicLosses: {...},
|
|
322
|
+
# skinEffectLosses: {...},
|
|
323
|
+
# proximityEffectLosses: {...}
|
|
324
|
+
# }
|
|
325
|
+
|
|
326
|
+
# Individual components
|
|
327
|
+
ohmic = PyOpenMagnetics.calculate_ohmic_losses(coil, operating_point, temperature)
|
|
328
|
+
skin = PyOpenMagnetics.calculate_skin_effect_losses(coil, winding_losses, temperature)
|
|
329
|
+
|
|
330
|
+
# Proximity effect (requires field calculation first)
|
|
331
|
+
field = PyOpenMagnetics.calculate_magnetic_field_strength_field(operating_point, magnetic)
|
|
332
|
+
prox = PyOpenMagnetics.calculate_proximity_effect_losses(coil, temperature, winding_losses, field)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### Wire-Level Losses
|
|
336
|
+
```python
|
|
337
|
+
# Per-meter calculations
|
|
338
|
+
R_dc = PyOpenMagnetics.calculate_dc_resistance_per_meter(wire, temperature)
|
|
339
|
+
P_dc = PyOpenMagnetics.calculate_dc_losses_per_meter(wire, current, temperature)
|
|
340
|
+
P_skin = PyOpenMagnetics.calculate_skin_ac_losses_per_meter(wire, current, temperature)
|
|
341
|
+
R_ac = PyOpenMagnetics.calculate_skin_ac_resistance_per_meter(wire, current, temperature)
|
|
342
|
+
|
|
343
|
+
# AC factor (Rac/Rdc)
|
|
344
|
+
Fr = PyOpenMagnetics.calculate_skin_ac_factor(wire, current, temperature)
|
|
345
|
+
|
|
346
|
+
# Effective current density
|
|
347
|
+
J_eff = PyOpenMagnetics.calculate_effective_current_density(wire, current, temperature)
|
|
348
|
+
|
|
349
|
+
# Skin depth
|
|
350
|
+
delta = PyOpenMagnetics.calculate_effective_skin_depth("copper", current, temperature)
|
|
351
|
+
|
|
352
|
+
# Per-winding DC resistance
|
|
353
|
+
R_dc_winding = PyOpenMagnetics.calculate_dc_resistance_per_winding(coil, temperature)
|
|
354
|
+
|
|
355
|
+
# Resistance matrix
|
|
356
|
+
R_matrix = PyOpenMagnetics.calculate_resistance_matrix(coil, operating_point, temperature)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
### 5. WINDING ENGINE
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
# Main winding function - places turns in winding window
|
|
365
|
+
coil_wound = PyOpenMagnetics.wind(
|
|
366
|
+
coil, # Coil with functional description
|
|
367
|
+
repetitions=2, # Pattern repetitions
|
|
368
|
+
proportion_per_winding=[0.5, 0.5], # Window share per winding
|
|
369
|
+
pattern=[0, 1], # Interleaving: P-S-P-S (winding indices)
|
|
370
|
+
margin_pairs=[[0.001, 0.001]] # Margin tape [left, right] per winding in meters
|
|
371
|
+
)
|
|
372
|
+
# Result contains:
|
|
373
|
+
# - functionalDescription (input)
|
|
374
|
+
# - sectionsDescription (coarse level)
|
|
375
|
+
# - layersDescription (layer level)
|
|
376
|
+
# - turnsDescription (individual turns with coordinates)
|
|
377
|
+
|
|
378
|
+
# Alternative winding approaches
|
|
379
|
+
coil_sections = PyOpenMagnetics.wind_by_sections(
|
|
380
|
+
coil, repetitions, proportions, pattern, insulation_thickness
|
|
381
|
+
)
|
|
382
|
+
coil_layers = PyOpenMagnetics.wind_by_layers(coil, insulation_layers, insulation_thickness)
|
|
383
|
+
coil_turns = PyOpenMagnetics.wind_by_turns(coil)
|
|
384
|
+
|
|
385
|
+
# Planar (PCB) winding
|
|
386
|
+
coil_planar = PyOpenMagnetics.wind_planar(
|
|
387
|
+
coil, stack_up, border_distance, wire_spacing, insulation, core_distance
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Check if winding fits
|
|
391
|
+
fits = PyOpenMagnetics.are_sections_and_layers_fitting(coil)
|
|
392
|
+
|
|
393
|
+
# Get layers by winding index
|
|
394
|
+
primary_layers = PyOpenMagnetics.get_layers_by_winding_index(coil, 0)
|
|
395
|
+
secondary_layers = PyOpenMagnetics.get_layers_by_winding_index(coil, 1)
|
|
396
|
+
|
|
397
|
+
# Get layers by section
|
|
398
|
+
layers = PyOpenMagnetics.get_layers_by_section(coil, section_index=0)
|
|
399
|
+
|
|
400
|
+
# Get sections description
|
|
401
|
+
sections = PyOpenMagnetics.get_sections_description_conduction(coil)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
### 6. DESIGN ADVISER
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
# Process inputs (REQUIRED before adviser - adds harmonics for loss calculation)
|
|
410
|
+
inputs = {
|
|
411
|
+
"designRequirements": {
|
|
412
|
+
"magnetizingInductance": {"nominal": 100e-6, "minimum": 90e-6, "maximum": 110e-6},
|
|
413
|
+
"turnsRatios": [{"nominal": 5.0}],
|
|
414
|
+
"leakageInductance": [{"maximum": 5e-6}],
|
|
415
|
+
"insulation": {
|
|
416
|
+
"insulationType": "Functional", # or "Basic", "Supplementary", "Reinforced"
|
|
417
|
+
"pollutionDegree": "P2",
|
|
418
|
+
"overvoltageCategory": "OVC-II"
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
"operatingPoints": [operating_point]
|
|
422
|
+
}
|
|
423
|
+
processed = PyOpenMagnetics.process_inputs(inputs)
|
|
424
|
+
|
|
425
|
+
# Get recommended cores only
|
|
426
|
+
weights = {"COST": 1.0, "EFFICIENCY": 1.0, "DIMENSIONS": 0.5}
|
|
427
|
+
cores = PyOpenMagnetics.calculate_advised_cores(
|
|
428
|
+
processed,
|
|
429
|
+
weights,
|
|
430
|
+
max_results=10,
|
|
431
|
+
core_mode="STANDARD_CORES" # or "AVAILABLE_CORES"
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Get complete magnetic designs
|
|
435
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(
|
|
436
|
+
processed,
|
|
437
|
+
max_results=5,
|
|
438
|
+
core_mode="STANDARD_CORES" # or "AVAILABLE_CORES"
|
|
439
|
+
)
|
|
440
|
+
# Result structure: {"data": [{"mas": {...}, "scoring": float, "scoringPerFilter": {...}}, ...]}
|
|
441
|
+
|
|
442
|
+
# From custom catalog
|
|
443
|
+
catalog_result = PyOpenMagnetics.calculate_advised_magnetics_from_catalog(
|
|
444
|
+
processed, catalog_magnetics, max_results=5
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# From cache (faster for repeated queries)
|
|
448
|
+
cache_result = PyOpenMagnetics.calculate_advised_magnetics_from_cache(
|
|
449
|
+
processed, max_results=5
|
|
450
|
+
)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
### 7. CONVERTER TOPOLOGY PROCESSORS
|
|
456
|
+
|
|
457
|
+
PyOpenMagnetics includes built-in support for common power converter topologies:
|
|
458
|
+
|
|
459
|
+
> **⚠️ IMPORTANT API NOTES:**
|
|
460
|
+
>
|
|
461
|
+
> **1. Function Discrepancy:** `process_flyback()` may return schema errors. Use `process_converter("flyback", ...)` instead:
|
|
462
|
+
> ```python
|
|
463
|
+
> # If this fails with schema error:
|
|
464
|
+
> # inputs = PyOpenMagnetics.process_flyback(flyback_specs)
|
|
465
|
+
>
|
|
466
|
+
> # Use this instead:
|
|
467
|
+
> inputs = PyOpenMagnetics.process_converter("flyback", flyback_specs, False)
|
|
468
|
+
> ```
|
|
469
|
+
>
|
|
470
|
+
> **2. Case-Sensitive `core_mode`:** The `core_mode` parameter in `calculate_advised_magnetics()` MUST be lowercase:
|
|
471
|
+
> ```python
|
|
472
|
+
> # WRONG - returns empty results or error:
|
|
473
|
+
> result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "STANDARD_CORES")
|
|
474
|
+
>
|
|
475
|
+
> # CORRECT:
|
|
476
|
+
> result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "standard cores")
|
|
477
|
+
> # Valid values: "standard cores" or "available cores"
|
|
478
|
+
> ```
|
|
479
|
+
>
|
|
480
|
+
> **3. Double Brackets for `desiredDutyCycle`:** Must be nested list `[[value]]` not `[value]`:
|
|
481
|
+
> ```python
|
|
482
|
+
> flyback_specs = {
|
|
483
|
+
> "desiredDutyCycle": [[0.45]], # ✓ Correct
|
|
484
|
+
> # "desiredDutyCycle": [0.45], # ✗ Wrong - causes schema error
|
|
485
|
+
> }
|
|
486
|
+
> ```
|
|
487
|
+
|
|
488
|
+
#### Flyback Converter
|
|
489
|
+
```python
|
|
490
|
+
flyback = {
|
|
491
|
+
"inputVoltage": {"minimum": 85, "nominal": 120, "maximum": 265},
|
|
492
|
+
"diodeVoltageDrop": 0.7,
|
|
493
|
+
"efficiency": 0.85,
|
|
494
|
+
"maximumDrainSourceVoltage": 600,
|
|
495
|
+
"maximumDutyCycle": 0.5,
|
|
496
|
+
"operatingPoints": [{
|
|
497
|
+
"outputVoltages": [12, 5],
|
|
498
|
+
"outputCurrents": [2.0, 0.5],
|
|
499
|
+
"switchingFrequency": 100000,
|
|
500
|
+
"ambientTemperature": 40,
|
|
501
|
+
"mode": "CCM" # or "DCM", "BCM"
|
|
502
|
+
}],
|
|
503
|
+
"desiredInductance": 150e-6,
|
|
504
|
+
"desiredTurnsRatios": [8.0, 19.2],
|
|
505
|
+
"desiredDutyCycle": [[0.45, 0.45]]
|
|
506
|
+
}
|
|
507
|
+
inputs = PyOpenMagnetics.process_flyback(flyback)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
> **💡 Tip:** If `process_flyback()` returns schema errors, use the generic `process_converter()`:
|
|
511
|
+
> ```python
|
|
512
|
+
> inputs = PyOpenMagnetics.process_converter("flyback", flyback_specs, use_ngspice=False)
|
|
513
|
+
> ```
|
|
514
|
+
|
|
515
|
+
#### Buck Converter
|
|
516
|
+
```python
|
|
517
|
+
buck = {
|
|
518
|
+
"inputVoltage": {"minimum": 8, "nominal": 12, "maximum": 14},
|
|
519
|
+
"diodeVoltageDrop": 0.0, # Synchronous
|
|
520
|
+
"efficiency": 0.95,
|
|
521
|
+
"currentRippleRatio": 0.3,
|
|
522
|
+
"operatingPoints": [{
|
|
523
|
+
"outputVoltage": 3.3,
|
|
524
|
+
"outputCurrent": 5.0,
|
|
525
|
+
"switchingFrequency": 500000,
|
|
526
|
+
"ambientTemperature": 25
|
|
527
|
+
}],
|
|
528
|
+
"desiredInductance": 4.7e-6
|
|
529
|
+
}
|
|
530
|
+
inputs = PyOpenMagnetics.process_buck(buck)
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
> **💡 Tip:** If `process_buck()` returns schema errors, use the generic `process_converter()`:
|
|
534
|
+
> ```python
|
|
535
|
+
> inputs = PyOpenMagnetics.process_converter("buck", buck_specs, use_ngspice=False)
|
|
536
|
+
> ```
|
|
537
|
+
|
|
538
|
+
#### Boost Converter
|
|
539
|
+
```python
|
|
540
|
+
boost = {
|
|
541
|
+
"inputVoltage": {"minimum": 85, "nominal": 120, "maximum": 265},
|
|
542
|
+
"diodeVoltageDrop": 1.0,
|
|
543
|
+
"efficiency": 0.98,
|
|
544
|
+
"currentRippleRatio": 0.2,
|
|
545
|
+
"operatingPoints": [{
|
|
546
|
+
"outputVoltage": 400,
|
|
547
|
+
"outputCurrent": 2.5,
|
|
548
|
+
"switchingFrequency": 65000,
|
|
549
|
+
"ambientTemperature": 40
|
|
550
|
+
}],
|
|
551
|
+
"desiredInductance": 250e-6
|
|
552
|
+
}
|
|
553
|
+
inputs = PyOpenMagnetics.process_boost(boost)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
> **💡 Tip:** If `process_boost()` returns schema errors, use the generic `process_converter()`:
|
|
557
|
+
> ```python
|
|
558
|
+
> inputs = PyOpenMagnetics.process_converter("boost", boost_specs, use_ngspice=False)
|
|
559
|
+
> ```
|
|
560
|
+
|
|
561
|
+
#### Single-Switch Forward
|
|
562
|
+
```python
|
|
563
|
+
single_switch_forward = {
|
|
564
|
+
"inputVoltage": {"minimum": 36, "nominal": 48, "maximum": 72},
|
|
565
|
+
"diodeVoltageDrop": 0.5,
|
|
566
|
+
"efficiency": 0.90,
|
|
567
|
+
"currentRippleRatio": 0.3,
|
|
568
|
+
"operatingPoints": [{
|
|
569
|
+
"outputVoltages": [5],
|
|
570
|
+
"outputCurrents": [10],
|
|
571
|
+
"switchingFrequency": 250000,
|
|
572
|
+
"ambientTemperature": 50
|
|
573
|
+
}],
|
|
574
|
+
"desiredInductance": 50e-6,
|
|
575
|
+
"desiredTurnsRatios": [9.6, 9.6], # [demagnetization, output]
|
|
576
|
+
"desiredOutputInductances": [10e-6]
|
|
577
|
+
}
|
|
578
|
+
inputs = PyOpenMagnetics.process_single_switch_forward(single_switch_forward)
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
> **💡 Tip:** If `process_single_switch_forward()` returns schema errors, use the generic `process_converter()`:
|
|
582
|
+
> ```python
|
|
583
|
+
> inputs = PyOpenMagnetics.process_converter("single_switch_forward", forward_specs, use_ngspice=False)
|
|
584
|
+
> ```
|
|
585
|
+
|
|
586
|
+
#### Two-Switch Forward
|
|
587
|
+
```python
|
|
588
|
+
two_switch_forward = {
|
|
589
|
+
"inputVoltage": {"minimum": 300, "nominal": 400, "maximum": 420},
|
|
590
|
+
"diodeVoltageDrop": 0.7,
|
|
591
|
+
"efficiency": 0.92,
|
|
592
|
+
"dutyCycle": 0.45,
|
|
593
|
+
"currentRippleRatio": 0.25,
|
|
594
|
+
"operatingPoints": [{
|
|
595
|
+
"outputVoltages": [24],
|
|
596
|
+
"outputCurrents": [20],
|
|
597
|
+
"switchingFrequency": 100000,
|
|
598
|
+
"ambientTemperature": 55
|
|
599
|
+
}],
|
|
600
|
+
"desiredInductance": 200e-6,
|
|
601
|
+
"desiredTurnsRatios": [16.7],
|
|
602
|
+
"desiredOutputInductances": [25e-6]
|
|
603
|
+
}
|
|
604
|
+
inputs = PyOpenMagnetics.process_two_switch_forward(two_switch_forward)
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
> **💡 Tip:** If `process_two_switch_forward()` returns schema errors, use the generic `process_converter()`:
|
|
608
|
+
> ```python
|
|
609
|
+
> inputs = PyOpenMagnetics.process_converter("two_switch_forward", forward_specs, use_ngspice=False)
|
|
610
|
+
> ```
|
|
611
|
+
|
|
612
|
+
#### Active Clamp Forward
|
|
613
|
+
```python
|
|
614
|
+
active_clamp_forward = {
|
|
615
|
+
"inputVoltage": {"minimum": 36, "nominal": 48, "maximum": 60},
|
|
616
|
+
"diodeVoltageDrop": 0.5,
|
|
617
|
+
"efficiency": 0.93,
|
|
618
|
+
"currentRippleRatio": 0.2,
|
|
619
|
+
"operatingPoints": [{
|
|
620
|
+
"outputVoltages": [12],
|
|
621
|
+
"outputCurrents": [8],
|
|
622
|
+
"switchingFrequency": 350000,
|
|
623
|
+
"ambientTemperature": 45
|
|
624
|
+
}],
|
|
625
|
+
"desiredInductance": 30e-6,
|
|
626
|
+
"desiredTurnsRatios": [4.0],
|
|
627
|
+
"desiredOutputInductances": [5e-6]
|
|
628
|
+
}
|
|
629
|
+
inputs = PyOpenMagnetics.process_active_clamp_forward(active_clamp_forward)
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
> **💡 Tip:** If `process_active_clamp_forward()` returns schema errors, use the generic `process_converter()`:
|
|
633
|
+
> ```python
|
|
634
|
+
> inputs = PyOpenMagnetics.process_converter("active_clamp_forward", forward_specs, use_ngspice=False)
|
|
635
|
+
> ```
|
|
636
|
+
|
|
637
|
+
#### Push-Pull
|
|
638
|
+
```python
|
|
639
|
+
push_pull = {
|
|
640
|
+
"inputVoltage": {"minimum": 22, "nominal": 24, "maximum": 28},
|
|
641
|
+
"diodeVoltageDrop": 0.7,
|
|
642
|
+
"efficiency": 0.90,
|
|
643
|
+
"dutyCycle": 0.45,
|
|
644
|
+
"currentRippleRatio": 0.3,
|
|
645
|
+
"maximumDrainSourceVoltage": 100,
|
|
646
|
+
"operatingPoints": [{
|
|
647
|
+
"outputVoltage": 48,
|
|
648
|
+
"outputCurrent": 5,
|
|
649
|
+
"switchingFrequency": 100000,
|
|
650
|
+
"ambientTemperature": 40
|
|
651
|
+
}],
|
|
652
|
+
"desiredInductance": 100e-6,
|
|
653
|
+
"desiredTurnsRatios": [1.0],
|
|
654
|
+
"desiredOutputInductance": 50e-6
|
|
655
|
+
}
|
|
656
|
+
inputs = PyOpenMagnetics.process_push_pull(push_pull)
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
> **💡 Tip:** If `process_push_pull()` returns schema errors, use the generic `process_converter()`:
|
|
660
|
+
> ```python
|
|
661
|
+
> inputs = PyOpenMagnetics.process_converter("push_pull", push_pull_specs, use_ngspice=False)
|
|
662
|
+
> ```
|
|
663
|
+
|
|
664
|
+
#### Isolated Buck
|
|
665
|
+
```python
|
|
666
|
+
isolated_buck = {
|
|
667
|
+
"inputVoltage": {"minimum": 280, "nominal": 310, "maximum": 375},
|
|
668
|
+
"diodeVoltageDrop": 0.7,
|
|
669
|
+
"efficiency": 0.91,
|
|
670
|
+
"currentRippleRatio": 0.25,
|
|
671
|
+
"operatingPoints": [{
|
|
672
|
+
"outputVoltages": [15],
|
|
673
|
+
"outputCurrents": [3],
|
|
674
|
+
"switchingFrequency": 200000,
|
|
675
|
+
"ambientTemperature": 35
|
|
676
|
+
}],
|
|
677
|
+
"desiredInductance": 80e-6,
|
|
678
|
+
"desiredTurnsRatios": [20.0]
|
|
679
|
+
}
|
|
680
|
+
inputs = PyOpenMagnetics.process_isolated_buck(isolated_buck)
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
> **💡 Tip:** If `process_isolated_buck()` returns schema errors, use the generic `process_converter()`:
|
|
684
|
+
> ```python
|
|
685
|
+
> inputs = PyOpenMagnetics.process_converter("isolated_buck", isolated_buck_specs, use_ngspice=False)
|
|
686
|
+
> ```
|
|
687
|
+
|
|
688
|
+
#### Isolated Buck-Boost
|
|
689
|
+
```python
|
|
690
|
+
isolated_buck_boost = {
|
|
691
|
+
"inputVoltage": {"minimum": 18, "nominal": 24, "maximum": 36},
|
|
692
|
+
"diodeVoltageDrop": 0.5,
|
|
693
|
+
"efficiency": 0.88,
|
|
694
|
+
"currentRippleRatio": 0.3,
|
|
695
|
+
"operatingPoints": [{
|
|
696
|
+
"outputVoltages": [24],
|
|
697
|
+
"outputCurrents": [2],
|
|
698
|
+
"switchingFrequency": 150000,
|
|
699
|
+
"ambientTemperature": 30
|
|
700
|
+
}],
|
|
701
|
+
"desiredInductance": 60e-6,
|
|
702
|
+
"desiredTurnsRatios": [1.0]
|
|
703
|
+
}
|
|
704
|
+
inputs = PyOpenMagnetics.process_isolated_buck_boost(isolated_buck_boost)
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
> **💡 Tip:** If `process_isolated_buck_boost()` returns schema errors, use the generic `process_converter()`:
|
|
708
|
+
> ```python
|
|
709
|
+
> inputs = PyOpenMagnetics.process_converter("isolated_buck_boost", isolated_bb_specs, use_ngspice=False)
|
|
710
|
+
> ```
|
|
711
|
+
|
|
712
|
+
#### LLC Resonant Converter
|
|
713
|
+
```python
|
|
714
|
+
llc = {
|
|
715
|
+
"inputVoltage": {"minimum": 400, "maximum": 400},
|
|
716
|
+
"desiredInductance": 100e-6,
|
|
717
|
+
"desiredTurnsRatios": [1.0],
|
|
718
|
+
"minSwitchingFrequency": 80000,
|
|
719
|
+
"maxSwitchingFrequency": 120000,
|
|
720
|
+
"operatingPoints": [{
|
|
721
|
+
"outputVoltages": [48.0],
|
|
722
|
+
"outputCurrents": [5.0],
|
|
723
|
+
"switchingFrequency": 100000,
|
|
724
|
+
"ambientTemperature": 25
|
|
725
|
+
}]
|
|
726
|
+
}
|
|
727
|
+
inputs = PyOpenMagnetics.process_converter("llc", llc, use_ngspice=False)
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
#### Current Transformer
|
|
731
|
+
```python
|
|
732
|
+
current_transformer = {
|
|
733
|
+
"primaryCurrent": {
|
|
734
|
+
"waveform": {"data": [0, 10, 10, 0], "time": [0, 1e-6, 5e-6, 6e-6]},
|
|
735
|
+
"frequency": 100000
|
|
736
|
+
},
|
|
737
|
+
"operatingPoints": [{
|
|
738
|
+
"burdenResistance": 10,
|
|
739
|
+
"ambientTemperature": 25
|
|
740
|
+
}]
|
|
741
|
+
}
|
|
742
|
+
turns_ratio = 100
|
|
743
|
+
secondary_resistance = 5.0
|
|
744
|
+
inputs = PyOpenMagnetics.process_current_transformer(
|
|
745
|
+
current_transformer, turns_ratio, secondary_resistance
|
|
746
|
+
)
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
> **💡 Tip:** If `process_current_transformer()` returns schema errors, use the generic `process_converter()`:
|
|
750
|
+
> ```python
|
|
751
|
+
> inputs = PyOpenMagnetics.process_converter("current_transformer", ct_specs, use_ngspice=False)
|
|
752
|
+
> ```
|
|
753
|
+
|
|
754
|
+
#### Generic Converter Processor
|
|
755
|
+
```python
|
|
756
|
+
# Use process_converter for any topology with custom specs
|
|
757
|
+
result = PyOpenMagnetics.process_converter(
|
|
758
|
+
topology="flyback", # or "buck", "boost", "llc", etc.
|
|
759
|
+
converter_specs={...},
|
|
760
|
+
use_ngspice=True # Use SPICE simulation for waveforms (slower but accurate)
|
|
761
|
+
)
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
### 8. SIMULATION
|
|
767
|
+
|
|
768
|
+
```python
|
|
769
|
+
# Full simulation
|
|
770
|
+
models = {"coreLosses": "IGSE", "reluctance": "ZHANG"}
|
|
771
|
+
mas = PyOpenMagnetics.simulate(inputs, magnetic, models)
|
|
772
|
+
# Returns Mas with outputs populated
|
|
773
|
+
|
|
774
|
+
# Autocomplete partial structures
|
|
775
|
+
magnetic = PyOpenMagnetics.magnetic_autocomplete(partial_magnetic, config)
|
|
776
|
+
mas = PyOpenMagnetics.mas_autocomplete(partial_mas, config)
|
|
777
|
+
|
|
778
|
+
# Extract operating point from SPICE simulation
|
|
779
|
+
op = PyOpenMagnetics.extract_operating_point(
|
|
780
|
+
spice_file,
|
|
781
|
+
num_windings=2,
|
|
782
|
+
frequency=100000,
|
|
783
|
+
target_inductance=100e-6,
|
|
784
|
+
column_mapping={...}
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
# Export to SPICE
|
|
788
|
+
subcircuit = PyOpenMagnetics.export_magnetic_as_subcircuit(magnetic)
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
### 9. INSULATION COORDINATION
|
|
794
|
+
|
|
795
|
+
```python
|
|
796
|
+
# Calculate safety distances per IEC standards
|
|
797
|
+
insulation = PyOpenMagnetics.calculate_insulation(inputs)
|
|
798
|
+
# Returns:
|
|
799
|
+
# {
|
|
800
|
+
# creepageDistance: float (meters),
|
|
801
|
+
# clearance: float (meters),
|
|
802
|
+
# withstandVoltage: float (Volts),
|
|
803
|
+
# distanceThroughInsulation: float (meters),
|
|
804
|
+
# errorMessage: "" if successful
|
|
805
|
+
# }
|
|
806
|
+
|
|
807
|
+
# Get isolation side from winding index
|
|
808
|
+
isolation_side = PyOpenMagnetics.get_isolation_side_from_index(coil, winding_index=0)
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
### 10. VISUALIZATION
|
|
814
|
+
|
|
815
|
+
```python
|
|
816
|
+
# Core views
|
|
817
|
+
svg_core = PyOpenMagnetics.plot_core(core, use_colors=True)
|
|
818
|
+
svg_core_2d = PyOpenMagnetics.plot_core(core, axis=1, winding_windows=None, use_colors=True)
|
|
819
|
+
|
|
820
|
+
# Coil views
|
|
821
|
+
svg_coil = PyOpenMagnetics.plot_coil(coil, use_colors=True)
|
|
822
|
+
|
|
823
|
+
# Magnetic field visualization
|
|
824
|
+
svg_field = PyOpenMagnetics.plot_magnetic_field(magnetic, operating_point, use_colors=True)
|
|
825
|
+
svg_field_2d = PyOpenMagnetics.plot_magnetic_field(magnetic, operating_point, axis=1, use_colors=True)
|
|
826
|
+
|
|
827
|
+
# Electric field
|
|
828
|
+
svg_electric = PyOpenMagnetics.plot_electric_field(magnetic, operating_point)
|
|
829
|
+
|
|
830
|
+
# Individual components
|
|
831
|
+
svg_wire = PyOpenMagnetics.plot_wire(wire, use_colors=True)
|
|
832
|
+
svg_bobbin = PyOpenMagnetics.plot_bobbin(bobbin, use_colors=True)
|
|
833
|
+
|
|
834
|
+
# Winding descriptions
|
|
835
|
+
svg_sections = PyOpenMagnetics.plot_sections(magnetic, use_colors=True)
|
|
836
|
+
svg_layers = PyOpenMagnetics.plot_layers(magnetic, use_colors=True)
|
|
837
|
+
svg_turns = PyOpenMagnetics.plot_turns(magnetic, use_colors=True)
|
|
838
|
+
|
|
839
|
+
# Field maps
|
|
840
|
+
svg_field_map = PyOpenMagnetics.plot_field_map(magnetic, operating_point)
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
---
|
|
844
|
+
|
|
845
|
+
### 11. SIGNAL PROCESSING
|
|
846
|
+
|
|
847
|
+
```python
|
|
848
|
+
# Calculate harmonics from waveform
|
|
849
|
+
harmonics = PyOpenMagnetics.calculate_harmonics(waveform_data)
|
|
850
|
+
|
|
851
|
+
# Sample waveform
|
|
852
|
+
sampled = PyOpenMagnetics.calculate_sampled_waveform(waveform, num_samples=100)
|
|
853
|
+
|
|
854
|
+
# RMS power
|
|
855
|
+
P_rms = PyOpenMagnetics.calculate_rms_power(voltage_waveform, current_waveform)
|
|
856
|
+
|
|
857
|
+
# Instantaneous power
|
|
858
|
+
P_inst = PyOpenMagnetics.calculate_instantaneous_power(voltage_waveform, current_waveform)
|
|
859
|
+
|
|
860
|
+
# Processed data (basic)
|
|
861
|
+
processed = PyOpenMagnetics.calculate_basic_processed_data(waveform)
|
|
862
|
+
|
|
863
|
+
# Full processed data
|
|
864
|
+
processed = PyOpenMagnetics.calculate_processed_data(waveform)
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
---
|
|
868
|
+
|
|
869
|
+
### 12. SETTINGS AND CONFIGURATION
|
|
870
|
+
|
|
871
|
+
```python
|
|
872
|
+
# Get current settings
|
|
873
|
+
settings = PyOpenMagnetics.get_settings()
|
|
874
|
+
|
|
875
|
+
# Modify settings
|
|
876
|
+
settings["coilAllowMarginTape"] = True
|
|
877
|
+
settings["coilWindEvenIfNotFit"] = False
|
|
878
|
+
settings["useOnlyCoresInStock"] = True
|
|
879
|
+
settings["painterNumberPointsX"] = 100
|
|
880
|
+
settings["painterNumberPointsY"] = 100
|
|
881
|
+
PyOpenMagnetics.set_settings(settings)
|
|
882
|
+
|
|
883
|
+
# Reset to defaults
|
|
884
|
+
PyOpenMagnetics.reset_settings()
|
|
885
|
+
|
|
886
|
+
# Get physical constants
|
|
887
|
+
constants = PyOpenMagnetics.get_constants()
|
|
888
|
+
mu_0 = constants["vacuumPermeability"]
|
|
889
|
+
|
|
890
|
+
# Get default models
|
|
891
|
+
defaults = PyOpenMagnetics.get_default_models()
|
|
892
|
+
|
|
893
|
+
# Logging
|
|
894
|
+
PyOpenMagnetics.set_log_level("DEBUG") # or "INFO", "WARNING", "ERROR"
|
|
895
|
+
logs = PyOpenMagnetics.get_logs()
|
|
896
|
+
PyOpenMagnetics.clear_logs()
|
|
897
|
+
|
|
898
|
+
# Enable/disable string sink for logging
|
|
899
|
+
PyOpenMagnetics.enable_string_sink()
|
|
900
|
+
PyOpenMagnetics.disable_string_sink()
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
905
|
+
### 13. DATABASE MANAGEMENT
|
|
906
|
+
|
|
907
|
+
```python
|
|
908
|
+
# Load databases (automatic on import, but can be called explicitly)
|
|
909
|
+
PyOpenMagnetics.load_databases()
|
|
910
|
+
PyOpenMagnetics.read_databases()
|
|
911
|
+
|
|
912
|
+
# Load specific data
|
|
913
|
+
PyOpenMagnetics.load_core_materials()
|
|
914
|
+
PyOpenMagnetics.load_core_shapes()
|
|
915
|
+
PyOpenMagnetics.load_wires()
|
|
916
|
+
|
|
917
|
+
# Check if databases are empty
|
|
918
|
+
is_empty_materials = PyOpenMagnetics.is_core_material_database_empty()
|
|
919
|
+
is_empty_shapes = PyOpenMagnetics.is_core_shape_database_empty()
|
|
920
|
+
is_empty_wires = PyOpenMagnetics.is_wire_database_empty()
|
|
921
|
+
|
|
922
|
+
# Clear databases/cache
|
|
923
|
+
PyOpenMagnetics.clear_databases()
|
|
924
|
+
PyOpenMagnetics.clear_magnetic_cache()
|
|
925
|
+
|
|
926
|
+
# Clear logs
|
|
927
|
+
PyOpenMagnetics.clear_logs()
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
### 14. MAS (MAGNETIC AGNOSTIC STRUCTURE) OPERATIONS
|
|
933
|
+
|
|
934
|
+
```python
|
|
935
|
+
# Load MAS from file
|
|
936
|
+
mas = PyOpenMagnetics.load_mas("design.json")
|
|
937
|
+
|
|
938
|
+
# Load multiple MAS files
|
|
939
|
+
magnetics = PyOpenMagnetics.load_magnetics("folder_path/")
|
|
940
|
+
magnetics = PyOpenMagnetics.load_magnetics_from_file("file.json")
|
|
941
|
+
|
|
942
|
+
# Read MAS data
|
|
943
|
+
mas_data = PyOpenMagnetics.read_mas(mas_dict)
|
|
944
|
+
|
|
945
|
+
# Extract column names from data
|
|
946
|
+
names = PyOpenMagnetics.extract_column_names(data)
|
|
947
|
+
map_names = PyOpenMagnetics.extract_map_column_names(data)
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
---
|
|
951
|
+
|
|
952
|
+
### 15. UTILITY FUNCTIONS
|
|
953
|
+
|
|
954
|
+
```python
|
|
955
|
+
# Check if design fits
|
|
956
|
+
fits = PyOpenMagnetics.check_if_fits(magnetic, inputs)
|
|
957
|
+
|
|
958
|
+
# Delimit and compact data
|
|
959
|
+
delimited = PyOpenMagnetics.delimit_and_compact(data)
|
|
960
|
+
|
|
961
|
+
# Resolve dimension with tolerance
|
|
962
|
+
dimension = PyOpenMagnetics.resolve_dimension_with_tolerance(
|
|
963
|
+
nominal_value, tolerance_percent
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
# Add margin to section
|
|
967
|
+
PyOpenMagnetics.add_margin_to_section_by_index(coil, section_index, margin_left, margin_right)
|
|
968
|
+
|
|
969
|
+
# Log message
|
|
970
|
+
PyOpenMagnetics.log_message("Custom message", "INFO")
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
---
|
|
974
|
+
|
|
975
|
+
## Data Structures Reference
|
|
976
|
+
|
|
977
|
+
### Core Specification
|
|
978
|
+
```python
|
|
979
|
+
core = {
|
|
980
|
+
"functionalDescription": {
|
|
981
|
+
"type": "two-piece set", # "two-piece set", "toroidal", "closed shape"
|
|
982
|
+
"shape": shape_object_or_name, # e.g., "E 42/21/15" or full shape dict
|
|
983
|
+
"material": material_object_or_name, # e.g., "3C95" or full material dict
|
|
984
|
+
"gapping": [
|
|
985
|
+
{"type": "subtractive", "length": 0.001}, # 1mm gap
|
|
986
|
+
{"type": "additive", "length": 0.0005} # 0.5mm spacer
|
|
987
|
+
],
|
|
988
|
+
"numberStacks": 1 # Number of stacked cores
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
### Coil/Winding Specification
|
|
994
|
+
```python
|
|
995
|
+
coil = {
|
|
996
|
+
"bobbin": bobbin_data, # Optional
|
|
997
|
+
"functionalDescription": [
|
|
998
|
+
{
|
|
999
|
+
"name": "Primary",
|
|
1000
|
+
"numberTurns": 40,
|
|
1001
|
+
"numberParallels": 1,
|
|
1002
|
+
"wire": "Round 0.4 - Grade 1", # Wire name or full wire dict
|
|
1003
|
+
"isolationSide": "primary" # "primary", "secondary", "tertiary"
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
"name": "Secondary",
|
|
1007
|
+
"numberTurns": 5,
|
|
1008
|
+
"numberParallels": 2,
|
|
1009
|
+
"wire": "Round 0.8 - Grade 1",
|
|
1010
|
+
"isolationSide": "secondary"
|
|
1011
|
+
}
|
|
1012
|
+
]
|
|
1013
|
+
}
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
### Operating Point (Waveforms)
|
|
1017
|
+
```python
|
|
1018
|
+
operating_point = {
|
|
1019
|
+
"name": "Nominal",
|
|
1020
|
+
"conditions": {
|
|
1021
|
+
"ambientTemperature": 25, # Celsius
|
|
1022
|
+
"altitude": 0 # meters
|
|
1023
|
+
},
|
|
1024
|
+
"excitationsPerWinding": [
|
|
1025
|
+
{
|
|
1026
|
+
"name": "Primary",
|
|
1027
|
+
"frequency": 100000, # Hz
|
|
1028
|
+
"current": {
|
|
1029
|
+
"waveform": {
|
|
1030
|
+
"data": [0.2, 1.8, 1.8, 0.2], # Current samples [A]
|
|
1031
|
+
"time": [0, 4.5e-6, 4.5e-6, 10e-6] # Time samples [s]
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
"voltage": {
|
|
1035
|
+
"waveform": {
|
|
1036
|
+
"data": [311, 311, 0, 0], # Voltage samples [V]
|
|
1037
|
+
"time": [0, 4.5e-6, 4.5e-6, 10e-6]
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
]
|
|
1042
|
+
}
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
### Design Requirements
|
|
1046
|
+
```python
|
|
1047
|
+
design_requirements = {
|
|
1048
|
+
"magnetizingInductance": {
|
|
1049
|
+
"nominal": 100e-6, # Henries
|
|
1050
|
+
"minimum": 90e-6,
|
|
1051
|
+
"maximum": 110e-6
|
|
1052
|
+
},
|
|
1053
|
+
"turnsRatios": [
|
|
1054
|
+
{"nominal": 4.0, "minimum": 3.8, "maximum": 4.2} # N_pri / N_sec
|
|
1055
|
+
],
|
|
1056
|
+
"leakageInductance": [
|
|
1057
|
+
{"maximum": 5e-6} # Optional
|
|
1058
|
+
],
|
|
1059
|
+
"insulation": {
|
|
1060
|
+
"insulationType": "Functional", # "Basic", "Functional", "Supplementary", "Reinforced"
|
|
1061
|
+
"pollutionDegree": "P2", # "P1", "P2", "P3"
|
|
1062
|
+
"overvoltageCategory": "OVC-II", # "OVC-I", "OVC-II", "OVC-III", "OVC-IV"
|
|
1063
|
+
"standards": ["IEC 61558-1"],
|
|
1064
|
+
"altitude": {"maximum": 2000} # meters
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
### Complete Magnetic
|
|
1070
|
+
```python
|
|
1071
|
+
magnetic = {
|
|
1072
|
+
"core": core, # Core specification dict
|
|
1073
|
+
"coil": coil # Coil specification dict
|
|
1074
|
+
}
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
### MAS (Magnetic Agnostic Structure)
|
|
1078
|
+
```python
|
|
1079
|
+
mas = {
|
|
1080
|
+
"inputs": {
|
|
1081
|
+
"designRequirements": design_requirements,
|
|
1082
|
+
"operatingPoints": [operating_point]
|
|
1083
|
+
},
|
|
1084
|
+
"magnetic": magnetic,
|
|
1085
|
+
"outputs": { # Populated after simulation
|
|
1086
|
+
"coreLosses": 0.5, # Watts
|
|
1087
|
+
"windingLosses": 1.2, # Watts
|
|
1088
|
+
"temperatureRise": 40 # Kelvin
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
---
|
|
1094
|
+
|
|
1095
|
+
## Common Materials Reference
|
|
1096
|
+
|
|
1097
|
+
### Ferrite Materials (High Frequency, 100kHz-1MHz)
|
|
1098
|
+
| Material | Manufacturer | μi | Bsat (mT) | Use Case |
|
|
1099
|
+
|----------|--------------|-----|-----------|----------|
|
|
1100
|
+
| 3C90 | Ferroxcube | 2300 | 380 | General purpose |
|
|
1101
|
+
| 3C95 | Ferroxcube | 3000 | 380 | Power applications |
|
|
1102
|
+
| 3C96 | Ferroxcube | 2000 | 400 | High frequency |
|
|
1103
|
+
| 3F3 | Ferroxcube | 2000 | 400 | 200-500 kHz |
|
|
1104
|
+
| 3F4 | Ferroxcube | 900 | 350 | >500 kHz |
|
|
1105
|
+
| N87 | TDK/EPCOS | 2200 | 390 | Power ferrite |
|
|
1106
|
+
| N97 | TDK/EPCOS | 2300 | 400 | High temp |
|
|
1107
|
+
| PC40 | TDK | 2300 | 390 | Low loss |
|
|
1108
|
+
| PC95 | TDK | 3300 | 380 | High μ, low loss |
|
|
1109
|
+
|
|
1110
|
+
### Powder Core Materials (High DC Bias)
|
|
1111
|
+
| Material | Manufacturer | μi | Bsat (T) | Loss | Cost |
|
|
1112
|
+
|----------|--------------|-----|----------|------|------|
|
|
1113
|
+
| MPP | Magnetics Inc | 14-550 | 0.7 | Lowest | Highest |
|
|
1114
|
+
| High Flux | Magnetics Inc | 14-160 | 1.5 | Low | High |
|
|
1115
|
+
| Kool Mu | Magnetics Inc | 26-125 | 1.0 | Medium | Medium |
|
|
1116
|
+
| XFlux | Magnetics Inc | 26-90 | 1.6 | Higher | Low |
|
|
1117
|
+
| -26 | Micrometals | 75 | 1.4 | High | Lowest |
|
|
1118
|
+
| -52 | Micrometals | 75 | 1.3 | Medium | Low |
|
|
1119
|
+
|
|
1120
|
+
### Core Shape Families
|
|
1121
|
+
| Family | Description | Typical Use |
|
|
1122
|
+
|--------|-------------|-------------|
|
|
1123
|
+
| E, EI | Standard E-cores | General purpose |
|
|
1124
|
+
| EFD | Flat E-cores | Low profile |
|
|
1125
|
+
| ETD, EC | Round center leg | Power transformers |
|
|
1126
|
+
| PQ | Optimized power | High power density |
|
|
1127
|
+
| PM | Power module | Medium power |
|
|
1128
|
+
| RM | Rectangular module | Compact designs |
|
|
1129
|
+
| T | Toroidal | Low leakage, EMI |
|
|
1130
|
+
| P, PT | Pot cores | High inductance |
|
|
1131
|
+
| U, UI | U-cores | Large power |
|
|
1132
|
+
| LP | Planar | PCB integration |
|
|
1133
|
+
|
|
1134
|
+
---
|
|
1135
|
+
|
|
1136
|
+
## Best Practices
|
|
1137
|
+
|
|
1138
|
+
### 1. Always Process Inputs First
|
|
1139
|
+
```python
|
|
1140
|
+
# WRONG
|
|
1141
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "STANDARD_CORES")
|
|
1142
|
+
|
|
1143
|
+
# RIGHT
|
|
1144
|
+
processed = PyOpenMagnetics.process_inputs(inputs)
|
|
1145
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(processed, 5, "STANDARD_CORES")
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
### 2. Handle Result Format Properly
|
|
1149
|
+
```python
|
|
1150
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(processed, 5, "STANDARD_CORES")
|
|
1151
|
+
|
|
1152
|
+
if isinstance(result, dict) and "data" in result:
|
|
1153
|
+
data = result["data"]
|
|
1154
|
+
if isinstance(data, str):
|
|
1155
|
+
print(f"Error: {data}")
|
|
1156
|
+
elif not data:
|
|
1157
|
+
print("No suitable designs found")
|
|
1158
|
+
else:
|
|
1159
|
+
for item in data:
|
|
1160
|
+
mas = item["mas"]
|
|
1161
|
+
score = item["scoring"]
|
|
1162
|
+
magnetic = mas["magnetic"]
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
### 3. Check Saturation
|
|
1166
|
+
```python
|
|
1167
|
+
losses = PyOpenMagnetics.calculate_core_losses(core, coil, inputs, models)
|
|
1168
|
+
B_peak = losses["magneticFluxDensityPeak"]
|
|
1169
|
+
|
|
1170
|
+
if B_peak > 0.35: # For ferrite
|
|
1171
|
+
print("⚠ High flux density - risk of saturation")
|
|
1172
|
+
elif B_peak > 0.25:
|
|
1173
|
+
print("⚠ Moderate flux density - verify margin")
|
|
1174
|
+
else:
|
|
1175
|
+
print("✓ Safe flux density")
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
### 4. Consider Temperature
|
|
1179
|
+
```python
|
|
1180
|
+
# Calculate at max operating temperature
|
|
1181
|
+
temp = 100 # °C
|
|
1182
|
+
winding_losses = PyOpenMagnetics.calculate_winding_losses(magnetic, op, temp)
|
|
1183
|
+
params = PyOpenMagnetics.get_core_temperature_dependant_parameters(core, temp)
|
|
1184
|
+
B_sat_at_temp = params["magneticFluxDensitySaturation"]
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
### 5. Use Appropriate Wire for Frequency
|
|
1188
|
+
```python
|
|
1189
|
+
# Low frequency (<50kHz): Standard round wire
|
|
1190
|
+
wire = PyOpenMagnetics.find_wire_by_name("Round 0.5 - Grade 1")
|
|
1191
|
+
|
|
1192
|
+
# High frequency (>100kHz): Consider litz wire
|
|
1193
|
+
litz_wires = [w for w in PyOpenMagnetics.get_wire_names() if "litz" in w.lower()]
|
|
1194
|
+
|
|
1195
|
+
# Very high current: Use rectangular wire
|
|
1196
|
+
rect_wire = PyOpenMagnetics.find_wire_by_dimension(0.002, "rectangular", "IEC 60317")
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
---
|
|
1200
|
+
|
|
1201
|
+
## Error Handling
|
|
1202
|
+
|
|
1203
|
+
```python
|
|
1204
|
+
try:
|
|
1205
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "STANDARD_CORES")
|
|
1206
|
+
|
|
1207
|
+
if isinstance(result, dict):
|
|
1208
|
+
if "data" in result:
|
|
1209
|
+
data = result["data"]
|
|
1210
|
+
if isinstance(data, str):
|
|
1211
|
+
# Error message
|
|
1212
|
+
print(f"Adviser error: {data}")
|
|
1213
|
+
elif isinstance(data, list):
|
|
1214
|
+
# Success
|
|
1215
|
+
for item in data:
|
|
1216
|
+
mas = item.get("mas", item)
|
|
1217
|
+
score = item.get("scoring", 0)
|
|
1218
|
+
elif "error" in result:
|
|
1219
|
+
print(f"Error: {result['error']}")
|
|
1220
|
+
else:
|
|
1221
|
+
print(f"Unexpected result type: {type(result)}")
|
|
1222
|
+
|
|
1223
|
+
except Exception as e:
|
|
1224
|
+
print(f"Exception: {e}")
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
---
|
|
1228
|
+
|
|
1229
|
+
## Example Files
|
|
1230
|
+
|
|
1231
|
+
- `examples/flyback_design.py` - Complete flyback transformer design
|
|
1232
|
+
- `examples/flyback_220v_12v_1a.py` - 220V to 12V @ 1A flyback
|
|
1233
|
+
- `examples/buck_inductor.py` - Buck converter inductor
|
|
1234
|
+
- `examples/plot_flyback_design.py` - Visualization example
|
|
1235
|
+
- `tests/test_converter_endpoints.py` - All topology tests
|
|
1236
|
+
- `tests/test_core.py` - Core database and calculation tests
|
|
1237
|
+
- `tests/test_winding.py` - Wire and bobbin tests
|
|
1238
|
+
|
|
1239
|
+
---
|
|
1240
|
+
|
|
1241
|
+
## References
|
|
1242
|
+
|
|
1243
|
+
- **llms.txt** - Complete API reference in project root
|
|
1244
|
+
- **PyOpenMagnetics.pyi** - Type stubs for IDE support
|
|
1245
|
+
- **MAS Schema**: github.com/OpenMagnetics/MAS
|
|
1246
|
+
- **MKF Engine**: github.com/OpenMagnetics/MKF
|
|
1247
|
+
- **OpenMagnetics Web**: openmagnetics.com
|
|
1248
|
+
|
|
1249
|
+
---
|
|
1250
|
+
|
|
1251
|
+
## Simulation and Analysis
|
|
1252
|
+
|
|
1253
|
+
### Complete Simulation Workflow
|
|
1254
|
+
|
|
1255
|
+
```python
|
|
1256
|
+
import PyOpenMagnetics
|
|
1257
|
+
import json
|
|
1258
|
+
|
|
1259
|
+
# 1. Create or load magnetic design
|
|
1260
|
+
magnetic = {
|
|
1261
|
+
"core": {
|
|
1262
|
+
"functionalDescription": {
|
|
1263
|
+
"type": "two-piece set",
|
|
1264
|
+
"shape": "E 42/21/15",
|
|
1265
|
+
"material": "3C95",
|
|
1266
|
+
"gapping": [{"type": "subtractive", "length": 0.001}],
|
|
1267
|
+
"numberStacks": 1
|
|
1268
|
+
}
|
|
1269
|
+
},
|
|
1270
|
+
"coil": {
|
|
1271
|
+
"functionalDescription": [
|
|
1272
|
+
{
|
|
1273
|
+
"name": "Primary",
|
|
1274
|
+
"numberTurns": 40,
|
|
1275
|
+
"numberParallels": 1,
|
|
1276
|
+
"wire": "Round 0.4 - Grade 1",
|
|
1277
|
+
"isolationSide": "primary"
|
|
1278
|
+
},
|
|
1279
|
+
{
|
|
1280
|
+
"name": "Secondary",
|
|
1281
|
+
"numberTurns": 10,
|
|
1282
|
+
"numberParallels": 1,
|
|
1283
|
+
"wire": "Round 0.8 - Grade 1",
|
|
1284
|
+
"isolationSide": "secondary"
|
|
1285
|
+
}
|
|
1286
|
+
]
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
# 2. Create operating point
|
|
1291
|
+
operating_point = {
|
|
1292
|
+
"name": "Nominal",
|
|
1293
|
+
"conditions": {"ambientTemperature": 40},
|
|
1294
|
+
"excitationsPerWinding": [
|
|
1295
|
+
{
|
|
1296
|
+
"name": "Primary",
|
|
1297
|
+
"frequency": 100000,
|
|
1298
|
+
"current": {
|
|
1299
|
+
"processed": {
|
|
1300
|
+
"label": "Triangular",
|
|
1301
|
+
"dutyCycle": 0.45,
|
|
1302
|
+
"offset": 0.5,
|
|
1303
|
+
"peakToPeak": 0.4
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
]
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
# 3. Create inputs
|
|
1311
|
+
inputs = {
|
|
1312
|
+
"designRequirements": {
|
|
1313
|
+
"magnetizingInductance": {"nominal": 1000e-6},
|
|
1314
|
+
"turnsRatios": [{"nominal": 4.0}]
|
|
1315
|
+
},
|
|
1316
|
+
"operatingPoints": [operating_point]
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
# 4. Process inputs
|
|
1320
|
+
processed = PyOpenMagnetics.process_inputs(inputs)
|
|
1321
|
+
if isinstance(processed, dict) and "data" in processed:
|
|
1322
|
+
processed = processed["data"]
|
|
1323
|
+
|
|
1324
|
+
# 5. Calculate core losses
|
|
1325
|
+
models = {"coreLosses": "IGSE", "reluctance": "ZHANG"}
|
|
1326
|
+
core_losses = PyOpenMagnetics.calculate_core_losses(
|
|
1327
|
+
magnetic["core"],
|
|
1328
|
+
magnetic["coil"],
|
|
1329
|
+
processed,
|
|
1330
|
+
models
|
|
1331
|
+
)
|
|
1332
|
+
print(f"Core losses: {core_losses.get('coreLosses', 0):.3f} W")
|
|
1333
|
+
print(f"Peak flux density: {core_losses.get('magneticFluxDensityPeak', 0)*1000:.1f} mT")
|
|
1334
|
+
|
|
1335
|
+
# 6. Calculate winding losses
|
|
1336
|
+
winding_losses = PyOpenMagnetics.calculate_winding_losses(
|
|
1337
|
+
magnetic,
|
|
1338
|
+
operating_point,
|
|
1339
|
+
temperature=80
|
|
1340
|
+
)
|
|
1341
|
+
print(f"Winding losses: {winding_losses.get('windingLosses', 0):.3f} W")
|
|
1342
|
+
|
|
1343
|
+
# 7. Verify inductance
|
|
1344
|
+
L_actual = PyOpenMagnetics.calculate_inductance_from_number_turns_and_gapping(
|
|
1345
|
+
magnetic["core"],
|
|
1346
|
+
magnetic["coil"],
|
|
1347
|
+
operating_point,
|
|
1348
|
+
{"reluctance": "ZHANG"}
|
|
1349
|
+
)
|
|
1350
|
+
print(f"Actual inductance: {L_actual*1e6:.1f} µH")
|
|
1351
|
+
|
|
1352
|
+
# 8. Check saturation
|
|
1353
|
+
I_sat = PyOpenMagnetics.calculate_saturation_current(magnetic, temperature=100)
|
|
1354
|
+
print(f"Saturation current: {I_sat:.2f} A")
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
### Advanced Simulation Features
|
|
1358
|
+
|
|
1359
|
+
```python
|
|
1360
|
+
# Temperature-dependent simulation
|
|
1361
|
+
for temp in [25, 50, 75, 100]:
|
|
1362
|
+
params = PyOpenMagnetics.get_core_temperature_dependant_parameters(
|
|
1363
|
+
magnetic["core"],
|
|
1364
|
+
temperature=temp
|
|
1365
|
+
)
|
|
1366
|
+
B_sat = params["magneticFluxDensitySaturation"]
|
|
1367
|
+
mu_eff = params["effectivePermeability"]
|
|
1368
|
+
print(f"T={temp}°C: B_sat={B_sat*1000:.0f} mT, μ_eff={mu_eff:.0f}")
|
|
1369
|
+
|
|
1370
|
+
# Harmonic analysis
|
|
1371
|
+
harmonics = PyOpenMagnetics.calculate_harmonics(
|
|
1372
|
+
{"data": [0, 1, 0, -1, 0], "time": [0, 2.5e-6, 5e-6, 7.5e-6, 10e-6]}
|
|
1373
|
+
)
|
|
1374
|
+
|
|
1375
|
+
# Signal processing
|
|
1376
|
+
P_rms = PyOpenMagnetics.calculate_rms_power(voltage_waveform, current_waveform)
|
|
1377
|
+
P_inst = PyOpenMagnetics.calculate_instantaneous_power(voltage_waveform, current_waveform)
|
|
1378
|
+
```
|
|
1379
|
+
|
|
1380
|
+
---
|
|
1381
|
+
|
|
1382
|
+
## Visualization and Plotting
|
|
1383
|
+
|
|
1384
|
+
### Basic Plotting Functions
|
|
1385
|
+
|
|
1386
|
+
```python
|
|
1387
|
+
import PyOpenMagnetics
|
|
1388
|
+
|
|
1389
|
+
# Plot core only
|
|
1390
|
+
result = PyOpenMagnetics.plot_core(core, use_colors=True)
|
|
1391
|
+
if result.get('success'):
|
|
1392
|
+
svg_content = result['svg']
|
|
1393
|
+
with open('core.svg', 'w') as f:
|
|
1394
|
+
f.write(svg_content)
|
|
1395
|
+
|
|
1396
|
+
# Plot complete magnetic (core + windings)
|
|
1397
|
+
result = PyOpenMagnetics.plot_magnetic(magnetic)
|
|
1398
|
+
if result.get('success'):
|
|
1399
|
+
with open('magnetic.svg', 'w') as f:
|
|
1400
|
+
f.write(result['svg'])
|
|
1401
|
+
|
|
1402
|
+
# Plot wire
|
|
1403
|
+
wire = PyOpenMagnetics.find_wire_by_name("Round 0.5 - Grade 1")
|
|
1404
|
+
result = PyOpenMagnetics.plot_wire(wire)
|
|
1405
|
+
if result.get('success'):
|
|
1406
|
+
with open('wire.svg', 'w') as f:
|
|
1407
|
+
f.write(result['svg'])
|
|
1408
|
+
|
|
1409
|
+
# Plot bobbin
|
|
1410
|
+
result = PyOpenMagnetics.plot_bobbin(magnetic)
|
|
1411
|
+
if result.get('success'):
|
|
1412
|
+
with open('bobbin.svg', 'w') as f:
|
|
1413
|
+
f.write(result['svg'])
|
|
1414
|
+
```
|
|
1415
|
+
|
|
1416
|
+
### Field Visualization
|
|
1417
|
+
|
|
1418
|
+
```python
|
|
1419
|
+
# Magnetic field plot (requires operating point with current)
|
|
1420
|
+
operating_point = {
|
|
1421
|
+
"name": "Field Plot",
|
|
1422
|
+
"conditions": {"ambientTemperature": 25},
|
|
1423
|
+
"excitationsPerWinding": [
|
|
1424
|
+
{
|
|
1425
|
+
"name": "Primary",
|
|
1426
|
+
"frequency": 100000,
|
|
1427
|
+
"current": {
|
|
1428
|
+
"processed": {
|
|
1429
|
+
"label": "Triangular",
|
|
1430
|
+
"dutyCycle": 0.45,
|
|
1431
|
+
"offset": 0.5,
|
|
1432
|
+
"peakToPeak": 0.2
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
]
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
result = PyOpenMagnetics.plot_magnetic_field(magnetic, operating_point)
|
|
1440
|
+
if result.get('success'):
|
|
1441
|
+
with open('magnetic_field.svg', 'w') as f:
|
|
1442
|
+
f.write(result['svg'])
|
|
1443
|
+
|
|
1444
|
+
# Electric field plot (requires voltage)
|
|
1445
|
+
operating_point_with_voltage = {
|
|
1446
|
+
"name": "Electric Field",
|
|
1447
|
+
"conditions": {"ambientTemperature": 25},
|
|
1448
|
+
"excitationsPerWinding": [
|
|
1449
|
+
{
|
|
1450
|
+
"name": "Primary",
|
|
1451
|
+
"frequency": 100000,
|
|
1452
|
+
"current": {
|
|
1453
|
+
"processed": {
|
|
1454
|
+
"label": "Triangular",
|
|
1455
|
+
"dutyCycle": 0.45,
|
|
1456
|
+
"offset": 0.5,
|
|
1457
|
+
"peakToPeak": 0.2
|
|
1458
|
+
}
|
|
1459
|
+
},
|
|
1460
|
+
"voltage": {
|
|
1461
|
+
"processed": {
|
|
1462
|
+
"label": "Rectangular",
|
|
1463
|
+
"dutyCycle": 0.45,
|
|
1464
|
+
"offset": 100,
|
|
1465
|
+
"peakToPeak": 200
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
]
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
result = PyOpenMagnetics.plot_electric_field(magnetic, operating_point_with_voltage)
|
|
1473
|
+
if result.get('success'):
|
|
1474
|
+
with open('electric_field.svg', 'w') as f:
|
|
1475
|
+
f.write(result['svg'])
|
|
1476
|
+
```
|
|
1477
|
+
|
|
1478
|
+
### Complete Visualization Workflow
|
|
1479
|
+
|
|
1480
|
+
```python
|
|
1481
|
+
def visualize_complete_design(magnetic, operating_point, output_dir="output"):
|
|
1482
|
+
"""Generate all visualizations for a design."""
|
|
1483
|
+
import os
|
|
1484
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
1485
|
+
|
|
1486
|
+
visualizations = [
|
|
1487
|
+
("core", lambda: PyOpenMagnetics.plot_core(magnetic["core"])),
|
|
1488
|
+
("magnetic", lambda: PyOpenMagnetics.plot_magnetic(magnetic)),
|
|
1489
|
+
("magnetic_field", lambda: PyOpenMagnetics.plot_magnetic_field(magnetic, operating_point)),
|
|
1490
|
+
("electric_field", lambda: PyOpenMagnetics.plot_electric_field(magnetic, operating_point)),
|
|
1491
|
+
]
|
|
1492
|
+
|
|
1493
|
+
for name, plot_func in visualizations:
|
|
1494
|
+
try:
|
|
1495
|
+
result = plot_func()
|
|
1496
|
+
if result.get('success'):
|
|
1497
|
+
filename = os.path.join(output_dir, f"{name}.svg")
|
|
1498
|
+
with open(filename, 'w') as f:
|
|
1499
|
+
f.write(result['svg'])
|
|
1500
|
+
print(f"✓ Saved: {filename}")
|
|
1501
|
+
else:
|
|
1502
|
+
print(f"✗ {name} failed: {result.get('error')}")
|
|
1503
|
+
except Exception as e:
|
|
1504
|
+
print(f"✗ {name} error: {e}")
|
|
1505
|
+
```
|
|
1506
|
+
|
|
1507
|
+
---
|
|
1508
|
+
|
|
1509
|
+
## Converter-Based Design (converter.h Methods)
|
|
1510
|
+
|
|
1511
|
+
PyOpenMagnetics provides high-level converter-based design functions that automatically process converter specifications and generate magnetic designs. These are the Python bindings for the methods in `converter.h`.
|
|
1512
|
+
|
|
1513
|
+
### process_converter() - Generic Converter Processor
|
|
1514
|
+
|
|
1515
|
+
Process any converter topology to generate Inputs for magnetic design.
|
|
1516
|
+
|
|
1517
|
+
```python
|
|
1518
|
+
# Generic converter processor
|
|
1519
|
+
result = PyOpenMagnetics.process_converter(
|
|
1520
|
+
topology="flyback", # Topology name
|
|
1521
|
+
converter={
|
|
1522
|
+
"inputVoltage": {"minimum": 85, "maximum": 265},
|
|
1523
|
+
"desiredInductance": 1e-3,
|
|
1524
|
+
"desiredTurnsRatios": [10.0],
|
|
1525
|
+
"operatingPoints": [{
|
|
1526
|
+
"outputVoltages": [12.0],
|
|
1527
|
+
"outputCurrents": [2.0],
|
|
1528
|
+
"switchingFrequency": 100000,
|
|
1529
|
+
"ambientTemperature": 40
|
|
1530
|
+
}]
|
|
1531
|
+
},
|
|
1532
|
+
use_ngspice=True # Use SPICE simulation for accurate waveforms
|
|
1533
|
+
)
|
|
1534
|
+
|
|
1535
|
+
# Returns: {"designRequirements": {...}, "operatingPoints": [...]}
|
|
1536
|
+
# On error: {"error": "error message"}
|
|
1537
|
+
```
|
|
1538
|
+
|
|
1539
|
+
**Supported Topologies:**
|
|
1540
|
+
- `"flyback"` - Flyback converter (isolated, energy storing)
|
|
1541
|
+
- `"buck"` - Buck converter (step-down)
|
|
1542
|
+
- `"boost"` - Boost converter (step-up)
|
|
1543
|
+
- `"single_switch_forward"` - Single-switch forward
|
|
1544
|
+
- `"two_switch_forward"` - Two-switch forward
|
|
1545
|
+
- `"active_clamp_forward"` - Active clamp forward
|
|
1546
|
+
- `"push_pull"` - Push-pull converter
|
|
1547
|
+
- `"isolated_buck"` - Isolated buck
|
|
1548
|
+
- `"isolated_buck_boost"` - Isolated buck-boost
|
|
1549
|
+
- `"llc"` - LLC resonant converter
|
|
1550
|
+
- `"current_transformer"` - Current sensing transformer
|
|
1551
|
+
|
|
1552
|
+
### design_magnetics_from_converter() - Complete Design Pipeline
|
|
1553
|
+
|
|
1554
|
+
**This is the most powerful function for converter design!** It combines converter processing with the magnetic adviser to go directly from converter specs to ranked magnetic designs.
|
|
1555
|
+
|
|
1556
|
+
```python
|
|
1557
|
+
# Complete converter-to-magnetic design pipeline
|
|
1558
|
+
result = PyOpenMagnetics.design_magnetics_from_converter(
|
|
1559
|
+
topology="flyback",
|
|
1560
|
+
converter={
|
|
1561
|
+
"inputVoltage": {"minimum": 185, "maximum": 265},
|
|
1562
|
+
"desiredInductance": 800e-6,
|
|
1563
|
+
"desiredTurnsRatios": [13.5],
|
|
1564
|
+
"maximumDutyCycle": 0.45,
|
|
1565
|
+
"efficiency": 0.88,
|
|
1566
|
+
"diodeVoltageDrop": 0.5,
|
|
1567
|
+
"operatingPoints": [{
|
|
1568
|
+
"outputVoltages": [12.0],
|
|
1569
|
+
"outputCurrents": [2.0],
|
|
1570
|
+
"switchingFrequency": 100000,
|
|
1571
|
+
"ambientTemperature": 40
|
|
1572
|
+
}]
|
|
1573
|
+
},
|
|
1574
|
+
max_results=5, # Return top 5 designs
|
|
1575
|
+
core_mode="STANDARD_CORES", # or "AVAILABLE_CORES"
|
|
1576
|
+
use_ngspice=True, # Use SPICE for accurate waveforms
|
|
1577
|
+
weights={ # Optional scoring weights
|
|
1578
|
+
"COST": 1.0,
|
|
1579
|
+
"EFFICIENCY": 2.0,
|
|
1580
|
+
"DIMENSIONS": 0.5
|
|
1581
|
+
}
|
|
1582
|
+
)
|
|
1583
|
+
|
|
1584
|
+
# Returns: {"data": [{"mas": {...}, "scoring": float, "scoringPerFilter": {...}}, ...]}
|
|
1585
|
+
# Each result contains:
|
|
1586
|
+
# - "mas": Complete MAS object with magnetic design
|
|
1587
|
+
# - "scoring": Overall score (higher is better)
|
|
1588
|
+
# - "scoringPerFilter": Individual filter scores
|
|
1589
|
+
```
|
|
1590
|
+
|
|
1591
|
+
### Complete Example: Flyback Design with design_magnetics_from_converter()
|
|
1592
|
+
|
|
1593
|
+
```python
|
|
1594
|
+
import PyOpenMagnetics
|
|
1595
|
+
import json
|
|
1596
|
+
|
|
1597
|
+
# Define flyback converter specifications
|
|
1598
|
+
flyback_specs = {
|
|
1599
|
+
"inputVoltage": {
|
|
1600
|
+
"minimum": 185, # V AC minimum
|
|
1601
|
+
"maximum": 265 # V AC maximum
|
|
1602
|
+
},
|
|
1603
|
+
"desiredInductance": 800e-6, # 800 µH magnetizing inductance
|
|
1604
|
+
"desiredTurnsRatios": [13.5], # Np/Ns for 12V output
|
|
1605
|
+
"maximumDutyCycle": 0.45,
|
|
1606
|
+
"efficiency": 0.88,
|
|
1607
|
+
"diodeVoltageDrop": 0.5,
|
|
1608
|
+
"operatingPoints": [{
|
|
1609
|
+
"outputVoltages": [12.0], # 12V output
|
|
1610
|
+
"outputCurrents": [2.0], # 2A output
|
|
1611
|
+
"switchingFrequency": 100000, # 100 kHz
|
|
1612
|
+
"ambientTemperature": 40
|
|
1613
|
+
}]
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
# Design magnetics directly from converter specs
|
|
1617
|
+
print("Designing magnetics from converter specifications...")
|
|
1618
|
+
result = PyOpenMagnetics.design_magnetics_from_converter(
|
|
1619
|
+
topology="flyback",
|
|
1620
|
+
converter=flyback_specs,
|
|
1621
|
+
max_results=3,
|
|
1622
|
+
core_mode="STANDARD_CORES",
|
|
1623
|
+
use_ngspice=False, # Use analytical for speed
|
|
1624
|
+
weights={
|
|
1625
|
+
"COST": 1.0,
|
|
1626
|
+
"EFFICIENCY": 2.0, # Prioritize efficiency
|
|
1627
|
+
"DIMENSIONS": 0.5
|
|
1628
|
+
}
|
|
1629
|
+
)
|
|
1630
|
+
|
|
1631
|
+
# Process results
|
|
1632
|
+
if isinstance(result, dict) and "data" in result:
|
|
1633
|
+
designs = result["data"]
|
|
1634
|
+
print(f"\n✓ Found {len(designs)} suitable designs\n")
|
|
1635
|
+
|
|
1636
|
+
for i, design in enumerate(designs):
|
|
1637
|
+
mas = design["mas"]
|
|
1638
|
+
magnetic = mas["magnetic"]
|
|
1639
|
+
score = design["scoring"]
|
|
1640
|
+
|
|
1641
|
+
core = magnetic["core"]
|
|
1642
|
+
shape = core["functionalDescription"]["shape"]["name"]
|
|
1643
|
+
material = core["functionalDescription"]["material"]["name"]
|
|
1644
|
+
|
|
1645
|
+
print(f"Design #{i+1}: {shape} / {material}")
|
|
1646
|
+
print(f" Score: {score:.3f}")
|
|
1647
|
+
|
|
1648
|
+
# Get winding info
|
|
1649
|
+
if "coil" in magnetic and "functionalDescription" in magnetic["coil"]:
|
|
1650
|
+
for winding in magnetic["coil"]["functionalDescription"]:
|
|
1651
|
+
name = winding.get("name", "Winding")
|
|
1652
|
+
turns = winding.get("numberTurns", "?")
|
|
1653
|
+
print(f" {name}: {turns} turns")
|
|
1654
|
+
|
|
1655
|
+
# Calculate losses
|
|
1656
|
+
models = {"coreLosses": "IGSE", "reluctance": "ZHANG"}
|
|
1657
|
+
try:
|
|
1658
|
+
losses = PyOpenMagnetics.calculate_core_losses(
|
|
1659
|
+
core, magnetic["coil"], mas["inputs"], models
|
|
1660
|
+
)
|
|
1661
|
+
print(f" Core losses: {losses.get('coreLosses', 0):.3f} W")
|
|
1662
|
+
except:
|
|
1663
|
+
pass
|
|
1664
|
+
|
|
1665
|
+
print()
|
|
1666
|
+
else:
|
|
1667
|
+
print("No designs found or error occurred")
|
|
1668
|
+
```
|
|
1669
|
+
|
|
1670
|
+
### Topology-Specific Processors
|
|
1671
|
+
|
|
1672
|
+
For convenience, PyOpenMagnetics also provides dedicated functions for each topology:
|
|
1673
|
+
|
|
1674
|
+
```python
|
|
1675
|
+
# Flyback
|
|
1676
|
+
flyback_inputs = PyOpenMagnetics.process_flyback({
|
|
1677
|
+
"inputVoltage": {"minimum": 85, "maximum": 265},
|
|
1678
|
+
"desiredInductance": 1e-3,
|
|
1679
|
+
"desiredTurnsRatios": [10.0],
|
|
1680
|
+
"operatingPoints": [{
|
|
1681
|
+
"outputVoltages": [12.0],
|
|
1682
|
+
"outputCurrents": [2.0],
|
|
1683
|
+
"switchingFrequency": 100000,
|
|
1684
|
+
"ambientTemperature": 40
|
|
1685
|
+
}]
|
|
1686
|
+
})
|
|
1687
|
+
|
|
1688
|
+
# Buck
|
|
1689
|
+
buck_inputs = PyOpenMagnetics.process_buck({
|
|
1690
|
+
"inputVoltage": {"minimum": 8, "maximum": 14},
|
|
1691
|
+
"desiredInductance": 4.7e-6,
|
|
1692
|
+
"currentRippleRatio": 0.3,
|
|
1693
|
+
"operatingPoints": [{
|
|
1694
|
+
"outputVoltage": 3.3,
|
|
1695
|
+
"outputCurrent": 5.0,
|
|
1696
|
+
"switchingFrequency": 500000,
|
|
1697
|
+
"ambientTemperature": 25
|
|
1698
|
+
}]
|
|
1699
|
+
})
|
|
1700
|
+
|
|
1701
|
+
# Boost
|
|
1702
|
+
boost_inputs = PyOpenMagnetics.process_boost({
|
|
1703
|
+
"inputVoltage": {"minimum": 85, "maximum": 265},
|
|
1704
|
+
"desiredInductance": 250e-6,
|
|
1705
|
+
"currentRippleRatio": 0.2,
|
|
1706
|
+
"operatingPoints": [{
|
|
1707
|
+
"outputVoltage": 400,
|
|
1708
|
+
"outputCurrent": 2.5,
|
|
1709
|
+
"switchingFrequency": 65000,
|
|
1710
|
+
"ambientTemperature": 40
|
|
1711
|
+
}]
|
|
1712
|
+
})
|
|
1713
|
+
|
|
1714
|
+
# Other topologies
|
|
1715
|
+
forward_inputs = PyOpenMagnetics.process_single_switch_forward({...})
|
|
1716
|
+
forward2_inputs = PyOpenMagnetics.process_two_switch_forward({...})
|
|
1717
|
+
active_clamp_inputs = PyOpenMagnetics.process_active_clamp_forward({...})
|
|
1718
|
+
push_pull_inputs = PyOpenMagnetics.process_push_pull({...})
|
|
1719
|
+
isolated_buck_inputs = PyOpenMagnetics.process_isolated_buck({...})
|
|
1720
|
+
isolated_bb_inputs = PyOpenMagnetics.process_isolated_buck_boost({...})
|
|
1721
|
+
ct_inputs = PyOpenMagnetics.process_current_transformer({...}, turns_ratio=100)
|
|
1722
|
+
```
|
|
1723
|
+
|
|
1724
|
+
### When to Use Each Method
|
|
1725
|
+
|
|
1726
|
+
**Use `design_magnetics_from_converter()` when:**
|
|
1727
|
+
- You want a complete design from converter specs to magnetic designs
|
|
1728
|
+
- You need ranked/scored design recommendations
|
|
1729
|
+
- You want the fastest path from specs to buildable design
|
|
1730
|
+
|
|
1731
|
+
**Use `process_converter()` when:**
|
|
1732
|
+
- You want to customize the magnetic design process
|
|
1733
|
+
- You need to modify the inputs before running the adviser
|
|
1734
|
+
- You want to use the magnetic adviser with custom weights/filters
|
|
1735
|
+
|
|
1736
|
+
**Use topology-specific processors when:**
|
|
1737
|
+
- You know the exact topology
|
|
1738
|
+
- You want cleaner, more readable code
|
|
1739
|
+
- You don't need the flexibility of the generic processor
|
|
1740
|
+
|
|
1741
|
+
---
|
|
1742
|
+
|
|
1743
|
+
## SPICE Export
|
|
1744
|
+
|
|
1745
|
+
```python
|
|
1746
|
+
# Export magnetic as SPICE subcircuit
|
|
1747
|
+
subcircuit = PyOpenMagnetics.export_magnetic_as_subcircuit(magnetic)
|
|
1748
|
+
|
|
1749
|
+
# Save to file
|
|
1750
|
+
with open('transformer.spice', 'w') as f:
|
|
1751
|
+
f.write(subcircuit)
|
|
1752
|
+
|
|
1753
|
+
# The subcircuit can be used in SPICE simulators like LTspice, ngspice, etc.
|
|
1754
|
+
```
|
|
1755
|
+
|
|
1756
|
+
---
|
|
1757
|
+
|
|
1758
|
+
## Quick Reference Card
|
|
1759
|
+
|
|
1760
|
+
```python
|
|
1761
|
+
import PyOpenMagnetics as PyOM
|
|
1762
|
+
|
|
1763
|
+
# Database
|
|
1764
|
+
materials = PyOM.get_core_material_names()
|
|
1765
|
+
shapes = PyOM.get_core_shape_names()
|
|
1766
|
+
wires = PyOM.get_wire_names()
|
|
1767
|
+
|
|
1768
|
+
# Find items
|
|
1769
|
+
mat = PyOM.find_core_material_by_name("3C95")
|
|
1770
|
+
shape = PyOM.find_core_shape_by_name("E 42/21/15")
|
|
1771
|
+
wire = PyOM.find_wire_by_name("Round 0.5 - Grade 1")
|
|
1772
|
+
|
|
1773
|
+
# Design
|
|
1774
|
+
processed = PyOM.process_inputs(inputs)
|
|
1775
|
+
result = PyOM.calculate_advised_magnetics(processed, 5, "standard cores") # Note: lowercase!
|
|
1776
|
+
|
|
1777
|
+
# Losses
|
|
1778
|
+
models = {"coreLosses": "IGSE", "reluctance": "ZHANG"}
|
|
1779
|
+
core_losses = PyOM.calculate_core_losses(core, coil, inputs, models)
|
|
1780
|
+
winding_losses = PyOM.calculate_winding_losses(magnetic, op, 80)
|
|
1781
|
+
|
|
1782
|
+
# Visualization
|
|
1783
|
+
result = PyOM.plot_core(core, use_colors=True)
|
|
1784
|
+
if result.get('success'):
|
|
1785
|
+
svg = result['svg']
|
|
1786
|
+
|
|
1787
|
+
# SPICE Export
|
|
1788
|
+
spice = PyOM.export_magnetic_as_subcircuit(magnetic)
|
|
1789
|
+
```
|
|
1790
|
+
|
|
1791
|
+
---
|
|
1792
|
+
|
|
1793
|
+
## Troubleshooting & Common Issues
|
|
1794
|
+
|
|
1795
|
+
This section documents common problems encountered when using PyOpenMagnetics and their solutions.
|
|
1796
|
+
|
|
1797
|
+
### 0. Empty Results from `calculate_advised_magnetics()`
|
|
1798
|
+
|
|
1799
|
+
**Problem:** Function returns `{"data": []}` or no designs found despite valid inputs.
|
|
1800
|
+
|
|
1801
|
+
**Cause:** The `core_mode` parameter is case-sensitive and must be lowercase.
|
|
1802
|
+
|
|
1803
|
+
**Solution:**
|
|
1804
|
+
```python
|
|
1805
|
+
# WRONG - returns empty results:
|
|
1806
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "STANDARD_CORES")
|
|
1807
|
+
|
|
1808
|
+
# CORRECT:
|
|
1809
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "standard cores")
|
|
1810
|
+
# Alternative: "available cores"
|
|
1811
|
+
```
|
|
1812
|
+
|
|
1813
|
+
**Check:** Verify the result format:
|
|
1814
|
+
```python
|
|
1815
|
+
if isinstance(result, dict) and "data" in result:
|
|
1816
|
+
if isinstance(result["data"], list) and len(result["data"]) > 0:
|
|
1817
|
+
print(f"Found {len(result['data'])} designs")
|
|
1818
|
+
else:
|
|
1819
|
+
print("No designs found - check core_mode case!")
|
|
1820
|
+
```
|
|
1821
|
+
|
|
1822
|
+
---
|
|
1823
|
+
|
|
1824
|
+
### 1. Import Issues
|
|
1825
|
+
|
|
1826
|
+
**Problem:** `AttributeError: module 'PyOpenMagnetics' has no attribute 'process_flyback'`
|
|
1827
|
+
|
|
1828
|
+
**Cause:** The PyOpenMagnetics package is a C++ extension module (.so file) without a proper Python `__init__.py` file.
|
|
1829
|
+
|
|
1830
|
+
**Solution:** See the **Module Import Instructions** section in Installation above, or create an `__init__.py` file:
|
|
1831
|
+
|
|
1832
|
+
```python
|
|
1833
|
+
# In: site-packages/PyOpenMagnetics/__init__.py
|
|
1834
|
+
import os
|
|
1835
|
+
import importlib.util
|
|
1836
|
+
|
|
1837
|
+
# Get the path to the .so file
|
|
1838
|
+
so_file = os.path.join(os.path.dirname(__file__),
|
|
1839
|
+
'PyOpenMagnetics.cpython-311-x86_64-linux-gnu.so')
|
|
1840
|
+
|
|
1841
|
+
# Load the module
|
|
1842
|
+
spec = importlib.util.spec_from_file_location('PyOpenMagnetics', so_file)
|
|
1843
|
+
module = importlib.util.module_from_spec(spec)
|
|
1844
|
+
spec.loader.exec_module(module)
|
|
1845
|
+
|
|
1846
|
+
# Export all attributes
|
|
1847
|
+
for attr_name in dir(module):
|
|
1848
|
+
if not attr_name.startswith('_'):
|
|
1849
|
+
globals()[attr_name] = getattr(module, attr_name)
|
|
1850
|
+
```
|
|
1851
|
+
|
|
1852
|
+
**Alternative:** Load the module directly using importlib:
|
|
1853
|
+
|
|
1854
|
+
```python
|
|
1855
|
+
import importlib.util
|
|
1856
|
+
|
|
1857
|
+
spec = importlib.util.spec_from_file_location(
|
|
1858
|
+
'PyOpenMagnetics',
|
|
1859
|
+
'/path/to/PyOpenMagnetics.cpython-311-x86_64-linux-gnu.so'
|
|
1860
|
+
)
|
|
1861
|
+
PyOpenMagnetics = importlib.util.module_from_spec(spec)
|
|
1862
|
+
spec.loader.exec_module(PyOpenMagnetics)
|
|
1863
|
+
```
|
|
1864
|
+
|
|
1865
|
+
### 2. Case-Sensitive Parameters
|
|
1866
|
+
|
|
1867
|
+
**Problem:** `Exception: Input JSON does not conform to schema!`
|
|
1868
|
+
|
|
1869
|
+
**Cause:** The `core_mode` parameter in `calculate_advised_magnetics()` is case-sensitive.
|
|
1870
|
+
|
|
1871
|
+
**Solution:** Use lowercase for core_mode:
|
|
1872
|
+
|
|
1873
|
+
```python
|
|
1874
|
+
# WRONG:
|
|
1875
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "STANDARD_CORES")
|
|
1876
|
+
|
|
1877
|
+
# CORRECT:
|
|
1878
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "standard cores")
|
|
1879
|
+
```
|
|
1880
|
+
|
|
1881
|
+
Valid values are:
|
|
1882
|
+
- `"standard cores"` - Use standard catalog cores
|
|
1883
|
+
- `"available cores"` - Use only cores marked as available/in stock
|
|
1884
|
+
|
|
1885
|
+
### 3. Converter Schema Errors (All Topologies)
|
|
1886
|
+
|
|
1887
|
+
**Problem:** Any `process_*()` function (`process_flyback()`, `process_buck()`, `process_boost()`, etc.) returns `"Input JSON does not conform to schema!"` even with all required fields.
|
|
1888
|
+
|
|
1889
|
+
**Cause:** Topology-specific functions have stricter schema validation than the generic `process_converter()`.
|
|
1890
|
+
|
|
1891
|
+
**Solution - Use `process_converter()` as fallback for ALL converters:**
|
|
1892
|
+
```python
|
|
1893
|
+
# If this fails:
|
|
1894
|
+
inputs = PyOpenMagnetics.process_flyback(flyback_specs)
|
|
1895
|
+
|
|
1896
|
+
# Use this instead (works reliably):
|
|
1897
|
+
flyback_minimal = {
|
|
1898
|
+
"inputVoltage": {"minimum": 185, "maximum": 265},
|
|
1899
|
+
"diodeVoltageDrop": 0.7,
|
|
1900
|
+
"efficiency": 0.88,
|
|
1901
|
+
"desiredInductance": 800e-6,
|
|
1902
|
+
"desiredTurnsRatios": [18.0],
|
|
1903
|
+
"desiredDutyCycle": [[0.45]], # Note: double brackets!
|
|
1904
|
+
"operatingPoints": [{
|
|
1905
|
+
"outputVoltages": [12.0],
|
|
1906
|
+
"outputCurrents": [2.0],
|
|
1907
|
+
"switchingFrequency": 100000,
|
|
1908
|
+
"ambientTemperature": 40
|
|
1909
|
+
}]
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
inputs = PyOpenMagnetics.process_converter("flyback", flyback_minimal, False)
|
|
1913
|
+
```
|
|
1914
|
+
|
|
1915
|
+
**Alternative - Include ALL required fields for `process_flyback()`:**
|
|
1916
|
+
|
|
1917
|
+
```python
|
|
1918
|
+
flyback_specs = {
|
|
1919
|
+
# Required by basic Flyback schema:
|
|
1920
|
+
"inputVoltage": {"minimum": 185, "maximum": 265},
|
|
1921
|
+
"diodeVoltageDrop": 0.7,
|
|
1922
|
+
"currentRippleRatio": 0.3, # REQUIRED!
|
|
1923
|
+
"efficiency": 0.85,
|
|
1924
|
+
|
|
1925
|
+
# Required by AdvancedFlyback:
|
|
1926
|
+
"desiredInductance": 500e-6, # REQUIRED!
|
|
1927
|
+
"desiredTurnsRatios": [15.0], # REQUIRED!
|
|
1928
|
+
"desiredDutyCycle": [[0.45]], # REQUIRED! (note: double brackets)
|
|
1929
|
+
"maximumDutyCycle": 0.45, # Optional but recommended
|
|
1930
|
+
|
|
1931
|
+
"operatingPoints": [{
|
|
1932
|
+
"outputVoltages": [12.0],
|
|
1933
|
+
"outputCurrents": [2.0],
|
|
1934
|
+
"switchingFrequency": 100000,
|
|
1935
|
+
"ambientTemperature": 40
|
|
1936
|
+
}]
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
inputs = PyOpenMagnetics.process_flyback(flyback_specs)
|
|
1940
|
+
```
|
|
1941
|
+
|
|
1942
|
+
**Important:** The `desiredDutyCycle` field must be a nested list `[[duty_cycle]]`, not just `[duty_cycle]`.
|
|
1943
|
+
|
|
1944
|
+
### 4. Understanding Result Types
|
|
1945
|
+
|
|
1946
|
+
**Problem:** Difficulty parsing results from `calculate_advised_magnetics()`
|
|
1947
|
+
|
|
1948
|
+
**Solution:** Results are returned as dictionaries with a `data` key:
|
|
1949
|
+
|
|
1950
|
+
```python
|
|
1951
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "standard cores")
|
|
1952
|
+
|
|
1953
|
+
if isinstance(result, dict) and "data" in result:
|
|
1954
|
+
designs = result["data"]
|
|
1955
|
+
|
|
1956
|
+
if isinstance(designs, str):
|
|
1957
|
+
# Error message returned as string
|
|
1958
|
+
print(f"Error: {designs}")
|
|
1959
|
+
elif isinstance(designs, list):
|
|
1960
|
+
# Success - list of design dictionaries
|
|
1961
|
+
for design in designs:
|
|
1962
|
+
mas = design["mas"]
|
|
1963
|
+
score = design["scoring"]
|
|
1964
|
+
magnetic = mas["magnetic"]
|
|
1965
|
+
# ... process design
|
|
1966
|
+
```
|
|
1967
|
+
|
|
1968
|
+
### 5. Generic Converter Processor Reference
|
|
1969
|
+
|
|
1970
|
+
**Quick Reference:** Use `process_converter()` as a universal fallback for all topologies:
|
|
1971
|
+
|
|
1972
|
+
```python
|
|
1973
|
+
# Universal pattern for ALL converter topologies:
|
|
1974
|
+
inputs = PyOpenMagnetics.process_converter(
|
|
1975
|
+
topology="<topology_name>", # See list below
|
|
1976
|
+
converter=<converter_specs>, # Topology-specific dict
|
|
1977
|
+
use_ngspice=False # False = analytical (faster), True = SPICE (accurate)
|
|
1978
|
+
)
|
|
1979
|
+
```
|
|
1980
|
+
|
|
1981
|
+
**Supported topology names:**
|
|
1982
|
+
- `"flyback"` - Flyback converter
|
|
1983
|
+
- `"buck"` - Buck converter (step-down)
|
|
1984
|
+
- `"boost"` - Boost converter (step-up)
|
|
1985
|
+
- `"single_switch_forward"` - Single-switch forward
|
|
1986
|
+
- `"two_switch_forward"` - Two-switch forward
|
|
1987
|
+
- `"active_clamp_forward"` - Active clamp forward
|
|
1988
|
+
- `"push_pull"` - Push-pull converter
|
|
1989
|
+
- `"isolated_buck"` - Isolated buck
|
|
1990
|
+
- `"isolated_buck_boost"` - Isolated buck-boost
|
|
1991
|
+
- `"llc"` - LLC resonant converter
|
|
1992
|
+
- `"current_transformer"` - Current sensing transformer
|
|
1993
|
+
|
|
1994
|
+
**Example usage for different topologies:**
|
|
1995
|
+
```python
|
|
1996
|
+
# Buck converter
|
|
1997
|
+
inputs = PyOpenMagnetics.process_converter("buck", buck_specs, False)
|
|
1998
|
+
|
|
1999
|
+
# Boost converter
|
|
2000
|
+
inputs = PyOpenMagnetics.process_converter("boost", boost_specs, False)
|
|
2001
|
+
|
|
2002
|
+
# Forward converter
|
|
2003
|
+
inputs = PyOpenMagnetics.process_converter("single_switch_forward", forward_specs, False)
|
|
2004
|
+
|
|
2005
|
+
# LLC resonant
|
|
2006
|
+
inputs = PyOpenMagnetics.process_converter("llc", llc_specs, False)
|
|
2007
|
+
```
|
|
2008
|
+
|
|
2009
|
+
### 6. Database Loading
|
|
2010
|
+
|
|
2011
|
+
**Problem:** Empty database queries or "database empty" errors.
|
|
2012
|
+
|
|
2013
|
+
**Solution:** Databases are typically auto-loaded on import. If not, use:
|
|
2014
|
+
|
|
2015
|
+
```python
|
|
2016
|
+
# Check if databases are loaded
|
|
2017
|
+
if PyOpenMagnetics.is_core_material_database_empty():
|
|
2018
|
+
# Load databases (requires empty JSON config)
|
|
2019
|
+
PyOpenMagnetics.load_databases({})
|
|
2020
|
+
|
|
2021
|
+
# Verify
|
|
2022
|
+
print(f"Materials: {len(PyOpenMagnetics.get_core_materials())}")
|
|
2023
|
+
print(f"Shapes: {len(PyOpenMagnetics.get_core_shapes())}")
|
|
2024
|
+
print(f"Wires: {len(PyOpenMagnetics.get_wires())}")
|
|
2025
|
+
```
|
|
2026
|
+
|
|
2027
|
+
### 6. Function Argument Types
|
|
2028
|
+
|
|
2029
|
+
**Problem:** `TypeError: function(): incompatible function arguments`
|
|
2030
|
+
|
|
2031
|
+
**Cause:** PyOpenMagnetics uses pybind11 which requires exact argument types.
|
|
2032
|
+
|
|
2033
|
+
**Solution:** Use positional arguments instead of keyword arguments:
|
|
2034
|
+
|
|
2035
|
+
```python
|
|
2036
|
+
# For design_magnetics_from_converter():
|
|
2037
|
+
# WRONG (kwargs):
|
|
2038
|
+
result = PyOpenMagnetics.design_magnetics_from_converter(
|
|
2039
|
+
topology="flyback",
|
|
2040
|
+
converter=flyback_specs,
|
|
2041
|
+
max_results=5
|
|
2042
|
+
)
|
|
2043
|
+
|
|
2044
|
+
# CORRECT (positional):
|
|
2045
|
+
result = PyOpenMagnetics.design_magnetics_from_converter(
|
|
2046
|
+
"flyback", # topology_name
|
|
2047
|
+
flyback_specs, # converter_json
|
|
2048
|
+
5, # max_results
|
|
2049
|
+
"standard cores", # core_mode_json
|
|
2050
|
+
False, # use_ngspice
|
|
2051
|
+
None # weights_json
|
|
2052
|
+
)
|
|
2053
|
+
```
|
|
2054
|
+
|
|
2055
|
+
### 7. Waveform Data Format
|
|
2056
|
+
|
|
2057
|
+
**Problem:** Issues with operating point excitations
|
|
2058
|
+
|
|
2059
|
+
**Solution:** Waveforms must include both `data` (amplitude values) and `time` (timestamp values) arrays:
|
|
2060
|
+
|
|
2061
|
+
```python
|
|
2062
|
+
operating_point = {
|
|
2063
|
+
"name": "Nominal",
|
|
2064
|
+
"conditions": {"ambientTemperature": 40},
|
|
2065
|
+
"excitationsPerWinding": [
|
|
2066
|
+
{
|
|
2067
|
+
"name": "Primary",
|
|
2068
|
+
"frequency": 100000,
|
|
2069
|
+
"current": {
|
|
2070
|
+
"waveform": {
|
|
2071
|
+
"data": [0.2, 1.8, 1.8, 0.2], # Current in Amps
|
|
2072
|
+
"time": [0, 4.5e-6, 4.5e-6, 10e-6] # Time in seconds
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
]
|
|
2077
|
+
}
|
|
2078
|
+
```
|
|
2079
|
+
|
|
2080
|
+
### 8. Processing Inputs
|
|
2081
|
+
|
|
2082
|
+
**Best Practice:** Always use `process_inputs()` after `process_flyback()` to ensure harmonics are calculated:
|
|
2083
|
+
|
|
2084
|
+
```python
|
|
2085
|
+
# Step 1: Process flyback specs
|
|
2086
|
+
flyback_specs = {...}
|
|
2087
|
+
inputs = PyOpenMagnetics.process_flyback(flyback_specs)
|
|
2088
|
+
|
|
2089
|
+
# Step 2: Process inputs (calculates harmonics for loss analysis)
|
|
2090
|
+
processed = PyOpenMagnetics.process_inputs(inputs)
|
|
2091
|
+
|
|
2092
|
+
# Step 3: Get design recommendations
|
|
2093
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(
|
|
2094
|
+
processed,
|
|
2095
|
+
5,
|
|
2096
|
+
"standard cores"
|
|
2097
|
+
)
|
|
2098
|
+
```
|
|
2099
|
+
|
|
2100
|
+
### 9. Complete Working Example
|
|
2101
|
+
|
|
2102
|
+
```python
|
|
2103
|
+
import PyOpenMagnetics
|
|
2104
|
+
|
|
2105
|
+
# Define flyback converter specifications
|
|
2106
|
+
flyback_specs = {
|
|
2107
|
+
"inputVoltage": {"minimum": 185, "maximum": 265},
|
|
2108
|
+
"diodeVoltageDrop": 0.7,
|
|
2109
|
+
"currentRippleRatio": 0.3,
|
|
2110
|
+
"efficiency": 0.85,
|
|
2111
|
+
"desiredInductance": 500e-6,
|
|
2112
|
+
"desiredTurnsRatios": [15.0],
|
|
2113
|
+
"desiredDutyCycle": [[0.45]],
|
|
2114
|
+
"maximumDutyCycle": 0.45,
|
|
2115
|
+
"operatingPoints": [{
|
|
2116
|
+
"outputVoltages": [12.0],
|
|
2117
|
+
"outputCurrents": [2.0],
|
|
2118
|
+
"switchingFrequency": 100000,
|
|
2119
|
+
"ambientTemperature": 40
|
|
2120
|
+
}]
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
# Process flyback converter
|
|
2124
|
+
inputs = PyOpenMagnetics.process_flyback(flyback_specs)
|
|
2125
|
+
|
|
2126
|
+
# Get automatic design recommendations
|
|
2127
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(
|
|
2128
|
+
inputs,
|
|
2129
|
+
5, # max_results
|
|
2130
|
+
"standard cores" # core_mode (lowercase!)
|
|
2131
|
+
)
|
|
2132
|
+
|
|
2133
|
+
# Process results
|
|
2134
|
+
if isinstance(result, dict) and "data" in result:
|
|
2135
|
+
designs = result["data"]
|
|
2136
|
+
if isinstance(designs, list):
|
|
2137
|
+
print(f"Found {len(designs)} designs")
|
|
2138
|
+
for i, design in enumerate(designs):
|
|
2139
|
+
mas = design["mas"]
|
|
2140
|
+
score = design["scoring"]
|
|
2141
|
+
magnetic = mas["magnetic"]
|
|
2142
|
+
|
|
2143
|
+
core = magnetic["core"]["functionalDescription"]
|
|
2144
|
+
print(f"\nDesign #{i+1} (Score: {score:.2f}):")
|
|
2145
|
+
print(f" Core: {core['shape']['name']} / {core['material']['name']}")
|
|
2146
|
+
```
|
|
2147
|
+
|
|
2148
|
+
### 10. Debugging Tips
|
|
2149
|
+
|
|
2150
|
+
1. **Enable logging:**
|
|
2151
|
+
```python
|
|
2152
|
+
PyOpenMagnetics.set_log_level("DEBUG")
|
|
2153
|
+
```
|
|
2154
|
+
|
|
2155
|
+
2. **Check database status:**
|
|
2156
|
+
```python
|
|
2157
|
+
print(f"Materials empty: {PyOpenMagnetics.is_core_material_database_empty()}")
|
|
2158
|
+
print(f"Shapes empty: {PyOpenMagnetics.is_core_shape_database_empty()}")
|
|
2159
|
+
```
|
|
2160
|
+
|
|
2161
|
+
3. **Validate JSON structure:**
|
|
2162
|
+
```python
|
|
2163
|
+
import json
|
|
2164
|
+
|
|
2165
|
+
# Pretty-print inputs to verify structure
|
|
2166
|
+
print(json.dumps(inputs, indent=2, default=str))
|
|
2167
|
+
```
|
|
2168
|
+
|
|
2169
|
+
4. **Check function signatures:**
|
|
2170
|
+
```python
|
|
2171
|
+
# Use help() or check PyOpenMagnetics.pyi type stubs
|
|
2172
|
+
help(PyOpenMagnetics.calculate_advised_magnetics)
|
|
2173
|
+
```
|
|
2174
|
+
```
|
|
2175
|
+
## MAS Data Structures Reference
|
|
2176
|
+
|
|
2177
|
+
This section documents the Magnetic Agnostic Structure (MAS) JSON formats used by PyOpenMagnetics. Understanding these structures is essential for working with converter inputs and design outputs.
|
|
2178
|
+
|
|
2179
|
+
### 1. Inputs Structure
|
|
2180
|
+
|
|
2181
|
+
The `Inputs` structure is the main input to `calculate_advised_magnetics()`:
|
|
2182
|
+
|
|
2183
|
+
```python
|
|
2184
|
+
inputs = {
|
|
2185
|
+
"designRequirements": {
|
|
2186
|
+
"application": None, # Optional: application type
|
|
2187
|
+
"insulation": None, # Optional: insulation requirements
|
|
2188
|
+
"isolationSides": ["primary", "secondary"], # Isolation sides for windings
|
|
2189
|
+
"leakageInductance": None, # Optional: leakage inductance limits
|
|
2190
|
+
"magnetizingInductance": { # REQUIRED
|
|
2191
|
+
"nominal": 0.001, # Target inductance in Henries
|
|
2192
|
+
"minimum": None, # Optional: minimum acceptable
|
|
2193
|
+
"maximum": None, # Optional: maximum acceptable
|
|
2194
|
+
"excludeMinimum": None,
|
|
2195
|
+
"excludeMaximum": None
|
|
2196
|
+
},
|
|
2197
|
+
"market": None,
|
|
2198
|
+
"maximumDimensions": None, # Optional: size constraints
|
|
2199
|
+
"maximumWeight": None, # Optional: weight constraints
|
|
2200
|
+
"name": None,
|
|
2201
|
+
"operatingTemperature": None,
|
|
2202
|
+
"strayCapacitance": None,
|
|
2203
|
+
"subApplication": None,
|
|
2204
|
+
"terminalType": None,
|
|
2205
|
+
"topology": "Flyback Converter", # Auto-set by process_flyback()
|
|
2206
|
+
"turnsRatios": [ # REQUIRED for multi-winding
|
|
2207
|
+
{
|
|
2208
|
+
"nominal": 15.0, # Turns ratio (Np/Ns)
|
|
2209
|
+
"minimum": None,
|
|
2210
|
+
"maximum": None,
|
|
2211
|
+
"excludeMinimum": None,
|
|
2212
|
+
"excludeMaximum": None
|
|
2213
|
+
}
|
|
2214
|
+
],
|
|
2215
|
+
"wiringTechnology": None
|
|
2216
|
+
},
|
|
2217
|
+
"operatingPoints": [ # List of operating conditions
|
|
2218
|
+
{
|
|
2219
|
+
"conditions": {
|
|
2220
|
+
"ambientTemperature": 40.0, # °C
|
|
2221
|
+
"ambientRelativeHumidity": None,
|
|
2222
|
+
"cooling": None,
|
|
2223
|
+
"name": None
|
|
2224
|
+
},
|
|
2225
|
+
"excitationsPerWinding": [ # Current/voltage waveforms
|
|
2226
|
+
{
|
|
2227
|
+
"name": "Primary",
|
|
2228
|
+
"frequency": 100000, # Hz
|
|
2229
|
+
"current": {
|
|
2230
|
+
"harmonics": { # Auto-calculated by process_inputs()
|
|
2231
|
+
"amplitudes": [...],
|
|
2232
|
+
"frequencies": [...]
|
|
2233
|
+
}
|
|
2234
|
+
},
|
|
2235
|
+
"voltage": None # Optional
|
|
2236
|
+
}
|
|
2237
|
+
]
|
|
2238
|
+
}
|
|
2239
|
+
]
|
|
2240
|
+
}
|
|
2241
|
+
```
|
|
2242
|
+
|
|
2243
|
+
### 2. Design Results Structure
|
|
2244
|
+
|
|
2245
|
+
Output from `calculate_advised_magnetics()`:
|
|
2246
|
+
|
|
2247
|
+
```python
|
|
2248
|
+
result = {
|
|
2249
|
+
"data": [ # List of ranked designs
|
|
2250
|
+
{
|
|
2251
|
+
"mas": { # Complete MAS object
|
|
2252
|
+
"inputs": {...}, # Design requirements used
|
|
2253
|
+
"magnetic": { # The actual magnetic design
|
|
2254
|
+
"core": {...}, # Core specification
|
|
2255
|
+
"coil": {...} # Coil/winding specification
|
|
2256
|
+
},
|
|
2257
|
+
"outputs": { # Calculated after simulation
|
|
2258
|
+
"coreLosses": 0.5, # Watts
|
|
2259
|
+
"windingLosses": 1.2, # Watts
|
|
2260
|
+
"temperatureRise": 40 # Kelvin
|
|
2261
|
+
}
|
|
2262
|
+
},
|
|
2263
|
+
"scoring": 2.0, # Overall design score (higher is better)
|
|
2264
|
+
"scoringPerFilter": { # Individual filter scores
|
|
2265
|
+
"MagnetizingInductance": 1.0,
|
|
2266
|
+
"WindingWindow": 1.0,
|
|
2267
|
+
"Saturation": 1.0
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
]
|
|
2271
|
+
}
|
|
2272
|
+
```
|
|
2273
|
+
|
|
2274
|
+
### 3. Core Specification Structure
|
|
2275
|
+
|
|
2276
|
+
```python
|
|
2277
|
+
core = {
|
|
2278
|
+
"functionalDescription": {
|
|
2279
|
+
"type": "two-piece set", # "two-piece set", "toroidal", "closed shape"
|
|
2280
|
+
"shape": { # Core shape definition
|
|
2281
|
+
"name": "E 42/21/15", # Shape name
|
|
2282
|
+
"family": "e", # Shape family
|
|
2283
|
+
"dimensions": {...}, # Physical dimensions
|
|
2284
|
+
"magneticCircuit": "open"
|
|
2285
|
+
},
|
|
2286
|
+
"material": { # Core material
|
|
2287
|
+
"name": "3C95", # Material name
|
|
2288
|
+
"manufacturerInfo": {
|
|
2289
|
+
"name": "Ferroxcube"
|
|
2290
|
+
},
|
|
2291
|
+
"permeability": {...}, # Initial permeability vs temp/freq
|
|
2292
|
+
"saturation": [...], # B-H saturation curve
|
|
2293
|
+
"volumetricLosses": {...} # Core loss coefficients
|
|
2294
|
+
},
|
|
2295
|
+
"gapping": [ # Air gap specification
|
|
2296
|
+
{
|
|
2297
|
+
"type": "subtractive", # "subtractive", "additive", "distributed"
|
|
2298
|
+
"length": 0.001 # Gap length in meters
|
|
2299
|
+
}
|
|
2300
|
+
],
|
|
2301
|
+
"numberStacks": 1 # Number of stacked cores
|
|
2302
|
+
},
|
|
2303
|
+
"processedDescription": { # Auto-calculated by calculate_core_data()
|
|
2304
|
+
"effectiveParameters": {
|
|
2305
|
+
"effectiveArea": 1.57e-4, # Ae in m²
|
|
2306
|
+
"effectiveLength": 0.097, # le in meters
|
|
2307
|
+
"effectiveVolume": 1.52e-5 # Ve in m³
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
```
|
|
2312
|
+
|
|
2313
|
+
### 4. Coil/Winding Specification
|
|
2314
|
+
|
|
2315
|
+
```python
|
|
2316
|
+
coil = {
|
|
2317
|
+
"functionalDescription": [ # List of windings
|
|
2318
|
+
{
|
|
2319
|
+
"name": "Primary",
|
|
2320
|
+
"numberTurns": 45,
|
|
2321
|
+
"numberParallels": 1,
|
|
2322
|
+
"wire": { # Wire specification
|
|
2323
|
+
"name": "Round 0.4 - Grade 1", # or full wire dict
|
|
2324
|
+
"type": "round", # "round", "litz", "rectangular", "foil"
|
|
2325
|
+
"standard": "IEC 60317",
|
|
2326
|
+
"conductingDiameter": 0.0004,
|
|
2327
|
+
"outerDiameter": 0.000442
|
|
2328
|
+
},
|
|
2329
|
+
"isolationSide": "primary" # "primary", "secondary", "tertiary"
|
|
2330
|
+
},
|
|
2331
|
+
{
|
|
2332
|
+
"name": "Secondary",
|
|
2333
|
+
"numberTurns": 5,
|
|
2334
|
+
"numberParallels": 2,
|
|
2335
|
+
"wire": "Round 0.8 - Grade 1",
|
|
2336
|
+
"isolationSide": "secondary"
|
|
2337
|
+
}
|
|
2338
|
+
],
|
|
2339
|
+
"sectionsDescription": {...}, # Auto-generated by wind()
|
|
2340
|
+
"layersDescription": {...}, # Auto-generated by wind()
|
|
2341
|
+
"turnsDescription": {...} # Auto-generated by wind()
|
|
2342
|
+
}
|
|
2343
|
+
```
|
|
2344
|
+
|
|
2345
|
+
### 5. Operating Point with Waveforms
|
|
2346
|
+
|
|
2347
|
+
For manual waveform definition (not using process_flyback()):
|
|
2348
|
+
|
|
2349
|
+
```python
|
|
2350
|
+
operating_point = {
|
|
2351
|
+
"name": "Nominal",
|
|
2352
|
+
"conditions": {
|
|
2353
|
+
"ambientTemperature": 25, # °C
|
|
2354
|
+
"altitude": 0 # meters
|
|
2355
|
+
},
|
|
2356
|
+
"excitationsPerWinding": [
|
|
2357
|
+
{
|
|
2358
|
+
"name": "Primary",
|
|
2359
|
+
"frequency": 100000, # Hz
|
|
2360
|
+
"current": {
|
|
2361
|
+
"waveform": { # Time-domain waveform
|
|
2362
|
+
"data": [0.2, 1.8, 1.8, 0.2], # Current samples [A]
|
|
2363
|
+
"time": [0, 4.5e-6, 4.5e-6, 10e-6] # Time samples [s]
|
|
2364
|
+
}
|
|
2365
|
+
# OR use "harmonics" for frequency domain (auto-calculated)
|
|
2366
|
+
},
|
|
2367
|
+
"voltage": {
|
|
2368
|
+
"waveform": {
|
|
2369
|
+
"data": [311, 311, 0, 0], # Voltage samples [V]
|
|
2370
|
+
"time": [0, 4.5e-6, 4.5e-6, 10e-6]
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
]
|
|
2375
|
+
}
|
|
2376
|
+
```
|
|
2377
|
+
|
|
2378
|
+
**Note:** When using `process_flyback()`, the operating points are automatically generated from your converter specifications. The `process_inputs()` function then calculates harmonics from the waveforms for accurate loss analysis.
|
|
2379
|
+
|
|
2380
|
+
### 6. Wire Specification Formats
|
|
2381
|
+
|
|
2382
|
+
**By Name (Simplest):**
|
|
2383
|
+
```python
|
|
2384
|
+
wire = "Round 0.5 - Grade 1" # String name from database
|
|
2385
|
+
```
|
|
2386
|
+
|
|
2387
|
+
**By Full Specification:**
|
|
2388
|
+
```python
|
|
2389
|
+
wire = {
|
|
2390
|
+
"type": "round", # "round", "litz", "rectangular", "foil"
|
|
2391
|
+
"standard": "IEC 60317",
|
|
2392
|
+
"name": "Round 0.5 - Grade 1",
|
|
2393
|
+
"conductingDiameter": 0.0005, # Bare wire diameter (m)
|
|
2394
|
+
"outerDiameter": 0.000563, # With insulation (m)
|
|
2395
|
+
"material": "copper"
|
|
2396
|
+
}
|
|
2397
|
+
```
|
|
2398
|
+
|
|
2399
|
+
**Litz Wire:**
|
|
2400
|
+
```python
|
|
2401
|
+
wire = {
|
|
2402
|
+
"type": "litz",
|
|
2403
|
+
"name": "Litz 100x0.1",
|
|
2404
|
+
"numberConductors": 100,
|
|
2405
|
+
"outerDiameter": 0.0015,
|
|
2406
|
+
"strand": {
|
|
2407
|
+
"conductingDiameter": 0.0001,
|
|
2408
|
+
"outerDiameter": 0.00012
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
```
|
|
2412
|
+
|
|
2413
|
+
### 7. Accessing Design Results
|
|
2414
|
+
|
|
2415
|
+
```python
|
|
2416
|
+
# Get results from calculate_advised_magnetics()
|
|
2417
|
+
result = PyOpenMagnetics.calculate_advised_magnetics(inputs, 5, "standard cores")
|
|
2418
|
+
|
|
2419
|
+
if isinstance(result, dict) and "data" in result:
|
|
2420
|
+
designs = result["data"]
|
|
2421
|
+
|
|
2422
|
+
for i, design in enumerate(designs):
|
|
2423
|
+
mas = design["mas"]
|
|
2424
|
+
score = design["scoring"]
|
|
2425
|
+
magnetic = mas["magnetic"]
|
|
2426
|
+
|
|
2427
|
+
# Core information
|
|
2428
|
+
core = magnetic["core"]["functionalDescription"]
|
|
2429
|
+
print(f"Design {i+1}: Score={score:.2f}")
|
|
2430
|
+
print(f" Core: {core['shape']['name']} / {core['material']['name']}")
|
|
2431
|
+
print(f" Gap: {core['gapping'][0]['length']*1e3:.2f} mm")
|
|
2432
|
+
|
|
2433
|
+
# Winding information
|
|
2434
|
+
coil = magnetic["coil"]["functionalDescription"]
|
|
2435
|
+
for winding in coil:
|
|
2436
|
+
print(f" {winding['name']}: {winding['numberTurns']} turns")
|
|
2437
|
+
|
|
2438
|
+
# Losses (after simulation)
|
|
2439
|
+
if "outputs" in mas:
|
|
2440
|
+
outputs = mas["outputs"]
|
|
2441
|
+
print(f" Core Losses: {outputs.get('coreLosses', 0):.3f} W")
|
|
2442
|
+
print(f" Winding Losses: {outputs.get('windingLosses', 0):.3f} W")
|
|
2443
|
+
```
|