Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Units.cc
Go to the documentation of this file.
1/* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2020, 2023 PISM Authors
2 *
3 * This file is part of PISM.
4 *
5 * PISM is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option) any later
8 * version.
9 *
10 * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with PISM; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include "pism/util/Units.hh"
21
22#include <udunits2.h>
23
24#include "pism/external/calcalcs/utCalendar2_cal.h"
25
26#include "pism/util/error_handling.hh"
27
28namespace pism {
29
30namespace units {
31
33 Impl(const std::string &path) {
34 ut_system *tmp;
35
36 ut_set_error_message_handler(ut_ignore);
37
38 if (not path.empty()) {
39 tmp = ut_read_xml(path.c_str());
40 } else {
41 tmp = ut_read_xml(NULL);
42 }
43
44 if (tmp == NULL) {
45 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "ut_read_xml(%s) failed", path.c_str());
46 }
47 ut_set_error_message_handler(ut_write_to_stderr);
48
49 system = tmp;
50 }
52 ut_free_system(system);
53 }
54 ut_system *system;
55};
56
57/** Initialize the unit system by reading from an XML unit
58 * definition file.
59 */
60System::System(const std::string &path) {
61 m_impl.reset(new Impl(path));
62}
63
64//! \brief Convert a quantity from unit1 to unit2.
65/*!
66 * Example: convert(1, "m year-1", "m second-1").
67 *
68 * Please avoid using in computationally-intensive code.
69 */
70double convert(System::Ptr system, double input,
71 const std::string &spec1, const std::string &spec2) {
72 Converter c(Unit(system, spec1), Unit(system, spec2));
73
74 return c(input);
75}
76
77struct Unit::Impl {
78 Impl(System::Ptr sys, const std::string &spec)
79 : system(sys), unit_string(spec) {
80 unit = ut_parse(sys->m_impl->system, spec.c_str(), UT_ASCII);
81
82 if (unit == NULL) {
84 "unit specification '%s' is unknown or invalid",
85 spec.c_str());
86 }
87 }
88 Impl(const Unit::Impl &other) {
89
90 unit = ut_clone(other.unit);
91 if (unit == NULL) {
92 throw RuntimeError(PISM_ERROR_LOCATION, "ut_clone failed");
93 }
94
95 system = other.system;
97 }
99 reset();
100 }
101 void reset() {
102 ut_free(unit);
103 unit_string = "";
104 }
105
107 std::string unit_string;
108 ut_unit *unit;
109};
110
111Unit::Unit(System::Ptr system, const std::string &spec) {
112 m_impl.reset(new Impl(system, spec));
113}
114
115Unit::Unit(const Unit &other) {
116 m_impl.reset(new Impl(*other.m_impl));
117}
118
119Unit& Unit::operator=(const Unit& other) {
120 if (this == &other) {
121 return *this;
122 }
123
124 reset();
125
126 m_impl->system = other.m_impl->system;
127 m_impl->unit_string = other.m_impl->unit_string;
128
129 m_impl->unit = ut_clone(other.m_impl->unit);
130 if (m_impl->unit == NULL) {
131 throw RuntimeError(PISM_ERROR_LOCATION, "ut_clone failed");
132 }
133
134 return *this;
135}
136
137bool Unit::is_convertible(const Unit &other) const {
138 return ut_are_convertible(m_impl->unit, other.m_impl->unit) != 0;
139}
140
141std::string Unit::format() const {
142 return m_impl->unit_string;
143}
144
146 m_impl->reset();
147}
148
150 return m_impl->system;
151}
152
153DateTime Unit::date(double T, const std::string &calendar) const {
154 DateTime result;
155
156 int errcode = utCalendar2_cal(T, m_impl->unit,
157 &result.year, &result.month, &result.day,
158 &result.hour, &result.minute, &result.second,
159 calendar.c_str());
160 if (errcode != 0) {
162 "cannot convert time %f to a date in calendar %s",
163 T, calendar.c_str());
164 }
165 return result;
166}
167
168double Unit::time(const DateTime &d, const std::string &calendar) const {
169 double result;
170
171 int errcode = utInvCalendar2_cal(d.year, d.month, d.day,
172 d.hour, d.minute, d.second,
173 m_impl->unit,
174 &result,
175 calendar.c_str());
176 if (errcode != 0) {
178 "cannot convert date and time %d-%d-%d %d:%d:%f to time in calendar %s",
179 d.year, d.month, d.day,
180 d.hour, d.minute, d.second,
181 calendar.c_str());
182 }
183 return result;
184}
185
188 converter = cv_get_trivial();
189 }
190 Impl(System::Ptr sys, const std::string &spec1, const std::string &spec2) {
191
192 Unit u1(sys, spec1), u2(sys, spec2);
193
194 if (not u1.is_convertible(u2)) {
196 "cannot convert '%s' to '%s'",
197 spec1.c_str(), spec2.c_str());
198 }
199
200 converter = ut_get_converter(u1.m_impl->unit, u2.m_impl->unit);
201 if (not converter) {
203 "cannot create a converter from %s to %s",
204 spec1.c_str(), spec2.c_str());
205 }
206
207 }
208 Impl(const Unit &u1, const Unit &u2) {
209 if (not u1.is_convertible(u2)) {
210 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "cannot convert '%s' to '%s'",
211 u1.format().c_str(), u2.format().c_str());
212 }
213
214 converter = ut_get_converter(u1.m_impl->unit, u2.m_impl->unit);
215 if (not converter) {
217 "failed to create a converter from '%s' to '%s'",
218 u1.format().c_str(), u2.format().c_str());
219 }
220
221 }
223 cv_free(converter);
224 converter = NULL;
225 }
226 cv_converter *converter;
227};
228
230 m_impl.reset(new Impl());
231}
232
234 const std::string &spec1, const std::string &spec2) {
235 m_impl.reset(new Impl(sys, spec1, spec2));
236}
237
238Converter::Converter(const Unit &u1, const Unit &u2) {
239 m_impl.reset(new Impl(u1, u2));
240}
241
242bool are_convertible(const Unit &u1, const Unit &u2) {
243 return u1.is_convertible(u2);
244}
245
246double Converter::operator()(double input) const {
247 return cv_convert_double(m_impl->converter, input);
248}
249
250void Converter::convert_doubles(double *data, size_t length) const {
251 cv_convert_doubles(m_impl->converter, data, length, data);
252}
253
254} // end of namespace units
255
256} // end of namespace pism
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
std::shared_ptr< Impl > m_impl
Definition Units.hh:117
double operator()(double input) const
Definition Units.cc:246
void convert_doubles(double *data, size_t length) const
Definition Units.cc:250
System(const std::string &path="")
Definition Units.cc:60
std::shared_ptr< Impl > m_impl
Definition Units.hh:52
std::shared_ptr< System > Ptr
Definition Units.hh:47
DateTime date(double T, const std::string &calendar) const
Definition Units.cc:153
std::string format() const
Definition Units.cc:141
Unit & operator=(const Unit &other)
Definition Units.cc:119
System::Ptr system() const
Definition Units.cc:149
double time(const DateTime &d, const std::string &calendar) const
Definition Units.cc:168
Unit(System::Ptr system, const std::string &spec)
Definition Units.cc:111
std::shared_ptr< Impl > m_impl
Definition Units.hh:85
bool is_convertible(const Unit &other) const
Definition Units.cc:137
#define PISM_ERROR_LOCATION
bool are_convertible(const Unit &u1, const Unit &u2)
Definition Units.cc:242
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
Definition Units.cc:70
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
Definition Time.cc:146
cv_converter * converter
Definition Units.cc:226
Impl(const Unit &u1, const Unit &u2)
Definition Units.cc:208
Impl(System::Ptr sys, const std::string &spec1, const std::string &spec2)
Definition Units.cc:190
Impl(const std::string &path)
Definition Units.cc:33
Impl(System::Ptr sys, const std::string &spec)
Definition Units.cc:78
Impl(const Unit::Impl &other)
Definition Units.cc:88
std::string unit_string
Definition Units.cc:107
System::Ptr system
Definition Units.cc:106
int utCalendar2_cal(double val, ut_unit *dataunits, int *year, int *month, int *day, int *hour, int *minute, double *second, const char *calendar_name)
int utInvCalendar2_cal(int year, int month, int day, int hour, int minute, double second, ut_unit *user_unit, double *value, const char *calendar_name)