GalSim-developers/GalSim — pull request #514 by rmjarvis (2014-03-08 22:05:38+00:00)
Title: #511 Start moving GSObjects (and others) toward a more immutable design

This pull request is all about style, not new functionality.

I am convinced that many classes in python work better when they use an immutable design, where changes to the object are not done in place, but rather use a syntax like `bounds = bounds.shift(dx,dx)`. I explained my reasons for this in issue #511, so you can read more there if you weren't involved in that discussion already.

The best arguments, IMO, for the proposed change are [(1)](https://github.com/GalSim-developers/GalSim/issues/511#issuecomment-36480568) that the `apply` methods currently include a down cast to `GSObject`s, which is undesirable for a number of reasons, and [(2)](https://github.com/GalSim-developers/GalSim/issues/511#issuecomment-36545147) that it can be ambiguous what changes to a given object will do when that object used somewhere else, such as in a `Convolve` object.

There was some push back there, but enough support that I decided to go ahead and implement my proposal, so people could look at it more concretely. I still consider this a proposal, so please feel free to express your opinions about these changes.

I did not yet implement [David's suggestion](https://github.com/GalSim-developers/GalSim/issues/511#issuecomment-36482843) about making the original object accessible after a transformation (via `gal.original`), which I plan to do on branch #218 as part of an effort to make our `GSObject`s (and others) picklable. But I wanted to at least get the basic design change done sooner than later, so people can start using the new style now to lessen the impact of the change compared to waiting for our existing code base to grow even larger. I don't expect to get to #218 before we will want to release version 1.1.

Also, because it is a rather large change in the API, I did not remove any of the old functionality. The old syntax still works, but is marked obsolete. I figure we can officially deprecate the old syntax (with `DeprecationWarning`s) in the next minor release (version 1.2), and then remove it entirely in version 2.0.

Summary of the changes:
- `gal = gal.shear(shear)` replaces `gal.applyShear(shear)` or `gal = gal.createSheared(shear)`.
- `gal = gal.rotate(theta)` replaces `gal.applyRotation(theta)` or `gal = gal.createRotated(theta)`.
- `gal = gal.dilate(scale)` replaces `gal.applyDilation(scale)` or `gal = gal.createDilated(scale)`.
- `gal = gal.expand(scale)` replaces `gal.applyExpansion(scale)` or `gal = gal.createExpanded(scale)`.
- `gal = gal.magnify(mu)` replaces `gal.applyMagnification(mu)` or `gal = gal.createMagnified(mu)`.
- `gal = gal.shift(dx,dy)` replaces `gal.applyShift(dx,dy)` or `gal = gal.createShifted(dx,dy)`.
- `gal = gal.lens(g1,g2,mu)` replaces `gal.applyLensing(g1,g2,mu)` or `gal = gal.createLensed(g1,g2,mu)`.
- `gal = gal.transform(...)` replaces `gal.applyTransformation(...)` or `gal = gal.createTransformed(...)`.
- `gal = gal.withFlux(flux)` replaces `gal.setFlux(flux)`
- `gal *= flux_ratio` replaces `gal.scaleFlux(flux_ratio)`
- Similar changes for `CorrelatedNoise` class, which has a parallel set of `apply` and `create` methods.
- `cn = cn.convolvedWith(obj)` replaces `cn.convolveWith(obj)`.
- `noise = noise.withVariance(var)` replaces `noise.setVariance(var)` (for all `BaseNoise` classes, not just `CorrelatedNoise`).
- `noise = noise.rescaleVariance(var_ratio)` replaces `noise.scaleVariance(var_ratio)`.
- `noise *= var_ratio` is (and was) a synonym for the above.
- `shapelet = FitShapelet(sigma, order, image)` replaces `shapelet = Shapelet(sigma,order); shapelet.fitImage(image)`.
- Some setters in various classes that are pretty much never used are now marked discouraged in favor of just building a new object instead.
- Cleaned up the galsim namespace to only include the things that we actually want users to use, rather than importing everything from the `_galsim` namespace. Mostly this means that the `SB` classes are not available in the top namespace, but you can of course access them via `galsim._galsim.SBGaussian` if you really want them.
- Removed tests of the `SB` classes from the unit tests. We don't actually care about regression testing that functionality, so long as the corresponding `GSObject` regression tests pass.

I think after this, just about every class in our python layer except for `Image` will have an essentially immutable design. Of course `Image` doesn't make any sense to make immutable, so that's fine to leave it as is.

The only other mutability I noticed relates to `BaseDeviates` which are sort of inherently muting every time they generate a random number. But the rest of their functionality does pretty much follow an immutable design now.