ansys / pyansys-geometry

A Python wrapper for Ansys Geometry Services
https://geometry.docs.pyansys.com/
MIT License
32 stars 9 forks source link

PyGeometry is failing if I increase number of loops to create a geometry #1164

Closed PatilSuneel closed 1 month ago

PatilSuneel commented 1 month ago

🔍 Before submitting the issue

🐞 Description of the bug

Hi All, I am doing an excersise to understand how much time required to create an geometry and I am comparing between standalone Sc and PyGeometry. For this excersise I have taken a simple plate with a hole geometry. The code works somthing like below image where 50 indicates number of loops.

The code is working fine for up-to range = 50 if I increase the range values greater than 50 the code is failing, following is the error image

📝 Steps to reproduce

st = time.time()

modeler = launch_modeler(mode="spaceclaim")

outer_hole_radius = Distance(0.5, UNITS.m)
j = 0
k = 0

design = modeler.create_design("ExtrudedPlate")

for i in range (50):
originX = j
oroginY = k
point1X = originX-4
point1Y = oroginY+5
point2X = originX+4
point3Y = oroginY-5

center1X = originX+3
center1Y = oroginY+4
center2X = originX-3
center2Y = oroginY-4

sketch = Sketch()
(
    sketch.segment(start=Point2D([point1X, point1Y], unit=UNITS.m), end=Point2D([point2X, point1Y], unit=UNITS.m))
    .segment_to_point(end=Point2D([point2X, point3Y], unit=UNITS.m))
    .segment_to_point(end=Point2D([point1X, point3Y], unit=UNITS.m))
    .segment_to_point(end=Point2D([point1X, point1Y], unit=UNITS.m))
    .box(
        center=Point2D([originX, oroginY], unit=UNITS.m),
        width=Distance(3, UNITS.m),
        height=Distance(3, UNITS.m),
    )
    .circle(center=Point2D([center1X, center1Y], unit=UNITS.m), radius=outer_hole_radius)
    .circle(center=Point2D([center2X, center2Y], unit=UNITS.m), radius=outer_hole_radius)
    .circle(center=Point2D([center2X, center1Y], unit=UNITS.m), radius=outer_hole_radius)
    .circle(center=Point2D([center1X, center2Y], unit=UNITS.m), radius=outer_hole_radius)
)

body = design.extrude_sketch(f"PlateLayer"+str(i), sketch, Quantity(2, UNITS.m))

board_named_selection = design.create_named_selection("Plate"+str(i), bodies=[body])

j = j+8
k = k+10

et = time.time()

elapsed_time = et - st
print('Execution time:', elapsed_time, 'seconds')

💻 Which operating system are you using?

Windows

📀 Which ANSYS version are you using?

2024 R1

🐍 Which Python version are you using?

3.11

📦 Installed packages

addcopyfighandler==3.2.0
alabaster==0.7.13
altgraph==0.17.4
ansys-api-dbu==0.2.5
ansys-api-geometry==0.3.8
ansys-geometry-core==0.4.11
ansys-tools-path==0.4.1
anyio==4.0.0
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.3.0
astroid==3.0.1
astropy==5.3
asttokens==2.2.1
astunparse==1.6.3
async-lru==2.0.4
atomicwrites==1.4.1
attrs==23.1.0
auto-py-to-exe==2.42.0
autopep8==2.0.4
Babel==2.13.1
backcall==0.2.0
bcrypt==4.0.1
beartype==0.17.2
beautifulsoup4==4.12.2
binaryornot==0.4.4
black==23.11.0
bleach==6.1.0
bottle==0.12.25
bottle-websocket==0.2.9
cachetools==5.3.3
celluloid==0.2.0
certifi==2023.5.7
cffi==1.16.0
chardet==5.2.0
charset-normalizer==3.1.0
click==8.1.3
cloudpickle==2.2.1
colorama==0.4.6
comm==0.1.3
contourpy==1.1.0
cookiecutter==2.4.0
cryptography==41.0.5
cycler==0.11.0
dask==2023.6.1
debugpy==1.6.7
decorator==5.1.1
defusedxml==0.7.1
diff-match-patch==20230430
dill==0.3.7
docstring-to-markdown==0.13
docutils==0.20.1
Eel==0.16.0
executing==1.2.0
fastjsonschema==2.18.1
ffmpeg-python==0.2.0
flake8==6.1.0
fonttools==4.40.0
fqdn==1.5.1
fsspec==2023.6.0
future==0.18.3
gevent==23.9.1
gevent-websocket==0.10.1
google-api-core==2.17.1
google-api-python-client==2.122.0
google-auth==2.28.2
google-auth-httplib2==0.2.0
googleapis-common-protos==1.63.0
graphviz==0.20.1
greenlet==3.0.1
grpcio==1.62.1
grpcio-health-checking==1.62.1
httplib2==0.22.0
idna==3.4
imageio==2.31.1
imagesize==1.4.1
importlib-metadata==6.7.0
inflection==0.5.1
intervaltree==3.1.0
ipykernel==6.23.3
ipynb-py-convert==0.4.6
ipython==8.14.0
isoduration==20.11.0
isort==5.12.0
jaraco.classes==3.3.0
jedi==0.18.2
jellyfish==1.0.1
Jinja2==3.1.2
joblib==1.2.0
json5==0.9.14
jsonpointer==2.4
jsonschema==4.19.2
jsonschema-specifications==2023.7.1
jupyter-events==0.9.0
jupyter-lsp==2.2.0
jupyter_client==8.3.0
jupyter_core==5.3.1
jupyter_server==2.10.0
jupyter_server_terminals==0.4.4
jupyterlab==4.0.8
jupyterlab-pygments==0.2.2
jupyterlab_server==2.25.1
keyring==24.2.0
kiwisolver==1.4.4
lazy_loader==0.2
locket==1.0.0
lxml==4.9.2
markdown-it-py==3.0.0
MarkupSafe==2.1.3
matplotlib==3.7.1
matplotlib-inline==0.1.6
mccabe==0.7.0
mdurl==0.1.2
mglearn==0.2.0
mistune==3.0.2
more-itertools==10.1.0
mypy-extensions==1.0.0
nbclient==0.9.0
nbconvert==7.11.0
nbformat==5.9.2
nest-asyncio==1.5.6
networkx==3.1
notebook==7.0.6
notebook_shim==0.2.3
numpy==1.25.0
numpydoc==1.6.0
obs-websocket-py==1.0
overrides==7.4.0
packaging==23.1
pandas==2.0.2
pandocfilters==1.5.0
paramiko==3.3.1
parso==0.8.3
partd==1.4.0
pathspec==0.11.2
patsy==0.5.3
pefile==2023.2.7
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.5.0
Pint==0.23
platformdirs==3.8.0
plotly==5.20.0
pluggy==1.3.0
pooch==1.7.0
prometheus-client==0.18.0
prompt-toolkit==3.0.38
protobuf==4.25.3
psutil==5.9.5
ptyprocess==0.7.0
pure-eval==0.2.2
pyamg==5.0.0
pyasn1==0.5.1
pyasn1-modules==0.3.0
pycodestyle==2.11.1
pycparser==2.21
pydocstyle==6.3.0
pyerfa==2.0.0.3
pyflakes==3.1.0
pyflowchart==0.3.1
Pygments==2.15.1
pyinstaller==6.2.0
pyinstaller-hooks-contrib==2023.10
pylint==3.0.2
pylint-venv==3.0.3
pyls-spyder==0.4.0
PyNaCl==1.5.0
pyparsing==3.0.9
PyQt5==5.15.10
PyQt5-Qt5==5.15.2
PyQt5-sip==12.13.0
PyQtWebEngine==5.15.6
PyQtWebEngine-Qt5==5.15.2
python-dateutil==2.8.2
python-json-logger==2.0.7
python-lsp-black==1.3.0
python-lsp-jsonrpc==1.1.2
python-lsp-server==1.9.0
python-pptx==0.6.21
python-slugify==8.0.1
pytoolconfig==1.2.6
pytz==2023.3
pyvista==0.43.4
PyWavelets==1.4.1
pywin32==306
pywin32-ctypes==0.2.2
pywinpty==2.0.12
PyYAML==6.0
pyzmq==25.1.0
QDarkStyle==3.2.1
qstylizer==0.2.2
QtAwesome==1.2.3
qtconsole==5.5.0
QtPy==2.4.1
referencing==0.30.2
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rich==13.6.0
rope==1.11.0
rpds-py==0.12.0
rsa==4.9
Rtree==1.1.0
scikit-image==0.21.0
scikit-learn==1.3.2
scipy==1.10.1
scooby==0.9.2
seaborn==0.12.2
Send2Trash==1.8.2
SimpleITK==2.2.1
six==1.16.0
sniffio==1.3.0
snowballstemmer==2.2.0
sortedcontainers==2.4.0
soupsieve==2.5
Sphinx==7.2.6
sphinxcontrib-applehelp==1.0.7
sphinxcontrib-devhelp==1.0.5
sphinxcontrib-htmlhelp==2.0.4
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.6
sphinxcontrib-serializinghtml==1.1.9
spyder==5.5.0
spyder-kernels==2.5.0
spyder-notebook==0.5.1
stack-data==0.6.2
statsmodels==0.14.0
tabulate==0.9.0
tenacity==8.2.3
terminado==0.18.0
text-unidecode==1.3
textdistance==4.6.0
threadpoolctl==3.1.0
three-merge==0.1.1
tifffile==2023.4.12
tinycss2==1.2.1
tomli==2.0.1
tomlkit==0.12.2
toolz==0.12.0
tornado==6.3.2
traitlets==5.9.0
types-python-dateutil==2.8.19.14
typing_extensions==4.10.0
tzdata==2023.3
ujson==5.8.0
uri-template==1.3.0
uritemplate==4.1.1
urllib3==2.0.3
vtk==9.3.0
watchdog==3.0.0
wcwidth==0.2.6
webcolors==1.13
webencodings==0.5.1
websocket-client==1.6.0
whatthepatch==1.0.5
whichcraft==0.6.1
XlsxWriter==3.1.2
yapf==0.40.2
yellowbrick==1.5
zipp==3.15.0
zope.event==5.0
zope.interface==6.1
RobPasMue commented 1 month ago

Hi @PatilSuneel! Thanks for opening this issue. Let me try it out locally and get back to you.

RobPasMue commented 1 month ago

Okay so... I came up with a minimal failing example. The problem is not with the looping itself but actually with case 50 for your above script. Try running this script on your side, you will see that it also fails:

from pint import Quantity

from ansys.geometry.core import launch_modeler
from ansys.geometry.core.designer import Design
from ansys.geometry.core.math import Point2D
from ansys.geometry.core.misc import UNITS, Distance
from ansys.geometry.core.sketch import Sketch

modeler = launch_modeler(mode="spaceclaim")
outer_hole_radius = Distance(0.5, UNITS.m)
design = modeler.create_design("ExtrudedPlate")

def extrude_body(outer_hole_radius: Distance, design: Design, i: int):

    originX = i * 8
    oroginY = i * 10
    point1X = originX - 4
    point1Y = oroginY + 5
    point2X = originX + 4
    point3Y = oroginY - 5
    center1X = originX + 3
    center1Y = oroginY + 4
    center2X = originX - 3
    center2Y = oroginY - 4
    sketch = Sketch()
    (
        sketch.segment(
            start=Point2D([point1X, point1Y], unit=UNITS.m),
            end=Point2D([point2X, point1Y], unit=UNITS.m),
        )
        .segment_to_point(end=Point2D([point2X, point3Y], unit=UNITS.m))
        .segment_to_point(end=Point2D([point1X, point3Y], unit=UNITS.m))
        .segment_to_point(end=Point2D([point1X, point1Y], unit=UNITS.m))
        .box(
            center=Point2D([originX, oroginY], unit=UNITS.m),
            width=Distance(3, UNITS.m),
            height=Distance(3, UNITS.m),
        )
        .circle(center=Point2D([center1X, center1Y], unit=UNITS.m), radius=outer_hole_radius)
        .circle(center=Point2D([center2X, center2Y], unit=UNITS.m), radius=outer_hole_radius)
        .circle(center=Point2D([center2X, center1Y], unit=UNITS.m), radius=outer_hole_radius)
        .circle(center=Point2D([center1X, center2Y], unit=UNITS.m), radius=outer_hole_radius)
    )
    body = design.extrude_sketch(f"PlateLayer{i}", sketch, Quantity(2, UNITS.m))
    design.create_named_selection(f"Plate{i}", bodies=[body])

# Body 51 or i == 50
i = 50
extrude_body(outer_hole_radius, design, i)

modeler.close()

Continuing to investigate!

RobPasMue commented 1 month ago

Hi @PatilSuneel - so together with @b-matteo we identified the issue. The SpaceClaim design area is not infinite and with your current loop and dimensions you were going out of bounds. Bear in mind that the default units of SpaceClaim are mm and your bodies are being displaced in the range of m per iteration, leading to very big designs in extent.

I propose you do your test with the following script in which I changed the units to be cm for your bodies. You can see that instead of having to put UNITS.xx everywhere, I just changed the Python library's default unit of length to cm (saving some coding time).

Try running it yourself, this should work like a charm.

I also refactored the code so that the sketch creation, extrusion and named selection creation all happens in one atomic function:

import time

from ansys.geometry.core import launch_modeler
from ansys.geometry.core.designer import Design
from ansys.geometry.core.math import Point2D
from ansys.geometry.core.misc import UNITS, Distance, DEFAULT_UNITS
from ansys.geometry.core.sketch import Sketch

# Change default length units to cm
DEFAULT_UNITS.LENGTH = UNITS.cm

# Launch modeler
modeler = launch_modeler(mode="spaceclaim")

# Create a new design
design = modeler.create_design("ExtrudedPlate")

# Define the outer hole radius
outer_hole_radius = Distance(0.5)

def extrude_and_named_selection(outer_hole_radius: Distance, design: Design, i: int):

    originX = i * 8
    originY = i * 10
    point1X = originX - 4
    point1Y = originY + 5
    point2X = originX + 4
    point3Y = originY - 5
    center1X = originX + 3
    center1Y = originY + 4
    center2X = originX - 3
    center2Y = originY - 4
    sketch = Sketch()
    (
        sketch.segment(
            start=Point2D([point1X, point1Y]),
            end=Point2D([point2X, point1Y]),
        )
        .segment_to_point(end=Point2D([point2X, point3Y]))
        .segment_to_point(end=Point2D([point1X, point3Y]))
        .segment_to_point(end=Point2D([point1X, point1Y]))
        .box(
            center=Point2D([originX, originY]),
            width=Distance(3),
            height=Distance(3),
        )
        .circle(center=Point2D([center1X, center1Y]), radius=outer_hole_radius)
        .circle(center=Point2D([center2X, center2Y]), radius=outer_hole_radius)
        .circle(center=Point2D([center2X, center1Y]), radius=outer_hole_radius)
        .circle(center=Point2D([center1X, center2Y]), radius=outer_hole_radius)
    )
    body = design.extrude_sketch(f"PlateLayer{i}", sketch, Distance(2))
    design.create_named_selection(f"Plate{i}", bodies=[body])

# Time the execution - START
time_start = time.time()

for i in range(100):
    extrude_and_named_selection(outer_hole_radius, design, i)

time_end = time.time()
# Time the execution - END

print(f"Time taken: {time_end - time_start:.2f} seconds")

# Close the modeler
modeler.close()
PatilSuneel commented 1 month ago

Thanks for identifying the issue, I will try it out.

RobPasMue commented 1 month ago

If it works, feel free to close the issue. Otherwise post your findings here and we will look into it! =) Thanks @PatilSuneel

PatilSuneel commented 1 month ago

Yes it is working like a charm... Thanks a lot

RobPasMue commented 1 month ago

Nice! Thanks for the feedback. Closing the issue.