View Javadoc
1   package org.djunits.quantity;
2   
3   import org.djunits.quantity.def.AbsoluteQuantity;
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 AbsoluteQuantity<Temperature, TemperatureDifference, Temperature.Unit, 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 value 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 value, final Temperature.Unit unit, final Reference reference)
35      {
36          super(new TemperatureDifference(value, unit), reference);
37      }
38  
39      /**
40       * Instantiate a Temperature quantity with a unit and the KELVIN reference point.
41       * @param value 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 value, final Temperature.Unit unit)
45      {
46          this(value, unit, unit.getReference());
47      }
48  
49      /**
50       * Instantiate a Temperature quantity with a unit, expressed as a String, and a reference point.
51       * @param value 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 value, final String abbreviation, final Reference reference)
56      {
57          this(value, 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 value 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 value, final String abbreviation)
66      {
67          this(value, 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     @Override
118     public SIUnit siUnit()
119     {
120         return Temperature.Unit.SI_UNIT;
121     }
122 
123     /**
124      * Returns a Temperature representation of a textual representation of a value with a unit. The String representation that
125      * can be parsed is the double value in the unit, followed by a localized or English abbreviation of the unit. Spaces are
126      * allowed, but not required, between the value and the unit.
127      * @param text the textual representation to parse into a Temperature
128      * @param reference the reference point of this absolute temperature
129      * @return the Scalar representation of the value in its unit
130      * @throws IllegalArgumentException when the text cannot be parsed
131      * @throws NullPointerException when the text argument is null
132      */
133     public static Temperature valueOf(final String text, final Reference reference)
134     {
135         return new Temperature(Quantity.valueOf(text, TemperatureDifference.ZERO), reference);
136     }
137 
138     /**
139      * Returns a Temperature representation of a textual representation of a value with a unit, and the KELVIN reference. The
140      * String representation that can be parsed is the double value in the unit, followed by a localized or English abbreviation
141      * of the unit. Spaces are allowed, but not required, between the value and the unit.
142      * @param text the textual representation to parse into a Temperature
143      * @return the Scalar representation of the value in its unit
144      * @throws IllegalArgumentException when the text cannot be parsed
145      * @throws NullPointerException when the text argument is null
146      */
147     public static Temperature valueOf(final String text)
148     {
149         return new Temperature(Quantity.valueOf(text, TemperatureDifference.ZERO), Reference.KELVIN);
150     }
151 
152     /**
153      * Returns a Temperature based on a value and the textual representation of the unit, which can be localized.
154      * @param value the value to use
155      * @param unitString the textual representation of the unit
156      * @param reference the reference point of this absolute temperature
157      * @return the Scalar representation of the value in its unit
158      * @throws IllegalArgumentException when the unit cannot be parsed or is incorrect
159      * @throws NullPointerException when the unitString argument is null
160      */
161     public static Temperature of(final double value, final String unitString, final Reference reference)
162     {
163         return new Temperature(Quantity.of(value, unitString, TemperatureDifference.ZERO), reference);
164     }
165 
166     /**
167      * Returns a Temperature based on a value and the textual representation of the unit, which can be localized. Use the KELVIN
168      * reference.
169      * @param value the value to use
170      * @param unitString the textual representation of the unit
171      * @return the Scalar representation of the value in its unit
172      * @throws IllegalArgumentException when the unit cannot be parsed or is incorrect
173      * @throws NullPointerException when the unitString argument is null
174      */
175     public static Temperature of(final double value, final String unitString)
176     {
177         return new Temperature(Quantity.of(value, unitString, TemperatureDifference.ZERO), Reference.KELVIN);
178     }
179 
180     @Override
181     public TemperatureDifference subtract(final Temperature other)
182     {
183         var otherRef = other.relativeTo(getReference());
184         return TemperatureDifference.ofSi(si() - otherRef.si()).setDisplayUnit(getDisplayUnit());
185     }
186 
187     @Override
188     public Temperature add(final TemperatureDifference other)
189     {
190         return new Temperature(TemperatureDifference.ofSi(si() + other.si()).setDisplayUnit(getDisplayUnit()), getReference());
191     }
192 
193     @Override
194     public Temperature subtract(final TemperatureDifference other)
195     {
196         return new Temperature(TemperatureDifference.ofSi(si() - other.si()).setDisplayUnit(getDisplayUnit()), getReference());
197     }
198 
199     /**
200      * The reference class to define a reference point for the absolute temperature.
201      */
202     public static final class Reference extends AbstractReference<Reference, TemperatureDifference>
203     {
204         /** Kelvin. */
205         public static final Reference KELVIN =
206                 new Reference("KELVIN", "Kelvin scale temperature", TemperatureDifference.ZERO, null);
207 
208         /** Celsius. */
209         public static final Reference CELSIUS =
210                 new Reference("CELSIUS", "Celsius scale temperature", TemperatureDifference.ofSi(273.15), KELVIN);
211 
212         /** Fahrenheit. */
213         public static final Reference FAHRENHEIT = new Reference("FAHRENHEIT", "Fahrenheit scale temperature",
214                 TemperatureDifference.ofSi(273.15 - 32 / 1.8), KELVIN);
215 
216         /**
217          * Define a new reference point for an absolute temperature.
218          * @param id the id
219          * @param name the name or explanation
220          * @param offset the offset w.r.t. offsetReference
221          * @param offsetReference the reference to which the offset is relative
222          */
223         public Reference(final String id, final String name, final TemperatureDifference offset,
224                 final Reference offsetReference)
225         {
226             super(id, name, offset, offsetReference);
227         }
228 
229         /**
230          * Define a new reference point for the absolute temperature, with an offset to 0 kelvin.
231          * @param id the id
232          * @param name the name or explanation
233          * @param offset the offset w.r.t. offsetReference
234          */
235         public Reference(final String id, final String name, final TemperatureDifference offset)
236         {
237             super(id, name, offset, KELVIN);
238         }
239 
240         /**
241          * Define a new reference point for the absolute temperature, with an offset to 0 kelvin.
242          * @param id the id
243          * @param name the name or explanation
244          * @param offset the offset of this scale relative to 0 kelvin
245          */
246         public static void add(final String id, final String name, final TemperatureDifference offset)
247         {
248             new Reference(id, name, offset);
249         }
250 
251         /**
252          * Get a reference point for the absolute temperature, based on its id. Return null when the id could not be found.
253          * @param id the id
254          * @return the Temperature.Reference object
255          */
256         public static Reference get(final String id)
257         {
258             return AbstractReference.get(Temperature.Reference.class, id);
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 }