1 // Copyright (c) 2015-2016, Massachusetts Institute of Technology
2 // Copyright (c) 2016-2017 Sandia Corporation
3 // Copyright (c) 2017 NTESS, LLC.
4
5 // This file is part of the Compressed Continuous Computation (C3) Library
6 // Author: Alex A. Gorodetsky
7 // Contact: alex@alexgorodetsky.com
8
9 // All rights reserved.
10
11 // Redistribution and use in source and binary forms, with or without modification,
12 // are permitted provided that the following conditions are met:
13
14 // 1. Redistributions of source code must retain the above copyright notice,
15 // this list of conditions and the following disclaimer.
16
17 // 2. Redistributions in binary form must reproduce the above copyright notice,
18 // this list of conditions and the following disclaimer in the documentation
19 // and/or other materials provided with the distribution.
20
21 // 3. Neither the name of the copyright holder nor the names of its contributors
22 // may be used to endorse or promote products derived from this software
23 // without specific prior written permission.
24
25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36 //Code
37
38
39 /** \file linelm.c
40 * Provides routines for manipulating linear elements
41 */
42
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <math.h>
46 #include <complex.h>
47 #include <string.h>
48 #include <float.h>
49 #include <assert.h>
50
51 #include "stringmanip.h"
52 #include "array.h"
53 #include "linalg.h"
54 #include "futil.h"
55 #include "linelm.h"
56
57 /** \struct LinElemExpAopts
58 * \brief Approximation options of LinElemExp
59 * \var LinElemExpAopts::num_nodes
60 * number of basis functions or nodes
61 * \var LinElemExpAopts::node_alloc
62 * indicator whether nodes were self allocated
63 * \var LinElemExpAopts::nodes
64 * nodes
65 * \var LinElemExpAopts::adapt
66 * whether or not to adapt (0 or 1)
67 * \var LinElemExpAopts::lb
68 * lower bound
69 * \var LinElemExpAopts::ub
70 * upper bound
71 * \var LinElemExpAopts::delta
72 * adaptation function value tolerance
73 * \var LinElemExpAopts::hmin
74 * adaptation node spacing tolerance
75 */
76 struct LinElemExpAopts{
77
78 size_t num_nodes;
79 int node_alloc;
80 double * nodes;
81 int adapt;
82
83 double lb;
84 double ub;
85 double delta;
86 double hmin;
87
88 // for periodic boundary conditions
89 double * Lp;
90 double * p;
91 };
92
93 /********************************************************//**
94 Allocate approximation arguments (by reference)
95
96 \param[in] N - number of nodes
97 \param[in] x - nodes
98
99 \return approximation arguments
100 *************************************************************/
lin_elem_exp_aopts_alloc(size_t N,double * x)101 struct LinElemExpAopts * lin_elem_exp_aopts_alloc(size_t N, double * x)
102 {
103 assert (x != NULL);
104 struct LinElemExpAopts * aopts = NULL;
105 aopts = malloc(sizeof(struct LinElemExpAopts));
106 if (aopts == NULL){
107 fprintf(stderr,"Memory error allocate LinElemExpAopts\n");
108 exit(1);
109 }
110 aopts->num_nodes = N;
111 aopts->node_alloc = 0;
112 aopts->nodes = x;
113 aopts->lb = x[0];
114 aopts->ub = x[N-1];
115
116 /* printf("in allocation: lin_elem_exp_aopts nodes = "); dprint(N,x); */
117 aopts->adapt = 0;
118 aopts->delta = DBL_MAX;
119 aopts->hmin = DBL_MAX;
120
121 return aopts;
122 }
123
124 /********************************************************//**
125 Allocate approximation arguments for adaptation
126
127 \param[in] N - number of nodes
128 \param[in] x - starting nodes reference
129 \param[in] lb - lower bound lb==x[0] if x! NULL
130 \param[in] ub - upper bound ub==x[0] if x! NULL
131 \param[in] delta - size of deviation from linear
132 \param[in] hmin - minimum spacing
133
134 \return approximation arguments
135 *************************************************************/
136 struct LinElemExpAopts *
lin_elem_exp_aopts_alloc_adapt(size_t N,double * x,double lb,double ub,double delta,double hmin)137 lin_elem_exp_aopts_alloc_adapt(size_t N, double * x,
138 double lb, double ub,
139 double delta, double hmin)
140 {
141 struct LinElemExpAopts * aopts = NULL;
142 aopts = malloc(sizeof(struct LinElemExpAopts));
143 if (aopts == NULL){
144 fprintf(stderr,"Memory error allocate LinElemExpAopts\n");
145 exit(1);
146 }
147 aopts->num_nodes= N;
148 aopts->node_alloc = 0;
149 aopts->lb = lb;
150 aopts->ub = ub;
151 if (N > 0){
152 aopts->nodes = x;
153 }
154 else{
155 aopts->nodes = NULL;
156 }
157 aopts->adapt = 1;
158 aopts->delta = delta;
159 aopts->hmin = hmin;
160
161 return aopts;
162 }
163
164 /********************************************************//**
165 Free memory allocated to approximation arguments
166
167 \param[in,out] aopts - approximation arguments
168 *************************************************************/
lin_elem_exp_aopts_free(struct LinElemExpAopts * aopts)169 void lin_elem_exp_aopts_free(struct LinElemExpAopts * aopts)
170 {
171 if (aopts != NULL){
172 if (aopts->node_alloc == 1){
173 free(aopts->nodes); aopts->nodes = NULL;
174 }
175 free(aopts); aopts = NULL;
176 }
177 }
178
179 /********************************************************//**
180 (deep)Free memory allocated to approximation arguments
181
182 \param[in,out] aopts - approximation arguments
183 *************************************************************/
lin_elem_exp_aopts_free_deep(struct LinElemExpAopts ** aopts)184 void lin_elem_exp_aopts_free_deep(struct LinElemExpAopts ** aopts)
185 {
186 if (*aopts != NULL){
187 if ((*aopts)->node_alloc == 1){
188 free((*aopts)->nodes); (*aopts)->nodes = NULL;
189 }
190 free(*aopts); *aopts = NULL;
191 }
192 }
193
194 /********************************************************//**
195 Get number of nodes
196 *************************************************************/
lin_elem_exp_aopts_get_num_nodes(const struct LinElemExpAopts * aopts)197 size_t lin_elem_exp_aopts_get_num_nodes(const struct LinElemExpAopts * aopts)
198 {
199 assert (aopts != NULL);
200 return aopts->num_nodes;
201 }
202
203 /********************************************************//**
204 Get the lower bound
205 *************************************************************/
lin_elem_exp_aopts_get_lb(const struct LinElemExpAopts * aopts)206 double lin_elem_exp_aopts_get_lb(const struct LinElemExpAopts * aopts)
207 {
208 assert (aopts != NULL);
209 return aopts->lb;
210 }
211
212 /********************************************************//**
213 Get the upper bound
214 *************************************************************/
lin_elem_exp_aopts_get_ub(const struct LinElemExpAopts * aopts)215 double lin_elem_exp_aopts_get_ub(const struct LinElemExpAopts * aopts)
216 {
217 assert (aopts != NULL);
218 return aopts->ub;
219 }
220
221 /********************************************************//**
222 Sets new nodes (by reference) for approximation options.
223 frees old ones if
224 previously allocated
225
226 \param[in,out] aopts - approximation arguments
227 \param[in] N - number of nodes
228 \param[in] nodes - nodes
229 *************************************************************/
lin_elem_exp_aopts_set_nodes(struct LinElemExpAopts * aopts,size_t N,double * nodes)230 void lin_elem_exp_aopts_set_nodes(struct LinElemExpAopts * aopts,
231 size_t N, double * nodes)
232 {
233
234 if (aopts == NULL){
235 fprintf(stderr,"Must allocate LinElemExpAopts before setting nodes\n");
236 exit(1);
237 }
238 if (aopts->node_alloc == 1){
239 free(aopts->nodes); aopts->nodes = NULL;
240 }
241 aopts->num_nodes= N;
242 aopts->node_alloc = 0;
243 if (N > 0){
244 aopts->nodes = nodes;
245 }
246 }
247
248 /********************************************************//**
249 Sets new nodes for approximation options.
250 frees old ones if
251 previously allocated
252
253 \param[in,out] aopts - approximation arguments
254 \param[in] N - number of nodes
255 \param[in] nodes - nodes
256 *************************************************************/
lin_elem_exp_aopts_set_nodes_copy(struct LinElemExpAopts * aopts,size_t N,const double * nodes)257 void lin_elem_exp_aopts_set_nodes_copy(struct LinElemExpAopts * aopts,
258 size_t N, const double * nodes)
259 {
260
261 if (aopts == NULL){
262 fprintf(stderr,"Must allocate LinElemExpAopts before setting nodes\n");
263 exit(1);
264 }
265 if (aopts->node_alloc == 1){
266 free(aopts->nodes); aopts->nodes = NULL;
267 }
268 aopts->num_nodes = N;
269 aopts->node_alloc = 1;
270 aopts->nodes = calloc_double(N);
271 memmove(aopts->nodes,nodes,N*sizeof(double));
272
273 }
274
275 /********************************************************//**
276 Set adaptation
277
278 \param[in,out] aopts - approximation arguments
279 \param[in] delta - maximum deviation from linear
280 \param[in] hmin - minimum distance between nodes
281 *************************************************************/
lin_elem_exp_aopts_set_adapt(struct LinElemExpAopts * aopts,double delta,double hmin)282 void lin_elem_exp_aopts_set_adapt(struct LinElemExpAopts * aopts,
283 double delta, double hmin)
284 {
285 if (aopts == NULL){
286 fprintf(stderr,"Must allocate LinElemExpAopts before turning on adapting\n");
287 exit(1);
288 }
289 aopts->adapt = 1;
290 aopts->delta = delta;
291 aopts->hmin = hmin;
292 }
293 /********************************************************//**
294 Setting delta for adaptation
295
296 \param[in,out] aopts - approximation arguments
297 \param[in] delta - maximum deviation from linear
298 *************************************************************/
lin_elem_exp_aopts_set_delta(struct LinElemExpAopts * aopts,double delta)299 void lin_elem_exp_aopts_set_delta(struct LinElemExpAopts * aopts, double delta)
300 {
301 if (aopts == NULL){
302 fprintf(stderr,"Must allocate LinElemExpAopts before setting delta\n");
303 exit(1);
304 }
305 aopts->delta = delta;
306 }
307
308 /********************************************************//**
309 Setting hmin for adaptation
310
311 \param[in,out] aopts - approximation arguments
312 \param[in] hmin - minimum distance between nodes
313 *************************************************************/
lin_elem_exp_aopts_set_hmin(struct LinElemExpAopts * aopts,double hmin)314 void lin_elem_exp_aopts_set_hmin(struct LinElemExpAopts * aopts, double hmin)
315 {
316 if (aopts == NULL){
317 fprintf(stderr,"Must allocate LinElemExpAopts before setting hmin\n");
318 exit(1);
319 }
320 aopts->hmin = hmin;
321 }
322
323
324 ///////////////////////////////////////////////
325
326
327 /********************************************************//**
328 * Get number of free parameters
329 *
330 * \note Can change this later to include knot locations
331 *************************************************************/
lin_elem_exp_aopts_get_nparams(const struct LinElemExpAopts * lexp)332 size_t lin_elem_exp_aopts_get_nparams(const struct LinElemExpAopts* lexp)
333 {
334 assert (lexp != NULL);
335 return lexp->num_nodes;
336 }
337
338 /********************************************************//**
339 * Set number of free parameters
340 *
341 * \note Can change this later to include knot locations
342 *************************************************************/
lin_elem_exp_aopts_set_nparams(struct LinElemExpAopts * lexp,size_t num)343 void lin_elem_exp_aopts_set_nparams(struct LinElemExpAopts* lexp, size_t num)
344 {
345 assert (lexp != NULL);
346 lexp->num_nodes = num;
347 fprintf(stderr,"Warning: setting new nparams in linelem aopts. Do I need to adjust the node locations?\n");
348 }
349
350
351 /********************************************************//**
352 * Allocate a Linear Element Expansion
353 *
354 * \return Allocated Expansion
355 *************************************************************/
lin_elem_exp_alloc()356 struct LinElemExp * lin_elem_exp_alloc()
357 {
358
359 struct LinElemExp * p = NULL;
360 if ( NULL == (p = malloc(sizeof(struct LinElemExp)))){
361 fprintf(stderr, "failed to allocate memory for LinElemExp.\n");
362 exit(1);
363 }
364 p->num_nodes = 0;
365 p->nodes = NULL;
366 p->coeff = NULL;
367 p->diff = NULL;
368 p->inner = NULL;
369
370 p->Lp = NULL;
371 return p;
372 }
373
374 /********************************************************//**
375 Make a copy of a linear element expansion
376
377 \param[in] lexp - linear element expansion to copy
378
379 \return linear element expansion
380 *************************************************************/
lin_elem_exp_copy(struct LinElemExp * lexp)381 struct LinElemExp * lin_elem_exp_copy(struct LinElemExp * lexp)
382 {
383
384 struct LinElemExp * p = NULL;
385 if (lexp != NULL){
386 p = lin_elem_exp_alloc();
387 p->num_nodes = lexp->num_nodes;
388 if (lexp->nodes != NULL){
389 p->nodes = calloc_double(p->num_nodes);
390 memmove(p->nodes,lexp->nodes,p->num_nodes*sizeof(double));
391 }
392 if (lexp->coeff != NULL){
393 p->coeff = calloc_double(p->num_nodes);
394 memmove(p->coeff,lexp->coeff,p->num_nodes*sizeof(double));
395 }
396
397 if (lexp->Lp != NULL){
398 p->Lp = calloc_double(p->num_nodes*p->num_nodes);
399 memmove(p->Lp, lexp->Lp, p->num_nodes*p->num_nodes * sizeof(double));
400 }
401 }
402 return p;
403 }
404
405 /********************************************************//**
406 * Free a Linear Element Expansion
407 *
408 * \param[in,out] exp - expansion to free
409 *************************************************************/
lin_elem_exp_free(struct LinElemExp * exp)410 void lin_elem_exp_free(struct LinElemExp * exp)
411 {
412 if (exp != NULL){
413 free(exp->nodes); exp->nodes = NULL;
414 free(exp->coeff); exp->coeff = NULL;
415 free(exp->diff); exp->diff = NULL;
416 free(exp->Lp); exp->Lp = NULL;
417 free(exp); exp = NULL;
418 }
419 }
420
421 /* static void compute_inner_helper(struct LinElemExp * exp) */
422 /* { */
423 /* if (exp != NULL){ */
424 /* if (exp->coeff != NULL){ */
425 /* if (exp->diff != NULL){ */
426 /* free(exp->diff); exp->diff = NULL; */
427 /* } */
428
429 /* //left */
430 /* exp->diff = calloc_double(exp->num_nodes); */
431 /* for (size_t ii = 0; ii < exp->num_nodes-1; ii++){ */
432 /* exp->diff[ii] = exp->coeff[ii+1]-exp->coeff[ii]; */
433 /* } */
434 /* } */
435 /* } */
436 /* } */
437
438 /********************************************************//**
439 Initialize a linear element expansion
440
441 \param[in] num_nodes - number of nodes/basis functions
442 \param[in] nodes - nodes
443 \param[in] coeff - weights on nodes
444
445 \return linear element expansion
446
447 \note
448 makes a copy of nodes and coefficients
449 *************************************************************/
lin_elem_exp_init(size_t num_nodes,double * nodes,double * coeff)450 struct LinElemExp * lin_elem_exp_init(size_t num_nodes, double * nodes,
451 double * coeff)
452 {
453 struct LinElemExp * lexp = lin_elem_exp_alloc();
454 assert (num_nodes > 1);
455 lexp->num_nodes = num_nodes;
456 lexp->nodes = calloc_double(num_nodes);
457 lexp->coeff = calloc_double(num_nodes);
458 memmove(lexp->nodes,nodes,num_nodes*sizeof(double));
459 memmove(lexp->coeff,coeff,num_nodes*sizeof(double));
460 /* compute_diff(lexp); */
461 return lexp;
462 }
463
464 /********************************************************//**
465 Initialize a linear element expansion with particular parameters
466
467 \param[in] opts - options
468 \param[in] dim - number of parameters
469 \param[in] param - parameters
470
471 \return linear element expansion
472
473 \note
474 makes a copy of nodes and coefficients
475 *************************************************************/
476 struct LinElemExp *
lin_elem_exp_create_with_params(struct LinElemExpAopts * opts,size_t dim,const double * param)477 lin_elem_exp_create_with_params(struct LinElemExpAopts * opts,
478 size_t dim, const double * param)
479 {
480 assert (opts != NULL);
481 assert (opts->num_nodes == dim);
482 assert (opts->nodes != NULL);
483 struct LinElemExp * lexp = lin_elem_exp_alloc();
484
485 lexp->num_nodes = dim;;
486 lexp->nodes = calloc_double(dim);
487 lexp->coeff = calloc_double(dim);
488
489 memmove(lexp->nodes,opts->nodes,dim*sizeof(double));
490 memmove(lexp->coeff,param,dim*sizeof(double));
491
492 return lexp;
493 }
494
495 /********************************************************//**
496 Get number of nodes
497 *************************************************************/
lin_elem_exp_get_num_nodes(const struct LinElemExp * lexp)498 size_t lin_elem_exp_get_num_nodes(const struct LinElemExp * lexp)
499 {
500 assert (lexp != NULL);
501 return lexp->num_nodes;
502 }
503
504 /********************************************************//**
505 Get number of params
506 *************************************************************/
lin_elem_exp_get_num_params(const struct LinElemExp * lexp)507 size_t lin_elem_exp_get_num_params(const struct LinElemExp * lexp)
508 {
509 assert (lexp != NULL);
510 return lexp->num_nodes;
511 }
512
513 /********************************************************//**
514 Get the parameters of a linear element expansion
515
516 \param[in] lexp - expansion
517 \param[in] param - parameters
518
519 \returns number of parameters
520 *************************************************************/
lin_elem_exp_get_params(const struct LinElemExp * lexp,double * param)521 size_t lin_elem_exp_get_params(const struct LinElemExp * lexp, double * param)
522 {
523 assert (lexp != NULL);
524 memmove(param,lexp->coeff,lexp->num_nodes*sizeof(double));
525 return lexp->num_nodes;
526 }
527
528 /********************************************************//**
529 Get a reference to parameters of a linear element expansion
530
531 \param[in] lexp - expansion
532 \param[in,out] nparam - parameters
533
534 \returns reference to parameters
535 *************************************************************/
lin_elem_exp_get_params_ref(const struct LinElemExp * lexp,size_t * nparam)536 double * lin_elem_exp_get_params_ref(const struct LinElemExp * lexp, size_t * nparam)
537 {
538 assert (lexp != NULL);
539 *nparam = lexp->num_nodes;
540 return lexp->coeff;
541 }
542
543 /********************************************************//**
544 Update the parameters (coefficients) for a linear element expansion
545
546 \param[in] lexp - expansion
547 \param[in] dim - number of parameters
548 \param[in] param - parameters
549
550 \returns 0 if succesfull
551 *************************************************************/
552 int
lin_elem_exp_update_params(struct LinElemExp * lexp,size_t dim,const double * param)553 lin_elem_exp_update_params(struct LinElemExp * lexp,
554 size_t dim, const double * param)
555 {
556 assert (lexp != NULL);
557 assert (lexp->num_nodes == dim);
558 for (size_t ii = 0; ii < dim; ii++){
559 lexp->coeff[ii] = param[ii];
560 }
561
562 return 0;
563 }
564
565 /********************************************************//**
566 * Serialize a LinElemExp
567 *
568 * \param[in] ser - location at which to serialize
569 * \param[in] f - function to serialize
570 * \param[in,out] totSizeIn - if not NULL then return size of struct
571 * if NULL then serialiaze
572 *
573 * \return pointer to the end of the serialization
574 *************************************************************/
575 unsigned char *
serialize_lin_elem_exp(unsigned char * ser,struct LinElemExp * f,size_t * totSizeIn)576 serialize_lin_elem_exp(unsigned char * ser, struct LinElemExp * f,
577 size_t * totSizeIn)
578 {
579
580 // 3 * sizeof(size_t)-- 1 for num_nodes, 2 for sizes of nodes and coeffs
581 size_t totsize = 3*sizeof(size_t) + 2 * f->num_nodes*sizeof(double);
582
583 if (totSizeIn != NULL){
584 *totSizeIn = totsize;
585 return ser;
586 }
587
588 unsigned char * ptr = serialize_size_t(ser, f->num_nodes);
589 //serializing a pointer also serializes its size
590 ptr = serialize_doublep(ptr, f->nodes, f->num_nodes);
591 ptr = serialize_doublep(ptr, f->coeff, f->num_nodes);
592 return ptr;
593
594 }
595
596 /********************************************************//**
597 * Deserialize a linear element expansion
598 *
599 * \param[in] ser - serialized structure
600 * \param[in,out] f - function
601 *
602 * \return ptr - ser + number of bytes of poly expansion
603 *************************************************************/
deserialize_lin_elem_exp(unsigned char * ser,struct LinElemExp ** f)604 unsigned char * deserialize_lin_elem_exp(unsigned char * ser,
605 struct LinElemExp ** f)
606 {
607
608 *f = lin_elem_exp_alloc();
609
610 unsigned char * ptr = ser;
611 ptr = deserialize_size_t(ptr,&((*f)->num_nodes));
612 ptr = deserialize_doublep(ptr, &((*f)->nodes), &((*f)->num_nodes));
613 ptr = deserialize_doublep(ptr, &((*f)->coeff), &((*f)->num_nodes));
614
615 return ptr;
616 }
617
618 /********************************************************//**
619 * Get index of the node immediately to the left of x
620 *************************************************************/
lin_elem_exp_find_interval(const struct LinElemExp * f,double x)621 size_t lin_elem_exp_find_interval(const struct LinElemExp * f, double x)
622 {
623
624 size_t indmin = 0;
625 size_t indmax = f->num_nodes-1;
626 size_t indmid = indmin + (indmax - indmin)/2;
627
628 while (indmid != indmin){
629 if (fabs(x - f->nodes[indmid]) <= 1e-15){
630 /* printf("eventually here!\n"); */
631 return indmid;
632 }
633 else if (x < f->nodes[indmid]){
634 indmax = indmid;
635 }
636 else { // x > f->nodes[indmid]
637 indmin = indmid;
638 }
639 indmid = indmin + (indmax-indmin)/2;
640 // printf("indmid = %zu, indmin=%zu,indmax=%zu\n",indmid,indmin,indmax);
641 }
642
643 return indmin;
644 }
645
646
647 /********************************************************//**
648 * Evaluate the lin elem expansion
649 *
650 * \param[in] f - function
651 * \param[in] x - location
652 *
653 * \return value
654 *************************************************************/
lin_elem_exp_eval(const struct LinElemExp * f,double x)655 double lin_elem_exp_eval(const struct LinElemExp * f, double x)
656 {
657 if ((x < f->nodes[0]) || (x > f->nodes[f->num_nodes-1])){
658 return 0.0;
659 }
660
661 size_t indmin = lin_elem_exp_find_interval(f,x);
662 /* printf("indmin = %zu\n",indmin); */
663 /* printf("x = %G\n",x); */
664
665 if (fabs(x - f->nodes[indmin]) <= 1e-15){
666 return f->coeff[indmin];
667 }
668
669 double den = f->nodes[indmin+1]-f->nodes[indmin];
670 double t = (f->nodes[indmin+1]-x)/den;
671 double value = f->coeff[indmin] * t + f->coeff[indmin+1]*(1.0-t);
672 return value;
673 }
674
675 /********************************************************//**
676 * Evaluate the derivative of a linear lin elem expansion
677 *
678 * \param[in] f - function
679 * \param[in] x - location
680 *
681 * \return value
682 *************************************************************/
lin_elem_exp_deriv_eval(const struct LinElemExp * f,double x)683 double lin_elem_exp_deriv_eval(const struct LinElemExp * f, double x)
684 {
685 if ((x < f->nodes[0]) || (x > f->nodes[f->num_nodes-1])){
686 return 0.0;
687 }
688
689 size_t indmin = lin_elem_exp_find_interval(f,x);
690 /* printf("indmin = %zu\n",indmin); */
691 /* printf("x = %G\n",x); */
692
693 if (fabs(x - f->nodes[indmin]) <= 1e-15){
694 if (indmin == 0){
695 double plus = f->coeff[indmin+1];
696 double curr = f->coeff[indmin];
697 return (plus-curr)/(f->nodes[indmin+1]-f->nodes[indmin]);
698 }
699 else if (indmin < f->num_nodes-1){
700 double plus = f->coeff[indmin+1];
701 double minus = f->coeff[indmin-1];
702 return (plus-minus)/(f->nodes[indmin+1]-f->nodes[indmin-1]);
703 }
704 else{
705 double curr = f->coeff[indmin];
706 double minus = f->coeff[indmin-1];
707 return (curr-minus)/(f->nodes[indmin]-f->nodes[indmin-1]);
708 }
709 }
710
711 double den = f->nodes[indmin+1]-f->nodes[indmin];
712 double dtdx = -1.0/den;
713
714 double value = (f->coeff[indmin]- f->coeff[indmin+1])*dtdx;
715 return value;
716 }
717
718 /********************************************************//**
719 * Evaluate the lin elem expansion
720 *
721 * \param[in] poly - function
722 * \param[in] N - number of evaluations
723 * \param[in] x - location at which to evaluate
724 * \param[in] incx - increment of x
725 * \param[in,out] y - allocated space for evaluations
726 * \param[in] incy - increment of y
727 ************************************************************/
lin_elem_exp_evalN(const struct LinElemExp * poly,size_t N,const double * x,size_t incx,double * y,size_t incy)728 void lin_elem_exp_evalN(const struct LinElemExp * poly, size_t N,
729 const double * x, size_t incx, double * y, size_t incy)
730 {
731 for (size_t ii = 0; ii < N; ii++){
732 y[ii*incy] = lin_elem_exp_eval(poly,x[ii*incx]);
733 }
734 }
735
736
737
738 /********************************************************//**
739 * Get the value of the expansion at a particular node
740 *
741 * \param[in] f - function
742 * \param[in] node - location
743 *
744 * \return value
745 *************************************************************/
lin_elem_exp_get_nodal_val(const struct LinElemExp * f,size_t node)746 double lin_elem_exp_get_nodal_val(const struct LinElemExp * f, size_t node)
747 {
748 assert (f != NULL);
749 assert (node < f->num_nodes);
750 return f->coeff[node];
751 }
752
753
754
755 /********************************************************//**
756 * Take a derivative same nodes,
757 *
758 * \param[in] f - function
759 *
760 * \return derivative
761 *************************************************************/
lin_elem_exp_deriv(const struct LinElemExp * f)762 struct LinElemExp * lin_elem_exp_deriv(const struct LinElemExp * f)
763 {
764 struct LinElemExp * le = lin_elem_exp_init(f->num_nodes,
765 f->nodes,f->coeff);
766
767 assert(f->num_nodes > 1);
768 if (f->num_nodes <= 5){
769 //first order for first part
770 le->coeff[0] = (f->coeff[1]-f->coeff[0])/
771 (f->nodes[1]-f->nodes[0]);
772 // second order centeral
773 for (size_t ii = 1; ii < le->num_nodes-1; ii++){
774 le->coeff[ii] = (f->coeff[ii+1]-f->coeff[ii-1])/
775 (f->nodes[ii+1]-f->nodes[ii-1]);
776 }
777 // first order last
778 le->coeff[le->num_nodes-1] =
779 (f->coeff[le->num_nodes-1] - f->coeff[le->num_nodes-2]) /
780 (f->nodes[f->num_nodes-1] - f->nodes[f->num_nodes-2]);
781 }
782 else{ // mostly fourth order
783
784 //first order for first part
785 le->coeff[0] = (f->coeff[1]-f->coeff[0])/
786 (f->nodes[1]-f->nodes[0]);
787 // second order
788 le->coeff[1] = (f->coeff[2]-f->coeff[0]) / (f->nodes[2]-f->nodes[0]);
789
790 // fourth order central
791 for (size_t ii = 2; ii < le->num_nodes-2; ii++){
792 double sum_all = f->nodes[ii+2]-f->nodes[ii-2];
793 double sum_mid = f->nodes[ii+1]-f->nodes[ii-1];
794 double r_num = pow(f->nodes[ii+2]-f->nodes[ii],3) +
795 pow(f->nodes[ii] - f->nodes[ii-2],3);
796 double r_den = pow(f->nodes[ii+1]-f->nodes[ii],3) +
797 pow(f->nodes[ii]-f->nodes[ii-1],3);
798
799 double r = r_num/r_den;
800
801 double den = r*sum_mid - sum_all;
802 double num = r*(f->coeff[ii+1]-f->coeff[ii-1]) -
803 f->coeff[ii+2] + f->coeff[ii-2];
804 le->coeff[ii] = num/den;
805 }
806
807 // second order
808 le->coeff[f->num_nodes-2] = (f->coeff[f->num_nodes-1] -
809 f->coeff[f->num_nodes-3]) /
810 (f->nodes[f->num_nodes-1] -
811 f->nodes[f->num_nodes-3]);
812
813 // first order last
814 le->coeff[le->num_nodes-1] =
815 (f->coeff[le->num_nodes-1] - f->coeff[le->num_nodes-2]) /
816 (f->nodes[f->num_nodes-1] - f->nodes[f->num_nodes-2]);
817 }
818
819 return le;
820 }
821
822 /********************************************************//**
823 * Take a second derivative same nodes,
824 *
825 * \param[in] f - function
826 *************************************************************/
lin_elem_exp_dderiv(const struct LinElemExp * f)827 struct LinElemExp * lin_elem_exp_dderiv(const struct LinElemExp * f)
828 {
829
830 struct LinElemExp * temp = lin_elem_exp_deriv(f);
831 struct LinElemExp * out = lin_elem_exp_deriv(temp);
832 lin_elem_exp_free(temp); temp = NULL;
833 return out;
834 }
835
lin_elem_exp_dderiv_initialize_fourier(struct LinElemExp * f)836 void lin_elem_exp_dderiv_initialize_fourier(struct LinElemExp * f)
837 {
838 double dx = f->nodes[1] - f->nodes[0]; // assumes constant dx for now.
839 for (size_t ii = 1; ii < f->num_nodes-1; ii++){
840 double dx2 = f->nodes[ii+1] - f->nodes[ii];
841 if (fabs(dx2-dx) > 1e-15){
842 fprintf(stderr, "lin_elem_exp_dderiv_periodic only defined for uniform spacing\n");
843 fprintf(stderr, "%3.15G %3.15G\n", dx, dx2);
844 exit(1);
845 }
846
847 }
848
849 if (f->Lp != NULL){
850 free(f->Lp); f->Lp = NULL;
851 }
852
853
854 size_t nx = f->num_nodes;
855 double ub = f->nodes[nx-1] + dx; // because periodic!
856 double lb = f->nodes[0];
857 /* double ub = -lb; */
858
859 double * x = f->nodes;
860
861 /* printf("nodes = "); dprint(nx, x); */
862
863 /* double dp = 2.0 * M_PI / (ub - lb); */
864 double dp = 2.0 * M_PI / (ub - lb);
865 double * p = calloc_double(nx);
866 f->Lp = calloc_double(nx * nx);
867 for (size_t ii = 0; ii < nx; ii++){
868 for (size_t jj = 0; jj < nx; jj++){
869 f->Lp[ii*nx + jj] = 0.0;
870 }
871 }
872
873 for (size_t ii = 0; ii < nx; ii++){
874 p[ii] = dp * ii - dp * nx / 2.0;
875 }
876
877 for (size_t ll=0; ll<nx; ll++){
878 for (size_t jj=0; jj<nx; jj++){
879 for (size_t kk=0; kk<nx; kk++){
880 double update = creal(cexp((_Complex double)I*(x[jj]-x[ll])*p[kk])*pow(p[kk],2)*dx*dp/(2*M_PI));
881 f->Lp[ll * nx + jj] = f->Lp[ll*nx + jj] - update;
882 }
883 }
884 }
885 free(p); p = NULL;
886 }
887
888 /********************************************************//**
889 * Take a second derivative same nodes,
890 *
891 * \param[in] f - function
892 *************************************************************/
893 struct LinElemExp *
lin_elem_exp_dderiv_periodic(struct LinElemExp * f)894 lin_elem_exp_dderiv_periodic(struct LinElemExp * f)
895 {
896
897 assert (f != NULL);
898
899 if (f->Lp == NULL){
900 lin_elem_exp_dderiv_initialize_fourier(f);
901 }
902
903 // assumes constant dx for now.
904 size_t nx = f->num_nodes;
905 double dx = f->nodes[1] - f->nodes[0];
906 for (size_t ii = 1; ii < f->num_nodes-1; ii++){
907 double dx2 = f->nodes[ii+1] - f->nodes[ii];
908 if (fabs(dx2-dx) > 1e-15){
909 fprintf(stderr, "lin_elem_exp_dderiv_periodic only defined for uniform spacing\n");
910 fprintf(stderr, "%3.15G %3.15G\n", dx, dx2);
911 exit(1);
912 }
913
914 }
915
916 /* printf("Lp = \n"); */
917 /* for (size_t ii = 0; ii < nx; ii++){ */
918 /* for (size_t jj = 0; jj < nx; jj++){ */
919 /* printf("%3.5G ", Lp[ii * nx + jj]); */
920 /* } */
921 /* printf("\n"); */
922 /* } */
923 /* printf("\n"); */
924
925 /* for (size_t ii = 0; ii < ) */
926 double * new_vals = calloc_double(nx);
927 for (size_t jj = 0; jj < nx; jj++){
928 new_vals[jj] = 0.0;
929 for (size_t kk = 0; kk < nx; kk++){
930 new_vals[jj] += f->Lp[kk*nx + jj] * f->coeff[kk];
931 }
932 }
933 struct LinElemExp * out = lin_elem_exp_init(nx, f->nodes, new_vals);
934
935 free(new_vals); new_vals = NULL;
936
937 return out;
938 }
939
940
941
942 /********************************************************//*
943 * Evaluate the gradient of a linear element expansion
944 * with respect to the coefficients of each basis
945 *
946 * \param[in] f - polynomial expansion
947 * \param[in] nx - number of x points
948 * \param[in] x - location at which to evaluate
949 * \param[in,out] grad - gradient values (N,nx)
950 *
951 * \return out - value
952 *************************************************************/
lin_elem_exp_param_grad_eval(struct LinElemExp * f,size_t nx,const double * x,double * grad)953 int lin_elem_exp_param_grad_eval(
954 struct LinElemExp * f, size_t nx, const double * x, double * grad)
955 {
956
957 size_t nparam = f->num_nodes;
958 /* assert (nparam == lexp->nnodes); */
959 for (size_t ii = 0; ii < nx; ii++){
960 size_t indmin = lin_elem_exp_find_interval(f,x[ii]);
961
962 /* printf("x = %G, indmin = %zu\n",x[ii],indmin); */
963 for (size_t jj = 0; jj < indmin; jj++)
964 {
965 grad[ii*nparam+jj] = 0.0;
966 }
967
968 for (size_t jj = indmin+2; jj < nparam; jj++)
969 {
970 grad[ii*nparam+jj] = 0.0;
971 }
972
973 double den = f->nodes[indmin+1]-f->nodes[indmin];
974 double t = (f->nodes[indmin+1]-x[ii])/den;
975
976 grad[ii*nparam+indmin] = t;
977 grad[ii*nparam+indmin+1] = 1.0-t;
978
979 /* double value = f->coeff[indmin] * t + f->coeff[indmin+1]*(1.0-t); */
980 /* fprintf(stderr,"Lin_elem_exp_param_grad_eval IS NOT YET IMPLEMENTED\n"); */
981 /* exit(1); */
982 }
983
984 if (grad != NULL){
985 /* dprint(nparam*nx,grad); */
986 /* exit(1); */
987 }
988 return 0;
989 }
990
991 /********************************************************//*
992 * Evaluate the gradient of an linear element expansion
993 * with respect to the coefficients of each basis
994 *
995 * \param[in] f - polynomial expansion
996 * \param[in] x - location at which to evaluate
997 * \param[in,out] grad - gradient values (N)
998 *
999 * \return out - value
1000 *************************************************************/
lin_elem_exp_param_grad_eval2(struct LinElemExp * f,double x,double * grad)1001 double lin_elem_exp_param_grad_eval2(
1002 struct LinElemExp * f, double x, double * grad)
1003 {
1004 assert (grad != NULL);
1005
1006 size_t nparam = lin_elem_exp_get_num_params(f);
1007 size_t indmin = lin_elem_exp_find_interval(f,x);
1008 for (size_t jj = 0; jj < indmin; jj++)
1009 {
1010 grad[jj] = 0.0;
1011 }
1012
1013 for (size_t jj = indmin+2; jj < nparam; jj++)
1014 {
1015 grad[jj] = 0.0;
1016 }
1017
1018 double den = f->nodes[indmin+1]-f->nodes[indmin];
1019 double t = (f->nodes[indmin+1]-x)/den;
1020
1021 grad[indmin] = t;
1022 grad[indmin+1] = 1.0-t;
1023
1024 double value = f->coeff[indmin] * t + f->coeff[indmin+1]*(1.0-t);
1025
1026 return value;
1027 }
1028
1029 /********************************************************//**
1030 Take a gradient of the squared norm
1031 with respect to its parameters, and add a scaled version
1032 of this gradient to *grad*
1033
1034 \param[in] f - linear element expansion
1035 \param[in] scale - scaling for additional gradient
1036 \param[in,out] grad - gradient, on output adds scale * new_grad
1037
1038 \return 0 - success, 1 -failure
1039
1040 ************************************************************/
1041 int
lin_elem_exp_squared_norm_param_grad(const struct LinElemExp * f,double scale,double * grad)1042 lin_elem_exp_squared_norm_param_grad(const struct LinElemExp * f,
1043 double scale, double * grad)
1044 {
1045 if (grad == NULL){
1046 return 0;
1047 }
1048
1049 double dx = f->nodes[1]-f->nodes[0];
1050 double term1 = ((f->coeff[0]-1.0)/3.0 + f->coeff[1]/2.0) * dx;
1051 grad[0] += 2.0 * scale * term1;
1052 double dx2,df;
1053 for (size_t ii = 1; ii < f->num_nodes-1; ii++){
1054 // left side of ii
1055 df = f->coeff[ii] - f->coeff[ii-1];
1056 grad[ii] += 2.0 * scale * (0.5 * f->coeff[ii] * dx + 1.0/3.0 * df * dx);
1057
1058 // right side
1059 dx2 = dx * dx;
1060 term1 = ((f->coeff[ii]-1.0)/3.0 + f->coeff[ii+1]/2.0) * dx;
1061 grad[ii] += 2.0 * scale * term1;
1062
1063 dx = dx2;
1064 }
1065
1066 // left side of last node node
1067 size_t ii = f->num_nodes-1;
1068 df = f->coeff[ii] - f->coeff[ii-1];
1069 grad[ii] += 2.0 * scale * (0.5 * f->coeff[ii] * dx + 1.0/3.0 * df * dx);
1070
1071 return 0;
1072 }
1073
1074 /********************************************************//**
1075 * Integrate the Linear Element Approximation
1076 *
1077 * \param[in] f - function
1078 *
1079 * \return integral
1080 *************************************************************/
lin_elem_exp_integrate(const struct LinElemExp * f)1081 double lin_elem_exp_integrate(const struct LinElemExp * f)
1082 {
1083
1084 assert (f->num_nodes>1 );
1085 double dx = f->nodes[1]-f->nodes[0];
1086 double integral = f->coeff[0] * dx * 0.5;
1087 /* printf("0: integrand(%3.15g) = %3.15G\n", f->nodes[0], integral); */
1088 for (size_t ii = 1; ii < f->num_nodes-1;ii++){
1089 dx = f->nodes[ii+1]-f->nodes[ii-1];
1090 integral += f->coeff[ii] * dx * 0.5;
1091 /* double newval = f->coeff[ii]*dx*0.5; */
1092 /* printf("%zu: integrand(%3.15g) = %3.15G\n", ii, f->nodes[ii], newval); */
1093 }
1094 dx = f->nodes[f->num_nodes-1]-f->nodes[f->num_nodes-2];
1095 integral += f->coeff[f->num_nodes-1] * dx * 0.5;
1096 return integral;
1097 }
1098
lin_elem_exp_integrate_weighted(const struct LinElemExp * f)1099 double lin_elem_exp_integrate_weighted(const struct LinElemExp * f)
1100 {
1101 (void)(f);
1102 NOT_IMPLEMENTED_MSG("lin_elem_exp_integrate_weighted")
1103 return 0.0;
1104 }
1105
1106
1107 /********************************************************//**
1108 * Determine if two functions have the same discretization
1109 *
1110 * \param[in] f - function
1111 * \param[in] g - function
1112 *
1113 * \return 1 - yes
1114 * 0 - no
1115 *************************************************************/
lin_elem_sdiscp(const struct LinElemExp * f,const struct LinElemExp * g)1116 static int lin_elem_sdiscp(const struct LinElemExp * f,
1117 const struct LinElemExp * g)
1118 {
1119 if (f->num_nodes != g->num_nodes){
1120 return 0;
1121 }
1122 for (size_t ii = 0; ii < f->num_nodes;ii++){
1123 if (fabs(f->nodes[ii] - g->nodes[ii]) > 1e-15){
1124 return 0;
1125 }
1126 }
1127 return 1;
1128 }
1129
1130 /********************************************************//**
1131 * Compute Integrals necessary for inner products for an element
1132 * \f[
1133 * left = \int_{x_1}^{x_2} (x_2-x)^2 dx
1134 * \f]
1135 * \f[
1136 * mixed = \int_{x_1}^{x_2} (x_2-x)(x-x_1) dx
1137 * \f]
1138 * \f[
1139 * right = \int_{x_1}^{x_2} (x-x_1)^2 dx
1140 * \f]
1141 *
1142 * \param[in] x1 - left boundary of element
1143 * \param[in] x2 - right boundary of element
1144 * \param[in,out] left - first integral
1145 * \param[in,out] mixed - second integral
1146 * \param[in,out] right - third integral
1147 *************************************************************/
1148 /* static void lin_elem_exp_inner_element( */
1149 /* double x1, double x2, double * left, double * mixed, */
1150 /* double * right) */
1151 /* { */
1152 /* double dx = (x2-x1); */
1153 /* double x2sq = x2*x2; */
1154 /* double x1sq = x1*x1; */
1155 /* double x2cu = x2sq * x2; */
1156 /* double x1cu = x1sq * x1; */
1157 /* double dx2 = x2sq - x1sq; //pow(x2,2)-pow(x1,2); */
1158 /* double dx3 = (x2cu - x1cu)/3.0; //(pow(x2,3)-pow(x1,3))/3.0; */
1159
1160 /* *left = x2sq*dx - x2*dx2 + dx3; // pow(x2,2)*dx - x2*dx2 + dx3; */
1161 /* *mixed = (x2+x1) * dx2/2.0 - x1*x2*dx - dx3; */
1162 /* *right = dx3 - x1*dx2 + x1sq * dx; //pow(x1,2)*dx; */
1163 /* } */
1164
1165 /********************************************************//**
1166 * Interpolate two linear element expansions onto the same grid
1167 * keeping only those nodes needed for inner product
1168 *
1169 * \param[in] f - function
1170 * \param[in] g - function
1171 * \param[in,out] x - interpolated nodes
1172 * \param[in,out] fvals - values of f
1173 * \param[in,out] gvals - values of g
1174
1175 * \return number of nodes
1176
1177 * \note
1178 x,fvals,gvals must all be previously allocaed with enough space
1179 at least f->num_nodes + g->num_nodes is enough for a
1180 conservative allocation
1181 *************************************************************/
lin_elem_exp_inner_same_grid(const struct LinElemExp * f,const struct LinElemExp * g,double * x,double * fvals,double * gvals)1182 static size_t lin_elem_exp_inner_same_grid(
1183 const struct LinElemExp * f, const struct LinElemExp * g, double * x,
1184 double * fvals, double * gvals)
1185 {
1186 size_t nnodes = 0;
1187 size_t inodef = 0;
1188 size_t inodeg = 0;
1189
1190 if (f->nodes[0] > g->nodes[g->num_nodes-1]){
1191 return 0;
1192 }
1193 if (g->nodes[0] > f->nodes[f->num_nodes-1])
1194 {
1195 return 0;
1196 }
1197 while(f->nodes[inodef+1] < g->nodes[0]){
1198 inodef++;
1199 }
1200 while (g->nodes[inodeg+1] < f->nodes[0]){
1201 inodeg++;
1202 }
1203
1204 while ((inodef < f->num_nodes) && (inodeg < g->num_nodes)){
1205 if (fabs(f->nodes[inodef] - g->nodes[inodeg]) < 1e-15){
1206 x[nnodes] = f->nodes[inodef];
1207 fvals[nnodes] = f->coeff[inodef];
1208 gvals[nnodes] = g->coeff[inodeg];
1209 inodef++;
1210 inodeg++;
1211 nnodes++;
1212 }
1213 else if (f->nodes[inodef] < g->nodes[inodeg]){
1214 x[nnodes] = f->nodes[inodef];
1215 fvals[nnodes] = f->coeff[inodef];
1216 gvals[nnodes] = lin_elem_exp_eval(g,x[nnodes]);
1217 inodef++;
1218 nnodes++;
1219 }
1220 else if (g->nodes[inodeg] < f->nodes[inodef]){
1221 x[nnodes] = g->nodes[inodeg];
1222 fvals[nnodes] = lin_elem_exp_eval(f, x[nnodes]);
1223 gvals[nnodes] = g->coeff[inodeg];
1224 inodeg++;
1225 nnodes++;
1226 }
1227 }
1228 //printf("nodes are "); dprint(nnodes,x);
1229 // printf("fvalues are "); dprint(nnodes,fvals);
1230 // printf("gvalues are "); dprint(nnodes, gvals);
1231 return nnodes;
1232 }
1233
1234 /********************************************************//**
1235 * Inner product between two functions with the same discretizations
1236 *
1237 * \param[in] N - number of nodes
1238 * \param[in] x - nodes
1239 * \param[in] f - coefficients of first function
1240 * \param[in] g - coefficients of second function
1241 *
1242 * \return inner product
1243 *************************************************************/
lin_elem_exp_inner_same(size_t N,double * x,double * f,double * g)1244 static double lin_elem_exp_inner_same(size_t N, double * x,
1245 double * f, double * g)
1246 {
1247
1248 // This first part is a high accuracy scheme, but assumes higher
1249 // order structure than piecewise linear
1250 /* double value = 0.0; */
1251 /* double left,mixed,right,dx2; */
1252 /* for (size_t ii = 0; ii < N-1; ii++){ */
1253 /* dx2 = (x[ii+1]-x[ii])*(x[ii+1] - x[ii]); */
1254 /* lin_elem_exp_inner_element(x[ii],x[ii+1],&left,&mixed,&right); */
1255 /* /\* double new_val = (f[ii] * g[ii]) / dx2 * left + *\/ */
1256 /* /\* (f[ii] * g[ii+1]) / dx2 * mixed + *\/ */
1257 /* /\* (g[ii] * f[ii+1]) / dx2 * mixed + *\/ */
1258 /* /\* (f[ii+1] * g[ii+1]) / dx2 * right; *\/ */
1259 /* /\* printf("%zu: integrand(%3.15g) = %3.15G\n", ii, x[ii], new_val); *\/ */
1260 /* /\* value += new_val; *\/ */
1261 /* value += (f[ii] * g[ii]) / dx2 * left + */
1262 /* (f[ii] * g[ii+1]) / dx2 * mixed + */
1263 /* (g[ii] * f[ii+1]) / dx2 * mixed + */
1264 /* (f[ii+1] * g[ii+1]) / dx2 * right; */
1265 /* } */
1266
1267 /* printf("nodes are "); */
1268 /* dprint(N, x); */
1269 // assuming quadratic not accurate when spacing is too large
1270 /* double value1 = 0.0; */
1271 /* for (size_t ii = 0; ii < N-1; ii++){ */
1272 /* double m1 = (f[ii+1] - f[ii]) / (x[ii+1] - x[ii]); */
1273 /* double m2 = (g[ii+1] - g[ii]) / (x[ii+1] - x[ii]); */
1274 /* double d1 = (f[ii+1] - m1 * x[ii+1]); */
1275 /* double d2 = (g[ii+1] - m2 * x[ii+1]); */
1276
1277 /* double t1 = d1*d2 * (x[ii+1] - x[ii]); */
1278 /* double t2 = 0.5 * (d1 * m2 + m1 * d2) * (x[ii+1] * x[ii+1] - x[ii] * x[ii]); */
1279 /* double t3 = m1 * m2 / 3.0 * (x[ii+1] * x[ii+1] * x[ii+1] - x[ii] * x[ii] * x[ii]); */
1280 /* value1 += (t1 + t2 + t3); */
1281 /* /\* printf("%zu: integrand(%3.15g) = %3.15G\n", ii, x[ii], value1); *\/ */
1282 /* } */
1283
1284 /* printf("\n\n"); */
1285 double dx = x[1]-x[0];
1286 double value = f[0]*g[0]*dx * 0.5;
1287 /* printf("0: integrand(%3.15g) = %3.15G\n", x[0], value); */
1288 for (size_t ii = 1; ii < N-1; ii++){
1289 dx = x[ii+1]-x[ii-1];
1290 /* dx = x[ii+1]-x[ii]; */
1291 /* double new_val = f[ii]*g[ii]*0.5*dx; */
1292 value += f[ii]*g[ii]*0.5*dx;
1293 /* printf("%zu: integrand(%3.15g) = %3.15G\n", ii, x[ii], value); */
1294 }
1295 dx = x[N-1] - x[N-2];
1296 value += f[N-1]*g[N-1]*dx*0.5;
1297
1298 /* printf("diff values is = %3.15G\n", value1 - value); */
1299 return value;
1300 }
1301
1302 /********************************************************//**
1303 * Inner product between two functions
1304 *
1305 * \param[in] f - function
1306 * \param[in] g - function
1307 *
1308 * \return inner product
1309 *************************************************************/
lin_elem_exp_inner(const struct LinElemExp * f,const struct LinElemExp * g)1310 double lin_elem_exp_inner(const struct LinElemExp * f,
1311 const struct LinElemExp * g)
1312 {
1313
1314 double value = 0.0;
1315 int samedisc = lin_elem_sdiscp(f,g);
1316 if (samedisc == 1){
1317 // printf("here?!\n");
1318 /* if (f->diff == NULL){ */
1319 /* compute_diff(f); */
1320 /* } */
1321 /* if (g->diff == NULL){ */
1322 /* compute_diff(g); */
1323 /* } */
1324 value = lin_elem_exp_inner_same(f->num_nodes,f->nodes,
1325 f->coeff, g->coeff);
1326 }
1327 else{
1328
1329 double xnew[10000];
1330 double fnew[10000];
1331 double gnew[10000];
1332 double * xuse,*fuse,*guse;
1333 double * xx = NULL;
1334 double * ff = NULL;
1335 double * gg = NULL;
1336 size_t n_new = f->num_nodes + g->num_nodes;
1337 if (n_new >= 10000){
1338 xx = calloc_double(n_new);
1339 ff = calloc_double(n_new);
1340 gg = calloc_double(n_new);
1341 xuse = xx;
1342 fuse = ff;
1343 guse = gg;
1344 }
1345 else{
1346 xuse = xnew;
1347 fuse = fnew;
1348 guse = gnew;
1349 }
1350
1351 size_t nnodes = lin_elem_exp_inner_same_grid(f,g, xuse,fuse,guse);
1352 if (nnodes > 2){
1353 value = lin_elem_exp_inner_same(nnodes,xuse,fuse,guse);
1354 }
1355 else{
1356 printf("weird =\n");
1357 printf("f = \n");
1358 print_lin_elem_exp(f,3,NULL,stdout);
1359 printf("g = \n");
1360 print_lin_elem_exp(g,3,NULL,stdout);
1361 assert(1 == 0);
1362 }
1363
1364 if ( xx != NULL){
1365 free(xx); xx = NULL;
1366 free(ff); ff = NULL;
1367 free(gg); gg = NULL;
1368 }
1369 // printf("there\n");
1370 }
1371 return value;
1372 }
1373
1374 /********************************************************//**
1375 Add two functions with same discretization levels
1376
1377 \param[in] a - scaled value
1378 \param[in] f - function
1379 \param[in,out] g - function
1380
1381 \returns 0 successful
1382 1 error
1383
1384 \note
1385 Could be sped up by keeping track of evaluations
1386 *************************************************************/
lin_elem_exp_axpy_same(double a,const struct LinElemExp * f,struct LinElemExp * g)1387 static int lin_elem_exp_axpy_same(double a, const struct LinElemExp * f,
1388 struct LinElemExp * g)
1389 {
1390
1391 cblas_daxpy(g->num_nodes,a,f->coeff,1,g->coeff,1);
1392 return 0;
1393 }
1394
1395
1396 /********************************************************//**
1397 * Interpolate two linear element expansions onto the same grid
1398 *
1399 * \param[in] f - function
1400 * \param[in] g - function
1401 * \param[in,out] x - interpolated nodes
1402
1403 * \return number of nodes
1404
1405 * \note
1406 x must be previously allocaed with enough space
1407 at least f->num_nodes + g->num_nodes is enough for a
1408 conservative allocation
1409 *************************************************************/
lin_elem_exp_interp_same_grid(const struct LinElemExp * f,const struct LinElemExp * g,double * x)1410 static size_t lin_elem_exp_interp_same_grid(
1411 const struct LinElemExp * f,
1412 const struct LinElemExp * g, double * x)
1413 {
1414 size_t nnodes = 0;
1415 size_t inodef = 0;
1416 size_t inodeg = 0;
1417
1418 while ((inodef < f->num_nodes) || (inodeg < g->num_nodes)){
1419 if (inodef == f->num_nodes){
1420 x[nnodes] = g->nodes[inodeg];
1421 inodeg++;
1422 nnodes++;
1423 }
1424 else if (inodeg == g->num_nodes){
1425 x[nnodes] = f->nodes[inodef];
1426 inodef++;
1427 nnodes++;
1428 }
1429 else if (fabs(f->nodes[inodef] - g->nodes[inodeg]) < 1e-15){
1430 x[nnodes] = f->nodes[inodef];
1431 inodef++;
1432 inodeg++;
1433 nnodes++;
1434 }
1435 else if (f->nodes[inodef] < g->nodes[inodeg]){
1436 x[nnodes] = f->nodes[inodef];
1437 inodef++;
1438 nnodes++;
1439 }
1440 else if (g->nodes[inodeg] < f->nodes[inodef]){
1441 x[nnodes] = g->nodes[inodeg];
1442 inodeg++;
1443 nnodes++;
1444 }
1445 }
1446
1447 return nnodes;
1448 }
1449
1450
1451 /********************************************************//**
1452 Add two functions
1453
1454 \param[in] a - scaled value
1455 \param[in] f - function
1456 \param[in,out] g - function
1457
1458 \returns 0 successful
1459 1 error
1460
1461 \note
1462 Could be sped up by keeping track of evaluations
1463 *************************************************************/
lin_elem_exp_axpy(double a,const struct LinElemExp * f,struct LinElemExp * g)1464 int lin_elem_exp_axpy(double a,
1465 const struct LinElemExp * f,
1466 struct LinElemExp * g)
1467 {
1468
1469 int res = 0;
1470 int samedisc = lin_elem_sdiscp(f,g);
1471 if (samedisc == 1){
1472 res = lin_elem_exp_axpy_same(a,f, g);
1473 }
1474 else{
1475 double * x = calloc_double(f->num_nodes+g->num_nodes);
1476 double * coeff = calloc_double(f->num_nodes+g->num_nodes);
1477 size_t num = lin_elem_exp_interp_same_grid(f,g,x);
1478 // printf("interpolated!\n");
1479 for (size_t ii = 0; ii < num; ii++){
1480 coeff[ii] = a*lin_elem_exp_eval(f,x[ii]) +
1481 lin_elem_exp_eval(g,x[ii]);
1482 }
1483 // printf("good\n");
1484 g->num_nodes = num;
1485 free(g->nodes); g->nodes = x;
1486 // printf("bad!\n");
1487 free(g->coeff); g->coeff = coeff;
1488 // printf("word\n");
1489 }
1490 return res;
1491 }
1492
1493 struct lefg
1494 {
1495 const struct LinElemExp * f;
1496 const struct LinElemExp * g;
1497 };
1498
leprod(size_t N,const double * x,double * out,void * arg)1499 static int leprod(size_t N, const double * x,double * out, void * arg)
1500 {
1501 struct lefg * fg = arg;
1502 for (size_t ii = 0; ii < N; ii++){
1503 out[ii] = lin_elem_exp_eval(fg->f,x[ii]);
1504 out[ii] *= lin_elem_exp_eval(fg->g,x[ii]);
1505 }
1506 return 0;
1507 }
1508
1509 /********************************************************//**
1510 Multiply two functions
1511
1512 \param[in] f - first function
1513 \param[in] g - second function
1514
1515 \returns product
1516
1517 \note
1518 *************************************************************/
lin_elem_exp_prod(const struct LinElemExp * f,const struct LinElemExp * g)1519 struct LinElemExp * lin_elem_exp_prod(const struct LinElemExp * f,
1520 const struct LinElemExp * g)
1521 {
1522
1523 double lb = f->nodes[0] < g->nodes[0] ? g->nodes[0] : f->nodes[0];
1524 double ub = f->nodes[f->num_nodes-1] > g->nodes[g->num_nodes-1] ? g->nodes[g->num_nodes-1] : f->nodes[f->num_nodes-1];
1525
1526 struct lefg fg;
1527 fg.f = f;
1528 fg.g = g;
1529 struct LinElemExpAopts * opts = NULL;
1530 double hmin = 1e-3;
1531 double delta = 1e-4;
1532 int samedisc = lin_elem_sdiscp(f,g);
1533 if (samedisc == 1){
1534 opts = lin_elem_exp_aopts_alloc(f->num_nodes, f->nodes);
1535 }
1536 else{
1537 opts = lin_elem_exp_aopts_alloc_adapt(0,NULL,lb,ub,delta,hmin);
1538 }
1539
1540
1541 struct Fwrap * fw = fwrap_create(1,"general-vec");
1542 fwrap_set_fvec(fw,leprod,&fg);
1543
1544 struct LinElemExp * prod = NULL;
1545 prod = lin_elem_exp_approx(opts,fw);
1546 fwrap_destroy(fw);
1547
1548 lin_elem_exp_aopts_free(opts);
1549 return prod;
1550 }
1551
1552 /********************************************************//**
1553 Compute the norm of a function
1554
1555 \param[in] f - function
1556
1557 \return norm
1558 *************************************************************/
lin_elem_exp_norm(const struct LinElemExp * f)1559 double lin_elem_exp_norm(const struct LinElemExp * f)
1560 {
1561 double norm = lin_elem_exp_inner(f,f);
1562 return sqrt(norm);
1563 }
1564
1565 /********************************************************//**
1566 Compute the maximum of the function
1567
1568 \param[in] f - function
1569 \param[in,out] x - location of maximum
1570
1571 \return value of maximum
1572 *************************************************************/
lin_elem_exp_max(const struct LinElemExp * f,double * x)1573 double lin_elem_exp_max(const struct LinElemExp * f, double * x)
1574 {
1575 double mval = f->coeff[0];
1576 *x = f->nodes[0];
1577 for (size_t ii = 1; ii < f->num_nodes;ii++){
1578 if (f->coeff[ii] > mval){
1579 mval = f->coeff[ii];
1580 *x = f->nodes[ii];
1581 }
1582 }
1583 return mval;
1584 }
1585
1586 /********************************************************//**
1587 Compute the minimum of the function
1588
1589 \param[in] f - function
1590 \param[in,out] x - location of minimum
1591
1592 \return value of minimum
1593 *************************************************************/
lin_elem_exp_min(const struct LinElemExp * f,double * x)1594 double lin_elem_exp_min(const struct LinElemExp * f, double * x)
1595 {
1596 double mval = f->coeff[0];
1597 *x = f->nodes[0];
1598 for (size_t ii = 1; ii < f->num_nodes;ii++){
1599 if (f->coeff[ii] < mval){
1600 mval = f->coeff[ii];
1601 *x = f->nodes[ii];
1602 }
1603 }
1604 return mval;
1605 }
1606
1607 /********************************************************//**
1608 Compute the maximum of the absolute value function
1609
1610 \param[in] f - function
1611 \param[in,out] x - location of absolute value max
1612 \param[in] size - size of x variable (sizeof(double) or sizeof(size_t))
1613 \param[in] optargs - optimization arguments
1614
1615 \return value
1616 *************************************************************/
lin_elem_exp_absmax(const struct LinElemExp * f,void * x,size_t size,void * optargs)1617 double lin_elem_exp_absmax(const struct LinElemExp * f, void * x,
1618 size_t size,
1619 void * optargs)
1620 {
1621 if (optargs == NULL){
1622 size_t dsize = sizeof(double);
1623 double mval = fabs(f->coeff[0]);
1624 if (size == dsize){
1625 *(double *)(x) = f->nodes[0];
1626 }
1627 else{
1628 *(size_t *)(x) = 0;
1629 }
1630 for (size_t ii = 1; ii < f->num_nodes;ii++){
1631 if (fabs(f->coeff[ii]) > mval){
1632 mval = fabs(f->coeff[ii]);
1633 if (size == dsize){
1634 *(double *)(x) = f->nodes[ii];
1635 }
1636 else{
1637 *(size_t *)(x) = ii;
1638 }
1639 }
1640 }
1641 return mval;
1642 }
1643 else{
1644 assert (size == sizeof(double));
1645 struct c3Vector * optnodes = optargs;
1646 double mval = fabs(lin_elem_exp_eval(f,optnodes->elem[0]));
1647 *(double *)(x) = optnodes->elem[0];
1648 for (size_t ii = 0; ii < optnodes->size; ii++){
1649 double val = fabs(lin_elem_exp_eval(f,optnodes->elem[ii]));
1650 if (val > mval){
1651 mval = val;
1652 *(double *)(x) = optnodes->elem[ii];
1653 }
1654 }
1655 return mval;
1656 }
1657 }
1658
1659 /********************************************************//**
1660 Compute an error estimate
1661
1662 \param[in] f - function
1663 \param[in,out] errs - errors for each element
1664 \param[in] dir - direction (1 for forward, -1 for backward)
1665 \param[in] type - 0 for Linf, 2 for L2
1666
1667 \return maximum error
1668
1669 \note
1670 The error estimate consists of finite difference approximations
1671 to the second derivative of the function
1672 Left boundary always uses forward difference and
1673 right boundary always uses backward
1674 *************************************************************/
lin_elem_exp_err_est(struct LinElemExp * f,double * errs,short dir,short type)1675 double lin_elem_exp_err_est(struct LinElemExp * f, double * errs, short dir, short type)
1676 {
1677 assert (f->num_nodes > 2);
1678 double value = 0.0;
1679
1680 double dx = (f->nodes[1]-f->nodes[0]);
1681 double dx2 = (f->nodes[2]-f->nodes[1]);
1682 double m1 = (f->coeff[1]-f->coeff[0])/dx;
1683 double mid1 = (f->nodes[1] + f->nodes[0])/2.0;
1684 double m2 = (f->coeff[2]-f->coeff[1])/dx2;
1685 double mid2 = (f->nodes[2] + f->nodes[1])/2.0;
1686 double err = (m2-m1)/(mid2-mid1);
1687 if (type == 0){
1688 errs[0] = pow(dx,2)*fabs(err)/8.0;
1689 value = errs[0];
1690 }
1691 else if (type == 2){
1692 errs[0] = pow(dx,2)*sqrt(pow(err,2)*dx)/8.0;
1693 value += errs[0];
1694 }
1695 dx = dx2;
1696 double m3,mid3;
1697 for (size_t ii = 2; ii < f->num_nodes-1; ii++){
1698 dx2 = (f->nodes[ii+1]-f->nodes[ii]);
1699 m3 = (f->coeff[ii+1]-f->coeff[ii]);
1700 mid3 = (f->nodes[ii+1] + f->nodes[ii])/2.0;
1701 if (dir == 1){
1702 err = (m3-m2)/(mid3-mid2);
1703 }
1704 else{
1705 err = (m2-m1)/(mid2-mid1);
1706 }
1707 if (type == 0){
1708 errs[ii-1] = pow(dx,2)*fabs(err)/8.0;
1709 if (errs[ii-1] > value){
1710 value = errs[ii-1];
1711 }
1712 }
1713 else if (type == 2){
1714 errs[ii-1] = pow(dx,2)*sqrt(pow(err,2)*dx)/8.0;
1715 value += errs[ii-1];
1716 }
1717
1718 m1 = m2;
1719 mid1 = mid2;
1720 m2 = m3;
1721 mid2 = mid3;
1722 dx = dx2;
1723 }
1724 err = (m2-m1)/(mid2-mid1);
1725 size_t ii = f->num_nodes-1;
1726 if (type == 0){
1727 errs[ii-1] = pow(dx,2)*fabs(err)/8.0;
1728 if (errs[ii-1] > value){
1729 value = errs[ii-1];
1730 }
1731 }
1732 else if (type == 2){
1733 errs[ii-1] = pow(dx,2)*sqrt(pow(err,2)*dx)/8.0;
1734 value += errs[ii-1];
1735 }
1736
1737 return value;
1738 }
1739
1740 /// @private
1741 struct LinElemXY
1742 {
1743 double x;
1744 double y;
1745 struct LinElemXY * next;
1746 };
1747
1748 /// @private
xy_init(double x,double y)1749 struct LinElemXY * xy_init(double x, double y)
1750 {
1751 struct LinElemXY * xy = malloc(sizeof(struct LinElemXY));
1752 if (xy == NULL){
1753 fprintf(stderr,"Cannot allocate LinElemXY struct for lin element adapting\n");
1754 exit(1);
1755 }
1756
1757 xy->x = x;
1758 xy->y = y;
1759 xy->next = NULL;
1760 return xy;
1761 }
1762
1763 /// @private
xy_append(struct LinElemXY ** xy,double x,double y)1764 void xy_append(struct LinElemXY ** xy, double x, double y)
1765 {
1766
1767 if (*xy == NULL){
1768 /* printf("xy is null so initialize\n"); */
1769 *xy = xy_init(x,y);
1770 /* printf("got it\n"); */
1771 }
1772 else{
1773 /* printf("xy == NULL = %d\n",xy==NULL); */
1774 /* printf("xy is not null!\n"); */
1775 struct LinElemXY * temp = *xy;
1776 /* printf("iterate\n"); */
1777 while (temp->next != NULL){
1778 /* printf("iterate\n"); */
1779 temp = temp->next;
1780 }
1781 /* printf("done iterating\n"); */
1782 temp->next = xy_init(x,y);
1783
1784 /* if ((*xy)->next == NULL){ */
1785 /* printf("next is null!\n"); */
1786 /* (*xy)->next = xy_init(x,y); */
1787 /* } */
1788 /* else{ */
1789 /* printf("next is not null!\n"); */
1790 /* xy_append(&((*xy)->next),x,y); */
1791 /* printf("appended next!\n"); */
1792 /* } */
1793 /* printf("got it2\n"); */
1794 }
1795 }
1796
1797 /// @private
xy_concat(struct LinElemXY ** xy,struct LinElemXY * r)1798 void xy_concat(struct LinElemXY ** xy,struct LinElemXY * r)
1799 {
1800
1801 if (*xy == NULL){
1802 *xy = r;
1803 }
1804 else{
1805 xy_concat(&((*xy)->next),r);
1806 }
1807 }
1808
1809 /// @private
xy_last(struct LinElemXY * xy)1810 struct LinElemXY * xy_last(struct LinElemXY * xy)
1811 {
1812 if (xy == NULL){
1813 return NULL;
1814 }
1815 struct LinElemXY * temp = xy;
1816 while (temp->next != NULL){
1817 temp = temp->next;
1818 }
1819 return temp;
1820 }
1821
1822 /// @private
lin_elem_xy_get_x(struct LinElemXY * xy)1823 double lin_elem_xy_get_x(struct LinElemXY * xy)
1824 {
1825 return xy->x;
1826 }
1827
1828 /// @private
lin_elem_xy_get_y(struct LinElemXY * xy)1829 double lin_elem_xy_get_y(struct LinElemXY * xy)
1830 {
1831 return xy->y;
1832 }
1833
1834 /// @private
lin_elem_xy_next(struct LinElemXY * xy)1835 struct LinElemXY * lin_elem_xy_next(struct LinElemXY * xy)
1836 {
1837 return xy->next;
1838 }
1839
1840 /// @private
lin_elem_xy_length(struct LinElemXY * xy)1841 size_t lin_elem_xy_length(struct LinElemXY * xy)
1842 {
1843 size_t count = 0;
1844 struct LinElemXY * temp = xy;
1845 if (temp == NULL){
1846 return count;
1847 }
1848 while (temp!= NULL){
1849 count ++;
1850 temp = temp->next;
1851 }
1852 return count;
1853 }
1854
1855 /// @private
lin_elem_xy_free(struct LinElemXY * xy)1856 void lin_elem_xy_free(struct LinElemXY * xy)
1857 {
1858 if (xy != NULL){
1859 lin_elem_xy_free(xy->next); xy->next = NULL;
1860 free(xy); xy = NULL;
1861 }
1862 }
1863
1864 /********************************************************//**
1865 Recursively partition
1866
1867 \param[in] f - function
1868 \param[in] xl - left bound
1869 \param[in] fl - left evaluation
1870 \param[in] xr - right bound
1871 \param[in] fr - right evaluation
1872 \param[in] delta - value tolerance
1873 \param[in] hmin - input tolerance
1874 \param[in] xy - xypairs
1875 *************************************************************/
lin_elem_adapt(struct Fwrap * f,double xl,double fl,double xr,double fr,double delta,double hmin,struct LinElemXY ** xy)1876 void lin_elem_adapt(struct Fwrap * f,
1877 double xl, double fl,double xr, double fr,
1878 double delta, double hmin,struct LinElemXY ** xy)
1879 {
1880 //xy_append(xy,xl,fl);
1881 if ((xr - xl) <= hmin){
1882 /* printf("adapt is within bounds!\n"); */
1883 /* printf("adding left (xl,fl)=(%G,%G)\n",xl,fl); */
1884 xy_append(xy,xl,fl);
1885 /* printf("added left (xl,fl)=(%G,%G)\n",xl,fl); */
1886
1887 xy_append(xy,xr,fr);
1888 /* printf("done!\n"); */
1889 }
1890 else{
1891 double mid = (xl+xr)/2.0;
1892 double fmid;
1893 fwrap_eval(1,&mid,&fmid,f);
1894 /* printf("xl = %3.15G, xr = %3.15G, fl = %3.15G, fr = %3.15G, fmid = %3.15G\n", xl, xr, fl, fr, fmid); */
1895 /* printf("adapt! diff=%3.15G, delta=%3.15G\n", (fl+fr)/2.0-fmid, delta); */
1896 if (fabs( (fl+fr)/2.0 - fmid )/fabs(fmid) < delta){
1897 /* if (fabs( (fl+fr)/2.0 - fmid ) < delta){ */
1898 // maybe should add the midpoint since evaluated
1899 /* printf("finish again! xy==null?=%d\n\n",xy==NULL); */
1900 /* printf("adding the left %G,%G\n",xl,fl); */
1901 xy_append(xy,xl,fl);
1902 xy_append(xy,mid,fmid);
1903 /* printf("added the left %G,%G\n",xl,fl); */
1904 /* printf("adding the right %G,%G\n",xr,fr); */
1905 xy_append(xy,xr,fr);
1906 /* printf("added the right %G,%G\n",xr,fr); */
1907 }
1908 else{
1909 /* printf("adapt further\n"); */
1910 lin_elem_adapt(f,xl,fl,mid,fmid,delta,hmin,xy);
1911 struct LinElemXY * last = NULL;//xy_last(*xy);
1912 lin_elem_adapt(f,mid,fmid,xr,fr,delta,hmin,&last);
1913 if (last != NULL){
1914 xy_concat(xy,last->next);
1915 free(last);
1916 }
1917 }
1918 }
1919 }
1920
1921
1922 /********************************************************//**
1923 Approximate a function
1924
1925 \param[in] opts - approximation options
1926 \param[in] f - function
1927
1928 \return Approximated function
1929 *************************************************************/
1930 struct LinElemExp *
lin_elem_exp_approx(struct LinElemExpAopts * opts,struct Fwrap * f)1931 lin_elem_exp_approx(struct LinElemExpAopts * opts, struct Fwrap * f)
1932 {
1933
1934 assert(opts != NULL);
1935 struct LinElemExp * lexp = lin_elem_exp_alloc();
1936 if (opts->adapt == 0){
1937 assert (opts->nodes != NULL);
1938
1939 // allocate nodes and coefficients
1940 size_t N = opts->num_nodes;
1941 lexp->num_nodes = N;
1942 lexp->nodes = calloc_double(N);
1943 lexp->coeff = calloc_double(N);
1944
1945 /* printf("in approx: lin_elem_exp_aopts nodes = "); dprint(N,opts->nodes); */
1946 // copy nodes from options
1947 memmove(lexp->nodes,opts->nodes,N*sizeof(double));
1948
1949 // evaluate the function
1950 /* printf("evaluate points\n"); */
1951 /* dprint(N,lexp->nodes); */
1952 fwrap_eval(N,lexp->nodes,lexp->coeff,f);
1953
1954 if (fabs(lexp->nodes[0] - opts->nodes[0]) > 1e-15){
1955 fprintf(stderr, "In lin_elem_exp_approx\n");
1956 fprintf(stderr,"N nodes %zu\n",N);
1957 fprintf(stderr,"First approx_opt_node is %G\n",opts->nodes[0]);
1958 fprintf(stderr,"First expansion opt_node is %G\n",lexp->nodes[0]);
1959 exit(1);
1960 }
1961 /* printf("cannot evaluate them"); */
1962 }
1963 else{
1964 /* printf("adapting!\n"); */
1965 /* printf("not here!\n"); */
1966 // adapt
1967 struct LinElemXY * xy = NULL;
1968 if (opts->nodes == NULL){ // no nodes yet specified
1969 double xl = opts->lb;
1970 double xr = opts->ub;
1971 double fl,fr;
1972 fwrap_eval(1,&xl,&fl,f);
1973 fwrap_eval(1,&xr,&fr,f);
1974
1975 lin_elem_adapt(f,xl,fl,xr,fr,opts->delta,opts->hmin,&xy);
1976 lexp->num_nodes = lin_elem_xy_length(xy);
1977 lexp->nodes = calloc_double(lexp->num_nodes);
1978 lexp->coeff = calloc_double(lexp->num_nodes);
1979 struct LinElemXY * temp = xy;
1980 for (size_t ii = 0; ii < lexp->num_nodes; ii++){
1981 lexp->nodes[ii] = temp->x;
1982 lexp->coeff[ii] = temp->y;
1983 temp = temp->next;
1984 }
1985 lin_elem_xy_free(xy); xy = NULL;
1986 }
1987 else{
1988 // starting nodes specified
1989 assert (opts->num_nodes > 1);
1990 double xl = opts->nodes[0];
1991 double xr = opts->nodes[1];
1992
1993 double fl,fr;
1994 fwrap_eval(1,&xl,&fl,f);
1995 fwrap_eval(1,&xr,&fr,f);
1996
1997 lin_elem_adapt(f,xl,fl,xr,fr,opts->delta,opts->hmin,&xy);
1998 for (size_t ii = 2; ii < opts->num_nodes; ii++){
1999 /* printf("on node = %zu\n",ii); */
2000 xl = xr;
2001 fl = fr;
2002
2003 xr = opts->nodes[ii];
2004 fwrap_eval(1,&xr,&fr,f);
2005
2006 /* printf("(xl,fl,xr,fr)=(%G,%G,%G,%G)\n",xl,fl,xr,fr); */
2007 struct LinElemXY * temp = NULL;
2008 lin_elem_adapt(f,xl,fl,xr,fr,opts->delta,opts->hmin,&temp);
2009 /* printf("adapted\n"); */
2010 if (temp != NULL){
2011 xy_concat(&xy,temp->next);
2012 free(temp);
2013 }
2014 }
2015 /* printf("finished here\n"); */
2016 lexp->num_nodes = lin_elem_xy_length(xy);
2017 lexp->nodes = calloc_double(lexp->num_nodes);
2018 lexp->coeff = calloc_double(lexp->num_nodes);
2019 struct LinElemXY * temp = xy;
2020 for (size_t ii = 0; ii < lexp->num_nodes; ii++){
2021 lexp->nodes[ii] = temp->x;
2022 lexp->coeff[ii] = temp->y;
2023 temp = temp->next;
2024 }
2025 lin_elem_xy_free(xy); xy = NULL;
2026 }
2027
2028 }
2029
2030 assert (lexp->num_nodes != 0);
2031 return lexp;
2032 }
2033
2034 /********************************************************//**
2035 Return a zero function
2036
2037 \param[in] opts - extra arguments depending on function_class, sub_type, etc.
2038 \param[in] force_param - if == 1 then approximation will have the number of parameters
2039 defined by *get_nparams, for each approximation type
2040 if == 0 then it may be more compressed
2041
2042 \return p - zero function
2043 ************************************************************/
2044 struct LinElemExp *
lin_elem_exp_zero(const struct LinElemExpAopts * opts,int force_param)2045 lin_elem_exp_zero(const struct LinElemExpAopts * opts, int force_param)
2046 {
2047
2048 struct LinElemExp * lexp = NULL;
2049 if (force_param == 0){
2050 lexp = lin_elem_exp_constant(0.0,opts);
2051 }
2052 else{
2053 lexp = lin_elem_exp_alloc();
2054 if (opts->num_nodes == 0){
2055 lexp->num_nodes = 2;
2056 lexp->nodes = linspace(opts->lb,opts->ub,2);
2057 }
2058 else{
2059 lexp->num_nodes = opts->num_nodes;
2060 lexp->nodes = calloc_double(opts->num_nodes);
2061 memmove(lexp->nodes,opts->nodes,opts->num_nodes*sizeof(double));
2062 }
2063 lexp->coeff = calloc_double(lexp->num_nodes);
2064 for (size_t ii = 0; ii < lexp->num_nodes; ii++){
2065 lexp->coeff[ii] = 0.0;
2066 }
2067 }
2068 return lexp;
2069 }
2070
2071
2072 /********************************************************//**
2073 Create a constant function
2074
2075 \param[in] a - function value
2076 \param[in] opts - options
2077
2078 \return function
2079 *************************************************************/
2080 struct LinElemExp *
lin_elem_exp_constant(double a,const struct LinElemExpAopts * opts)2081 lin_elem_exp_constant(double a,
2082 const struct LinElemExpAopts * opts)
2083 {
2084
2085 struct LinElemExp * lexp = lin_elem_exp_alloc();
2086 if (opts->num_nodes == 0){
2087 lexp->num_nodes = 2;
2088 lexp->nodes = linspace(opts->lb,opts->ub,2);
2089 }
2090 else{
2091 lexp->num_nodes = opts->num_nodes;
2092 lexp->nodes = calloc_double(opts->num_nodes);
2093 memmove(lexp->nodes,opts->nodes,opts->num_nodes*sizeof(double));
2094 }
2095 lexp->coeff = calloc_double(lexp->num_nodes);
2096 for (size_t ii = 0; ii < lexp->num_nodes; ii++){
2097 lexp->coeff[ii] = a;
2098 }
2099 assert (lexp->num_nodes != 0);
2100 return lexp;
2101 }
2102
2103 /********************************************************//**
2104 Create a linear function
2105 \f[
2106 y = ax + b
2107 \f]
2108
2109 \param[in] a - function value
2110 \param[in] b - y intercept
2111 \param[in] opts - options
2112
2113 \return function
2114 *************************************************************/
2115 struct LinElemExp *
lin_elem_exp_linear(double a,double b,const struct LinElemExpAopts * opts)2116 lin_elem_exp_linear(double a, double b,
2117 const struct LinElemExpAopts * opts)
2118 {
2119
2120 struct LinElemExp * lexp = lin_elem_exp_alloc();
2121 if (opts->num_nodes == 0){
2122 lexp->num_nodes = 2;
2123 lexp->nodes = linspace(opts->lb,opts->ub,2);
2124 }
2125 else{
2126 lexp->num_nodes = opts->num_nodes;
2127 lexp->nodes = calloc_double(opts->num_nodes);
2128 memmove(lexp->nodes,opts->nodes,opts->num_nodes*sizeof(double));
2129 }
2130 lexp->coeff = calloc_double(lexp->num_nodes);
2131 for (size_t ii = 0; ii < lexp->num_nodes; ii++){
2132 lexp->coeff[ii] = a * lexp->nodes[ii] + b;
2133 }
2134 assert (lexp->num_nodes != 0);
2135 return lexp;
2136 }
2137
2138 /*******************************************************//**
2139 Update a linear function
2140
2141 \param[in] f - existing linear function
2142 \param[in] a - slope of the function
2143 \param[in] offset - offset of the function
2144
2145 \returns 0 if successfull, 1 otherwise
2146 ***********************************************************/
2147 int
lin_elem_exp_linear_update(struct LinElemExp * f,double a,double offset)2148 lin_elem_exp_linear_update(struct LinElemExp * f,
2149 double a, double offset)
2150 {
2151 (void) f;
2152 (void) a;
2153 (void) offset;
2154 NOT_IMPLEMENTED_MSG("lin_elem_exp_linear_update");
2155 return 1;
2156 }
2157
2158
2159 /********************************************************//**
2160 Return a quadratic function a * (x - offset)^2 = a (x^2 - 2offset x + offset^2)
2161
2162 \param[in] a - quadratic coefficients
2163 \param[in] offset - shift of the function
2164 \param[in] opts - extra arguments depending on function_class, sub_type, etc.
2165
2166 \return quadratic
2167 *************************************************************/
2168 struct LinElemExp *
lin_elem_exp_quadratic(double a,double offset,const struct LinElemExpAopts * opts)2169 lin_elem_exp_quadratic(double a, double offset,
2170 const struct LinElemExpAopts * opts)
2171 {
2172 (void)(a);
2173 (void)(offset);
2174 (void)(opts);
2175 NOT_IMPLEMENTED_MSG("lin_elem_exp_quadratic");
2176 return NULL;
2177 }
2178
2179 /********************************************************//**
2180 Multiply by -1
2181
2182 \param[in,out] f - function
2183 *************************************************************/
lin_elem_exp_flip_sign(struct LinElemExp * f)2184 void lin_elem_exp_flip_sign(struct LinElemExp * f)
2185 {
2186 for (size_t ii = 0; ii < f->num_nodes; ii++){
2187 f->coeff[ii] *= -1.0;
2188 }
2189 assert (f->num_nodes != 0);
2190 }
2191
2192 /********************************************************//**
2193 Generate an orthonormal basis
2194
2195 \param[in] n - number of basis function
2196 \param[in,out] f - linear element expansions with allocated nodes
2197 and coefficients set to zero
2198 \param[in] opts - approximation options
2199
2200 \note
2201 Uses modified gram schmidt to determine function coefficients
2202 Each function f[ii] must have the same nodes
2203 *************************************************************/
lin_elem_exp_orth_basis(size_t n,struct LinElemExp ** f,struct LinElemExpAopts * opts)2204 void lin_elem_exp_orth_basis(size_t n, struct LinElemExp ** f, struct LinElemExpAopts * opts)
2205 {
2206 assert (opts != NULL);
2207
2208 if (opts->adapt == 0){
2209 assert (opts->nodes != NULL);
2210 /* assert (n <= opts->num_nodes); */
2211 double * zeros = calloc_double(opts->num_nodes);
2212 /* double * zeros = calloc_double(n); */
2213 /* double * nodes = linspace(opts->lb, opts->ub, n); */
2214 for (size_t ii = 0; ii < n; ii++){
2215 f[ii] = lin_elem_exp_init(opts->num_nodes,opts->nodes,zeros);
2216 /* f[ii] = lin_elem_exp_init(n,opts->nodes,zeros); */
2217 if (ii < opts->num_nodes){
2218 f[ii]->coeff[ii] = 1.0;
2219 }
2220 }
2221 /* free(nodes); nodes = NULL; */
2222 double norm, proj;
2223 for (size_t ii = 0; ii < n; ii++){
2224 norm = lin_elem_exp_norm(f[ii]);
2225 if (norm > 1e-200){
2226 lin_elem_exp_scale(1/norm,f[ii]);
2227 assert (f[ii]->num_nodes != 0);
2228 for (size_t jj = ii+1; jj < n; jj++){
2229 proj = lin_elem_exp_inner(f[ii],f[jj]);
2230 lin_elem_exp_axpy(-proj,f[ii],f[jj]);
2231 }
2232 }
2233 }
2234
2235
2236 /* for (size_t ii = n) */
2237 free(zeros); zeros = NULL;
2238 }
2239 else{
2240 // not on a grid I can do whatever I want
2241 assert (n > 1);
2242 double * nodes = linspace(opts->lb,opts->ub,n);
2243 double * zeros = calloc_double(n);
2244 for (size_t ii = 0; ii < n; ii++){
2245 f[ii] = lin_elem_exp_init(n,nodes,zeros);
2246 f[ii]->coeff[ii] = 1.0;
2247 }
2248 double norm, proj;
2249 for (size_t ii = 0; ii < n; ii++){
2250 norm = lin_elem_exp_norm(f[ii]);
2251 lin_elem_exp_scale(1/norm,f[ii]);
2252 assert (f[ii]->num_nodes != 0);
2253 for (size_t jj = ii+1; jj < n; jj++){
2254 proj = lin_elem_exp_inner(f[ii],f[jj]);
2255 lin_elem_exp_axpy(-proj,f[ii],f[jj]);
2256 }
2257
2258 }
2259 free(zeros); zeros = NULL;
2260 free(nodes); nodes = NULL;
2261 }
2262
2263
2264 /* double norm, proj; */
2265 /* for (size_t ii = 0; ii < n; ii++){ */
2266 /* assert (f[ii]->num_nodes >= n); */
2267 /* f[ii]->coeff[ii] = 1.0; */
2268 /* } */
2269 /* for (size_t ii = 0; ii < n; ii++){ */
2270 /* norm = lin_elem_exp_norm(f[ii]); */
2271 /* lin_elem_exp_scale(1/norm,f[ii]); */
2272 /* for (size_t jj = ii+1; jj < n; jj++){ */
2273 /* proj = lin_elem_exp_inner(f[ii],f[jj]); */
2274 /* lin_elem_exp_axpy(-proj,f[ii],f[jj]); */
2275 /* } */
2276 /* } */
2277 }
2278
2279 /********************************************************//**
2280 Scale a function
2281
2282 \param[in] a - value
2283 \param[in,out] f - function
2284 *************************************************************/
lin_elem_exp_scale(double a,struct LinElemExp * f)2285 void lin_elem_exp_scale(double a, struct LinElemExp * f)
2286 {
2287 for (size_t ii = 0; ii < f->num_nodes; ii++){
2288 f->coeff[ii] *= a;
2289 }
2290 }
2291
2292 /********************************************************//**
2293 Get lower bound
2294
2295 \param[in] f - function
2296
2297 \return lower bound
2298 *************************************************************/
lin_elem_exp_get_lb(struct LinElemExp * f)2299 double lin_elem_exp_get_lb(struct LinElemExp * f)
2300 {
2301 return f->nodes[0];
2302 }
2303
2304 /********************************************************//**
2305 Get upper bound
2306
2307 \param[in] f - function
2308
2309 \return upper bound
2310 *************************************************************/
lin_elem_exp_get_ub(struct LinElemExp * f)2311 double lin_elem_exp_get_ub(struct LinElemExp * f)
2312 {
2313 return f->nodes[f->num_nodes-1];
2314 }
2315
2316
compare_le(const void * a,const void * b)2317 static int compare_le (const void * a, const void * b)
2318 {
2319 const double * aa = a;
2320 const double * bb = b;
2321 if (*aa > *bb){
2322 return 1;
2323 }
2324 else{
2325 return 0;
2326 }
2327 }
2328
2329 /********************************************************//**
2330 Create a linear element function with zeros at particular
2331 locations and 1 everyhwhere else.
2332
2333 \param[in] nzeros - number of zeros
2334 \param[in] zero_locs - locations of zeros
2335 \param[in] opts - linear expansion options
2336
2337 \return upper bound
2338 *************************************************************/
2339 struct LinElemExp *
lin_elem_exp_onezero(size_t nzeros,double * zero_locs,struct LinElemExpAopts * opts)2340 lin_elem_exp_onezero(size_t nzeros, double * zero_locs,
2341 struct LinElemExpAopts * opts)
2342 {
2343 assert (opts != NULL);
2344 if (opts->adapt == 1){
2345 // can do whatever I want
2346 if (nzeros == 0){
2347 double * nodes = calloc_double(2);
2348 double * coeff = calloc_double(2);
2349 struct LinElemExp * le = lin_elem_exp_init(2,nodes,coeff);
2350 le->coeff[0] = 1.0;
2351 free(nodes); nodes = NULL;
2352 free(coeff); coeff = NULL;
2353 /* print_lin_elem_exp(le,3,NULL,stdout); */
2354 return le;
2355 }
2356 else{
2357 struct LinElemExp * le = NULL;
2358 double * sorted_arr = calloc_double(nzeros);
2359 memmove(sorted_arr,zero_locs,nzeros*sizeof(double));
2360 qsort (sorted_arr, nzeros, sizeof(double), compare_le);
2361
2362 int onlb = 0;
2363 int onub = 0;
2364 double difflb = fabs(sorted_arr[0]-opts->lb);
2365 if (difflb < 1e-14){
2366 onlb = 1;
2367 }
2368 double diffub = fabs(zero_locs[nzeros-1]-opts->ub);
2369 if (diffub < 1e-14){
2370 onub = 1;
2371 }
2372 if ((onlb == 0) && (onub == 0)){
2373 double * nodes = calloc_double(nzeros+2);
2374 double * coeff = calloc_double(nzeros+2);
2375 nodes[0] = opts->lb;
2376 coeff[0] = 0.0;
2377 for (size_t jj = 1; jj < nzeros+1; jj++){
2378 nodes[jj] = sorted_arr[jj-1];
2379 coeff[jj] = 0.0;
2380 }
2381 nodes[nzeros+1] = opts->ub;
2382 coeff[nzeros+1] = 1.0;
2383 le = lin_elem_exp_init(nzeros+2,nodes,coeff);
2384 free(nodes); nodes = NULL;
2385 free(coeff); coeff = NULL;
2386 }
2387 else if ((onlb == 1) && (onub == 1)){
2388 double * nodes = calloc_double(nzeros+1);
2389 double * coeff = calloc_double(nzeros+1);
2390 nodes[0] = opts->lb;
2391 coeff[0] = 0.0;
2392 nodes[1] = (opts->lb + sorted_arr[1])/2.0;
2393 coeff[1] = 1.0;
2394 for (size_t jj = 2; jj< nzeros+1; jj++){
2395 nodes[jj] = sorted_arr[jj-1];
2396 coeff[jj] = 0.0;
2397 }
2398 le = lin_elem_exp_init(nzeros+1,nodes,coeff);
2399 free(nodes); nodes = NULL;
2400 free(coeff); coeff = NULL;
2401 }
2402 else if ((onlb == 1) && (onub == 0)){
2403 double * nodes = calloc_double(nzeros+1);
2404 double * coeff = calloc_double(nzeros+1);
2405 nodes[0] = opts->lb;
2406 coeff[0] = 0.0;
2407 for (size_t jj = 1; jj < nzeros; jj++){
2408 nodes[jj] = sorted_arr[jj-1];
2409 coeff[jj] = 0.0;
2410 }
2411 nodes[nzeros] = opts->ub;
2412 coeff[nzeros] = 1.0;
2413 le = lin_elem_exp_init(nzeros+1,nodes,coeff);
2414 free(nodes); nodes = NULL;
2415 free(coeff); coeff = NULL;
2416 }
2417 else if ((onlb == 0) && (onub == 1)){
2418 double * nodes = calloc_double(nzeros+1);
2419 double * coeff = calloc_double(nzeros+1);
2420 nodes[0] = opts->lb;
2421 coeff[0] = 1.0;
2422 for (size_t jj = 1; jj< nzeros+1; jj++){
2423 nodes[jj] = sorted_arr[jj-1];
2424 coeff[jj] = 0.0;
2425 }
2426 le = lin_elem_exp_init(nzeros+1,nodes,coeff);
2427 free(nodes); nodes = NULL;
2428 free(coeff); coeff = NULL;
2429 }
2430 else{
2431 assert (1 == 0);
2432 }
2433 /* print_lin_elem_exp(le,3,NULL,stdout); */
2434 return le;
2435 }
2436 }
2437 else{
2438 assert(opts->nodes != NULL);
2439 assert(opts->num_nodes > nzeros);
2440 double * coeff = calloc_double(opts->num_nodes);
2441 struct LinElemExp * le = lin_elem_exp_init(opts->num_nodes, opts->nodes, coeff);
2442 for (size_t ii = 0; ii < opts->num_nodes; ii++){
2443 int nonzero = 1;
2444 for (size_t jj = 0; jj < nzeros; jj++){
2445 if (fabs(le->nodes[ii] - zero_locs[jj]) < 1e-14){
2446 nonzero = 0;
2447 break;
2448 }
2449 }
2450 if (nonzero == 1){
2451 le->coeff[ii] = 1.0;
2452 }
2453 }
2454 free(coeff);
2455 return le;
2456 }
2457 }
2458
2459 /********************************************************//**
2460 Print a linear element function
2461
2462 \param[in] f - linear element function
2463 \param[in] args - extra arguments (not used I think)
2464 \param[in] prec - precision with which to save it
2465 \param[in] stream - stream to print to
2466 ************************************************************/
print_lin_elem_exp(const struct LinElemExp * f,size_t prec,void * args,FILE * stream)2467 void print_lin_elem_exp(const struct LinElemExp * f, size_t prec,
2468 void * args, FILE * stream)
2469 {
2470 if (f == NULL){
2471 fprintf(stream, "Lin Elem Expansion is NULL\n");
2472 }
2473 else{
2474 assert (args == NULL);
2475 fprintf(stream, "Lin Elem Expansion: num_nodes=%zu\n",f->num_nodes);
2476 for (size_t ii = 0; ii < f->num_nodes; ii++){
2477 if (prec < 100){
2478 fprintf(stream, "(%3.*G,%3.*G) ",(int)prec,f->nodes[ii],
2479 (int)prec,f->coeff[ii]);
2480 }
2481 }
2482 fprintf(stream,"\n");
2483 }
2484 }
2485
2486 /********************************************************//**
2487 Save a linear element expansion in text format
2488
2489 \param[in] f - function to save
2490 \param[in] stream - stream to save it to
2491 \param[in] prec - precision with which to save it
2492 ************************************************************/
lin_elem_exp_savetxt(const struct LinElemExp * f,FILE * stream,size_t prec)2493 void lin_elem_exp_savetxt(const struct LinElemExp * f,
2494 FILE * stream, size_t prec)
2495 {
2496 assert (f != NULL);
2497 fprintf(stream,"%zu ",f->num_nodes);
2498 for (size_t ii = 0; ii < f->num_nodes; ii++){
2499 if (prec < 100){
2500 fprintf(stream, "%3.*G ",(int)prec,f->nodes[ii]);
2501 fprintf(stream, "%3.*G ",(int)prec,f->coeff[ii]);
2502 }
2503 }
2504 }
2505
2506 /********************************************************//**
2507 Load a linear element expansion in text format
2508
2509 \param[in] stream - stream to save it to
2510
2511 \return Linear Element Expansion
2512 ************************************************************/
lin_elem_exp_loadtxt(FILE * stream)2513 struct LinElemExp * lin_elem_exp_loadtxt(FILE * stream)//, size_t prec)
2514 {
2515 size_t num_nodes;
2516
2517 int num = fscanf(stream,"%zu ",&num_nodes);
2518 assert (num == 1);
2519 /* printf("number of nodes read = %zu\n",num_nodes); */
2520 double * nodes = calloc_double(num_nodes);
2521 double * coeff = calloc_double(num_nodes);
2522 for (size_t ii = 0; ii < num_nodes; ii++){
2523 num = fscanf(stream,"%lG",nodes+ii);
2524 assert (num == 1);
2525 num = fscanf(stream,"%lG",coeff+ii);
2526 assert (num == 1);
2527 };
2528 struct LinElemExp * lexp = lin_elem_exp_init(num_nodes,nodes,coeff);
2529 free(nodes); nodes = NULL;
2530 free(coeff); coeff = NULL;
2531 return lexp;
2532 }
2533
2534