diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0f7867f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + ruff: + uses: ./.github/workflows/ruff.yml + + tests: + uses: ./.github/workflows/tests.yml + + docs: + needs: [tests] + permissions: + contents: write + uses: ./.github/workflows/docs.yml + with: + deploy: ${{ github.event_name == 'push' }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..65a7dcd --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,35 @@ +name: Docs + +on: + workflow_call: + inputs: + deploy: + description: "Push the built docs to gh-pages" + type: boolean + default: false + +permissions: + contents: write + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.11 + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v4 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material- + - run: pip install ".[docs]" + - name: Build docs + run: mkdocs build + + - name: Deploy docs + if: inputs.deploy + run: mkdocs gh-deploy --force \ No newline at end of file diff --git a/.github/workflows/rull.yml b/.github/workflows/ruff.yml similarity index 75% rename from .github/workflows/rull.yml rename to .github/workflows/ruff.yml index 3ea32a9..7f00e5e 100644 --- a/.github/workflows/rull.yml +++ b/.github/workflows/ruff.yml @@ -1,10 +1,7 @@ name: Ruff on: - push: - branches: - - main - pull_request: + workflow_call: jobs: ruff: @@ -21,4 +18,4 @@ jobs: run: ruff check . - name: Ruff format check - run: ruff format . --check \ No newline at end of file + run: ruff format . --check diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 607d8a4..2bb014c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,13 +1,10 @@ name: Tests on: - push: - branches: - - main - pull_request: + workflow_call: jobs: - pytest: + test: runs-on: ubuntu-latest strategy: diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d943beb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Lukáš Adam + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 146c10d..f78a644 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ This repo contains Python code for automatic rotor design and optimization of synchronous reluctance machines (SynRMs). The motivation is to improve the performance of SynRMs without using permanent magnets by optimizing the rotor flux barrier geometry. Candidate designs are generated in Python, simulated in Ansys Electronics Desktop, and optimized to increase the average torque and reduce torque ripple. +[Documentation](https://ai-rice.github.io/MachineDesign/) + Run "run.py" for random design evaluation and "run_optimization.py" for Bayesian optimization. Ansys Electronics Desktop and PyAEDT are required. diff --git a/docs/WINDING.md b/docs/WINDING.md new file mode 100644 index 0000000..7ac3ece --- /dev/null +++ b/docs/WINDING.md @@ -0,0 +1,84 @@ +# Winding layout + +`machine_design/generic/winding.py` computes the phase and polarity layout of each slot, and assigns it to an AEDT model. It covers `compute_winding_layout` and `assign_slot_currents_to_aedt`. + +## Basic usage + +```python +compute_winding_layout(phases, slots, pole_pairs, coil_pitch=None, layers=1) +``` + +- phases, slots, pole_pairs: the machine's basic parameters. +- coil_pitch: coil span in slots. +- layers: 1 for a single layer winding (default), 2 for double layer. + +**Example 1**, using the synrm_3f_36s motor (3 phases, 36 slots, 2 pole pairs, coil_pitch=9): + +```python +from machine_design.generic.winding import compute_winding_layout + +layout = compute_winding_layout(phases=3, slots=36, pole_pairs=2, coil_pitch=9, layers=1) +``` + +The first 9 slots of layout.phase and layout.polarity give: +``` +phase[:9] = ['A','A','A','C','C','C','B','B','B'] +polarity[:9] = [1, 1, 1, -1, -1, -1, 1, 1, 1] +``` + +**Summary:** Slots 0-2 carry phase A (positive), slots 3-5 carry phase C (negative), and slots 6-8 carry phase B (positive). + +**Example 2**, using the synrm_5f_40s motor (5 phases, 40 slots, 2 pole pairs, coil_pitch=9): + +```python +from machine_design.generic.winding import compute_winding_layout + +layout = compute_winding_layout(phases=5, slots=40, pole_pairs=2, coil_pitch=9, layers=1) +``` + +The first 10 slots of layout.phase and layout.polarity give: +``` +phase[:10] = ['A','D','D','B','B','E','E','C','C','A'] +polarity[:10] = [1, -1, -1, 1, 1, -1, -1, 1, 1, -1] +``` + +**Summary:** Slot 0 carries phase A (positive), slots 1-2 carry phase D (negative), slots 3-4 carry phase B (positive), slots 5-6 carry phase E (negative), slots 7-8 carry phase C (positive), and slot 9 carries phase A (negative). + +## Meaning of the outputs +compute_winding_layout returns a WindingLayout. For Example 1 above: + +| Field | Value | Meaning | +|---|---|---| +| `coil_pitch` | `9` | Coil span in slots, including the automatically selected value if applicable. | +| `phase` | `['A','A','A','C',...]` | Phase letter of each slot's front coil side. | +| `polarity` | `[1,1,1,-1,...]` | Current direction of each slot's front coil side. | +| `phase_order` | `['A','B','C']` | The phase letters used in this winding. | +| `winding_factor` | `0.9598` | Pitch times distribution factor (0-1) (a warning is raised below 0.9). | +| `phase_angles` | `{'A':0.0,'B':-120.0,'C':-240.0}` | Electrical angle of each phase. | +| `p2_min_fem` | `1` | Minimum pole count for an FEM sector to close periodically (odd means the sector needs an antiperiodic boundary). | +| `phase_a_axis` | `120.0` | Electrical axis of phase A in the model (0-180 deg) (used to compute `InitPos`). | +| `phase_back` / `polarity_back` | `None` | Second (back) coil side per slot (only set for `layers=2`, `None` for a single layer). | + + +## Double layer windings (layers=2) +With layers=2, each slot holds two radially stacked coil slides: a front side, described by phase/polarity as before, and a back side, described by phase_back/polarity_back (same format, same slot order). The back side is the front side shifted by coil_pitch slots, with reversed polarity. + +```python +layout = compute_winding_layout(phases=3, slots=36, pole_pairs=2, coil_pitch=9, layers=2) +``` + +The first 9 slots of layout.phase/layout.polarity (front) and layout.phase_back/layout.polarity_back (back): +``` +phase[:9] = ['A','A','A','C','C','C','B','B','B'] +phase_back[:9] = ['A','A','A','C','C','C','B','B','B'] +polarity[:9] = [1, 1, 1, -1, -1, -1, 1, 1, 1] +polarity_back[:9] = [1, 1, 1, -1, -1, -1, 1, 1, 1] +``` + +Front and back happen to be identical here only because `coil_pitch=9` is a full pole pitch. A shorter pitch would make them differ. + +To assign a double layer layout to AEDT, build both coil sides as separate named objects (one set per layer) and pass the second layer's object names as `coil_objects_back` to `assign_slot_currents_to_aedt`: + +```python +assign_slot_currents_to_aedt(m2d, coil_objects, layout, phase_currents, coil_objects_back=coil_objects_back) +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..7b8bd19 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,7 @@ +This website contains the documentation for the [MachineDesign](https://github.com/AI-RICE/MachineDesign) project. + +The aim of the project is to provide a library for designing barriers in electrical machines, from geometry and winding generation to simulation and optimization. The core functionality includes: + +- computing and assigning winding layouts for different phases, slots, and pole combinations, see [Winding layout](WINDING.md). +- building and simulating machine models through Ansys Electronics Desktop (AEDT). +- optimizing rotor barrier geometry and current setpoints with Bayesian optimization. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..b2b430a --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,26 @@ +site_name: MachineDesign +repo_url: https://github.com/AI-RICE/MachineDesign +repo_name: MachineDesign + +theme: + name: "material" + features: + - navigation.sections + - content.code.copy + +markdown_extensions: + - attr_list + - admonition + - pymdownx.details + - pymdownx.superfences + +plugins: + - markdown-exec + - mkdocstrings: + handlers: + python: + paths: [machine_design] + +nav: + - Introduction: index.md + - Winding layout: WINDING.md diff --git a/pyproject.toml b/pyproject.toml index 6e7ff08..cf77626 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,13 @@ dependencies=[ dev = [ 'pytest', ] +docs = [ + "mkdocs>=1.6,<2.0", + "mkdocs-material>=9.0", + "mkdocstrings", + "mkdocstrings-python", + "markdown-exec", +] [tool.setuptools] packages = [