View Javadoc
1   package org.djunits.quantity;
2   
3   import org.djunits.quantity.def.AbsQuantity;
4   import org.djunits.quantity.def.AbstractReference;
5   import org.djunits.quantity.def.Quantity;
6   import org.djunits.unit.AbstractUnit;
7   import org.djunits.unit.UnitRuntimeException;
8   import org.djunits.unit.Units;
9   import org.djunits.unit.scale.LinearScale;
10  import org.djunits.unit.scale.Scale;
11  import org.djunits.unit.si.SIUnit;
12  import org.djunits.unit.system.UnitSystem;
13  
14  /**
15   * Temperature is the absolute equivalent of Temperature, and represents a true temperature rather than a temperature
16   * difference.
17   * <p>
18   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
19   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
20   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
21   * @author Alexander Verbraeck
22   */
23  public class Temperature extends AbsQuantity<Temperature, TemperatureDifference, Temperature.Reference>
24  {
25      /** */
26      private static final long serialVersionUID = 600L;
27  
28      /**
29       * Instantiate a Temperature quantity with a unit and a reference point.
30       * @param valueInUnit the temperature value, expressed in a temperature unit
31       * @param unit the temperature unit in which the value is expressed, relative to the reference point
32       * @param reference the reference point of this absolute temperature
33       */
34      public Temperature(final double valueInUnit, final Temperature.Unit unit, final Reference reference)
35      {
36          super(new TemperatureDifference(valueInUnit, unit), reference);
37      }
38  
39      /**
40       * Instantiate a Temperature quantity with a unit and the KELVIN reference point.
41       * @param valueInUnit the temperature value, expressed in a temperature unit
42       * @param unit the temperature unit in which the value is expressed, relative to the reference point
43       */
44      public Temperature(final double valueInUnit, final Temperature.Unit unit)
45      {
46          this(valueInUnit, unit, unit.getReference());
47      }
48  
49      /**
50       * Instantiate a Temperature quantity with a unit, expressed as a String, and a reference point.
51       * @param valueInUnit the temperature value, expressed in the unit, relative to the reference point
52       * @param abbreviation the String abbreviation of the unit in which the value is expressed
53       * @param reference the reference point of this absolute temperature
54       */
55      public Temperature(final double valueInUnit, final String abbreviation, final Reference reference)
56      {
57          this(valueInUnit, Units.resolve(Temperature.Unit.class, abbreviation), reference);
58      }
59  
60      /**
61       * Instantiate a Temperature quantity with a unit, expressed as a String, and the reference point belonging to the unit.
62       * @param valueInUnit the temperature value, expressed in the unit, relative to the reference point
63       * @param abbreviation the String abbreviation of the unit in which the value is expressed
64       */
65      public Temperature(final double valueInUnit, final String abbreviation)
66      {
67          this(valueInUnit, Units.resolve(Temperature.Unit.class, abbreviation),
68                  Units.resolve(Temperature.Unit.class, abbreviation).getReference());
69      }
70  
71      /**
72       * Instantiate a Temperature instance based on an temperature and a reference point.
73       * @param temperature the temperature, relative to the reference point
74       * @param reference the reference point of this absolute temperature
75       */
76      public Temperature(final TemperatureDifference temperature, final Reference reference)
77      {
78          super(temperature, reference);
79      }
80  
81      /**
82       * Instantiate a Temperature instance based on an temperature and the KELVIN reference point.
83       * @param temperature the temperature, relative to the reference point
84       */
85      public Temperature(final TemperatureDifference temperature)
86      {
87          this(temperature, Reference.KELVIN);
88      }
89  
90      /**
91       * Return a Temperature instance based on an SI value and a reference point.
92       * @param si the temperature si value, relative to the reference point
93       * @param reference the reference point of this absolute temperature
94       * @return the Temperature instance based on an SI value
95       */
96      public static Temperature ofSi(final double si, final Reference reference)
97      {
98          return new Temperature(si, Temperature.Unit.SI, reference);
99      }
100 
101     /**
102      * Return a Temperature instance based on an SI value and the KELVIN reference point.
103      * @param si the temperature si value, relative to the reference point
104      * @return the Temperature instance based on an SI value
105      */
106     public static Temperature ofSi(final double si)
107     {
108         return new Temperature(si, Temperature.Unit.SI, Reference.KELVIN);
109     }
110 
111     @Override
112     public Temperature instantiate(final TemperatureDifference temperature, final Reference reference)
113     {
114         return new Temperature(temperature, reference);
115     }
116 
117     /**
118      * Returns a Temperature representation of a textual representation of a value with a unit. The String representation that
119      * can be parsed is the double value in the unit, followed by a localized or English abbreviation of the unit. Spaces are
120      * allowed, but not required, between the value and the unit.
121      * @param text the textual representation to parse into a Temperature
122      * @param reference the reference point of this absolute temperature
123      * @return the Scalar representation of the value in its unit
124      * @throws IllegalArgumentException when the text cannot be parsed
125      * @throws NullPointerException when the text argument is null
126      */
127     public static Temperature valueOf(final String text, final Reference reference)
128     {
129         return new Temperature(Quantity.valueOf(text, TemperatureDifference.ZERO), reference);
130     }
131 
132     /**
133      * Returns a Temperature representation of a textual representation of a value with a unit, and the KELVIN reference. The
134      * String representation that can be parsed is the double value in the unit, followed by a localized or English abbreviation
135      * of the unit. Spaces are allowed, but not required, between the value and the unit.
136      * @param text the textual representation to parse into a Temperature
137      * @return the Scalar representation of the value in its unit
138      * @throws IllegalArgumentException when the text cannot be parsed
139      * @throws NullPointerException when the text argument is null
140      */
141     public static Temperature valueOf(final String text)
142     {
143         return new Temperature(Quantity.valueOf(text, TemperatureDifference.ZERO), Reference.KELVIN);
144     }
145 
146     /**
147      * Returns a Temperature based on a value and the textual representation of the unit, which can be localized.
148      * @param valueInUnit the value, expressed in the unit as given by unitString
149      * @param unitString the textual representation of the unit
150      * @param reference the reference point of this absolute temperature
151      * @return the Scalar representation of the value in its unit
152      * @throws IllegalArgumentException when the unit cannot be parsed or is incorrect
153      * @throws NullPointerException when the unitString argument is null
154      */
155     public static Temperature of(final double valueInUnit, final String unitString, final Reference reference)
156     {
157         return new Temperature(Quantity.of(valueInUnit, unitString, TemperatureDifference.ZERO), reference);
158     }
159 
160     /**
161      * Returns a Temperature based on a value and the textual representation of the unit, which can be localized. Use the KELVIN
162      * reference.
163      * @param valueInUnit the value, expressed in the unit as given by unitString
164      * @param unitString the textual representation of the unit
165      * @return the Scalar representation of the value in its unit
166      * @throws IllegalArgumentException when the unit cannot be parsed or is incorrect
167      * @throws NullPointerException when the unitString argument is null
168      */
169     public static Temperature of(final double valueInUnit, final String unitString)
170     {
171         return new Temperature(Quantity.of(valueInUnit, unitString, TemperatureDifference.ZERO), Reference.KELVIN);
172     }
173 
174     @Override
175     public TemperatureDifference subtract(final Temperature other)
176     {
177         var otherRef = other.relativeTo(getReference());
178         return TemperatureDifference.ofSi(si() - otherRef.si()).setDisplayUnit(getDisplayUnit());
179     }
180 
181     @Override
182     public Temperature add(final TemperatureDifference other)
183     {
184         return new Temperature(TemperatureDifference.ofSi(si() + other.si()).setDisplayUnit(getDisplayUnit()), getReference());
185     }
186 
187     @Override
188     public Temperature subtract(final TemperatureDifference other)
189     {
190         return new Temperature(TemperatureDifference.ofSi(si() - other.si()).setDisplayUnit(getDisplayUnit()), getReference());
191     }
192 
193     /**
194      * The reference class to define a reference point for the absolute temperature.
195      */
196     public static final class Reference extends AbstractReference<Reference, Temperature, TemperatureDifference>
197     {
198         /** Kelvin. */
199         public static final Reference KELVIN =
200                 new Reference("KELVIN", "Kelvin scale temperature", TemperatureDifference.ZERO, null);
201 
202         /** Celsius. */
203         public static final Reference CELSIUS =
204                 new Reference("CELSIUS", "Celsius scale temperature", TemperatureDifference.ofSi(273.15), KELVIN);
205 
206         /** Fahrenheit. */
207         public static final Reference FAHRENHEIT = new Reference("FAHRENHEIT", "Fahrenheit scale temperature",
208                 TemperatureDifference.ofSi(273.15 - 32 / 1.8), KELVIN);
209 
210         /**
211          * Define a new reference point for an absolute temperature.
212          * @param id the id
213          * @param name the name or explanation
214          * @param offset the offset w.r.t. offsetReference
215          * @param offsetReference the reference to which the offset is relative
216          */
217         public Reference(final String id, final String name, final TemperatureDifference offset,
218                 final Reference offsetReference)
219         {
220             super(id, name, offset, offsetReference);
221         }
222 
223         /**
224          * Define a new reference point for the absolute temperature, with an offset to 0 kelvin.
225          * @param id the id
226          * @param name the name or explanation
227          * @param offset the offset w.r.t. offsetReference
228          */
229         public Reference(final String id, final String name, final TemperatureDifference offset)
230         {
231             super(id, name, offset, KELVIN);
232         }
233 
234         /**
235          * Define a new reference point for the absolute temperature, with an offset to 0 kelvin.
236          * @param id the id
237          * @param name the name or explanation
238          * @param offset the offset of this scale relative to 0 kelvin
239          */
240         public static void add(final String id, final String name, final TemperatureDifference offset)
241         {
242             new Reference(id, name, offset);
243         }
244 
245         /**
246          * Get a reference point for the absolute temperature, based on its id. Return null when the id could not be found.
247          * @param id the id
248          * @return the Temperature.Reference object
249          */
250         public static Reference get(final String id)
251         {
252             return AbstractReference.get(Temperature.Reference.class, id);
253         }
254 
255         @Override
256         public Temperature instantiate(final TemperatureDifference temperatureDifference)
257         {
258             return new Temperature(temperatureDifference, this);
259         }
260     }
261 
262     /******************************************************************************************************/
263     /********************************************** UNIT CLASS ********************************************/
264     /******************************************************************************************************/
265 
266     /**
267      * Temperature.Unit encodes the units of relative and absolute temperature. Note that he reference is not initialized
268      * immediately for the units, since Reference needs working units to initialize itself. Therefore, references will be
269      * allocated when an reference is retrieved for the first time.
270      * <p>
271      * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
272      * See for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
273      * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
274      * @author Alexander Verbraeck
275      */
276     @SuppressWarnings("checkstyle:constantname")
277     public static class Unit extends AbstractUnit<Temperature.Unit, TemperatureDifference>
278     {
279         /** The dimensions of temperature: K. */
280         public static final SIUnit SI_UNIT = SIUnit.of("K");
281 
282         /** Kelvin. */
283         public static final Temperature.Unit K = new Temperature.Unit("K", "kelvin", 1.0, UnitSystem.SI_BASE);
284 
285         /** The SI or BASE unit. */
286         public static final Temperature.Unit SI = K.generateSiPrefixes(false, false);
287 
288         /** Degree Celsius. */
289         public static final Temperature.Unit degC =
290                 new Temperature.Unit("degC", "\u00B0C", "degree Celsius", new LinearScale(1.0), UnitSystem.SI_DERIVED);
291 
292         /** Degree Fahrenheit. */
293         public static final Temperature.Unit degF =
294                 new Temperature.Unit("degF", "\u00B0F", "degree Fahrenheit", new LinearScale(5.0 / 9.0), UnitSystem.OTHER);
295 
296         /** Degree Rankine. */
297         public static final Temperature.Unit degR =
298                 new Temperature.Unit("degR", "\u00B0R", "degree Rankine", new LinearScale(5.0 / 9.0), UnitSystem.OTHER);
299 
300         /** Degree Reaumur. */
301         public static final Temperature.Unit degRe =
302                 new Temperature.Unit("degRe", "\u00B0R\u00E9", "degree Reaumur", new LinearScale(4.0 / 5.0), UnitSystem.OTHER);
303 
304         /** the default reference for this unit (used for absolute quantities). */
305         private Reference reference;
306 
307         /** Guard to assign default references lazily, safely, exactly once. */
308         private static volatile boolean defaultReferencesAssigned = false;
309 
310         /**
311          * Create a new Temperature unit.
312          * @param id the id or main abbreviation of the unit
313          * @param name the full name of the unit
314          * @param scaleFactorToBaseUnit the scale factor of the unit to convert it TO the base (SI) unit
315          * @param unitSystem the unit system such as SI or IMPERIAL
316          */
317         public Unit(final String id, final String name, final double scaleFactorToBaseUnit, final UnitSystem unitSystem)
318         {
319             super(id, name, new LinearScale(scaleFactorToBaseUnit), unitSystem);
320         }
321 
322         /**
323          * Return a derived unit for this unit, with textual abbreviation(s) and a display abbreviation.
324          * @param textualAbbreviation the textual abbreviation of the unit, which doubles as the id
325          * @param displayAbbreviation the display abbreviation of the unit
326          * @param name the full name of the unit
327          * @param scale the scale to use to convert between this unit and the standard (e.g., SI, BASE) unit
328          * @param unitSystem unit system, e.g. SI or Imperial
329          */
330         public Unit(final String textualAbbreviation, final String displayAbbreviation, final String name, final Scale scale,
331                 final UnitSystem unitSystem)
332         {
333             super(textualAbbreviation, displayAbbreviation, name, scale, unitSystem);
334         }
335 
336         /**
337          * Ensure default references for all Temperature units are assigned once and only when needed. This avoids circular
338          * initialization between Temperature.Unit, Temperature.Reference, and TemperatureDifference. The method is deliberately
339          * minimal: it only assigns references to existing unit singletons and does not create new objects or resolve units.
340          */
341         private static void ensureDefaultReferencesAssigned()
342         {
343             if (defaultReferencesAssigned)
344             {
345                 return;
346             }
347             synchronized (Unit.class)
348             {
349                 if (!defaultReferencesAssigned)
350                 {
351                     // IMPORTANT:
352                     // - Assign only to already-constructed constants.
353                     // - Do NOT create new objects here.
354                     // - Do NOT call Units.resolve(...) or valueOf(...) here.
355                     // - Do NOT refer to TemperatureDifference.ZERO, etc., from here.
356 
357                     K.reference = Reference.KELVIN;
358                     degC.reference = Reference.CELSIUS;
359                     degF.reference = Reference.FAHRENHEIT;
360                     degR.reference = Reference.KELVIN; // Rankine uses Kelvin as default reference
361                     degRe.reference = Reference.CELSIUS; // Réaumur uses Celsius as default reference
362 
363                     defaultReferencesAssigned = true;
364                 }
365             }
366         }
367 
368         /**
369          * Return the default reference for this unit. References are assigned lazily and safely (once) to avoid circular class
370          * initialization issues.
371          * @return the reference for an absolute temperature
372          */
373         protected Reference getReference()
374         {
375             if (this.reference == null)
376             {
377                 // assign for all units exactly once, then return for this instance
378                 ensureDefaultReferencesAssigned();
379             }
380             return this.reference;
381         }
382 
383         @Override
384         public SIUnit siUnit()
385         {
386             return SI_UNIT;
387         }
388 
389         @Override
390         public Unit getBaseUnit()
391         {
392             return SI;
393         }
394 
395         @Override
396         public TemperatureDifference ofSi(final double si)
397         {
398             return TemperatureDifference.ofSi(si);
399         }
400 
401         @Override
402         public Unit deriveUnit(final String textualAbbreviation, final String displayAbbreviation, final String name,
403                 final double scaleFactor, final UnitSystem unitSystem)
404         {
405             if (getScale() instanceof LinearScale ls)
406             {
407                 return new Temperature.Unit(textualAbbreviation, displayAbbreviation, name,
408                         new LinearScale(ls.getScaleFactorToBaseUnit() * scaleFactor), unitSystem);
409             }
410             throw new UnitRuntimeException("Only possible to derive a unit from a unit with a linear scale");
411         }
412 
413     }
414 
415 }