mbsim  4.0.0
MBSim Kernel
piecewise_polynom_function_impl.h
1/* Copyright (C) 2004-2009 MBSim Development Team
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 *
17 * Contact: martin.o.foerg@googlemail.com
18 */
19
20 template<typename Ret, typename Arg>
21 void PiecewisePolynomFunction<Ret(Arg)>::calculateSplinePeriodic() {
22 double hi, hii;
23 int N = x.size();
24 if(nrm2(y.row(0)-y.row(y.rows()-1))>epsroot) this->throwError("(PiecewisePolynomFunction::calculateSplinePeriodic): f(0)= "+fmatvec::toString(y.row(0))+"!="+fmatvec::toString(y.row(y.rows()-1))+" =f(end)");
25 fmatvec::SqrMat C(N-1,fmatvec::INIT,0.0);
26 fmatvec::Mat rs(N-1,y.cols(),fmatvec::INIT,0.0);
27
28 // Matrix C and vector rs C*c=rs
29 for(int i=0; i<N-3;i++) {
30 hi = x(i+1) - x(i);
31 hii = x(i+2)-x(i+1);
32 C(i,i) = hi;
33 C(i,i+1) = 2*(hi+hii);
34 C(i,i+2) = hii;
35 rs.set(i, 3.*((y.row(i+2)-y.row(i+1))/hii - (y.row(i+1)-y.row(i))/hi));
36 }
37
38 // last but one row
39 hi = x(N-2)-x(N-3);
40 hii = x(N-1)-x(N-2);
41 C(N-3,N-3) = hi;
42 C(N-3,N-2)= 2*(hi+hii);
43 C(N-3,0)= hii;
44 rs.set(N-3, 3.*((y.row(N-1)-y.row(N-2))/hii - (y.row(N-2)-y.row(N-3))/hi));
45
46 // last row
47 double h1 = x(1)-x(0);
48 double hN_1 = x(N-1)-x(N-2);
49 C(N-2,0) = 2*(h1+hN_1);
50 C(N-2,1) = h1;
51 C(N-2,N-2)= hN_1;
52 rs.set(N-2, 3.*((y.row(1)-y.row(0))/h1 - (y.row(0)-y.row(N-2))/hN_1));
53
54 // solve C*c = rs -> TODO BETTER: RANK-1-MODIFICATION FOR LINEAR EFFORT (Simeon - Numerik 1)
55 fmatvec::Mat c = slvLU(C,rs);
56 fmatvec::Mat ctmp(N,y.cols());
57 ctmp.set(N-1, c.row(0)); // CN = c1
58 ctmp.set(fmatvec::RangeV(0,N-2),fmatvec::RangeV(0,y.cols()-1), c);
59
60 // vector ordering of the further coefficients
61 fmatvec::Mat d(N-1,y.cols(),fmatvec::INIT,0.0);
62 fmatvec::Mat b(N-1,y.cols(),fmatvec::INIT,0.0);
63 fmatvec::Mat a(N-1,y.cols(),fmatvec::INIT,0.0);
64
65 for(int i=0; i<N-1; i++) {
66 hi = x(i+1)-x(i);
67 a.set(i, y.row(i));
68 d.set(i, (ctmp.row(i+1) - ctmp.row(i) ) / 3. / hi);
69 b.set(i, (y.row(i+1)-y.row(i)) / hi - (ctmp.row(i+1) + 2.*ctmp.row(i) ) / 3. * hi);
70 }
71
72 breaks.resize(N);
73 breaks = x;
74 coefs.clear();
75 coefs.push_back(d);
76 coefs.push_back(c);
77 coefs.push_back(b);
78 coefs.push_back(a);
79 }
80
81 template<typename Ret, typename Arg>
82 void PiecewisePolynomFunction<Ret(Arg)>::calculateSplineNatural() {
83 // first row
84 int i=0;
85 int N = x.size();
86 if(N<4) this->throwError("(PiecewisePolynomFunction::calculateSplineNatural): number of datapoints does not match, must be greater 3");
87 fmatvec::SqrMat C(N-2,fmatvec::INIT,0.0);
88 fmatvec::Mat rs(N-2,y.cols(),fmatvec::INIT,0.0);
89 double hi = x(i+1)-x(i);
90 double hii = x(i+2)-x(i+1);
91 C(i,i) = 2*hi+2*hii;
92 C(i,i+1) = hii;
93 rs.set(i, 3.*(y.row(i+2)-y.row(i+1))/hii - 3.*(y.row(i+1)-y.row(i))/hi);
94
95 // last row
96 i = (N-3);
97 hi = x(i+1)-x(i);
98 hii = x(i+2)-x(i+1);
99 C(i,i-1) = hi;
100 C(i,i) = 2*hii + 2*hi;
101 rs.set(i, 3.*(y.row(i+2)-y.row(i+1))/hii - 3.*(y.row(i+1)-y.row(i))/hi);
102
103 for(i=1;i<N-3;i++) {
104 hi = x(i+1)-x(i);
105 hii = x(i+2)-x(i+1);
106 C(i,i-1) = hi;
107 C(i,i) = 2*(hi+hii);
108 C(i,i+1) = hii;
109 rs.set(i, 3.*(y.row(i+2)-y.row(i+1))/hii - 3.*(y.row(i+1)-y.row(i))/hi);
110 }
111
112 // solve C*c = rs with C tridiagonal
113 fmatvec::Mat C_rs(N-2,N-1+y.cols(),fmatvec::INIT,0.0);
114 C_rs.set(fmatvec::RangeV(0,N-3),fmatvec::RangeV(0,N-3), C); // C_rs=[C rs] for Gauss in matrix
115 C_rs.set(fmatvec::RangeV(0,N-3),fmatvec::RangeV(N-2,N-2+y.cols()-1), rs);
116 for(i=1; i<N-2; i++) C_rs.set(i, C_rs.row(i) - C_rs.row(i-1)*C_rs(i,i-1)/C_rs(i-1,i-1)); // C_rs -> upper triangular matrix C1 -> C1 rs1
117 fmatvec::Mat rs1 = C_rs(fmatvec::RangeV(0,N-3),fmatvec::RangeV(N-2,N-2+y.cols()-1));
118 fmatvec::Mat C1 = C_rs(fmatvec::RangeV(0,N-3),fmatvec::RangeV(0,N-3));
119 fmatvec::Mat c(N-2,y.cols(),fmatvec::INIT,0.0);
120 for(i=N-3;i>=0 ;i--) { // backward substitution
121 fmatvec::RowVecV sum_ciCi(y.cols(),fmatvec::NONINIT);
122 sum_ciCi.init(0.);
123 for(int ii=i+1; ii<=N-3; ii++) sum_ciCi = sum_ciCi + C1(i,ii)*c.row(ii);
124 c.set(i, (rs1.row(i) - sum_ciCi)/C1(i,i));
125 }
126 fmatvec::Mat ctmp(N,y.cols(),fmatvec::INIT,0.0);
127 ctmp.set(fmatvec::RangeV(1,N-2),fmatvec::RangeV(0,y.cols()-1), c); // c1=cN=0 natural splines c=[ 0; c; 0]
128
129 // vector ordering of the further coefficients
130 fmatvec::Mat d(N-1,y.cols(),fmatvec::INIT,0.0);
131 fmatvec::Mat b(N-1,y.cols(),fmatvec::INIT,0.0);
132 fmatvec::Mat a(N-1,y.cols(),fmatvec::INIT,0.0);
133
134 for(i=0; i<N-1; i++) {
135 hi = x(i+1)-x(i);
136 a.set(i, y.row(i));
137 d.set(i, (ctmp.row(i+1) - ctmp.row(i) ) / 3. / hi);
138 b.set(i, (y.row(i+1)-y.row(i)) / hi - (ctmp.row(i+1) + 2.*ctmp.row(i) ) / 3. * hi);
139 }
140
141 breaks.resize(N);
142 breaks = x;
143 coefs.clear();
144 coefs.push_back(d);
145 coefs.push_back(ctmp(fmatvec::RangeV(0,N-2),fmatvec::RangeV(0,y.cols()-1)));
146 coefs.push_back(b);
147 coefs.push_back(a);
148 }
149
150 template<typename Ret, typename Arg>
151 void PiecewisePolynomFunction<Ret(Arg)>::calculatePLinear() {
152 int N = x.size(); // number of supporting points
153
154 breaks.resize(N);
155 breaks = x;
156
157 fmatvec::Mat m(N-1,y.cols(),fmatvec::INIT,0.0);
158 fmatvec::Mat a(N-1,y.cols(),fmatvec::INIT,0.0);
159 for(int i=1;i<N;i++) {
160 m.set(i-1, (y.row(i)-y.row(i-1))/(x(i)-x(i-1))); // slope
161 a.set(i-1, y.row(i-1));
162 }
163 coefs.clear();
164 coefs.push_back(m);
165 coefs.push_back(a);
166 }
167
168 template<typename Ret, typename Arg>
169 Ret PiecewisePolynomFunction<Ret(Arg)>::ZerothDerivative::operator()(const double& x) {
170 if(parent->extrapolationMethod==error) {
171 if(x-1e-13>(parent->breaks)(parent->nPoly))
172 throw std::runtime_error("(PiecewisePolynomFunction::operator()): x out of range! x= "+fmatvec::toString(x)+", upper bound= "+fmatvec::toString((parent->breaks)(parent->nPoly)));
173 if(x+1e-13<(parent->breaks)(0))
174 throw std::runtime_error("(PiecewisePolynomFunction::operator()): x out of range! x= "+fmatvec::toString(x)+", lower bound= "+fmatvec::toString((parent->breaks)(0)));
175 }
176 if(parent->extrapolationMethod==linear && (x<parent->breaks(0) || x>parent->breaks(parent->nPoly))) {
177 int idx = x<parent->breaks(0) ? 0 : parent->nPoly;
178 auto xv = parent->breaks(idx);
179 auto value = parent->f(xv);
180 auto der = parent->fd(xv);
181 return value + der * (x - parent->breaks(idx));
182 }
183
184 if ((fabs(x-xSave)<macheps) && !firstCall)
185 return FromVecV<Ret>::cast(ySave);
186 else {
187 firstCall = false;
188 if(x<(parent->breaks)(parent->index)) // saved index still OK? otherwise search downwards
189 while((parent->index) > 0 && (parent->breaks)(parent->index) > x)
190 (parent->index)--;
191 else if(x>(parent->breaks)((parent->index)+1)) { // saved index still OK? otherwise search upwards
192 while((parent->index) < (parent->nPoly) && (parent->breaks)(parent->index) <= x)
193 (parent->index)++;
194 (parent->index)--;
195 }
196
197 const double dx = x - (parent->breaks)(parent->index); // local coordinate
198 fmatvec::VecV yi = trans(((parent->coefs)[0]).row(parent->index));
199 for(int i=1;i<=(parent->order);i++) // Horner scheme
200 yi = yi*dx+trans(((parent->coefs)[i]).row(parent->index));
201 xSave=x;
202 ySave <<= yi;
203 return FromVecV<Ret>::cast(yi);
204 }
205 }
206
207 template<typename Ret, typename Arg>
208 Ret PiecewisePolynomFunction<Ret(Arg)>::FirstDerivative::operator()(const double& x) {
209 if(parent->extrapolationMethod==error) {
210 if(x-1e-13>(parent->breaks)(parent->nPoly)) throw std::runtime_error("(PiecewisePolynomFunction::diff1): x out of range! x= "+fmatvec::toString(x)+", upper bound= "+fmatvec::toString((parent->breaks)(parent->nPoly)));
211 if(x+1e-13<(parent->breaks)(0)) throw std::runtime_error("(PiecewisePolynomFunction::diff1): x out of range! x= "+fmatvec::toString(x)+" lower bound= "+fmatvec::toString((parent->breaks)(0)));
212 }
213 if(parent->extrapolationMethod==linear && (x<parent->breaks(0) || x>parent->breaks(parent->nPoly))) {
214 int idx = x<parent->breaks(0) ? 0 : parent->nPoly;
215 auto xv = parent->breaks(idx);
216 auto der = parent->fd(xv);
217 return der;
218 }
219
220 if ((fabs(x-xSave)<macheps) && !firstCall)
221 return FromVecV<Ret>::cast(ySave);
222 else {
223 firstCall = false;
224 if(x<(parent->breaks)(parent->index)) // saved index still OK? otherwise search downwards
225 while((parent->index) > 0 && (parent->breaks)(parent->index) > x)
226 (parent->index)--;
227 else if(x>(parent->breaks)((parent->index)+1)) { // saved index still OK? otherwise search upwards
228 while((parent->index) < (parent->nPoly) && (parent->breaks)(parent->index) <= x)
229 (parent->index)++;
230 (parent->index)--;
231 }
232
233 double dx = x - (parent->breaks)(parent->index);
234 fmatvec::VecV yi = trans(((parent->coefs)[0]).row(parent->index))*double(parent->order);
235 for(int i=1;i<parent->order;i++)
236 yi = yi*dx+trans(((parent->coefs)[i]).row(parent->index))*double((parent->order)-i);
237 xSave=x;
238 ySave <<= yi;
239 return FromVecV<Ret>::cast(yi);
240 }
241 }
242
243 template<typename Ret, typename Arg>
244 Ret PiecewisePolynomFunction<Ret(Arg)>::SecondDerivative::operator()(const double& x) {
245 if(parent->extrapolationMethod==error) {
246 if(x-1e-13>(parent->breaks)(parent->nPoly)) throw std::runtime_error("(PiecewisePolynomFunction::diff2): x out of range! x= "+fmatvec::toString(x)+" upper bound= "+fmatvec::toString((parent->breaks)(parent->nPoly)));
247 if(x+1e-13<(parent->breaks)(0)) throw std::runtime_error("(PiecewisePolynomFunction::diff2): x out of range! x= "+fmatvec::toString(x)+" lower bound= "+fmatvec::toString((parent->breaks)(0)));
248 }
249 if(parent->extrapolationMethod==linear && (x<parent->breaks(0) || x>parent->breaks(parent->nPoly)))
250 return FromVecV<Ret>::cast(fmatvec::VecV(parent->getRetSize().first, fmatvec::INIT, 0.0));
251
252 if ((fabs(x-xSave)<macheps) && !firstCall)
253 return FromVecV<Ret>::cast(ySave);
254 else {
255 firstCall = false;
256 if(x<(parent->breaks)(parent->index)) // saved index still OK? otherwise search downwards
257 while((parent->index) > 0 && (parent->breaks)(parent->index) > x)
258 (parent->index)--;
259 else if(x>(parent->breaks)((parent->index)+1)) { // saved index still OK? otherwise search upwards
260 while((parent->index) < (parent->nPoly) && (parent->breaks)(parent->index) <= x)
261 (parent->index)++;
262 (parent->index)--;
263 }
264
265 double dx = x - (parent->breaks)(parent->index);
266 fmatvec::VecV yi = trans(((parent->coefs)[0]).row(parent->index))*double(parent->order)*double((parent->order)-1);
267 for(int i=1;i<=((parent->order)-2);i++)
268 yi = yi*dx+trans(((parent->coefs)[i]).row(parent->index))*double((parent->order)-i)*double((parent->order)-i-1);
269 xSave=x;
270 ySave <<= yi;
271 return FromVecV<Ret>::cast(yi);
272 }
273 }
274
275 template<typename Ret, typename Arg>
276 void PiecewisePolynomFunction<Ret(Arg)>::setCoefficientsArray(const std::vector<fmatvec::MatV> &allCoefs) {
277 interpolationMethod=useBreaksAndCoefs;
278
279 // read all coefficient and convert to coefs
280 coefs.resize(allCoefs[0].cols());
281 for(auto &x : coefs)
282 x.resize(allCoefs[0].rows(),allCoefs.size());
283 for(size_t c=0; c<allCoefs.size(); ++c) {
284 if(allCoefs[c].cols()!=static_cast<int>(coefs.size()))
285 this->throwError("The number of columns in the coefficients elements differ.");
286 if(allCoefs[c].rows()!=static_cast<int>(coefs[0].rows()))
287 this->throwError("The number of rows in the coefficients elements differ.");
288 for(int deg=0; deg<allCoefs[c].cols(); deg++)
289 coefs[deg].set(c, allCoefs[c].col(deg));
290 }
291 }
292
293 template<typename Ret, typename Arg>
294 void PiecewisePolynomFunction<Ret(Arg)>::addCoefficients(const fmatvec::MatV &coef) {
295 interpolationMethod=useBreaksAndCoefs;
296
297 // read all coefficient and convert to coefs
298 if(coefs.size()==0)
299 coefs.resize(coef.cols());
300 if(static_cast<int>(coefs.size())!=coef.cols())
301 this->throwError("The added coefficients have a different spline order.");
302 for(auto &x : coefs) {
303 auto xold(std::move(x));
304 x.resize(coef.rows(),xold.cols()+1);
305 x.set(fmatvec::RangeV(0,xold.rows()-1), fmatvec::RangeV(0,xold.cols()-1), std::move(xold));
306 }
307 if(coef.cols()!=static_cast<int>(coefs.size()))
308 this->throwError("The number of columns in the coefficients elements differ.");
309 if(coef.rows()!=static_cast<int>(coefs[0].rows()))
310 this->throwError("The number of rows in the coefficients elements differ.");
311 for(int deg=0; deg<coef.cols(); deg++)
312 coefs[deg].set(coefs[deg].cols()-1, coef.col(deg));
313 }
314
315 template<typename Ret, typename Arg>
316 void PiecewisePolynomFunction<Ret(Arg)>::initializeUsingXML(xercesc::DOMElement * element) {
317 xercesc::DOMElement *e;
318 fmatvec::VecV x;
319 fmatvec::MatV y;
320 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"x");
321 if (e) {
322 setx(MBXMLUtils::E(e)->getText<fmatvec::Vec>());
323 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"y");
324 sety(MBXMLUtils::E(e)->getText<fmatvec::Mat>(x.size(), 0));
325 }
326 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"xy");
327 if (e) {
328 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"xy");
329 setxy(MBXMLUtils::E(e)->getText<fmatvec::Mat>());
330 }
331 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"interpolationMethod");
332 if(e) {
333 std::string str=MBXMLUtils::X()%MBXMLUtils::E(e)->getFirstTextChild()->getData();
334 str=str.substr(1,str.length()-2);
335 if(str=="cSplinePeriodic") interpolationMethod=cSplinePeriodic;
336 else if(str=="cSplineNatural") interpolationMethod=cSplineNatural;
337 else if(str=="piecewiseLinear") interpolationMethod=piecewiseLinear;
338 else interpolationMethod=useBreaksAndCoefs;
339 }
340
341 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"breaks");
342 if(e) {
343 setBreaks(MBXMLUtils::E(e)->getText<fmatvec::Vec>());
344
345 std::vector<fmatvec::MatV> allCoefs;
346 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"coefficientsArray");
347 if(e) {
348 xercesc::DOMElement* ee=e->getFirstElementChild();
349 if(MBXMLUtils::E(ee)->getTagName()==MBSIM%"ele") {
350 while(ee) {
351 allCoefs.emplace_back(MBXMLUtils::E(ee)->getText<fmatvec::MatV>());
352 ee=ee->getNextElementSibling();
353 }
354 }
355 }
356 else {
357 // deprecated interface
358 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"coefficients");
359 while(e) {
360 MBXMLUtils::Deprecated::message(this, "[<coefficients>...</coefficients>]+ is deprecated, "
361 "please use <setCoefficientsArray>[<ele>...</ele>]+</setCoefficientsArray>", e);
362 allCoefs.emplace_back(MBXMLUtils::E(e)->getText<fmatvec::Mat>());
363 e=MBXMLUtils::E(e)->getNextElementSiblingNamed(MBSIM%"coefficients");
364 }
365 }
366 setCoefficientsArray(allCoefs);
367 }
368
369 e=MBXMLUtils::E(element)->getFirstElementChildNamed(MBSIM%"extrapolationMethod");
370 if(e) {
371 std::string str=MBXMLUtils::X()%MBXMLUtils::E(e)->getFirstTextChild()->getData();
372 str=str.substr(1,str.length()-2);
373 if(str=="error") extrapolationMethod=error;
374 else if(str=="continuePolynom") extrapolationMethod=continuePolynom;
375 else if(str=="linear") extrapolationMethod=linear;
376 else this->throwError("Unknown extrapolationMethod.");
377 }
378 }
static void message(const fmatvec::Atom *ele, const std::string &msg, const xercesc::DOMElement *e=nullptr)