brandondube / dygdug

1 stars 5 forks source link

Awkward and inefficient actuator array juggling #9

Open brandondube opened 2 years ago

brandondube commented 2 years ago

As-is, within dygdug and prysm we have:

# prysm DM, update
dm.actuators[:] = actuators[:]
dm.render()

This interface predominantly stems from wanting the DM to be like something in real life and "own" the actuator array. The [:] syntax is just a way for copying elements without changing the actual array itself, which allows the DM object to keep ownership of "its" array, although this seems to be a very pedantic point and not one that really matters.

Then we need a mechanism for the outside user to update the DM(s) in a coronagraph model, and we have:

# dygdug SingleDMLyotCoronagraph
    def update_dm(self, actuators):
        self.dm.actuators[:] = actuators.reshape(self.dm.actuators.shape)[:]
        self.dm_wfe = fttools.pad2d(self.dm.render(wfe=True), out_shape=self.Nmodel)
        return

This serves two purposes: 1) if EFC gives us a flat vector of actuators, we make it square to make the DM model happy. 2) the DM model does not have to be prysm

As a side effect we also embed the necessary pad or crop to get to the same array size as the rest of the model. Maybe that should get pushed inside prysm's DM type; it seems reasonable to do so.

EFC uses this as:

        y = self.Gstar.dot(Ew)
        self.act_cmds -= self.loop_gain * y
        old_wfe = self.c.dm_wfe
        self.c.update_dm(self.act_cmds)

So we basically have three types, all having their own concept of an array or interface for the DM.

In merging them we have to deal with the following issues:

1) If we have pruned dead actuators (outside the pupil, e.g.) then the reshape trick breaks

2) if there are multiple DMs, we need a way for EFC to pack all of their actuators into one vector

3) 1D vs 2D representations

brandondube commented 2 years ago

In dealing with multiple DMs, it is perhaps convenient for the coronagraph models to expose deformable mirrors to the user as ~=

c = Coronagraph()
c.dms
>>> (something_about_dm0, something_about_dm1, ...)

c.update_dms([dm1_acts,dm2_acts, ...])

The something could be the number of actuators (NxN), any masks, ... a bit TBD.

Update actuators and update wavefront error map should stay bundled. This way the re-rendering of the DM surface stays deliberate and irregular, e.g. propagating N wavelengths through the DM sequentially, without re-drawing the surface N times.