Skip to content
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

Telescope perturbations #304

Merged
merged 13 commits into from
Feb 8, 2023
Merged

Telescope perturbations #304

merged 13 commits into from
Feb 8, 2023

Conversation

jmeyers314
Copy link
Member

PR adds rigid body perturbations of telescope optics and surface figure perturbations as Zernike polynomials. Also restores functionality where intra/extra focal wavefront sensors are automatically displaced.

I didn't attempt bending modes or gravitational surface figure errors here; that'll be next.

@jmeyers314
Copy link
Member Author

A couple potential upgrade thoughts for this PR:

For now, I hard coded the config processing of angles to require radians. For instance:

telescope:
    file_name: LSST_r.yaml
    perturb:
        rotX:
            M2: 1.e-3  # radians implicit
        rotY:
            LSSTCamera: 1.e-3  # radians implicit

it might be nice to use the normal config angle processing, e.g.,

telescope:
    file_name: LSST_r.yaml
    perturb:
        rotX:
            M2: 1.2 arcmin
        rotY:
            LSSTCamera: 0.03 deg

I suppose that's probably doable by inserting some galsim.config.ParseValue statements inside TelescopeLoader.getKwargs.

For shifts and Zernike perturbation amplitudes, the units are implicitly meters. A stretch goal might be to also make this flexible, e.g.:

telescope:
    file_name: LSST_r.yaml
    perturb:
        shift:
            M2: [0.0, 0.0, 1e-3] m
            LSSTCamera: [0.0, 0.1, 0.0] mm
telescope:
    file_name: LSST_r.yaml
    perturb:
        Zernike:
            idx: [4, 5]
            val: [1.e-4, 3.e-4] mm

That might properly require some GalSim dev, (I don't think we had a reason to process physical lengths before), or we could do it special for imsim telescope loading.

imsim/batoid_wcs.py Outdated Show resolved Hide resolved
for group in perturb:
for ptype, pvals in group.items():
if ptype == 'shift':
for optic, shift in pvals.items():
Copy link
Contributor

Choose a reason for hiding this comment

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

This will only work if shift is a literal float value. I think we probably want this to be something that gets parsed in the normal way for config items so people could set them to change randomly according to some config-specified prescription or be lookups from some file or whatever.
To make that work, just change this to:

for optic in pvals.keys():
    shift = galsim.config.ParseValue(pvals, optic, base, float)

You would also need to add base as a parameter to this function, but that's straightforward.
Likewise for the rest ofc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. Is there a good default value for base that'd make it convenient to still use load_telescope outside of the config context, e.g.,

telescope = imsim.load_telescope("LSST_r.yaml", perturb={'shift':{'M2': [0.0, 0.0, 1e-6]}})

?

I see that if I pass base=None to ParseValue it'll default to base=config. What's the use case for base != config?

Copy link
Member Author

Choose a reason for hiding this comment

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

Also, I think the ParseValue snippet above may only work for a single scalar float, not a list of 3 floats?

Copy link
Contributor

@rmjarvis rmjarvis Nov 17, 2022

Choose a reason for hiding this comment

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

What's the use case for base != config?

Normally config is the current local config dict. E.g. here, it's pvals, the dict we're working on at the moment. base should always be the base dict, where things like the main rng stuff and eval_variables, etc. can live. Having a default base=None is ok, but I would have thought we'd always be calling this in the context of a config parsing step, so you'd always have a dict to pass it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I think the ParseValue snippet above may only work for a single scalar float, not a list of 3 floats?

Yes. If you want to allow either lists or floats, they would have to be checked explicitly I guess. They aren't considered equivalent types in the config parsing. (Lists are allowed with value_type=list, but it's considered a different thing than when value_type=float.)

Copy link
Member Author

Choose a reason for hiding this comment

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

ParseValue(pvals, optic, base, list) works, but I suppose this doesn't type check for a list-of-floats.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. I think if you tell it to parse a list, it will just return the list as is, without iterating further to parse each item as a float.

@rmjarvis
Copy link
Contributor

it might be nice to use the normal config angle processing, e.g.,

This would be easy to implement with the ParseValue bit by setting the type (last argument) to galsim.Angle rather than float. The m vs mm, etc. units would be a littl eharder. We don't have any "distance" type currently in GalSim, although we could add that.
Probably an arbitrary astropy.Quantity would be the way to do that, which could either be given as a string (eval into a Quantity) or separate float and unit items (so the float could be further specified as a parsed value).

@rmjarvis
Copy link
Contributor

Ref. GalSim-developers/GalSim#1194

@jmeyers314 jmeyers314 force-pushed the telescope_perturbations branch from c44a4e9 to 378b24f Compare November 17, 2022 18:00
@jmeyers314
Copy link
Member Author

After looking at this again with fresh eyes, I think I might have the proposed hierarchy backwards. For example, instead of:

                    perturb:
                        shift:
                            M1: [1.e-3, 1.e-3, 0.0]
                            LSSTCamera: [0.0, 0.0, -1.e-3]

it's probably a bit more natural to collect all perturbations of a given optic together:

                    perturb:
                        M1:
                            shift: [1.e-3, 1.e-3, 0.0]
                        LSSTCamera:
                            shift: [0.0, 0.0, -1.e-3]

One could also break apart that shift key/value into shiftX, shiftY, shiftZ and parse with the value_type=float enforced (instead of value_type=list with no easily enforceable constraint on the length of the list or the type of the list items).

@rmjarvis
Copy link
Contributor

rmjarvis commented Jan 5, 2023

I like the proposed transposition. That feels more natural to me too.

One could also break apart that shift key/value into shiftX, shiftY, shiftZ and parse with the value_type=float enforced (instead of value_type=list with no easily enforceable constraint on the length of the list or the type of the list items).

It's not out-of-the-box enforceable, but it wouldn't be too easy to write a short helper function that would parse something as a 3-component list.

parse_xyz(xyz, base):
    if not isinstance(xyz, list) or len(xyz) != 3:
        raise ValueError("Expecting a list of 3 elements)
    parsed_xyz, safe = zip([galsim.config.ParseValue(xyz, i, base, float) for i in range(3)])
    safe = all(safe)
    return parsed_xyz, safe

Then in the main function, you would have things like

if ptype == 'shift':
    shift, safe = parse_xyz(pval, base)
    all_safe = all_safe and safe

@jmeyers314
Copy link
Member Author

I think we probably want this to be something that gets parsed in the normal way for config items so people could set them to change randomly according to some config-specified prescription

Does this apply to the Zernike coefficients as well? I.e., is there a config mechanism to make random length-n lists and not just individual random values that we should make available?

@rmjarvis
Copy link
Contributor

Does this apply to the Zernike coefficients as well? I.e., is there a config mechanism to make random length-n lists and not just individual random values that we should make available?

Only via Eval items. And I'm not sure how likely it would be that anyone would want to do that.

I was mostly thinking for shift items and other physical perturbations that people would likely want to draw from some normal distribution with an expected mean and sigma to simulate the expected uncertainty in the telescope positioning.

@jmeyers314
Copy link
Member Author

And I'm not sure how likely it would be that anyone would want to do that.

Yeah, probably not Zernike perturbations directly, but I could imagine randomly perturbing bending modes once those get implemented.

@jmeyers314 jmeyers314 force-pushed the telescope_perturbations branch from d8752f5 to e1d65b0 Compare January 10, 2023 19:23
@jmeyers314 jmeyers314 force-pushed the telescope_perturbations branch from ea4b3bb to 23f5760 Compare January 10, 2023 21:01
@jmeyers314
Copy link
Member Author

@rmjarvis , I think this is ready for you to take a look again.

@rmjarvis
Copy link
Contributor

This looks good from my perspective. Not all the parameters are parsed yet -- some of them still need to be bare floats, rather than e.g. a random value, but it's not clear to me that this is a problem. Probably let's just merge this and start using it, and we'll see if there are use cases where we want more customization in the way we define anything. It might be that this covers all the plausible use cases already.

@rmjarvis rmjarvis merged commit 6b14a25 into main Feb 8, 2023
@rmjarvis rmjarvis deleted the telescope_perturbations branch February 8, 2023 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants