bjornbm / dimensional

Dimensional library variant built on Data Kinds, Closed Type Families, TypeNats (GHC 7.8+).
BSD 3-Clause "New" or "Revised" License
102 stars 17 forks source link

Plane and solid angles as base dimensions? #72

Open bjornbm opened 9 years ago

bjornbm commented 9 years ago

See the issue and comments posted by fredhpitts at: https://code.google.com/p/dimensional/issues/detail?id=43

They have been copied below for posterity(?) as google code is going away next year.

@dmcclean feel free to add your thoughts here as well!


Issue 43: Plane and solid angle inconsistencies in dimensional-tf

Reported by fredhpitts, Apr 12, 2015

What steps will reproduce the problem?

  1. Edit TF.lhs and Quantities.lhs to add plane and solid angles to the 7 base dimensions.
  2. Attempt to compile and execute Test.hs

What is the expected output? What do you see instead? Expect Test.hs to compile and run successfully. Test.hs does not compile because 1) in SIUnits.lhs, lumen' should be defined ascandela * steradian' not `candela / steradian' (see luminus flux entry of Table 3 at http://physics.nist.gov/cuu/Units/units.html) 2) in Quantities.lhs, DAngularVelocity should be defined as plane angle per time, not DFrequency, which has dimension of reciprocal time. 3) in Quantities.lhs, DRadiantIntensity should be defined as having the dimensions of power divided by solid angle 4) in Quantities.lhs, DRadiance should be defined as having the dimensions of irradiance divided by solid angle

What version of the product are you using? On what operating system? dimensional-tf-0.3.0.1 downloaded with cabal. Fedora 20

Please provide any additional information below. I fully appreciate that only item 1) above is an error and that the remaining three items are a consequence of plane and solid angles being added to the list of primary dimensions. But taken as a whole, the items highlight the danger of not adding plane and solid angles to the list. Even though dimensionless, they are physical quantities with various units of measure (radian, degree, grad, steradian, square degree, etc) that can easily get lost or misused as in item 1).


Apr 24, 2015 #1 bjorn.buckwalter

Thanks for the report. I have fixed the definition of lumen.

However, I have to decline the request for adding plane and solid angles as base dimensions. Doing so would not be consistent with the SI and I believe it would likely introduce as many problems and ambiguities as it would remove. For example, should dividing two lengths result in a Dimensionless or a PlaneAngle? I recommend wrapping your angles in newtypes when you are particularly concerned about mixing them up or misusing them.

Status: Fixed


May 9 (3 days ago) #2 fredhpitts via email

The answers to "should dividing two lengths result in Dimensionless' or a PlaneAngle" are: 1)Dimensionless' if you have no need to know how the dimensionless number came in to being in subsequent computation. 2) A ratio of lengths if you're dealing with ratios of mass, for instance, in subsequent computation. Adding length ratios and mass ratios is as much a dimension error as adding length and mass quantities. And even addition of length ratios is meaningless if the length ratios are not on the same basis. 3) The only circumstance in which dividing two lengths should result in a PlaneAngle is when the numerator is the distance along a circle arc and the denominator is the radius length of the same circle.

Case 3 above is the exception and should be handled as such (i.e. wrapped in a function). Being consistent with SI is laudable, but ignoring the issues that are not dealt with by SI will significantly restrict the usefulness of dimensional consistency software. I think blind canceling of dimensions during multiplications and divisions is a fundamental mistake. Finesse is needed here so as to not loose valuable information.

BTW, at one time PlaneAngle and SolidAngle were recognized as secondary dimensions in SI. For whatever reason they were deprecated. That does mean they not important to implementing dimensional consistency software. I dare say you would not have made the error in the lumen implementation if SolidAngle had been rigorously accounted for by the code. Isn't the point of dimensional consistency software to prevent such errors.

Furthermore dimension consistency software needs to address vector and affine quantities which SI says nothing about.[?]


Today (moments ago) #3 bjorn.buckwalter

Thanks again. I appreciate you taking your time and effort writing this. I agree with you in principle, and you are absolutely correct that the error in lumen would have been avoided if SolidAngle was tracked in it's own base dimension.

On the other hand I don't think it is an easy problem to solve, with an obvious implementation and API. As you say: finesse is needed to make it work well. But if you are willing to help draft one we could consider adding it.

FYI current and future development of dimensional is being performed on the not-yet-on-hackage dimensional-dk (https://github.com/bjornbm/dimensional-dk) that is intended to supersede dimensional and dimensional-tf. Nowadays my co-maintainer Douglas McClean (https://github.com/dmcclean) actually does most of the work on improving and expanding functionality, and he is quite sympathetic with your concerns. If you are interested in contributing I suggest that you continue the discussion of what a possible implementation could look like with him on Github while I follow from the side-lines. I've copied our conversation here to https://github.com/bjornbm/dimensional-dk/issues/72 for further discussion.

dmcclean commented 9 years ago

My notes on this:

From a 5/10 email to Bjorn

Raises some interesting points.

Taking the easy part first: The point about vector and affine quantities is something we are tackling in -experimental, and it really isn't problematic it's just about legwork. (The one problematic part is that linear is a nice package, but only works when the vector types are also functors, almost worth adding a Numeric.Units.Dimensional.DK.MorallyDubiousFunctorInstance module with the orphan...)

On the angle part, I'm personally inclined to agree. There is a widely accepted argument that angles are dimensionless because they are defined to be one length divided by another. On view A this is true and the essence of angles. On another view, view B, (apparently this person's) it is true but unhelpful from an engineering and software engineering perspective. On a third view, C, we should hunt around for-even a slightly twisted-definition of angle where this isn't the case so that angles can have dimensions.

On one hand, I like the fact that we are playing it straight down the line in terms of the NIST/BIPM/UCUM documents. On the other hand though, I'm halfway between B and C. There's a lot to be gained from an engineering perspective by tracking it. AFAICS it's impossible to do both without splitting the entire ecosystem, which stinks because you lose the chance for dimensional to be a strong basis for interoperation between libraries that deal with physical things. I think the best you can hope for is a convenient and functional DropAngles tyoe family that zeroes out the 8th and 9th base dimensions, and the obvious term level function that goes with it.

If you make the jump, it does work out and is pretty nice, except for its non-standards-compliance. You change the types of all the trig functions in the obvious way. The one big problem I had with it, which needs some thinking, is that there are two kinds of vector cross products, one common one that introduces an angle^-1, e.g. r x F, w x (r1 - r2). But try as I might, I can't twist things enough to get that to be the case in the Lorentz force equation, unless somehow the B-field is carrying a angle^-1 that I can't see.

At any rate, with the smell of the two cross product operators, it all worked out when I compiled my autopilot program against a prototype version that tracked these dimensions, and caught one goof up in some flight plan code on a branch I had never actually tried in the simulator.

Persuasive arguments on both sides of this one.

From a follow up email of later the same day

Possible line of argument for view C over view B is that angles measured on the sphere are still very useful, and locally look just like angles on the plane, but the angle = arc length / radius definition doesn't work in that setting, and so you need a whole new definition of "spherical angle" and to jiggle things around and embed S2 in R3 to use the arc length / radius definition operationally. If you accept that angles have a dimension, the situation is more cohesive.

dmcclean commented 9 years ago

In my opinion there are two key steps to making this happen:

  1. Explaining why/how the angle dimension is implicit in some cross products in freshman physics but not others.
  2. Making a project decision to depart from the standards bodies and most textbooks in favor of what is arguably a better software engineering practice for the reasons fredhpitts mentions.
bjornbm commented 9 years ago
  1. Explaining why/how the angle dimension is implicit in some cross products in freshman physics but not others.

Just throwing this out here despite not having taken the time to understand this problem clearly, and understanding Geometric Algebra even less: have you looked into GA at all, and could GA's outer product/bivector shed some light on what is going on?

dmcclean commented 9 years ago

Hmm, possibly. I'll have to look in to that. Call that (a).

Other possibilities include: (b) that it has something to do with pseudovectors (c) that it really is implicit in the individual equations, and isn't related to the cross product at all (d) that the whole project of treating angles as a dimension really does lead to serious problems

dmcclean commented 9 years ago

I don't have a clue what that geometric algebra page is trying to say.

bjornbm commented 9 years ago

Right, I don't want to pretend that I do either. What I thought might have some relevance was the section I linked to on Rotating Systems which ended with the following comment on “bi-products” of the cross product:

Unlike the cross product description of torque, \tau = \mathbf{r} \times F, the geometric algebra description does not introduce a vector in the normal direction; a vector that does not exist in two and that is not unique in greater than three dimensions. The unit bivector describes the plane and the orientation of the rotation, and the sense of the rotation is relative to the angle between the vectors {\hat{u}} and {\hat{v}}.

Anyway, it was a long shot and I haven't tried to wrap my head around the implications.

dmcclean commented 9 years ago

I should be sleeping, but I had an interesting idea about this which I am going to jot down briefly and in a half-baked fashion.

It may be possible to have the best of both worlds here after all, without fragmenting into two incompatible systems.

Let Dimension be the nine-dimensional kind of dimensions, SI basis extended with plane angles and solid angles. Let this be the case for everyone, both those who intend to track angles as dimensions and those who do not.

Let this type family exist for forgetting about angles:

type family RemoveAngles (d :: Dimension) where
  RemoveAngles ('Dim l m t i th n j pa sa) = 'Dim l m t i th n j 'Zero 'Zero

If you want to forget angles, you can removeAngles :: Dimensional v d a -> Dimensional v (RemoveAngles d) a. Alternatively if you want to introduce them or change them, it's coerceAngles :: Dimensional v ('Dim l m t i th n j pa sa) a -> Dimensional v ('Dim l m t i th n j pa' sa') a. Both are the identity, equating quantities that track angles with quantities that don't by taking the position that angles/solid angles actually are dimensionless and so radian and steradian are both one, and so multiplying/dividing by one is the identity.

Now introduce two modules. People who like tracking angles can import one, and others the other. Both re-export almost all the same definitions, the only things that change are things like these (giving only the angular definitions, the others being as they are today modulo addition of the two new base dimensions to dimension synonyms):

type DPlaneAngle = 'Dim 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Zero 'Pos1 'Zero

type DTorque = 'Dim 'Pos2 'Pos1 'Neg2 'Zero 'Zero 'Zero 'Zero 'Neg1 'Zero

sin :: PlaneAngle a -> Dimensionless a
asin :: Dimensionless a -> PlaneAngle a

Now each person can act according to his own wishes by importing the right module, but both can talk to each other because all the interfaces are written in terms of the same Dimension kind, all that isn't shared are synonyms. When worlds collide because someone wrote functionality you want to use without sharing your perspective on angles you can rescue it by appropriate use of coerceAngles at the boundaries. removeAngles offers a version of coerceAngles that isn't burdened by an ambiguous result type for people who want to work in the standard angle-less basis.

Withholding comment on whether this is actually a good design until I have much more time to think about it, but I thought it was worth putting on paper.

dmcclean commented 9 years ago

Better still, perhaps only the angleless module needs to include removeAngles and coerceAngles, for the person who imported the angular module can achieve their effects more explicitly by multiplying/dividing by her radian and steradian.

They aid the angleless person because the radian and steradian that he has in scope cannot be used to perform the conversion, and he would be deeply confused by needing to import both.

bjornbm commented 9 years ago

I like this. I agree that removeAngles and coerceAngles should only be in the angleless module.

Note that the RemoveAngles type class isn't necessary as the type can be written directly in removeAngles. But is it a nice to have? I'd lean towards leaving it out until a need is demonstrated.

In the angular module, would it be useful to provide functions/operators for common cases when you want your dimensionless (ratio) to be an angle? E.g.

planeAngle :: Num a => Dimensionless a -> PlaneAngle a
planeAngle x = x * (1 *~ radian)
--
-- | Calculate, e.g., an angle, angular velocity, etc.
(/°) :: Fractional a => Quantity d a -> Length a -> Quantity (d / Length) a 
arc /° radius = arc / radius * (1 *~ radian) 

Or should we just leave it to the user to convert by * (1 *~ radian)?

dmcclean commented 9 years ago

Could definitely be the case that we don't need the type family, as the anonymous version of it is pretty straightforward.

Convenience methods like the ones you suggest are probably a good idea. I would advocate supplying them in both modules even though they would be yet more type-restricted synonyms for id in the angleless module.

I have no idea how the formatting in your comment got garbled. I tried to fix it and failed. Strangely the preview showed it working. But either the garbling or my brain is preventing me from understanding the second part of that suggestion.

I think it's worth a shot to see how it works. Got any ideas on module names?

bjornbm commented 9 years ago

Don't know either what is up with the formatting. My initial submission of the comment was via email, but that turned out as a bit of a disaster. Still strange that the preview is OK but not the comment itself. Github bug.

Perhaps something like "SI" for the angleless module and "Supplementary" for the angular module? This is based on radian and steradian being considered "SI supplementary units” from 1960 to 1995.

See by the way pages 69 and 75 of SP330 regarding the supplemental units and their removal. E.g.

the interpretation given by the CIPM in 1969 for the class of supplementary units introduced in Resolution 12 of the 11th Conférence Générale des Poids et Mesures (CGPM) in 1960 allows the freedom of treating the radian and the steradian as SI base units,

and

such a possibility compromises the internal coherence of the SI based on only seven base units,

and

the study of the formalisms in use in the scientific field shows that none exists which is at the same time coherent and convenient and in which the quantities plane angle and solid angle might be considered as base quantities,

We are aware of the above of course, but I am repeating it here for reference. From my point of view the aim of the angleless module is to preserve coherence, while the angular module relaxes coherence in favour of treating the radian and the steradian as SI base units and pays the price in manual conversion between angles and dimensionless values. Would you agree?

dmcclean commented 9 years ago

I agree. I'm curious if they found formalisms which were coherent but not convenient, or if that is just rhetorical. It still feels like the ratio-of-arc-length-to-radius definition of angle shouldn't be the only one and that some other might lead to coherent definitions; however that intuition could well be totally incorrect.

dmcclean commented 9 years ago

Hiccup: does this make the name of siUnit a lie?

Could change it to baseUnit as exported by the angular module?

dmcclean commented 9 years ago

Also, what types do we want for sinh and friends?

dmcclean commented 9 years ago

Also, still need a bit more planning on module names. Would we create flavored versions of .Quantities, .Prelude, and .SIUnits as well? If so, would there be any module with the undecorated names that we have now at all?

dmcclean commented 9 years ago

This branch is getting things rolling. Haven't done any of the module splitting yet, but did convert everything to angular flavors. https://github.com/dmcclean/dimensional-dk/tree/angles

bjornbm commented 9 years ago

How about:

siUnit = removeAngles . baseUnit  -- or baseunit . removeAngles

It seems this doesn't break the current siUnit.

For sinh et al, what viable alternatives do you see to Dimensionless a -> Dimensionless a?

dmcclean commented 9 years ago

I'm not entirely clear on what sinh actually is, so I wasn't sure if it was Dimensionless a -> Dimensionless a or PlaneAngle a -> Dimensionless a like sin is.

That definition for siUnit is good, I think. It gets the somewhat strange type siUnit :: Unit ('Dim l m t i th n j 'Zero 'Zero) a, which is good because it keeps you from using it without removing angles from the quantities you are using it with.

bjornbm commented 9 years ago

I'm not a big sinh user either, but from what I gather of the Wikipedia page it is a mapping from an d^2 (area in some sense) to a d and perhaps it could be given a correspondingly more generic type. But sine the area must be normalised to the unit hyperbola anyway it seems reasonable to assume the dimension has been normalised away as well. And if one strictly follows the formula for the unit hyperbola x^2 - y^2 = 1 it follows that the dimensions of x and y must be DOne (assuming the formula is to be interpreted this stricly and that 1 could not actually be of any unit). With this handwaving I propose sticking to Dimensionless a -> Dimensionless a until we have reason otherwise. At least as far as I can tell PlaneAngle a -> Dimensionless a is not appropriate.

dmcclean commented 9 years ago

Agreed.

bjornbm commented 9 years ago

Regarding modules: to minimize duplication you could perhaps extract the units involving angles into their own modules. The fairly simple separation of .SIUnits and .NonSI will not be sufficient but perhaps be complemented by, e.g., .SIUnits.SIAngles and SIUnits.SupplementalAngles (selectively imported by two different preludes). Similarly for Quantities. Would that suffice?

(As a side note, is the separation of quantities and units worthwhile or should we just throw the quantities in e.g. SIUnits(.*) to reduce the multitude of modules?)

Or is it better to separate the module hierarchies at a higher level? Something along the lines of

Numeric.Units.Dimensional.DK.Common.SIUnits  -- all units without angles.
Numeric.Units.Dimensional.DK.SI.SIUnits  -- imports Common.SIUnits and adds angles.
Numeric.Units.Dimensional.DK.SI.Prelude  -- imports SI.SIUnits, etc.
Numeric.Units.Dimensional.DK.Supplemental.SIUnits  -- imports Common.SIUnits and adds angles.
Numeric.Units.Dimensional.DK.Supplemental.Prelude  -- imports Supplemental.SIUnits, etc.
dmcclean commented 9 years ago

I was thinking that there isn't really duplication because we can just re-export everything except for the things that are different, and so the user would only need to import as many things as they do today.

I think (but it's a good question) that the separate Quantities module is useful because (a) it grabs a trillion names, so it probably doesn't all belong in the core module, and (b) it is useful even if you don't want to use SI units.

Suggestion (omitting all the leading Numeric.Units.Dimensional. line noise...):

All the definitions would live in the undecorated modules (chosen since the only Dimension definitions are angular). The SI flavored versions would be a straight import/re-export of them, hiding a few things and implementing their replacements.

If it turns out we don't like it, I'd vote for scrapping it entirely. OTOH if it turns out we do like it, this will give new users an obvious place to start while still providing an out for people who want the strict SI version. (Thought about naming it strict, but people would think it had to do with evaluation order...)

bjornbm commented 9 years ago

I agree with your suggestion for the modules.

As proposed we are committing to the angular version as the canonical one. I fear that it may be premature even if we are initially pleased with it – after all it took the BIPM/CIPM/CGPM some 35 years to finally decide against the supplemental units. But on the other hand pulling the supplemental dimensions in a future version should not cause any breakage if we keep a removeAngles et al (which would then be identity also at type level) except for explicit uses of Dim. And besides breakage is what major version bumps are for.

By the way, perhaps a type synonym SIDim l m t i th n j = 'Dim l m t i th n j 'Zero 'Zero would be useful for clarity, and could be use to reduce uses of Dim.

On the topic on breakage and major version bumps, I think we will be able to remove the DK and just call this dimensional 1.0. Any dimensional-fd evolutions can continue in < 1.0 or in a dimensional-fd/-classic package. Would you agree?

dmcclean commented 9 years ago

That synonym is a good idea. I was trying to think of how to write it but I was screwing it up by not putting all the type variables on the LHS. Bringing back the type family would be another option because other than coerceAngles and removeAngles, the core module doesn't examine the internal structure of Dimensions at all, all of that is encapsulated in the two dimensions modules.

I think I would agree with the last suggestion, because I think it is pretty clear that this is the one you want if your compiler can deal with it, for a variety of reasons. Will that lead to both packages dimensional and dimensional-dk exposing different modules with the same name? How heavily is that frowned upon? Are you proposing to "rename" dimensional to dimensional-classic and releasing this as a major version bump of dimensional? Seems fine to me as long as we document it?

bjornbm commented 9 years ago

Are you proposing to "rename" dimensional to dimensional-classic and releasing this as a major version bump of dimensional?

Yes. Not 100 % sure about whether it is worth renaming, and in that case whether to rename to -classic or -fd. I might be more inclined to not rename and also deprecate dimensional-tf to save myself from the effort trying to keep them in sync (Quantities module and units).

dmcclean commented 9 years ago

Good questions both. I can't answer because all my projects are running on the bleeding edge of GHC versions and so it doesn't impact me.

Restructuring modules now as discussed.

dmcclean commented 9 years ago

See https://github.com/dmcclean/dimensional-dk/blob/24a78392b9355b43d0e86728044b2a1f7e074473/src/Numeric/Units/Dimensional/DK/SI/Quantities.hs for a place where having the type family does cut down on verbosity. Do you think this is sufficient reason to keep it? If so I will promote it to the type-level dimensions module.

dmcclean commented 9 years ago

Taking this path would require answering the question in #80.

bjornbm commented 9 years ago

Seems like the RemoveAngles type family may be worth keeping the.  Should it be renamed to ToSIDim or something like that instead?

On Sat, Jul 18, 2015 at 8:38 PM, Douglas McClean notifications@github.com wrote:

See https://github.com/dmcclean/dimensional-dk/blob/24a78392b9355b43d0e86728044b2a1f7e074473/src/Numeric/Units/Dimensional/DK/SI/Quantities.hs for a place where having the type family does cut down on verbosity. Do you think this is sufficient reason to keep it? If so I will promote it to the type-level dimensions module.

Reply to this email directly or view it on GitHub: https://github.com/bjornbm/dimensional-dk/issues/72#issuecomment-122577743

dmcclean commented 9 years ago

Not a bad idea. So we have:

type SIDim l m t i th n j = 'Dim l m t i th n j 'Zero 'Zero

type family ToSIDim (d :: Dimension) :: Dimension where
  ToSIDim ('Dim l m t i th n j pa sa) = SIDim l m t i th n j

My only concern is that it makes the connection between type-level removeAngles and term-level RemoveAngles less obvious, but that should be solved by using it in the type of removeAngles so there is a clear link in the documentation.

dmcclean commented 9 years ago

Without injective type families the documentation idea in my last comment doesn't really work, so I am skipping it.

dmcclean commented 9 years ago

If we want to take this branch, we should probably think about #23 some more, because a fair amount of my code that deals with angles doesn't need the type PlaneAngle Double it needs the newtype wrapper around that that equates x with x + 2πn.

dmcclean commented 9 years ago

So your suggestion about fixing the angular momentum and moment of inertia definitions from the ones I had goofed up works. AFAICS the only remaining issues with this idea are:

  1. Deciding if it's a good idea or not
  2. Making sure we like the module names
  3. Writing some documentation for the top level that explains to users that they have this choice and examples of taking either decision
  4. Determining the answer to #80