ansys / pydpf-core

Data Processing Framework - Python Core
http://dpf.docs.pyansys.com/
MIT License
67 stars 25 forks source link

Scoping as an input pin of a custom operator is not set to None if undefined #1008

Open pthieffry opened 1 year ago

pthieffry commented 1 year ago

Before submitting the issue

Description of the bug

With a code like this one, mscoping or tscoping are not set to None but defined as scoping with 0 ids which can lead to scope an operator on an empty scoping.

`import numpy as np

from ansys.dpf import core as dpf from ansys.dpf.core.custom_operator import CustomOperatorBase, record_operator from ansys.dpf.core.operator_specification import CustomSpecification, SpecificationProperties, \ PinSpecification

class myStressOperator(CustomOperatorBase): @property def name(self): return "my_stress_operator"

@property
def specification(self) -> CustomSpecification:
    spec = CustomSpecification()
    spec.description = "Example of a custom operator with different input types"
    spec.inputs = {0: PinSpecification("multiplier", [float], "scalar with which to multiply stress fields"),
        1: PinSpecification("time_scoping", [dpf.Scoping], "Time scoping for stress results"),
        2: PinSpecification("mesh_scoping", [dpf.Scoping], "Mesh scoping for stress results"),
        4: PinSpecification("data_sources", [dpf.DataSources], "Data source")
    }
    spec.outputs = {
        0: PinSpecification("multiplied_stresses", [dpf.FieldsContainer]),
        1: PinSpecification("success", [float])
    }
    spec.properties = SpecificationProperties("my stress operator", "result")
    return spec

def run(self):
    multiplier= self.get_input(0, float)
    tscoping=self.get_input(1, dpf.Scoping)
    mscoping=self.get_input(2, dpf.Scoping)
    ds=self.get_input(4, dpf.DataSources)
    print(ds)
    if ds==None:
        success=-1
    else:
        # compute stats                        
        stresses=dpf.operators.result.stress(data_sources=ds)

        # If mesh scoping is defined
        if mscoping==None or len(mscoping.ids)!=0 :                
            stresses.inputs.mesh_scoping.connect(mscoping)

        # If time scoping is defined
        if tscoping==None or len(tscoping.ids)!=0:
            stresses.inputs.time_scoping.connect(tscoping)

        mult_stress_op=dpf.operators.math.scale_fc(ponderation=multiplier,fields_container=stresses)            

        success=0
        self.set_output(0, mult_stress_op.outputs.fields_container())
        self.set_output(1, success)            
        self.set_succeeded()

def load_operators(args): record_operator(myStressOperator, args) `

Steps To Reproduce

Use above code as a custom operator

Which Operating System causes the issue?

Windows

Which DPF/Ansys version are you using?

Ansys 2023 R2

Which Python version causes the issue?

3.8

Installed packages

ansys-api-assembly 0.2.18 ansys-api-fluent 0.3.12 ansys-api-mapdl 0.5.1 ansys-api-mechanical 0.1.0 ansys-api-meshing-prime 0.1.1 ansys-api-platform-instancemanagement 1.0.0b3 ansys-api-systemcoupling 0.1.0 ansys-corba 0.1.1 ansys-dpf-composites 0.2.0 ansys-dpf-core 0.8.1 ansys-dpf-flowdiagram 0.1.0 ansys-dpf-gate 0.3.1 ansys-dpf-gatebin 0.3.1 ansys-dpf-post 0.4.0 ansys-fluent-core 0.14.0 ansys-grantami-bomanalytics 1.1.3 ansys-grantami-bomanalytics-openapi 1.0.0 ansys-grpc-dpf 0.7.1 ansys-mapdl-core 0.65.0 ansys-mapdl-reader 0.52.11 ansys-math-core 0.1.1 ansys-mechanical-core 0.8.0 ansys-meshing-prime 0.3.2 ansys-motorcad-core 0.1.3 ansys-openapi-common 1.2.1 ansys-optislang-core 0.2.1 ansys-platform-instancemanagement 1.1.1 ansys-pyassembly 0.7.dev0 ansys-pythonnet 3.1.0rc1 ansys-seascape 0.2.0 ansys-systemcoupling-core 0.1.3 ansys-templates 0.5.1 ansys-tools-path 0.2.4 anyio 3.6.2 appdirs 1.4.4 arrow 1.2.3 asttokens 2.2.1 backcall 0.2.0 binaryornot 0.4.4 cachetools 5.3.0 certifi 2022.12.7 cffi 1.15.1 chardet 5.1.0 charset-normalizer 3.1.0 click 8.1.3 cloudpickle 2.2.1 clr-loader 0.2.5 colorama 0.4.6 comm 0.1.3 contourpy 1.0.7 cookiecutter 2.1.1 cryptography 41.0.1 cycler 0.11.0 debugpy 1.6.6 decorator 5.1.1 executing 1.2.0 fastapi 0.95.0 fonttools 4.39.2 geomdl 5.3.1 gmsh 4.11.1 google-api-core 2.11.0 google-api-python-client 2.81.0 google-auth 2.16.2 google-auth-httplib2 0.1.0 googleapis-common-protos 1.58.0 grpc-interceptor 0.15.1 grpcio 1.51.3 grpcio-status 1.48.2 h11 0.14.0 h5py 3.9.0 httplib2 0.21.0 httptools 0.5.0 idna 3.4 imageio 2.26.0 importlib-metadata 4.13.0 importlib-resources 5.12.0 ipydatawidgets 4.3.3 ipykernel 6.22.0 ipython 8.11.0 ipywidgets 8.0.6 isort 5.12.0 jedi 0.18.2 Jinja2 3.1.2 jinja2-time 0.2.0 jupyter-client 6.1.12 jupyter_core 5.3.0 jupyterlab-widgets 3.0.7 kiwisolver 1.4.4 lxml 4.9.2 markdown-it-py 3.0.0 MarkupSafe 2.1.2 matplotlib 3.7.1 matplotlib-inline 0.1.6 mdurl 0.1.2 meshio 5.3.4 nest-asyncio 1.5.6 networkx 3.1 numpy 1.24.2 packaging 23.0 pandas 2.0.2 parso 0.8.3 pickleshare 0.7.5 Pillow 9.4.0 pip 23.1.2 platformdirs 3.1.1 plumbum 1.8.2 pooch 1.7.0 prompt-toolkit 3.0.38 protobuf 3.20.3 protoc-gen-swagger 0.1.0 psutil 5.9.4 pure-eval 0.2.2 pyaedt 0.6.76 pyansys 2023.2.3 pyansys-tools-versioning 0.3.3 pyasn1 0.4.8 pyasn1-modules 0.2.8 pycparser 2.21 pydantic 1.10.7 Pygments 2.14.0 pyiges 0.2.1 pyparsing 3.0.9 pypiwin32 223 pyspnego 0.9.1 python-dateutil 2.8.2 python-dotenv 1.0.0 python-slugify 8.0.1 pythonnet 3.0.1 pythreejs 2.4.2 pytwin 0.3.0 pytz 2023.3 pyvista 0.38.4 pywin32 305 PyYAML 6.0 pyzmq 25.0.2 requests 2.28.2 requests-negotiate-sspi 0.5.2 requests-ntlm 1.2.0 rich 13.4.2 rpyc 5.3.1 rsa 4.9 scipy 1.10.1 scooby 0.7.1 setuptools 56.0.0 six 1.16.0 sniffio 1.3.0 spyder-kernels 2.1.3 stack-data 0.6.2 starlette 0.26.1 text-unidecode 1.3 tornado 6.2 tqdm 4.65.0 traitlets 5.9.0 traittypes 0.2.1 typing_extensions 4.5.0 tzdata 2023.3 ujson 5.7.0 uritemplate 4.1.1 urllib3 1.26.15 uvicorn 0.21.1 vtk 9.2.6 watchfiles 0.18.1 wcwidth 0.2.6 websockets 10.4 widgetsnbextension 4.0.7 zipp 3.15.0

PProfizi commented 1 year ago

Hello @pthieffry,

I could not find an example where the treatment of scopings in custom Python operators is showcased this way. If the if functions concerning scopings in the run method above are themselves custom, then to me it seems like expected behavior. Giving an empty scoping as input should return no data. What you be, in your opinion, the expected behavior?

pthieffry commented 1 year ago

@PProfizi I would expect the scoping to be None if the user hasn't connected anything to that pin. My original code in the run function only had mscoping==None as a test and I ended up with empty results, as I did not realize the mscoping was indeed there but with empty ids.

In a regular operator, not specifying a scoping takes all ids, not none.

I called my operator like this: `tscop=dpf.Scoping() tscop.ids=[2,3,4] # steps 2 to 4

stresses=dpf.operators.result.stress(data_sources=ds) stresses.inputs.time_scoping.connect(tscop)

mstress= dpf.Operator("my_stress_operator") mstress.inputs.data_sources.connect(ds) mstress.inputs.multiplier.connect(3.1) mstress.inputs.time_scoping.connect(tscop)`

In this case, mesh scoping is not defined.

PProfizi commented 1 year ago

@pthieffry if I understood correctly, the issue is that despite not having connected input pin mesh_scoping to the operator, the method self.get_input(2, dpf.Scoping) returns an empty Scoping instead of a None or a scoping with all IDs as you would expect it to.

pthieffry commented 1 year ago

@PProfizi exactly

PProfizi commented 1 year ago

@pthieffry based on the current implementation of CustomOperatorBase.get_input this is the expected behavior as it tries to return the type asked. Here a Scoping is asked, so in case of nothing connected it returns an empty Scoping. I think in your case the solution would be indeed to test whether the scoping is empty, else we will need a consequent rework of this feature. We could rework it to return None is the input pin was not defined. I guess you would have the same issue with other types.

pthieffry commented 1 year ago

@PProfizi As this is not consistent with regular operators, I would recommend to fix this - either returning None or returning a scoping with all ids (likely more complex)

PProfizi commented 1 year ago

pinging @cbellot000 and @rlagha for their input