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.
Files changed (83) hide show
  1. pyopenmagnetics-1.3.0/AGENTS.md +2443 -0
  2. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/CMakeLists.txt +4 -1
  3. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/PKG-INFO +33 -1
  4. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/README.md +32 -0
  5. pyopenmagnetics-1.3.0/examples/complete_simulation_example.py +539 -0
  6. pyopenmagnetics-1.3.0/examples/converter_design_example.py +336 -0
  7. pyopenmagnetics-1.3.0/examples/debug_bobbin.py +47 -0
  8. pyopenmagnetics-1.3.0/examples/debug_coil.py +64 -0
  9. pyopenmagnetics-1.3.0/examples/debug_core.py +49 -0
  10. pyopenmagnetics-1.3.0/examples/debug_plotting.py +29 -0
  11. pyopenmagnetics-1.3.0/examples/flyback_220v_12v_1a.py +640 -0
  12. pyopenmagnetics-1.3.0/examples/flyback_220v_12v_2a_complete.py +588 -0
  13. pyopenmagnetics-1.3.0/examples/flyback_bh_curve.png +0 -0
  14. pyopenmagnetics-1.3.0/examples/flyback_core.png +0 -0
  15. pyopenmagnetics-1.3.0/examples/flyback_summary.png +0 -0
  16. pyopenmagnetics-1.3.0/examples/flyback_waveforms.png +0 -0
  17. pyopenmagnetics-1.3.0/examples/list_plot_funcs.py +14 -0
  18. pyopenmagnetics-1.3.0/examples/plot_flyback_design.py +486 -0
  19. pyopenmagnetics-1.3.0/examples/plot_flyback_pyom.py +238 -0
  20. pyopenmagnetics-1.3.0/examples/test_field_calc.py +76 -0
  21. pyopenmagnetics-1.3.0/examples/test_field_plot.py +57 -0
  22. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/pyproject.toml +6 -1
  23. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/.github/workflows/ci.yml +0 -0
  24. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/.github/workflows/publish.yml +0 -0
  25. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/.gitignore +0 -0
  26. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/LICENSE +0 -0
  27. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/PyOpenMagnetics.pyi +0 -0
  28. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/api/MAS.py +0 -0
  29. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/api/mas_db_reader.py +0 -0
  30. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/api/validation.py +0 -0
  31. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/clear_cibuildwheel_cache.sh +0 -0
  32. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/docs/compatibility.md +0 -0
  33. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/docs/errors.md +0 -0
  34. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/docs/performance.md +0 -0
  35. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/examples/README.md +0 -0
  36. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/examples/buck_inductor.py +0 -0
  37. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/examples/flyback_design.py +0 -0
  38. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/force_fresh_build.sh +0 -0
  39. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/llms.txt +0 -0
  40. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/01_getting_started.ipynb +0 -0
  41. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/02_buck_inductor.ipynb +0 -0
  42. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/03_core_losses.ipynb +0 -0
  43. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/notebooks/README.md +0 -0
  44. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/requirements.txt +0 -0
  45. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/advisers.cpp +0 -0
  46. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/advisers.h +0 -0
  47. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/bobbin.cpp +0 -0
  48. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/bobbin.h +0 -0
  49. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/common.h +0 -0
  50. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/converter.cpp +0 -0
  51. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/converter.h +0 -0
  52. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/core.cpp +0 -0
  53. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/core.h +0 -0
  54. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/database.cpp +0 -0
  55. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/database.h +0 -0
  56. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/logging.cpp +0 -0
  57. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/logging.h +0 -0
  58. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/losses.cpp +0 -0
  59. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/losses.h +0 -0
  60. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/module.cpp +0 -0
  61. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/plotting.cpp +0 -0
  62. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/plotting.h +0 -0
  63. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/settings.cpp +0 -0
  64. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/settings.h +0 -0
  65. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/simulation.cpp +0 -0
  66. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/simulation.h +0 -0
  67. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/utils.cpp +0 -0
  68. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/utils.h +0 -0
  69. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/winding.cpp +0 -0
  70. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/winding.h +0 -0
  71. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/wire.cpp +0 -0
  72. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/src/wire.h +0 -0
  73. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/test.py +0 -0
  74. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/__init__.py +0 -0
  75. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/conftest.py +0 -0
  76. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_converter_endpoints.py +0 -0
  77. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_core.py +0 -0
  78. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_core_adviser.py +0 -0
  79. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_examples_integration.py +0 -0
  80. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_inputs.py +0 -0
  81. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_logging.py +0 -0
  82. {pyopenmagnetics-1.2.2 → pyopenmagnetics-1.3.0}/tests/test_magnetic_adviser.py +0 -0
  83. {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
+ ```