PISM, A Parallel Ice Sheet Model  stable v2.0.4 committed by Constantine Khrulev on 2022-05-25 12:02:27 -0800
Units.cc
Go to the documentation of this file.
1 /* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2020 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 "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 
28 #include "pism_utilities.hh"
29 
30 namespace pism {
31 
32 namespace units {
33 
34 struct System::Impl {
35  Impl(const std::string &path) {
36  ut_system *tmp;
37 
38  ut_set_error_message_handler(ut_ignore);
39 
40  if (not path.empty()) {
41  tmp = ut_read_xml(path.c_str());
42  } else {
43  tmp = ut_read_xml(NULL);
44  }
45 
46  if (tmp == NULL) {
47  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "ut_read_xml(%s) failed", path.c_str());
48  }
49  ut_set_error_message_handler(ut_write_to_stderr);
50 
51  system = tmp;
52  }
53  ~Impl() {
54  ut_free_system(system);
55  }
56  ut_system *system;
57 };
58 
59 /** Initialize the unit system by reading from an XML unit
60  * definition file.
61  */
62 System::System(const std::string &path) {
63  m_impl.reset(new Impl(path));
64 }
65 
66 //! \brief Convert a quantity from unit1 to unit2.
67 /*!
68  * Example: convert(1, "m year-1", "m second-1").
69  *
70  * Please avoid using in computationally-intensive code.
71  */
72 double convert(System::Ptr system, double input,
73  const std::string &spec1, const std::string &spec2) {
74  Converter c(Unit(system, spec1), Unit(system, spec2));
75 
76  return c(input);
77 }
78 
79 struct Unit::Impl {
80  Impl(System::Ptr sys, const std::string &spec)
81  : system(sys), unit_string(spec) {
82  unit = ut_parse(sys->m_impl->system, spec.c_str(), UT_ASCII);
83 
84  if (unit == NULL) {
86  "unit specification '%s' is unknown or invalid",
87  spec.c_str());
88  }
89  }
90  Impl(const Unit::Impl &other) {
91 
92  unit = ut_clone(other.unit);
93  if (unit == NULL) {
94  throw RuntimeError(PISM_ERROR_LOCATION, "ut_clone failed");
95  }
96 
97  system = other.system;
98  unit_string = other.unit_string;
99  }
100  ~Impl() {
101  reset();
102  }
103  void reset() {
104  ut_free(unit);
105  unit_string = "";
106  }
107 
109  std::string unit_string;
110  ut_unit *unit;
111 };
112 
113 Unit::Unit(System::Ptr system, const std::string &spec) {
114  m_impl.reset(new Impl(system, spec));
115 }
116 
117 Unit::Unit(const Unit &other) {
118  m_impl.reset(new Impl(*other.m_impl));
119 }
120 
121 Unit& Unit::operator=(const Unit& other) {
122  if (this == &other) {
123  return *this;
124  }
125 
126  reset();
127 
128  m_impl->system = other.m_impl->system;
129  m_impl->unit_string = other.m_impl->unit_string;
130 
131  m_impl->unit = ut_clone(other.m_impl->unit);
132  if (m_impl->unit == NULL) {
133  throw RuntimeError(PISM_ERROR_LOCATION, "ut_clone failed");
134  }
135 
136  return *this;
137 }
138 
139 bool Unit::is_convertible(const Unit &other) const {
140  return ut_are_convertible(m_impl->unit, other.m_impl->unit) != 0;
141 }
142 
143 std::string Unit::format() const {
144  return m_impl->unit_string;
145 }
146 
147 void Unit::reset() {
148  m_impl->reset();
149 }
150 
152  return m_impl->system;
153 }
154 
155 DateTime Unit::date(double T, const std::string &calendar) const {
156  DateTime result;
157 
158  int errcode = utCalendar2_cal(T, m_impl->unit,
159  &result.year, &result.month, &result.day,
160  &result.hour, &result.minute, &result.second,
161  calendar.c_str());
162  if (errcode != 0) {
164  "cannot convert time %f to a date in calendar %s",
165  T, calendar.c_str());
166  }
167  return result;
168 }
169 
170 double Unit::time(const DateTime &d, const std::string &calendar) const {
171  double result;
172 
173  int errcode = utInvCalendar2_cal(d.year, d.month, d.day,
174  d.hour, d.minute, d.second,
175  m_impl->unit,
176  &result,
177  calendar.c_str());
178  if (errcode != 0) {
180  "cannot convert date and time %d-%d-%d %d:%d:%f to time in calendar %s",
181  d.year, d.month, d.day,
182  d.hour, d.minute, d.second,
183  calendar.c_str());
184  }
185  return result;
186 }
187 
189  Impl() {
190  converter = cv_get_trivial();
191  }
192  Impl(System::Ptr sys, const std::string &spec1, const std::string &spec2) {
193 
194  Unit u1(sys, spec1), u2(sys, spec2);
195 
196  if (not u1.is_convertible(u2)) {
198  "cannot convert '%s' to '%s'",
199  spec1.c_str(), spec2.c_str());
200  }
201 
202  converter = ut_get_converter(u1.m_impl->unit, u2.m_impl->unit);
203  if (not converter) {
205  "cannot create a converter from %s to %s",
206  spec1.c_str(), spec2.c_str());
207  }
208 
209  }
210  Impl(const Unit &u1, const Unit &u2) {
211  if (not u1.is_convertible(u2)) {
212  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "cannot convert '%s' to '%s'",
213  u1.format().c_str(), u2.format().c_str());
214  }
215 
216  converter = ut_get_converter(u1.m_impl->unit, u2.m_impl->unit);
217  if (not converter) {
219  "failed to create a converter from '%s' to '%s'",
220  u1.format().c_str(), u2.format().c_str());
221  }
222 
223  }
224  ~Impl() {
225  cv_free(converter);
226  converter = NULL;
227  }
228  cv_converter *converter;
229 };
230 
232  m_impl.reset(new Impl());
233 }
234 
236  const std::string &spec1, const std::string &spec2) {
237  m_impl.reset(new Impl(sys, spec1, spec2));
238 }
239 
240 Converter::Converter(const Unit &u1, const Unit &u2) {
241  m_impl.reset(new Impl(u1, u2));
242 }
243 
244 bool are_convertible(const Unit &u1, const Unit &u2) {
245  return u1.is_convertible(u2);
246 }
247 
248 double Converter::operator()(double input) const {
249  return cv_convert_double(m_impl->converter, input);
250 }
251 
252 void Converter::convert_doubles(double *data, size_t length) const {
253  cv_convert_doubles(m_impl->converter, data, length, data);
254 }
255 
256 } // end of namespace units
257 
258 } // 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:116
double operator()(double input) const
Definition: Units.cc:248
void convert_doubles(double *data, size_t length) const
Definition: Units.cc:252
System(const std::string &path="")
Definition: Units.cc:62
std::shared_ptr< Impl > m_impl
Definition: Units.hh:51
std::shared_ptr< System > Ptr
Definition: Units.hh:47
DateTime date(double T, const std::string &calendar) const
Definition: Units.cc:155
std::string format() const
Definition: Units.cc:143
Unit & operator=(const Unit &other)
Definition: Units.cc:121
System::Ptr system() const
Definition: Units.cc:151
double time(const DateTime &d, const std::string &calendar) const
Definition: Units.cc:170
Unit(System::Ptr system, const std::string &spec)
Definition: Units.cc:113
std::shared_ptr< Impl > m_impl
Definition: Units.hh:84
bool is_convertible(const Unit &other) const
Definition: Units.cc:139
void reset()
Definition: Units.cc:147
#define PISM_ERROR_LOCATION
bool are_convertible(const Unit &u1, const Unit &u2)
Definition: Units.cc:244
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:72
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
Definition: Time.cc:145
cv_converter * converter
Definition: Units.cc:228
Impl(const Unit &u1, const Unit &u2)
Definition: Units.cc:210
Impl(System::Ptr sys, const std::string &spec1, const std::string &spec2)
Definition: Units.cc:192
Impl(const std::string &path)
Definition: Units.cc:35
ut_system * system
Definition: Units.cc:56
Impl(System::Ptr sys, const std::string &spec)
Definition: Units.cc:80
Impl(const Unit::Impl &other)
Definition: Units.cc:90
std::string unit_string
Definition: Units.cc:109
System::Ptr system
Definition: Units.cc:108
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)