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