astropy / astropy

Astronomy and astrophysics core library
https://www.astropy.org
BSD 3-Clause "New" or "Revised" License
4.43k stars 1.78k forks source link

Context manager for CGS E&M units? #4359

Open mhvk opened 8 years ago

mhvk commented 8 years ago

(This is a follow-up of #4355, combining comments by @eteq and myself; EDIT (2021): this also is meant to replace #1687, #5879 - calculate magnetic energy density, etc.; #4086 - coulomb-statcoulomb equivalency)

There are multiple versions of the E&M system in cgs [1] which are mutually incompatible. As a result, many cgs E&M unit conversions in astropy are not normally possible. It might be possible to devise a context manager within which conversion is allowed, i.e., one would do something like

with u.set_em_system(u.cgs.emu):
    ...

and one could then happily convert charge per second into the relevant current, or calculate a force from just charge1 * charge2 / distance**2 in the cgs ESU system.

[1] https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units#Derivation_of_CGS_units_in_electromagnetism

embray commented 8 years ago

That would make sense, and is in line with what I was considering we would need when I first tried to tackle cgs E&M units. That was way back and at the time I don't think we even had equivalencies fully working yet, though my memory is hazy. I definitely think this would be the right approach.

embray commented 8 years ago

See also #745.

embray commented 8 years ago

Another similar / related issue is #4086.

mhvk commented 8 years ago

Reading #4086, I wondered whether the equivalency system itself might be good enough, i.e., whether one could device equivalencies which would make it possible to do something like

with u.set_enabled_equivalencies(u.electromagnetic('emu')):
    ...

(with exact names TBD). Given the description on the wikipedia page where, e.g., the cgs use system explicitly defines charge via the electrostatic force, this might indeed be handy.

However, it is not quite clear how to let this interact with constants, where really within the context manager constants.e should have a different unit. Possibly, the above set_em_system would simply incorporate setting any required equivalencies.

embray commented 8 years ago

Right, I was also thinking an equivalency would do the trick. As for constants we've already talked elsewhere about allowing multiple definitions for a constant, so might incorporate that somehow.

mhvk commented 4 years ago

@MaxNoe - above the question had turned to whether a set of equivalencies might do the trick. My sense is that that is the case, but I'm not completely sure. It might be good to start with a list (or better, a set of tests!) that would do what you wanted, and then write the equivalencies to make it work...

p.s. As a probably worse alternative, one could make a new "constants" system in which mu0 and eps0 are whatever they should be in cgs. This is worse though in that the constants system has to be chosen per astropy session, i.e., one cannot easily work with different units systems inside different libraries.

maxnoe commented 4 years ago

I don't think equivalencies are the right API choice. It's not about converting units, it is the unit system itself that needs to be changed.

The worst thing in my opinion, is not being able to tell in which unit system equations or functions are written. With a context manager or decorator for functions, this will be clear:

with u.gaussian_cgs:
    print(u.gauss.decompose())

print(u.gauss.decompose())

This should result in

1 cm(-1/2) g(1/2) s(-1)
1e-4 kg A(-1) s(-2) 

As a probably worse alternative, one could make a new "constants" system in which mu0 and eps0 are whatever they should be in cgs.

The problem is not that they have different values, they simply do not exist. So one should not even define them when using cgs.

maxnoe commented 4 years ago

This is the way it is implemented by the way at the moment. However, the error message could be better:

In [1]: import astropy.constants as c
In [2]: c.e.name, c.e.gauss.value, c.e.gauss.unit
Out[2]: ('Electron charge', 4.80320467299766e-10, Unit("Fr"))
In [3]: c.eps0.name, c.eps0.gauss.value, c.eps0.gauss.unit
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-ecbc894f3cc9> in <module>
----> 1 c.eps0.name, c.eps0.gauss.value, c.eps0.gauss.unit

~/.local/anaconda3/lib/python3.7/site-packages/astropy/units/quantity.py in __getattr__(self, attr)
    815                 "'{0}' object has no '{1}' member".format(
    816                     self.__class__.__name__,
--> 817                     attr))
    818
    819         def get_virtual_unit_attribute():

AttributeError: 'EMCODATA2014' object has no 'gauss' member
mhvk commented 4 years ago

@MaxNoe - Without equivalencies, I'm not sure how one can avoid some of the units actually being defined differently in terms of other units, i.e., u.G effectively changing. The easiest way to do that is using a method similar to that forhaving different constants (which also changes the units), but the problem then is that it becomes a full-session choice. This, of course, is risky: e.g., what happens if I call a library, and the library calculate a magnetic pressure using b**2/(2*c.mu0)?

Indeed, this may be the problem with a u.gaussian_cgs context as well. The advantage of it being a set of equivalencies is that they are used after all else fails, so the library would work fine.

maxnoe commented 4 years ago

Without equivalencies, I'm not sure how one can avoid some of the units actually being defined differently in terms of other units, i.e., u.G effectively changing.

That's kind of my point. I don't want to avoid that. Imho a clear separation of the different unit systems with transformation explictly at borders is preferable and possibly also the only correct way to do it.

This much reminds me of the encoding/decoding/string discussions in 2 vs. 3. 2 assumed interchangebility where there in reality was none, which lead to subtle errors as soon as non-ascii was encountered, 3 forces you to convert between bytes / unicode codepoints.

The different unit systems are not really interoperable, formulas in EM look very different, natural constants differ or don't exist in either system. So having the astropy units be SI by default and have a context manager or session settting to switch to CGS/Gauss or another unit system might be the clean solution.

mhvk commented 4 years ago

I can see the preference within one piece of code, but, as I noted, if one goes for a system like that for setting constants, then it does imply the change is session-wide, i.e., one can not set a system just for a piece of code, and it will also affect any library calls, etc.

Anyway., I think we're reaching the point where a piece of code would help discussion...

Firestar-Reimu commented 11 months ago

So Fr (statC/esu) cannot be calculated as 1 Fr = 1 g1/2 cm3/2 s−1 and Gauss cannot be calculated as 1 Gauss = cm-1/2 g1/2 s−1?

mhvk commented 11 months ago

By default, things effectively are treated as SI equivalents, and the cgs conversions depends on which system one is in. So, the units system needs to have a way to communicate what system one wants to be in, which is what this issue is about!