Skip to content

Commit 8b9f183

Browse files
committed
Fixes issues with external state variables
1 parent 98e4acd commit 8b9f183

5 files changed

Lines changed: 106 additions & 56 deletions

File tree

bindings/python/mgis/fenics/gradient_flux.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def __init__(self, variable, expression, name, symmetric=None):
7777
self.variable = variable
7878
if symmetric is None:
7979
self.expression = expression
80+
# TODO: treat axisymmetric case
8081
elif symmetric:
8182
if ufl.shape(expression) == (2, 2):
8283
self.expression = as_vector([symmetric_tensor_to_vector(expression)[i] for i in range(4)])
@@ -124,8 +125,8 @@ def _evaluate_at_quadrature_points(self, x):
124125

125126
class Var(Gradient):
126127
""" A simple variable """
127-
# def __init__(self, variable, name):
128-
# return Gradient.__init__(self, variable, variable, name)
128+
def __init__(self, variable, expression, name):
129+
Gradient.__init__(self, variable, expression, name)
129130

130131
def _evaluate_at_quadrature_points(self, x):
131132
local_project(x, self.function_space, self.dx, self.function)

bindings/python/mgis/fenics/nonlinear_material.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"""
1010
import mgis.behaviour as mgis_bv
1111
from .gradient_flux import Var
12+
from .utils import compute_on_quadrature
1213
import dolfin
1314
import subprocess
1415
import os
@@ -24,7 +25,8 @@ class MFrontNonlinearMaterial:
2425
This class handles the loading of a MFront behaviour through MGIS.
2526
"""
2627
def __init__(self, path, name, hypothesis="3d",
27-
material_properties={}, parameters={}):
28+
material_properties={}, parameters={},
29+
rotation_matrix=None):
2830
"""
2931
Parameters
3032
-----------
@@ -42,12 +44,17 @@ def __init__(self, path, name, hypothesis="3d",
4244
parameters : dict
4345
a dictionary of parameters. The dictionary keys must match the parameter
4446
names declared in the MFront behaviour. Values must be constants.
47+
rotation_matrix : Numpy array, list of list, UFL matrix
48+
a 3D rotation matrix expressing the rotation from the global
49+
frame to the material frame. The matrix can be spatially variable
50+
(either UFL matrix or function of Tensor type)
4551
"""
4652
self.path = path
4753
self.name = name
4854
# Defining the modelling hypothesis
4955
self.hypothesis = mgis_hypothesis[hypothesis]
5056
self.material_properties = material_properties
57+
self.rotation_matrix = rotation_matrix
5158
# Loading the behaviour
5259
try:
5360
self.load_behaviour()
@@ -74,44 +81,39 @@ def load_behaviour(self):
7481
else:
7582
self.behaviour = mgis_bv.load(self.path, self.name, self.hypothesis)
7683

77-
def set_data_manager(self, ngauss):
84+
def set_data_manager(self, degree, ngauss, mesh):
7885
# Setting the material data manager
7986
self.data_manager = mgis_bv.MaterialDataManager(self.behaviour, ngauss)
80-
self.update_material_properties()
87+
self.update_material_properties(degree, mesh)
8188

8289
def update_parameters(self, parameters):
8390
for (key, value) in parameters.items():
8491
self.behaviour.setParameter(key, value)
8592

86-
def update_material_properties(self, material_properties=None):
93+
def update_material_properties(self, degree, mesh, material_properties=None):
8794
if material_properties is not None:
8895
self.material_properties = material_properties
8996
for s in [self.data_manager.s0, self.data_manager.s1]:
9097
for (key, value) in self.material_properties.items():
9198
if type(value) in [int, float]:
9299
mgis_bv.setMaterialProperty(s, key, value)
93100
else:
94-
if isinstance(value, dolfin.Function):
95-
value = value.vector().get_local()
96-
mgis_bv.setMaterialProperty(s, key, value, mgis_bv.MaterialStateManagerStorageMode.LocalStorage)
101+
values = compute_on_quadrature(value, mesh, degree).vector().get_local()
102+
mgis_bv.setMaterialProperty(s, key, values, mgis_bv.MaterialStateManagerStorageMode.LocalStorage)
97103

98-
def update_external_state_variables(self, external_state_variables):
104+
def update_external_state_variables(self, degree, mesh, external_state_variables):
99105
s = self.data_manager.s1
100106
for (key, value) in external_state_variables.items():
101107
if type(value) in [int, float]:
102108
mgis_bv.setExternalStateVariable(s, key, value)
103109
elif isinstance(value, dolfin.Constant):
104110
mgis_bv.setExternalStateVariable(s, key, float(value))
105111
else:
106-
if isinstance(value, dolfin.Function):
107-
values = value.vector().get_local()
108-
elif isinstance(value, Var):
112+
if isinstance(value, Var):
109113
value.update()
110114
values = value.function.vector().get_local()
111115
else:
112-
print(isinstance(value, Var))
113-
print(value)
114-
raise NotImplementedError("{} type is not supported for external state variables".format(type(value)))
116+
values = compute_on_quadrature(value, mesh, degree).vector().get_local()
115117
mgis_bv.setExternalStateVariable(s, key, values, mgis_bv.MaterialStateManagerStorageMode.LocalStorage)
116118

117119
def get_parameter(self, name):

bindings/python/mgis/fenics/nonlinear_problem.py

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def __init__(self, u, material, quadrature_degree=2, bcs=None):
6161
# compute Gauss points numbers
6262
self.ngauss = Function(self.W0).vector().local_size()
6363
# Set data manager
64-
self.material.set_data_manager(self.ngauss)
64+
self.material.set_data_manager(self.quadrature_degree, self.ngauss, self.mesh)
6565
# dummy function used for computing global quantity
6666
self._dummy_function = Function(self.W0)
6767

@@ -90,6 +90,14 @@ def __init__(self, u, material, quadrature_degree=2, bcs=None):
9090

9191
self.dt = 0
9292

93+
if self.material.rotation_matrix is not None:
94+
if isinstance(self.material.rotation_matrix, (list, tuple, np.ndarray)):
95+
self.rotation_values = np.asarray(self.material.rotation_matrix).ravel()
96+
else:
97+
self.rotation_values = compute_on_quadrature(self.material.rotation_matrix,
98+
self.mesh, self.quadrature_degree)
99+
self.rotation_values = self.rotation_values.vector().get_local()
100+
93101
self.state_variables = {"internal": None,
94102
"external": dict.fromkeys(self.material.get_external_state_variable_names(), None)}
95103
self.gradients = dict.fromkeys(self.material.get_gradient_names(), None)
@@ -194,12 +202,9 @@ def register_external_state_variable(self, name, expression):
194202
vtype = self.material.behaviour.external_state_variables[pos].type
195203
if vtype != mgis_bv.VariableType.Scalar:
196204
raise NotImplementedError("Only scalar external state variables are handled")
197-
if isinstance(expression, Constant):
198-
self.state_variables["external"].update({name: expression})
199-
elif type(expression) == float:
200-
self.state_variables["external"].update({name: Constant(expression)})
201-
else:
202-
self.state_variables["external"].update({name: Var(self.u, expression, name)})
205+
if type(expression) == float:
206+
expression = Constant(expression)
207+
self.state_variables["external"].update({name: Var(self.u, expression, name)})
203208

204209
def set_loading(self, Fext):
205210
"""
@@ -212,38 +217,20 @@ def set_loading(self, Fext):
212217
"""
213218
self._Fext = ufl.replace(Fext, {self.u: self.u_})
214219

215-
def set_quadrature_function_spaces(self):
216-
cell = self.mesh.ufl_cell()
217-
W0e = get_quadrature_element(cell, self.quadrature_degree)
218-
# scalar quadrature space
219-
self.W0 = FunctionSpace(self.mesh, W0e)
220-
# compute Gauss points numbers
221-
self.ngauss = Function(self.W0).vector().local_size()
222-
# Set data manager
223-
self.material.set_data_manager(self.ngauss)
224-
225-
# Get strain measure dimension
226-
self.strain_dim = ufl.shape(self.strain_measure(self.u))[0]
227-
# Define quadrature spaces for stress/strain and tangent matrix
228-
Wsige = get_quadrature_element(cell, self.quadrature_degree, self.strain_dim)
229-
# stress/strain quadrature space
230-
self.Wsig = FunctionSpace(self.mesh, Wsige)
231-
Wce = get_quadrature_element(cell, self.quadrature_degree, (self.strain_dim, self.strain_dim))
232-
# tangent matrix quadrature space
233-
self.WCt = FunctionSpace(self.mesh, Wce)
234-
235220
def initialize_external_state_variables(self):
236221
for (s, size) in zip(self.material.get_external_state_variable_names(), self.material.get_external_state_variable_sizes()):
237222
state_var = self.state_variables["external"][s]
238-
if isinstance(state_var, Gradient):
239-
state_var.initialize_function(self.mesh, self.quadrature_degree)
240-
values = state_var.function.vector().get_local()
241-
mgis_bv.setExternalStateVariable(self.material.data_manager.s0, s,
242-
values, mgis_bv.MaterialStateManagerStorageMode.LocalStorage)
243-
elif isinstance(state_var, Constant):
223+
if isinstance(state_var, Constant):
244224
mgis_bv.setExternalStateVariable(self.material.data_manager.s0, s, float(state_var))
245225
else:
246-
raise ValueError("External state variable '{}' has not been registered.".format(s))
226+
if isinstance(state_var, Var):
227+
state_var.initialize_function(self.mesh, self.quadrature_degree)
228+
values = state_var.function.vector().get_local()
229+
else:
230+
values = compute_on_quadrature(state_var, self.mesh,
231+
self.quadrature_degree).vector().get_local()
232+
mgis_bv.setExternalStateVariable(self.material.data_manager.s0, s,
233+
values, mgis_bv.MaterialStateManagerStorageMode.LocalStorage)
247234

248235
def initialize_gradients(self):
249236
buff=0
@@ -287,7 +274,7 @@ def initialize_tangent_blocks(self):
287274
except:
288275
value = self.state_variables["external"].get(t[1], None)
289276
if value is not None and isinstance(value, Var):
290-
flux_gradients.append(value)
277+
flux_gradients.append(value)
291278
else:
292279
raise ValueError("'{}' could not be associated with a registered gradient or state variable.".format(t[1]))
293280
self.fluxes[f].initialize_tangent_blocks(flux_gradients)
@@ -347,15 +334,23 @@ def update_tangent_blocks(self):
347334
except KeyError:
348335
raise KeyError("'{}' could not be found as a flux or an internal state variable.")
349336
block_shape = self.flattened_block_shapes[i]
350-
t.vector().set_local(self.material.data_manager.K[:,buff:buff+block_shape].flatten())
337+
tang_block_vals = self.material.data_manager.K[:,buff:buff+block_shape].ravel()
338+
if self.material.rotation_matrix is not None:
339+
mgis_bv.rotateTangentOperatorBlocks(tang_block_vals, self.material.behaviour,
340+
self.rotation_values)
341+
t.vector().set_local(tang_block_vals)
351342
buff += block_shape
352343

353344
def update_fluxes(self):
354345
buff = 0
355346
for (i, f) in enumerate(self.material.get_flux_names()):
356347
flux = self.fluxes[f]
357348
block_shape = self.material.get_flux_sizes()[i]
358-
flux.function.vector().set_local(self.material.data_manager.s1.thermodynamic_forces[:,buff:buff+block_shape].flatten())
349+
flux_vals = self.material.data_manager.s1.thermodynamic_forces[:,buff:buff+block_shape].ravel()
350+
if self.material.rotation_matrix is not None:
351+
mgis_bv.rotateThermodynamicForces(flux_vals, self.material.behaviour,
352+
self.rotation_values)
353+
flux.function.vector().set_local(flux_vals)
359354
buff += block_shape
360355

361356
def update_gradients(self):
@@ -365,6 +360,9 @@ def update_gradients(self):
365360
gradient.update()
366361
block_shape = self.material.get_gradient_sizes()[i]
367362
grad_vals = gradient.function.vector().get_local()
363+
if self.material.rotation_matrix is not None:
364+
mgis_bv.rotateGradients(grad_vals, self.material.behaviour,
365+
self.rotation_values)
368366
if gradient.shape > 0:
369367
grad_vals = grad_vals.reshape((self.material.data_manager.n, gradient.shape))
370368
else:
@@ -384,7 +382,8 @@ def update_internal_state_variables(self):
384382
def update_constitutive_law(self):
385383
"""Performs the consitutive law update"""
386384
self.update_gradients()
387-
self.material.update_external_state_variables(self.state_variables["external"])
385+
self.material.update_external_state_variables(self.quadrature_degree, self.mesh,
386+
self.state_variables["external"])
388387
# integrate the behaviour
389388
mgis_bv.integrate(self.material.data_manager, self.integration_type,
390389
self.dt, 0, self.material.data_manager.n);

bindings/python/mgis/fenics/utils.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,48 @@ def get_quadrature_element(cell, degree, dim=0):
118118
elif type(dim) == tuple:
119119
return TensorElement("Quadrature", cell, degree=degree, shape=dim, quad_scheme='default')
120120
else:
121-
raise ValueError("Wrong shape for dim=", dim)
121+
raise ValueError("Wrong shape for dim=", dim)
122+
123+
def interpolate_on_quadrature(f, degree):
124+
V = f.function_space()
125+
Ve = V.ufl_element()
126+
if Ve.family()=="Quadrature":
127+
return f
128+
else:
129+
shape = Ve.value_shape()
130+
if len(shape) == 1:
131+
shape = shape[0]
132+
Vge = Ve.__class__("Quadrature", Ve.cell(), degree, shape,
133+
quad_scheme="default")
134+
Vg = FunctionSpace(V.mesh(), Vge)
135+
fg = Function(Vg)
136+
fg.interpolate(f)
137+
return fg
138+
139+
def project_on_quadrature(f, mesh, degree):
140+
shape = f.ufl_shape
141+
Vge = get_quadrature_element(mesh.ufl_cell(), degree, shape)
142+
Vg = FunctionSpace(mesh, Vge)
143+
dx = Measure("dx", domain=mesh, metadata={"quadrature_scheme": "default",
144+
"quadrature_degree": degree})
145+
fg = Function(Vg)
146+
fg.assign(local_project(f, Vg, dx))
147+
return fg
148+
149+
def compute_on_quadrature(f, mesh, degree):
150+
shape = f.ufl_shape
151+
Vge = get_quadrature_element(mesh.ufl_cell(), degree, shape)
152+
Vg = FunctionSpace(mesh, Vge)
153+
dx = Measure("dx", domain=mesh, metadata={"quadrature_scheme": "default",
154+
"quadrature_degree": degree})
155+
fg = Function(Vg)
156+
if (isinstance(f, function.function.Function) or
157+
isinstance(f, function.function.Constant) or
158+
isinstance(f, function.function.Expression)):
159+
fg.interpolate(f)
160+
# return interpolate_on_quadrature(f, degree)
161+
else:
162+
fg.assign(local_project(f, Vg, dx))
163+
return fg
164+
# fg.interpolate(f)
165+
# return project_on_quadrature(f, degree)

docs/web/release-notes-1.2.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ module:
127127

128128
# Issues solved
129129

130+
## Issue #66: `mgis.fenics`: Missing update of external state variables when seen as UFL expression
131+
132+
Declaring external state variables as UFL expressions were not properly updated between time steps.
133+
130134
## Issues #54: Inform the calling code about `@DissipatedEnergy` and/or `@InternalEnergy`
131135

132136
The `Behaviour` class now exposes two new boolean data members:
@@ -241,4 +245,4 @@ The previous instruction imports the `mgis::MFrontGenericInterface` target, whic
241245

242246
For details, see <https://github.com/thelfer/MFrontGenericInterfaceSupport/issues/25>
243247

244-
# References
248+
# References

0 commit comments

Comments
 (0)