Esempio n. 1
0
class Nucleon(Baryon):
    __upinit = Baryon.__init__

    def __init__(self, u, d, name, mass, doc, **what):
        what['constituents'] = {Quark.item.up: u, Quark.item.down: d}
        self.__upinit(name, mass, doc, **what)

    mass = Quantity(1 / mol / mol.Avogadro,
                    gram,
                    doc="""Atomic Mass Unit, AMU.

This is the nominal mass of a nucleon.
In reality, both proton and neutron are a fraction of a percent heavier.

The Mole is so defined that a Mole of carbon-12 weighs exactly 12 grams.  The
carbon-12 nucleus comprises six protons and six neutrons.  Thus dividing one
gram by the number of items in a Mole thereof (Avogadro's constant) yields one
twelfth of the mass of a carbon-12 atom, nominally (half the mass of an electron
plus) the average of the masses of neutron and proton, albeit the binding energy
of the nucleus reduces this value (by more than the electron mass).
""")

    amuk = Quantity(mass / Thermal.k,
                    doc="""AMU scaled down by Boltzmann's constant.

If we look at the ideal gas law, P*V = N*k*T, for N items of a gas with mass m
per item, we get density = N*m/V = m*P/k/T.  Since m is the relative molecular
(or atomic) mass, M, times the atomic mass unit, we can write it as M * AMU and
obtain density = M*amuk*P/T with M a pure number (and, typically, very close to
an integer).  Thus, at standard temperature (zero Celsius) and pressure (one
Atmosphere), density is just M times 44.618 grams per cubic metre.\n""")
Esempio n. 2
0
def KLfamily(nm, lnom, lsym, lm, lme, lrate,
             mnom, mm, mme, pnom, pm, pme,
             mev=mega*eV.mass, Hz=Hertz):
    """Deciphering Kaye&Laby p449.

    Positional arguments are as follows:

      neutrino mass -- upper bound, measured in MeV

      lepton name -- string
      lepton symbol -- string
      lepton mass -- in MeV
      lepton mass error -- half-width of error bar on previous
      lepton decay rate -- fraction of the given lepton species which decay per second

      -ve quark name -- name of the quark with -ve charge e/3
      -ve quark mass -- mass estimate, in GeV, for the -ve quark
      -ve quark mass error -- half-width of error-bar on previous

      +ve quark name -- name of the quark with +ve charge 2*e/3
      +ve quark mass -- mass estimate, in GeV, for the +ve quark
      +ve quark mass error -- half-width of error bar on previous\n"""

    return Family(Neutrino(lnom, mass=Quantity.below(nm, mev)),
                  Lepton(lnom, mass=Quantity.within(lm, lme, mev), symbol=lsym,
                         decay=lrate * Hz),
                  dQuark(mnom, mass=Quantity.within(mm, mme, kilo*mev)),
                  uQuark(pnom, mass=Quantity.within(pm, pme, kilo*mev)))
Esempio n. 3
0
def waterviscosity(T,
                   A=2.414e-5 * Pascal * second,
                   K=Kelvin,
                   ten=Quantity(10)):
    """Variation of water's dynamic viscosity with temperature.

    Takes one argument, an absolute temperature (e.g. a return from
    study.value.archaea's Centigrade or Fahrenheit).  Result is probably only
    valid if this is a temperature at which water is a liquid !\n"""
    return A * ten**(247.8 / (T / K - 140))  # adapted from Wikipedia
Esempio n. 4
0
def KLfamily(nm,
             lnom,
             lsym,
             lm,
             lme,
             lrate,
             mnom,
             mm,
             mme,
             pnom,
             pm,
             pme,
             mev=mega * eV.mass,
             Hz=Hertz):
    """Deciphering Kaye&Laby p449.

    Positional arguments are as follows:

      neutrino mass -- upper bound, measured in MeV

      lepton name -- string
      lepton symbol -- string
      lepton mass -- in MeV
      lepton mass error -- half-width of error bar on previous
      lepton decay rate -- fraction of the given lepton species which decay per second

      -ve quark name -- name of the quark with -ve charge e/3
      -ve quark mass -- mass estimate, in GeV, for the -ve quark
      -ve quark mass error -- half-width of error-bar on previous

      +ve quark name -- name of the quark with +ve charge 2*e/3
      +ve quark mass -- mass estimate, in GeV, for the +ve quark
      +ve quark mass error -- half-width of error bar on previous\n"""

    return Family(
        Neutrino(lnom, mass=Quantity.below(nm, mev)),
        Lepton(lnom,
               mass=Quantity.within(lm, lme, mev),
               symbol=lsym,
               decay=lrate * Hz),
        dQuark(mnom, mass=Quantity.within(mm, mme, kilo * mev)),
        uQuark(pnom, mass=Quantity.within(pm, pme, kilo * mev)))
Esempio n. 5
0
        self.__p *= self.__x  # a power of x
        k = 2  # sentinel: if self.__k gets to 2, we've converged !
        # Newton-Raphson to find a zero of f:
        f = self.__p - (self.__p - 1) / (self.__x - 1)
        g = f.derivative
        while k != self.__k:
            k = self.__k
            self.__k -= f(k) * 1. / g(k)

        return k


from study.value.quantity import Quantity

golden = Quantity((1 + 5.**.5) / 2,
                  doc="""The golden ratio.

This is the positive solution to the quadratic equation x*x = x+1; divide -1 by
it to get the negative solution.  One can re-write the equation as (2*x-1)**2 =
4*x*x -4*x +1 = 4*(x*x-x) + 1 = 5, whence the solutions are (1 +/- 5**.5)/2.

A rectangle whose long side is this times its short side has the property that,
if your cut from it a square on a short side, the remainder rectangle, though
smaller, has the same shape, i.e. ratio of long side (which was the original's
short side) to short side (the difference between the original's sides).
""")

assert golden**2 == golden + 1

del Quantity
Esempio n. 6
0
  debt -- description of debts and mortgages
  job -- description of a job

See study.LICENSE for copyright and license information.
"""
from study.value.quantity import Quantity

quid = Quantity.base_unit(
    "£",
    "Pound Sterling",
    """The base unit of British currency.

Used to be 20 shillings (21 shillings made a Guinea); each shilling was 12
pence, each penny was four farthings.  A florin was two shillings; a crown was
five.  Apparently a pound was also called a sovereign.  HTML supports character
entity &sterling; for the Pound Sterling.

To properly handle money within a system of units, I need support for variation
in time and space (conversion factors between different currencies vary with
time; and you'll get different exchange rates from different trading partners).
Then again, conversion factors between systems of units also show similar
variation - contrast the different nations' archaic units of length, and notice
how units of volume got re-defined by assorted legislative acts over the years.
""",
)

# It's clearly inadequate to treat money units as approximate multiples of one
# another: each is an exact unit in its place, it's only the conversion between
# them that's approximate.
krone = quid / Quantity.within(10, 2)

del Quantity
Esempio n. 7
0
def between(lo, hi, units, *args, **what):
    return Quantity.flat(lo, hi, None, units, *args, **what)
Esempio n. 8
0
                 violet=photon(380, 420, 'violet')) # purple
del photon

visible.also(spectrum=(visible.red, visible.orange, visible.yellow,
                       visible.green, visible.cyan, visible.blue,
                       visible.indigo, visible.violet),
             rainbow=Quantity.within(
        138.7, .7, arc.degree,
        """The angle through which a rainbow turns visible light.

This varies with the colour of the light, red being turned least and blue most.
Since the angle exceeds a quarter turn, the arc of a (pure water) rainbow
appears centred on the direction opposite to the light source (generally the
sun), at an angle ranging from 40.6 (violet) to 42 (red) degrees from that
direction.  The spray-bow resulting from sea-spray is tighter - sea water
droplets turn light through a larger angle than pure water droplets.
""",
                              secondary=between(
            127, 130, arc.degree,
            """The angle through which a secondary rainbow turns visible light.

Compare visible.rainbow, the primary angle: for the secondary rainbow, red (130
degrees) is turned more than violet (127 degrees); since this range is less than
that of the primary rainbow (but still more than a quarter turn), the secondary
bow appears outside the primary.
""")))

radio = Photon(name="radio", frequency = Quantity.below(3, giga * Hertz))
microwave = Photon(name="microwave",
                   wavelength = between(1, 100, milli * metre),
                   frequency = between(1, 100, 3 * giga * Hertz))
infrared = Photon(name="infra-red",
Esempio n. 9
0
composed of the first column: indeed, most matter is hydrogen, comprising a
proton and an electron; the proton is made of two up quarks and one down.

See also: elements.py
See study.LICENSE for copyright and license information.
"""
from study.snake.lazy import Lazy
from study.value.quantity import Quantity, Object
from study.value.units import pi, arc, bykind, \
     harpo, femto, pico, nano, micro, milli, kilo, mega, giga, tera, peta, exa, \
     gram, metre, mol, second, year, Volt, Angstrom, Hertz, Joule, Tesla
from physics import Quantum, Vacuum, Thermal
from decay import Decay

eV = Quantity(
    Quantum.Millikan,
    Volt,
    doc='The electron-Volt: a standard unit of energy in particle physics.')


def between(lo, hi, units, *args, **what):
    return Quantity.flat(lo, hi, None, units, *args, **what)


class Particle(Object):
    # needs merged in with units-related toys etc.
    __obinit = Object.__init__

    def __init__(self, name, *args, **what):
        """Describe a particle's rest-state.

        Required argument, name, is the name of the particle.  Other arguments
Esempio n. 10
0
class Bodalizer (Lazy):
    """The auto-Bodalizer.

An instance of this class tries to automatically select three constants zero,
unit and base for which a family of orbits' radii have values close to
        zero + unit * base**i
for various i.  It allows i to change in steps other than 1, but should (though
it presently does not) try to ensure that such exceptions are rare.

Constructor takes a sequence of space.body.Object instances, selects a sub-set
of them (the instances of the earliest of Planet, Planetoid, Body or Object to
have several instances in the sequence) and records the orbital radii of this
sub-set.  When it comes to need values for zero, unit and base it does the
necessary computation to match this sequence of radii to the above pattern.

Public attributes:
 * zero, unit, base -- the defining attributes mentioned above.

Public methods:
 * use(zero, unit, base) -- over-ride the given attributes
 * index(radius) -- i = log((radius - zero) / unit) / log(base)
"""

    def __init__(self, seq):
        # First identfy your demos ...
        plenty, k = len(seq), 3
        while k * k < plenty: k = 1 + k
        plenty = k # max(3, sqrt(len(seq)) rounded up)
        self.__seq = self.__enough(seq, plenty)

    def __len__(self): return len(self.__seq)
    def __getitem__(self, key):
        return self.zero + self.unit * self.base**key

    def __repr__(self):
        return 'lambda i: %s + %s * %s ** i' % (self.zero, self.unit, self.base)

    def index(self, radius):
        r = (radius - self.zero) / self.unit
        if r.low < .0001: return 0 # avoid domain errors and hugely -ve answers ...
        # (while incidentally being polite to Mercury)
        return r.log / self.__base

    # Auto-detection of zero, unit and base:

    from study.value.quantity import Quantity, tera
    # NB: Any scalar Quantity has attributes exp and log (among others) we can exploit ;^)

    def median(seq, span=1, Q=Quantity.flat): # tool func
        seq.sort()
        i, b = divmod(len(seq), 2)
        if b: best = seq[i].best
        else: best = .5 * (seq[i-1].best + seq[i].best)
        i, b = divmod(len(seq) - span, 2)
        if b: lo, hi = seq[i].best, seq[-1-i].best
        else: lo, hi = seq[i].low, seq[-1-i].high
        return Q(lo, hi, best)

    def __enough(self, seq, plenty, mid=median, cache=[]):
        try: bodytypes = cache[0]
        except IndexError:
            from body import Planet, Planetoid, Body, Object
            bodytypes = ( Planet, Planetoid, Body, Object )
            cache.append(bodytypes)

        for k in bodytypes:
            row = [x for x in seq if isinstance(x, k)]
            if len(row) < plenty: continue

            # try to eliminate any initial or final arithmetic sequences
            row = [ x.orbit.radius for x in row ]
            gap = [ y - x for x, y in zip(row[:-1], row[1:]) ]
            rat = [ (y / x).log for x, y in zip(gap[:-1], gap[1:]) ]
            cut = mid(rat) / 5

            # Could perhaps do better by considering every ratio of differences
            # among entries; these are all base**j * (base**i - 1)/(base**k - 1)
            # if we do things in the right order; and every difference between
            # entries with adjacent indices yields, where it's used as
            # denominator, a simple k = 1 so these ratios are base**j *
            # (base**(i-1) + ... + base**2 + base + 1)

            score = rat[0]
            while score < cut:
                row, rat = row[1:], rat[1:]
                score = score + rat[0]

            score = rat[-1]
            while score < cut:
                row, rat = row[:-1], rat[:-1]
                score = score + rat[-1]

            if len(row) >= plenty:
                return row

        return [ x.orbit.radius for x in seq ]

    from study.value.SI import metre
    Unit = Quantity(tera * metre / 7) # Arbitrary Unit of length (approximates the AU)
    del tera, metre

    def use(self, zero=None, unit=None, base=None, Q=Quantity):
        if zero is None:
            try: del self.zero
            except AttributeError: pass
        else: self.zero = zero

        if unit is None:
            try: del self.unit
            except AttributeError: pass
        else: self.unit = unit

        if base is None:
            try: del self.base
            except AttributeError: pass
            try: del self.__base
            except AttributeError: pass
        else:
            self.unit # Force lazy evaluation so we can blot out its __base ...
            self.base = Q(base)
            self.__base = self.base.log

    def _lazy_get_unit_(self, ig, mid=median, AU=Unit):
        row = [x - self.zero for x in self.__seq if x > self.zero]
        # That forced computation of zero, making base available ...
        offs = [ ((x / AU).log / self.__base) % 1 for x in row ]
        frac = offs[:]
        frac.sort()
        gaps = [ frac[0] + 1 - frac[-1] ] + [y - x for x, y in zip(frac[:-1], frac[1:])]
        ind = gaps.index(max(gaps)) # frac[ind-1] and frac[ind] differ by max(gaps)
        dim = (frac[ind-1] + frac[ind]) * .5 # the anti-middle

        # Now index row as nicely as we can hope for:
        i, ind = len(offs), []
        while i > 0:
            i = i - 1
            r = offs[i]
            if r > dim: r = r - 1
            n = (row[i] / AU).log / self.__base - r
            ind.insert(0, int(n.best))
        # but that leaves an arbitrary offset in ind.
        offs = [n - i - 1 for i, n in enumerate(ind)]
        # print 'offsets:', offs
        offs.sort()
        n = offs[len(offs) / 3]
        return mid([ r / self.base**(i - n) for r, i in zip(row, ind) ])

    del median

    def _lazy_get_base_(self, ignored):
        self.zero # force evaluation so we compute __base
        return self.__base.exp

    import math
    def __spread(self, z, e=math.exp, AU=Unit):
        # icky complex-to-complex used in hunting good values for zero and base
        zero, b = e(z.imag) * AU, e(z.real)
        # it may be prudent to frob oz ...
        oz = (zero / self.__seq[0] / 10).evaluate(lambda x: x + 1/x)

        row = [x for x in self.__seq if x > zero]
        if b > 0 and len(row) > 1:
            row = [ ((x / AU).log / b) % 1 for x in row ]
            row.sort()
            gap = max([ row[0] + 1 - row[-1] ] +
                      [ y - x for x, y in zip(row[:-1], row[1:]) ])
            span, oz = ((1 - gap) / len(row)).best, (oz / len(row)).best
        else:
            count = len(self.__seq) + 1 - len(row)
            try: span, oz = (e(b) + e(-b)) * count, (oz * count).best
            except OverflowError:
                raise OverflowError(z, b, oz)

        return float(span) + 1j * float(oz)

    from study.maths.search import Search

    def _lazy_get_zero_(self, ignored, S=Search, e=math.exp, AU=Unit, Q=Quantity):
        # Mercury's orbit as zero:
        z = self.__seq[0]
        # Saturn vs. Venus gives median log(ratio)/(difference in index)
        try: b = ((self.__seq[5] - z) / (self.__seq[1] - z)).log * .2
        except IndexError:
            b = Q(2).log # fall back on 2

        guess = float(b.log.best) + 1j * float((z / AU).log.best)
        hunt = S(self.__spread, guess, stride=.1)
        hunt.rummage()

        z = hunt.best[0]
        self.__base = Q(e(z.real))
        return e(z.imag) * AU

    del Quantity, Search, Unit, math
Esempio n. 11
0
    This isn't the right way to wrap it, but if any wrapping's to be done, it
    should be done here, not by bodging Radiator's __init__ method !\n"""

    try:
        if temperature > 0: T = temperature * Kelvin
        else: T = Centigrade(temperature)
    except TypeError: # temperature has units, so > 0 check bombed.
        if temperature / Kelvin > 0: T = temperature # TypeError if wrong units
        else: T = temperature + Centigrade(0)

    if T / Kelvin < 0:
        raise ValueError, 'Negative temperature, even after Centigrade coercion'

    return Radiator(T, *args, **what)

from study.value.quantity import Quantity
Human = Radiator(Quantity.fromDecimal(309.5, 1, None, Kelvin),
                 oral=Centigrade(Quantity.gaussian(36.8, .5)),
                 axillary=Centigrade(Quantity.flat(36, 36.9, 36.6)),
                 __doc__="""Human body as a radiator.

The human body maintains a roughly constant temperature, so naturally radiates
as a body of that temperature, in so far as it's exposed.  Skin temperature is
doubtless less than one would measure in an arm-pit (an 'axillary' measurement,
commonly used in Russia and Poland), which is about .2 K lower than oral
measurement (common in the anglophone world); which, in turn, is about .5 K
below anal measurements; while core temperatures are presumed to be higher yet
than this.
""")
del Quantity, Centigrade
Esempio n. 12
0
class Particle(Object):
    # needs merged in with units-related toys etc.
    __obinit = Object.__init__

    def __init__(self, name, *args, **what):
        """Describe a particle's rest-state.

        Required argument, name, is the name of the particle.  Other arguments
        are handled as for Object (q.v.) except for these keywords:

          constituents -- should be a mapping { particle: number } with self
                          comprising the given number of instances of each
                          particle.  Shalln't appear as an attribute: see the
                          eponymous method.

        The following keyword arguments, if supplied, should match the
        interpretation of them used in this class:

          decay -- probability per unit time of decaying
          lifetime -- expected time to decay, inverse of decay
          halflife -- half-life of particle
          decays -- a decay.Decays (q.v.) object.

        You can use an Object(particle, kinetic=energy) to specify a particle of
        the given type with some specified kinetic energy.\n"""
        try:
            self.__bits = what['constituents']
        except KeyError:
            pass  # self will be deemed primitive
        else:
            del what['constituents']

        self.__obinit(*args, **what)

        self._store_as_(name, self.__class__)
        self.__name = name

    _unborrowable_attributes_ = Object._unborrowable_attributes_ + (
        'name', 'symbol', 'charge', 'anti')

    def constituents(self, *primitives):
        """Returns self's composition.

        Takes any number of classes (irrelevant unless derived from Particle)
        and particles (irrelevant unless (possibly indirect) instances of
        Particle) to be deemed primitive and returns a dictionary mapping
        primitive particles to their multiplicities within self.  Regardless of
        any classes supplied as arguments, any particle whose composition wasn't
        specified when constructing it is deemed primitive.

        To get the composition specified when self was constructed, pass
        Particle as the sole argument; pass no arguments to get the particle
        reduced to its most primitive constituents; pass Nucleon to get a
        nucleus reduced to its nucleons; etc.\n"""

        try:
            bits = self.__bits
        except AttributeError:
            return {self: 1}
        bits, ans = bits.copy(), {}

        def carve(obj,
                  m=[x for x in primitives if issubclass(x, Particle)],
                  p=[x for x in primitives if isinstance(x, Particle)]):
            """Returns None if obj is primitive, else its constituents. """

            try:
                bok = obj.__bits
            except AttributeError:
                return None

            if obj in p: return None

            for k in m:
                if isinstance(obj, k): return None

            return bok

        while bits:
            for k, v in bits.items():
                del bits[k]

                b = carve(k)
                if b:
                    for q, r in b.items():
                        assert q is not k
                        bits[q] = bits.get(q, 0) + v * r
                else:
                    ans[k] = ans.get(k, 0) + v

        return ans

    def __bindener(self, bok):
        sum = -self.energy
        for k, v in bok.items():
            sum = sum + k.energy * abs(v)
        return sum

    def bindingenergy(self, *primitives):
        return self.__bindener(self.constituents(*primitives))

    def bindingfraction(self, *primitives):
        return self.bindingenergy(*primitives) / self.energy

    def bindingenergyper(self, *primitives):
        bok = self.constituents(*primitives)
        return self.__bindener(bok) / sum(abs(v) for v in bok.values())

    class __ItemCarrier(Lazy):
        __upinit = Lazy.__init__

        def __init__(self, *args, **what):
            ali = what.get('lazy_aliases', {})
            # Only relevant to Quark and its bases:
            ali.update({'top': 'truth', 'bottom': 'beauty'})
            what['lazy_aliases'] = ali
            self.__upinit(*args, **what)

        # Only relevant to Lepton and its bases:
        def _lazy_get_positron_(self, ignored):
            return self.electron.anti

        class _lazy_get_anti_(Lazy):
            def __init__(self, source, ignored):
                self.__source = source

            def _lazy_lookup_(self, key):
                return getattr(self.__source, key).anti

    def _store_as_(self, name, cls, root=None, ItemCarrier=__ItemCarrier):
        """Each sub-class of Particle carries a namespace full of its instances.

        That includes indirect instances but only applies to strict sub-classes,
        not to Particle itself.  Since Neutrino and Photon use anomalous naming,
        I let sub-classes over-ride _store_as_, but this base-class
        implementation should be good enough for most classes - it chases back
        up the __bases__ graph towards Particle doing the work.

        The namespace carrying the instances of the class is the .item attribute
        of the class, which is created the first time a particle of the class is
        stored.  The .item of a class should not be set otherwise: this method
        provides a special class for .item objects, which handles particle
        aliasing and other issues.  The .item object of a class also has a .anti
        sub-object for ease of reading; .item.anti.electron is effectively a
        synonym for .item.electron.anti, and likewise for other particles. """

        if root is None:
            root = Particle  # can't set as default; not defined yet
        todo, done = [cls], [Particle]
        while todo:
            k, todo = todo[0], todo[1:]
            try:
                i = k.item
            except AttributeError:
                i = k.item = ItemCarrier()

            done.append(k)
            try:
                getattr(i, name)
            except AttributeError:
                setattr(i, name, self)
                for b in k.__bases__:
                    if b not in done and issubclass(b, root):
                        todo.append(b)

    del __ItemCarrier

    def _lazy_get_name_(self, ignored):
        return self.__name

    __oblook = Object._lazy_lookup_

    def _lazy_lookup_(self, key):
        ans = self.__oblook(key)
        try:
            ans.document('The %s of the %s %s.' %
                         (key, self.name, self.__class__.__name__))
        except (AttributeError, TypeError):
            pass
        return ans

    def _lazy_get_magneton_(self, ignored):
        return self.charge * self.spin / self.mass

    log2 = Quantity(2).log

    def _lazy_get_decay_(self, ignored, zero=0 / second, ln2=log2):
        """Fractional decay rate.

        This is defined by: the probability density for decay of the particle at
        time t is r*exp(-t*r) with r as the .decay attribute.  Unless otherwise
        specified, this is presumed to be zero; however, it may be specified
        when you initialise - either directly, e.g. Fermion(decay=32/second), or
        indirectly via attribute halflife or (better) decays; see constructor
        documentation.

        The defining formula implies that the probability of decay before some
        specified time T is 1-exp(-T*r), making the half-life log(2)/r, and the
        mean time until decay is 1/r.\n"""

        try:
            halflife = self.__dict__['halflife']
        except AttributeError:
            pass
        else:
            return ln2 / halflife

        try:
            lifetime = self.__dict__['lifetime']
        except AttributeError:
            pass
        else:
            return 1. / halflife

        try:
            decays = self.decays
        except AttributeError:
            pass
        else:
            return decays.rate

        return zero

    def _lazy_get_halflife_(self, ignored, ln2=log2):
        """Time taken for the probability of having decayed to reach half"""
        return ln2 / self.decay

    del log2

    def _lazy_get_lifetime_(self, ignored):
        """Expected time to decay, for a particle of the given type"""
        return 1 / self.decay

    def _lazy_get_anti_(self, ignored):
        """Returns self's anti-particle."""

        # the anti-electron is anomalously named :^o
        try:
            nom = {
                'electron': 'positron'
                # any other anomalies ?
            }[self.name]
        except KeyError:
            nom = 'anti-%s' % self.name

        try:
            bits = {}
            for k, v in self.__bits.items():
                bits[k] = -v

        except AttributeError:
            bits = {self: -1}

        ans = self.__class__(nom,
                             self,
                             _charge=-self._charge,
                             constituents=bits)
        ans.anti = self  # NB cyclic reference; ans is about to become self.anti

        return ans

    def _lazy_get__charge_(self, ignored):
        """Charge in units of on third the positron's charge.

        This is an exact integer value, far more suitable for working with than
        the actual charge, whose error bar grows with each arithmetic operation.\n"""

        try:
            bits = self.__bits
        except AttributeError:
            return 0

        q = 0
        for k, v in bits.items():
            q = q + v * k._charge

        return q

    def _lazy_get_charge_(self, ignored, unit=Quantum.Millikan / 3):
        return self._charge * unit

    def _lazy_get_period_(self, ignored, k=Quantum.h / Vacuum.c**2):
        """de Broglie wave period along world-line: h/c/c/mass"""
        return k / self.restmass

    def _lazy_get_energy_(self, ignored, h=Quantum.h, P=Quantum.Planck):

        try:
            m = self.__dict__['mass']
        except KeyError:
            pass
        else:
            return m.energy

        try:
            t = self.__dict__['period']
        except KeyError:
            pass
        else:
            return h / t
        try:
            f = self.__dict__['frequency']
        except KeyError:
            pass
        else:
            return h * f
        try:
            f = self.__dict__['nu']
        except KeyError:
            pass
        else:
            return Planck * f

        raise AttributeError('energy', 'mass', 'period', 'frequency', 'nu')

    def _lazy_get_mass_(self, ignored):
        return self.energy.mass

    def _lazy_get_qperm_(self, ignored):
        """Charge-to-mass ratio"""
        return self.charge / self.mass

    def _lazy_get_frequency_(self, ignored, h=Quantum.h):
        return self.energy / h

    def _lazy_get_nu_(self, ignored, h=Quantum.Planck):
        try:
            return self.frequency / turn
        except AttributeError:
            pass
        return self.energy / h

    def _lazy_get_momentum_(self, ignored, hbar=Quantum.hbar, h=Quantum.h):
        try:
            k = self.__dict__['wavevector']
        except KeyError:
            pass
        else:
            return hbar * k

        try:
            d = self.__dict__['wavelength']
        except KeyError:
            pass
        else:
            return h / d

        raise AttributeError('momentum', 'wavevector', 'wavelength')

    def _lazy_get_wavevector_(self, ignored, hbar=Quantum.hbar):
        return self.momentum / hbar

    def _lazy_get_wavelength_(self, ignored, h=Quantum.h):
        return h / self.momentum

    def resolve(self, aperture):
        """Resolving power of an aperture.

        Returns the angle subtended, at an aperture of given diameter, by the
        gap between a pair of objects that can just be resolved by an aparatus
        observing these objects through that aperture using particles whose
        wavelength is equal to that of self.\n"""
        return (self.wavelength / aperture).arcSin

    def _lazy_get_restmass_(self, ignored, csqr=Vacuum.c**2):
        return (self.mass**2 - abs(self.momentum)**2 / csqr)**.5

    def __str__(self):
        return self.__name

    def __repr__(self):
        return '%s.%s' % (self._namespace, self.__name)

    def _lazy_get__namespace_(self, ignored):
        """Namespace in which to look for self `normally'.

        This should usually be self's class; however, where a class has
        sub-classes to make distinctions (e.g. that between bosonic and
        fermionic nuclei, below) one orthodoxly ignores, self may prefer to be
        sought in the base-class with the nice familiar name rather than in the
        pedantically more apt derived class.\n"""

        return '%s.item' % self.__class__.__name__

    def __hash__(self):
        return hash(self.__name)
Esempio n. 13
0
def between(lo, hi, units, *args, **what):
    return Quantity.flat(lo, hi, None, units, *args, **what)
Esempio n. 14
0
Fragments:
  debt -- description of debts and mortgages
  job -- description of a job

See study.LICENSE for copyright and license information.
"""
from study.value.quantity import Quantity

quid = Quantity.base_unit(
    '£', 'Pound Sterling', """The base unit of British currency.

Used to be 20 shillings (21 shillings made a Guinea); each shilling was 12
pence, each penny was four farthings.  A florin was two shillings; a crown was
five.  Apparently a pound was also called a sovereign.  HTML supports character
entity &sterling; for the Pound Sterling.

To properly handle money within a system of units, I need support for variation
in time and space (conversion factors between different currencies vary with
time; and you'll get different exchange rates from different trading partners).
Then again, conversion factors between systems of units also show similar
variation - contrast the different nations' archaic units of length, and notice
how units of volume got re-defined by assorted legislative acts over the years.
""")

# It's clearly inadequate to treat money units as approximate multiples of one
# another: each is an exact unit in its place, it's only the conversion between
# them that's approximate.
krone = quid / Quantity.within(10, 2)

del Quantity
Esempio n. 15
0
def waterviscosity(T,
                   A=2.414e-5 * Pascal * second,
                   K=Kelvin,
                   ten=Quantity(10)):
    """Variation of water's dynamic viscosity with temperature.

    Takes one argument, an absolute temperature (e.g. a return from
    study.value.archaea's Centigrade or Fahrenheit).  Result is probably only
    valid if this is a temperature at which water is a liquid !\n"""
    return A * ten**(247.8 / (T / K - 140))  # adapted from Wikipedia


water = Substance(
    density=Quantity.fromDecimal(
        1 - 27e-6, 6, None, kilogramme / litre, """Density of water.

at 277.13K, when density is maximal.  The definition of the UK gallon used to
make the density of water 10 pound / gallon at some specific temperature; but
now both pound and gallon are defined in terms of SI.\n"""),
    viscosity=waterviscosity,
    heat=Heats(capacity=Quantity(
        1, calorie / gram / Kelvin, """The specific heat capacity of water.

The definition of the (short) calorie is as the energy it takes to heat one
gram of water by one degree Celsius.  Naturally, this varies with temperature;
see calorie's documentation for consequences.
""")),
    temperature=Temperatures(
        triple=Quantity(
            273.16, Kelvin,
            "Triple point of water (by definition of the Kelvin)."),
        melt=Quantity(273.150, Kelvin,
Esempio n. 16
0
class BlackHole(Object):  # should be based on chemy.thermal.Radiator
    """A simple Schwarzschild black hole.

    Unlike other species of particle, black holes can have pretty much any
    mass.  Their mass, radius, apparent temperature and surface gravity are
    all simply related to one another.  Any one of these four quantities
    suffices for our initializer.  See the documentation of Thermal.Hawking
    and Cosmos.Schwarzschild for details; and
    http://casa.colorado.edu/~ajsh/hawk.html\n"""

    __upinit = Object.__init__

    def __init__(self, *args, **what):
        """Set up a description of a black hole.

        Any positional arguments that are instances of Quantity shall be used
        as keyword arguments using the kind of quantity as name, treating a
        length as radius.  Pass at least one of mass, radius, (surface)
        gravity and temperature, either as keyword or positional.\n"""

        row = []
        for arg in args:
            if not isinstance(arg, Quantity): row.append(arg)
            else: bykind(arg, what, {'length': 'radius'})

        self.__upinit(*row, **what)

    def __repr__(self):
        return 'BlackHole(mass=%s)' % ` self.mass `

    def _lazy_get_mass_(self, ig, tm=Thermal.Hawking):
        return tm / self.temperature

    def _lazy_get_radius_(self, ig, mr=Cosmos.Schwarzschild):
        return mr * self.mass

    def _lazy_get_gravity_(self, ig, rg=Vacuum.c**2 * .5):
        return rg / self.radius

    def _lazy_get_temperature_(self,
                               ig,
                               gt=Quantum.hbar * .5 / pi / Vacuum.c /
                               Thermal.k):
        return gt * self.gravity

    def _lazy_get_tidal_(self, ig):
        return 2 * self.gravity / self.radius

    def _lazy_get_diameter_(self, ignored):
        return 2 * self.radius

    def _lazy_get_circumference_(self, ignored, tp=2 * pi):
        return tp * self.radius

    def _lazy_get_area_(self, ignored, fp=4 * pi):
        return fp * self.radius**2

    def _lazy_get_volume_(self, ignored, ftp=pi / .75):
        return ftp * self.radius**3

    def _lazy_get_luminosity_(self, ig, q=Quantum.h / 30, k=Cosmos.kappa):
        """Rate of energy loss due to Hawking radiation.

A black hole emits a photon of wavelength comparable to its size about once
per the time light takes to travel that far.  So big black holes are very
stable and little ones blow up really fast.

The power output of the thermal radiation - the Hawking luminosity - is given
by the usual Stefan-Boltzmann law,

        L = A sigma T**4

where A is the surface area, 4 pi r**2, sigma is the Stefan-Boltzmann constant
and T is the apparent temperature of the black hole.  These yield

        L = 4 pi sigma (hbar c / 4 / pi / k)**4 / r**2
          = 4*(pi**3/60/c**2/hbar**3) * (hbar*c/(4*pi))**4 * (c**2/(2*G*m))**2
          = hbar * c**6 / 15 / pi / 1024 / G**2 / m**2
          = h / 480 / kappa**2 / m**2

However, L rises by a factor N/2 where N is the number of particle types
having mass less than k.T/c/c, counting two helicity types of photons
separately so that N is at least 2 for any positive T.  (But presumably this
is actually true for the Stefan-Boltzmann law generally, at least in so far as
the thermal energy is accessible to some mechanism capable of interacting with
the particle type.  I must also guess that particle types don't turn on
step-wise; as k.T/c/c nears the mass of a particle type, some of the particle
must surely show up before they reach full power.)  Note that k.T/c/c =
hbar.c/8/pi/G/m is the mass limit for particle types; this is the square of
the Planck mass divided by 4.pi.m.

Neutrinos are the least massive particles type, so should provide the lowest
temperature deviation from the above; which, once observed, would give us a
datum on the mass of neutrinos.  However, maybe the thermal energy of a
material is normally fairly well de-coupled from any thermal energy inside its
nuclei, hence there's no channel for normal thermal energy to couple with a
process capable of producing neutrinos.  The black hole should be free of this
issue, so we can expect its evaporation to be affected by neutrinos
first.  The known upper bound of 82e-36 kg (see below) implies a temperature
of around half a million Kelvin below which we should notice neutrinos
complicating this law.
"""
        return q / (k * 4 * self.mass)**2

    MassCubedRate = Quantity(
        -.1, Quantum.h / (4 * Vacuum.c * Cosmos.kappa)**2,
        """Rate of decrease of the cube of a black hole's mass.

  ... the evaporation time is shorter for smaller black holes (evaporation
  time t is proportional to M**3), and black holes with masses less than about
  1e11 kg (the mass of a small mountain) can evaporate in less than the age of
  the Universe.

Observe that dm/dt = -L/c/c (see BlackHole._lazy_get_luminosity_.__doc__)
gives

        d(m**3)/dt = 3 m**2 dm/dt = -h * N / 320 / c**2 / kappa**2

constant, except for N's variation, confirming that time to evaporate is
proportional to cube of mass: cube of mass decreases at a constant rate,
11.897 peta kg**3 / s, while N = 2.  This (a dozen cubic tonnes per
microsecond) also deserves to be imortalized as someone's constant ;^)

The above-mentioned 1e11 kg black hole that would evaporate within the present
age of the universe has an initial temperature of over a tera Kelvin.  Which
is at least tiny compared to the Planck temperature ... to evaporate within a
century, a black hole would need to have mass at most a third of a million
tonnes; with a radius less than half an atto-metre, this would be a very good
approximation to a point mass.
""")

    def _lazy_get_lifespan_(self, ig, rate=-MassCubedRate):
        """How long it'll take this black hole to evaporate.

        Assumes the black hole is isolated in a universe whose cosmic
        microwave background, if any, has an effective temperature
        significantly lower than that of the black hole.  This condition is
        not met by most known black holes; it would only be met, in the known
        universe, by a black hole whose mass is significantly smaller than
        that of the Moon.

        A black hole, with temperature equal to that of our observed cosmic
        microwave background, would have mass .6127 times that of the
        Moon.  It would be in thermal equilibrium, if otherwise isolated, so
        would not be shrinking at all.  However, the cosmic microwave
        background cools adiabatically as the universe expands, so its
        temperature will eventually fall below that of the black hole, causing
        it to evaporate (albeit slowly), so warm, hence evaporate faster and
        duly decay; so it would not last for ever - but, even if fully
        isolated as assumed here, its .lifespan is more than 17e33 times the
        present age of our universe; which is a respectably good approximation
        to 'for ever' !\n"""
        return self.mass**3 / rate

    VolumeRate = Quantity(
        -.1 / 768 / pi**2, Quantum.h * Cosmos.kappa * Vacuum.c,
        """Rate of decrease of volume of a black hole.

Given that r = 2.G.m/c/c is proportional to m, we can infer (from
MassCubedRate, q.v.) that r**3 also decreases at the constant rate h kappa c N
/ 20480 / pi**3, or about 39e-66 cubic metres per second when N = 2, whence
the nominal volume of the black hole decreases at 4.pi/3 times this, i.e. h
kappa c N / 15360 / pi**2 or about 0.16e-63 cubic metres per second when N =
2.  With k.T inversely proportional to mass, we can infer that (k.T)**-3
decreases at rate pi**3 kappa N / (h c)**2 / 40, so 1/T**3 decreases at
6.433e-54 / second / Kelvin**3 when N = 2.  Note that a black hole hot enough
to boil Osmium at its surface (i.e. above 5300 Kelvin) would have radius about
32 nano metres, mass 23 peta tonnes (about 3e-4 of the Moon's mass) yet would
still take over 28e33 years to boil away ...

Of course, all these evaporation times are over-estimates; they ignore the
fact that N increases as T grows.  At half a peta Kelvin, k.T/c/c exceeds the
mass of the heaviest quark, Truth; at 2.6 peta Kelvin it exceeds the mass of a
Uranium atom.  If N only counts fundamental particle types, it's at least 14
at the first of these and I can't say for sure that it ever gets above that;
othewise, it's functionally infinite (every isotope, including all the
unstable ones, of every known element; plus quite a lot of stranger things) by
around the second, which is only five times as big.  A half peta Kelvin black
hole has mass just over a quarter of a million tonnes and radius under 0.4e-18
metres; with N=14 this will take about 7 years to evaporate.

It seems reasonable to guess that nothing but neutrinos (and photons) have less
mass than the electron.  On that assumption, N is at most 5 for temperatures
below 5.9 giga Kelvin, radii above 31 femto metres (of order the size of
nuclei), masses above 21 giga tonne.  Even if we (conservatively) suppose N
jumps to infinity at this threshold, we only eliminate the last 23 peta years of
the time a black hole takes to evaporate; and at least 40% of the remainder
survives the N-scaling; for just the age of the universe to remain requires only
22 million tonnes of extra mass, about one part in a thousand.  Thus any black
hole bigger or colder than the threshold just described will last essentially
for ever; and even an asteroid a couple of kilometres across has more mass than
that.
""")
Esempio n. 17
0
del Nucleon

def waterviscosity(T,
                   A=2.414e-5 * Pascal * second, K=Kelvin,
                   ten=Quantity(10)):
    """Variation of water's dynamic viscosity with temperature.

    Takes one argument, an absolute temperature (e.g. a return from
    study.value.archaea's Centigrade or Fahrenheit).  Result is probably only
    valid if this is a temperature at which water is a liquid !\n"""
    return A * ten**(247.8/(T/K -140)) # adapted from Wikipedia

water = Substance(
        density = Quantity.fromDecimal(1 -27e-6, 6, None, kilogramme / litre,
                                       """Density of water.

at 277.13K, when density is maximal.  The definition of the UK gallon used to
make the density of water 10 pound / gallon at some specific temperature; but
now both pound and gallon are defined in terms of SI.\n"""),

        viscosity = waterviscosity,
        heat = Heats(
    capacity = Quantity(1, calorie / gram / Kelvin,
                        """The specific heat capacity of water.

The definition of the (short) calorie is as the energy it takes to heat one
gram of water by one degree Celsius.  Naturally, this varies with temperature;
see calorie's documentation for consequences.
""")),
        temperature = Temperatures(
    triple = Quantity(273.16, Kelvin,
                      "Triple point of water (by definition of the Kelvin)."),