diff --git a/doc/changelog.d/551.added.md b/doc/changelog.d/551.added.md new file mode 100644 index 000000000..06af3c969 --- /dev/null +++ b/doc/changelog.d/551.added.md @@ -0,0 +1 @@ +geopath property \ No newline at end of file diff --git a/examples/core/opt-prop.py b/examples/core/opt-prop.py index ad85097ed..deaae03e7 100644 --- a/examples/core/opt-prop.py +++ b/examples/core/opt-prop.py @@ -16,7 +16,7 @@ # + from pathlib import Path -from ansys.speos.core import GeoRef, Project, Speos +from ansys.speos.core import Project, Speos # - @@ -34,21 +34,24 @@ def create_helper_geometries(project: Project): """Create bodies and faces.""" def create_face(body): - ( + face = ( body.create_face(name="TheFaceF") .set_vertices([0, 0, 0, 1, 0, 0, 0, 1, 0]) .set_facets([0, 1, 2]) .set_normals([0, 0, 1, 0, 0, 1, 0, 0, 1]) .commit() ) + return face + data = {"bodies": [], "faces": []} root_part = project.create_root_part().commit() - body_b1 = root_part.create_body(name="TheBodyB").commit() - body_b2 = root_part.create_body(name="TheBodyC").commit() - body_b3 = root_part.create_body(name="TheBodyD").commit() - body_b4 = root_part.create_body(name="TheBodyE").commit() - for b in [body_b1, body_b2, body_b3, body_b4]: - create_face(b) + data["bodies"].append(root_part.create_body(name="TheBodyB").commit()) + data["bodies"].append(root_part.create_body(name="TheBodyC").commit()) + data["bodies"].append(root_part.create_body(name="TheBodyD").commit()) + data["bodies"].append(root_part.create_body(name="TheBodyE").commit()) + for b in data["bodies"]: + data["faces"].append(create_face(b)) + return data # ## Model Setup @@ -80,7 +83,9 @@ def create_face(body): p = Project(speos=speos) print(p) -create_helper_geometries(p) +data = create_helper_geometries(p) +bodies = data["bodies"] +faces = data["faces"] # ## Create VOP (volume optical property) # @@ -93,8 +98,8 @@ def create_face(body): # This optical property will be applied to two bodies named : "TheBodyB" and "TheBodyC". op1.set_geometries( geometries=[ - GeoRef.from_native_link(geopath="TheBodyB"), - GeoRef.from_native_link(geopath="TheBodyC"), + bodies[0], + bodies[1], ] ) print(op1) @@ -120,8 +125,8 @@ def create_face(body): # This optical property will be applied to two bodies named : "TheBodyD" and "TheBodyE". op2.set_geometries( geometries=[ - GeoRef.from_native_link(geopath="TheBodyD"), - GeoRef.from_native_link(geopath="TheBodyE"), + bodies[2], + bodies[3], ] ) op2.commit() @@ -138,7 +143,7 @@ def create_face(body): op3 = p.create_optical_property(name="Material.FOP") op3.set_surface_mirror(reflectance=90) # SOP : mirror # This optical property will be applied a face from TheBodyD named : "TheFaceF". -op3.set_geometries(geometries=[GeoRef.from_native_link(geopath="TheBodyD/TheFaceF")]) +op3.set_geometries(geometries=[faces[2]]) op3.commit() print(op3) diff --git a/examples/core/simulation.py b/examples/core/simulation.py index a897be3e5..16dc60651 100644 --- a/examples/core/simulation.py +++ b/examples/core/simulation.py @@ -13,7 +13,7 @@ # + from pathlib import Path -from ansys.speos.core import GeoRef, Project, Speos +from ansys.speos.core import Project, Speos from ansys.speos.core.simulation import SimulationInteractive, SimulationInverse # - @@ -48,7 +48,7 @@ # is running as a service. In this example, the server and # client are the same machine. -speos = Speos(host="localhost", port=50098) +speos = Speos(host=HOSTNAME, port=GRPC_PORT) # ### Create a new project # @@ -66,9 +66,13 @@ # ### Prepare the root part root_part = p.create_root_part() -root_part.create_body(name="Body.1").create_face(name="Face.1").set_vertices( - [0, 1, 2, 0, 2, 2, 1, 2, 2] -).set_facets([0, 1, 2]).set_normals([0, 0, 1, 0, 0, 1, 0, 0, 1]) +body_1 = root_part.create_body(name="Body.1") +face_1 = ( + body_1.create_face(name="Face.1") + .set_vertices([0, 1, 2, 0, 2, 2, 1, 2, 2]) + .set_facets([0, 1, 2]) + .set_normals([0, 0, 1, 0, 0, 1, 0, 0, 1]) +) root_part.commit() # ### Prepare an optical property @@ -79,7 +83,7 @@ # Choose the geometry for this optical property : Body.1 -opt_prop.set_geometries(geometries=[GeoRef.from_native_link(geopath="Body.1")]) +opt_prop.set_geometries(geometries=[body_1]) opt_prop.commit() @@ -95,7 +99,7 @@ # ### Prepare a surface source source1 = p.create_source(name=SOURCE_NAME) -source1.set_exitance_constant(geometries=[(GeoRef.from_native_link(geopath="Body.1/Face.1"), True)]) +source1.set_exitance_constant(geometries=[(face_1, True)]) # define a spectrum which is not monochromatic so it can be used in both direct and inverse # simulation source1.set_spectrum().set_blackbody() diff --git a/src/ansys/speos/core/body.py b/src/ansys/speos/core/body.py index 3be692f0b..6382085d8 100644 --- a/src/ansys/speos/core/body.py +++ b/src/ansys/speos/core/body.py @@ -29,6 +29,7 @@ from ansys.speos.core import proto_message_utils import ansys.speos.core.face as face +from ansys.speos.core.geo_ref import GeoRef from ansys.speos.core.kernel.body import ProtoBody from ansys.speos.core.kernel.client import SpeosClient import ansys.speos.core.part as part @@ -81,6 +82,14 @@ def __init__( self._geom_features = [] + @property + def geo_path(self) -> GeoRef: + """Geometry path to be used within other speos objects.""" + geo_paths = [self._name] + if isinstance(self._parent_part, part.Part.SubPart): + geo_paths.insert(0, self._parent_part.geo_path.metadata["GeoPath"]) + return GeoRef.from_native_link("/".join(geo_paths)) + def create_face( self, name: str, diff --git a/src/ansys/speos/core/face.py b/src/ansys/speos/core/face.py index e39110268..62c199e75 100644 --- a/src/ansys/speos/core/face.py +++ b/src/ansys/speos/core/face.py @@ -28,6 +28,7 @@ from ansys.speos.core import proto_message_utils import ansys.speos.core.body as body +from ansys.speos.core.geo_ref import GeoRef from ansys.speos.core.kernel.client import SpeosClient from ansys.speos.core.kernel.face import ProtoFace @@ -76,6 +77,15 @@ def __init__( # Create local Face self._face = ProtoFace(name=name, description=description, metadata=metadata) + @property + def geo_path(self) -> GeoRef: + """Geometry path to be used within other speos objects.""" + geo_paths = [self._name] + parent = self._parent_body + if isinstance(parent, body.Body): + geo_paths.insert(0, parent.geo_path.metadata["GeoPath"]) + return GeoRef.from_native_link("/".join(geo_paths)) + def set_vertices(self, values: List[float]) -> Face: """Set the face vertices. diff --git a/src/ansys/speos/core/opt_prop.py b/src/ansys/speos/core/opt_prop.py index da3dd3e8a..602e2594a 100644 --- a/src/ansys/speos/core/opt_prop.py +++ b/src/ansys/speos/core/opt_prop.py @@ -24,13 +24,16 @@ from __future__ import annotations from difflib import SequenceMatcher -from typing import List, Mapping, Optional +from typing import List, Mapping, Optional, Union import uuid +import ansys.speos.core.body as body +import ansys.speos.core.face as face from ansys.speos.core.geo_ref import GeoRef from ansys.speos.core.kernel.scene import ProtoScene from ansys.speos.core.kernel.sop_template import ProtoSOPTemplate from ansys.speos.core.kernel.vop_template import ProtoVOPTemplate +import ansys.speos.core.part as part import ansys.speos.core.project as project import ansys.speos.core.proto_message_utils as proto_message_utils @@ -264,7 +267,10 @@ def set_volume_library(self, path: str) -> OptProp: self._vop_template.library.material_file_uri = path return self - def set_geometries(self, geometries: Optional[List[GeoRef]] = None) -> OptProp: + def set_geometries( + self, + geometries: Optional[List[Union[GeoRef, body.Body, face.Face, part.Part.SubPart]]] = None, + ) -> OptProp: """Select geometries on which the optical properties will be applied. Parameters @@ -281,8 +287,17 @@ def set_geometries(self, geometries: Optional[List[GeoRef]] = None) -> OptProp: if geometries is None: self._material_instance.ClearField("geometries") else: + geo_paths = [] + for gr in geometries: + if isinstance(gr, GeoRef): + geo_paths.append(gr) + elif isinstance(gr, (face.Face, body.Body, part.Part.SubPart)): + geo_paths.append(gr.geo_path) + else: + msg = f"Type {type(gr)} is not supported as Optical property geometry input." + raise TypeError(msg) self._material_instance.geometries.geo_paths[:] = [ - gr.to_native_link() for gr in geometries + gp.to_native_link() for gp in geo_paths ] return self diff --git a/src/ansys/speos/core/part.py b/src/ansys/speos/core/part.py index eabe4ab9c..85e3a8280 100644 --- a/src/ansys/speos/core/part.py +++ b/src/ansys/speos/core/part.py @@ -31,6 +31,7 @@ from ansys.speos.core import proto_message_utils import ansys.speos.core.body as body import ansys.speos.core.face as face +from ansys.speos.core.geo_ref import GeoRef from ansys.speos.core.kernel.client import SpeosClient from ansys.speos.core.kernel.part import ProtoPart import ansys.speos.core.project as project @@ -100,6 +101,14 @@ def __init__( self._geom_features = [] + @property + def geo_path(self) -> GeoRef: + """Geometry path to be used within other speos objects.""" + geo_paths = [self._name] + if isinstance(self._parent_part, Part.SubPart): + geo_paths.insert(0, self._parent_part.geo_path.metadata["GeoPath"]) + return GeoRef.from_native_link("/".join(geo_paths)) + def create_body( self, name: str, diff --git a/src/ansys/speos/core/source.py b/src/ansys/speos/core/source.py index 9e5352462..2edafd477 100644 --- a/src/ansys/speos/core/source.py +++ b/src/ansys/speos/core/source.py @@ -32,6 +32,8 @@ project as project, proto_message_utils as proto_message_utils, ) +import ansys.speos.core.body as body +import ansys.speos.core.face as face from ansys.speos.core.geo_ref import GeoRef from ansys.speos.core.intensity import Intensity from ansys.speos.core.kernel.client import SpeosClient @@ -959,7 +961,9 @@ def set_intensity(self) -> Intensity: return self._intensity - def set_exitance_constant(self, geometries: List[tuple[GeoRef, bool]]) -> SourceSurface: + def set_exitance_constant( + self, geometries: List[tuple[Union[GeoRef, face.Face, body.Body], bool]] + ) -> SourceSurface: """Set existence constant. Parameters @@ -979,12 +983,25 @@ def set_exitance_constant(self, geometries: List[tuple[GeoRef, bool]]) -> Source "geo_paths" ) if geometries != []: - my_list = [ - ProtoScene.GeoPath(geo_path=gr.to_native_link(), reverse_normal=reverse_normal) - for (gr, reverse_normal) in geometries - ] + geo_paths = [] + for gr, reverse_normal in geometries: + if isinstance(gr, GeoRef): + geo_paths.append( + ProtoScene.GeoPath( + geo_path=gr.to_native_link(), reverse_normal=reverse_normal + ) + ) + elif isinstance(gr, (face.Face, body.Body)): + geo_paths.append( + ProtoScene.GeoPath( + geo_path=gr.geo_path.to_native_link(), reverse_normal=reverse_normal + ) + ) + else: + msg = f"Type {type(gr)} is not supported as Surface Source geometry input." + raise TypeError(msg) self._source_instance.surface_properties.exitance_constant_properties.geo_paths.extend( - my_list + geo_paths ) return self diff --git a/tests/core/test_opt_prop.py b/tests/core/test_opt_prop.py index 4e3795821..e27cc3288 100644 --- a/tests/core/test_opt_prop.py +++ b/tests/core/test_opt_prop.py @@ -148,16 +148,19 @@ def test_reset_optical_property(speos: Speos): root_part = p.create_root_part() body_b = root_part.create_body(name="TheBodyB") - body_b.create_face(name="TheFaceF").set_vertices([0, 0, 0, 1, 0, 0, 0, 1, 0]).set_facets( - [0, 1, 2] - ).set_normals([0, 0, 1, 0, 0, 1, 0, 0, 1]) + ( + body_b.create_face(name="TheFaceF") + .set_vertices([0, 0, 0, 1, 0, 0, 0, 1, 0]) + .set_facets([0, 1, 2]) + .set_normals([0, 0, 1, 0, 0, 1, 0, 0, 1]) + ) root_part.commit() # Create + commit op1 = ( p.create_optical_property(name="Material.1") .set_volume_opaque() - .set_geometries(geometries=[GeoRef.from_native_link("TheBodyB")]) + .set_geometries(geometries=[body_b.geo_path]) .commit() ) assert op1.vop_template_link is not None @@ -233,9 +236,12 @@ def test_get_optical_property(speos: Speos, capsys): root_part = p.create_root_part() body_a = root_part.create_body(name="TheBodyA") - body_a.create_face(name="TheFaceF").set_vertices([0, 0, 0, 1, 0, 0, 0, 1, 0]).set_facets( - [0, 1, 2] - ).set_normals([0, 0, 1, 0, 0, 1, 0, 0, 1]) + face = ( + body_a.create_face(name="TheFaceF") + .set_vertices([0, 0, 0, 1, 0, 0, 0, 1, 0]) + .set_facets([0, 1, 2]) + .set_normals([0, 0, 1, 0, 0, 1, 0, 0, 1]) + ) body_b = root_part.create_body(name="TheBodyB") body_b.create_face(name="TheFaceF").set_vertices([0, 0, 0, 1, 0, 0, 0, 1, 0]).set_facets( [0, 1, 2] @@ -245,7 +251,7 @@ def test_get_optical_property(speos: Speos, capsys): op1 = ( p.create_optical_property(name="Material.1") .set_volume_opaque() - .set_geometries(geometries=[GeoRef.from_native_link("TheBodyA")]) + .set_geometries(geometries=[body_a]) .commit() ) @@ -269,7 +275,7 @@ def test_get_optical_property(speos: Speos, capsys): p.create_optical_property(name="OpticalProperty2") .set_volume_optic(index=1.7, absorption=0.01, constringence=55) .set_surface_opticalpolished() - .set_geometries(geometries=[GeoRef.from_native_link("TheBodyB")]) + .set_geometries(geometries=[body_b]) .commit() ) @@ -292,6 +298,7 @@ def test_get_optical_property(speos: Speos, capsys): p.create_optical_property(name="OpticalProperty3") .set_volume_none() .set_surface_library(path=str(Path(test_path) / "R_test.anisotropicbsdf")) + .set_geometries(geometries=[face]) .commit() ) diff --git a/tests/core/test_part.py b/tests/core/test_part.py index a7ff496e3..b001219c2 100644 --- a/tests/core/test_part.py +++ b/tests/core/test_part.py @@ -51,7 +51,7 @@ def test_create_body(speos: Speos): # Add empty body body1 = root_part.create_body(name="Body.1").commit() assert len(root_part._geom_features) == 1 - + assert body1.geo_path.metadata["GeoPath"] == "Body.1" assert len(root_part.part_link.get().body_guids) == 1 assert root_part.part_link.get().body_guids[0] == body1.body_link.key @@ -59,7 +59,7 @@ def test_create_body(speos: Speos): body2 = root_part.create_body(name="Body.2") root_part.commit() assert len(root_part._geom_features) == 2 - + assert body2.geo_path.metadata["GeoPath"] == "Body.2" assert len(root_part.part_link.get().body_guids) == 2 assert root_part.part_link.get().body_guids[1] == body2.body_link.key @@ -84,7 +84,7 @@ def test_create_face(speos: Speos): root_part.commit() assert len(body1._geom_features) == 1 assert len(body1.body_link.get().face_guids) == 1 - + assert face0.geo_path.metadata["GeoPath"] == "Body.1/TheFaceF" # Add a face face1 = ( body1.create_face(name="Face.1") @@ -94,7 +94,7 @@ def test_create_face(speos: Speos): .commit() ) assert len(body1._geom_features) == 2 - + assert face1.geo_path.metadata["GeoPath"] == "Body.1/Face.1" assert len(body1.body_link.get().face_guids) == 2 assert body1.body_link.get().face_guids[1] == face1.face_link.key assert face1.face_link.get().vertices == [0, 1, 0, 0, 2, 0, 1, 2, 0] @@ -110,7 +110,7 @@ def test_create_face(speos: Speos): .commit() ) assert len(body1._geom_features) == 3 - + assert face2.geo_path.metadata["GeoPath"] == "Body.1/Face.2" assert len(body1.body_link.get().face_guids) == 3 assert body1.body_link.get().face_guids[2] == face2.face_link.key assert face2.face_link.get().vertices == [0, 0, 0, 1, 0, 0, 0, 1, 0] @@ -140,6 +140,7 @@ def test_create_subpart(speos: Speos): ) assert len(root_part._geom_features) == 1 assert len(root_part.part_link.get().parts) == 1 + assert sp1.geo_path.metadata["GeoPath"] == "SubPart.1" assert root_part.part_link.get().parts[0] == sp1._part_instance assert sp1._part_instance.axis_system == [ 5, @@ -168,6 +169,7 @@ def test_create_subpart(speos: Speos): root_part.commit() assert len(root_part._geom_features) == 2 assert len(root_part.part_link.get().parts) == 2 + assert sp2.geo_path.metadata["GeoPath"] == "SubPart.2" assert root_part.part_link.get().parts[1] == sp2._part_instance assert sp2._part_instance.axis_system == [ 15, @@ -211,7 +213,7 @@ def test_create_subpart_body(speos: Speos): # Add empty body body1 = sp1.create_body(name="Body.1").commit() assert len(sp1._geom_features) == 1 - + assert body1.geo_path.metadata["GeoPath"] == "SubPart.1/Body.1" assert len(sp1.part_link.get().body_guids) == 1 assert sp1.part_link.get().body_guids[0] == body1.body_link.key @@ -219,7 +221,7 @@ def test_create_subpart_body(speos: Speos): body2 = sp1.create_body(name="Body.2") root_part.commit() assert len(sp1._geom_features) == 2 - + assert body2.geo_path.metadata["GeoPath"] == "SubPart.1/Body.2" assert len(sp1.part_link.get().body_guids) == 2 assert sp1.part_link.get().body_guids[1] == body2.body_link.key @@ -266,6 +268,7 @@ def test_create_subpart_subpart(speos: Speos): 1, ] assert sp11._part_instance.part_guid == sp11.part_link.key + assert sp11.geo_path.metadata["GeoPath"] == "SubPart.1/SubPart.11" assert ( sp11.part_link.get().name == "SubPart.11" ) # part contained in a sub part gets same name as the sub part @@ -294,6 +297,7 @@ def test_create_subpart_subpart(speos: Speos): 1, ] assert sp12._part_instance.part_guid == sp12.part_link.key + assert sp12.geo_path.metadata["GeoPath"] == "SubPart.1/SubPart.12" assert ( sp12.part_link.get().name == "SubPart.12" ) # part contained in a sub part gets same name as the sub part diff --git a/tests/core/test_source.py b/tests/core/test_source.py index dedaadcd5..cdca7e39b 100644 --- a/tests/core/test_source.py +++ b/tests/core/test_source.py @@ -384,7 +384,7 @@ def test_keep_same_internal_feature(speos: Speos): # SURFACE SOURCE source1 = SourceSurface(project=p, name="Surface.1") - source1.set_exitance_constant(geometries=[(GeoRef.from_native_link("BodyB"), False)]) + source1.set_exitance_constant(geometries=[(body_b, False)]) source1.commit() spectrum_guid = source1.source_template_link.get().surface.spectrum_guid intensity_guid = source1.source_template_link.get().surface.intensity_guid