OOFEM 3.0
Loading...
Searching...
No Matches
feitspline.C
Go to the documentation of this file.
1/*
2 *
3 * ##### ##### ###### ###### ### ###
4 * ## ## ## ## ## ## ## ### ##
5 * ## ## ## ## #### #### ## # ##
6 * ## ## ## ## ## ## ## ##
7 * ## ## ## ## ## ## ## ##
8 * ##### ##### ## ###### ## ##
9 *
10 *
11 * OOFEM : Object Oriented Finite Element Code
12 *
13 * Copyright (C) 1993 - 2025 Borek Patzak
14 *
15 *
16 *
17 * Czech Technical University, Faculty of Civil Engineering,
18 * Department of Structural Mechanics, 166 29 Prague, Czech Republic
19 *
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
29 *
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 */
34
35#include "feitspline.h"
36#include "floatarray.h"
37#include "floatmatrix.h"
38#include "mathfem.h"
39#include "iga.h"
40
41namespace oofem {
42// optimized version of A4.4 for d=1
43#define OPTIMIZED_VERSION_A4dot4
44
45
46void TSplineInterpolation :: initializeFrom(InputRecord &ir, ParameterManager&pm, int elnum, int priority)
47{
48 BSplineInterpolation :: initializeFrom(ir, pm, elnum, priority);
49
50 IntArray localIndexKnotVector_tmp;
51
52 InputFieldType IFT_localIndexKnotVector [ 3 ] = {
56 };
57 int max_deg = 0;
58 for ( int i = 0; i < nsd; i++ ) {
59 if ( degree [ i ] > max_deg ) {
60 max_deg = degree [ i ];
61 }
62 }
63
64 //openLocalKnotVector.resize( 3 * max_deg + 2 );
65
67
68 for ( int n = 0; n < nsd; n++ ) {
69 localIndexKnotVector_tmp.clear();
70 IR_GIVE_FIELD(ir, localIndexKnotVector_tmp, IFT_localIndexKnotVector [ n ]);
71 if ( localIndexKnotVector_tmp.giveSize() != totalNumberOfControlPoints * ( degree [ n ] + 2 ) ) {
72 throw ValueInputException(ir, IFT_localIndexKnotVector [ n ], "invalid size of knot vector");
73 }
74
75 int pos = 0;
76 for ( int i = 0; i < totalNumberOfControlPoints; i++ ) {
77 auto &indexKnotVec = localIndexKnotVector [ i ] [ n ];
78 indexKnotVec.resize( degree [ 2 ] + 2 );
79
80 int p = 0;
81 for ( int j = 0; j < degree [ n ] + 2; j++ ) {
82 indexKnotVec [ p++ ] = localIndexKnotVector_tmp[pos++];
83 }
84
85 // check for monotonicity of local index knot vector with multiplicity
86 int indexKnotVal = indexKnotVec [ 0 ];
87 for ( int j = 1; j < degree [ n ] + 2; j++ ) {
88 if ( indexKnotVal > indexKnotVec [ j ] ) {
89 throw ValueInputException(ir, IFT_localIndexKnotVector [ n ], "local index knot vector is not monotonic");
90 }
91
92 /* this is only for the case when TSpline = NURBS
93 * if(indexKnotVal+1 < indexKnotVec[j])
94 * OOFEM_ERROR("local index knot vector %s of control point %d is not continuous",
95 * IFT_localIndexKnotVectorString[n], i+1);
96 */
97 indexKnotVal = indexKnotVec [ j ];
98 }
99
100 // check for nondegeneracy of local index knot vector
101 if ( indexKnotVal == indexKnotVec [ 0 ] ) {
102 throw ValueInputException(ir, IFT_localIndexKnotVector [ n ], "local index knot vector is degenerated");
103 }
104
105 // check for range of local index knot vector
106 if ( indexKnotVec [ 0 ] <= 0 || indexKnotVal > knotValues [ n ].giveSize() ) {
107 throw ValueInputException(ir, IFT_localIndexKnotVector [ n ], "local index knot vector out of range");
108 }
109 }
110 }
111
113}
114
115
116void TSplineInterpolation :: evalN(FloatArray &answer, const FloatArray &lcoords, const FEICellGeometry &cellgeo) const
117{
118 const FEIIGAElementGeometryWrapper &gw = static_cast< const FEIIGAElementGeometryWrapper& >(cellgeo);
120 IntArray span(nsd);
121 IntArray mask;
122 double sum = 0.0, val;
123 int count;
124
125 if ( nsd != 2 ) {
126 OOFEM_ERROR("implemented for nsd = %d", nsd);
127 }
128
129 if ( gw.knotSpan ) {
130 span = * gw.knotSpan;
131 } else {
132 for ( int i = 0; i < nsd; i++ ) {
133 span[i] = this->findSpan(numberOfControlPoints [ i ], degree [ i ], lcoords[i], knotVector [ i ]);
134 }
135 }
136
137 // identify which basis functions are nonzero
138 giveKnotSpanBasisFuncMask(span, mask);
139 count = mask.giveSize();
140 answer.resize(count);
141
142 if ( nsd == 2 ) {
143 for ( int k = 0; k < count; k++ ) {
144 for ( int i = 0; i < nsd; i++ ) {
145 N[i] = this->basisFunction(lcoords[i], degree [ i ], * giveKnotValues(i + 1), localIndexKnotVector [ mask[k] - 1 ] [ i ]);
146 }
147
148 answer[k] = val = N[0] * N[1] * this->weights.at( mask[k] ); // Nu*Nv*w
149 sum += val;
150 }
151 }
152
153 answer.times(1./sum);
154}
155
156
157double TSplineInterpolation :: evaldNdx(FloatMatrix &answer, const FloatArray &lcoords, const FEICellGeometry &cellgeo) const
158{
159 const FEIIGAElementGeometryWrapper &gw = static_cast< const FEIIGAElementGeometryWrapper& >(cellgeo);
160 FloatMatrix jacobian(nsd, nsd);
161 FloatArray temp(nsd);
162 IntArray span(nsd);
163 IntArray mask;
164 double Jacob = 0.;
165 int count;
166 std :: vector< FloatArray > tmp_ders(nsd);
167 std :: vector< FloatMatrix > ders(nsd);
168
169 /*
170 * IntArray Bin(2,2); // binomial coefficients from 0 to d=1
171 * // Bin(n,k)=(n above k)=n!/k!(n-k)! for n>=k>=0
172 * // lower triangle corresponds to Pascal triangle
173 * // according to A4.4 it seems that only coefficients in lower triangle except the first column are used
174 */
175 if ( nsd != 2 ) {
176 OOFEM_ERROR("not implemented for nsd = %d", nsd);
177 }
178
179 if ( gw.knotSpan ) {
180 span = * gw.knotSpan;
181 } else {
182 for ( int i = 0; i < nsd; i++ ) {
183 span[i] = this->findSpan(numberOfControlPoints [ i ], degree [ i ], lcoords[i], knotVector [ i ]);
184 }
185 }
186
187 // identify which basis functions are nonzero
188 giveKnotSpanBasisFuncMask(span, mask);
189 count = mask.giveSize();
190 answer.resize(count, nsd);
191
192 for ( int i = 0; i < nsd; i++ ) {
193 ders [ i ].resize(2, count);
194 }
195
196 if ( nsd == 2 ) {
197 FloatMatrix Aders [ 2 ]; // derivatives in each coordinate direction on BSpline
198 //FloatMatrix Sders[nsd]; // derivatives in each coordinate direction on TSpline
199 FloatMatrix wders; // derivatives in w direction on BSpline
200
201 // resizing to (2,2) has nothing common with nsd
202 // it is related to the fact that 0th and 1st derivatives are computed
203 for ( int i = 0; i < nsd; i++ ) {
204 Aders [ i ].resize(2, 2);
205 Aders [ i ].zero();
206 //Sders[i].resize(2,2);
207 }
208
209 wders.resize(2, 2);
210 wders.zero();
211
212 for ( int k = 0; k < count; k++ ) {
213 for ( int i = 0; i < nsd; i++ ) {
214 // it would be simpler if I could pass k-th column of ders[i] directly to dersBasisFunction HUHU array
215 this->dersBasisFunction(1, lcoords[i], degree [ i ], * giveKnotValues(i + 1), localIndexKnotVector [ mask[k] - 1 ] [ i ], tmp_ders [ i ]);
216 ders [ i ](0, k) = tmp_ders [ i ][0];
217 ders [ i ](1, k) = tmp_ders [ i ][1];
218 }
219
220 // calculation of jacobian matrix in similar fashion as A4.4
221 // calculate values and derivatives of nonrational Bspline surface with weights at first (Aders, wders)
222 const auto &vertexCoords = cellgeo.giveVertexCoordinates( mask[k] );
223 double w = this->weights.at( mask[k] );
224 double xw = vertexCoords[0] * w;
225 double yw = vertexCoords[1] * w;
226 double product;
227
228 product = tmp_ders [ 0 ][0] * tmp_ders [ 1 ][0]; // Nu*Nv
229 Aders [ 0 ](0, 0) += product * xw; // x=sum Nu*Nv*x*w
230 Aders [ 1 ](0, 0) += product * yw; // x=sum Nu*Nv*y*w
231 wders(0, 0) += product * w; // w=sum Nu*Nv*w
232
233 product = tmp_ders [ 0 ][1] * tmp_ders [ 1 ][0]; // dNu/du*Nv
234 Aders [ 0 ](1, 0) += product * xw; // dx/du=sum dNu/du*Nv*x*w
235 Aders [ 1 ](1, 0) += product * yw; // dy/du=sum dNu/du*Nv*y*w
236 wders(1, 0) += product * w; // dw/du=sum dNu/du*Nv*w
237
238 product = tmp_ders [ 0 ][0] * tmp_ders [ 1 ][1]; // Nu*dNv/dv
239 Aders [ 0 ](0, 1) += product * xw; // dx/dv=sum Nu*dNv/dv*x*w
240 Aders [ 1 ](0, 1) += product * yw; // dy/dv=sum Nu*dNv/dv*y*w
241 wders(0, 1) += product * w; // dw/dv=sum Nu*dNv/dv*w
242 }
243
244 double weight = wders(0, 0);
245
246 // optimized version of A4.4 for d=1, binomial coefficients ignored
247 /*
248 * Sders[0](0,0) = Aders[0](0,0) / weight;
249 * Sders[1](0,0) = Aders[1](0,0) / weight;
250 * Sders[0](0,1) = (Aders[0](0,1)-wders(0,1)*Sders[0](0,0)) / weight;
251 * Sders[1](0,1) = (Aders[1](0,1)-wders(0,1)*Sders[1](0,0)) / weight;
252 * Sders[0](1,0) = (Aders[0](1,0)-wders(1,0)*Sders[0](0,0)) / weight;
253 * Sders[1](1,0) = (Aders[1](1,0)-wders(1,0)*Sders[1](0,0)) / weight;
254 *
255 * jacobian(0,0) = Sders[0](1,0); // dx/du
256 * jacobian(0,1) = Sders[1](1,0); // dy/du
257 * jacobian(1,0) = Sders[0](0,1); // dx/dv
258 * jacobian(1,1) = Sders[1](0,1); // dy/dv
259 */
260
261 temp[0] = Aders [ 0 ](0, 0) / weight;
262 temp[1] = Aders [ 1 ](0, 0) / weight;
263 jacobian(1, 0) = ( Aders [ 0 ](0, 1) - wders(0, 1) * temp[0] ) / weight; // dx/dv
264 jacobian(1, 1) = ( Aders [ 1 ](0, 1) - wders(0, 1) * temp[1] ) / weight; // dy/dv
265 jacobian(0, 0) = ( Aders [ 0 ](1, 0) - wders(1, 0) * temp[0] ) / weight; // dx/du
266 jacobian(0, 1) = ( Aders [ 1 ](1, 0) - wders(1, 0) * temp[1] ) / weight; // dy/du
267
268 Jacob = jacobian.giveDeterminant();
269
270 //calculation of derivatives of TSpline basis functions with respect to local parameters
271 double product = Jacob * weight * weight;
272
273 for ( int k = 0; k < count; k++ ) {
274 double w = this->weights.at( mask[k] );
275 // dNu/du*Nv*w*sum(Nv*Nu*w) - Nu*Nv*w*sum(dNu/du*Nv*w)
276 temp[0] = ders [ 0 ](1, k) * ders [ 1 ](0, k) * w * weight - ders [ 0 ](0, k) * ders [ 1 ](0, k) * w * wders(1, 0);
277 // Nu*dNv/dv*w*sum(Nv*Nu*w) - Nu*Nv*w*sum(Nu*dNv/dv*w)
278 temp[1] = ders [ 0 ](0, k) * ders [ 1 ](1, k) * w * weight - ders [ 0 ](0, k) * ders [ 1 ](0, k) * w * wders(0, 1);
279
280 answer(k, 0) = ( jacobian(1, 1) * temp[0] - jacobian(0, 1) * temp[1] ) / product;
281 answer(k, 1) = ( -jacobian(1, 0) * temp[0] + jacobian(0, 0) * temp[1] ) / product;
282 }
283 }
284
285 return Jacob;
286}
287
288
289void TSplineInterpolation :: local2global(FloatArray &answer, const FloatArray &lcoords, const FEICellGeometry &cellgeo) const
290{
291 /* Based on SurfacePoint A4.3 implementation*/
292 const FEIIGAElementGeometryWrapper &gw = static_cast< const FEIIGAElementGeometryWrapper& >(cellgeo);
294 IntArray span(nsd);
295 IntArray mask;
296 double weight = 0.0;
297 int count;
298
299 if ( nsd != 2 ) {
300 OOFEM_ERROR("not implemented for nsd = %d", nsd);
301 }
302
303 if ( gw.knotSpan ) {
304 span = * gw.knotSpan;
305 } else {
306 for ( int i = 0; i < nsd; i++ ) {
307 span[i] = this->findSpan(numberOfControlPoints [ i ], degree [ i ], lcoords[i], knotVector [ i ]);
308 }
309 }
310
311 // identify which basis functions are nonzero
312 giveKnotSpanBasisFuncMask(span, mask);
313 count = mask.giveSize();
314
315 answer.resize(nsd);
316 answer.zero();
317
318 if ( nsd == 2 ) {
319 for ( int k = 0; k < count; k++ ) {
320 for ( int i = 0; i < nsd; i++ ) {
321 N[i] = this->basisFunction(lcoords[i], degree [ i ], * giveKnotValues(i + 1), localIndexKnotVector [ mask[k] - 1 ] [ i ]);
322 }
323
324 const auto &vertexCoords = cellgeo.giveVertexCoordinates( mask[k] );
325 double w = this->weights.at( mask[k] );
326 double xw = vertexCoords[0] * w;
327 double yw = vertexCoords[1] * w;
328
329 double product = N[0] * N[1]; // Nu*Nv
330 answer[0] += product * xw; // x=sum Nu*Nv*x*w
331 answer[1] += product * yw; // y=sum Nu*Nv*y*w
332 weight += product * w; // w=sum Nu*Nv*w
333 }
334 }
335
336 answer.times(1.0 / weight);
337}
338
339
340void TSplineInterpolation :: giveJacobianMatrixAt(FloatMatrix &jacobian, const FloatArray &lcoords, const FEICellGeometry &cellgeo) const
341{
342 //
343 // Based on Algorithm A4.4 (p. 137) for d=1
344 //
345 const FEIIGAElementGeometryWrapper &gw = static_cast< const FEIIGAElementGeometryWrapper& >(cellgeo);
346 FloatArray temp(nsd);
347 IntArray span(nsd);
348 IntArray mask;
349 int count;
350 std :: vector< FloatArray > ders(nsd);
351 jacobian.resize(nsd, nsd);
352
353 /*
354 * IntArray Bin(2,2); // binomial coefficients from 0 to d=1
355 * // Bin(n,k)=(n above k)=n!/k!(n-k)! for n>=k>=0
356 * // lower triangle corresponds to Pascal triangle
357 * // according to A4.4 it seems that only coefficients in lower triangle except the first column are used
358 */
359 if ( nsd != 2 ) {
360 OOFEM_ERROR("not implemented for nsd = %d", nsd);
361 }
362
363 if ( gw.knotSpan ) {
364 span = * gw.knotSpan;
365 } else {
366 for ( int i = 0; i < nsd; i++ ) {
367 span[i] = this->findSpan(numberOfControlPoints [ i ], degree [ i ], lcoords[i], knotVector [ i ]);
368 }
369 }
370
371 // identify which basis functions are nonzero
372 giveKnotSpanBasisFuncMask(span, mask);
373 count = mask.giveSize();
374
375 if ( nsd == 2 ) {
376 FloatMatrix Aders [ 2 ]; // derivatives in each coordinate direction on BSpline
377 //FloatMatrix Sders[nsd]; // derivatives in each coordinate direction on TSpline
378 FloatMatrix wders; // derivatives in w direction on BSpline
379
380 // resizing to (2,2) has nothing common with nsd
381 // it is related to the fact that 0th and 1st derivatives are computed
382 for ( int i = 0; i < nsd; i++ ) {
383 Aders [ i ].resize(2, 2);
384 Aders [ i ].zero();
385 //Sders[i].resize(2,2);
386 }
387
388 wders.resize(2, 2);
389 wders.zero();
390
391 for ( int k = 0; k < count; k++ ) {
392 for ( int i = 0; i < nsd; i++ ) {
393 this->dersBasisFunction(1, lcoords[i], degree [ i ], * giveKnotValues(i + 1), localIndexKnotVector [ mask[k] - 1 ] [ i ], ders [ i ]);
394 }
395
396 // calculation of jacobian matrix in similar fashion as A4.4
397 // calculate values and derivatives of nonrational Bspline surface with weights at first (Aders, wders)
398 const auto &vertexCoords = cellgeo.giveVertexCoordinates( mask[k] );
399 double w = this->weights.at( mask[k] );
400 double xw = vertexCoords[0] * w;
401 double yw = vertexCoords[1] * w;
402 double product;
403
404 product = ders [ 0 ][0] * ders [ 1 ][0]; // Nu*Nv
405 Aders [ 0 ](0, 0) += product * xw; // x=sum Nu*Nv*x*w
406 Aders [ 1 ](0, 0) += product * yw; // x=sum Nu*Nv*y*w
407 wders(0, 0) += product * w; // w=sum Nu*Nv*w
408
409 product = ders [ 0 ][1] * ders [ 1 ][0]; // dNu/du*Nv
410 Aders [ 0 ](1, 0) += product * xw; // dx/du=sum dNu/du*Nv*x*w
411 Aders [ 1 ](1, 0) += product * yw; // dy/du=sum dNu/du*Nv*y*w
412 wders(1, 0) += product * w; // dw/du=sum dNu/du*Nv*w
413
414 product = ders [ 0 ][0] * ders [ 1 ][1]; // Nu*dNv/dv
415 Aders [ 0 ](0, 1) += product * xw; // dx/dv=sum Nu*dNv/dv*x*w
416 Aders [ 1 ](0, 1) += product * yw; // dy/dv=sum Nu*dNv/dv*y*w
417 wders(0, 1) += product * w; // dw/dv=sum Nu*dNv/dv*w
418 }
419
420 double weight = wders(0, 0);
421
422 // optimized version of A4.4 for d=1, binomial coefficients ignored
423#if 0
424 Sders[0](0,0) = Aders[0](0,0) / weight;
425 Sders[1](0,0) = Aders[1](0,0) / weight;
426 Sders[0](0,1) = (Aders[0](0,1)-wders(0,1)*Sders[0](0,0)) / weight;
427 Sders[1](0,1) = (Aders[1](0,1)-wders(0,1)*Sders[1](0,0)) / weight;
428 Sders[0](1,0) = (Aders[0](1,0)-wders(1,0)*Sders[0](0,0)) / weight;
429 Sders[1](1,0) = (Aders[1](1,0)-wders(1,0)*Sders[1](0,0)) / weight;
430
431 jacobian(0,0) = Sders[0](1,0); // dx/du
432 jacobian(0,1) = Sders[1](1,0); // dy/du
433 jacobian(1,0) = Sders[0](0,1); // dx/dv
434 jacobian(1,1) = Sders[1](0,1); // dy/dv
435#endif
436
437 temp[0] = Aders [ 0 ](0, 0) / weight;
438 temp[1] = Aders [ 1 ](0, 0) / weight;
439 jacobian(1, 0) = ( Aders [ 0 ](0, 1) - wders(0, 1) * temp[0] ) / weight; // dx/dv
440 jacobian(1, 1) = ( Aders [ 1 ](0, 1) - wders(0, 1) * temp[1] ) / weight; // dy/dv
441 jacobian(0, 0) = ( Aders [ 0 ](1, 0) - wders(1, 0) * temp[0] ) / weight; // dx/du
442 jacobian(0, 1) = ( Aders [ 1 ](1, 0) - wders(1, 0) * temp[1] ) / weight; // dy/du
443 }
444}
445
446
447// knotSpan corresponds to knot span in terms of BSpline;
448// it should not matter which part of IGAIntegrationElement if covering more spans is addressed;
449// this implementation relies on the fact that IGAIntegrationElements are those subsets
450// of T-mesh cells on which there are fully (this means not only partially) nonzero relevant basis functions
451
452int TSplineInterpolation :: giveKnotSpanBasisFuncMask(const IntArray &knotSpan, IntArray &mask) const
453{
454 FloatArray knotStart(nsd), knotEnd(nsd);
455
456 // resize the mask initially to the size corresponding to BSpline case
457 // but there may be more nonzero basis functions
458 if ( nsd == 2 ) {
459 mask.preallocate( ( degree [ 0 ] + 1 ) * ( degree [ 1 ] + 1 ) );
460 } else {
461 OOFEM_ERROR("not implemented for nsd = %d", nsd);
462 }
463
464 // get starting and ending knots
465 for ( int j = 0; j < nsd; j++ ) {
466 knotStart[j] = knotVector [ j ] [ knotSpan[j] ];
467 knotEnd[j] = knotVector [ j ] [ knotSpan[j] + 1 ];
468 }
469
470 // for each control point check
471 for ( int i = 0; i < totalNumberOfControlPoints; i++ ) {
472 // whether local knot vector overlaps the given knot span
473 int nonzero = 1;
474 for ( int j = 0; j < nsd; j++ ) {
475 if ( ( knotEnd[j] <= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ 0 ]) ) ||
476 ( knotStart[j] >= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ degree [ j ] + 1 ]) ) ) {
477 nonzero = 0;
478 break;
479 }
480 }
481
482 if ( nonzero ) {
483 mask.followedBy(i + 1, 4);
484 }
485 }
486
487 return 1;
488}
489
490
491
492// knotSpan corresponds to knot span in terms of BSpline;
493// it should not matter which part of IGAIntegrationElement if covering more spans is addressed;
494// this implementation relies on the fact that IGAIntegrationElements are those subsets
495// of T-mesh cells on which there are fully (this means not only partially) nonzero relevant basis functions
496
497int TSplineInterpolation :: giveNumberOfKnotSpanBasisFunctions(const IntArray &knotSpan) const
498{
499 int answer = 0;
500 FloatArray knotStart(nsd), knotEnd(nsd);
501
502 // get starting and ending knots
503 for ( int j = 0; j < nsd; j++ ) {
504 knotStart[j] = knotVector [ j ] [ knotSpan[j] ];
505 knotEnd[j] = knotVector [ j ] [ knotSpan[j] + 1 ];
506 }
507
508 // for each control point check
509 for ( int i = 0; i < totalNumberOfControlPoints; i++ ) {
510 answer++;
511 // whether local knot vector overlaps the given knot span
512 for ( int j = 0; j < nsd; j++ ) {
513 if ( ( knotEnd[j] <= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ 0 ]) ) ||
514 ( knotStart[j] >= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ degree [ j ] + 1 ]) ) ) {
515 answer--;
516 break;
517 }
518 }
519 }
520
521 return answer;
522}
523
524
525
526// starKnotSpan and endKnotSpan correspond to knot span in terms of BSpline;
527// should the number of non-zero basis function be calculated for single knot span
528// starKnotSpan and endKnotSpan are equal;
529// some of the basis function may not cover the whole knot span interval !!!
530
531int TSplineInterpolation :: giveKnotSpanBasisFuncMask(const IntArray &startKnotSpan, const IntArray &endKnotSpan, IntArray &mask) const
532{
533 int nonzero;
534 FloatArray knotStart(nsd), knotEnd(nsd);
535
536 // resize the mask initially to the size corresponding to BSpline case
537 // but there may be more nonzero basis functions
538 if ( nsd == 2 ) {
539 mask.preallocate( ( degree [ 0 ] + 1 ) * ( degree [ 1 ] + 1 ) );
540 } else {
541 OOFEM_ERROR("not implemented for nsd = %d", nsd);
542 }
543
544 // get starting and ending knots
545 for ( int j = 0; j < nsd; j++ ) {
546 knotStart[j] = knotVector [ j ] [ startKnotSpan[j] ];
547 knotEnd[j] = knotVector [ j ] [ endKnotSpan[j] + 1 ];
548 }
549
550 // for each control point check
551 for ( int i = 0; i < totalNumberOfControlPoints; i++ ) {
552 // whether local knot vector overlaps at least partially the knot span interval
553 nonzero = 1;
554 for ( int j = 0; j < nsd; j++ ) {
555 if ( ( knotEnd[j] <= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ 0 ]) ) ||
556 ( knotStart[j] >= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ degree [ j ] + 1 ]) ) ) {
557 nonzero = 0;
558 break;
559 }
560 }
561
562 if ( nonzero ) {
563 mask.followedBy(i + 1, 4);
564 }
565 }
566
567 return 1;
568}
569
570
571// starKnotSpan and endKnotSpan correspond to knot apan in terms of BSpline;
572// should the number of non-zero basis function be calculated for single knot span
573// starKnotSpan and endKnotSpan are equal
574// some of the basis function may not cover the whole knot span interval !!!
575
576int TSplineInterpolation :: giveNumberOfKnotSpanBasisFunctions(const IntArray &startKnotSpan, const IntArray &endKnotSpan) const
577{
578 int answer = 0;
579 FloatArray knotStart(nsd), knotEnd(nsd);
580
581 // get starting and ending knots
582 for ( int j = 0; j < nsd; j++ ) {
583 knotStart[j] = knotVector [ j ] [ startKnotSpan[j] ];
584 knotEnd[j] = knotVector [ j ] [ endKnotSpan[j] + 1 ];
585 }
586
587 // for each control point check
588 for ( int i = 0; i < totalNumberOfControlPoints; i++ ) {
589 answer++;
590 // whether local knot vector overlaps at least partially the knot span interval
591 for ( int j = 0; j < nsd; j++ ) {
592 if ( ( knotEnd[j] <= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ 0 ]) ) ||
593 ( knotStart[j] >= knotValues [ j ].at(localIndexKnotVector [ i ] [ j ] [ degree [ j ] + 1 ]) ) ) {
594 answer--;
595 break;
596 }
597 }
598 }
599
600 return answer;
601}
602
603
604
605// call corresponding BSpline methods for open local knot vector
606
607double TSplineInterpolation :: basisFunction(double u, int p, const FloatArray &U, const IntArray &I) const
608{
609 int span, prepend, append;
610 FloatArray N, openLocalKnotVector;
611
612 createLocalKnotVector(openLocalKnotVector, p, U, I, prepend, append);
613 span = BSplineInterpolation :: findSpan(prepend + append, p, u, openLocalKnotVector);
614 BSplineInterpolation :: basisFuns(N, span, u, p, openLocalKnotVector);
615
616 // extract the middle basis function
617 // this corresponds to index p-span, however prepended knotspans must be considered
618 return N(p - span + prepend);
619}
620
621
622// call corresponding BSpline methods for open local knot vector
623
624void TSplineInterpolation :: dersBasisFunction(int n, double u, int p, const FloatArray &U, const IntArray &I, FloatArray &ders) const
625{
626 int span, prepend, append;
627 FloatArray openLocalKnotVector;
628 FloatMatrix Ders;
629
630 createLocalKnotVector(openLocalKnotVector, p, U, I, prepend, append);
631 span = BSplineInterpolation :: findSpan(prepend + append, p, u, openLocalKnotVector);
632 BSplineInterpolation :: dersBasisFuns(n, u, span, p, openLocalKnotVector, Ders);
633
634 // extract the middle basis function and its derivatives
635 // this corresponds to index p-span, however prepended knotspans must be considered
636 ders.resize(n + 1);
637 for ( int i = 0; i <= n; i++ ) {
638 ders[i] = Ders(i, p - span + prepend);
639 }
640}
641
642
643void TSplineInterpolation :: createLocalKnotVector(FloatArray &openLocalKnotVector, int p, const FloatArray &U, const IntArray &I, int &prepend, int &append) const
644{
645 int j = 0, index_first = I [ 0 ], index_last = I [ p + 1 ], mult_first = 1, mult_last = 1;
646 double first = U.at(index_first), last = U.at(index_last);
647 int max_deg = 0;
648 for ( int i = 0; i < nsd; i++ ) {
649 if ( degree [ i ] > max_deg ) {
650 max_deg = degree [ i ];
651 }
652 }
653 openLocalKnotVector.resize( 3 * max_deg + 2 );
654
655 for ( int i = 1; i < p + 1; i++ ) {
656 if ( I [ i ] != index_first ) {
657 break;
658 }
659
660 mult_first++;
661 }
662
663 for ( int i = p; i > 0; i-- ) {
664 if ( I [ i ] != index_last ) {
665 break;
666 }
667
668 mult_last++;
669 }
670
671 prepend = p + 1 - mult_first;
672 append = p + 1 - mult_last;
673
674 // prepend first knot (once more)
675 for ( int i = 0; i <= prepend; i++ ) {
676 openLocalKnotVector [ j++ ] = first;
677 }
678
679 // copy middle of knot vector (without first and last)
680 for ( int i = 1; i <= p; i++ ) {
681 openLocalKnotVector [ j++ ] = U.at(I [ i ]);
682 }
683
684 // append last knot (once more)
685 for ( int i = 0; i <= append; i++ ) {
686 openLocalKnotVector [ j++ ] = last;
687 }
688}
689} // end namespace oofem
#define N(a, b)
std::array< int, 3 > degree
Degree in each direction.
Definition feibspline.h:68
std::array< FloatArray, 3 > knotVector
Knot vectors [nsd][knot_vector_size].
Definition feibspline.h:80
std::array< int, 3 > numberOfControlPoints
numberOfControlPoints[nsd]
Definition feibspline.h:78
const FloatArray * giveKnotValues(int dim) const override
Definition feibspline.h:191
int findSpan(int n, int p, double u, const FloatArray &U) const
Definition feibspline.C:774
int nsd
Number of spatial directions.
Definition feibspline.h:66
std::array< FloatArray, 3 > knotValues
Knot values [nsd].
Definition feibspline.h:70
virtual const FloatArray giveVertexCoordinates(int i) const =0
const IntArray * knotSpan
Definition iga.h:60
void resize(Index s)
Definition floatarray.C:94
double & at(Index i)
Definition floatarray.h:202
void zero()
Zeroes all coefficients of receiver.
Definition floatarray.C:683
void times(double s)
Definition floatarray.C:834
void resize(Index rows, Index cols)
Definition floatmatrix.C:79
void zero()
Zeroes all coefficient of receiver.
double giveDeterminant() const
void followedBy(const IntArray &b, int allocChunk=0)
Definition intarray.C:94
void preallocate(int futureSize)
Definition intarray.C:79
int giveSize() const
Definition intarray.h:211
std::vector< std::array< IntArray, 3 > > localIndexKnotVector
Local index knot vector of the dimensions [totalNumberOfControlPoints][nsd][degree+2].
Definition feitspline.h:66
int giveKnotSpanBasisFuncMask(const IntArray &knotSpan, IntArray &mask) const override
Definition feitspline.C:452
void createLocalKnotVector(FloatArray &answer, int p, const FloatArray &U, const IntArray &I, int &prepend, int &append) const
Definition feitspline.C:643
double basisFunction(double u, int p, const FloatArray &U, const IntArray &I) const
Definition feitspline.C:607
void dersBasisFunction(int n, double u, int p, const FloatArray &U, const IntArray &I, FloatArray &ders) const
Definition feitspline.C:624
#define OOFEM_ERROR(...)
Definition error.h:79
#define _IFT_TSplineInterpolation_weights
Definition feitspline.h:52
#define _IFT_TSplineInterpolation_localIndexKnotVectorW
Definition feitspline.h:55
#define _IFT_TSplineInterpolation_localIndexKnotVectorV
Definition feitspline.h:54
#define _IFT_TSplineInterpolation_localIndexKnotVectorU
Definition feitspline.h:53
#define IR_GIVE_FIELD(__ir, __value, __id)
Definition inputrecord.h:67
double sum(const FloatArray &x)
double product(const FloatArray &x)
const char * InputFieldType
Identifier of fields in input records.
Definition inputrecord.h:59

This page is part of the OOFEM-3.0 documentation. Copyright Copyright (C) 1994-2025 Borek Patzak Bořek Patzák
Project e-mail: oofem@fsv.cvut.cz
Generated at for OOFEM by doxygen 1.15.0 written by Dimitri van Heesch, © 1997-2011