-
Notifications
You must be signed in to change notification settings - Fork 30
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
Unify stage coordinates' units across instamatic #109
Conversation
…tions and `float_deg` stage tilts
@stefsmeets I plan to go through existing uses of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lovely work, didn't know you could do this with types. I'm not aware of any specific bits that use the 'FEI' way, mostly the code converts to the same scale in the microscope class.
Have you managed to get this to work in mypy? We should consider adding that as a test if we are adding more and more types to ensure that the types are actually enforced and checked.
I left some comments in the code to clean up some of the types.
Let me know once it is ready and I'll happily merge this.
x: int_nm = None, | ||
y: int_nm = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x: int_nm = None, | |
y: int_nm = None, | |
x: Optional[int_nm] = None, | |
y: Optional[int_nm] = None, |
x: int_nm = None, | ||
y: int_nm = None, | ||
z: int_nm = None, | ||
a: float_deg = None, | ||
b: float_deg = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x: int_nm = None, | |
y: int_nm = None, | |
z: int_nm = None, | |
a: float_deg = None, | |
b: float_deg = None, | |
x: Optional[int_nm] = None, | |
y: Optional[int_nm] = None, | |
z: Optional[int_nm] = None, | |
a: Optional[float_deg] = None, | |
b: Optional[float_deg] = None, |
x: int_nm = None, | ||
y: int_nm = None, | ||
z: int_nm = None, | ||
a: float_deg = None, | ||
b: float_deg = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x: int_nm = None, | |
y: int_nm = None, | |
z: int_nm = None, | |
a: float_deg = None, | |
b: float_deg = None, | |
x: Optional[int_nm] = None, | |
y: Optional[int_nm] = None, | |
z: Optional[int_nm] = None, | |
a: Optional[float_deg] = None, | |
b: Optional[float_deg] = None, |
x: int_nm = None, | ||
y: int_nm = None, | ||
z: int_nm = None, | ||
a: float_deg = None, | ||
b: float_deg = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x: int_nm = None, | |
y: int_nm = None, | |
z: int_nm = None, | |
a: float_deg = None, | |
b: float_deg = None, | |
x: Optional[int_nm] = None, | |
y: Optional[int_nm] = None, | |
z: Optional[int_nm] = None, | |
a: Optional[float_deg] = None, | |
b: Optional[float_deg] = None, |
x: int_nm = None, | ||
y: int_nm = None, | ||
z: int_nm = None, | ||
a: float_deg = None, | ||
b: float_deg = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x: int_nm = None, | |
y: int_nm = None, | |
z: int_nm = None, | |
a: float_deg = None, | |
b: float_deg = None, | |
x: Optional[int_nm] = None, | |
y: Optional[int_nm] = None, | |
z: Optional[int_nm] = None, | |
a: Optional[float_deg] = None, | |
b: Optional[float_deg] = None, |
x: int_nm = None, | ||
y: int_nm = None, | ||
z: int_nm = None, | ||
a: float_deg = None, | ||
b: float_deg = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x: int_nm = None, | |
y: int_nm = None, | |
z: int_nm = None, | |
a: float_deg = None, | |
b: float_deg = None, | |
x: Optional[int_nm] = None, | |
y: Optional[int_nm] = None, | |
z: Optional[int_nm] = None, | |
a: Optional[float_deg] = None, | |
b: Optional[float_deg] = None, |
@stefsmeets Addressing individual issues:
PS: Some examples of scripts that would need rewriting if changed to dataclass:
x0, y0, z0, a0, b0 = ctrl.stage.get()
xpoint, ypoint, zpoint, aaa, bbb = self.ctrl.stage.get()
self.logger.info(
f'Stage position: x = {xpoint}, y = {ypoint}. Z height adjusted to {zpoint}. Tilt angle x {aaa} deg, Tilt angle y {bbb} deg'
)
|
…gePositionTuple`
…ng them as floats `:.1f`
…ore than typing
…'t have `Annotated`
…ha, beta are not numbers
…mpatible with python3.7, 3.8
… `float` type, not `int`
I took the liberty to modify |
Looks good, give me a tag when this PR is ready and I'll pull the trigger on both this and instamatic-dev/instamatic-tecnai-server#2 |
@stefsmeets I have ran it on my microscope and had no issues, I think it is ready from my point of view. I had some time to look at the PR "from a distance" and my newfound concern is that one feature of this PR that is not highlighted is that x/y/z now MUST be integers. Based on the hints and comments, I think this was always supposed to be the case, but I am concerned that in many places (such as in tests) x/y/z were used as floats, and forcing them into ints might have some unexpected consequences. I would really love to hear your opinion on that before merging (@viljarjf ?), especially since coercing x/y/z info floats is just as good from my perspective and might avoid these issues. What do you think? Edit: Benefits of floats over int: bigger precision, better handling for simulation, future proofing. Benefits of int over float: easier presentation, consistency, comparisons, simplicity. |
I have very little skin in the game anymore. On the JEOL it worked with ints, and Python converts to float on the fly if necessary. If it works on the Tecnai that is good enough for me. Happy to wait for a thumbs up from @viljarjf though. The tests were made with a hypothetical scenario in mind, the numbers/types are meaningless, it's just checking whether the move in projection/along the optical axis is numerically correct. |
@stefsmeets Thank you, that is good to hear. Well, regarding experience, I still have very little of it and I don't think the state of hardware has changed drastically in the last few years. Just to be sure, I also asked a colleague in the lab who suggests that the sphere of confusion for most goniometers is still ~over 300 nm and recent experiments with significantly more precise piezo-stages were not successful so it is likely x/y/z might stay integers for quite some time. |
Nice work! I had something like this in my backlog too. |
Yeah, this is true. It is impossible to move such small amounts. I played with the piezo stage many years ago, and even that is not 1 nm precision. I'm going to merge this PR, thanks for the nice work @Baharis ! |
The context
Currently, instamatic uses multiple different types to handle stage position variables. Stage length coordinates x, y, z are sometimes expressed as
int
in nanometers (Jeol microscope), sometimes asfloat
in microns (FEI microscopes); sometimes, the same values are also supplied as neitherint
norfloat
but rather their numpy counterparts. This is especially harmful for a server without numpy that is unable to convertnumpy.int32
toint
. Similarly, even though documentation suggests the angles should always be instances ofint
(though this is also not very clear), in numerous places the angles are instances offloat
:From
src/instamatic/microscope/interface/fei_microscope.py
,FEIMicroscope
lines 148–156 — micron distances,float
degrees:From
src/instamatic/microscope/interface/jeol_microscope.py
,JeolMicroscope
lines 282–285 — nanometer distances,int
degrees:From
src/instamatic/controller.py
,find_eucentric_height
lines 404–414 (excerpt) — nanometernumpy.int_
stage.z
:This PR aims to unify the units used to express the position of microscope stage across instamatic. To this aim, it firstly introduces a small module
instamatic.typing
with two new Annotated types:int_nm
andfloat_deg
. These types are thin wrappers aroundint
andfloat
i.e.type(int_nm(1.0)) == int
. The sole purpose of the new annotated wrappers is to display the unit in type hints in addition to the comments. The only difference betweenint
andint_nm
is an annotation that appears next to the later in the integrated development environment:int_nm
andfloat_deg
are just type hints; in order to enforce actual change, some interface methods, including these passed tostage.get
andstage.set
, have been modified to now always accept and return:x
,y
,z
as annotated typeint_nm
, actual typeint
a
,b
as annotated typefloat_deg
, actual typefloat
Major changes
FEIMicroscope
andFEISimuMicroscope
'sget
/setStagePosition
now communicate distances in nanometers instead of microns, making their behavior consistent with the rest of Instamatic.Minor changes
int_nm
&float_deg
type;Annotated
wrappers aroundint
andfloat
and affect only type hinting;Annotated
is not available, they are just aliases of (equal to)int
andfloat
.setStagePosition
input x/y/z/a/b input is now correctly marked astyping.Optional
;getStagePosition
andsetStagePosition
methods now return an instance ofStagePositionTuple
.StagePositionTuple
was moved to a newsrc/instamatic/microscope/typing.py
to remove risk of circular imports and allow use as type hints;z = {zc} nm
instead ofz = {zc:.1f} nm
as the trailing decimal is irrelevant (always zero).Bug fixes
microscope.components.stage:Stage.set
could raiseTypeError
if supplied only partial optional input due to use ofround
.microscope.client.py:MicroscopeClient._eval_dct
, change hint type fromdict
toDict
to comply with python 3.7.Effect on the codebase
This change might be breaking for existing script. If there are any scripts designed and used on FEI machines only that use hard-coded distances expressed in microns, they will need to be updated to utilize nanometers instead. However, with this step they should become a step closer to be fully interoperable between machines.
To the best of my understanding the change of units in FEI should have no tangible effect on Joel microscopes whatsoever.
Compatibility note
This PR is accompanied by and should be merged alongside PR #2 of Instamatic Tecnai Server.