PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
Units.cc
Go to the documentation of this file.
1/* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2020, 2023, 2025 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::string() const {
142 return m_impl->unit_string;
143}
144
145Unit::operator std::string() const {
146 return m_impl->unit_string;
147}
148
149
151 m_impl->reset();
152}
153
155 return m_impl->system;
156}
157
158DateTime Unit::date(double T, const std::string &calendar) const {
159 DateTime result;
160
161 int errcode = utCalendar2_cal(T, m_impl->unit,
162 &result.year, &result.month, &result.day,
163 &result.hour, &result.minute, &result.second,
164 calendar.c_str());
165 if (errcode != 0) {
167 "cannot convert time %f to a date in calendar %s",
168 T, calendar.c_str());
169 }
170 return result;
171}
172
173double Unit::time(const DateTime &d, const std::string &calendar) const {
174 double result;
175
176 int errcode = utInvCalendar2_cal(d.year, d.month, d.day,
177 d.hour, d.minute, d.second,
178 m_impl->unit,
179 &result,
180 calendar.c_str());
181 if (errcode != 0) {
183 "cannot convert date and time %d-%d-%d %d:%d:%f to time in calendar %s",
184 d.year, d.month, d.day,
185 d.hour, d.minute, d.second,
186 calendar.c_str());
187 }
188 return result;
189}
190
193 converter = cv_get_trivial();
194 }
195 Impl(System::Ptr sys, const std::string &spec1, const std::string &spec2) {
196
197 Unit u1(sys, spec1), u2(sys, spec2);
198
199 if (not u1.is_convertible(u2)) {
201 "cannot convert '%s' to '%s'",
202 spec1.c_str(), spec2.c_str());
203 }
204
205 converter = ut_get_converter(u1.m_impl->unit, u2.m_impl->unit);
206 if (converter == nullptr) {
208 "cannot create a converter from %s to %s",
209 spec1.c_str(), spec2.c_str());
210 }
211
212 }
213 Impl(const Unit &u1, const Unit &u2) {
214 if (not u1.is_convertible(u2)) {
215 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "cannot convert '%s' to '%s'",
216 u1.string().c_str(), u2.string().c_str());
217 }
218
219 converter = ut_get_converter(u1.m_impl->unit, u2.m_impl->unit);
220 if (converter == nullptr) {
222 "failed to create a converter from '%s' to '%s'",
223 u1.string().c_str(), u2.string().c_str());
224 }
225
226 }
228 cv_free(converter);
229 converter = NULL;
230 }
231 cv_converter *converter;
232};
233
235 m_impl.reset(new Impl());
236}
237
239 const std::string &spec1, const std::string &spec2) {
240 m_impl.reset(new Impl(sys, spec1, spec2));
241}
242
243Converter::Converter(const Unit &u1, const Unit &u2) {
244 m_impl.reset(new Impl(u1, u2));
245}
246
247bool are_convertible(const Unit &u1, const Unit &u2) {
248 return u1.is_convertible(u2);
249}
250
251double Converter::operator()(double input) const {
252 return cv_convert_double(m_impl->converter, input);
253}
254
255void Converter::convert_doubles(double *data, size_t length) const {
256 cv_convert_doubles(m_impl->converter, data, length, data);
257}
258
259} // end of namespace units
260
261} // 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:119
double operator()(double input) const
Definition Units.cc:251
void convert_doubles(double *data, size_t length) const
Definition Units.cc:255
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:158
Unit & operator=(const Unit &other)
Definition Units.cc:119
System::Ptr system() const
Definition Units.cc:154
double time(const DateTime &d, const std::string &calendar) const
Definition Units.cc:173
std::string string() const
Definition Units.cc:141
Unit(System::Ptr system, const std::string &spec)
Definition Units.cc:111
std::shared_ptr< Impl > m_impl
Definition Units.hh:87
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:247
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:231
Impl(const Unit &u1, const Unit &u2)
Definition Units.cc:213
Impl(System::Ptr sys, const std::string &spec1, const std::string &spec2)
Definition Units.cc:195
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)