# # How to create a source # This tutorial demonstrates how to create a source. # # There are different type of sources available: luminaire source, surface source, ray file source. # # ## Prerequisites # # ### Perform imports # + from pathlib import Path from ansys.speos.core import Face, Project, Speos, launcher from ansys.speos.core.generic.parameters import CieType from ansys.speos.core.kernel.client import ( default_docker_channel, ) from ansys.speos.core.source import ( SourceAmbientCieStandardGeneralSky, SourceAmbientCieStandardOvercastSky, SourceAmbientEnvironment, SourceAmbientNaturalLight, SourceAmbientUniform, SourceAmbientUsStandard, SourceDisplay, SourceLuminaire, SourceRayFile, SourceSurface, SourceThermic, ) # - # ### Define constants # Constants help ensure consistency and avoid repetition throughout the example. 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. IES = "IES_C_DETECTOR.ies" # ### Define helper functions def create_helper_geometries(project: Project): """Create bodies and faces.""" def create_face(body): f = body.create_face(name="TheFaceF") f.vertices = [0, 0, 0, 1, 0, 0, 0, 1, 0] f.facets = [0, 1, 2] f.normals = [0, 0, 1, 0, 0, 1, 0, 0, 1] f.commit() 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) # ## Model Setup # # ### Load assets # The assets used to run this example are available in the # [PySpeos repository](https://github.com/ansys/pyspeos/) on GitHub. # # > **Note:** Make sure you # > have downloaded simulation assets and set ``assets_data_path`` # > to point to the assets folder. 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. if USE_DOCKER: speos = Speos(channel=default_docker_channel()) else: speos = launcher.launch_local_speos_rpc_server(port=GRPC_PORT) # ### Create a new project # # The only way to create a source using the core layer, is to create it from a project. # The ``Project`` class is instantiated by passing a ``Speos`` instance p = Project(speos=speos) print(p) # ### Source Creation # # **Create locally:** # The mention "local: " is added when printing the source data and information is not yet # pushed to the RPC server intensity_file_path = assets_data_path / IES source1 = p.create_source(name="Luminaire.1", feature_type=SourceLuminaire) # type luminaire source1.intensity_file_uri = intensity_file_path print(source1) # **Push it to the server.** # # After it is committed to the server, the mention "local: " is no more present when printing the # source. source1.commit() print(source1) # **Changing additional Source Properties** # # Setting several more characteristics. intensity_file_path = assets_data_path / IES source2 = p.create_source(name="Luminaire.2", feature_type=SourceLuminaire) source2.intensity_file_uri = intensity_file_path source2.flux.set_radiant() source2.flux.value = 1.0 # select flux radiant # choose the source location [Origin, Xvector, Yvector, Zvector] source2.axis_system = [20, 50, 10, 1, 0, 0, 0, 1, 0, 0, 0, 1] source2.spectrum.set_blackbody() # choose blackbody with default value for the spectrum source2.commit() # Push to the server print(source2) # **Source Instance** # # As mention "local: " is added if it is not yet committed to the server. print(source1) # **Project:** # # Committed feature will appear inside the project information. print(p) # **Update:** # # > **Note:** If you are manipulating a source already committed, don't forget to commit your # > changes. # > If you don't, you will still only watch what is committed on the server. source1.flux.set_radiant() source1.flux.value = 1.2 # modify radiant flux value source1.axis_system = [17, 10, 10, 1, 0, 0, 0, 1, 0, 0, 0, 1] # modify axis system source1.spectrum.set_halogen() # modify spectrum by choosing halogen source1.commit() # Push changes to the server print(source1) # **Reset** # # Possibility to reset local values from the one available in the server. source1.flux.set_luminous().value = 683.0 # modify to luminous flux BUT no commit source1.reset() # reset -> this will apply the server value to the local value the local value will be back to # halogen source1.delete() # delete (to display the local value with the below print) print(source1) # **Delete** # # Once the data is deleted from the server, you can still work with local data and maybe commit # later. # + source2.delete() print(source2) source1.delete() print(p) # - # ## Other Sources Examples # ### Ray-file source # + ray_file_path = assets_data_path / "Rays.ray" source3 = p.create_source(name="Ray-file.1", feature_type=SourceRayFile) # type ray file source3.ray_file_uri = ray_file_path source3.commit() print(source3) # - # + source3.flux.set_luminous() source3.flux.value = 55 source3.commit() print(source3) # - # + source3.delete() # - # ### Surface source # + create_helper_geometries(p) source4 = p.create_source(name="Surface.1", feature_type=SourceSurface) face1 = p.find(name="TheBodyB/TheFaceF", feature_type=Face)[0] face2 = p.find(name="TheBodyC/TheFaceF", feature_type=Face)[0] source4.set_exitance_constant().geometries = [ (face1, False), (face2, True), ] source4.commit() print(source4) # - # + source4.flux.set_luminous_intensity() source4.flux.value = 6 source4.intensity.set_gaussian().axis_system = [10, 50, 20, 1, 0, 0, 0, 1, 0, 0, 0, 1] source4.commit() print(source4) # - # + source4.delete() print(source4) # - # ### Ambient natural light source # + source5 = p.create_source(name="NaturalLight.1", feature_type=SourceAmbientNaturalLight) source5.turbidity = 4 source5.with_sky = True print(source5.zenith_direction) # default zenith direction print(source5.north_direction) # default north direction source5.reverse_north_direction = True print(source5) source5.commit() print(source5) # - # + source5.set_sun_automatic().year = 2026 source5.set_sun_automatic().month = 12 source5.set_sun_automatic().day = 31 source5.set_sun_automatic().hour = 12 source5.set_sun_automatic().minute = 23 source5.set_sun_automatic().longitude = 10 source5.set_sun_automatic().latitude = 45 source5.set_sun_automatic().time_zone = "CST" source5.commit() print(source5) # - # + source5.delete() # - # ### Ambient uniform light source # + ambient_uniform_source = p.create_source(name="Uniform.1", feature_type=SourceAmbientUniform) print(ambient_uniform_source.luminance) # default luminance value print(ambient_uniform_source.zenith_direction) # default zenith direction ambient_uniform_source.mirrored_extent = True ambient_uniform_source.luminance = 500.0 ambient_uniform_source.zenith_direction = [0, 1, 0] ambient_uniform_source.reverse_zenith_direction = True ambient_uniform_source.spectrum.set_blackbody().temperature = 5500 ambient_uniform_source.commit() print(ambient_uniform_source) # - # + ambient_uniform_source.set_sun_manual().direction = [1, 0, 0] ambient_uniform_source.set_sun_manual().reverse_sun = True ambient_uniform_source.commit() print(ambient_uniform_source) # - # + ambient_uniform_source.delete() # - # ### Ambient CIE Standard General Sky light source # + ambient_cie_standard_general_sky_source = p.create_source( name="CieStandardGeneralSky.1", feature_type=SourceAmbientCieStandardGeneralSky ) print(ambient_cie_standard_general_sky_source.luminance) # default luminance value print(ambient_cie_standard_general_sky_source.zenith_direction) # default zenith direction print(ambient_cie_standard_general_sky_source.north_direction) # default north direction print(ambient_cie_standard_general_sky_source.cie_type) # default cie setting ambient_cie_standard_general_sky_source.luminance = 500.0 ambient_cie_standard_general_sky_source.north_direction = [1, 0, 0] ambient_cie_standard_general_sky_source.reverse_north_direction = True ambient_cie_standard_general_sky_source.cie_type = CieType.standard_overcast ambient_cie_standard_general_sky_source.commit() print(ambient_cie_standard_general_sky_source) # - # + ambient_cie_standard_general_sky_source.set_sun_manual().direction = [0, 0.707, 0.707] ambient_cie_standard_general_sky_source.set_sun_manual().reverse_sun = True ambient_cie_standard_general_sky_source.commit() print(ambient_cie_standard_general_sky_source) # - # + ambient_cie_standard_general_sky_source.delete() # - # ### Ambient U.S. Standard light source # + ambient_us_standard_source = p.create_source( name="UsStandard.1", feature_type=SourceAmbientUsStandard ) print(ambient_us_standard_source.zenith_direction) # default zenith direction print(ambient_us_standard_source.north_direction) # default north direction ambient_us_standard_source.zenith_direction = [0, 1, 0] ambient_us_standard_source.north_direction = [1, 0, 0] ambient_us_standard_source.reverse_north_direction = True ambient_us_standard_source.set_sun_manual().direction = [0, 0.707, 0.707] ambient_us_standard_source.commit() print(ambient_us_standard_source) # - # + ambient_us_standard_source.delete() # ### Ambient CIE overcast light source # + ambient_cie_overcast_source = p.create_source( name="CieOvercast.1", feature_type=SourceAmbientCieStandardOvercastSky ) print(ambient_cie_overcast_source.luminance) # default luminance value print(ambient_cie_overcast_source.zenith_direction) # default zenith direction print(ambient_cie_overcast_source.spectrum.set_blackbody().temperature) # default spectrum ambient_cie_overcast_source.luminance = 750.0 ambient_cie_overcast_source.zenith_direction = [0, 1, 0] ambient_cie_overcast_source.reverse_zenith_direction = True ambient_cie_overcast_source.spectrum.set_blackbody().temperature = 4500 ambient_cie_overcast_source.commit() print(ambient_cie_overcast_source) # - # + ambient_cie_overcast_source.delete() # - # ### Ambient environment light source # + source6 = p.create_source(name="Environment.1", feature_type=SourceAmbientEnvironment) image_file_uri = assets_data_path / "stars.exr" source6.image_file_uri = image_file_uri print(source6.zenith_direction) # default zenith direction print(source6.north_direction) # default north direction source6.reverse_north_direction = True print(source6.luminance) # default luminance value source6.set_predefined_color_space().set_color_space_adobergb() source6.set_predefined_color_space().set_color_space_srgb() print(source6.color_space) source6.set_userdefined_color_space().set_white_point_type_d50() print(source6.set_userdefined_color_space().white_point_type) source6.set_userdefined_color_space().red_spectrum = ( assets_data_path / "LG_50M_Colorimetric_short.sv5" / "Red Spectrum.spectrum" ) source6.set_userdefined_color_space().blue_spectrum = ( assets_data_path / "LG_50M_Colorimetric_short.sv5" / "Blue Spectrum.spectrum" ) source6.set_userdefined_color_space().green_spectrum = ( assets_data_path / "LG_50M_Colorimetric_short.sv5" / "Blue Spectrum.spectrum" ) source6.commit() print(source6) # - # + ground_plane = ( p.create_ground_plane() ) # Only usable when there is at least one Ambient Environment Source ground_plane.ground_zenith = [0, 0, 1] ground_plane.ground_origin = [10, 0, 0] ground_plane.ground_height = 1000 ground_plane.commit() print(p) ground_plane2 = p.create_ground_plane() ground_plane2.ground_height = 2000 ground_plane2.commit() print(p) # only 1 ground plane is taken per project, the second overwrites the previous one. # - # + ground_plane.delete() source6.delete() # - # ### Display Source d_src = p.create_source(name="Display.1", feature_type=SourceDisplay) assert isinstance(d_src, SourceDisplay) d_src.x_start = -6 d_src.x_end = 6 d_src.y_start = -5 d_src.y_end = 5 d_src.axis_system = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1] d_src.image_file_uri = assets_data_path / "test_display_source.1.speos" / "pyspeos.png" d_src.commit() print(d_src) # + d_src.delete() # - # ### Thermic source # A thermic source emits radiation based on thermal properties of surfaces. # Two exitance modes are available: **emissive faces** (uniform temperature per face set) # and **temperature field** (temperature distribution read from a file). # #### Emissive faces mode # # Create a thermic source and assign geometry faces with a uniform temperature. # + source_thermic = p.create_source(name="Thermic.1", feature_type=SourceThermic) print(source_thermic) # local: default values before commit # - # + # Reuse geometry created earlier in the example (TheBodyD/TheFaceF) thermic_face = p.find(name="TheBodyD/TheFaceF", feature_type=Face)[0] thermic_face_op = p.create_optical_property(name="ThermicFaceOP.1") thermic_face_op.set_volume_none() thermic_face_op.set_surface_mirror().reflectance = 0 thermic_face_op.geometries = [thermic_face] thermic_face_op.commit() source_thermic.set_emissive_faces().geometries = [(thermic_face, False)] source_thermic.set_emissive_faces().temperature = 3000 # temperature in K source_thermic.commit() print(source_thermic) # - # + # Increase temperature and recommit source_thermic.set_emissive_faces().temperature = 5000 source_thermic.commit() print(source_thermic) # - # #### Temperature field mode # # Switch the same source to temperature field mode. The temperature distribution # is read from an `.OPTTemperatureField` file. An axis plane positions the field # in the scene, and a surface optical property (SOP) controls reflectance. # + temperature_field_file = assets_data_path / "TemperatureField_Tank.OPTTemperatureField" source_thermic.set_temperature_field().temperature_field_uri = temperature_field_file source_thermic.set_temperature_field().axis_plane = [0, 0, 0, 1, 0, 0, 0, 1, 0] print(source_thermic.set_temperature_field().axis_plane) # default axis plane # - # + # Adjust SOP mirror reflectance for the temperature field surface source_thermic.set_temperature_field().sop.set_surface_mirror().reflectance = 10 source_thermic.commit() print(source_thermic) # - # + # Reset to server state and verify temperature field uri is preserved source_thermic.reset() print(source_thermic.set_temperature_field().temperature_field_uri) # - # + source_thermic.delete() # - # ### Clean up # When creating sources, this creates some intermediate objects (spectrums, intensity templates). # # Deleting a source does not delete in cascade those objects # because they could be used by some other entities from core layer. # # Then at the end of the example, we just clean all databases # + for item in speos.client.intensity_templates().list() + speos.client.spectrums().list(): item.delete() speos.close()