How to create an optical property#

This tutorial demonstrates how to create an optical property.

What is an optical property?#

An optical property (also named material), gathers two notions: the surface optical property (SOP) and the volume optical property (VOP).

The property is then applied to a geometry (like bodies, faces).

Prerequisites#

Perform imports#

[1]:
from pathlib import Path

from ansys.speos.core import Project, Speos, launcher
from ansys.speos.core.kernel.client import (
    default_docker_channel,
)

Define constants#

Constants help ensure consistency and avoid repetition throughout the example.

[2]:
HOSTNAME = "localhost"
GRPC_PORT = 50098  # Be sure the Speos GRPC Server has been started on this port.
USE_DOCKER = True  # Set to False if you're running this example locally as a Notebook.

Define helper functions#

[3]:
def create_helper_geometries(project: Project):
    """Create bodies and faces."""

    def create_face(body):
        face = body.create_face(name="TheFaceF")
        face.vertices = [0, 1, 0, 0, 2, 0, 1, 2, 0]
        face.facets = [0, 1, 2]
        face.normals = [0, 0, 1, 0, 0, 1, 0, 0, 1]
        face.commit()
        return face

    data = {"bodies": [], "faces": []}
    root_part = project.create_root_part().commit()
    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#

Load assets#

The assets used to run this example are available in the PySpeos repository on GitHub.

Note: Make sure you have downloaded simulation assets and set assets_data_path to point to the assets folder.

[4]:
if USE_DOCKER:  # Running on the remote server.
    assets_data_path = Path("/app") / "assets"
else:
    assets_data_path = Path("/path/to/your/download/assets/directory")

Connect to the RPC Server#

This Python client connects to a server where the Speos engine is running as a service. In this example, the server and client are the same machine. The launch_local_speos_rpc_method can be used to start a local instance of the service.

[5]:
if USE_DOCKER:
    speos = Speos(channel=default_docker_channel())
else:
    speos = launcher.launch_local_speos_rpc_server(port=GRPC_PORT)
/home/runner/work/pyspeos/pyspeos/.venv/lib/python3.14/site-packages/ansys/tools/common/cyberchannel.py:188: UserWarning: Starting gRPC client without TLS on localhost:50098. This is INSECURE. Consider using a secure connection.
  warn(f"Starting gRPC client without TLS on {target}. This is INSECURE. Consider using a secure connection.")

Create a new project#

The only way to create an optical property using the core layer, is to create it from a project. The Project class is instantiated by passing a Speos instance

[6]:
p = Project(speos=speos)
print(p)
data = create_helper_geometries(p)
bodies = data["bodies"]
faces = data["faces"]
{
    "name": "",
    "description": "",
    "metadata": {},
    "part_guid": "",
    "sub_scene_anchor_axis_system": [],
    "sources": [],
    "sensors": [],
    "simulations": [],
    "materials": [],
    "scenes": []
}

Create VOP (volume optical property)#

Create locally. The mention “local: “ is added when printing the optical property.

[7]:
op1 = p.create_optical_property(name="Material.1")
op1.set_surface_mirror()  # SOP : mirror
op1.sop_mirror.reflectance = 80
op1.set_volume_opaque()  # VOP : opaque
# This optical property will be applied to two bodies named : "TheBodyB" and "TheBodyC".
op1.geometries = [bodies[0], bodies[1]]
[8]:
print(op1)
local: {
    "name": "Material.1",
    "geometries": {
        "geo_paths": [
            "TheBodyB",
            "TheBodyC"
        ]
    },
    "display_name": "",
    "description": "",
    "metadata": {},
    "sop_guids": [],
    "vop": {
        "name": "Material.1.VOP",
        "opaque": {},
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.1.SOP",
            "mirror": {
                "reflectance": 80.0
            },
            "description": "",
            "metadata": {}
        }
    ]
}

Push it to the server.#

Now that it is committed to the server, the mention “local: “ is no more present when printing the optical property.

[9]:
op1.commit()
print(op1)
{
    "name": "Material.1",
    "metadata": {
        "UniqueId": "451d8581-684f-44fd-8178-892efabb518f"
    },
    "vop_guid": "623388e0-17f2-4849-814c-a7cc97070c43",
    "geometries": {
        "geo_paths": [
            "TheBodyB",
            "TheBodyC"
        ]
    },
    "sop_guid": "96b1991b-7c26-4c3f-8b71-f48b96ce05c1",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "vop": {
        "name": "Material.1.VOP",
        "opaque": {},
        "description": "",
        "metadata": {}
    },
    "sop": {
        "name": "Material.1.SOP",
        "mirror": {
            "reflectance": 80.0
        },
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.1.SOP",
            "mirror": {
                "reflectance": 80.0
            },
            "description": "",
            "metadata": {}
        }
    ]
}

Another example.#

Setting several more characteristics.

[10]:
op2 = p.create_optical_property(name="Material.2")
op2.set_surface_opticalpolished()  # SOP : optical polished
op2.set_volume_library()
op2.vop_library.material_file_uri = (
    assets_data_path / "AIR.material"
)  # VOP : selected library via a file .material
# This optical property will be applied to two bodies named : "TheBodyD" and "TheBodyE".
op2.geometries = [bodies[2], bodies[3]]
op2.commit()
print(op2)
{
    "name": "Material.2",
    "metadata": {
        "UniqueId": "43439d5e-9be4-4395-82db-566aa274acce"
    },
    "vop_guid": "d8bce9c3-4801-449d-a7cc-653095f82440",
    "geometries": {
        "geo_paths": [
            "TheBodyD",
            "TheBodyE"
        ]
    },
    "sop_guid": "5ec91478-8d37-4959-87bd-bd7a3b0e5740",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "vop": {
        "name": "Material.2.VOP",
        "library": {
            "material_file_uri": "/app/assets/AIR.material"
        },
        "description": "",
        "metadata": {}
    },
    "sop": {
        "name": "Material.2.SOP",
        "optical_polished": {},
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.2.SOP",
            "optical_polished": {},
            "description": "",
            "metadata": {}
        }
    ]
}

Create FOP (face optical property)#

Sometimes it is needed to create property but only for surface.

In this case, no call for set_volume_xxx function is needed, and we will select a face for the geometries.

[11]:
op3 = p.create_optical_property(name="Material.FOP")
op3.set_surface_mirror()
op3.sop_mirror.reflectance = 90  # SOP : mirror
# This optical property will be applied a face from TheBodyD named : "TheFaceF".
op3.geometries = [faces[2]]
op3.commit()
print(op3)
{
    "name": "Material.FOP",
    "metadata": {
        "UniqueId": "aeda4f27-193f-4814-bd2f-2dfe4f32a818"
    },
    "geometries": {
        "geo_paths": [
            "TheBodyD/TheFaceF"
        ]
    },
    "sop_guid": "90a0f90a-dac1-4e81-9022-8216dd79c3fd",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "sop": {
        "name": "Material.FOP.SOP",
        "mirror": {
            "reflectance": 90.0
        },
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.FOP.SOP",
            "mirror": {
                "reflectance": 90.0
            },
            "description": "",
            "metadata": {}
        }
    ]
}

Default values#

Some default values are available when applicable in every methods and class.

[12]:
op4 = p.create_optical_property(name="Material.3").commit()
print(op4)
{
    "name": "Material.3",
    "metadata": {
        "UniqueId": "5e01ca2e-6396-4c5f-a131-202be5d47315"
    },
    "sop_guid": "95378a7f-1c35-4ae6-b18f-de02064adf8a",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "sop": {
        "name": "Material.3.SOP",
        "mirror": {
            "reflectance": 100.0
        },
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.3.SOP",
            "mirror": {
                "reflectance": 100.0
            },
            "description": "",
            "metadata": {}
        }
    ]
}

Read#

Material Instance Information#

A mention “local: “ is added if it is not yet committed to the server.

[13]:
print(op1)
{
    "name": "Material.1",
    "metadata": {
        "UniqueId": "451d8581-684f-44fd-8178-892efabb518f"
    },
    "vop_guid": "623388e0-17f2-4849-814c-a7cc97070c43",
    "geometries": {
        "geo_paths": [
            "TheBodyB",
            "TheBodyC"
        ]
    },
    "sop_guid": "96b1991b-7c26-4c3f-8b71-f48b96ce05c1",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "vop": {
        "name": "Material.1.VOP",
        "opaque": {},
        "description": "",
        "metadata": {}
    },
    "sop": {
        "name": "Material.1.SOP",
        "mirror": {
            "reflectance": 80.0
        },
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.1.SOP",
            "mirror": {
                "reflectance": 80.0
            },
            "description": "",
            "metadata": {}
        }
    ]
}

The get methods allows you to get some properties of your feature

[14]:
print("op1 name: {}".format(op1.get(key="name")))
print("geometries linked to op1: {}".format(op1.get(key="geo_paths")))
print("op1 surface optical properties info: {}".format(op1.get(key="sops")))
print("op1 volume optical property info: {}".format(op1.get(key="vop")))
# user can use get with vop type as key word to check volume property type
print(
    "op1 {} opaque type volume optical property".format(
        "is" if op1.get(key="opaque") is not None else "is not"
    )
)

print(op2)
print("op2 name: {}".format(op2.get(key="name")))
print(
    "op2 {} optical polished type surface property".format(
        ("is" if "optical_polished" in op2.get(key="sops")[0] else "is not")
    )
)
# an alternative way to check the type of optical property
print(
    "op2 {} library type volume optical property".format(
        "is" if "library" in op2.get(key="vop") is not None else "is not"
    )
)

print(op3)
print("op3 name: {}".format(op3.get(key="name")))
print("op3 has reflectance value of {}".format(op3.get(key="sops")[0]["mirror"]["reflectance"]))
op1 name: Material.1
geometries linked to op1: ['TheBodyB', 'TheBodyC']
op1 surface optical properties info: [{'name': 'Material.1.SOP', 'mirror': {'reflectance': 80.0}, 'description': '', 'metadata': {}}]
op1 volume optical property info: {'name': 'Material.1.VOP', 'opaque': {}, 'description': '', 'metadata': {}}
op1 is opaque type volume optical property
{
    "name": "Material.2",
    "metadata": {
        "UniqueId": "43439d5e-9be4-4395-82db-566aa274acce"
    },
    "vop_guid": "d8bce9c3-4801-449d-a7cc-653095f82440",
    "geometries": {
        "geo_paths": [
            "TheBodyD",
            "TheBodyE"
        ]
    },
    "sop_guid": "5ec91478-8d37-4959-87bd-bd7a3b0e5740",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "vop": {
        "name": "Material.2.VOP",
        "library": {
            "material_file_uri": "/app/assets/AIR.material"
        },
        "description": "",
        "metadata": {}
    },
    "sop": {
        "name": "Material.2.SOP",
        "optical_polished": {},
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.2.SOP",
            "optical_polished": {},
            "description": "",
            "metadata": {}
        }
    ]
}
op2 name: Material.2
op2 is optical polished type surface property
op2 is library type volume optical property
{
    "name": "Material.FOP",
    "metadata": {
        "UniqueId": "aeda4f27-193f-4814-bd2f-2dfe4f32a818"
    },
    "geometries": {
        "geo_paths": [
            "TheBodyD/TheFaceF"
        ]
    },
    "sop_guid": "90a0f90a-dac1-4e81-9022-8216dd79c3fd",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "sop": {
        "name": "Material.FOP.SOP",
        "mirror": {
            "reflectance": 90.0
        },
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.FOP.SOP",
            "mirror": {
                "reflectance": 90.0
            },
            "description": "",
            "metadata": {}
        }
    ]
}
op3 name: Material.FOP
op3 has reflectance value of 90.0

Project Information#

Committed feature information will appear inside a project information.

[15]:
print(p)
{
    "part_guid": "8be776b8-3bf5-4d5c-80d7-71a709d81789",
    "materials": [
        {
            "name": "Material.1",
            "metadata": {
                "UniqueId": "451d8581-684f-44fd-8178-892efabb518f"
            },
            "vop_guid": "623388e0-17f2-4849-814c-a7cc97070c43",
            "geometries": {
                "geo_paths": [
                    "TheBodyB",
                    "TheBodyC"
                ]
            },
            "sop_guid": "96b1991b-7c26-4c3f-8b71-f48b96ce05c1",
            "display_name": "",
            "description": "",
            "sop_guids": [],
            "vop": {
                "name": "Material.1.VOP",
                "opaque": {},
                "description": "",
                "metadata": {}
            },
            "sop": {
                "name": "Material.1.SOP",
                "mirror": {
                    "reflectance": 80.0
                },
                "description": "",
                "metadata": {}
            }
        },
        {
            "name": "Material.2",
            "metadata": {
                "UniqueId": "43439d5e-9be4-4395-82db-566aa274acce"
            },
            "vop_guid": "d8bce9c3-4801-449d-a7cc-653095f82440",
            "geometries": {
                "geo_paths": [
                    "TheBodyD",
                    "TheBodyE"
                ]
            },
            "sop_guid": "5ec91478-8d37-4959-87bd-bd7a3b0e5740",
            "display_name": "",
            "description": "",
            "sop_guids": [],
            "vop": {
                "name": "Material.2.VOP",
                "library": {
                    "material_file_uri": "/app/assets/AIR.material"
                },
                "description": "",
                "metadata": {}
            },
            "sop": {
                "name": "Material.2.SOP",
                "optical_polished": {},
                "description": "",
                "metadata": {}
            }
        },
        {
            "name": "Material.FOP",
            "metadata": {
                "UniqueId": "aeda4f27-193f-4814-bd2f-2dfe4f32a818"
            },
            "geometries": {
                "geo_paths": [
                    "TheBodyD/TheFaceF"
                ]
            },
            "sop_guid": "90a0f90a-dac1-4e81-9022-8216dd79c3fd",
            "display_name": "",
            "description": "",
            "sop_guids": [],
            "sop": {
                "name": "Material.FOP.SOP",
                "mirror": {
                    "reflectance": 90.0
                },
                "description": "",
                "metadata": {}
            }
        },
        {
            "name": "Material.3",
            "metadata": {
                "UniqueId": "5e01ca2e-6396-4c5f-a131-202be5d47315"
            },
            "sop_guid": "95378a7f-1c35-4ae6-b18f-de02064adf8a",
            "display_name": "",
            "description": "",
            "sop_guids": [],
            "sop": {
                "name": "Material.3.SOP",
                "mirror": {
                    "reflectance": 100.0
                },
                "description": "",
                "metadata": {}
            }
        }
    ],
    "name": "",
    "description": "",
    "metadata": {},
    "sub_scene_anchor_axis_system": [],
    "sources": [],
    "sensors": [],
    "simulations": [],
    "scenes": []
}

Update#

Tipp: if you are manipulating an optical property already committed, don’t forget to commit your changes.

If you don’t, you will still only watch what is committed on the server.

[16]:
print("op1 surface type before update: {}".format(op1.get(key="sops")[0]))
op1.set_volume_optic()
op1.set_surface_opticalpolished()
op1.commit()
print(op1)
print("op1 surface type after update: {}".format(op1.get(key="sops")[0]))
op1 surface type before update: {'name': 'Material.1.SOP', 'mirror': {'reflectance': 80.0}, 'description': '', 'metadata': {}}
{
    "name": "Material.1",
    "metadata": {
        "UniqueId": "451d8581-684f-44fd-8178-892efabb518f"
    },
    "vop_guid": "623388e0-17f2-4849-814c-a7cc97070c43",
    "geometries": {
        "geo_paths": [
            "TheBodyB",
            "TheBodyC"
        ]
    },
    "sop_guid": "96b1991b-7c26-4c3f-8b71-f48b96ce05c1",
    "display_name": "",
    "description": "",
    "sop_guids": [],
    "vop": {
        "name": "Material.1.VOP",
        "optic": {
            "index": 1.5,
            "absorption": 0.0
        },
        "description": "",
        "metadata": {}
    },
    "sop": {
        "name": "Material.1.SOP",
        "optical_polished": {},
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.1.SOP",
            "optical_polished": {},
            "description": "",
            "metadata": {}
        }
    ]
}
op1 surface type after update: {'name': 'Material.1.SOP', 'optical_polished': {}, 'description': '', 'metadata': {}}

Reset#

Possibility to reset local values from the one available in the server.

[17]:
op1.set_surface_mirror()  # set surface as a mirror but no commit
op1.reset()  # reset -> this will apply the server value to the local value
op1.delete()  # delete (to display the local value with the below print)
print(op1)
local: {
    "name": "Material.1",
    "vop_guid": "",
    "geometries": {
        "geo_paths": [
            "TheBodyB",
            "TheBodyC"
        ]
    },
    "display_name": "",
    "description": "",
    "metadata": {},
    "sop_guids": [],
    "vop": {
        "name": "Material.1.VOP",
        "optic": {
            "index": 1.5,
            "absorption": 0.0
        },
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.1.SOP",
            "optical_polished": {},
            "description": "",
            "metadata": {}
        }
    ]
}

Delete#

Once the data is deleted from the server, you can still work with local data and maybe commit later.

[18]:
op2.delete()
print(op2)
local: {
    "name": "Material.2",
    "vop_guid": "",
    "geometries": {
        "geo_paths": [
            "TheBodyD",
            "TheBodyE"
        ]
    },
    "display_name": "",
    "description": "",
    "metadata": {},
    "sop_guids": [],
    "vop": {
        "name": "Material.2.VOP",
        "library": {
            "material_file_uri": "/app/assets/AIR.material"
        },
        "description": "",
        "metadata": {}
    },
    "sops": [
        {
            "name": "Material.2.SOP",
            "optical_polished": {},
            "description": "",
            "metadata": {}
        }
    ]
}

Clean up all Optical properties

[19]:
op1.delete()
op3.delete()
op4.delete()
[19]:
<ansys.speos.core.opt_prop.OptProp at 0x7fcc3f960770>
[20]:
speos.close()
[20]:
True