Skip to content

FEAT: add geometry-mechanical-dpf workflow #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions .github/workflows/geometry-mechanical-dpf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
name: Geometry Mechanical DPF Workflow

on:
push:
branches:
- main
pull_request:
paths:
- 'geometry-mechanical-dpf/**'

env:
MAIN_PYTHON_VERSION: '3.12'
GEOMETRY_DOCKER_IMAGE: 'ghcr.io/ansys/geometry'
MECHANICAL_DOCKER_IMAGE: 'ghcr.io/ansys/mechanical'
ANSRV_GEO_PORT: 700
ANSRV_GEO_LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }}
ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
geometry:
name: Geometry
runs-on: [self-hosted, Windows, pyansys-workflows]
strategy:
fail-fast: false
matrix:
ansys-release: [24.1, 24.2]
steps:

- name: Checkout code
uses: actions/checkout@v4
with:
sparse-checkout: 'geometry-mechanical-dpf'

- name: Set up Python ${{ env.MAIN_PYTHON_VERSION }}
uses: actions/setup-python@v4
with:
python-version: ${{ env.MAIN_PYTHON_VERSION }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m venv .venv
.venv/Scripts/activate
pip install -r geometry-mechanical-dpf/requirements_${{ matrix.ansys-release }}.txt

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Download (if needed) and run Geometry service container
run: |
docker pull ${{ env.GEOMETRY_DOCKER_IMAGE }}:windows-${{ matrix.ansys-release }}

- name: Run the PyAnsys Geometry script
env:
ANSYS_GEOMETERY_RELEASE: ${{ env.GEOMETRY_DOCKER_IMAGE }}:windows-${{ matrix.ansys-release }}
run: |
.venv/Scripts/activate
python geometry-mechanical-dpf/01_geometry.py

- name: Store the outputs
uses: actions/upload-artifact@v4
with:
name: geometry-outputs-${{ matrix.ansys-release }}
path: geometry-mechanical-dpf/outputs

- name: Stop any remaining containers
if: always()
run: |
$dockerContainers = docker ps -a -q
if (-not [string]::IsNullOrEmpty($dockerContainers)) {
docker stop $dockerContainers
docker rm $dockerContainers
}

mech-dpf:
name: Mechanical - Dpf
runs-on: ubuntu-latest-8-cores
needs: geometry
strategy:
fail-fast: false
matrix:
ansys-release: [24.1, 24.2]
container:
image: 'ghcr.io/ansys/mechanical:${{ matrix.ansys-release }}.0'
options: --entrypoint /bin/bash
steps:

- name: Checkout code
uses: actions/checkout@v4
with:
sparse-checkout: 'geometry-mechanical-dpf'

- name: Set up Python
run: |
apt update
apt install --reinstall ca-certificates
apt install software-properties-common -y
add-apt-repository ppa:deadsnakes/ppa -y
apt install -y python${{ env.MAIN_PYTHON_VERSION }} python${{ env.MAIN_PYTHON_VERSION }}-venv
python${{ env.MAIN_PYTHON_VERSION }} -m venv /env

- name: Install dependencies
run: |
. /env/bin/activate
python -m pip install --upgrade pip
pip install -r geometry-mechanical-dpf/requirements_${{ matrix.ansys-release }}.txt

- name: Check out the geometry outputs
uses: actions/download-artifact@v4
with:
name: geometry-outputs-${{ matrix.ansys-release }}
path: geometry-mechanical-dpf/outputs

- name: Run the PyMechanical script
env:
NUM_CORES: 1
ANSYS_WORKBENCH_LOGGING_CONSOLE: 0
ANSYS_WORKBENCH_LOGGING: 0
ANSYS_WORKBENCH_LOGGING_FILTER_LEVEL: 2
ANSYS_MECHANICAL_RELEASE: ${{ matrix.ansys-release }}
run: |
. /env/bin/activate
xvfb-run mechanical-env python geometry-mechanical-dpf/02_mechanical.py > pymechlogs${{ matrix.ansys-release }}.txt 2>&1 || true
cat pymechlogs${{ matrix.ansys-release }}.txt

- name: Run the PyDPF script
run: |
. /env/bin/activate
xvfb-run python geometry-mechanical-dpf/03_dpf.py > pydpflogs${{ matrix.ansys-release }}.txt 2>&1 || true
cat pydpflogs${{ matrix.ansys-release }}.txt

- name: Store the outputs
uses: actions/upload-artifact@v4
with:
name: pymechanical-dpf-outputs-${{ matrix.ansys-release }}
path: geometry-mechanical-dpf/outputs
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ for every part of the simulation process. The available workflows are:
- For geometry: Ansys SpaceClaim / Ansys Discovery / Ansys Geometry Service
- For meshing: Ansys Fluent Meshing
- For simulation: Ansys Fluent Solver
- [Geometry, mechanical and post-processing](./geometry-mechanical-dpf): this workflow demonstrates how to
create a printed circuit board (PCB) geometry, mesh, run steady state and transient thermal analysis,
and post-process using DPF. The geometry generated is a simple PCB with multiple chips.
The exported CAD file (PMDB format) is then imported inside Ansys Mechanical
to run a steady-state thermal analysis followed by transient analysis.
All temperature results in different chips are displayed using DPF. The involved Ansys products are:
- For geometry: Ansys SpaceClaim / Ansys Discovery / Ansys Geometry Service
- For simulation: Ansys Mechanical
- For post-procesing: Ansys Data Processing Framework

## How to run the workflows

Expand Down
170 changes: 170 additions & 0 deletions geometry-mechanical-dpf/01_geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# 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.

import os
from pathlib import Path

from ansys.geometry.core import launch_modeler
from ansys.geometry.core.connection import GEOMETRY_SERVICE_DOCKER_IMAGE, GeometryContainers
from ansys.geometry.core.designer import DesignFileFormat
from ansys.geometry.core.math import Plane, Point2D, Point3D, UnitVector3D
from ansys.geometry.core.misc import DEFAULT_UNITS, UNITS
from ansys.geometry.core.sketch import Sketch

# Check env vars to see which image to launch
#
# --- ONLY FOR WORKFLOW RUNS ---
image = None
if "ANSYS_GEOMETRY_RELEASE" in os.environ:
image_tag = os.environ["ANSYS_GEOMETRY_RELEASE"]
for geom_services in GeometryContainers:
if image_tag == f"{GEOMETRY_SERVICE_DOCKER_IMAGE}:{geom_services.value[2]}":
print(f"Using {image_tag} image")
image = geom_services
break

# -- Parameters --
#
GRAPHICS_BOOL = False # Set to True to display the mesh
OUTPUT_DIR = Path(Path(__file__).parent, "outputs") # Output directory

# -- Start a modeler session --
#
modeler = launch_modeler(image=image)
print(modeler)

# -- Create and plot a sketch --
#
# Define default length units
DEFAULT_UNITS.LENGTH = UNITS.cm

# Define the radius of holes in pcb
pcb_hole_radius = 1

# Create PCB Substrate
sketch_substrate = Sketch()
(
sketch_substrate.segment(Point2D([5, 0]), Point2D([122, 0]))
.arc_to_point(Point2D([127, 5]), Point2D([122, 5]))
.segment_to_point(Point2D([127, 135]))
.arc_to_point(Point2D([122, 140]), Point2D([122, 135]))
.segment_to_point(Point2D([5, 140]))
.arc_to_point(Point2D([0, 135]), Point2D([5, 135]))
.segment_to_point(Point2D([0, 5]))
.arc_to_point(Point2D([5, 0]), Point2D([5, 5]))
.circle(Point2D([6.35, 6.35]), radius=3.94 / 2)
.circle(Point2D([127 - 6.35, 6.35]), radius=3.94 / 2)
.circle(Point2D([127 - 6.35, 140 - 6.35]), radius=3.94 / 2)
.circle(Point2D([6.35, 140 - 6.35]), radius=3.94 / 2)
)
substrate_height = 1.575
plane = Plane(
origin=Point3D([0, 0, substrate_height]),
direction_x=[1, 0, 0],
direction_y=[0, 1, 0],
)

# create IC
sketch_IC = Sketch(plane)
sketch_IC.box(Point2D([62 / 2 + 7.5, 51 / 2 + 5]), 15, 10)

# create capacitor sketch
sketch_capacitor = Sketch(plane=plane)
sketch_capacitor.circle(center=Point2D([95, 104]), radius=4.4)

# create ic
sketch_ic_7 = Sketch(plane=plane)
sketch_ic_7.box(Point2D([25, 108]), 18, 24)

# create ic
sketch_ic_8 = Sketch(plane=plane)
sketch_ic_8.box(Point2D([21, 59]), 10, 18)

# -- Perform some modeling operations --
#
# Now that the sketch is ready to be extruded, perform some modeling operations,
# including creating the design, creating the body directly on the design, and
# plotting the body.

# Start by creating the Design
design = modeler.create_design("pcb_design")

# Create all necessary components for pcb
component = design.add_component("PCB")
component.extrude_sketch("substrate", sketch_substrate, distance=substrate_height)
ic_1 = component.extrude_sketch("ic-1", sketch_IC, distance=4.5)

ic_2 = ic_1.copy(parent=component, name="ic-2")
ic_2.translate(direction=UnitVector3D([1, 0, 0]), distance=17)

ic_3 = ic_1.copy(parent=component, name="ic-3")
ic_3.translate(direction=UnitVector3D([0, 1, 0]), distance=17)

ic_4 = ic_2.copy(parent=component, name="ic-4")
ic_4.translate(direction=UnitVector3D([1, 0, 0]), distance=17)

ic_5 = ic_2.copy(parent=component, name="ic-5")
ic_5.translate(direction=UnitVector3D([0, 1, 0]), distance=17)

ic_6 = ic_5.copy(parent=component, name="ic-6")
ic_6.translate(direction=UnitVector3D([1, 0, 0]), distance=17)

ic_7 = component.extrude_sketch("ic-7", sketch=sketch_ic_7, distance=2)
ic_8 = component.extrude_sketch("ic-8", sketch=sketch_ic_8, distance=2)


capacitor_1 = component.extrude_sketch("capacitor_1", sketch_capacitor, distance=20)
capacitor_2 = capacitor_1.copy(parent=component, name="capacitor_2")
capacitor_2.translate(direction=UnitVector3D([0, 1, 0]), distance=-20)
capacitor_3 = capacitor_1.copy(parent=component, name="capacitor_3")
capacitor_3.translate(direction=UnitVector3D([0, 1, 0]), distance=-40)
capacitor_4 = capacitor_1.copy(parent=component, name="capacitor_4")
capacitor_4.translate(direction=UnitVector3D([0, 1, 0]), distance=-60)

# Create named selections
for body in component.bodies:
design.create_named_selection(name=body.name, bodies=[body])

# Plot the the entire geometry
if GRAPHICS_BOOL:
design.plot()

# -- Export file --
#
# Once modeling operations are finalized, you can export files
# in different formats. For the formats supported by DMS, see the
# ``DesignFileFormat`` class in the ``Design`` module documentation.
#
# Export files in PMDB format for Mechanical.

# Download the design in FMD format
OUTPUT_DIR.mkdir(exist_ok=True)
download_file = Path(OUTPUT_DIR, "pcb.pmdb")
design.download(file_location=download_file, format=DesignFileFormat.PMDB)

# -- Close session --
#
# When you finish interacting with your modeling service, you should close the active
# server session. This frees resources wherever the service is running.
#
# Close the server session.
modeler.close()
Loading
Loading