Skip to content

Commit

Permalink
Merge branch 'master' into feat/add-docbuild-tox-environment
Browse files Browse the repository at this point in the history
  • Loading branch information
moe-ad authored Jan 14, 2025
2 parents bff1de1 + 5db303f commit 2a3060c
Show file tree
Hide file tree
Showing 16 changed files with 153 additions and 70 deletions.
3 changes: 2 additions & 1 deletion .ci/build_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"win": "win_amd64",
"manylinux1": "manylinux1_x86_64",
"manylinux_2_17": "manylinux_2_17_x86_64",
"linux": "manylinux_2_17_x86_64", # Accommodate tox.ini platform substitutions
# Accommodate tox.ini automatic platform substitutions
"linux": "manylinux_2_17_x86_64",
"win32": "win_amd64",
"darwin": "any",
}
Expand Down
2 changes: 1 addition & 1 deletion requirements/requirements_install.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
importlib-metadata==8.5.0
numpy==2.1.3
numpy==2.2.1
packaging==24.2
psutil==6.1.1
tqdm==4.67.1
3 changes: 1 addition & 2 deletions src/ansys/dpf/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import pkg_resources

try:
import importlib.metadata as importlib_metadata
Expand All @@ -25,7 +24,7 @@
except: # pragma: no cover
pass

installed = [d.project_name for d in pkg_resources.working_set]
installed = [d.metadata["Name"] for d in importlib_metadata.distributions()]
check_for = ["ansys-dpf-gatebin", "ansys-dpf-gate", "ansys-grpc-dpf"]
if any([c in installed for c in check_for]):
raise ImportError(f"Error during import of ansys-dpf-core:\n"
Expand Down
35 changes: 11 additions & 24 deletions src/ansys/dpf/core/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,23 @@

"""Version for ansys-dpf-core."""

from packaging.version import parse as parse_version

# Minimal DPF server version supported
min_server_version = "4.0"


class ServerToAnsysVersion:
legacy_version_map = {
"1.0": "2021R1",
"2.0": "2021R2",
"3.0": "2022R1",
"4.0": "2022R2",
"5.0": "2023R1",
"6.0": "2023R2",
"6.1": "2023R2",
"6.2": "2023R2",
"7.0": "2024R1",
"7.1": "2024R1",
"8.0": "2024R2",
"8.1": "2024R2",
"8.2": "2024R2",
"9.0": "2025R1",
"9.1": "2025R1",
"10.0": "2025R2",
}

def __getitem__(self, item):
if len(item) == 3:
return self.legacy_version_map[item]
else:
split = item.split(".")
return split[0] + "R" + split[1]
version = parse_version(item)
# The current DPF versioning scheme is MAJOR.MINOR.PATCH
# Compute release version equivalent (YEAR+'R'+REVISION)
# The revision is 'R1' for any odd major DPF version, 'R2' for even major versions.
ansys_revision = 2 - version.major % 2
# The year is 2021 for DPF 1.0, and bumped every two releases.
ansys_year = 2020 + version.major // 2 + version.major % 2
# Return the corresponding Ansys release
return f"{ansys_year}R{ansys_revision}"


server_to_ansys_version = ServerToAnsysVersion()
2 changes: 1 addition & 1 deletion src/ansys/dpf/core/mesh_scoping_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,4 @@ def named_selection_scoping(
A scoping containing the IDs of the entities in the named selection.
The location depends on the type of entities targeted by the named selection.
"""
return model.metadata.named_selection(named_selection_name)
return model.metadata.named_selection(named_selection=named_selection_name, server=server)
33 changes: 26 additions & 7 deletions src/ansys/dpf/core/meshed_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@

"""MeshedRegion."""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING: # pragma: nocover
from ansys.dpf.core.server_types import AnyServerType
from ansys.dpf.core.scoping import Scoping

import traceback
import warnings

Expand Down Expand Up @@ -378,36 +386,47 @@ def _get_available_named_selections(self):
named_selections.append(self._api.meshed_region_get_named_selection_name(self, index))
return named_selections

def named_selection(self, named_selection):
"""
Scoping containing the list of nodes or elements in the named selection.
def named_selection(
self,
named_selection: str,
server: AnyServerType = None,
) -> Scoping:
"""Scoping containing the list of nodes or elements in the named selection.
Parameters
----------
named_selection : str
named_selection:
Name of the named selection.
server:
Server on which to create the scoping if different from the server of the model.
Returns
-------
named_selection : Scoping
named_selection:
A scoping containing the IDs of the entities in the named selection.
The location depends on the type of entities targeted by the named selection.
"""
if server_meet_version("2.1", self._server):
out = self._api.meshed_region_get_named_selection_scoping(self, named_selection)
return scoping.Scoping(scoping=out, server=self._server)
out_scoping = scoping.Scoping(scoping=out, server=self._server)
else:
if hasattr(self, "_stream_provider"):
from ansys.dpf.core.dpf_operator import Operator

op = Operator("scoping_provider_by_ns", server=self._server)
op.connect(1, named_selection)
op.connect(3, self._stream_provider, 0)
return op.get_output(0, types.scoping)
out_scoping = op.get_output(0, types.scoping)
else:
raise Exception(
"Getting a named selection from a meshed region is "
"only implemented for meshed region created from a "
"model for server version 2.0. Please update your server."
)
if server:
# Copy the scoping to another server
out_scoping = out_scoping.deep_copy(server=server)
return out_scoping

@version_requires("3.0")
def set_named_selection_scoping(self, named_selection_name, scoping):
Expand Down
22 changes: 17 additions & 5 deletions src/ansys/dpf/core/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING: # pragma: nocover
from ansys.dpf.core.server_types import AnyServerType
from ansys.dpf.core.scoping import Scoping

from ansys import dpf
from ansys.dpf.core import Operator
from ansys.dpf.core.common import types
Expand Down Expand Up @@ -587,19 +595,23 @@ def available_named_selections(self):
"""
return self.meshed_region.available_named_selections

def named_selection(self, named_selection):
def named_selection(self, named_selection: str, server: AnyServerType = None) -> Scoping:
"""Scoping containing the list of nodes or elements in the named selection.
Parameters
----------
named_selection : str
name of the named selection
named_selection:
Name of the named selection.
server:
Server on which to create the scoping if different from the server of the model.
Returns
-------
named_selection : :class:`ansys.dpf.core.scoping.Scoping`
named_selection:
A scoping containing the IDs of the entities in the named selection.
The location depends on the type of entities targeted by the named selection.
"""
return self.meshed_region.named_selection(named_selection)
return self.meshed_region.named_selection(named_selection=named_selection, server=server)

def _build_connector(self):
return DataSourcesOrStreamsConnector(self)
43 changes: 28 additions & 15 deletions src/ansys/dpf/gate/load_api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import os
import subprocess # nosec B404
import sys

import packaging.version
import pkg_resources
import importlib
try:
import importlib.metadata as importlib_metadata
except ImportError: # Python < 3.10 (backport)
import importlib_metadata as importlib_metadata
from ansys.dpf.gate.generated import capi
from ansys.dpf.gate import utils, errors
from ansys.dpf.gate._version import __ansys_version__
Expand Down Expand Up @@ -74,19 +79,27 @@ def _find_latest_ansys_versions():

def _paths_to_dpf_server_library_installs() -> dict:
path_per_version = {}
installed_packages = pkg_resources.working_set
for i in installed_packages:
if "ansys-dpf-server" in i.key:
file_name = pkg_resources.to_filename(i.project_name.replace("ansys-dpf-", ""))
try:
module = importlib.import_module("ansys.dpf." + file_name)
path_per_version[
packaging.version.parse(module.__version__)
] = module.__path__[0]
except ModuleNotFoundError:
pass
except AttributeError:
pass
for d in importlib_metadata.distributions():
distribution_name = d.metadata["Name"]
if "ansys-dpf-server" in distribution_name:
# Cannot use the distribution.files() as those only list the files in the site-packages,
# which for editable installations does not necessarily give the actual location of the
# source files. It may rely on a Finder, which has to run.
# The most robust way of resolving the location is to let the import machinery do its
# job, using importlib.import_module. We do not want however to actually import the
# server libraries found, which is why we do it in a subprocess.
package_path = subprocess.check_output( # nosec B603
args=[
sys.executable,
"-c",
f"""import importlib
print(importlib.import_module('ansys.dpf.server{distribution_name[16:]}'.replace('-', '_')).__path__[0])""",
],
text=True,
).rstrip()
path_per_version[
packaging.version.parse(d.version)
] = package_path
return path_per_version


Expand Down
Binary file modified src/ansys/dpf/gatebin/Ans.Dpf.GrpcClient.dll
Binary file not shown.
Binary file modified src/ansys/dpf/gatebin/DPFClientAPI.dll
Binary file not shown.
Binary file modified src/ansys/dpf/gatebin/libAns.Dpf.GrpcClient.so
Binary file not shown.
Binary file modified src/ansys/dpf/gatebin/libDPFClientAPI.so
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/test_checkversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ def test_version():
from ansys.dpf.core._version import server_to_ansys_version

assert server_to_ansys_version["1.0"] == "2021R1"
assert server_to_ansys_version["2099.9"] == "2099R9"
assert server_to_ansys_version["10.0.12"] == "2025R2"
11 changes: 11 additions & 0 deletions tests/test_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
from ansys.dpf.core import fields_container_factory
from ansys.dpf.core import fields_factory
from ansys.dpf.core import mesh_scoping_factory
from ansys.dpf.core import server
from ansys.dpf.core import server_factory
from ansys.dpf.core import time_freq_scoping_factory
from ansys.dpf.core.common import locations

Expand Down Expand Up @@ -297,3 +299,12 @@ def test_named_selection_scoping(model_with_ns):
scop = mesh_scoping_factory.named_selection_scoping("SELECTION", model)
assert scop is not None
assert len(scop.ids) != 0


def test_named_selection_scoping_with_deepcopy(model_with_ns):
model = Model(model_with_ns)
server_2 = server.start_local_server(config=server_factory.AvailableServerConfigs.GrpcServer)
scop = mesh_scoping_factory.named_selection_scoping("SELECTION", model, server_2)
assert scop is not None
assert len(scop.ids) != 0
assert scop._server == server_2
41 changes: 41 additions & 0 deletions tests/test_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (C) 2020 - 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
# 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 pytest

from ansys.dpf.core._version import server_to_ansys_version


@pytest.mark.parametrize(
"server_version,ansys_version",
[
# Current DPF versioning
("1.0", "2021R1"),
("2.0", "2021R2"),
("2.1", "2021R2"),
("3.0", "2022R1"),
("2023.0", "3032R1"),
("2023.1.12", "3032R1"),
],
)
def test_server_to_ansys_version(server_version, ansys_version):
assert server_to_ansys_version[server_version] == ansys_version
26 changes: 13 additions & 13 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# This is work in progress, testing workflow in local/CI is gradually being transferred to tox

# Usage instructions:

# `tox` will run all tests sequentially, `tox --parallel` will run all tests in parallel (much faster).
# Run specific selection of tests with `tox -e pretest,<list-of-tests>,posttest` e.g., `tox -e pretest,test-api,test-launcher,posttest`
# `--parallel` flag can be passed when running specific selections.

# For packaging, build wheels for specific platform with `tox -e build-wheel -- <platform_name>`.
# If `tox -e build-wheel` is run without passing a platform, tox will automatically build the ffl wheels based on the operating system
# on which it is executing: windows -> "win_amd64", linux -> "manylinux_2_17_x86_64", mac -> "any"

[tox]
description = Default tox environment list and core configurations

# List all tests to run in parallel or sequential mode here
# So invocation can be specified as `tox`/`tox --parallel` to run all tests in sequential/parallel mode
envlist = pretest,test-{api,launcher,server,local_server,multi_server,remote_workflow,remote_operator,workflow,service,operators},posttest

isolated_build_env = build
Expand All @@ -24,17 +27,6 @@ pass_env =
ANSYSLMD_LICENSE_FILE
AWP_ROOT242

package = external # To allow custom wheel builds

[testenv:build_external]
description = Environment for custom build of package wheels, solves PyDPF custom wheel building requirement

package_glob = {toxinidir}{/}dist{/}ansys_dpf_core*

# {on_platform} substitution to automatically detect os type.
commands =
python .ci/build_wheel.py -p {on_platform} -w

[testenv:pretest]
description = Environment to kill servers and organize test files prior to testing

Expand Down Expand Up @@ -160,3 +152,11 @@ commands =
import os, shutil, glob; os.makedirs('build/html/_images', exist_ok=True); \
[(shutil.copy(src, 'build/html/_images') if os.path.exists(src) else print(f'Source not found: {src}')) for src in \
glob.glob('{env:SOURCE_DIR}/examples/04-advanced/02-volume_averaged_stress/*') + glob.glob('{env:SOURCE_DIR}/examples/12-fluids/02-fluids_results/*')]"

[testenv:build-wheel]
description = Environment for custom build of package wheels

skip_install = True

commands =
python .ci/build_wheel.py -p {posargs:{on_platform}} -w

0 comments on commit 2a3060c

Please sign in to comment.