Skip to content

feat: intensitysensor #653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft

feat: intensitysensor #653

wants to merge 12 commits into from

Conversation

StefanThoene
Copy link
Collaborator

@StefanThoene StefanThoene commented Jul 3, 2025

Description

Create Sensor class

Issue linked

Checklist

  • I have tested my changes locally.
  • I have added necessary documentation or updated existing documentation.
  • I have followed the coding style guidelines of this project.
  • I have added appropriate tests (unit, integration, system).
  • I have reviewed my changes before submitting this pull request.
  • I have linked the issue or issues that are solved by the PR if any.
  • I have assigned this PR to myself.
  • I have made sure that the title of my PR follows Conventional commits style (e.g. feat: add optical property)
  • I have agreed with the Contributor License Agreement (CLA).

@github-actions github-actions bot added the enhancement New features or code improvements label Jul 3, 2025
Copy link

codecov bot commented Jul 3, 2025

Codecov Report

Attention: Patch coverage is 25.17241% with 217 lines in your changes missing coverage. Please review.

Project coverage is 85.87%. Comparing base (b80e61e) to head (a8a5933).

Files with missing lines Patch % Lines
src/ansys/speos/core/sensor.py 25.17% 217 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #653      +/-   ##
==========================================
- Coverage   89.03%   85.87%   -3.16%     
==========================================
  Files          39       39              
  Lines        5581     5870     +289     
==========================================
+ Hits         4969     5041      +72     
- Misses        612      829     +217     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

return False

@nearfield.setter
def nearfield(self, value):
Copy link
Collaborator

Choose a reason for hiding this comment

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

if using property/setter methods, you will need to call the responding parameters setter methods as well.
i.e. for near field as true, then you need set call distance and cell diameter as well.
otherwise, the default setting will not happen.

self.x_sampling = 180

@property
def x_start(self) -> float:
Copy link
Collaborator

@pluAtAnsys pluAtAnsys Jul 3, 2025

Choose a reason for hiding this comment

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

then x_start will be shown in the auto-completion methods list even the orientation is set as conoscopic.... I am not sure if this is good...

Copy link
Collaborator Author

@StefanThoene StefanThoene Jul 4, 2025

Choose a reason for hiding this comment

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

yes butt you can't set it and it will return none if the sensor is in conoscopic mode

@pluAtAnsys pluAtAnsys linked an issue Jul 3, 2025 that may be closed by this pull request
pluAtAnsys and others added 3 commits July 4, 2025 09:50
change from cell integration angle to cell diameter
rename theta_max_sampling
add default values for nearfield sub elements
@github-actions github-actions bot added the testing Anything related to tests label Jul 4, 2025
By default, ``0.3491``
"""
if self.nearfield:
diameter = self.cell_distance * np.tan(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Suggested change
diameter = self.cell_distance * np.tan(
diameter = 2* self.cell_distance * np.tan(

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i think that needs to be changed like that

return False

@nearfield.setter
def nearfield(self, value):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing docstring :)

raise TypeError("Sensor position is not in nearfield")

@property
def cell_diameter(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to keep the cell_diameter?
It implies to transform cell_integration_angle and cell_distance to get it. Whereas this kind of operation could be done by the customer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The Speos Ui only has Celld iameter

Comment on lines +3900 to +3902
if type(self._type) is str:
return self._type
elif isinstance(self._type, BaseSensor.Colorimetric):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if type(self._type) is str:
return self._type
elif isinstance(self._type, BaseSensor.Colorimetric):
if isinstance(self._type, BaseSensor.Colorimetric):

Copy link
Collaborator

Choose a reason for hiding this comment

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

(sugg) Because the else will do exactly same as the first if

)
return self._type

def set_layer_type_none(self) -> SensorRadiance:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def set_layer_type_none(self) -> SensorRadiance:
def set_layer_type_none(self) -> SensorXMPIntensity:

self,
) -> Union[
None,
SensorIrradiance,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is not correct, but I don't know why it is here.

Suggested change
SensorIrradiance,

SensorIrradiance,
BaseSensor.LayerTypeFace,
BaseSensor.LayerTypeSequence,
BaseSensor.LayerTypeIncidenceAngle,
Copy link
Collaborator

Choose a reason for hiding this comment

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

No LayerTypeIncidenceAngle for Intensity sensor

Suggested change
BaseSensor.LayerTypeIncidenceAngle,

Comment on lines +3953 to +3956
ansys.speos.core.sensor.SensorIrradiance,\
ansys.speos.core.sensor.BaseSensor.LayerTypeFace,\
ansys.speos.core.sensor.BaseSensor.LayerTypeSequence,\
ansys.speos.core.sensor.BaseSensor.LayerTypeIncidenceAngle\
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ansys.speos.core.sensor.SensorIrradiance,\
ansys.speos.core.sensor.BaseSensor.LayerTypeFace,\
ansys.speos.core.sensor.BaseSensor.LayerTypeSequence,\
ansys.speos.core.sensor.BaseSensor.LayerTypeIncidenceAngle\
ansys.speos.core.sensor.BaseSensor.LayerTypeFace,\
ansys.speos.core.sensor.BaseSensor.LayerTypeSequence,\

]
Instance of Layertype Class for this sensor feature
"""
return self._layer_type
Copy link
Collaborator

Choose a reason for hiding this comment

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

Then what is returned when layer type is None, or Source?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess it could be harmonized like it is done for the type
-> property type
-> properties spectral and colorimetric

def x_end(self) -> float:
"""The maximum value on x-axis (deg).
By default, ``45``.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am wondering if a getter is the correct place to indicate default value.

@echambla
Copy link
Collaborator

@StefanThoene I see clearly how you want to use the properties.
Indeed all properties are available, and the user gets an error if he tries to use the wrong one depending on the current object.
I think this can work as well as the fluent api.

My only concern is the following:
As mentioned by Pengyuan, the user will have access to a huge number of properties, and will maybe be a bit lost to understand what can really be used or not.
Whereas the fluent api was guiding him step by step.

The decision is not only on my side.
Maybe it would be good to have some other AEs feedback, also @jomadec.

BUT, this is a really good feature to try this, you are right.
Maybe it would be good to complete with some basic examples so that we have a more specific idea about how it is used?

Thanks a lot for you work

Copy link
Contributor

@SMoraisAnsys SMoraisAnsys left a comment

Choose a reason for hiding this comment

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

Here are some comments. The comments on "return None" and "const variable" are echoed multiple times but I've stopped doing it and some cases are not highlighted by the review. Please have a look though :)

Concerning the use of property, I'm clearly approving this approach. As I said before, correct use of property improves readability and user experience. Speaking as a Python user, this is something common in Python packages and it is strongly leveraged in other PyAnsys packages. @echambla If there is a reason to focus on fluent API please contact me so that you can clarify that topic to me

Comment on lines +3830 to +3834
"""Property containing if the sensor is positioned in nearfield or infinity."""
if self._sensor_template.intensity_sensor_template.HasField("near_field"):
return True
else:
return False
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"""Property containing if the sensor is positioned in nearfield or infinity."""
if self._sensor_template.intensity_sensor_template.HasField("near_field"):
return True
else:
return False
"""Return True if the sensor is positioned in nearfield, otherwise False."""
return self._sensor_template.intensity_sensor_template.HasField("near_field")

I'm guessing that HasField returns a boolean. If not, ignore this comment :)

Comment on lines +3841 to +3842
self.cell_distance = 10
self.cell_diameter = 0.3491
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you define CELL_DISTANCE and CELL_DIAMETER at the beginning of the file and leverage it in the file (both code and docs) ? That will help you with maintenance and testing since changing the value in a single place will be applyed everywhere the "constant" variable is imported and used.

self.cell_diameter = 0.3491
else:
if self._sensor_template.intensity_sensor_template.HasField("near_field"):
self._sensor_template.intensity_sensor_template.ClearField("near_field")
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be worth to warn your user if HasField("near_field") is False ? Otherwise you can just use elif right ?

def cell_distance(self):
"""Distance of the Detector to origin in mm.
By default, ``10``
Copy link
Contributor

Choose a reason for hiding this comment

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

cf const var

Comment on lines +3853 to +3856
if self.nearfield:
return self._sensor_template.intensity_sensor_template.near_field.cell_distance
else:
return None
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if self.nearfield:
return self._sensor_template.intensity_sensor_template.near_field.cell_distance
else:
return None
if self.nearfield:
return self._sensor_template.intensity_sensor_template.near_field.cell_distance

Not sure the else is needed. By default python returns None if nothing is returned

self._set_default_dimension_values()

def set_viewing_direction_from_source(self):
"""Set viewing direction from Source Looking At Sensor."""
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"""Set viewing direction from Source Looking At Sensor."""
"""Set viewing direction from source looking at sensor."""

self._sensor_template.intensity_sensor_template.from_source_looking_at_sensor.SetInParent()

def set_viewing_direction_from_sensor(self):
"""Set viewing direction from Sensor Looking At Source."""
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"""Set viewing direction from Sensor Looking At Source."""
"""Set viewing direction from sensor looking at source."""

def x_start(self) -> float:
"""The minimum value on x-axis (deg).
By default, ``45``.
Copy link
Contributor

Choose a reason for hiding this comment

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

echo const var

"""
template = self._sensor_template.intensity_sensor_template
if template.HasField("intensity_orientation_conoscopic"):
return None
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you log a warning here if that's not something that should be used with conoscopic ?

@pluAtAnsys If you really want to avoid that kind of behavior, you could create specific classes for x_as_parallel and conoscopic ? Each would inherit from SensorXMPIntensity and have a subset of dedicated properties. Note that this would start to be verbose if you want to move your sensor from X to Conoscopic.

else:
raise TypeError("Only Conoscopic Sensor has theta_max dimension")

def set_type_photometric(self) -> SensorXMPIntensity:
Copy link
Contributor

Choose a reason for hiding this comment

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

Just wondering if set_type_photometric, set_type_colorimetric, set_type_radiometric, set_type_spectral, set_layer_type_none, set_layer_type_source, set_layer_type_face and set_layer_type_sequence should be hidden or be homogenized ? Some of them return self other return self._type.
If possible I would hidde them if that can be set in the init through some parameter and if this isn't supposed to change while the object is alive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New features or code improvements testing Anything related to tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add intensity sensor
5 participants