Skip to content

Commit f64b5b2

Browse files
committed
2 parents c2b1bfd + d18691a commit f64b5b2

File tree

12 files changed

+410
-88
lines changed

12 files changed

+410
-88
lines changed

LoopStructural/interpolators/cython/dsi_helper.pyx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,91 @@ def cg(double [:,:,:] EG, long long [:,:] neighbours, long long [:,:] elements,d
9393
c[ncons,position_to_write] -= norm[i]*e2[i][itr_right]*area
9494
ncons+=1
9595
return idc, c, ncons
96+
def constant_norm(double [:,:,:] EG, long long [:,:] neighbours, long long [:,:] elements,double [:,:] nodes, long long [:] region):
97+
cdef int Nc, Na, i,Ns, j, ne, ncons, e, n, neigh
98+
Nc = 5 #numer of constraints shared nodes + independent
99+
Na = 4 #number of nodes
100+
Ns = Na -1
101+
ne = len(neighbours)
102+
ncons = 0
103+
cdef int [:] flag = np.zeros(ne,dtype=np.int32)
104+
cdef double [:,:] c = np.zeros((len(neighbours)*4,Nc))
105+
cdef long long [:,:] idc = np.zeros((ne*4,5),dtype=np.int64)
106+
cdef long long [3] common
107+
cdef double [:] norm = np.zeros((3))
108+
cdef double [:,:] shared_pts = np.zeros((3,3))
109+
cdef double [:] v1 = np.zeros(3)
110+
cdef double [:] v2 = np.zeros(3)
111+
cdef double [:,:] e1
112+
cdef double [:,:] e2
113+
cdef double area = 0
114+
cdef long long [:] idl = np.zeros(4,dtype=np.int64)
115+
cdef long long [:] idr = np.zeros(4,dtype=np.int64)
116+
for e in range(ne):
117+
idl = elements[e,:]
118+
e1 = EG[e,:,:]
119+
flag[e] = 1
120+
# if not in region then skip this tetra
121+
if region[idl[0]] == 0 or region[idl[1]] == 0 or region[idl[2]] == 0 or region[idl[3]] == 0:
122+
continue
123+
for n in range(4):
124+
neigh = neighbours[e,n]
125+
idr = elements[neigh,:]
126+
if neigh < 0:
127+
continue
128+
if flag[neigh]== 1:
129+
continue
130+
131+
# if not in region then skip this tetra
132+
if region[idr[0]] == 0 or region[idr[1]] == 0 or region[idr[2]] == 0 or region[idr[3]] == 0:
133+
continue
134+
135+
e2 = EG[neigh,:,:]
136+
137+
for i in range(Nc):
138+
idc[ncons,i] = -1
139+
140+
i = 0
141+
for itr_right in range(Na):
142+
for itr_left in range(Na):
143+
if idl[itr_left] == idr[itr_right]:
144+
common[i] = idl[itr_left]
145+
i+=1
146+
for j in range(3):
147+
for k in range(3):
148+
shared_pts[j][k] = nodes[common[j]][k]#common
149+
for i in range(3):
150+
v1[i] = shared_pts[0,i] - shared_pts[1,i]
151+
v2[i] = shared_pts[2,i]-shared_pts[1,i]
152+
norm[0] = v2[2]*v1[1] - v1[2]*v2[1]
153+
norm[1] = v1[2]*v2[0] - v1[0]*v2[2]
154+
norm[2] = v1[0]*v2[1] - v1[1]*v2[0]
155+
156+
# we want to weight the cg by the area of the shared face
157+
# area of triangle is half area of parallelogram
158+
# https://math.stackexchange.com/questions/128991/how-to-calculate-the-area-of-a-3d-triangle
159+
area = 0.5*np.linalg.norm(norm)
160+
for itr_left in range(Na):
161+
idc[ncons,itr_left] = idl[itr_left]
162+
c[ncons,itr_left] = np.sqrt(e1[0][itr_left]*e1[0][itr_left]+e1[1][itr_left]*e1[1][itr_left]+e1[2][itr_left]*e1[2][itr_left])
163+
164+
next_available_position = Na
165+
for itr_right in range(Na):
166+
common_index = -1
167+
for itr_left in range(Na):
168+
if idc[ncons,itr_left] == idr[itr_right]:
169+
common_index = itr_left
170+
171+
position_to_write = 0
172+
if common_index != -1:
173+
position_to_write = common_index
174+
else:
175+
position_to_write = 4#next_available_position
176+
next_available_position+=1
177+
idc[ncons,position_to_write] = idr[itr_right]
178+
c[ncons,position_to_write] -= np.sqrt(e2[0][itr_right]*e2[0][itr_right]+e2[1][itr_right]*e2[1][itr_right]+e2[2][itr_right]*e2[2][itr_right])
179+
ncons+=1
180+
return idc, c, ncons
96181
def fold_cg(double [:,:,:] EG, double [:,:] X, long long [:,:] neighbours, long long [:,:] elements,double [:,:] nodes):
97182
cdef int Nc, Na, i,Ns, j, ne, ncons, e, n, neigh
98183
Nc = 5 #numer of constraints shared nodes + independent

LoopStructural/interpolators/discrete_fold_interpolator.py

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ def update_fold(self, fold):
8282
logger.error('updating fold, this should be done by accessing the fold attribute')
8383
self.fold = fold
8484

85-
def add_fold_constraints(self, fold_orientation=10., fold_axis_w=10., fold_regularisation=.1,
85+
def add_fold_constraints(self, fold_orientation=10., fold_axis_w=10., fold_regularisation=[.1,0.01,0.01],
8686
fold_normalisation=1.,
87-
fold_norm=1.):
87+
fold_norm=1., step=2):
8888
"""
8989
9090
Parameters
@@ -93,12 +93,15 @@ def add_fold_constraints(self, fold_orientation=10., fold_axis_w=10., fold_regul
9393
weight for the fold direction/orientation in the least squares system
9494
fold_axis_w : double
9595
weight for the fold axis in the least squares system
96-
fold_regularisation : double
96+
fold_regularisation : list
9797
weight for the fold regularisation in the least squares system
9898
fold_normalisation : double
9999
weight for the fold norm constraint in the least squares system
100100
fold_norm
101101
length of the interpolation norm in the least squares system
102+
step: int
103+
array step for adding constraints
104+
102105
103106
Returns
104107
-------
@@ -115,57 +118,84 @@ def add_fold_constraints(self, fold_orientation=10., fold_axis_w=10., fold_regul
115118
# calculate the fold geometry for the elements barycentre
116119
deformed_orientation, fold_axis, dgz = \
117120
self.fold.get_deformed_orientation(self.support.barycentre())
118-
121+
element_idx = np.arange(self.support.n_elements)
122+
np.random.shuffle(element_idx)
119123
# calculate element volume for weighting
120124
vecs = nodes[:, 1:, :] - nodes[:, 0, None, :]
121125
vol = np.abs(np.linalg.det(vecs)) / 6
122126
if fold_orientation is not None:
123127
"""
124128
dot product between vector in deformed ori plane = 0
125129
"""
130+
np.random.shuffle(element_idx)
131+
126132
logger.info("Adding fold orientation constraint to %s w = %f"%(self.propertyname, fold_orientation))
127-
A = np.einsum('ij,ijk->ik', deformed_orientation, eg)
128-
A *= vol[:, None]
133+
A = np.einsum('ij,ijk->ik', deformed_orientation[element_idx[::step],:], eg[element_idx[::step],:,:])
134+
A *= vol[element_idx[::step], None]
129135
A *= fold_orientation
130-
B = np.zeros(self.support.n_elements)
131-
idc = self.support.get_elements()
136+
B = np.zeros(A.shape[0])
137+
idc = self.support.get_elements()[element_idx[::step],:]
132138
self.add_constraints_to_least_squares(A, B, idc)
133139

134140
if fold_axis_w is not None:
135141
"""
136142
dot product between axis and gradient should be 0
137143
"""
144+
np.random.shuffle(element_idx)
145+
138146
logger.info("Adding fold axis constraint to %s w = %f"%(self.propertyname,fold_axis_w))
139-
A = np.einsum('ij,ijk->ik', fold_axis, eg)
140-
A *= vol[:, None]
147+
A = np.einsum('ij,ijk->ik', fold_axis[element_idx[::step],:], eg[element_idx[::step],:,:])
148+
A *= vol[element_idx[::step], None]
141149
A *= fold_axis_w
142-
B = np.zeros(self.support.n_elements).tolist()
143-
self.add_constraints_to_least_squares(A, B, self.support.get_elements())
150+
B = np.zeros(A.shape[0]).tolist()
151+
idc = self.support.get_elements()[element_idx[::step],:]
152+
153+
self.add_constraints_to_least_squares(A, B, idc)
144154

145155
if fold_normalisation is not None:
146156
"""
147157
specify scalar norm in X direction
148158
"""
159+
np.random.shuffle(element_idx)
160+
149161
logger.info("Adding fold normalisation constraint to %s w = %f"%(self.propertyname,fold_normalisation))
150-
A = np.einsum('ij,ijk->ik', dgz, eg)
151-
A *= vol[:, None]
162+
A = np.einsum('ij,ijk->ik', dgz[element_idx[::step],:], eg[element_idx[::step],:,:])
163+
A *= vol[element_idx[::step], None]
152164
A *= fold_normalisation
153-
B = np.ones(self.support.n_elements)
165+
B = np.ones(A.shape[0])
154166

155167
if fold_norm is not None:
156168
B[:] = fold_norm
157169
B *= fold_normalisation
158-
B *= vol
159-
self.add_constraints_to_least_squares(A, B, self.support.get_elements())
170+
B *= vol[element_idx[::step]]
171+
idc = self.support.get_elements()[element_idx[::step],:]
172+
173+
self.add_constraints_to_least_squares(A, B, idc)
160174

161175
if fold_regularisation is not None:
162176
"""
163177
fold constant gradient
164178
"""
165-
logger.info("Adding fold regularisation constraint to %s w = %f"%(self.propertyname,fold_regularisation))
179+
logger.info("Adding fold regularisation constraint to {} w = {} {} {}".format(self.propertyname,
180+
fold_regularisation[0],fold_regularisation[1],fold_regularisation[1]))
181+
166182
idc, c, ncons = fold_cg(eg, dgz, self.support.get_neighbours(), self.support.get_elements(), self.support.nodes)
167183
A = np.array(c[:ncons, :])
168-
A *= fold_regularisation
184+
A *= fold_regularisation[0]
185+
B = np.zeros(A.shape[0])
186+
idc = np.array(idc[:ncons, :])
187+
self.add_constraints_to_least_squares(A, B, idc)
188+
189+
idc, c, ncons = fold_cg(eg, deformed_orientation, self.support.get_neighbours(), self.support.get_elements(), self.support.nodes)
190+
A = np.array(c[:ncons, :])
191+
A *= fold_regularisation[1]
192+
B = np.zeros(A.shape[0])
193+
idc = np.array(idc[:ncons, :])
194+
self.add_constraints_to_least_squares(A, B, idc)
195+
196+
idc, c, ncons = fold_cg(eg, fold_axis, self.support.get_neighbours(), self.support.get_elements(), self.support.nodes)
197+
A = np.array(c[:ncons, :])
198+
A *= fold_regularisation[2]
169199
B = np.zeros(A.shape[0])
170200
idc = np.array(idc[:ncons, :])
171201
self.add_constraints_to_least_squares(A, B, idc)

LoopStructural/interpolators/discrete_interpolator.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,22 @@ def add_constraints_to_least_squares(self, A, B, idc, name='undefined'):
147147
A = np.array(A)
148148
B = np.array(B)
149149
idc = np.array(idc)
150-
if np.any(np.isnan(idc)) or np.any(np.isnan(A)) or np.any(np.isnan(B)):
151-
logger.warning("Constraints contain nan not adding constraints: {}".format(name))
152-
return
150+
153151
nr = A.shape[0]
152+
if A.shape != idc.shape:
153+
return
154+
154155
if len(A.shape) > 2:
155156
nr = A.shape[0] * A.shape[1]
157+
A = A.reshape((A.shape[0]*A.shape[1],A.shape[2]))
158+
idc = idc.reshape((idc.shape[0]*idc.shape[1],idc.shape[2]))
159+
# going to assume if any are nan they are all nan
160+
mask = np.any(np.isnan(A),axis=1)
161+
A[mask,:] = 0
162+
if np.any(np.isnan(idc)) or np.any(np.isnan(A)) or np.any(np.isnan(B)):
163+
logger.warning("Constraints contain nan not adding constraints: {}".format(name))
164+
# return
165+
156166
rows = np.arange(0, nr).astype(int)
157167
rows += self.c_
158168
constraint_ids = rows.copy()

LoopStructural/interpolators/piecewiselinear_interpolator.py

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ def _setup_interpolator(self, **kwargs):
7272
self.interpolation_weights[key] = kwargs[key]
7373
if self.interpolation_weights['cgw'] > 0.:
7474
self.up_to_date = False
75-
self.add_constant_gradient(self.interpolation_weights['cgw'])
75+
self.add_constant_gradient(self.interpolation_weights['cgw'],
76+
direction_feature=kwargs.get('direction_feature',None),
77+
direction_vector=kwargs.get('direction_vector',None)
78+
)
7679
logger.info("Using constant gradient regularisation w = %f"
7780
%self.interpolation_weights['cgw'])
7881
logger.info("Added %i gradient constraints, %i normal constraints,"
@@ -84,7 +87,10 @@ def _setup_interpolator(self, **kwargs):
8487
self.add_ctr_pts(self.interpolation_weights['cpw'])
8588
self.add_tangent_ctr_pts(self.interpolation_weights['tpw'])
8689
self.add_interface_ctr_pts(self.interpolation_weights['ipw'])
87-
def add_constant_gradient(self, w=0.1):
90+
if 'constant_norm' in kwargs:
91+
self.add_constant_norm(w=kwargs['constant_norm'])
92+
93+
def add_constant_gradient(self, w= 0.1, direction_vector=None, direction_feature=None):
8894
"""
8995
Add the constant gradient regularisation to the system
9096
@@ -96,8 +102,59 @@ def add_constant_gradient(self, w=0.1):
96102
-------
97103
98104
"""
105+
if direction_feature is not None:
106+
print('dir fe')
107+
direction_vector = direction_feature.evaluate_gradient(self.support.barycentre())
108+
if direction_vector is not None:
109+
if direction_vector.shape[0] == 1:
110+
# if using a constant direction, tile array so it works for cg calc
111+
direction_vector = np.tile(direction_vector,(self.support.barycentre().shape[0],1))
112+
113+
114+
# iterate over all elements
115+
A, idc, B = self.support.get_constant_gradient(region=self.region,direction=direction_vector)
116+
A = np.array(A)
117+
B = np.array(B)
118+
idc = np.array(idc)
119+
120+
gi = np.zeros(self.support.n_nodes)
121+
gi[:] = -1
122+
gi[self.region] = np.arange(0, self.nx)
123+
idc = gi[idc]
124+
outside = ~np.any(idc == -1, axis=1)
125+
126+
# w/=A.shape[0]
127+
self.add_constraints_to_least_squares(A[outside, :] * w,
128+
B[outside] * w, idc[outside, :],
129+
name='regularisation')
130+
return
131+
132+
def add_direction_constant_gradient(self, w= 0.1, direction_vector=None, direction_feature=None):
133+
"""
134+
Add the constant gradient regularisation to the system where regularisation is projected
135+
on a vector
136+
137+
Parameters
138+
----------
139+
w (double) - weighting of the cg parameter
140+
direction_vector
141+
direction_feature
142+
143+
Returns
144+
-------
145+
146+
"""
147+
if direction_feature:
148+
print('dir fe')
149+
direction_vector = direction_feature.evaluate_gradient(self.support.barycentre())
150+
if direction_vector:
151+
if direction_vector.shape[0] == 1:
152+
# if using a constant direction, tile array so it works for cg calc
153+
direction_vector = np.tile(direction_vector,(self.support.barycentre().shape[0],1))
154+
155+
99156
# iterate over all elements
100-
A, idc, B = self.support.get_constant_gradient(region=self.region)
157+
A, idc, B = self.support.get_constant_gradient(region=self.region,direction=direction_vector)
101158
A = np.array(A)
102159
B = np.array(B)
103160
idc = np.array(idc)
@@ -114,6 +171,37 @@ def add_constant_gradient(self, w=0.1):
114171
name='regularisation')
115172
return
116173

174+
175+
def add_constant_norm(self, w=0.1):
176+
"""
177+
Add the constant gradient regularisation to the system
178+
179+
Parameters
180+
----------
181+
w (double) - weighting of the cg parameter
182+
183+
Returns
184+
-------
185+
186+
"""
187+
# iterate over all elements
188+
A, idc, B = self.support.get_constant_norm(region=self.region)
189+
A = np.array(A)
190+
B = np.array(B)
191+
idc = np.array(idc)
192+
193+
gi = np.zeros(self.support.n_nodes)
194+
gi[:] = -1
195+
gi[self.region] = np.arange(0, self.nx)
196+
idc = gi[idc]
197+
outside = ~np.any(idc == -1, axis=1)
198+
199+
# w/=A.shape[0]
200+
self.add_constraints_to_least_squares(A[outside, :] * w,
201+
B[outside] * w, idc[outside, :],
202+
name='norm_regularisation')
203+
return
204+
117205
def add_gradient_ctr_pts(self, w=1.0):
118206
"""
119207
Adds gradient constraints to the least squares system with a weight
@@ -325,6 +413,7 @@ def add_gradient_orthogonal_constraint(self, points, vector, w=1.0,
325413
vertices, element_gradients, tetras, inside = self.support.get_tetra_gradient_for_location(points[:,:3])
326414
#e, inside = self.support.elements_for_array(points[:, :3])
327415
#nodes = self.support.nodes[self.support.elements[e]]
416+
vector /= np.linalg.norm(vector,axis=1)[:,None]
328417
vecs = vertices[:, 1:, :] - vertices[:, 0, None, :]
329418
vol = np.abs(np.linalg.det(vecs)) # / 6
330419
# d_t = self.support.get_elements_gradients(e)
@@ -340,7 +429,7 @@ def add_gradient_orthogonal_constraint(self, points, vector, w=1.0,
340429
gi[self.region] = np.arange(0, self.nx).astype(int)
341430
w /= 3
342431
idc = gi[tetras]
343-
B = np.zeros(idc.shape[0])
432+
B = np.zeros(idc.shape[0])+B
344433
outside = ~np.any(idc == -1, axis=1)
345434
self.add_constraints_to_least_squares(A[outside, :] * w,
346435
B[outside], idc[outside, :])

0 commit comments

Comments
 (0)