001/*
002 * Units of Measurement Enum Implementation
003 * Copyright © 2005-2021, 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-385, Unit-API nor the names of their contributors may be used to endorse or promote products
017 *    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 tech.uom.impl.enums;
031
032import java.lang.ref.Reference;
033import java.lang.ref.WeakReference;
034import java.util.Map;
035
036import javax.measure.Dimension;
037import javax.measure.UnitConverter;
038
039import tech.uom.impl.enums.function.AbstractConverter;
040import tech.uom.impl.enums.unit.SimpleDimension;
041
042/**
043 * <p> This class represents the physical model used for dimensional analysis.</p>
044 *
045* <p> In principle, dimensions of physical quantities could be defined as "fundamental"
046 *     (such as momentum or energy or electric current) making such quantities
047 *     uncommensurate (not comparable). Modern physics has cast doubt on 
048 *     the very existence of incompatible fundamental dimensions of physical quantities.
049 *     For example, most physicists do not recognize temperature, 
050 *     {@link QuantityDimension#TEMPERATURE Θ}, as a fundamental dimension since it 
051 *     essentially expresses the energy per particle per degree of freedom, 
052 *     which can be expressed in terms of energy (or mass, length, and time).
053 *     To support, such model the method {@link #getConverter} may 
054 *     returns a non-null value for distinct dimensions.</p> 
055 *     
056  * <p> The default model is {@link StandardModel Standard}. Applications may
057 *     use one of the predefined model or create their own.
058 *     [code]
059 *     DimensionalModel relativistic = new DimensionalModel() {
060 *         public Dimension getFundamentalDimension(QuantityDimension dimension) {
061 *             if (dimension.equals(QuantityDimension.LENGTH)) return QuantityDimension.TIME; // Consider length derived from time.
062 *                 return super.getDimension(dimension); // Returns product of fundamental dimension.
063 *             }
064 *             public UnitConverter getDimensionalTransform(QuantityDimension dimension) {
065 *                 if (dimension.equals(QuantityDimension.LENGTH)) return new RationalConverter(1, 299792458); // Converter (1/C) from LENGTH SI unit (m) to TIME SI unit (s).
066 *                 return super.getDimensionalTransform(dimension);
067 *             }
068 *     };
069 *     LocalContext.enter();
070 *     try {
071 *         DimensionalModel.setCurrent(relativistic); // Current thread use the relativistic model.
072 *         SI.KILOGRAM.getConverterToAny(SI.JOULE); // Allowed.
073 *         ...
074 *     } finally {
075 *         LocalContext.exit();
076 *     }
077 *     [/code]</p>
078 *     
079 * @see <a href="http://en.wikipedia.org/wiki/Dimensional_analysis">Wikipedia: Dimensional Analysis</a>
080 * @author  <a href="mailto:units@catmedia.us">Werner Keil</a>
081 * @version 0.2, $Date$
082 */
083public abstract class DimensionalModel {
084
085    /**
086     * Holds the current model.
087     */
088    private static Reference<DimensionalModel> Current = new WeakReference<DimensionalModel>(new StandardModel());
089
090    /**
091     * Returns the physics model used by the current thread
092     * (by default an instance of {@link StandardModel}).
093     *
094     * @return the getCurrent physical model.
095     * @see LocalContext
096     */
097    public static DimensionalModel current() {
098        return DimensionalModel.Current.get();
099    }
100
101//    /**
102//     * Sets the current physics model (local to the current thread when executing
103//     * within a {@link LocalContext}).
104//     *
105//     * @param  model the context-local physics model.
106//     * @see    #getCurrent
107//     */
108//    public static void setCurrent(DimensionalModel model) {
109//        DimensionalModel.Current. .Current set(model);
110//    }
111
112    /**
113     * Default constructor (allows for derivation).
114     */
115    protected DimensionalModel() {
116    }
117
118    /**
119     * Returns the fundamental dimension for the one specified.
120     * If the specified dimension is a dimensional product, the dimensional
121     * product of its fundamental dimensions is returned.
122     * Physical quantities are considered commensurate only if their
123     * fundamental dimensions are equals using the current physics model.
124     *
125     * @param dimension the dimension for which the fundamental dimension is returned.
126     * @return <code>this</code> or a rational product of fundamental dimension.
127     */
128    public Dimension getFundamentalDimension(Dimension dimension) {
129        Map<? extends Dimension, Integer> dimensions = dimension.getBaseDimensions();
130        if (dimensions == null) return dimension; // Fundamental dimension.
131        // Dimensional Product.
132        Dimension fundamentalProduct = SimpleDimension.INSTANCE;
133        for (Map.Entry<? extends Dimension, Integer> e : dimensions.entrySet()) {
134             fundamentalProduct = fundamentalProduct.multiply(this.getFundamentalDimension(e.getKey())).pow(e.getValue());
135        }
136        return fundamentalProduct;
137    }
138
139    /**
140     * Returns the dimensional transform of the specified dimension.
141     * If the specified dimension is a fundamental dimension or
142     * a product of fundamental dimensions the identity converter is
143     * returned; otherwise the converter from the system unit (SI) of
144     * the specified dimension to the system unit (SI) of its fundamental
145     * dimension is returned.
146     *
147     * @param dimension the dimension for which the dimensional transform is returned.
148     * @return the dimensional transform (identity for fundamental dimensions).
149     */
150    public UnitConverter getDimensionalTransform(Dimension dimension) {
151        Map<? extends Dimension, Integer> dimensions = dimension.getBaseDimensions();
152        if (dimensions == null) return AbstractConverter.IDENTITY; // Fundamental dimension.
153        // Dimensional Product.
154        UnitConverter toFundamental = AbstractConverter.IDENTITY;
155        for (Map.Entry<? extends Dimension, Integer> e : dimensions.entrySet()) {
156            UnitConverter cvtr = this.getDimensionalTransform(e.getKey());
157            if (!(cvtr.isLinear()))
158                throw new UnsupportedOperationException("Non-linear dimensional transform");
159            int pow = e.getValue();
160            if (pow < 0) { // Negative power.
161                pow = -pow;
162                cvtr = cvtr.inverse();
163            }
164            for (int j = 0; j < pow; j++) {
165                toFundamental = toFundamental.concatenate(cvtr);
166            }
167        }
168        return toFundamental;
169    }
170
171}