001/*
002 * International System of Units (SI)
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil and others.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-363, Units of Measurement nor the names of their contributors may be used to
017 *    endorse or promote products derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package si.uom;
031
032import static tec.uom.se.unit.MetricPrefix.*;
033
034import javax.measure.Quantity;
035import javax.measure.Unit;
036import javax.measure.quantity.Acceleration;
037import javax.measure.quantity.Angle;
038import javax.measure.quantity.Area;
039import javax.measure.quantity.Energy;
040import javax.measure.quantity.Length;
041import javax.measure.quantity.Mass;
042
043import si.uom.quantity.Action;
044import si.uom.quantity.AngularAcceleration;
045import si.uom.quantity.AngularSpeed;
046import si.uom.quantity.DynamicViscosity;
047import si.uom.quantity.ElectricPermittivity;
048import si.uom.quantity.IonizingRadiation;
049import si.uom.quantity.KinematicViscosity;
050import si.uom.quantity.Luminance;
051import si.uom.quantity.MagneticFieldStrength;
052import si.uom.quantity.MagneticPermeability;
053import si.uom.quantity.MagnetomotiveForce;
054import si.uom.quantity.Radiance;
055import si.uom.quantity.RadiantIntensity;
056import si.uom.quantity.WaveNumber;
057import tec.uom.se.AbstractSystemOfUnits;
058import tec.uom.se.AbstractUnit;
059import tec.uom.se.format.SimpleUnitFormat;
060import tec.uom.se.function.MultiplyConverter;
061import tec.uom.se.function.PiMultiplierConverter;
062import tec.uom.se.function.RationalConverter;
063import tec.uom.se.unit.AlternateUnit;
064import tec.uom.se.unit.ProductUnit;
065import tec.uom.se.unit.TransformedUnit;
066import tec.uom.se.unit.Units;
067
068/**
069 * <p>
070 * This class defines all SI (Système International d'Unités) base units and
071 * derived units as well as units that are accepted for use with the SI units.
072 * </p>
073 *
074 * @see <a href=
075 *      "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia:
076 *      International System of Units</a>
077 * @see <a href="http://physics.nist.gov/cuu/Units/outside.html">Units outside
078 *      the SI that are accepted for use with the SI</a>
079 * @see <a href="http://www.bipm.org/utils/common/pdf/si_brochure_8.pdf">SI 2006
080 *      - Official Specification</a>
081 * @see tec.uom.se.unit.MetricPrefix
082 *
083 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
084 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
085 * @version 1.0.6, September 2, 2017
086 */
087public final class SI extends Units {
088    /**
089     * The singleton instance.
090     */
091    private static final SI INSTANCE = new SI();
092
093    ////////////////////////////////
094    // SI DERIVED ALTERNATE UNITS //
095    ////////////////////////////////
096
097    /**
098     * The SI unit for magnetomotive force (standard name <code>At</code>).
099     */
100    public static final Unit<MagnetomotiveForce> AMPERE_TURN = addUnit(
101            new AlternateUnit<MagnetomotiveForce>(Units.AMPERE, "At"), MagnetomotiveForce.class);
102
103    //////////////////////////////
104    // SI DERIVED PRODUCT UNITS //
105    //////////////////////////////
106
107    /**
108     * The SI unit for acceleration quantities (standard name
109     * <code>m/s²</code>).
110     */
111    public static final Unit<Acceleration> METRE_PER_SQUARE_SECOND = addUnit(
112            new ProductUnit<Acceleration>(METRE_PER_SECOND.divide(SECOND)), Acceleration.class);
113    /**
114     * Alias {@link #METRE_PER_SQUARE_SECOND}
115     * 
116     * @deprecated use METRE_PER_SQUARE_SECOND
117     */
118    public static final Unit<Acceleration> METRES_PER_SQUARE_SECOND = METRE_PER_SQUARE_SECOND;
119
120    /**
121     * The SI unit for action quantities (standard name <code>j.s</code>).
122     */
123    public static final Unit<Action> JOULE_SECOND = addUnit(new ProductUnit<Action>(JOULE.multiply(SECOND)),
124            Action.class);
125
126    /**
127     * The SI unit for electric permittivity (standard name <code>ε</code>,
128     * <code>F/m </code> or <code>F·m−1</code>). In electromagnetism, absolute
129     * permittivity is the measure of resistance that is encountered when
130     * forming an electric field in a medium.
131     */
132    public static final Unit<ElectricPermittivity> FARAD_PER_METRE = addUnit(
133            new AlternateUnit<ElectricPermittivity>(FARAD.divide(METRE), "ε"), ElectricPermittivity.class);
134  
135    /**
136     * The SI unit for magnetic permeability quantities (standard name
137     * <code>N/A2</code>).
138     */
139    public static final Unit<MagneticPermeability> NEWTON_PER_SQUARE_AMPERE = addUnit(
140            new ProductUnit<MagneticPermeability>(NEWTON.divide(AMPERE.pow(2))), MagneticPermeability.class);
141
142    /**
143     * The SI unit for wave number quantities (standard name <code>1/m</code>).
144     */
145    public static final Unit<WaveNumber> RECIPROCAL_METRE = addUnit(new ProductUnit<WaveNumber>(METRE.pow(-1)),
146            WaveNumber.class);
147
148    /**
149     * The SI unit for dynamic viscosity quantities (standard name
150     * <code>Pa.s</code>).
151     */
152    public static final Unit<DynamicViscosity> PASCAL_SECOND = addUnit(
153            new ProductUnit<DynamicViscosity>(PASCAL.multiply(SECOND)), DynamicViscosity.class);
154
155    /**
156     * Luminance is a photometric measure of the luminous intensity per unit
157     * area of light travelling in a given direction. It describes the amount of
158     * light that passes through, is emitted or reflected from a particular
159     * area, and falls within a given solid angle. The SI unit for luminance is
160     * candela per square metre (<code>cd/m2</code>).
161     * 
162     * @see <a href="https://en.wikipedia.org/wiki/Luminance"> Wikipedia:
163     *      Luminance</a>
164     */
165    public static final Unit<Luminance> CANDELA_PER_SQUARE_METRE = addUnit(
166            new ProductUnit<Luminance>(CANDELA.divide(SQUARE_METRE)), Luminance.class);
167
168    /**
169     * The SI unit for kinematic viscosity quantities (standard name
170     * <code>m2/s"</code>).
171     */
172    public static final Unit<KinematicViscosity> SQUARE_METRE_PER_SECOND = addUnit(
173            new ProductUnit<KinematicViscosity>(SQUARE_METRE.divide(SECOND)), KinematicViscosity.class);
174
175    /**
176     * Alias for {@link #SQUARE_METRE_PER_SECOND}
177     * 
178     * @deprecated use SQUARE_METRE_PER_SECOND
179     */
180    public static final Unit<KinematicViscosity> SQUARE_METRES_PER_SECOND = SQUARE_METRE_PER_SECOND;
181
182    /**
183     * A magnetic field is the magnetic effect of electric currents and magnetic
184     * materials. The magnetic field at any given point is specified by both a
185     * direction and a magnitude (or strength); as such it is a vector field.
186     * The H-field is measured in amperes per metre (<code>A/m</code>) in SI
187     * units.
188     * 
189     * @see <a href="https://en.wikipedia.org/wiki/Magnetic_field#The_H-field">
190     *      Wikipedia: Magnetic Field - The H Field</a>
191     */
192    public static final Unit<MagneticFieldStrength> AMPERE_PER_METRE = addUnit(
193            new ProductUnit<MagneticFieldStrength>(AMPERE.divide(METRE)), MagneticFieldStrength.class);
194
195
196    /**
197     * The SI unit for ionizing radiation quantities (standard name
198     * <code>C/kg"</code>).
199     */
200    public static final Unit<IonizingRadiation> COULOMB_PER_KILOGRAM = addUnit(
201            new ProductUnit<IonizingRadiation>(COULOMB.divide(KILOGRAM)), IonizingRadiation.class);
202 
203    /**
204     * The SI unit for radiant intensity (standard name <code>W/sr</code>).
205     */
206    public static final Unit<RadiantIntensity> WATT_PER_STERADIAN = addUnit(
207            WATT.divide(STERADIAN).asType(RadiantIntensity.class));
208
209    /**
210     * The SI unit for radiance (standard name <code>W⋅sr−1⋅m−2</code>).
211     */
212    public static final Unit<Radiance> WATT_PER_STERADIAN_PER_SQUARE_METRE = addUnit(
213            WATT_PER_STERADIAN.divide(SQUARE_METRE).asType(Radiance.class));
214
215    /**
216     *  The SI unit of angular speed (standard name
217     * <code>rad/s</code>).
218     * @see AngularSpeed
219     */
220    public static final Unit<AngularSpeed> RADIAN_PER_SECOND = addUnit(
221        new ProductUnit<AngularSpeed>(RADIAN.divide(SECOND)), "Radian per second", AngularSpeed.class);
222    
223    /**
224     *  The SI unit of angular acceleration (standard name
225     * <code>rad/s²</code>).
226     * @see AngularAcceleration
227     */
228    public static final Unit<AngularAcceleration> RADIAN_PER_SQUARE_SECOND = addUnit(
229        new ProductUnit<AngularAcceleration>(RADIAN_PER_SECOND.divide(SECOND)), "Radian per square second", AngularAcceleration.class);
230
231    
232    /////////////////////////////////////////////////////////////////
233    // Units outside the SI that are accepted for use with the SI. //
234    /////////////////////////////////////////////////////////////////
235
236    /**
237     * An angle unit accepted for use with SI units (standard name
238     * <code>deg</code>).
239     * 
240     * @deprecated use NonSI
241     */
242    public static final Unit<Angle> DEGREE_ANGLE = addUnit(
243            new TransformedUnit<Angle>(RADIAN, new PiMultiplierConverter().concatenate(new RationalConverter(1, 180))));
244
245    /**
246     * An angle unit accepted for use with SI units (standard name
247     * <code>'</code>).
248     * @deprecated use NonSI
249     */
250    public static final Unit<Angle> MINUTE_ANGLE = addUnit(new TransformedUnit<Angle>(RADIAN,
251            new PiMultiplierConverter().concatenate(new RationalConverter(1, 180 * 60))));
252
253    /**
254     * An angle unit accepted for use with SI units (standard name
255     * <code>''</code>).
256     * @deprecated use NonSI
257     */
258    public static final Unit<Angle> SECOND_ANGLE = addUnit(new TransformedUnit<Angle>(RADIAN,
259            new PiMultiplierConverter().concatenate(new RationalConverter(1, 180 * 60 * 60))));
260
261    /**
262     * A mass unit accepted for use with SI units (standard name
263     * <code>t</code>).
264     */
265    public static final Unit<Mass> TONNE = AbstractSystemOfUnits.Helper.addUnit(INSTANCE.units,
266            new TransformedUnit<Mass>(KILOGRAM, new RationalConverter(1000, 1)), "Tonne", "t");
267
268    /**
269     * An energy unit accepted for use with SI units (standard name
270     * <code>eV</code>). The electronvolt is the kinetic energy acquired by an
271     * electron passing through a potential difference of 1 V in vacuum. The
272     * value must be obtained by experiment, and is therefore not known exactly.
273     */
274    public static final Unit<Energy> ELECTRON_VOLT = new TransformedUnit<Energy>(JOULE,
275            new MultiplyConverter(1.602176487E-19));
276    // CODATA 2006 - http://physics.nist.gov/cuu/Constants/codata.pdf
277
278    /**
279     * A mass unit accepted for use with SI units (standard name
280     * <code>u</code>). The unified atomic mass unit is equal to 1/12 of the
281     * mass of an unbound atom of the nuclide 12C, at rest and in its ground
282     * state. The value must be obtained by experiment, and is therefore not
283     * known exactly.
284     */
285    public static final Unit<Mass> UNIFIED_ATOMIC_MASS = addUnit(
286            new TransformedUnit<Mass>(KILOGRAM, new MultiplyConverter(1.660538782E-27)), "Unified atomic mass", "u",
287            true);
288    // CODATA 2006 - http://physics.nist.gov/cuu/Constants/codata.pdf
289
290    /**
291     * A length unit accepted for use with SI units (standard name
292     * <code>UA</code>). The astronomical unit is a unit of length. Its value is
293     * such that, when used to describe the motion of bodies in the solar
294     * system, the heliocentric gravitation constant is (0.017 202 098 95)2
295     * ua3·d-2. The value must be obtained by experiment, and is therefore not
296     * known exactly.
297     */
298    public static final Unit<Length> ASTRONOMICAL_UNIT = addUnit(
299            new TransformedUnit<Length>(METRE, new MultiplyConverter(149597871000.0)));
300    // Best estimate source: http://maia.usno.navy.mil/NSFA/CBE.html
301
302    /**
303     * An angle unit accepted for use with SI units (standard name
304     * <code>rev</code>).
305     */
306    public static final Unit<Angle> REVOLUTION = addUnit(
307            new TransformedUnit<Angle>(RADIAN, new PiMultiplierConverter().concatenate(new RationalConverter(2, 1))));
308
309    /**
310     * An angle unit accepted for use with SI units (standard name
311     * <code>ha</code>).
312     */
313    public static final Unit<Area> HECTARE = new TransformedUnit<Area>(SQUARE_METRE, new RationalConverter(10000, 1));
314
315    /////////////////////
316    // Collection View //
317    /////////////////////
318    
319    /**
320     * Default constructor (prevents this class from being instantiated).
321     */
322    private SI() { // Singleton
323    }
324
325    @Override
326    public String getName() {
327        return SI.class.getSimpleName(); // for Java SE this works
328    }
329    
330    /**
331     * Returns the singleton instance of this class.
332     *
333     * @return the metric system instance.
334     */
335    public static SI getInstance() {
336        return INSTANCE;
337    }
338    
339    /**
340     * Adds a new unit not mapped to any specified quantity type and puts a text
341     * as symbol or label.
342     *
343     * @param unit
344     *            the unit being added.
345     * @param name
346     *            the string to use as name
347     * @param text
348     *            the string to use as label or symbol
349     * @param isLabel
350     *            if the string should be used as a label or not
351     * @return <code>unit</code>.
352     */
353    private static <U extends Unit<?>> U addUnit(U unit, String name, String text, boolean isLabel) {
354        if (isLabel) {
355            SimpleUnitFormat.getInstance().label(unit, text);
356        }
357        if (name != null && unit instanceof AbstractUnit) {
358            return Helper.addUnit(INSTANCE.units, unit, name);
359        } else {
360            INSTANCE.units.add(unit);
361        }
362        return unit;
363    }
364
365    /**
366     * Adds a new unit not mapped to any specified quantity type and puts a text
367     * as symbol or label.
368     *
369     * @param unit
370     *            the unit being added.
371     * @param text
372     *            the string to use as label or symbol
373     * @param isLabel
374     *            if the string should be used as a label or not
375     * @return <code>unit</code>.
376     */
377    @SuppressWarnings("unused")
378    private static <U extends Unit<?>> U addUnit(U unit, String text, boolean isLabel) {
379        return addUnit(unit, null, text, isLabel);
380    }
381
382    /**
383     * Adds a new unit not mapped to any specified quantity type.
384     *
385     * @param unit
386     *            the unit being added.
387     * @return <code>unit</code>.
388     */
389    private static <U extends Unit<?>> U addUnit(U unit) {
390        INSTANCE.units.add(unit);
391        return unit;
392    }
393    
394    /**
395     * Adds a new unit with name and label and maps it to the specified quantity type.
396     *
397     * @param unit
398     *            the unit being added.
399     * @param name
400     *            the string to use as name
401     * @param label
402     *            the string to use as label
403     * @param type
404     *            the quantity type.
405     * @return <code>unit</code>.
406     */
407    private static <U extends AbstractUnit<?>> U addUnit(U unit, String name, String label, Class<? extends Quantity<?>> type) {
408        INSTANCE.quantityToUnit.put(type, unit);
409        return addUnit(unit, name, label, true);
410    }
411    
412    /**
413     * Adds a new unit with a name and maps it to the specified quantity type.
414     *
415     * @param unit
416     *            the unit being added.
417     * @param name
418     *            the string to use as name
419     * @param type
420     *            the quantity type.
421     * @return <code>unit</code>.
422     */
423    private static <U extends AbstractUnit<?>> U addUnit(U unit, String name, Class<? extends Quantity<?>> type) {
424        INSTANCE.quantityToUnit.put(type, unit);
425        return addUnit(unit, name, null, false);
426    }
427
428    /**
429     * Adds a new unit and maps it to the specified quantity type.
430     *
431     * @param unit
432     *            the unit being added.
433     * @param type
434     *            the quantity type.
435     * @return <code>unit</code>.
436     */
437    private static <U extends AbstractUnit<?>> U addUnit(U unit, Class<? extends Quantity<?>> type) {
438        INSTANCE.units.add(unit);
439        INSTANCE.quantityToUnit.put(type, unit);
440        return unit;
441    }
442
443    // //////////////////////////////////////////////////////////////////////////
444    // Label adjustments for SI
445    static {
446        SimpleUnitFormat.getInstance().label(TONNE, "t");
447        SimpleUnitFormat.getInstance().label(MEGA(TONNE), "Mt");
448    }
449}