;;; An Element is a basis element of a finite-element space. ;;; All the elements in a space are ordered and indexed; ;;; it's useful if the element knows its index (I think). ;;; An element's neighbors are the indices of the other elements ;;; whose support has nontrivial intersection with this element. (define-class Element Object ((= index) (= neighbors))) ; including itself ;; we ensure that the index is the *last* element of the neighbors list. (define-handy-method (initialize! (e Element)) (let loop ((nabors (Neighbors->list neighbors)) (result (list index))) (if (null? nabors) (set! neighbors (Neighbors<-list result)) (loop (cdr nabors) (if (eq? (car nabors) index) result (cons (car nabors) result))))) (call-next-method)) ;;; Neighbors will be used in various parts of the the system, so ;;; we turn it into an abstract data type with ;;; constructors, deconstructors, accessors, and modifiers. (define (Neighbors->list n) (vector->list n)) (define (Neighbors<-list l) (list->vector l)) (define (Neighbors-ref n i) (vector-ref n i)) (define (Neighbors-set! n i val) (vector-set! n i val)) (define (Neighbors-length n) (vector-length n)) ;;; I anticipate there being Cubic-elements, mixed elements, etc. ;;; Now we will use just Linear-element (piecewise linear, continuous ;;; on triangles). (define-class Linear-element Element ()) ;;; A finite element space of any kind is associated with a triangulation, ;;; and has elements, each of which is an Element. ;;; Note that we're going to layer this as much as possible; the ;;; Finite-element-space is an object that doesn't contain the ;;; numerical coordinates of any vector in the space, that comes in ;;; the Finite-element-vector. (define-class Finite-element-space Object ((= triangulation) (= elements) ; the elements of the finite element space (= neighbors))) ; the elements' neighbors ;;; Neighbors and elements are stored in a vector (define (Finite-element-space-elements->list e) (vector->list e)) (define (Finite-element-space-elements<-list l) (list->vector l)) (define (Finite-element-space-neighbors->list e) (vector->list e)) (define (Finite-element-space-neighbors<-list l) (list->vector l)) (define (Finite-element-space-neighbors-length space) (vector-length (Finite-element-space-neighbors space))) ;;; A linear finite element space, i.e., a space containing Linear-elements. (define-class Linear-finite-element-space Finite-element-space ()) ;;; A Finite-element-vector contains a (pointer to) a Finite-element-space ;;; and an f64vector containing the numerical data of the vector. ;;; Note: this class is treated as a parameterized class, with the parameter ;;; being the space. (define-class Finite-element-vector Object ((= space) (= data maybe-uninitialized: immutable:))) (define-handy-method (initialize! (v Finite-element-vector)) ;; set it to zero if not otherwise initialized (if (not (field-defined? v 'data)) (initialize-field-value! v (make-f64vector (Finite-element-space-neighbors-length space) 0.0) 'data)) (call-next-method)) ;;; Since it's a subclass of Vector, we need to define all the ;;; methods for that space. We mainly use the generic methods ;;; on the data fields. (define-method (Vector-dimension (x Finite-element-vector)) (f64vector-length (Finite-element-vector-data x))) (define-handy-method (check-same-class (x Finite-element-vector) y) (and (call-next-method) (eq? space (Finite-element-vector-space y)))) (define-method (Vector-add (x Finite-element-vector) y) (if (check-same-class x y) (instantiate-from (x Finite-element-vector) data: (Vector-add (Finite-element-vector-data x) (Finite-element-vector-data y))) (error "Vector-add---underlying finite element spaces are not the same"))) (define-method (Vector-subtract (x Finite-element-vector) y) (if (check-same-class x y) (instantiate-from (x Finite-element-vector) data: (Vector-subtract (Finite-element-vector-data x) (Finite-element-vector-data y))) (error "Vector-subtract---underlying finite element spaces are not the same"))) (define-handy-method (Vector-scale x (y Finite-element-vector)) (instantiate-from (y Finite-element-vector) data: (Vector-scale x data))) (define-method (Vector-*axpy a (x Finite-element-vector) y) (if (check-same-class x y) (instantiate-from (x Finite-element-vector) data: (Vector-*axpy a (Finite-element-vector-data x) (Finite-element-vector-data y))) (error "Vector-*axpy---underlying finite element spaces are not the same"))) (define-method (Vector-dot-product (x Finite-element-vector) y) (if (check-same-class x y) (Vector-dot-product (Finite-element-vector-data x) (Finite-element-vector-data y)) (error "Vector-dot-product---underlying finite element spaces are not the same"))) (define-handy-method (Vector-zero (x Finite-element-vector)) (instantiate-from (x Finite-element-vector) data: (Vector-zero data))) (define-handy-method (Vector-copy (x Finite-element-vector)) ;; new copy of data, don't copy space (instantiate-from (x Finite-element-vector) data: (Vector-copy data))) ;;; I thought it would be useful to define a subclass for Linear-finite-element-vector (define-class Linear-finite-element-vector Finite-element-vector ()) ;;; It did prove to be useful---I'll define max and min for ;;; Linear-finite-element-vectors since it makes sense there (somewhat) ;;; with the Lagrange basis elements; I won't define it for general ;;; Finite-element-vectors. (define-method (Lattice-min (x Linear-finite-element-vector) y) (if (check-same-class x y) (instantiate-from (x Finite-element-vector) data: (Lattice-min (Finite-element-vector-data x) (Finite-element-vector-data y))) (error "Lattice-min---underlying finite element spaces are not the same"))) (define-method (Lattice-max (x Linear-finite-element-vector) y) (if (check-same-class x y) (instantiate-from (x Finite-element-vector) data: (Lattice-max (Finite-element-vector-data x) (Finite-element-vector-data y))) (error "Lattice-max---underlying finite element spaces are not the same"))) ;;; it's useful for testing purposes to be able to interpolate a given function to ;;; a linear finite element space (define (interpolate-procedure->Linear-finite-element-vector u fes) ;; u is a function of Point p ;; fes is a Linear-finite-element-space (let ((t (Finite-element-space-triangulation fes))) (instantiate Linear-finite-element-vector data: (let ((result (make-f64vector (Triangulation-vertices-length t)))) ;; we've constructed the f64vector, now we fill it with the values ;; of u at (Vertex-point v) of each vertex in t (Triangulation-for-each-vertex (lambda (v) (with-access v (Vertex point index) (f64vector-set! result index (u point)))) t) ;; return it so it can be placed in Linear-finite-element-vector-data result) space: fes)))