Skip to content
Draft
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0f11fee
Pushed draft branch for thermic source
ansmcailler Jul 2, 2025
87864cb
Merge branch 'main' into feat/thermicsource
Jul 2, 2025
9debcf0
Merge branch 'main' into MCR_thermicsource
pluAtAnsys Jul 2, 2025
ddf82a6
ci: auto fixes from pre-commit.com hooks.
pre-commit-ci[bot] Jul 2, 2025
a12a993
Merge remote-tracking branch 'origin/MCR_thermicsource' into feat/the…
Jul 2, 2025
5f00348
Re-adding full project.py script
ansmcailler Jul 2, 2025
5466a49
ci: auto fixes from pre-commit.com hooks.
pre-commit-ci[bot] Jul 2, 2025
6c6d0d7
chore: adding changelog file 651.added.md [dependabot-skip]
pyansys-ci-bot Jul 2, 2025
3a9263d
run pre-commit to add missing docstring
Jul 2, 2025
dcbb88a
refactor the test code into example as temp location.
Jul 2, 2025
fd1f665
Merge remote-tracking branch 'origin/feat/thermicsource' into feat/th…
Jul 2, 2025
7de324e
add key to the Intensity class to be filled
Jul 3, 2025
ee721b9
remove no need to check if it is temperature field
Jul 3, 2025
76274b5
Fix issue with intensity template
echambla Jul 3, 2025
e5f8132
property and setter method
Jul 3, 2025
81f0f61
add unittest
Jul 3, 2025
5ebe14b
add example
Jul 3, 2025
ff25390
improve the unittest coverage
Jul 3, 2025
8092f7e
Merge branch 'main' into feat/thermicsource
pluAtAnsys Jul 4, 2025
54501ef
Merge branch 'main' into feat/thermicsource
pluAtAnsys Jul 7, 2025
a0c6f78
Merge branch 'main' into feat/thermicsource
pluAtAnsys Jul 7, 2025
47f7a83
Merge branch 'main' into feat/thermicsource
pluAtAnsys Jul 9, 2025
2e8be76
Merge branch 'main' into feat/thermicsource
pluAtAnsys Jul 30, 2025
9b60e42
Merge branch 'main' into feat/thermicsource
pluAtAnsys Aug 4, 2025
ff5d345
Finalized thermic source basic development (incl unit test)
ansmcailler Sep 5, 2025
2855c91
ci: auto fixes from pre-commit.com hooks.
pre-commit-ci[bot] Sep 5, 2025
ae44b32
chore: adding changelog file 651.added.md [dependabot-skip]
pyansys-ci-bot Sep 5, 2025
4e60a56
Merge branch 'main' into feat/thermicsource
pluAtAnsys Sep 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/651.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add thermic source
17 changes: 17 additions & 0 deletions examples/core/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
SourceLuminaire,
SourceRayFile,
SourceSurface,
SourceThermic,
)

# -
Expand Down Expand Up @@ -254,6 +255,22 @@ def create_face(body):
source5.delete()
# -

# ### Thermic source

# +
source6 = p.create_source(name="Thermic.1", feature_type=SourceThermic)
source6.set_emissive_faces(geometries=[(GeoRef.from_native_link("TheBodyB/TheFaceF"), False)])
print(source6)

source6.commit()
print(source6)
# -

# +
source6.delete()
# -


# When creating sources, this creates some intermediate objects (spectrums, intensity templates).
#
# Deleting a source does not delete in cascade those objects
Expand Down
20 changes: 20 additions & 0 deletions examples/core/source_thermic_example_tmp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pathlib import Path
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please consider refactor this into unittest or example.
if needed, add the speos file used into the tests/assert folder


import ansys.speos.core as core
from ansys.speos.core.launcher import launch_local_speos_rpc_server
import ansys.speos.core.source as source

speos = launch_local_speos_rpc_server(version="252")
speos_file = Path.cwd() / "Speos Bench" / "Inverse.1.1.speos" / "Inverse.1.1.speos"
project = core.Project(speos=speos, path=speos_file)
mysource = project.find(name=".*", name_regex=True, feature_type=source.SourceThermic)
mysource[0].set_emissive_faces_temp(value=100)
mysource[0].commit()
mysimu = project.find(name=".*", name_regex=True, feature_type=core.simulation.SimulationInverse)
mysimu = mysimu[0]
new_source = project.create_source(name="test", feature_type=source.SourceThermic)
new_source.commit()
print(project)

# mysimu.compute_CPU()
# print(mysimu.result_list)
20 changes: 19 additions & 1 deletion src/ansys/speos/core/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
SourceLuminaire,
SourceRayFile,
SourceSurface,
SourceThermic,
)
from ansys.speos.core.speos import Speos

Expand Down Expand Up @@ -158,7 +159,9 @@ def create_source(
description: str = "",
feature_type: type = SourceSurface,
metadata: Optional[Mapping[str, str]] = None,
) -> Union[SourceSurface, SourceRayFile, SourceLuminaire, SourceAmbientNaturalLight]:
) -> Union[
SourceSurface, SourceRayFile, SourceLuminaire, SourceAmbientNaturalLight, SourceThermic
]:
"""Create a new Source feature.

Parameters
Expand Down Expand Up @@ -224,6 +227,13 @@ def create_source(
description=description,
metadata=metadata,
)
case "SourceThermic":
feature = SourceThermic(
project=self,
name=name,
description=description,
metadata=metadata,
)
case _:
msg = "Requested feature {} does not exist in supported list {}".format(
feature_type, [SourceSurface, SourceLuminaire, SourceRayFile]
Expand Down Expand Up @@ -438,6 +448,7 @@ def find(
SourceSurface,
SourceLuminaire,
SourceRayFile,
SourceThermic,
SensorIrradiance,
SensorRadiance,
SensorCamera,
Expand Down Expand Up @@ -804,6 +815,13 @@ def _fill_features(self):
source_instance=src_inst,
default_values=False,
)
elif src_inst.HasField("thermic_properties"):
src_feat = SourceThermic(
project=self,
name=src_inst.name,
source_instance=src_inst,
default_values=False,
)
if src_feat is not None:
self._features.append(src_feat)

Expand Down
140 changes: 140 additions & 0 deletions src/ansys/speos/core/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,146 @@ def delete(self) -> SourceSurface:
return self


class SourceThermic(BaseSource):
"""ThermicSource.

By default, a flux from intensity file is chosen, with an incandescent spectrum.

Parameters
----------
project : ansys.speos.core.project.Project
Project that will own the feature.
name : str
Name of the feature.
description : str
Description of the feature.
By default, ``""``.
metadata : Optional[Mapping[str, str]]
Metadata of the feature.
By default, ``{}``.
default_values : bool
Uses default values when True.
"""

def __init__(
self,
project: project.Project,
name: str,
description: str = "",
metadata: Optional[Mapping[str, str]] = None,
source_instance: Optional[ProtoScene.SourceInstance] = None,
default_values: bool = True,
) -> None:
if metadata is None:
metadata = {}

super().__init__(
project=project,
name=name,
description=description,
metadata=metadata,
source_instance=source_instance,
)
self._speos_client = self._project.client
self._name = name

self._intensity = Intensity(
speos_client=self._speos_client,
name=name + ".Intensity",
key=self._source_template.thermic.intensity_guid,
)

if default_values:
# Default values
self.set_emissive_faces(geometries=[])
self.emissive_faces_temperature = 2000

def set_emissive_faces(self, geometries: List[tuple[GeoRef, bool]]) -> SourceThermic:
"""Set emssive faces for thermic source.

Parameters
----------
geometries : List[tuple[ansys.speos.core.geo_ref.GeoRef, bool]]
List of (face, reverseNormal).

Returns
-------
ansys.speos.core.source.ThermicSource
Thermic source.
"""
self._source_instance.thermic_properties.emissive_faces_properties.ClearField("geo_paths")
if geometries != []:
my_list = [
ProtoScene.GeoPath(geo_path=gr.to_native_link(), reverse_normal=reverse_normal)
for (gr, reverse_normal) in geometries
]
self._source_instance.thermic_properties.emissive_faces_properties.geo_paths.extend(
my_list
)
self._source_template.thermic.emissives_faces.SetInParent()
return self

@property
def emissive_faces_temperature(self) -> float:
"""Get temperature settings for emissive faces.

Returns
-------
float
temperature settings for emissive faces.

"""
if self._source_template.thermic.HasField("emissives_faces"):
return self._source_template.thermic.emissives_faces.temperature
else:
raise AttributeError("This feature is not defined as emissive faces.")

@emissive_faces_temperature.setter
def emissive_faces_temperature(self, value: float) -> None:
"""Set temperature settings for emissive faces.

Parameters
----------
value: float
temperature settings for emissive faces.

Returns
-------
None

"""
self._source_template.thermic.emissives_faces.temperature = value

def commit(self) -> SourceThermic:
"""Save feature: send the local data to the speos server database.

Returns
-------
ansys.speos.core.source.SourceThermic
Source feature.
"""
# intensity
self._intensity.commit()
self._source_template.thermic.intensity_guid = self._intensity.intensity_template_link.key

# source base
super().commit()
return self

def reset(self) -> SourceThermic:
"""Reset feature: override local data by the one from the speos server database.

Returns
-------
ansys.speos.core.source.SourceThermic
Source feature.
"""
self._intensity.reset()
# source base
super().reset()
return self


class BaseSourceAmbient(BaseSource):
"""
Super Class for ambient sources.
Expand Down
48 changes: 48 additions & 0 deletions tests/core/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
SourceLuminaire,
SourceRayFile,
SourceSurface,
SourceThermic,
)
from tests.conftest import test_path

Expand Down Expand Up @@ -525,6 +526,53 @@ def test_create_natural_light_source(speos: Speos):
source2.delete()


def test_create_thermic_source(speos: Speos):
"""Test creation of thermic source."""
p = Project(speos=speos)

source1 = p.create_source(name="Thermic Source", feature_type=SourceThermic)
# Geometry
root_part = p.create_root_part().commit()
body_b1 = root_part.create_body(name="TheBodyB1").commit()
face_b1_f1 = (
body_b1.create_face(name="TheFaceF1")
.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()
)
# Optical Property
p.create_optical_property("OpaqueMirror50").set_volume_opaque().set_surface_mirror(
reflectance=50
).set_geometries(geometries=[body_b1.geo_path]).commit()

source1.set_emissive_faces(geometries=[(face_b1_f1.geo_path, False)])
source1.commit()

assert source1._source_template.thermic.HasField("emissives_faces")
assert source1._source_template.thermic.emissives_faces.temperature == 2000
assert source1._source_instance.HasField("thermic_properties")
assert source1._source_instance.thermic_properties.HasField("emissive_faces_properties")
assert (
source1._source_instance.thermic_properties.emissive_faces_properties.geo_paths[0].geo_path
== "TheBodyB1/TheFaceF1"
)
assert (
source1._source_instance.thermic_properties.emissive_faces_properties.geo_paths[
0
].reverse_normal
is False
)

intensity = speos.client[source1.source_template_link.get().thermic.intensity_guid]
assert intensity.get().HasField("cos")
assert intensity.get().cos.N == 1

source1.emissive_faces_temperature = 3500
assert source1._source_template.thermic.emissives_faces.temperature == 3500
assert source1.emissive_faces_temperature == 3500


def test_keep_same_internal_feature(speos: Speos):
"""Test regarding source internal features (like spectrum, intensity).

Expand Down
Loading