Skip to content

Commit d0bb357

Browse files
dipinknairRobPasMueMaxJPRey
authored
FEAT: add geometry-mechanical-dpf workflow (#11)
Co-authored-by: Roberto Pastor Muela <[email protected]> Co-authored-by: Maxime Rey <[email protected]>
1 parent f1e6b38 commit d0bb357

File tree

7 files changed

+606
-0
lines changed

7 files changed

+606
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
name: Geometry Mechanical DPF Workflow
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
paths:
9+
- 'geometry-mechanical-dpf/**'
10+
11+
env:
12+
MAIN_PYTHON_VERSION: '3.12'
13+
GEOMETRY_DOCKER_IMAGE: 'ghcr.io/ansys/geometry'
14+
MECHANICAL_DOCKER_IMAGE: 'ghcr.io/ansys/mechanical'
15+
ANSRV_GEO_PORT: 700
16+
ANSRV_GEO_LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }}
17+
ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}}
18+
19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
jobs:
24+
geometry:
25+
name: Geometry
26+
runs-on: [self-hosted, Windows, pyansys-workflows]
27+
strategy:
28+
fail-fast: false
29+
matrix:
30+
ansys-release: [24.1, 24.2]
31+
steps:
32+
33+
- name: Checkout code
34+
uses: actions/checkout@v4
35+
with:
36+
sparse-checkout: 'geometry-mechanical-dpf'
37+
38+
- name: Set up Python ${{ env.MAIN_PYTHON_VERSION }}
39+
uses: actions/setup-python@v4
40+
with:
41+
python-version: ${{ env.MAIN_PYTHON_VERSION }}
42+
43+
- name: Install dependencies
44+
run: |
45+
python -m pip install --upgrade pip
46+
python -m venv .venv
47+
.venv/Scripts/activate
48+
pip install -r geometry-mechanical-dpf/requirements_${{ matrix.ansys-release }}.txt
49+
50+
- name: Login to GitHub Container Registry
51+
uses: docker/login-action@v3
52+
with:
53+
registry: ghcr.io
54+
username: ${{ github.actor }}
55+
password: ${{ secrets.GITHUB_TOKEN }}
56+
57+
- name: Download (if needed) and run Geometry service container
58+
run: |
59+
docker pull ${{ env.GEOMETRY_DOCKER_IMAGE }}:windows-${{ matrix.ansys-release }}
60+
61+
- name: Run the PyAnsys Geometry script
62+
env:
63+
ANSYS_GEOMETERY_RELEASE: ${{ env.GEOMETRY_DOCKER_IMAGE }}:windows-${{ matrix.ansys-release }}
64+
run: |
65+
.venv/Scripts/activate
66+
python geometry-mechanical-dpf/01_geometry.py
67+
68+
- name: Store the outputs
69+
uses: actions/upload-artifact@v4
70+
with:
71+
name: geometry-outputs-${{ matrix.ansys-release }}
72+
path: geometry-mechanical-dpf/outputs
73+
74+
- name: Stop any remaining containers
75+
if: always()
76+
run: |
77+
$dockerContainers = docker ps -a -q
78+
if (-not [string]::IsNullOrEmpty($dockerContainers)) {
79+
docker stop $dockerContainers
80+
docker rm $dockerContainers
81+
}
82+
83+
mech-dpf:
84+
name: Mechanical - Dpf
85+
runs-on: ubuntu-latest-8-cores
86+
needs: geometry
87+
strategy:
88+
fail-fast: false
89+
matrix:
90+
ansys-release: [24.1, 24.2]
91+
container:
92+
image: 'ghcr.io/ansys/mechanical:${{ matrix.ansys-release }}.0'
93+
options: --entrypoint /bin/bash
94+
steps:
95+
96+
- name: Checkout code
97+
uses: actions/checkout@v4
98+
with:
99+
sparse-checkout: 'geometry-mechanical-dpf'
100+
101+
- name: Set up Python
102+
run: |
103+
apt update
104+
apt install --reinstall ca-certificates
105+
apt install software-properties-common -y
106+
add-apt-repository ppa:deadsnakes/ppa -y
107+
apt install -y python${{ env.MAIN_PYTHON_VERSION }} python${{ env.MAIN_PYTHON_VERSION }}-venv
108+
python${{ env.MAIN_PYTHON_VERSION }} -m venv /env
109+
110+
- name: Install dependencies
111+
run: |
112+
. /env/bin/activate
113+
python -m pip install --upgrade pip
114+
pip install -r geometry-mechanical-dpf/requirements_${{ matrix.ansys-release }}.txt
115+
116+
- name: Check out the geometry outputs
117+
uses: actions/download-artifact@v4
118+
with:
119+
name: geometry-outputs-${{ matrix.ansys-release }}
120+
path: geometry-mechanical-dpf/outputs
121+
122+
- name: Run the PyMechanical script
123+
env:
124+
NUM_CORES: 1
125+
ANSYS_WORKBENCH_LOGGING_CONSOLE: 0
126+
ANSYS_WORKBENCH_LOGGING: 0
127+
ANSYS_WORKBENCH_LOGGING_FILTER_LEVEL: 2
128+
ANSYS_MECHANICAL_RELEASE: ${{ matrix.ansys-release }}
129+
run: |
130+
. /env/bin/activate
131+
xvfb-run mechanical-env python geometry-mechanical-dpf/02_mechanical.py > pymechlogs${{ matrix.ansys-release }}.txt 2>&1 || true
132+
cat pymechlogs${{ matrix.ansys-release }}.txt
133+
134+
- name: Run the PyDPF script
135+
run: |
136+
. /env/bin/activate
137+
xvfb-run python geometry-mechanical-dpf/03_dpf.py > pydpflogs${{ matrix.ansys-release }}.txt 2>&1 || true
138+
cat pydpflogs${{ matrix.ansys-release }}.txt
139+
140+
- name: Store the outputs
141+
uses: actions/upload-artifact@v4
142+
with:
143+
name: pymechanical-dpf-outputs-${{ matrix.ansys-release }}
144+
path: geometry-mechanical-dpf/outputs

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ for every part of the simulation process. The available workflows are:
3333
- For geometry: Ansys SpaceClaim / Ansys Discovery / Ansys Geometry Service
3434
- For meshing: Ansys Fluent Meshing
3535
- For simulation: Ansys Fluent Solver
36+
- [Geometry, mechanical and post-processing](./geometry-mechanical-dpf): this workflow demonstrates how to
37+
create a printed circuit board (PCB) geometry, mesh, run steady state and transient thermal analysis,
38+
and post-process using DPF. The geometry generated is a simple PCB with multiple chips.
39+
The exported CAD file (PMDB format) is then imported inside Ansys Mechanical
40+
to run a steady-state thermal analysis followed by transient analysis.
41+
All temperature results in different chips are displayed using DPF. The involved Ansys products are:
42+
- For geometry: Ansys SpaceClaim / Ansys Discovery / Ansys Geometry Service
43+
- For simulation: Ansys Mechanical
44+
- For post-procesing: Ansys Data Processing Framework
3645

3746
## How to run the workflows
3847

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates.
2+
# SPDX-License-Identifier: MIT
3+
#
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
import os
24+
from pathlib import Path
25+
26+
from ansys.geometry.core import launch_modeler
27+
from ansys.geometry.core.connection import GEOMETRY_SERVICE_DOCKER_IMAGE, GeometryContainers
28+
from ansys.geometry.core.designer import DesignFileFormat
29+
from ansys.geometry.core.math import Plane, Point2D, Point3D, UnitVector3D
30+
from ansys.geometry.core.misc import DEFAULT_UNITS, UNITS
31+
from ansys.geometry.core.sketch import Sketch
32+
33+
# Check env vars to see which image to launch
34+
#
35+
# --- ONLY FOR WORKFLOW RUNS ---
36+
image = None
37+
if "ANSYS_GEOMETRY_RELEASE" in os.environ:
38+
image_tag = os.environ["ANSYS_GEOMETRY_RELEASE"]
39+
for geom_services in GeometryContainers:
40+
if image_tag == f"{GEOMETRY_SERVICE_DOCKER_IMAGE}:{geom_services.value[2]}":
41+
print(f"Using {image_tag} image")
42+
image = geom_services
43+
break
44+
45+
# -- Parameters --
46+
#
47+
GRAPHICS_BOOL = False # Set to True to display the mesh
48+
OUTPUT_DIR = Path(Path(__file__).parent, "outputs") # Output directory
49+
50+
# -- Start a modeler session --
51+
#
52+
modeler = launch_modeler(image=image)
53+
print(modeler)
54+
55+
# -- Create and plot a sketch --
56+
#
57+
# Define default length units
58+
DEFAULT_UNITS.LENGTH = UNITS.cm
59+
60+
# Define the radius of holes in pcb
61+
pcb_hole_radius = 1
62+
63+
# Create PCB Substrate
64+
sketch_substrate = Sketch()
65+
(
66+
sketch_substrate.segment(Point2D([5, 0]), Point2D([122, 0]))
67+
.arc_to_point(Point2D([127, 5]), Point2D([122, 5]))
68+
.segment_to_point(Point2D([127, 135]))
69+
.arc_to_point(Point2D([122, 140]), Point2D([122, 135]))
70+
.segment_to_point(Point2D([5, 140]))
71+
.arc_to_point(Point2D([0, 135]), Point2D([5, 135]))
72+
.segment_to_point(Point2D([0, 5]))
73+
.arc_to_point(Point2D([5, 0]), Point2D([5, 5]))
74+
.circle(Point2D([6.35, 6.35]), radius=3.94 / 2)
75+
.circle(Point2D([127 - 6.35, 6.35]), radius=3.94 / 2)
76+
.circle(Point2D([127 - 6.35, 140 - 6.35]), radius=3.94 / 2)
77+
.circle(Point2D([6.35, 140 - 6.35]), radius=3.94 / 2)
78+
)
79+
substrate_height = 1.575
80+
plane = Plane(
81+
origin=Point3D([0, 0, substrate_height]),
82+
direction_x=[1, 0, 0],
83+
direction_y=[0, 1, 0],
84+
)
85+
86+
# create IC
87+
sketch_IC = Sketch(plane)
88+
sketch_IC.box(Point2D([62 / 2 + 7.5, 51 / 2 + 5]), 15, 10)
89+
90+
# create capacitor sketch
91+
sketch_capacitor = Sketch(plane=plane)
92+
sketch_capacitor.circle(center=Point2D([95, 104]), radius=4.4)
93+
94+
# create ic
95+
sketch_ic_7 = Sketch(plane=plane)
96+
sketch_ic_7.box(Point2D([25, 108]), 18, 24)
97+
98+
# create ic
99+
sketch_ic_8 = Sketch(plane=plane)
100+
sketch_ic_8.box(Point2D([21, 59]), 10, 18)
101+
102+
# -- Perform some modeling operations --
103+
#
104+
# Now that the sketch is ready to be extruded, perform some modeling operations,
105+
# including creating the design, creating the body directly on the design, and
106+
# plotting the body.
107+
108+
# Start by creating the Design
109+
design = modeler.create_design("pcb_design")
110+
111+
# Create all necessary components for pcb
112+
component = design.add_component("PCB")
113+
component.extrude_sketch("substrate", sketch_substrate, distance=substrate_height)
114+
ic_1 = component.extrude_sketch("ic-1", sketch_IC, distance=4.5)
115+
116+
ic_2 = ic_1.copy(parent=component, name="ic-2")
117+
ic_2.translate(direction=UnitVector3D([1, 0, 0]), distance=17)
118+
119+
ic_3 = ic_1.copy(parent=component, name="ic-3")
120+
ic_3.translate(direction=UnitVector3D([0, 1, 0]), distance=17)
121+
122+
ic_4 = ic_2.copy(parent=component, name="ic-4")
123+
ic_4.translate(direction=UnitVector3D([1, 0, 0]), distance=17)
124+
125+
ic_5 = ic_2.copy(parent=component, name="ic-5")
126+
ic_5.translate(direction=UnitVector3D([0, 1, 0]), distance=17)
127+
128+
ic_6 = ic_5.copy(parent=component, name="ic-6")
129+
ic_6.translate(direction=UnitVector3D([1, 0, 0]), distance=17)
130+
131+
ic_7 = component.extrude_sketch("ic-7", sketch=sketch_ic_7, distance=2)
132+
ic_8 = component.extrude_sketch("ic-8", sketch=sketch_ic_8, distance=2)
133+
134+
135+
capacitor_1 = component.extrude_sketch("capacitor_1", sketch_capacitor, distance=20)
136+
capacitor_2 = capacitor_1.copy(parent=component, name="capacitor_2")
137+
capacitor_2.translate(direction=UnitVector3D([0, 1, 0]), distance=-20)
138+
capacitor_3 = capacitor_1.copy(parent=component, name="capacitor_3")
139+
capacitor_3.translate(direction=UnitVector3D([0, 1, 0]), distance=-40)
140+
capacitor_4 = capacitor_1.copy(parent=component, name="capacitor_4")
141+
capacitor_4.translate(direction=UnitVector3D([0, 1, 0]), distance=-60)
142+
143+
# Create named selections
144+
for body in component.bodies:
145+
design.create_named_selection(name=body.name, bodies=[body])
146+
147+
# Plot the the entire geometry
148+
if GRAPHICS_BOOL:
149+
design.plot()
150+
151+
# -- Export file --
152+
#
153+
# Once modeling operations are finalized, you can export files
154+
# in different formats. For the formats supported by DMS, see the
155+
# ``DesignFileFormat`` class in the ``Design`` module documentation.
156+
#
157+
# Export files in PMDB format for Mechanical.
158+
159+
# Download the design in FMD format
160+
OUTPUT_DIR.mkdir(exist_ok=True)
161+
download_file = Path(OUTPUT_DIR, "pcb.pmdb")
162+
design.download(file_location=download_file, format=DesignFileFormat.PMDB)
163+
164+
# -- Close session --
165+
#
166+
# When you finish interacting with your modeling service, you should close the active
167+
# server session. This frees resources wherever the service is running.
168+
#
169+
# Close the server session.
170+
modeler.close()

0 commit comments

Comments
 (0)