PyOpenMagnetics 1.3.13__tar.gz → 1.4.1__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 (88) hide show
  1. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/.github/workflows/publish.yml +34 -15
  2. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/AGENTS.md +77 -16
  3. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/CMakeLists.txt +17 -7
  4. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/PKG-INFO +24 -1
  5. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/README.md +23 -0
  6. pyopenmagnetics-1.4.1/__init__.py +4 -0
  7. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/llms.txt +35 -0
  8. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/pyproject.toml +6 -3
  9. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/advisers.cpp +106 -0
  10. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/bobbin.cpp +81 -0
  11. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/bobbin.h +5 -0
  12. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/common.h +1 -0
  13. pyopenmagnetics-1.4.1/src/converter.cpp +4199 -0
  14. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/core.cpp +240 -7
  15. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/core.h +10 -0
  16. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/database.cpp +80 -0
  17. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/database.h +5 -0
  18. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/losses.cpp +110 -3
  19. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/losses.h +5 -0
  20. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/plotting.cpp +250 -12
  21. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/plotting.h +5 -0
  22. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/settings.cpp +110 -2
  23. pyopenmagnetics-1.4.1/src/settings.h +25 -0
  24. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/simulation.cpp +404 -3
  25. pyopenmagnetics-1.4.1/src/simulation.h +57 -0
  26. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/utils.cpp +289 -3
  27. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/utils.h +12 -0
  28. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/winding.cpp +98 -3
  29. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/winding.h +5 -0
  30. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/wire.cpp +60 -1
  31. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/wire.h +4 -0
  32. pyopenmagnetics-1.3.13/src/converter.cpp +0 -1460
  33. pyopenmagnetics-1.3.13/src/settings.h +0 -17
  34. pyopenmagnetics-1.3.13/src/simulation.h +0 -37
  35. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/.github/workflows/ci.yml +0 -0
  36. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/.gitignore +0 -0
  37. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/LICENSE +0 -0
  38. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/PyOpenMagnetics.pyi +0 -0
  39. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/api/MAS.py +0 -0
  40. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/api/mas_db_reader.py +0 -0
  41. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/api/validation.py +0 -0
  42. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/clear_cibuildwheel_cache.sh +0 -0
  43. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/docs/compatibility.md +0 -0
  44. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/docs/errors.md +0 -0
  45. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/docs/performance.md +0 -0
  46. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/README.md +0 -0
  47. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/buck_inductor.py +0 -0
  48. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/complete_simulation_example.py +0 -0
  49. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/converter_design_example.py +0 -0
  50. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/debug_bobbin.py +0 -0
  51. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/debug_coil.py +0 -0
  52. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/debug_core.py +0 -0
  53. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/debug_plotting.py +0 -0
  54. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/flyback_220v_12v_1a.py +0 -0
  55. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/flyback_220v_12v_2a_complete.py +0 -0
  56. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/flyback_bh_curve.png +0 -0
  57. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/flyback_core.png +0 -0
  58. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/flyback_design.py +0 -0
  59. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/flyback_summary.png +0 -0
  60. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/flyback_waveforms.png +0 -0
  61. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/list_plot_funcs.py +0 -0
  62. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/plot_flyback_design.py +0 -0
  63. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/plot_flyback_pyom.py +0 -0
  64. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/test_field_calc.py +0 -0
  65. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/examples/test_field_plot.py +0 -0
  66. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/force_fresh_build.sh +0 -0
  67. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/notebooks/01_getting_started.ipynb +0 -0
  68. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/notebooks/02_buck_inductor.ipynb +0 -0
  69. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/notebooks/03_core_losses.ipynb +0 -0
  70. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/notebooks/README.md +0 -0
  71. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/requirements.txt +0 -0
  72. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/advisers.h +0 -0
  73. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/converter.h +0 -0
  74. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/logging.cpp +0 -0
  75. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/logging.h +0 -0
  76. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/src/module.cpp +0 -0
  77. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/test.py +0 -0
  78. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/__init__.py +0 -0
  79. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/conftest.py +0 -0
  80. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_converter_endpoints.py +0 -0
  81. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_core.py +0 -0
  82. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_core_adviser.py +0 -0
  83. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_examples_integration.py +0 -0
  84. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_inputs.py +0 -0
  85. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_logging.py +0 -0
  86. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_magnetic_adviser.py +0 -0
  87. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_plotting.py +0 -0
  88. {pyopenmagnetics-1.3.13 → pyopenmagnetics-1.4.1}/tests/test_winding.py +0 -0
@@ -22,15 +22,24 @@ env:
22
22
  jobs:
23
23
  # Build and publish Linux wheels independently
24
24
  build_and_publish_linux:
25
- name: Build and Publish Linux wheels
25
+ name: Build and Publish Linux wheels (${{ matrix.python }})
26
26
  runs-on: ubuntu-22.04
27
+ # Each matrix job builds a single CPython version. Building all four
28
+ # (cp310-cp313) sequentially in one job exceeded the runner's window and
29
+ # was killed mid-compile (exit 143). One version per parallel job keeps
30
+ # each job short and well under any timeout.
31
+ timeout-minutes: 120
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ python: [cp310, cp311, cp312, cp313]
27
36
  environment:
28
37
  name: pypi
29
38
  url: https://pypi.org/p/pyopenmagnetics
30
39
  permissions:
31
40
  id-token: write # Required for trusted publishing
32
41
  if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
33
-
42
+
34
43
  steps:
35
44
  - uses: actions/checkout@v4
36
45
  with:
@@ -40,9 +49,9 @@ jobs:
40
49
  uses: actions/cache@v4
41
50
  with:
42
51
  path: ~/.cache/cibuildwheel
43
- key: cibuildwheel-linux-${{ hashFiles('**/pyproject.toml') }}
52
+ key: cibuildwheel-linux-${{ matrix.python }}-${{ hashFiles('**/pyproject.toml') }}
44
53
  restore-keys: |
45
- cibuildwheel-linux-
54
+ cibuildwheel-linux-${{ matrix.python }}-
46
55
 
47
56
  - name: Cache npm
48
57
  uses: actions/cache@v4
@@ -101,7 +110,7 @@ jobs:
101
110
  - name: Build wheels
102
111
  run: python -m cibuildwheel --output-dir wheelhouse
103
112
  env:
104
- CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
113
+ CIBW_BUILD: ${{ matrix.python }}-*
105
114
  CIBW_SKIP: "*-win32 *-manylinux_i686 *musllinux* pp*"
106
115
  CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
107
116
  CIBW_BEFORE_ALL_LINUX: |
@@ -134,15 +143,20 @@ jobs:
134
143
 
135
144
  # Build and publish Windows wheels independently
136
145
  build_and_publish_windows:
137
- name: Build and Publish Windows wheels
146
+ name: Build and Publish Windows wheels (${{ matrix.python }})
138
147
  runs-on: windows-2022
148
+ timeout-minutes: 120
149
+ strategy:
150
+ fail-fast: false
151
+ matrix:
152
+ python: [cp310, cp311, cp312, cp313]
139
153
  environment:
140
154
  name: pypi
141
155
  url: https://pypi.org/p/pyopenmagnetics
142
156
  permissions:
143
157
  id-token: write # Required for trusted publishing
144
158
  if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
145
-
159
+
146
160
  steps:
147
161
  - uses: actions/checkout@v4
148
162
  with:
@@ -152,9 +166,9 @@ jobs:
152
166
  uses: actions/cache@v4
153
167
  with:
154
168
  path: C:\Users\runneradmin\AppData\Local\pypa\cibuildwheel\Cache
155
- key: cibuildwheel-windows-${{ hashFiles('**/pyproject.toml') }}
169
+ key: cibuildwheel-windows-${{ matrix.python }}-${{ hashFiles('**/pyproject.toml') }}
156
170
  restore-keys: |
157
- cibuildwheel-windows-
171
+ cibuildwheel-windows-${{ matrix.python }}-
158
172
 
159
173
  - name: Cache npm
160
174
  uses: actions/cache@v4
@@ -183,7 +197,7 @@ jobs:
183
197
  - name: Build wheels
184
198
  run: python -m cibuildwheel --output-dir wheelhouse
185
199
  env:
186
- CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
200
+ CIBW_BUILD: ${{ matrix.python }}-*
187
201
  CIBW_SKIP: "*-win32 *-manylinux_i686 *musllinux* pp*"
188
202
  CIBW_BEFORE_ALL_WINDOWS: npm install -g quicktype
189
203
  CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core "cmake<4" ninja pybind11
@@ -208,16 +222,21 @@ jobs:
208
222
 
209
223
  # Build and publish macOS wheels independently
210
224
  build_and_publish_macos:
211
- name: Build and Publish macOS wheels
225
+ name: Build and Publish macOS wheels (${{ matrix.python }})
212
226
  runs-on: macos-15
227
+ timeout-minutes: 120
213
228
  continue-on-error: true # Allow macOS to fail without blocking
229
+ strategy:
230
+ fail-fast: false
231
+ matrix:
232
+ python: [cp310, cp311, cp312, cp313]
214
233
  environment:
215
234
  name: pypi
216
235
  url: https://pypi.org/p/pyopenmagnetics
217
236
  permissions:
218
237
  id-token: write # Required for trusted publishing
219
238
  if: (github.event_name == 'release' || github.event_name == 'workflow_dispatch') && (github.event_name != 'workflow_dispatch' || !inputs.skip_macos)
220
-
239
+
221
240
  steps:
222
241
  - uses: actions/checkout@v4
223
242
  with:
@@ -227,9 +246,9 @@ jobs:
227
246
  uses: actions/cache@v4
228
247
  with:
229
248
  path: ~/Library/Caches/cibuildwheel
230
- key: cibuildwheel-macos-${{ hashFiles('**/pyproject.toml') }}
249
+ key: cibuildwheel-macos-${{ matrix.python }}-${{ hashFiles('**/pyproject.toml') }}
231
250
  restore-keys: |
232
- cibuildwheel-macos-
251
+ cibuildwheel-macos-${{ matrix.python }}-
233
252
 
234
253
  - name: Cache npm
235
254
  uses: actions/cache@v4
@@ -263,7 +282,7 @@ jobs:
263
282
  - name: Build wheels
264
283
  run: python -m cibuildwheel --output-dir wheelhouse
265
284
  env:
266
- CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
285
+ CIBW_BUILD: ${{ matrix.python }}-*
267
286
  CIBW_SKIP: "*-win32 *-manylinux_i686 *musllinux* pp*"
268
287
  CIBW_BEFORE_ALL_MACOS: |
269
288
  for i in {1..5}; do
@@ -1,5 +1,18 @@
1
1
  # AGENTS.md — PyOpenMagnetics AI Assistant Guide
2
2
 
3
+ > **MKF is authoritative for all magnetics math.** Before implementing any
4
+ > magnetics calculation or assuming an API exists, consult
5
+ > [`../MKF/CAPABILITIES.md`](../MKF/CAPABILITIES.md). If a needed capability
6
+ > isn't listed there, ask — don't reinvent it here.
7
+
8
+ > **Two stub files in this repo:**
9
+ > - `PyOpenMagnetics.pyi` — **curated**. Authoritative on arg conventions
10
+ > and gotchas. Read this first.
11
+ > - `PyOpenMagnetics.generated.pyi` — **auto-generated** from the compiled
12
+ > `.so` via pybind11-stubgen. Wider coverage (every exported symbol) but
13
+ > raw — signatures are right, conventions may not be. Use as a discovery
14
+ > index; when it disagrees with the curated stub, trust the curated one.
15
+
3
16
  > **This file is the SINGLE SOURCE OF TRUTH for any AI agent using PyOpenMagnetics.**
4
17
  > Every instruction was derived from reading the C++ source code
5
18
  > (`converter.cpp`, `magnetics.cpp`, `cores.cpp`, `wires.cpp`, `settings.cpp`,
@@ -615,26 +628,74 @@ subcircuit = PyOM.export_magnetic_as_subcircuit(magnetic)
615
628
 
616
629
  ## 11. Supported Topologies
617
630
 
618
- | Topology string | Method A class | Singular/Plural | Notes |
619
- |---|---|---|---|
620
- | `"flyback"` | `Flyback` | PLURAL + **mode required** | Most common |
621
- | `"buck"` | `Buck` | **SINGULAR** | |
622
- | `"boost"` | `Boost` | **SINGULAR** | |
623
- | `"single_switch_forward"` | `SingleSwitchForward` | PLURAL | switchingFreq REQUIRED |
624
- | `"two_switch_forward"` | `TwoSwitchForward` | PLURAL | switchingFreq REQUIRED |
625
- | `"active_clamp_forward"` | `ActiveClampForward` | PLURAL | switchingFreq REQUIRED |
626
- | `"push_pull"` | `PushPull` | PLURAL | Same as Forward |
627
- | `"llc"` | `Llc` | PLURAL | |
628
- | `"isolated_buck"` | `IsolatedBuck` | PLURAL | |
629
- | `"isolated_buck_boost"` | `IsolatedBuckBoost` | PLURAL | |
630
- | Others (`"cllc"`, `"dab"`, `"psfb"`, `"pshb"`) | internal fallback | varies | |
631
-
632
- Per-topology thin wrappers for `process_converter`:
631
+ `process_converter("<topology>", json, use_ngspice)` is the universal entry
632
+ point and accepts every string below, plus its `"advanced_<topology>"` form
633
+ (the internal builds the `Advanced*` MKF class either way "advanced" is
634
+ selected by the JSON payload shape, e.g. presence of `desiredInductance` /
635
+ `desiredMagnetizingInductance` / `desiredBoostInductance`, not by the name).
636
+
637
+ ### 11.1 Per-topology API parity matrix
638
+
639
+ Every topology exposes the **same four surfaces** (✓ = present). Columns:
640
+ - **inputs** `calculate_<t>_inputs(json)` (basic) and
641
+ `calculate_advanced_<t>_inputs(json)` (advanced)
642
+ - **process** dedicated `process_<t>(json)` thin wrapper (all are also
643
+ reachable via the generic `process_converter`)
644
+ - **simulate** — `simulate_<t>_ideal_waveforms(json)` (ngspice)
645
+ - **ngspice** `generate_<t>_ngspice_circuit(json, input_voltage_index=0, operating_point_index=0)`
646
+
647
+ | Topology string | inputs (basic / advanced) | process_* | simulate | generate ngspice |
648
+ |---|---|---|---|---|
649
+ | `flyback` | ✓ / ✓ | ✓ | ✓ (+`simulate_flyback_with_magnetic`) | ✓ |
650
+ | `buck` | ✓ / ✓ | ✓ | ✓ | ✓ |
651
+ | `boost` | ✓ / ✓ | ✓ | ✓ | ✓ |
652
+ | `single_switch_forward` | ✓ / ✓ | ✓ | ✓ (`simulate_forward_*`) | ✓ (`generate_forward_*`) |
653
+ | `two_switch_forward` | ✓ / ✓ | ✓ | ✓ | ✓ |
654
+ | `active_clamp_forward` | ✓ / ✓ | ✓ | ✓ | ✓ |
655
+ | `push_pull` | ✓ / ✓ | ✓ | ✓ | ✓ |
656
+ | `isolated_buck` | ✓ / ✓ | ✓ | ✓ | ✓ |
657
+ | `isolated_buck_boost` | ✓ / ✓ | ✓ | ✓ | ✓ |
658
+ | `cuk` | ✓ / ✓ | ✓ | ✓ | ✓ |
659
+ | `sepic` | ✓ / ✓ | ✓ | ✓ | ✓ |
660
+ | `zeta` | ✓ / ✓ | ✓ | ✓ | ✓ |
661
+ | `four_switch_buck_boost` | ✓ / ✓ | ✓ | ✓ | ✓ |
662
+ | `weinberg` | ✓ / ✓ | ✓ | ✓ | ✓ |
663
+ | `llc` | ✓ / ✓ | via `process_converter` | ✓ | ✓ |
664
+ | `cllc` | ✓ / ✓ | via `process_converter` | ✓ | ✓ |
665
+ | `clllc` | ✓ / ✓ | ✓ | ✓ | ✓ |
666
+ | `src` | ✓ / ✓ | ✓ | ✓ | ✓ |
667
+ | `dab` | ✓ / ✓ | via `process_converter` | ✓ | ✓ |
668
+ | `phase_shifted_full_bridge` (`psfb`) | ✓ / ✓ | via `process_converter` | ✓ | ✓ |
669
+ | `phase_shifted_half_bridge` (`pshb`) | ✓ / ✓ | via `process_converter` | ✓ | ✓ |
670
+ | `asymmetric_half_bridge` (`ahb`) | ✓ / ✓ | ✓ | ✓ | ✓ |
671
+ | `vienna` | ✓ / ✓ | ✓ | ✓ | ✓ |
672
+ | `power_factor_correction` (`pfc`) | ✓ / — ¹ | via `process_converter` | ✓ (`simulate_pfc_waveforms`) | ✓ ² |
673
+ | `current_transformer` | — ³ | ✓ ³ | — | — |
674
+ | `common_mode_choke` (`cmc`) | `calculate_cmc_inputs` / `calculate_advanced_cmc_inputs` | — | `simulate_cmc_ideal_waveforms`, `simulate_cmc_lisn_waveforms` | `generate_cmc_ngspice_circuit` |
675
+ | `differential_mode_choke` (`dmc`) | `calculate_dmc_inputs` | `propose_dmc_design`, `verify_dmc_attenuation` | `simulate_dmc_waveforms` | `generate_dmc_ngspice_circuit` |
676
+
677
+ ¹ PFC has no `AdvancedPowerFactorCorrection` class in MKF — basic-only by design;
678
+ there is intentionally no `calculate_advanced_pfc_inputs`.
679
+ ² `generate_pfc_ngspice_circuit(json, dc_resistance=0.1, simulation_time=0.02,
680
+ time_step=1e-8)` — PFC is a line-frequency model, so it takes circuit-damping
681
+ parameters instead of the `input_voltage_index` / `operating_point_index`
682
+ sweep indices used by the DC-DC family. Inductance comes from an explicit
683
+ `"inductance"` field or is derived from `"mode"` (CCM/CrCM/DCM).
684
+ ³ Current transformer is measurement-only: `process_current_transformer(json,
685
+ turns_ratio, secondary_resistance=0.0)`.
686
+
687
+ ### 11.2 Dedicated `process_*` thin wrappers
688
+
633
689
  `process_flyback()`, `process_buck()`, `process_boost()`,
634
690
  `process_single_switch_forward()`, `process_two_switch_forward()`,
635
691
  `process_active_clamp_forward()`, `process_push_pull()`,
636
692
  `process_isolated_buck()`, `process_isolated_buck_boost()`,
637
- `process_current_transformer(json, turns_ratio, secondary_resistance=0.0)`
693
+ `process_cuk()`, `process_sepic()`, `process_zeta()`,
694
+ `process_four_switch_buck_boost()`, `process_asymmetric_half_bridge()`,
695
+ `process_weinberg()`, `process_vienna()`, `process_clllc()`, `process_src()`,
696
+ `process_current_transformer(json, turns_ratio, secondary_resistance=0.0)`.
697
+ Topologies without a dedicated wrapper (`llc`, `cllc`, `dab`, `psfb`, `pshb`,
698
+ `pfc`) are reached through `process_converter("<topology>", json)`.
638
699
 
639
700
  ---
640
701
 
@@ -24,16 +24,21 @@ else ()
24
24
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-deprecated-declarations -Wno-unused-parameter -Wno-switch")
25
25
  set(CMAKE_POSITION_INDEPENDENT_CODE ON)
26
26
 
27
- # set(CMAKE_BUILD_TYPE RelWithDebInfo)
28
27
  # set(CMAKE_BUILD_TYPE MinSizeRel)
29
- set(CMAKE_BUILD_TYPE Release)
30
-
28
+ # set(CMAKE_BUILD_TYPE Release)
29
+ set(CMAKE_BUILD_TYPE RelWithDebInfo)
30
+
31
31
  # Enable Link Time Optimization to reduce binary size
32
32
  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) # TEMP: disable LTO (OOM)
33
-
34
- # Strip symbols and use size optimization
35
- set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s -Os")
36
- set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--strip-all")
33
+
34
+ # Wheel/CI build: -O1 keeps compilation fast and low-memory. -g is
35
+ # intentionally dropped — full debug info on the large pybind11 + json
36
+ # translation units (converter.cpp is ~6k lines) balloons RAM during
37
+ # compile and OOM-kills CI runners (exit 143). Frame pointers are kept
38
+ # for usable backtraces. We REPLACE (not append) the flags so the -g in
39
+ # the default RelWithDebInfo flags is stripped too. For local debugging
40
+ # with symbols, re-add -g here.
41
+ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O1 -DNDEBUG -fno-omit-frame-pointer")
37
42
  endif()
38
43
 
39
44
  SET(MAS_DIRECTORY "${CMAKE_BINARY_DIR}/MAS/")
@@ -384,5 +389,10 @@ include_directories("src/")
384
389
 
385
390
  install(TARGETS PyOpenMagnetics LIBRARY DESTINATION .)
386
391
 
392
+ # Install the package __init__.py so `import PyOpenMagnetics` exposes the
393
+ # compiled extension's API. Without it the wheel installs as an empty PEP-420
394
+ # namespace package and `PyOpenMagnetics.<fn>` resolves to nothing.
395
+ install(FILES __init__.py DESTINATION .)
396
+
387
397
  # Install documentation files for AI assistants
388
398
  install(FILES AGENTS.md llms.txt PyOpenMagnetics.pyi DESTINATION .)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PyOpenMagnetics
3
- Version: 1.3.13
3
+ Version: 1.4.1
4
4
  Summary: Python wrapper for OpenMagnetics
5
5
  Author-Email: Alfonso Martinez <Alfonso_VII@hotmail.com>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -314,6 +314,29 @@ magnetics = get_advised_magnetics(inputs, max_results=5)
314
314
  |----------|-------------|
315
315
  | `export_magnetic_as_subcircuit(magnetic, ...)` | Export as SPICE model |
316
316
 
317
+ ### Converter Topologies
318
+
319
+ All 24 power topologies are exposed with a uniform API. Use the generic
320
+ `process_converter("<topology>", converter, use_ngspice)` (also accepts
321
+ `"advanced_<topology>"`), or the per-topology functions below.
322
+
323
+ | Function family | Description |
324
+ |----------|-------------|
325
+ | `process_converter(name, json, use_ngspice=True)` | Universal dispatch for every topology |
326
+ | `design_magnetics_from_converter(name, json, max_results, core_mode, ...)` | Converter → advised magnetic designs (single call) |
327
+ | `calculate_<t>_inputs(json)` | Build MAS inputs (basic mode) for topology `<t>` |
328
+ | `calculate_advanced_<t>_inputs(json)` | Build MAS inputs (advanced mode) |
329
+ | `simulate_<t>_ideal_waveforms(json)` | ngspice ideal-waveform simulation |
330
+ | `generate_<t>_ngspice_circuit(json, input_voltage_index=0, operating_point_index=0)` | Generate ngspice netlist |
331
+
332
+ `<t>` ∈ `flyback, buck, boost, single_switch_forward, two_switch_forward,
333
+ active_clamp_forward, push_pull, isolated_buck, isolated_buck_boost, cuk,
334
+ sepic, zeta, four_switch_buck_boost, weinberg, llc, cllc, clllc, src, dab,
335
+ psfb, pshb, ahb, vienna`. PFC is basic-only (`calculate_pfc_inputs`,
336
+ `generate_pfc_ngspice_circuit(json, dc_resistance=0.1, simulation_time=0.02,
337
+ time_step=1e-8)`); common-/differential-mode chokes use the `cmc` / `dmc`
338
+ families. See `AGENTS.md` §11 for the full per-topology parity matrix.
339
+
317
340
  ## Core Materials
318
341
 
319
342
  PyOpenMagnetics includes materials from major manufacturers:
@@ -297,6 +297,29 @@ magnetics = get_advised_magnetics(inputs, max_results=5)
297
297
  |----------|-------------|
298
298
  | `export_magnetic_as_subcircuit(magnetic, ...)` | Export as SPICE model |
299
299
 
300
+ ### Converter Topologies
301
+
302
+ All 24 power topologies are exposed with a uniform API. Use the generic
303
+ `process_converter("<topology>", converter, use_ngspice)` (also accepts
304
+ `"advanced_<topology>"`), or the per-topology functions below.
305
+
306
+ | Function family | Description |
307
+ |----------|-------------|
308
+ | `process_converter(name, json, use_ngspice=True)` | Universal dispatch for every topology |
309
+ | `design_magnetics_from_converter(name, json, max_results, core_mode, ...)` | Converter → advised magnetic designs (single call) |
310
+ | `calculate_<t>_inputs(json)` | Build MAS inputs (basic mode) for topology `<t>` |
311
+ | `calculate_advanced_<t>_inputs(json)` | Build MAS inputs (advanced mode) |
312
+ | `simulate_<t>_ideal_waveforms(json)` | ngspice ideal-waveform simulation |
313
+ | `generate_<t>_ngspice_circuit(json, input_voltage_index=0, operating_point_index=0)` | Generate ngspice netlist |
314
+
315
+ `<t>` ∈ `flyback, buck, boost, single_switch_forward, two_switch_forward,
316
+ active_clamp_forward, push_pull, isolated_buck, isolated_buck_boost, cuk,
317
+ sepic, zeta, four_switch_buck_boost, weinberg, llc, cllc, clllc, src, dab,
318
+ psfb, pshb, ahb, vienna`. PFC is basic-only (`calculate_pfc_inputs`,
319
+ `generate_pfc_ngspice_circuit(json, dc_resistance=0.1, simulation_time=0.02,
320
+ time_step=1e-8)`); common-/differential-mode chokes use the `cmc` / `dmc`
321
+ families. See `AGENTS.md` §11 for the full per-topology parity matrix.
322
+
300
323
  ## Core Materials
301
324
 
302
325
  PyOpenMagnetics includes materials from major manufacturers:
@@ -0,0 +1,4 @@
1
+ from .PyOpenMagnetics import * # noqa: F401,F403
2
+ from . import PyOpenMagnetics as _ext # noqa: F401
3
+
4
+ __all__ = [name for name in dir(_ext) if not name.startswith("_")]
@@ -763,6 +763,41 @@ inputs = {
763
763
  processed = PyOpenMagnetics.process_inputs(inputs)
764
764
  ```
765
765
 
766
+ ### Low-Level Per-Topology API (uniform across all topologies)
767
+
768
+ Beyond the high-level `process_converter` / `design_magnetics_from_converter`
769
+ entry points, every topology exposes the **same four function families**, so
770
+ any topology can be driven the same way:
771
+
772
+ ```python
773
+ # 1. Input builders — basic and advanced modes (advanced = JSON carries a
774
+ # desired* inductance/turns-ratio field; both reach the same MKF engine):
775
+ inputs = PyOpenMagnetics.calculate_dab_inputs(converter)
776
+ inputs_adv = PyOpenMagnetics.calculate_advanced_dab_inputs(converter)
777
+
778
+ # 2. Generic dispatch (accepts "<topology>" and "advanced_<topology>"):
779
+ inputs = PyOpenMagnetics.process_converter("vienna", converter, True)
780
+
781
+ # 3. Ideal-waveform SPICE simulation:
782
+ waveforms = PyOpenMagnetics.simulate_vienna_ideal_waveforms(converter)
783
+
784
+ # 4. ngspice netlist generation:
785
+ netlist = PyOpenMagnetics.generate_vienna_ngspice_circuit(converter, 0, 0)
786
+ ```
787
+
788
+ Coverage is uniform for: `flyback, buck, boost, single_switch_forward,
789
+ two_switch_forward, active_clamp_forward, push_pull, isolated_buck,
790
+ isolated_buck_boost, cuk, sepic, zeta, four_switch_buck_boost, weinberg, llc,
791
+ cllc, clllc, src, dab, psfb, pshb, asymmetric_half_bridge (ahb), vienna`.
792
+ Both `calculate_<t>_inputs` and `calculate_advanced_<t>_inputs` exist for each
793
+ of these. Exceptions: **PFC** has no advanced variant
794
+ (`calculate_pfc_inputs` only; `generate_pfc_ngspice_circuit(json,
795
+ dc_resistance=0.1, simulation_time=0.02, time_step=1e-8)` uses a
796
+ line-frequency model with no sweep indices), and **current_transformer** is
797
+ measurement-only (`process_current_transformer`). Common- and
798
+ differential-mode chokes have their own `calculate_cmc_inputs` /
799
+ `calculate_advanced_cmc_inputs` / `calculate_dmc_inputs` families.
800
+
766
801
  ## Related Projects
767
802
  - **MKF**: C++ magnetics engine - github.com/OpenMagnetics/MKF
768
803
  - **MAS**: Magnetic Agnostic Structure JSON schema - github.com/OpenMagnetics/MAS
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "PyOpenMagnetics"
7
- version = "1.3.13"
7
+ version = "1.4.1"
8
8
  requires-python = ">=3.8"
9
9
  authors = [
10
10
  { name="Alfonso Martinez", email="Alfonso_VII@hotmail.com" },
@@ -33,7 +33,7 @@ sdist.exclude = [".venv*", ".venv*/**", "build", "build/**", "dist", "dist/**",
33
33
  wheel.packages = ["."]
34
34
  wheel.install-dir = "PyOpenMagnetics"
35
35
  # Ensure AGENTS.md and llms.txt are included in sdist and wheel
36
- sdist.include = ["AGENTS.md", "llms.txt", "PyOpenMagnetics.pyi"]
36
+ sdist.include = ["__init__.py", "AGENTS.md", "llms.txt", "PyOpenMagnetics.pyi"]
37
37
 
38
38
  [[tool.scikit-build.overrides]]
39
39
  if.platform-system = "^linux"
@@ -62,7 +62,10 @@ before-all = [
62
62
  "npm --version",
63
63
  "npm install -g quicktype@23.0.170"
64
64
  ]
65
- environment = { GIT_LFS_SKIP_SMUDGE=1, CMAKE_BUILD_PARALLEL_LEVEL="3" }
65
+ # Parallel level 2 (not 3): two large pybind11 TUs compiling at once stays
66
+ # within the runner's RAM. Combined with dropping -g (see CMakeLists.txt),
67
+ # this avoids the OOM that recycled the runner mid-compile (exit 143).
68
+ environment = { GIT_LFS_SKIP_SMUDGE=1, CMAKE_BUILD_PARALLEL_LEVEL="2" }
66
69
 
67
70
  [tool.cibuildwheel.macos]
68
71
  before-all = "npm --version && npm install -g quicktype@23.0.170"
@@ -236,6 +236,98 @@ json calculate_advised_magnetics_from_cache(json inputsJson, json filterFlowJson
236
236
  }
237
237
  }
238
238
 
239
+ json calculate_advised_sections(json masJson, json patternJson, int repetitions) {
240
+ try {
241
+ OpenMagnetics::Mas mas(masJson);
242
+ std::vector<size_t> pattern;
243
+ for (auto& elem : patternJson) {
244
+ pattern.push_back(elem);
245
+ }
246
+ auto bobbin = mas.get_magnetic().get_coil().get_bobbin();
247
+ if (std::holds_alternative<std::string>(bobbin)) {
248
+ auto bobbinString = std::get<std::string>(bobbin);
249
+ if (bobbinString == "Dummy") {
250
+ mas.get_mutable_magnetic().get_mutable_coil().set_bobbin(
251
+ OpenMagnetics::Bobbin::create_quick_bobbin(mas.get_mutable_magnetic().get_mutable_core()));
252
+ }
253
+ }
254
+ for (size_t windingIndex = 0; windingIndex < mas.get_magnetic().get_coil().get_functional_description().size(); ++windingIndex) {
255
+ mas.get_mutable_magnetic().get_mutable_coil().get_mutable_functional_description()[windingIndex].set_wire("Dummy");
256
+ }
257
+ auto sections = OpenMagnetics::CoilAdviser().get_advised_sections(mas, pattern, repetitions);
258
+ json result = json::array();
259
+ for (auto& section : sections) {
260
+ json aux;
261
+ to_json(aux, section);
262
+ result.push_back(aux);
263
+ }
264
+ return result;
265
+ }
266
+ catch (const std::exception& exc) {
267
+ return json{{"error", std::string("calculate_advised_sections: ") + exc.what()}};
268
+ }
269
+ }
270
+
271
+ json calculate_advised_coil(json masJson) {
272
+ try {
273
+ OpenMagnetics::Settings::GetInstance().set_coil_delimit_and_compact(true);
274
+ OpenMagnetics::Mas mas(masJson);
275
+ for (size_t windingIndex = 0; windingIndex < mas.get_magnetic().get_coil().get_functional_description().size(); ++windingIndex) {
276
+ mas.get_mutable_magnetic().get_mutable_coil().get_mutable_functional_description()[windingIndex].set_wire("Dummy");
277
+ }
278
+ mas.get_mutable_magnetic().get_mutable_coil().set_turns_description(std::nullopt);
279
+ mas.get_mutable_magnetic().get_mutable_coil().set_layers_description(std::nullopt);
280
+ mas.get_mutable_magnetic().get_mutable_coil().set_sections_description(std::nullopt);
281
+ mas.get_mutable_magnetic().get_mutable_coil().set_groups_description(std::nullopt);
282
+ OpenMagnetics::CoilAdviser coilAdviser;
283
+ auto masMagneticsWithCoil = coilAdviser.get_advised_coil(mas, 1);
284
+ if (masMagneticsWithCoil.size() > 0) {
285
+ json result;
286
+ to_json(result, masMagneticsWithCoil[0]);
287
+ return result;
288
+ }
289
+ else {
290
+ return json{{"error", "No coil found"}};
291
+ }
292
+ }
293
+ catch (const std::exception& exc) {
294
+ return json{{"error", std::string("calculate_advised_coil: ") + exc.what()}};
295
+ }
296
+ }
297
+
298
+ json calculate_advised_wires(json windingJson, json sectionJson, json currentJson, json solidInsulationRequirementsJson, double temperature, uint8_t numberSections, size_t maximumNumberResults, bool usePlanarWires) {
299
+ try {
300
+ OpenMagnetics::Settings::GetInstance().set_coil_delimit_and_compact(true);
301
+ OpenMagnetics::Winding winding(windingJson);
302
+ OpenMagnetics::WireSolidInsulationRequirements wireSolidInsulationRequirements(solidInsulationRequirementsJson);
303
+ Section section(sectionJson);
304
+ SignalDescriptor current(currentJson);
305
+ OpenMagnetics::WireAdviser wireAdviser;
306
+ wireAdviser.set_wire_solid_insulation_requirements(wireSolidInsulationRequirements);
307
+ std::vector<std::pair<OpenMagnetics::Winding, double>> windingsWithScoring;
308
+ if (usePlanarWires) {
309
+ windingsWithScoring = wireAdviser.get_advised_planar_wire(winding, section, current, temperature, numberSections, maximumNumberResults);
310
+ }
311
+ else {
312
+ windingsWithScoring = wireAdviser.get_advised_wire(winding, section, current, temperature, numberSections, maximumNumberResults);
313
+ }
314
+ json results;
315
+ results["data"] = json::array();
316
+ for (auto& [w, scoring] : windingsWithScoring) {
317
+ json result;
318
+ json windingJson;
319
+ to_json(windingJson, w);
320
+ result["winding"] = windingJson;
321
+ result["scoring"] = scoring;
322
+ results["data"].push_back(result);
323
+ }
324
+ return results;
325
+ }
326
+ catch (const std::exception& exc) {
327
+ return json{{"error", std::string("calculate_advised_wires: ") + exc.what()}};
328
+ }
329
+ }
330
+
239
331
  void register_adviser_bindings(py::module& m) {
240
332
  m.def("calculate_advised_cores", &calculate_advised_cores,
241
333
  R"pbdoc(
@@ -386,6 +478,20 @@ void register_adviser_bindings(py::module& m) {
386
478
  Returns "Exception: No magnetics found in cache" if cache is empty.
387
479
  )pbdoc",
388
480
  py::arg("inputs_json"), py::arg("filter_flow_json"), py::arg("max_results"));
481
+
482
+ m.def("calculate_advised_sections", &calculate_advised_sections,
483
+ "Get advised coil sections.",
484
+ py::arg("mas"), py::arg("pattern"), py::arg("repetitions"));
485
+
486
+ m.def("calculate_advised_coil", &calculate_advised_coil,
487
+ "Get full coil design advice.",
488
+ py::arg("mas"));
489
+
490
+ m.def("calculate_advised_wires", &calculate_advised_wires,
491
+ "Get wire selection advice.",
492
+ py::arg("winding"), py::arg("section"), py::arg("current"),
493
+ py::arg("solid_insulation_requirements"), py::arg("temperature"), py::arg("number_sections"),
494
+ py::arg("max_results"), py::arg("use_planar_wires") = false);
389
495
  }
390
496
 
391
497
  } // namespace PyMKF
@@ -99,6 +99,48 @@ bool check_if_fits(json bobbinJson, double dimension, bool isHorizontalOrRadial)
99
99
  }
100
100
  }
101
101
 
102
+ json create_simple_bobbin_from_core(json coreJson) {
103
+ try {
104
+ OpenMagnetics::Core core(coreJson, false, false, false);
105
+ auto bobbin = OpenMagnetics::Bobbin::create_quick_bobbin(core);
106
+
107
+ json result;
108
+ to_json(result, bobbin);
109
+ return result;
110
+ }
111
+ catch (const std::exception &exc) {
112
+ return "Exception: " + std::string{exc.what()};
113
+ }
114
+ }
115
+
116
+ json create_simple_bobbin_from_core_with_custom_thickness(json coreJson, double thickness) {
117
+ try {
118
+ OpenMagnetics::Core core(coreJson, false, false, false);
119
+ auto bobbin = OpenMagnetics::Bobbin::create_quick_bobbin(core, thickness);
120
+
121
+ json result;
122
+ to_json(result, bobbin);
123
+ return result;
124
+ }
125
+ catch (const std::exception &exc) {
126
+ return "Exception: " + std::string{exc.what()};
127
+ }
128
+ }
129
+
130
+ json create_simple_bobbin_from_core_with_custom_thicknesses(json coreJson, double wallThickness, double columnThickness) {
131
+ try {
132
+ OpenMagnetics::Core core(coreJson, false, false, false);
133
+ auto bobbin = OpenMagnetics::Bobbin::create_quick_bobbin(core, wallThickness, columnThickness);
134
+
135
+ json result;
136
+ to_json(result, bobbin);
137
+ return result;
138
+ }
139
+ catch (const std::exception &exc) {
140
+ return "Exception: " + std::string{exc.what()};
141
+ }
142
+ }
143
+
102
144
  void register_bobbin_bindings(py::module& m) {
103
145
  m.def("get_bobbins", &get_bobbins, "Retrieve all available bobbins as JSON objects");
104
146
  m.def("get_bobbin_names", &get_bobbin_names, "Retrieve list of all bobbin names");
@@ -108,6 +150,45 @@ void register_bobbin_bindings(py::module& m) {
108
150
  m.def("calculate_bobbin_data", &calculate_bobbin_data, "Calculate bobbin specifications");
109
151
  m.def("process_bobbin", &process_bobbin, "Process bobbin geometry");
110
152
  m.def("check_if_fits", &check_if_fits, "Check if winding fits in available space");
153
+
154
+ m.def("create_simple_bobbin_from_core", &create_simple_bobbin_from_core,
155
+ R"pbdoc(
156
+ Create a simple bobbin from core data.
157
+
158
+ Args:
159
+ core_json: JSON Core object.
160
+
161
+ Returns:
162
+ JSON Bobbin object.
163
+ )pbdoc",
164
+ py::arg("core_json"));
165
+
166
+ m.def("create_simple_bobbin_from_core_with_custom_thickness", &create_simple_bobbin_from_core_with_custom_thickness,
167
+ R"pbdoc(
168
+ Create a simple bobbin from core data with custom wall thickness.
169
+
170
+ Args:
171
+ core_json: JSON Core object.
172
+ thickness: Wall thickness in meters.
173
+
174
+ Returns:
175
+ JSON Bobbin object.
176
+ )pbdoc",
177
+ py::arg("core_json"), py::arg("thickness"));
178
+
179
+ m.def("create_simple_bobbin_from_core_with_custom_thicknesses", &create_simple_bobbin_from_core_with_custom_thicknesses,
180
+ R"pbdoc(
181
+ Create a simple bobbin from core data with custom wall and column thicknesses.
182
+
183
+ Args:
184
+ core_json: JSON Core object.
185
+ wall_thickness: Wall thickness in meters.
186
+ column_thickness: Column thickness in meters.
187
+
188
+ Returns:
189
+ JSON Bobbin object.
190
+ )pbdoc",
191
+ py::arg("core_json"), py::arg("wall_thickness"), py::arg("column_thickness"));
111
192
  }
112
193
 
113
194
  } // namespace PyMKF
@@ -13,6 +13,11 @@ json calculate_bobbin_data(json magneticJson);
13
13
  json process_bobbin(json bobbinJson);
14
14
  bool check_if_fits(json bobbinJson, double dimension, bool isHorizontalOrRadial);
15
15
 
16
+ // Simple bobbin creation
17
+ json create_simple_bobbin_from_core(json coreJson);
18
+ json create_simple_bobbin_from_core_with_custom_thickness(json coreJson, double thickness);
19
+ json create_simple_bobbin_from_core_with_custom_thicknesses(json coreJson, double wallThickness, double columnThickness);
20
+
16
21
  void register_bobbin_bindings(py::module& m);
17
22
 
18
23
  } // namespace PyMKF