1# -*- coding: utf-8 -*-
2"""
3Test symbolic unit handling.
4
5
6
7
8"""
9
10# -----------------------------------------------------------------------------
11# Copyright (c) 2018, yt Development Team.
12#
13# Distributed under the terms of the Modified BSD License.
14#
15# The full license is in the LICENSE file, distributed with this software.
16# -----------------------------------------------------------------------------
17
18
19import numpy as np
20from numpy.testing import (
21    assert_almost_equal,
22    assert_allclose,
23    assert_array_almost_equal_nulp,
24    assert_equal,
25)
26import operator
27import pickle
28import pytest
29from sympy import Symbol
30
31from unyt.array import unyt_quantity
32from unyt.testing import assert_allclose_units
33from unyt.unit_registry import UnitRegistry
34from unyt.dimensions import (
35    mass,
36    length,
37    time,
38    temperature,
39    energy,
40    magnetic_field_cgs,
41    magnetic_field_mks,
42    power,
43    rate,
44)
45from unyt.exceptions import InvalidUnitOperation, UnitsNotReducible, UnitConversionError
46from unyt.unit_object import default_unit_registry, Unit, UnitParseError
47from unyt.unit_systems import cgs_unit_system, UnitSystem
48from unyt._unit_lookup_table import (
49    default_unit_symbol_lut,
50    name_alternatives,
51    unit_prefixes,
52)
53import unyt.unit_symbols as unit_symbols
54from unyt._physical_ratios import (
55    m_per_pc,
56    sec_per_year,
57    m_per_km,
58    m_per_mpc,
59    mass_sun_kg,
60)
61
62
63def test_no_conflicting_symbols():
64    """
65    Check unit symbol definitions for conflicts.
66
67    """
68    full_set = set(default_unit_symbol_lut.keys())
69
70    # go through all possible prefix combos
71    for symbol in default_unit_symbol_lut.keys():
72        if default_unit_symbol_lut[symbol][4]:
73            keys = unit_prefixes.keys()
74        else:
75            keys = [symbol]
76        for prefix in keys:
77            new_symbol = "%s%s" % (prefix, symbol)
78
79            # test if we have seen this symbol
80            assert new_symbol not in full_set, "Duplicate symbol: %s" % new_symbol
81
82            full_set.add(new_symbol)
83
84
85def test_dimensionless():
86    """
87    Create dimensionless unit and check attributes.
88
89    """
90    u1 = Unit()
91
92    assert u1.is_dimensionless
93    assert u1.expr == 1
94    assert u1.base_value == 1
95    assert u1.dimensions == 1
96    assert u1 != "hello!"
97    assert (u1 == "hello") is False
98
99    u2 = Unit("")
100
101    assert u2.is_dimensionless
102    assert u2.expr == 1
103    assert u2.base_value == 1
104    assert u2.dimensions == 1
105
106    assert_equal(u1.latex_repr, "")
107    assert_equal(u2.latex_repr, "")
108
109
110def test_create_from_string():
111    """
112    Create units with strings and check attributes.
113
114    """
115
116    u1 = Unit("kg * m**2 * s**-2")
117    assert u1.dimensions == energy
118    assert u1.base_value == 1.0
119
120    # make sure order doesn't matter
121    u2 = Unit("m**2 * s**-2 * kg")
122    assert u2.dimensions == energy
123    assert u2.base_value == 1.0
124
125    # Test rationals
126    u3 = Unit("kg**0.5 * m**-0.5 * s**-1")
127    assert u3.dimensions == magnetic_field_cgs
128    assert u3.base_value == 1.0
129
130    # sqrt functions
131    u4 = Unit("sqrt(kg)/sqrt(m)/s")
132    assert u4.dimensions == magnetic_field_cgs
133    assert u4.base_value == 1.0
134
135    # commutative sqrt function
136    u5 = Unit("sqrt(kg/m)/s")
137    assert u5.dimensions == magnetic_field_cgs
138    assert u5.base_value == 1.0
139
140    # nonzero CGS conversion factor
141    u6 = Unit("Msun/pc**3")
142    assert u6.dimensions == mass / length ** 3
143    assert_array_almost_equal_nulp(
144        np.array([u6.base_value]), np.array([mass_sun_kg / m_per_pc ** 3])
145    )
146
147    with pytest.raises(UnitParseError):
148        Unit("m**m")
149    with pytest.raises(UnitParseError):
150        Unit("m**g")
151    with pytest.raises(UnitParseError):
152        Unit("m+g")
153    with pytest.raises(UnitParseError):
154        Unit("m-g")
155    with pytest.raises(UnitParseError):
156        Unit("hello!")
157    with pytest.raises(UnitParseError):
158        Unit("True")
159    with pytest.raises(UnitParseError):
160        Unit("else")
161    with pytest.raises(UnitParseError):
162        Unit("hello(37)")
163    with pytest.raises(UnitParseError):
164        Unit("hello(foo=37)")
165
166    cm = Unit("cm")
167    data = 1 * cm
168
169    assert Unit(data) == cm
170    assert Unit(b"cm") == cm
171
172
173def test_create_from_expr():
174    """
175    Create units from sympy Exprs and check attributes.
176
177    """
178    pc_mks = m_per_pc
179    yr_mks = sec_per_year
180
181    # Symbol expr
182    s1 = Symbol("pc", positive=True)
183    s2 = Symbol("yr", positive=True)
184    # Mul expr
185    s3 = s1 * s2
186    # Pow expr
187    s4 = s1 ** 2 * s2 ** (-1)
188
189    u1 = Unit(s1)
190    u2 = Unit(s2)
191    u3 = Unit(s3)
192    u4 = Unit(s4)
193
194    assert u1.expr == s1
195    assert u2.expr == s2
196    assert u3.expr == s3
197    assert u4.expr == s4
198
199    assert_allclose_units(u1.base_value, pc_mks, 1e-12)
200    assert_allclose_units(u2.base_value, yr_mks, 1e-12)
201    assert_allclose_units(u3.base_value, pc_mks * yr_mks, 1e-12)
202    assert_allclose_units(u4.base_value, pc_mks ** 2 / yr_mks, 1e-12)
203
204    assert u1.dimensions == length
205    assert u2.dimensions == time
206    assert u3.dimensions == length * time
207    assert u4.dimensions == length ** 2 / time
208
209
210def test_create_with_duplicate_dimensions():
211    """
212    Create units with overlapping dimensions. Ex: km/Mpc.
213
214    """
215
216    u1 = Unit("J * s**-1")
217    u2 = Unit("km/s/Mpc")
218    km_mks = m_per_km
219    Mpc_mks = m_per_mpc
220
221    assert u1.base_value == 1
222    assert u1.dimensions == power
223
224    assert_allclose_units(u2.base_value, km_mks / Mpc_mks, 1e-12)
225    assert u2.dimensions == rate
226
227
228def test_create_new_symbol():
229    """
230    Create unit with unknown symbol.
231
232    """
233    u1 = Unit("abc", base_value=42, dimensions=(mass / time))
234
235    assert u1.expr == Symbol("abc", positive=True)
236    assert u1.base_value == 42
237    assert u1.dimensions == mass / time
238
239    u1 = Unit("abc", base_value=42, dimensions=length ** 3)
240
241    assert u1.expr == Symbol("abc", positive=True)
242    assert u1.base_value == 42
243    assert u1.dimensions == length ** 3
244
245    u1 = Unit("abc", base_value=42, dimensions=length * (mass * length))
246
247    assert u1.expr == Symbol("abc", positive=True)
248    assert u1.base_value == 42
249    assert u1.dimensions == length ** 2 * mass
250
251    with pytest.raises(UnitParseError):
252        Unit("abc", base_value=42, dimensions=length ** length)
253    with pytest.raises(UnitParseError):
254        Unit("abc", base_value=42, dimensions=length ** (length * length))
255    with pytest.raises(UnitParseError):
256        Unit("abc", base_value=42, dimensions=length - mass)
257    with pytest.raises(UnitParseError):
258        Unit("abc", base_value=42, dimensions=length + mass)
259
260
261def test_create_fail_on_unknown_symbol():
262    """
263    Fail to create unit with unknown symbol, without base_value and dimensions.
264
265    """
266    with pytest.raises(UnitParseError):
267        Unit(Symbol("jigawatts"))
268
269
270def test_create_fail_on_bad_symbol_type():
271    """
272    Fail to create unit with bad symbol type.
273
274    """
275    with pytest.raises(UnitParseError):
276        Unit([1])  # something other than Expr and str
277
278
279def test_create_fail_on_bad_dimensions_type():
280    """
281    Fail to create unit with bad dimensions type.
282
283    """
284    with pytest.raises(UnitParseError):
285        Unit("a", base_value=1, dimensions="(mass)")
286
287
288def test_create_fail_on_dimensions_content():
289    """
290    Fail to create unit with bad dimensions expr.
291
292    """
293    a = Symbol("a")
294    with pytest.raises(UnitParseError):
295        Unit("a", base_value=1, dimensions=a)
296
297
298def test_create_fail_on_base_value_type():
299    """
300    Fail to create unit with bad base_value type.
301
302    """
303    with pytest.raises(UnitParseError):
304        Unit("a", base_value="a", dimensions=(mass / time))
305
306
307def test_string_representation():
308    """
309    Check unit string representation.
310
311    """
312    pc = Unit("pc")
313    Myr = Unit("Myr")
314    speed = pc / Myr
315    dimensionless = Unit()
316
317    assert str(pc) == "pc"
318    assert str(Myr) == "Myr"
319    assert str(speed) == "pc/Myr"
320    assert repr(speed) == "pc/Myr"
321    assert str(dimensionless) == "dimensionless"
322    assert repr(dimensionless) == "(dimensionless)"
323
324
325def test_multiplication():
326    """
327    Multiply two units.
328
329    """
330    msun_mks = mass_sun_kg
331    pc_mks = m_per_pc
332
333    # Create symbols
334    msun_sym = Symbol("Msun", positive=True)
335    pc_sym = Symbol("pc", positive=True)
336    s_sym = Symbol("s", positive=True)
337
338    # Create units
339    u1 = Unit("Msun")
340    u2 = Unit("pc")
341
342    # Mul operation
343    u3 = u1 * u2
344
345    assert u3.expr == msun_sym * pc_sym
346    assert_allclose_units(u3.base_value, msun_mks * pc_mks, 1e-12)
347    assert u3.dimensions == mass * length
348
349    # Pow and Mul operations
350    u4 = Unit("pc**2")
351    u5 = Unit("Msun * s")
352
353    u6 = u4 * u5
354
355    assert u6.expr == pc_sym ** 2 * msun_sym * s_sym
356    assert_allclose_units(u6.base_value, pc_mks ** 2 * msun_mks, 1e-12)
357    assert u6.dimensions == length ** 2 * mass * time
358
359
360def test_division():
361    """
362    Divide two units.
363
364    """
365    pc_mks = m_per_pc
366    km_mks = m_per_km
367
368    # Create symbols
369    pc_sym = Symbol("pc", positive=True)
370    km_sym = Symbol("km", positive=True)
371    s_sym = Symbol("s", positive=True)
372
373    # Create units
374    u1 = Unit("pc")
375    u2 = Unit("km * s")
376
377    u3 = u1 / u2
378
379    assert u3.expr == pc_sym / (km_sym * s_sym)
380    assert_allclose_units(u3.base_value, pc_mks / km_mks, 1e-12)
381    assert u3.dimensions == 1 / time
382
383
384def test_power():
385    """
386    Take units to some power.
387
388    """
389    from sympy import nsimplify
390
391    pc_mks = m_per_pc
392    mK_mks = 1e-3
393    u1_dims = mass * length ** 2 * time ** -3 * temperature ** 4
394    u1 = Unit("kg * pc**2 * s**-3 * mK**4")
395
396    u2 = u1 ** 2
397
398    assert u2.dimensions == u1_dims ** 2
399    assert_allclose_units(u2.base_value, (pc_mks ** 2 * mK_mks ** 4) ** 2, 1e-12)
400
401    u3 = u1 ** (-1.0 / 3)
402
403    assert u3.dimensions == nsimplify(u1_dims ** (-1.0 / 3))
404    assert_allclose_units(
405        u3.base_value, (pc_mks ** 2 * mK_mks ** 4) ** (-1.0 / 3), 1e-12
406    )
407
408
409def test_equality():
410    """
411    Check unit equality with different symbols, but same dimensions and
412    base_value.
413
414    """
415    u1 = Unit("km * s**-1")
416    u2 = Unit("m * ms**-1")
417
418    assert u1 == u2
419    assert u1.copy() == u2
420
421
422def test_invalid_operations():
423    u1 = Unit("cm")
424    u2 = Unit("m")
425
426    with pytest.raises(InvalidUnitOperation):
427        u1 + u2
428    with pytest.raises(InvalidUnitOperation):
429        u1 += u2
430    with pytest.raises(InvalidUnitOperation):
431        1 + u1
432    with pytest.raises(InvalidUnitOperation):
433        u1 + 1
434    with pytest.raises(InvalidUnitOperation):
435        u1 - u2
436    with pytest.raises(InvalidUnitOperation):
437        u1 -= u2
438    with pytest.raises(InvalidUnitOperation):
439        1 - u1
440    with pytest.raises(InvalidUnitOperation):
441        u1 - 1
442    with pytest.raises(InvalidUnitOperation):
443        u1 *= u2
444    with pytest.raises(InvalidUnitOperation):
445        u1 * "hello!"
446    with pytest.raises(InvalidUnitOperation):
447        u1 /= u2
448    with pytest.raises(InvalidUnitOperation):
449        u1 / "hello!"
450    with pytest.raises(InvalidUnitOperation):
451        Unit("B") * Unit("V")
452    with pytest.raises(InvalidUnitOperation):
453        Unit("V") * Unit("B")
454    with pytest.raises(InvalidUnitOperation):
455        Unit("V") / Unit("Np")
456    with pytest.raises(InvalidUnitOperation):
457        Unit("dB") / Unit("dB")
458    with pytest.raises(InvalidUnitOperation):
459        Unit("B") ** 2
460
461
462def test_base_equivalent():
463    """
464    Check base equivalent of a unit.
465
466    """
467    Msun_mks = mass_sun_kg
468    Mpc_mks = m_per_mpc
469
470    u1 = Unit("Msun * Mpc**-3")
471    u2 = Unit("kg * m**-3")
472    u3 = u1.get_base_equivalent()
473
474    assert u2.expr == u3.expr
475    assert u2 == u3
476
477    assert_allclose_units(u1.base_value, Msun_mks / Mpc_mks ** 3, 1e-12)
478    assert u2.base_value == 1
479    assert u3.base_value == 1
480
481    mass_density = mass / length ** 3
482
483    assert u1.dimensions == mass_density
484    assert u2.dimensions == mass_density
485    assert u3.dimensions == mass_density
486
487    assert_allclose_units(
488        u1.get_conversion_factor(u3)[0], Msun_mks / Mpc_mks ** 3, 1e-12
489    )
490
491    with pytest.raises(UnitConversionError):
492        u1.get_conversion_factor(Unit("m"))
493
494    with pytest.raises(UnitConversionError):
495        u1.get_conversion_factor(Unit("degF"))
496
497    reg = UnitRegistry(unit_system=cgs_unit_system)
498
499    u = Unit("kg", registry=reg)
500
501    assert u.get_base_equivalent() == Unit("g")
502
503    u = Unit("kg")
504
505    assert u.get_base_equivalent() == Unit("kg")
506
507    u = Unit("A")
508    assert u.get_base_equivalent(unit_system="mks") == Unit("A")
509
510
511def test_temperature_offsets():
512    u1 = Unit("degC")
513    u2 = Unit("degF")
514
515    with pytest.raises(InvalidUnitOperation):
516        operator.mul(u1, u2)
517    with pytest.raises(InvalidUnitOperation):
518        operator.truediv(u1, u2)
519
520
521def test_latex_repr():
522    registry = UnitRegistry()
523
524    # create a fake comoving unit
525    registry.add(
526        "pccm",
527        registry.lut["pc"][0] / (1 + 2),
528        length,
529        "\\rm{pc}/(1+z)",
530        prefixable=True,
531    )
532
533    test_unit = Unit("Mpccm", registry=registry)
534    assert_almost_equal(test_unit.base_value, m_per_mpc / 3)
535    assert_equal(test_unit.latex_repr, r"\rm{Mpc}/(1+z)")
536
537    test_unit = Unit("cm**-3", base_value=1.0, registry=registry)
538    assert_equal(test_unit.latex_repr, "\\frac{1}{\\rm{cm}^{3}}")
539
540    test_unit = Unit("m_geom/l_geom**3")
541    assert_equal(test_unit.latex_repr, "\\frac{1}{\\rm{M}_\\odot^{2}}")
542
543    test_unit = Unit("1e9*cm")
544    assert_equal(test_unit.latex_repr, "1.0 \\times 10^{9}\\ \\rm{cm}")
545
546    test_unit = Unit("1.0*cm")
547    assert_equal(test_unit.latex_repr, "\\rm{cm}")
548
549
550def test_latitude_longitude():
551    lat = unit_symbols.lat
552    lon = unit_symbols.lon
553    deg = unit_symbols.deg
554    assert_equal(lat.units.base_offset, 90.0)
555    assert_equal((deg * 90.0).in_units("lat").value, 0.0)
556    assert_equal((deg * 180).in_units("lat").value, -90.0)
557    assert_equal((lat * 0.0).in_units("deg"), deg * 90.0)
558    assert_equal((lat * -90).in_units("deg"), deg * 180)
559
560    assert_equal(lon.units.base_offset, -180.0)
561    assert_equal((deg * 0.0).in_units("lon").value, -180.0)
562    assert_equal((deg * 90.0).in_units("lon").value, -90.0)
563    assert_equal((deg * 180).in_units("lon").value, 0.0)
564    assert_equal((deg * 360).in_units("lon").value, 180.0)
565
566    assert_equal((lon * -180.0).in_units("deg"), deg * 0.0)
567    assert_equal((lon * -90.0).in_units("deg"), deg * 90.0)
568    assert_equal((lon * 0.0).in_units("deg"), deg * 180.0)
569    assert_equal((lon * 180.0).in_units("deg"), deg * 360)
570
571
572def test_creation_from_ytarray():
573    from unyt import electrostatic_unit, elementary_charge_cgs
574
575    u1 = Unit(electrostatic_unit)
576    assert_equal(str(u1), "statC")
577    assert_equal(u1, Unit("esu"))
578    assert_equal(u1, electrostatic_unit.units)
579
580    u2 = Unit(elementary_charge_cgs)
581    assert_equal(str(u2), "4.80320467299766e-10*statC")
582    assert_equal(u2, Unit("4.80320467299766e-10*statC"))
583    assert_equal(u1, elementary_charge_cgs.units)
584
585    assert_allclose((u1 / u2).base_value, electrostatic_unit / elementary_charge_cgs)
586
587    with pytest.raises(UnitParseError):
588        Unit([1, 2, 3] * elementary_charge_cgs)
589
590
591def test_list_same_dimensions():
592    from unyt import m
593
594    reg = default_unit_registry
595    for equiv in reg.list_same_dimensions(m):
596        assert Unit(equiv).dimensions is length
597
598
599def test_decagram():
600    dag = Unit("dag")
601    g = Unit("g")
602    assert dag.get_conversion_factor(g) == (10.0, None)
603
604
605def test_pickle():
606    cm = Unit("cm")
607    assert cm == pickle.loads(pickle.dumps(cm))
608
609
610def test_preserve_offset():
611    from unyt import degF, dimensionless
612
613    new_unit = degF * dimensionless
614
615    assert new_unit is not degF
616    assert new_unit == degF
617    assert new_unit.base_offset == degF.base_offset
618
619    new_unit = degF / dimensionless
620
621    assert new_unit is not degF
622    assert new_unit == degF
623    assert new_unit.base_offset == degF.base_offset
624
625    with pytest.raises(InvalidUnitOperation):
626        dimensionless / degF
627
628
629def test_code_unit():
630    from unyt import UnitRegistry
631
632    ureg = UnitRegistry()
633    ureg.add("code_length", 10.0, length)
634    ureg.add("code_magnetic_field", 2.0, magnetic_field_mks)
635    u = Unit("code_length", registry=ureg)
636    assert u.is_code_unit is True
637    assert u.get_base_equivalent() == Unit("m")
638    u = Unit("cm")
639    assert u.is_code_unit is False
640
641    u = Unit("code_magnetic_field", registry=ureg)
642    assert u.get_base_equivalent("mks") == Unit("T")
643    with pytest.raises(UnitsNotReducible):
644        assert u.get_base_equivalent("cgs")
645
646    # see issue #60
647    u = Unit("s/m")
648    assert u.get_mks_equivalent() == Unit("s/m")
649    assert u.get_mks_equivalent() != Unit("ohm")
650    assert u.get_cgs_equivalent() == Unit("s/cm")
651
652    u = Unit("kC")
653    assert u.get_cgs_equivalent() == Unit("kesu")
654    assert u.get_cgs_equivalent().get_mks_equivalent() == u
655
656    UnitSystem(ureg.unit_system_id, "code_length", "kg", "s", registry=ureg)
657
658    u = Unit("cm", registry=ureg)
659    ue = u.get_base_equivalent("code")
660
661    assert str(ue) == "code_length"
662    assert ue.base_value == 10
663    assert ue.dimensions is length
664
665    class FakeDataset(object):
666        unit_registry = ureg
667
668    ds = FakeDataset()
669
670    UnitSystem(ds, "code_length", "kg", "s", registry=ureg)
671
672    u = Unit("cm", registry=ureg)
673    ue = u.get_base_equivalent(ds)
674
675    assert str(ue) == "code_length"
676    assert ue.base_value == 10
677    assert ue.dimensions is length
678
679    with pytest.raises(UnitParseError):
680        Unit("code_length")
681
682
683def test_bad_equivalence():
684    from unyt import cm
685
686    with pytest.raises(KeyError):
687        cm.has_equivalent("dne")
688
689
690def test_em_unit_base_equivalent():
691    from unyt import A, cm
692
693    with pytest.raises(UnitsNotReducible):
694        (A / cm).get_base_equivalent("cgs")
695
696
697def test_symbol_lut_length():
698    for v in default_unit_symbol_lut.values():
699        assert len(v) == 5
700
701
702def test_simplify():
703    import unyt as u
704
705    answers = {
706        u.Hz * u.s: "dimensionless",
707        u.kg / u.g: "1000",
708        u.Hz * u.s * u.km: "km",
709        u.kHz * u.s: "1000",
710        u.kHz * u.s * u.km: "1000*km",
711        u.kHz * u.s ** 2: "1000*s",
712        u.kHz * u.s ** 2 * u.km: "1000*km*s",
713        u.Hz ** -1 * u.s: "s/Hz",
714        u.Hz ** -1 * u.s * u.km: "km*s/Hz",
715        u.Hz ** 1.5 * u.s ** 1.7: "sqrt(Hz)*s**(7/10)",
716        u.Hz ** 1.5 * u.s ** 1.7 * u.km: "sqrt(Hz)*km*s**(7/10)",
717        u.m ** 2 / u.cm ** 2: "10000",
718    }
719
720    for unit, answer in answers.items():
721        assert str(unit.simplify()) == answer
722
723
724def test_micro_prefix():
725    import unyt as u
726
727    # both versions of unicode mu work correctly
728    assert u.um == um
729    assert u.um == um
730
731    # parsing both versions works as well
732    assert u.ug == u.Unit("µg")
733    assert u.ug == u.Unit("μg")
734
735
736def test_name_alternatives():
737    import unyt
738    from unyt._unit_lookup_table import (
739        default_unit_name_alternatives,
740        name_alternatives,
741        inv_name_alternatives,
742    )
743
744    # concatenated list of all alternative unit names
745    allowed_names = sum(name_alternatives.values(), [])
746
747    # ensure the values are all tuples and not e.g. strings
748    for val in default_unit_name_alternatives.values():
749        assert isinstance(val, tuple)
750
751    # all names are unique
752    assert len(set(allowed_names)) == len(allowed_names)
753    # each allowed name has a key in the inverse dict
754    assert len(inv_name_alternatives.keys()) == len(allowed_names)
755    assert set(inv_name_alternatives.keys()) == set(allowed_names)
756
757    for name in allowed_names:
758        assert hasattr(unyt, name)
759        assert hasattr(unyt.unit_symbols, name)
760
761
762def test_solar_unit_name_alternatives():
763    import unyt
764    from unyt import Unit
765
766    # check that m_sun, m_Sun, M_sun, M_Sun, msun, and Msun all work
767    for lower_name_prefix in "mrltz":
768        base_name = lower_name_prefix + "sun"
769        for name_prefix in [lower_name_prefix, lower_name_prefix.upper()]:
770            alternative_names = [name_prefix + suf for suf in ["sun", "_sun", "_Sun"]]
771            for name in alternative_names:
772                assert Unit(name) == Unit(base_name)
773                assert hasattr(unyt, name)
774                # only solar mass units are in physical constants
775                if lower_name_prefix == "m":
776                    assert hasattr(unyt.physical_constants, name)
777
778
779def test_attosecond():
780    from unyt import Unit, attosecond, second
781
782    assert Unit("as") == attosecond
783    assert str(Unit("as")) == "as"
784    assert Unit("as/s") == attosecond / second
785
786
787def test_micro():
788    from unyt import Unit
789
790    assert str(Unit("um")) == "μm"
791    assert str(Unit("us")) == "μs"
792
793
794def test_show_all_units_doc_table_ops():
795    for name in set(name_alternatives.keys()):
796        u = Unit(name)
797        (1 * u).in_mks()
798        try:
799            (1 * u).in_cgs()
800        except UnitsNotReducible:
801            pass
802
803
804def test_hPa_mbar():
805    assert Unit("hPa").dimensions == Unit("bar").dimensions
806    assert (5 * Unit("hPa") == 5 * Unit("mbar")).all()
807    assert (5 * Unit("hPa") != 1 * Unit("bar")).all()
808
809
810def test_percent():
811    a = 300 * Unit("percent")
812    b = 3.0 * Unit("dimensionless")
813    c = 300.0 * Unit("%")
814    d = 300.0 * Unit("V*%/V")
815
816    assert a == b
817    assert str(a) == "300 %"
818    assert repr(a) == "unyt_quantity(300, '%')"
819
820    assert a == c
821    assert c == d
822
823
824def test_equal_has_same_hash():
825    a = Unit("m")
826    b = Unit("m")
827    c = Unit("m*s/s")
828
829    assert a == b
830    assert b == c
831    assert hash(a) == hash(b)
832    assert hash(b) == hash(c)
833
834
835def test_bel_neper():
836    assert Unit("B").dimensions == Unit("Np").dimensions
837    a = 1 * Unit("B") / (np.log(10) / 2)
838    assert_allclose_units(a.to("Np"), 1 * Unit("Np"))
839    a = 2 * Unit("B")
840    b = 20 * Unit("decibel")
841    assert (a == b).all()
842    c = 2 * Unit("Np")
843    d = 20 * Unit("decineper")
844    assert (c == d).all()
845    assert Unit("dB") ** 1 == Unit("dB")
846
847
848def test_henry():
849    assert (Unit("H") / Unit("Ω")).dimensions == time
850
851
852def test_degC():
853    assert Unit("degree_celsius") == Unit("degC")
854    assert Unit("degree_Celsius") == Unit("degC")
855    assert Unit("Celsius") == Unit("degC")
856    assert Unit("°C") == Unit("degC")
857    a = 1 * Unit("degC")
858    assert str(a) == "1 °C"
859
860
861def test_delta_degC():
862    a = 1 * Unit("delta_degC")
863    assert str(a) == "1 Δ°C"
864
865
866def test_degF():
867    assert Unit("degree_fahrenheit") == Unit("degF")
868    assert Unit("degree_Fahrenheit") == Unit("degF")
869    assert Unit("Fahrenheit") == Unit("degF")
870    assert Unit("°F") == Unit("degF")
871    a = 1 * Unit("degF")
872    assert str(a) == "1 °F"
873
874
875def test_delta_degF():
876    a = 1 * Unit("delta_degF")
877    assert str(a) == "1 Δ°F"
878
879
880def test_mixed_registry_operations():
881
882    reg = UnitRegistry(unit_system="cgs")
883    reg.add("fake_length", 0.001, length)
884    a = unyt_quantity(1, units="fake_length", registry=reg)
885    b = unyt_quantity(1, "cm")
886
887    assert_almost_equal(a + b, b + a)
888    assert_almost_equal(a - b, -(b - a))
889    assert_almost_equal(a * b, b * a)
890    assert_almost_equal(b / a, b / a.in_units("km"))
891    assert_almost_equal(a / b, a / b.in_units("km"))
892