Skip to content

Commit 9c7e281

Browse files
Merge pull request #316 from Morpho-lang/jacobian
Improvements to functionals, including Jacobians.
2 parents 8a1f021 + 4db2767 commit 9c7e281

3 files changed

Lines changed: 147 additions & 2 deletions

File tree

src/geometry/functional.c

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3990,6 +3990,17 @@ objectintegralelementref *integral_getelementref(vm *v) {
39903990
return NULL;
39913991
}
39923992

3993+
/* ---------
3994+
* Elementid
3995+
* --------- */
3996+
3997+
static value integral_elementid(vm *v, int nargs, value *args) {
3998+
objectintegralelementref *elref = integral_getelementref(v);
3999+
if (!elref) { morpho_runtimeerror(v, INTEGRAL_SPCLFN, ELEMENTID_FUNCTION); return MORPHO_NIL; }
4000+
4001+
return MORPHO_INTEGER(elref->id);
4002+
}
4003+
39934004
/* --------
39944005
* Tangent
39954006
* -------- */
@@ -4036,6 +4047,7 @@ int normlhandle; // TL storage handle for normal vectors
40364047
/** Evaluates the normal vector */
40374048
void integral_evaluatenormal(vm *v, value *out) {
40384049
objectintegralelementref *elref = integral_getelementref(v);
4050+
40394051
if (!elref) { morpho_runtimeerror(v, INTEGRAL_SPCLFN, NORMAL_FUNCTION); return; }
40404052

40414053
int dim = elref->mesh->dim;
@@ -4334,21 +4346,113 @@ static value integral_cgfn(vm *v, int nargs, value *args) {
43344346
return out;
43354347
}
43364348

4349+
/* -------------------
4350+
* Jacobian
4351+
* ------------------- */
4352+
4353+
/*
4354+
* A reference triangle is mapped to a target triangle through a
4355+
* linear transformation (the pushforward); an inverse transformation (the pullback)
4356+
* exists if the triangle is not degenerate. This function computes the forward
4357+
* and inverse jacobians.
4358+
*/
4359+
4360+
int jacobianhandle; // TL storage handle for Jacobian
4361+
int invjacobianhandle; // TL storage handle for inverse Jacobian
4362+
4363+
void _fetchvertices(objectintegralelementref *elref, objectmesh *mesh, int nv, elementid *vid, double **x) {
4364+
// Fetch reference vertices
4365+
for (int j=0; j<nv; j++) matrix_getcolumn(elref->iref->mref->vert, vid[j], &x[j]);
4366+
}
4367+
4368+
void _edgevectors(grade g, int dim, double **x, double *out) {
4369+
for (int i=0; i<g; i++) functional_vecsub(dim, x[i+1], x[0], out + i*dim);
4370+
}
4371+
4372+
/** Evaluates the jacobian and inverse jacobian; returns either of these as requested */
4373+
void integral_evaluatejacobian(vm *v, value *jac, value *invjac) {
4374+
objectintegralelementref *elref = integral_getelementref(v);
4375+
4376+
if (!elref) {
4377+
morpho_runtimeerror(v, INTEGRAL_SPCLFN, JACOBIAN_FUNCTION); return;
4378+
}
4379+
4380+
int dim = elref->mesh->dim; // Dimension of the mesh
4381+
4382+
// Allocate matrices
4383+
objectmatrix *J=object_newmatrix(dim, dim, true);
4384+
objectmatrix *Jinv=object_newmatrix(dim, dim, true);
4385+
4386+
if (J) vm_settlvar(v, jacobianhandle, MORPHO_OBJECT(J));
4387+
if (Jinv) vm_settlvar(v, invjacobianhandle, MORPHO_OBJECT(Jinv));
4388+
4389+
if (!J || !Jinv) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return; }
4390+
4391+
// Now compute them
4392+
grade g = elref->g; // Grade of the element
4393+
int nv = elref->nv; //
4394+
4395+
double **X = elref->vertexposn; // Vertex positions of the target element
4396+
double *x[nv]; // Vertex positions of the reference element
4397+
4398+
objectmesh *mref = elref->iref->mref; // Reference mesh
4399+
if (mref) _fetchvertices(elref, mref, nv, elref->vid, x);
4400+
4401+
// Construct matrix of edge vectors for target and reference elements
4402+
double starget[dim*dim], sref[dim*dim], sinv[dim*dim];
4403+
objectmatrix St = MORPHO_STATICMATRIX(starget, dim, dim),
4404+
Sr = MORPHO_STATICMATRIX(sref, dim, dim),
4405+
Sinv = MORPHO_STATICMATRIX(sinv, dim, dim);
4406+
4407+
_edgevectors(g, dim, X, starget);
4408+
if (mref) {
4409+
_edgevectors(g, dim, x, sref);
4410+
matrix_inverse(&Sr, &Sinv);
4411+
} else {
4412+
matrix_identity(&Sinv); // If no reference, the reference is the unit triangle
4413+
}
4414+
4415+
matrix_mul(&St, &Sinv, J); // J = S . s^-1
4416+
4417+
matrix_inverse(J, Jinv); // Compute J^-1
4418+
4419+
if (jac) *jac = MORPHO_OBJECT(J);
4420+
if (invjac) *invjac = MORPHO_OBJECT(Jinv);
4421+
}
4422+
4423+
static value integral_jacobian(vm *v, int nargs, value *args) {
4424+
value out=MORPHO_NIL;
4425+
4426+
vm_gettlvar(v, jacobianhandle, &out);
4427+
if (MORPHO_ISNIL(out)) integral_evaluatejacobian(v, &out, NULL);
4428+
4429+
return out;
4430+
}
4431+
4432+
static value integral_invjacobian(vm *v, int nargs, value *args) {
4433+
value out=MORPHO_NIL;
4434+
4435+
vm_gettlvar(v, invjacobianhandle, &out);
4436+
if (MORPHO_ISNIL(out)) integral_evaluatejacobian(v, NULL, &out);
4437+
4438+
return out;
4439+
}
4440+
43374441
/* ----------------------
43384442
* General initialization
43394443
* ---------------------- */
43404444

43414445
/** Clears threadlocal storage */
43424446
void integral_cleartlvars(vm *v) {
4343-
int handles[] = { elementhandle, normlhandle, tangenthandle, cauchygreenhandle, -1 };
4447+
int handles[] = { elementhandle, normlhandle, tangenthandle, cauchygreenhandle, jacobianhandle, invjacobianhandle, -1 };
43444448

43454449
for (int i=0; handles[i]>=0; i++) {
43464450
vm_settlvar(v, handles[i], MORPHO_NIL);
43474451
}
43484452
}
43494453

43504454
void integral_freetlvars(vm *v) {
4351-
int handles[] = { normlhandle, tangenthandle, cauchygreenhandle, -1 };
4455+
int handles[] = { normlhandle, tangenthandle, cauchygreenhandle,jacobianhandle, invjacobianhandle, -1 };
43524456

43534457
for (int i=0; handles[i]>=0; i++) {
43544458
value val;
@@ -4948,10 +5052,13 @@ void functional_initialize(void) {
49485052
builtin_addclass(NEMATIC_CLASSNAME, MORPHO_GETCLASSDEFINITION(Nematic), objclass);
49495053
builtin_addclass(NEMATICELECTRIC_CLASSNAME, MORPHO_GETCLASSDEFINITION(NematicElectric), objclass);
49505054

5055+
builtin_addfunction(ELEMENTID_FUNCTION, integral_elementid, BUILTIN_FLAGSEMPTY);
49515056
builtin_addfunction(TANGENT_FUNCTION, integral_tangent, BUILTIN_FLAGSEMPTY);
49525057
builtin_addfunction(NORMAL_FUNCTION, integral_normal, BUILTIN_FLAGSEMPTY);
49535058
builtin_addfunction(GRAD_FUNCTION, integral_gradfn, BUILTIN_FLAGSEMPTY);
49545059
builtin_addfunction(CGTENSOR_FUNCTION, integral_cgfn, BUILTIN_FLAGSEMPTY);
5060+
builtin_addfunction(JACOBIAN_FUNCTION, integral_jacobian, BUILTIN_FLAGSEMPTY);
5061+
builtin_addfunction(INVJACOBIAN_FUNCTION, integral_invjacobian, BUILTIN_FLAGSEMPTY);
49555062

49565063
morpho_defineerror(VOLUMEENCLOSED_ZERO, ERROR_HALT, VOLUMEENCLOSED_ZERO_MSG);
49575064
morpho_defineerror(FUNC_INTEGRAND_MESH, ERROR_HALT, FUNC_INTEGRAND_MESH_MSG);
@@ -4990,6 +5097,8 @@ void functional_initialize(void) {
49905097
tangenthandle=vm_addtlvar();
49915098
normlhandle=vm_addtlvar();
49925099
cauchygreenhandle=vm_addtlvar();
5100+
jacobianhandle=vm_addtlvar();
5101+
invjacobianhandle=vm_addtlvar();
49935102

49945103
morpho_addfinalizefn(functional_finalize);
49955104
}

src/geometry/functional.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@
5656
#define FUNCTIONAL_INTEGRANDFORELEMENT_METHOD "integrandForElement"
5757

5858
/* Special functions that can be used in integrands */
59+
#define ELEMENTID_FUNCTION "elementid"
5960
#define TANGENT_FUNCTION "tangent"
6061
#define NORMAL_FUNCTION "normal"
6162
#define GRAD_FUNCTION "grad"
6263
#define CGTENSOR_FUNCTION "cgtensor"
64+
#define JACOBIAN_FUNCTION "jacobian"
65+
#define INVJACOBIAN_FUNCTION "invjacobian"
6366

6467
/* Functional names */
6568
#define LENGTH_CLASSNAME "Length"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Test the jacobian() function
2+
3+
import meshtools
4+
5+
// Create the reference triangle
6+
var mb = MeshBuilder()
7+
mb.addvertex([0,0])
8+
mb.addvertex([1,0])
9+
mb.addvertex([0,1])
10+
mb.addedge([0,1])
11+
mb.addedge([1,2])
12+
mb.addedge([2,0])
13+
mb.addface([0,1,2])
14+
15+
var mref = mb.build()
16+
17+
// Make the target triangle by cloning the reference..
18+
var m = mref.clone()
19+
m.setvertexmatrix(2*m.vertexmatrix()) // ... and scaling by 2
20+
21+
fn integrand(x) {
22+
var F = jacobian()
23+
//var Jinv = invjacobian()
24+
var Ft = F.transpose()
25+
26+
return (F*Ft).trace()
27+
}
28+
29+
var a=AreaIntegral(integrand, reference=mref, weightByReference=true).total(m)
30+
31+
print a
32+
// expect: 4
33+
// because Tr(F*Ft)=8; and the area of the reference tri. is 1/2

0 commit comments

Comments
 (0)