Skip to content
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
11 changes: 9 additions & 2 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ on:

env:
DOCKER_IMAGE_NAME: ghcr.io/ansys/prime
DOCKER_IMAGE_TAG: '25.1.2'
MAIN_PYTHON_VERSION: '3.12'
DOCKER_IMAGE_TAG: '25.1.4'
MAIN_PYTHON_VERSION: '3.10'
PACKAGE_NAME: 'ansys-meshing-prime'
PACKAGE_NAMESPACE: 'ansys.meshing.prime'
DOCUMENTATION_CNAME: 'prime.docs.pyansys.com'
Expand Down Expand Up @@ -102,6 +102,9 @@ jobs:
- name: Setup headless display
uses: pyvista/setup-headless-display-action@v4

- name: Install compatible pyvista and vtk
run: pip install "pyvista>=0.42" vtk

- name: "Run Ansys documentation building action"
uses: ansys/actions/doc-build@v9
with:
Expand All @@ -114,6 +117,7 @@ jobs:
PYPRIMEMESH_SPHINX_BUILD: 1
PYPRIMEMESH_IMAGE_TAG: ${{ env.DOCKER_IMAGE_TAG }}
ANSYSLMD_LICENSE_FILE: '1055@${{ secrets.LICENSE_SERVER }}'
PRIME_MODE: "GRPC_INSECURE"

testing:
name: Run Unit Tests
Expand All @@ -137,6 +141,9 @@ jobs:
path: tests/graphics/image_cache
key: pyvista-image-cache-${{ runner.os }}-v-${{ env.RESET_IMAGE_CACHE }}-${{ hashFiles('pyproject.toml') }}
restore-keys: pyvista-image-cache-${{ runner.os }}-v-${{ env.RESET_IMAGE_CACHE }}

- name: Install compatible pyvista and vtk
run: pip install "pyvista>=0.42" vtk

- name: "Run pytest"
uses: ansys/actions/tests-pytest@v9
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.d/1188.documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
updates to release/0.8
1 change: 1 addition & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from ansys.meshing.prime import __version__

os.environ["PRIME_MODE"] = "GRPC_INSECURE"
viz_interface.DOCUMENTATION_BUILD = True

# Project information
Expand Down
54 changes: 54 additions & 0 deletions doc/source/getting_started/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,60 @@ To install a basic version of the client, use this command instead:

pip install -e .

Connecting through gRPC
-----------------------

PyPrimeMesh uses gRPC to provide secure communications between client and server.
When you run the client and server on the same machine:

- For Linux OS, PyPrimeMesh uses UDS (Unix Domain Socket) for communications.

- For Windows OS, PyPrimeMesh uses interceptor to validate gRPC connections,
ensures the client is running on the same Windows user account as the server and authenticates the client.

When you launch PyPrimeMesh, gRPC establishes a connection between the Client and Server
through the secure option. **Secure** is the default option when you use launch_prime().
You should always use the **Secure** option to establish a secured connection between the client and server.

When you want to make an insecure connection between the client and server,
you may need to specify the connection type as follows:

.. code-block:: python

client = prime.launch_prime(
connection_type=prime.internals.config.ConnectionType.GRPC_INSECURE
)

.. note::
Insecure option is not recommended.

Connect securely using certificates
--------------------------------------

PyPrimeMesh offers secure connection using certificates. For secure connection with mutual TLS (mTLS), you may pass a client certificate directory and server certificate directory using client_certs_dir and server_certs_dir respectively to launch_prime().

client_certs_dir should contain the following files:

- client.crt

- client.key

- ca.crt

server_certs_dir should contain the following files:

- server.crt

- server.key

- ca.crt

.. note::
- Ensure that ca.crt file is the same for the client and the server. You should not modify the
file names in the client_certs_dir and server_certs_dir respectively.

- The path of input the files must be the same for server and client and should be on the shared network.
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammatical error: 'path of input the files' should be 'path of the input files' or 'path to the input files'.

Suggested change
- The path of input the files must be the same for server and client and should be on the shared network.
- The path to the input files must be the same for server and client and should be on the shared network.

Copilot uses AI. Check for mistakes.


Dependencies
------------
Expand Down
1 change: 1 addition & 0 deletions doc/styles/config/vocabularies/ANSYS/accept.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ automesh
Boolean
BRep
CAD
client_certs_dir
conformally
[Dd]efeature
defeaturing
Expand Down
30 changes: 28 additions & 2 deletions docker/build_docker_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,38 @@ def create_docker_image(dest_package_path):

# Build the docker image
print(">>> Building docker image. This might take some time...")
# Parse version from AWP_ROOT or command line argument
version = "latest" # default
if len(sys.argv) >= 3:
version = sys.argv[2]

# Get the script directory for robust file path handling
script_dir = os.path.dirname(os.path.abspath(__file__))
dockerfile_path = os.path.join(script_dir, "linux", "Dockerfile")

# Verify Dockerfile exists
if not os.path.exists(dockerfile_path):
print(f"XXXXXXX Dockerfile not found at {dockerfile_path}. Exiting process. XXXXXXX")
exit(1)

# Build Docker image with version tag
image_tag = f"ghcr.io/ansys/prime:{version}"
print(f">>> Building Docker image with tag: {image_tag}")

out = subprocess.run(
["docker", "build", "-f", "linux/Dockerfile", "-t", "ghcr.io/ansys/prime:latest", "."],
cwd=os.path.dirname(os.path.abspath(__file__)),
["docker", "build", "-f", dockerfile_path, "-t", image_tag, script_dir],
capture_output=True,
)

# Check if docker build was successful
if out.returncode != 0:
print("XXXXXXX Docker build failed. XXXXXXX")
print("STDOUT:", out.stdout.decode())
print("STDERR:", out.stderr.decode())
exit(1)
else:
print(f">>> Docker image built successfully with tag: {image_tag}")


# -------------------------------------------------------------------------------
#
Expand Down
2 changes: 1 addition & 1 deletion docker/linux/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ ENV AWP_ROOT251="/prime"
LABEL org.opencontainers.image.authors="ANSYS Inc."
LABEL org.opencontainers.image.vendor="ANSYS Inc."

ENTRYPOINT [ "/prime/meshing/Prime/runPrime.sh", "server", "--ip", "0.0.0.0" ]
ENTRYPOINT [ "/prime/meshing/Prime/runPrime.sh", "server", "--ip", "0.0.0.0" ]
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"

[project]
name = "ansys-meshing-prime"
version = "0.8.1"
version = "0.8.2"
description = "PyPrimeMesh is a Python client to Ansys Prime Server, which delivers core Ansys meshing technology."
readme = "README.md"
requires-python = ">=3.10,<4"
Expand Down Expand Up @@ -37,17 +37,17 @@ tests = [
"pytest-cov==6.1.1",
"pytest-pyvista==0.1.9",
"pytest-xvfb==3.1.1",
"pyvista[trame]==0.44.2"
"pyvista[trame]<=0.45.3"
]
doc = [
"ansys-sphinx-theme[autoapi]==1.4.2",
"ansys-tools-visualization-interface==0.8.3",
"ansys-tools-visualization-interface<=0.11.0",
"jupyter-sphinx==0.5.3",
"numpydoc==1.8.0",
"sphinx>=8.0.0,<8.2.0",
"sphinx_design==0.6.1",
"pyvista==0.44.2",
"sphinx-autodoc-typehints==3.1.0",
"sphinx-autodoc-typehints==3.0.1",
"sphinx-copybutton==0.5.2",
"sphinx-gallery==0.19.0",
"sphinx-notfound-page==1.1.0",
Expand Down
93 changes: 70 additions & 23 deletions src/ansys/meshing/prime/internals/client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright (C) 2024 - 2025 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
Expand All @@ -24,6 +23,7 @@

import logging
import os
from typing import Optional

import ansys.meshing.prime.examples as examples
import ansys.meshing.prime.internals.config as config
Expand All @@ -50,7 +50,8 @@ class Client(object):
Maximum time to wait for connection. The default is ``defaults.connection_timeout()``.
credentials : Any, optional
Credentials to connect to the server. The default is ``None``.

client_certs_dir : Optional[str]
Directory containing client certificates for mutual TLS.
Raises
------
ValueError
Expand All @@ -65,40 +66,88 @@ def __init__(
port: int = defaults.port(),
timeout: float = defaults.connection_timeout(),
credentials=None,
connection_type: config.ConnectionType = config.ConnectionType.GRPC_SECURE,
uds_file: Optional[str] = None,
client_certs_dir: Optional[str] = None,
**kwargs,
):
"""Initialize the client."""
self._default_model: Model = None
local = kwargs.get('local', False)
if local and server_process is not None:
raise ValueError('Local client cannot be instantiated with a server process')

if connection_type == config.ConnectionType.GRPC_INSECURE:
print("Warning (Client): Modification of these configurations is not recommended.")
print(
"Please see the documentation for your installed product for additional information"
)

self._local = local
self._process = server_process
self._comm = None
if not local:
try:
from ansys.meshing.prime.internals.grpc_communicator import (
GRPCCommunicator,
)
if (
connection_type == config.ConnectionType.GRPC_SECURE
or connection_type == config.ConnectionType.GRPC_INSECURE
):
try:
from ansys.meshing.prime.internals.grpc_communicator import (
GRPCCommunicator,
)

channel = kwargs.get('channel', None)
if channel is not None:
self._comm = GRPCCommunicator(channel=channel, timeout=timeout)
else:
self._comm = GRPCCommunicator(
ip=ip, port=port, timeout=timeout, credentials=credentials
channel = kwargs.get('channel', None)

if channel is not None:
self._comm = GRPCCommunicator(channel=channel, timeout=timeout)
else:
if (
os.name == 'nt'
or connection_type == config.ConnectionType.GRPC_INSECURE
):
if (
connection_type == config.ConnectionType.GRPC_INSECURE
and client_certs_dir is not None
):
print(
"Warning: Ignoring client certificate \
directory for insecure connections"
Comment on lines +113 to +114
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string continuation with backslash creates a multi-line string literal with embedded whitespace. Use implicit string concatenation with parentheses or a single-line string for cleaner code.

Suggested change
"Warning: Ignoring client certificate \
directory for insecure connections"
(
"Warning: Ignoring client certificate "
"directory for insecure connections"
)

Copilot uses AI. Check for mistakes.
)
client_certs_dir = None
self._comm = GRPCCommunicator(
ip=ip,
port=port,
timeout=timeout,
credentials=credentials,
client_certs_dir=client_certs_dir,
)
else:
if uds_file is None:
self._comm = GRPCCommunicator(
ip=ip,
port=port,
client_certs_dir=client_certs_dir,
timeout=timeout,
)
else:
self._comm = GRPCCommunicator(
uds_file=uds_file, timeout=timeout, credentials=credentials
)
setattr(self, 'port', port)
except ImportError as err:
logging.getLogger('PyPrimeMesh').error(
f'Failed to load grpc_communicator with message: {err.msg}'
)
setattr(self, 'port', port)
except ImportError as err:
logging.getLogger('PyPrimeMesh').error(
f'Failed to load grpc_communicator with message: {err.msg}'
)
raise
except ConnectionError:
self.exit()

logging.getLogger('PyPrimeMesh').error('Failed to connect to PRIME GRPC server')
raise
else:
logging.getLogger('PyPrimeMesh').error(f'Invalid server type: {connection_type}')
raise
except ConnectionError:
self.exit()

logging.getLogger('PyPrimeMesh').error('Failed to connect to PRIME GRPC server')
raise
else:
try:
from ansys.meshing.prime.internals.prime_communicator import (
Expand Down Expand Up @@ -161,14 +210,12 @@ def exit(self):
assert self._local == False
terminate_process(self._process)
self._process = None

if config.using_container():
container_name = getattr(self, 'container_name')
utils.stop_prime_github_container(container_name)
elif config.has_pim():
self.remote_instance.delete()
self.pim_client.close()

clear_examples = bool(int(os.environ.get('PYPRIMEMESH_CLEAR_EXAMPLES', '1')))
if clear_examples:
download_manager = examples.DownloadManager()
Expand Down
6 changes: 6 additions & 0 deletions src/ansys/meshing/prime/internals/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"""Configuration utility for PyPrimeMesh."""

from contextlib import contextmanager
from enum import Enum

__all__ = [
'enable_optimizing_numpy_arrays',
Expand All @@ -45,6 +46,11 @@
from ansys.meshing.prime.internals.logger import PrimeLogger


class ConnectionType(Enum):
GRPC_SECURE = (1,)
GRPC_INSECURE = (2,)
Comment on lines +50 to +51
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum values use single-element tuples (1,) and (2,) instead of simple integers. This is unnecessary and unconventional. Change to GRPC_SECURE = 1 and GRPC_INSECURE = 2 for clarity.

Suggested change
GRPC_SECURE = (1,)
GRPC_INSECURE = (2,)
GRPC_SECURE = 1
GRPC_INSECURE = 2

Copilot uses AI. Check for mistakes.


def _optimize_vectors():
"""Get the value of the flag for optimizing vectors."""
return __DEFAULT_USE_BINARY
Expand Down
Loading
Loading