PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
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 
28 namespace pism {
29 
30 namespace units {
31 
32 struct System::Impl {
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  }
51  ~Impl() {
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  */
60 System::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  */
70 double 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 
77 struct 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;
96  unit_string = other.unit_string;
97  }
98  ~Impl() {
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 
111 Unit::Unit(System::Ptr system, const std::string &spec) {
112  m_impl.reset(new Impl(system, spec));
113 }
114 
115 Unit::Unit(const Unit &other) {
116  m_impl.reset(new Impl(*other.m_impl));
117 }
118 
119 Unit& 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 
137 bool Unit::is_convertible(const Unit &other) const {
138  return ut_are_convertible(m_impl->unit, other.m_impl->unit) != 0;
139 }
140 
141 std::string Unit::format() const {
142  return m_impl->unit_string;
143 }
144 
145 void Unit::reset() {
146  m_impl->reset();
147 }
148 
150  return m_impl->system;
151 }
152 
153 DateTime 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 
168 double 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 
187  Impl() {
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  }
222  ~Impl() {
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 
238 Converter::Converter(const Unit &u1, const Unit &u2) {
239  m_impl.reset(new Impl(u1, u2));
240 }
241 
242 bool are_convertible(const Unit &u1, const Unit &u2) {
243  return u1.is_convertible(u2);
244 }
245 
246 double Converter::operator()(double input) const {
247  return cv_convert_double(m_impl->converter, input);
248 }
249 
250 void 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:116
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:51
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:84
bool is_convertible(const Unit &other) const
Definition: Units.cc:137
void reset()
Definition: Units.cc:145
#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
ut_system * system
Definition: Units.cc:54
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)