How to create a simulation#

This tutorial demonstrates how to create a simulation.

What is a simulation?#

A simulation contains selected sensors, sources to model ray-trace in space.

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,
)
from ansys.speos.core.simulation import (
    SimulationInteractive,
    SimulationInverse,
    SimulationVirtualBSDF,
)

Define constants#

The 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.
SOURCE_NAME = "Surface.1"
SENSOR_NAME = "Irradiance.1"

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.

[3]:
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..

[4]:
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.10/site-packages/ansys/tools/common/cyberchannel.py:187: 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 a simulation using the core layer, is to create it from a project. The Project class is instantiated by passing a Speos instance.

[5]:
p = Project(speos=speos)
print(p)
{
    "name": "",
    "description": "",
    "metadata": {},
    "part_guid": "",
    "sources": [],
    "sensors": [],
    "simulations": [],
    "materials": [],
    "scenes": []
}

Prepare prerequisites#

Create the necessary elements for a simulation: Sensor, source, root part, optical property are prerequisites.

Prepare the root part#

[6]:
root_part = p.create_root_part()
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()
[6]:
<ansys.speos.core.part.Part at 0x7fa0e08de890>

Prepare an optical property#

Create Optical Property

[7]:
opt_prop = p.create_optical_property("Material.1")
opt_prop.set_volume_opaque().set_surface_mirror()
[7]:
<ansys.speos.core.opt_prop.OptProp at 0x7fa0e08de3b0>

Choose the geometry for this optical property : Body.1

[8]:
opt_prop.set_geometries(geometries=[body_1])
opt_prop.commit()
[8]:
<ansys.speos.core.opt_prop.OptProp at 0x7fa0e08de3b0>

Prepare an irradiance sensor#

[9]:
sensor1 = p.create_sensor(name=SENSOR_NAME)
# set type to colorimetric or spectral so that the sensor can be used both in
# direct and inverse simulation
sensor1.set_type_colorimetric()
sensor1.commit()
[9]:
<ansys.speos.core.sensor.SensorIrradiance at 0x7fa0e08df730>

Prepare a surface source#

[10]:
source1 = p.create_source(name=SOURCE_NAME)
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()
source1.commit()
/home/runner/work/pyspeos/pyspeos/.venv/lib/python3.10/site-packages/ansys/speos/core/project.py:200: UserWarning: The pySpeos feature : SourceSurface needs a Speos Version of 2025 R2 SP0 or higher.
  feature = SourceSurface(
[10]:
<ansys.speos.core.source.SourceSurface at 0x7fa0d8664e20>

Create a simulation#

[11]:
simulation1 = p.create_simulation(name="Simulation.1")
simulation1.set_sensor_paths([SENSOR_NAME]).set_source_paths([SOURCE_NAME])
print(simulation1)
simulation1.commit()
print(simulation1)
local: {
    "name": "Simulation.1",
    "sensor_paths": [
        "Irradiance.1"
    ],
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "metadata": {},
    "simulation_guid": "",
    "simulation": {
        "direct_mc_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 100,
            "weight": {
                "minimum_energy_percentage": 0.005
            },
            "dispersion": true,
            "colorimetric_standard": "CIE_1931",
            "fast_transmission_gathering": false,
            "ambient_material_uri": "",
            "stop_condition_rays_number": "200000",
            "automatic_save_frequency": 1800
        },
        "name": "Simulation.1",
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.1",
        "job_type": "CPU"
    }
}
{
    "name": "Simulation.1",
    "metadata": {
        "UniqueId": "24c40d4d-e929-4ac4-8150-c425ef0e44fe"
    },
    "simulation_guid": "9a93b63a-49a8-4cdf-b057-382ac73a4139",
    "sensor_paths": [
        "Irradiance.1"
    ],
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "simulation": {
        "direct_mc_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 100,
            "weight": {
                "minimum_energy_percentage": 0.005
            },
            "dispersion": true,
            "colorimetric_standard": "CIE_1931",
            "fast_transmission_gathering": false,
            "ambient_material_uri": "",
            "stop_condition_rays_number": "200000",
            "automatic_save_frequency": 1800
        },
        "name": "Simulation.1",
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.1",
        "job_type": "CPU"
    }
}
/home/runner/work/pyspeos/pyspeos/.venv/lib/python3.10/site-packages/ansys/speos/core/project.py:282: UserWarning: The pySpeos feature : SimulationDirect needs a Speos Version of 2025 R2 SP0 or higher.
  feature = SimulationDirect(
/home/runner/work/pyspeos/pyspeos/.venv/lib/python3.10/site-packages/ansys/speos/core/simulation.py:947: UserWarning: Please note that setting a value for light expert option forces a sensorcommit when committing the Simulation class
  self.set_light_expert()

Set simulation characteristics#

Simulation is defined with the same default values as the GUI speos.

If the user would like to modify the simulation characteristics, it is possible to do so by setting the simulation characteristics as below.

[12]:
simulation2_direct = p.create_simulation(name="Simulation.2")
simulation2_direct.set_ambient_material_file_uri(
    uri=str(assets_data_path / "AIR.material")
).set_colorimetric_standard_CIE_1964().set_weight_none().set_dispersion(False)
simulation2_direct.geom_distance_tolerance = 0.01
simulation2_direct.max_impact = 200
simulation2_direct.set_sensor_paths([SENSOR_NAME]).set_source_paths([SOURCE_NAME]).commit()
print(simulation2_direct)
{
    "name": "Simulation.2",
    "metadata": {
        "UniqueId": "c57f8bd3-5701-4f42-a4d1-8b34f220d3d4"
    },
    "simulation_guid": "0a48ff68-c02e-4fe6-a592-14012ca49f98",
    "sensor_paths": [
        "Irradiance.1"
    ],
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "simulation": {
        "direct_mc_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 200,
            "colorimetric_standard": "CIE_1964",
            "ambient_material_uri": "/app/assets/AIR.material",
            "dispersion": false,
            "fast_transmission_gathering": false,
            "stop_condition_rays_number": "200000",
            "automatic_save_frequency": 1800
        },
        "name": "Simulation.2",
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.2",
        "job_type": "CPU"
    }
}

Read information#

Read simulation information

[13]:
print(simulation1)
{
    "name": "Simulation.1",
    "metadata": {
        "UniqueId": "24c40d4d-e929-4ac4-8150-c425ef0e44fe"
    },
    "simulation_guid": "9a93b63a-49a8-4cdf-b057-382ac73a4139",
    "sensor_paths": [
        "Irradiance.1"
    ],
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "simulation": {
        "direct_mc_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 100,
            "weight": {
                "minimum_energy_percentage": 0.005
            },
            "dispersion": true,
            "colorimetric_standard": "CIE_1931",
            "fast_transmission_gathering": false,
            "ambient_material_uri": "",
            "stop_condition_rays_number": "200000",
            "automatic_save_frequency": 1800
        },
        "name": "Simulation.1",
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.1",
        "job_type": "CPU"
    }
}

Read project information

[14]:
print(p)
{
    "part_guid": "28db24a0-37c4-4281-a3f3-ebc516b651fc",
    "sources": [
        {
            "name": "Surface.1",
            "metadata": {
                "UniqueId": "a863e39b-e385-4501-b988-97afd3d7e716"
            },
            "source_guid": "ba3b0864-606d-4916-83c8-0bac384b5af4",
            "description": "",
            "source": {
                "name": "Surface.1",
                "surface": {
                    "luminous_flux": {
                        "luminous_value": 683.0
                    },
                    "intensity_guid": "033b614b-2ce2-4469-b0f1-902b52f84ca0",
                    "exitance_constant": {
                        "geo_paths": [
                            {
                                "geo_path": "Body.1/Face.1",
                                "reverse_normal": true
                            }
                        ]
                    },
                    "spectrum_guid": "5c625448-ee5d-4635-9542-4666f273da6c",
                    "intensity": {
                        "name": "Surface.1.Intensity",
                        "cos": {
                            "N": 1.0,
                            "total_angle": 180.0
                        },
                        "description": "",
                        "metadata": {}
                    },
                    "spectrum": {
                        "name": "Surface.1.Spectrum",
                        "blackbody": {
                            "temperature": 2856.0
                        },
                        "description": "",
                        "metadata": {}
                    }
                },
                "description": "",
                "metadata": {}
            }
        }
    ],
    "sensors": [
        {
            "name": "Irradiance.1",
            "metadata": {
                "UniqueId": "c868aeb0-90f0-41e3-9d06-51cfa782b667"
            },
            "sensor_guid": "c0562348-5ba2-4e56-bf61-e755c80f9305",
            "description": "",
            "result_file_name": "",
            "sensor": {
                "irradiance_sensor_template": {
                    "sensor_type_colorimetric": {
                        "wavelengths_range": {
                            "w_start": 400.0,
                            "w_end": 700.0,
                            "w_sampling": 13
                        }
                    },
                    "illuminance_type_planar": {},
                    "dimensions": {
                        "x_start": -50.0,
                        "x_end": 50.0,
                        "x_sampling": 100,
                        "y_start": -50.0,
                        "y_end": 50.0,
                        "y_sampling": 100
                    },
                    "axis_system": [
                        0.0,
                        0.0,
                        0.0,
                        1.0,
                        0.0,
                        0.0,
                        0.0,
                        1.0,
                        0.0,
                        0.0,
                        0.0,
                        1.0
                    ],
                    "layer_type_none": {},
                    "ray_file_type": "RayFileNone",
                    "integration_direction": []
                },
                "name": "Irradiance.1",
                "description": "",
                "metadata": {}
            }
        }
    ],
    "simulations": [
        {
            "name": "Simulation.1",
            "metadata": {
                "UniqueId": "24c40d4d-e929-4ac4-8150-c425ef0e44fe"
            },
            "simulation_guid": "9a93b63a-49a8-4cdf-b057-382ac73a4139",
            "sensor_paths": [
                "Irradiance.1"
            ],
            "source_paths": [
                "Surface.1"
            ],
            "description": "",
            "simulation": {
                "direct_mc_simulation_template": {
                    "geom_distance_tolerance": 0.01,
                    "max_impact": 100,
                    "weight": {
                        "minimum_energy_percentage": 0.005
                    },
                    "dispersion": true,
                    "colorimetric_standard": "CIE_1931",
                    "fast_transmission_gathering": false,
                    "ambient_material_uri": "",
                    "stop_condition_rays_number": "200000",
                    "automatic_save_frequency": 1800
                },
                "name": "Simulation.1",
                "description": "",
                "metadata": {},
                "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
                "simulation_path": "Simulation.1",
                "job_type": "CPU"
            }
        },
        {
            "name": "Simulation.2",
            "metadata": {
                "UniqueId": "c57f8bd3-5701-4f42-a4d1-8b34f220d3d4"
            },
            "simulation_guid": "0a48ff68-c02e-4fe6-a592-14012ca49f98",
            "sensor_paths": [
                "Irradiance.1"
            ],
            "source_paths": [
                "Surface.1"
            ],
            "description": "",
            "simulation": {
                "direct_mc_simulation_template": {
                    "geom_distance_tolerance": 0.01,
                    "max_impact": 200,
                    "colorimetric_standard": "CIE_1964",
                    "ambient_material_uri": "/app/assets/AIR.material",
                    "dispersion": false,
                    "fast_transmission_gathering": false,
                    "stop_condition_rays_number": "200000",
                    "automatic_save_frequency": 1800
                },
                "name": "Simulation.2",
                "description": "",
                "metadata": {},
                "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
                "simulation_path": "Simulation.2",
                "job_type": "CPU"
            }
        }
    ],
    "materials": [
        {
            "name": "Material.1",
            "metadata": {
                "UniqueId": "9485b344-b30e-4168-8db9-07527c758982"
            },
            "vop_guid": "70509d27-dc65-433e-aab1-982f87b5aebe",
            "geometries": {
                "geo_paths": [
                    "Body.1"
                ]
            },
            "sop_guid": "991f3bf3-c9ef-420a-af55-c611f19ad226",
            "description": "",
            "sop_guids": [],
            "vop": {
                "name": "Material.1.VOP",
                "opaque": {},
                "description": "",
                "metadata": {}
            },
            "sop": {
                "name": "Material.1.SOP",
                "mirror": {
                    "reflectance": 100.0
                },
                "description": "",
                "metadata": {}
            }
        }
    ],
    "name": "",
    "description": "",
    "metadata": {},
    "scenes": []
}

Update simulation settings#

If you are manipulating a simulation already committed, remember to commit your changes.

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

[15]:
simulation1.set_ambient_material_file_uri(uri=str(assets_data_path / "AIR.material"))
simulation1.commit()
print(simulation1)
{
    "name": "Simulation.1",
    "metadata": {
        "UniqueId": "24c40d4d-e929-4ac4-8150-c425ef0e44fe"
    },
    "simulation_guid": "9a93b63a-49a8-4cdf-b057-382ac73a4139",
    "sensor_paths": [
        "Irradiance.1"
    ],
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "simulation": {
        "direct_mc_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 100,
            "weight": {
                "minimum_energy_percentage": 0.005
            },
            "dispersion": true,
            "ambient_material_uri": "/app/assets/AIR.material",
            "colorimetric_standard": "CIE_1931",
            "fast_transmission_gathering": false,
            "stop_condition_rays_number": "200000",
            "automatic_save_frequency": 1800
        },
        "name": "Simulation.1",
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.1",
        "job_type": "CPU"
    }
}

Reset#

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

[16]:
simulation1.max_impact = 1000  # adjust max impact but no commit
simulation1.reset()  # reset -> this will apply the server value to the local value
simulation1.delete()  # delete (to display the local value with the below print)
print(simulation1)
local: {
    "name": "Simulation.1",
    "sensor_paths": [
        "Irradiance.1"
    ],
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "metadata": {},
    "simulation_guid": "",
    "simulation": {
        "direct_mc_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 100,
            "weight": {
                "minimum_energy_percentage": 0.005
            },
            "dispersion": true,
            "ambient_material_uri": "/app/assets/AIR.material",
            "colorimetric_standard": "CIE_1931",
            "fast_transmission_gathering": false,
            "stop_condition_rays_number": "200000",
            "automatic_save_frequency": 1800
        },
        "name": "Simulation.1",
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.1",
        "job_type": "CPU"
    }
}

Other simulation examples#

Inverse simulation#

[17]:
simulation3 = p.create_simulation(name="Simulation.3", feature_type=SimulationInverse)
simulation3.set_sensor_paths(sensor_paths=[SENSOR_NAME]).set_source_paths(
    source_paths=[SOURCE_NAME]
).commit()
print(simulation3)
/home/runner/work/pyspeos/pyspeos/.venv/lib/python3.10/site-packages/ansys/speos/core/project.py:289: UserWarning: The pySpeos feature : SimulationInverse needs a Speos Version of 2025 R2 SP0 or higher.
  feature = SimulationInverse(
{
    "name": "Simulation.3",
    "metadata": {
        "UniqueId": "cba04ee6-15a6-478b-8695-92c548a6c83c"
    },
    "simulation_guid": "1dcda1f5-0150-4107-8373-80b744c8726e",
    "sensor_paths": [
        "Irradiance.1"
    ],
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "simulation": {
        "inverse_mc_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 100,
            "weight": {
                "minimum_energy_percentage": 0.005
            },
            "number_of_gathering_rays_per_source": 1,
            "colorimetric_standard": "CIE_1931",
            "dispersion": false,
            "splitting": false,
            "maximum_gathering_error": 0,
            "maximum_gathering_error_percentage": 0.0,
            "fast_transmission_gathering": false,
            "ambient_material_uri": "",
            "optimized_propagation_none": {
                "stop_condition_passes_number": 5
            },
            "automatic_save_frequency": 1800
        },
        "name": "Simulation.3",
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.3",
        "job_type": "CPU"
    }
}

Interactive simulation#

[18]:
simulation4 = p.create_simulation(name="Simulation.4", feature_type=SimulationInteractive)
simulation4.set_source_paths(source_paths=[SOURCE_NAME]).commit()
print(simulation4)
{
    "name": "Simulation.4",
    "metadata": {
        "UniqueId": "8bd4ba0a-f59b-4afa-bdcc-b3f0edea981b"
    },
    "simulation_guid": "66481679-8555-4696-9c9e-aff90a20ba70",
    "source_paths": [
        "Surface.1"
    ],
    "description": "",
    "sensor_paths": [],
    "simulation": {
        "name": "Simulation.4",
        "interactive_simulation_template": {
            "geom_distance_tolerance": 0.01,
            "max_impact": 100,
            "weight": {
                "minimum_energy_percentage": 0.005
            },
            "colorimetric_standard": "CIE_1931",
            "ambient_material_uri": "",
            "rays_number_per_sources": [],
            "light_expert": false,
            "impact_report": false
        },
        "description": "",
        "metadata": {},
        "scene_guid": "20418ce3-d833-482d-8c34-9f3b415724f1",
        "simulation_path": "Simulation.4",
        "job_type": "CPU"
    }
}

Virtual BSDF Bench simulation#

[19]:
# Change the material property from mirror to bsdf type
opt_prop.set_surface_library(path=str(assets_data_path / "R_test.anisotropicbsdf")).commit()
vbb = p.create_simulation(name="virtual_BSDF", feature_type=SimulationVirtualBSDF)
vbb.axis_system = [
    0.36,
    1.73,
    2.0,
    1.0,
    0.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    0.0,
    1.0,
]  # change the coordinate VBSDF to body center
vbb.commit()
results = vbb.compute_CPU()
print(results)
[upload_response {
  info {
    uri: "e59c6b58-812e-4c25-a2f7-70bd85390335"
    file_name: "virtual_BSDF.html"
    file_size: 27130
  }
  upload_duration {
    nanos: 311062
  }
}
, upload_response {
  info {
    uri: "7688112c-ee94-492b-ba64-a9a7d94ee309"
    file_name: "virtual_BSDF.anisotropicbsdf"
    file_size: 1261452
  }
  upload_duration {
    nanos: 2057155
  }
}
]

Simulation compute and stop#

The compute_CPU, compute_GPU calls are blocking. Thus it will return only once the simulation compute is finished.

If you want to have the possibility to stop it before it is finished, here an example.

[20]:
from threading import Thread
from time import sleep
[21]:
# Launch the compute_CPU in a thread and start the thread.
compute_thread = Thread(target=vbb.compute_CPU)
compute_thread.start()
# Wait 2 seconds then stop_computation
sleep(2)
vbb.stop_computation()
# Join the compute_thread
compute_thread.join()
[22]:
speos.close()
[22]:
True