Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ServerContext #606

Merged
merged 24 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from 18 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 ansys/dpf/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
from ansys.dpf.core import path_utilities
from ansys.dpf.core import settings
from ansys.dpf.core.server_factory import ServerConfig, AvailableServerConfigs
from ansys.dpf.core.server_context import apply_server_context, AvailableServerContexts

# for matplotlib
# solves "QApplication: invalid style override passed, ignoring it."
Expand Down
52 changes: 52 additions & 0 deletions ansys/dpf/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,58 @@ def __generate_code(TARGET_PATH, filename, name, symbol):
TARGET_PATH=LOCAL_PATH, filename=file_path, name=name, symbol=symbol
)

@version_requires("6.0")
def apply_context(self, context):
"""Defines the settings that will be used to load DPF's plugins.
A DPF xml file can be used to list the plugins and set up variables.

Parameters
----------
context : ServerContext
The context allows to choose which capabilities are available server side.

Notes
-----
Available with server's version starting at 6.0 (Ansys 2023R2).
"""
if not self._server().meet_version("6.0"):
raise errors.DpfVersionNotSupported("6.0")
if self._server().has_client():
error = self._api.data_processing_apply_context_on_client(
self._server().client, context.context_type.value, context.xml_path
)
else:
error = self._api.data_processing_apply_context(
context.context_type.value, context.xml_path
)

def initialize_with_context(self, context):
"""Defines the settings that will be used to initialize DPF.
A DPF xml file can be used to list the plugins and set up variables.

Parameters
----------
context : ServerContext
The context allows to choose which capabilities are available server side.

Notes
-----
Available with server's version starting at 4.0 (Ansys 2022R2) for InProcess Server
and starting at 6.0 (Ansys 2023R2) for Grpc Servers.
"""
if self._server().has_client():
if not self._server().meet_version("6.0"):
raise errors.DpfVersionNotSupported("6.0")
error = self._api.data_processing_initialize_with_context_on_client(
self._server().client, context.context_type.value, context.xml_path
)
else:
if not self._server().meet_version("4.0"):
raise errors.DpfVersionNotSupported("4.0")
error = self._api.data_processing_initialize_with_context(
context.context_type.value, context.xml_path
)

def get_runtime_client_config(self):
if self._server().has_client():
data_tree_tmp = (
Expand Down
98 changes: 98 additions & 0 deletions ansys/dpf/core/server_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
ServerContext
=============

Gives the ability to choose the context with which the server should be started.
The context allows to choose which capabilities are available.
"""
from enum import Enum


class EContextType(Enum):
pre_defined_environment = 0
"""DataProcessingCore.xml that is next to DataProcessingCore.dll/libDataProcessingCore.so will
be taken"""
premium = 1
"""Gets the Specific premium DataProcessingCore.xml."""
user_defined = 2
"""Load a user defined xml using its path."""
custom_defined = 3
"""Loads the xml named "DpfCustomDefined.xml" that the user can modify."""
entry = 4
"""Loads the minimum number of plugins for a basic usage."""


class ServerContext:
"""The context allows to choose which capabilities are available server side.

Parameters
----------
context_type : EContextType
Type of context.
xml_path : str, optional
Path to the xml to load.
"""
def __init__(self, context_type=EContextType.user_defined, xml_path=""):
Copy link
Contributor

Choose a reason for hiding this comment

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

@cbellot000 Why is the user_defined context the default one? I think this makes the xml_path non-optional in a way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PProfizi it's the default of this constructor (because you would only need to use the constructor directly when you want to speicify an xml), it doesn't make user defined the default context (which is SERVER_CONTEXT = AvailableServerContexts.entry)

self._context_type = context_type
self._xml_path = xml_path

@property
def context_type(self):
return self._context_type

@property
def xml_path(self):
return self._xml_path

def __str__(self):
return f"Server Context of type {self.context_type}" \
f" with {'no' if len(self.xml_path)==0 else ''} xml path" \
f"{'' if len(self.xml_path)==0 else ': ' + self.xml_path}"


class AvailableServerContexts:
pre_defined_environment = ServerContext(EContextType.pre_defined_environment)
"""DataProcessingCore.xml that is next to DataProcessingCore.dll/libDataProcessingCore.so will
be taken"""
premium = ServerContext(EContextType.premium)
"""Gets the Specific premium DataProcessingCore.xml to load most plugins with their
environments."""
custom_defined = ServerContext(EContextType.custom_defined)
"""Loads the xml named "DpfCustomDefined.xml" that the user can modify."""
entry = ServerContext(EContextType.entry)
"""Loads the minimum number of plugins for a basic usage. Is the default."""


SERVER_CONTEXT = AvailableServerContexts.entry


def apply_server_context(context=AvailableServerContexts.entry, server=None) -> None:
"""Allows to apply a context globally (if no server is specified) or to a
given server.
When called before any server is started, the context will be applied by default to any
new server.

The context allows to choose which capabilities are available server side.

Parameters
----------
context : ServerContext
Context to apply to the given server or to the newly started servers (when no server
is given).
server : server.DPFServer, optional
Server with channel connected to the remote or local instance. When
``None``, attempts to use the global server.

Notes
-----
Available with server's version starting at 6.0 (Ansys 2023R2).
"""
from ansys.dpf.core import SERVER
if server is None:
server = SERVER
global SERVER_CONTEXT
SERVER_CONTEXT = context
if server is not None:
from ansys.dpf.core.core import BaseService
base = BaseService(server, load_operators=False)
base.apply_context(context)
18 changes: 16 additions & 2 deletions ansys/dpf/core/server_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
server_to_ansys_version
)
from ansys.dpf.core.misc import __ansys_version__
from ansys.dpf.core import server_context
from ansys.dpf.gate import load_api, data_processing_grpcapi

import logging
Expand Down Expand Up @@ -613,6 +614,10 @@ def __init__(self,
self._create_shutdown_funcs()
self._check_first_call(num_connection_tryouts)
self.set_as_global(as_global=as_global)
try:
self._base_service.initialize_with_context(server_context.SERVER_CONTEXT)
except errors.DpfVersionNotSupported:
pass

def _check_first_call(self, num_connection_tryouts):
for i in range(num_connection_tryouts):
Expand Down Expand Up @@ -763,7 +768,6 @@ def __init__(self,
super().__init__(ansys_path=ansys_path, load_operators=load_operators)
# Load DataProcessingCore
from ansys.dpf.gate.utils import data_processing_core_load_api
from ansys.dpf.gate import data_processing_capi
name = "DataProcessingCore"
path = _get_dll_path(name, ansys_path)
try:
Expand All @@ -774,8 +778,14 @@ def __init__(self,
f"DPF directory not found at {os.path.dirname(path)}"
f"Unable to locate the following file: {path}")
raise e
data_processing_capi.DataProcessingCAPI.data_processing_initialize_with_context(1, None)
self.set_as_global(as_global=as_global)
try:
self._base_service.apply_context(server_context.SERVER_CONTEXT)
except errors.DpfVersionNotSupported:
self._base_service.initialize_with_context(
server_context.AvailableServerContexts.premium
Copy link
Contributor

Choose a reason for hiding this comment

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

@cbellot000 Why premium in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PProfizi premium was the old default (equivalent to standalone)

)
pass

@property
def version(self):
Expand Down Expand Up @@ -915,6 +925,10 @@ def __init__(

check_ansys_grpc_dpf_version(self, timeout)
self.set_as_global(as_global=as_global)
try:
self._base_service.initialize_with_context(server_context.SERVER_CONTEXT)
except errors.DpfVersionNotSupported:
pass

def _create_shutdown_funcs(self):
self._core_api = data_processing_grpcapi.DataProcessingGRPCAPI
Expand Down
3 changes: 2 additions & 1 deletion requirements/requirements_test.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pytest==7.2.0
pytest-cov==4.0.0
pytest-rerunfailures==10.2
pytest-order==1.0.1
pyvista==0.36.1
ansys-platform-instancemanagement==1.0.2
coverage==6.5.0
coverage==6.5.0
29 changes: 28 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ def cyclic_multistage():
return core.examples.download_multi_stage_cyclic_result()


SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_6_0 = meets_version(
get_server_version(core._global_server()), "6.0"
)
SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_5_0 = meets_version(
get_server_version(core._global_server()), "5.0"
)
Expand All @@ -198,6 +201,10 @@ def cyclic_multistage():
SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_3_0 = meets_version(
get_server_version(core._global_server()), "3.0"
)
SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_2_0 = meets_version(
Copy link
Contributor

Choose a reason for hiding this comment

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

@cbellot000 Is the minimum server version required 2.0 or 2.1?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's how it was used before

get_server_version(core._global_server()), "2.1"
)


IS_USING_GATEBIN = _try_use_gatebin()

Expand All @@ -210,7 +217,10 @@ def raises_for_servers_version_under(version):

def decorator(func):
@pytest.mark.xfail(
not meets_version(get_server_version(core._global_server()), version),
not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_3_0 if version == "3.0" else
not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_4_0 if version == "4.0" else
not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_5_0 if version == "5.0" else
not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_6_0 if version == "6.0" else True,
reason=f"Requires server version greater than or equal to {version}",
raises=core.errors.DpfVersionNotSupported,
)
Expand Down Expand Up @@ -294,6 +304,15 @@ def remote_config_server_type(request):
return request.param


@pytest.fixture(
scope="package",
params=configsserver_type,
ids=config_namesserver_type,
)
def config_server_type(request):
return request.param


configs_server_type_legacy_grpc, config_names_server_type_legacy_grpc = \
remove_none_available_config(
[
Expand Down Expand Up @@ -421,3 +440,11 @@ def count_servers():
# assert num_dpf_exe == 1

request.addfinalizer(count_servers)


# to call at the end
core.server.shutdown_all_session_servers()
try:
core.apply_server_context(core.AvailableServerContexts.premium)
except core.errors.DpfVersionNotSupported:
pass
6 changes: 0 additions & 6 deletions tests/test_meshregion.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@
from ansys import dpf
import conftest

from ansys.dpf.core.check_version import meets_version, get_server_version

SERVER_VERSION_HIGHER_THAN_3_0 = meets_version(
get_server_version(dpf.core._global_server()), "3.0"
)


@pytest.fixture()
def simple_bar_model(simple_bar, server_type):
Expand Down
7 changes: 7 additions & 0 deletions tests/test_multi_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from ansys.dpf.core.server_factory import ServerConfig, CommunicationProtocols


dpf.apply_server_context(dpf.AvailableServerContexts.entry)
cbellot000 marked this conversation as resolved.
Show resolved Hide resolved


@pytest.fixture(scope="module", params=[
ServerConfig(protocol=CommunicationProtocols.gRPC, legacy=False)
] if (isinstance(server._global_server(), server_types.InProcessServer)) else
Expand All @@ -27,6 +30,10 @@ def other_remote_server(request):
return server


dpf.server.shutdown_all_session_servers()
dpf.apply_server_context(dpf.AvailableServerContexts.premium)


@pytest.fixture()
def static_models(local_server, other_remote_server):
try:
Expand Down
27 changes: 6 additions & 21 deletions tests/test_plugins.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,19 @@
import os

import pytest

from ansys.dpf import core as dpf


def try_load_cff_operators():
try:
if os.name == "posix":
return False
dpf.load_library("Ans.Dpf.CFF.dll", "cff")
return True
except:
return False
# TODO: add loading for linux


@pytest.fixture()
def try_load_lsdyna_operators():
try:
dpf.load_library("Ans.Dpf.LSDYNA.dll", "lsdyna")
return True
except:
pytest.skip("Couldn't load lsdyna operators")
return False


@pytest.mark.skipif(
not try_load_lsdyna_operators(), reason="Couldn't load lsdyna operators"
)
def test_lsdyna(d3plot):
def test_lsdyna(d3plot, try_load_lsdyna_operators):
dpf.load_library("Ans.Dpf.LSDYNA.dll", "lsdyna")
ds = dpf.DataSources()
ds.set_result_file_path(d3plot, "d3plot")
Expand All @@ -38,19 +24,18 @@ def test_lsdyna(d3plot):
assert len(fc[0]) == 3195


@pytest.fixture()
def try_load_composites_operators():
try:
dpf.load_library("composite_operators.dll", "compo")
dpf.load_library("Ans.Dpf.EngineeringData.dll", "eng")
return True
except:
pytest.skip("Couldn't load composites operators")
return False


@pytest.mark.skipif(
not try_load_composites_operators(), reason="Couldn't load composites operators"
)
def test_eng(engineering_data_sources):
def test_eng(engineering_data_sources, try_load_composites_operators):
dpf.load_library("composite_operators.dll", "compo")
dpf.load_library("Ans.Dpf.EngineeringData.dll", "eng")
m = dpf.Model(engineering_data_sources)
Expand Down
Loading