Skip to content
Open
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
1 change: 1 addition & 0 deletions doc/changelog.d/1255.miscellaneous.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Changes from ado sp1 branch
6 changes: 3 additions & 3 deletions src/ansys/meshing/prime/core/dynaexportutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2584,9 +2584,9 @@ def _map_zone_type_with_material(self):
if zone_details['Material'] in self._material_assignedTo_zones:
try:
zoneId = zone_details['id']
self._material_assignedTo_zones[zone_details['Material']].append(
zoneId
)
self._material_assignedTo_zones[
zone_details['Material']
].append(zoneId)
except:
self._logger.warning(f'Zone {zone} does not have id for it ')
pass
Expand Down
7 changes: 5 additions & 2 deletions src/ansys/meshing/prime/core/fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,11 @@ def export_lsdyna_keyword_file(
# part_id = self._model.parts[0].id
args = {"partId": 0}
command_name = "PrimeMesh::FileIO/GetAbaqusSimulationData"
sim_data = self._comm.serve(self._model, command_name, self._object_id, args=args)
sim_data = json.loads(sim_data)
sim_data_str = self._comm.serve(self._model, command_name, self._object_id, args=args)
if not sim_data_str or sim_data_str.strip() == "":
sim_data = None
else:
sim_data = json.loads(sim_data_str)
if sim_data is not None:
mp = dynaexportutils.MaterialProcessor(self._model, sim_data)
all_mat_cmds = mp.get_all_material_commands()
Expand Down
817 changes: 594 additions & 223 deletions src/ansys/meshing/prime/core/mapdlcdbexportutils.py

Large diffs are not rendered by default.

102 changes: 99 additions & 3 deletions src/ansys/meshing/prime/internals/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

import logging
import os
import platform
import time
import json
from pathlib import Path
from typing import Optional

Expand All @@ -35,7 +38,6 @@

__all__ = ['Client']


class Client(object):
"""Provides the ``Client`` class for PyPrimeMesh.

Expand Down Expand Up @@ -79,7 +81,8 @@ def __init__(
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("Refer the documentation for your installed product for additional information.")
Expand Down Expand Up @@ -158,6 +161,13 @@ def __init__(
logging.getLogger('PyPrimeMesh').error(
f'Failed to load prime_communicator with message: {err.msg}'
)
model = self.model
results = json.loads(model._comm.serve(model,
"PrimeMesh::Model/GetServerProcessInformation",
model._object_id,
args={}))
#
self._generate_server_term_scripts(results['hostNames'], results['pids'])

@property
def model(self):
Expand Down Expand Up @@ -186,6 +196,85 @@ def run_on_server(self, recipe: str):
result = self._comm.run_on_server(recipe)
return result['Results']

def _generate_server_term_scripts(self, hostNames, pids):
"""Generates shell and batch scripts to kill processes on specified hostnames.

Parameters
----------
hostnames (list of str): A list of hostnames where processes are running.
'localhost' or the current machine's name implies local.
pids (list of int): A list of process IDs corresponding to the hostnames.

Returns
-------
tuple: A tuple containing the filenames of the generated shell and batch scripts.
(shell_script_filename, batch_script_filename)
"""
# --- Input Validation ---
current_hostname = platform.node().lower()
if not hostNames or not pids or len(hostNames) != len(pids):
logging.getLogger('PyPrimeMesh').info("Found invalid hostnames and PIDs," \
"skipped server post kill scripts creation.")
return None, None
if os.name == "nt":
script_content = "@echo off\n"
script_content += "REM This script kills the Ansys Prime Server processes.\n"
else:
script_content = "#!/bin/bash\n"
script_content += "# This script kills the Ansys Prime Server processes.\n"
#
for hostname, pid in zip(hostNames, pids):
if hostname.lower() == 'localhost' or hostname.lower() == current_hostname:
if os.name == "nt":
## Windows batch script
script_content += f":: Check and kill PID {pid}\n"
script_content += f"tasklist /FI \"PID eq {pid}\" | find \"{pid}\" >nul\n"
script_content += f"if %errorlevel%==0 (\n"
script_content += f" echo Attempting to killing process {pid}...\n"
script_content += f" taskkill /F /PID {pid} >nul 2>&1\n"
script_content += f" if %errorlevel%==0 (\n"
script_content += f" echo Process {pid} killed successfully.\n"
script_content += f" ) else (\n"
script_content += f" echo Server process {pid} still running.\n"
script_content += f" )\n"
script_content += f")\n\n"
else:
## Unix-like shell script
script_content += f"# --- Checking and killing PID {pid} ---\n"
script_content += f'if ps -p {pid} > /dev/null; then\n'
script_content += f' echo "Attempting to kill process {pid}..."\n'
script_content += f' kill -9 {pid} > /dev/null 2>&1\n'
script_content += f' if [ $? -eq 0 ]; then\n'
script_content += f' echo "Process {pid} killed successfully."\n'
script_content += f' else\n'
script_content += f' echo "Server process {pid} still running."\n'
script_content += f' fi\n'
script_content += f'\n\nfi\n'
# Timestamp and process information
current_dir = Path(os.getcwd())
script_base_name = f"cleanup-prime-{current_hostname}-{os.getpid()}-{int(time.time())}"
#logging.getLogger('PyPrimeMesh').info(f"Current directory: {current_dir}")
self._cleanup_script_path = (current_dir / f"{script_base_name}").with_suffix(
'.bat' if os.name == "nt" else '.sh')
# --- File Writing ---
try:
# Write the shell script
if os.name == "nt":
with open(self._cleanup_script_path, 'w') as batch_file:
batch_file.write(script_content)
else:
with open(self._cleanup_script_path, 'w') as shell_file:
shell_file.write(script_content)
# Make the shell script executable on Unix-like systems
os.chmod(self._cleanup_script_path, 0o755)
return None, None
except IOError as e:
logging.getLogger('PyPrimeMesh').info(f"Error writing script files: {e}")
return None, None
except Exception as e:
logging.getLogger('PyPrimeMesh').info(f"An unexpected error occurred: {e}")
return None, None

def exit(self):
"""Close the connection with the server.

Expand All @@ -207,14 +296,21 @@ def exit(self):
self._comm = None
if self._process is not None:
assert self._local == False
terminate_process(self._process)
terminate_process(self._process, self._cleanup_script_path)
self._cleanup_script_path = None
self._process = None
if config.using_container():
container_name = getattr(self, 'container_name', None)
if container_name:
utils.stop_prime_github_container(container_name)
elif config.has_pim():
self.remote_instance.delete()
self.pim_client.close()
#
if self._cleanup_script_path is not None:
shell_script = self._cleanup_script_path.with_suffix(".sh")
batch_script = self._cleanup_script_path.with_suffix(".bat")
utils.cleanup_script_files(shell_script, batch_script)
clear_examples = bool(int(os.environ.get('PYPRIMEMESH_CLEAR_EXAMPLES', '1')))
if clear_examples:
download_manager = examples.DownloadManager()
Expand Down
3 changes: 0 additions & 3 deletions src/ansys/meshing/prime/internals/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,10 @@

from ansys.meshing.prime.internals.logger import PrimeLogger


class ConnectionType(Enum):
"""Type of connection to be established.

Default is secure connection.
"""

GRPC_SECURE = 1
"""Secure connection.
This option ensures secure connection between client and server.
Expand Down
2 changes: 0 additions & 2 deletions src/ansys/meshing/prime/internals/cyberchannel.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,12 @@

logger = logging.getLogger(__name__)


@dataclass
class CertificateFiles:
cert_file: str | Path | None = None
key_file: str | Path | None = None
ca_file: str | Path | None = None


def create_channel(
transport_mode: str,
host: str | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/ansys/meshing/prime/internals/grpc_communicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@


def get_secure_channel(client_certs_dir: str, server_host: str, server_port: int):
"""Create a secure gRPC channel using the provided TLS files.
"""Creates a secure gRPC channel using the provided TLS files.

Parameters
----------
Expand Down
59 changes: 58 additions & 1 deletion src/ansys/meshing/prime/internals/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,34 @@ def get_child_processes(process):
return children


def terminate_process(process: subprocess):
def cleanup_script_files(shell_script, batch_script) -> bool:
"""Clean up script files used for process termination.

Parameters
----------
shell_script : Path
Path to the shell script file.
batch_script : Path
Path to the batch script file.

Returns
-------
bool
True if all files were successfully deleted or didn't exist, False if any deletion failed
"""
success = True
for script_file in [shell_script, batch_script]:
try:
if os.path.exists(script_file):
os.remove(script_file)
except Exception as e:
logging.getLogger('PyPrimeMesh').warning(
f"Could not delete script file {script_file}: {e}"
)
success = False
return success

def terminate_process(process: subprocess, unique_id: str = None):
"""Terminates a process.

Parameters
Expand All @@ -120,6 +147,36 @@ def terminate_process(process: subprocess):
for child in get_child_processes(process.pid):
os.kill(child, signal.SIGTERM)

shell_script = unique_id.with_suffix(".sh")
batch_script = unique_id.with_suffix(".bat")

if os.name == "nt": # Windows
if os.path.exists(batch_script):
try:
subprocess.run([batch_script], check=True, shell=True)
logging.getLogger('PyPrimeMesh').info("Prime server process successfully terminated.")
except subprocess.CalledProcessError as e:
logging.getLogger('PyPrimeMesh').error(f"Error: Failed to terminate prime server process. Details: {e}")
except FileNotFoundError:
logging.getLogger('PyPrimeMesh').warning(f"Warning: Prime server process might still be running.")
else:
logging.getLogger('PyPrimeMesh').warning(f"Warning: Cannot terminate prime server on Windows.")
else: # Unix-like (Linux, etc..)
if os.path.exists(shell_script):
try:
if not os.access(shell_script, os.X_OK):
os.chmod(shell_script, 0o755)
subprocess.run(["bash", shell_script], check=True)
logging.getLogger('PyPrimeMesh').info("Prime server process successfully terminated.")
except subprocess.CalledProcessError as e:
logging.getLogger('PyPrimeMesh').error(f"Error: Failed to terminate prime server process. Details: {e}")
except FileNotFoundError:
logging.getLogger('PyPrimeMesh').warning(f"Warning: Prime server process might still be running.")
else:
logging.getLogger('PyPrimeMesh').warning(f"Warning: Cannot terminate prime server on Unix-like system.")

cleanup_successful = cleanup_script_files(shell_script, batch_script)

if process.stdin is not None:
process.stdin.close()
if process.stdout is not None:
Expand Down
Loading