def binary_constraints(self, lower_bounds, upper_bounds): if upper_bounds is None: raise ValueError("Please supply upper bounds for the binary control variables.") n_turbines = len(self._parameters['binary']) lower_bounds = n_turbines*[dolfin_adjoint.Constant(lower_bounds)] upper_bounds = n_turbines*[dolfin_adjoint.Constant(upper_bounds)] return lower_bounds, upper_bounds
def _build_function_space(self): class Exterior(fa.SubDomain): def inside(self, x, on_boundary): return on_boundary and (fa.near(x[1], 1) or fa.near(x[0], 1) or fa.near(x[0], 0) or fa.near(x[1], 0)) class Left(fa.SubDomain): def inside(self, x, on_boundary): return on_boundary and fa.near(x[0], 0) class Right(fa.SubDomain): def inside(self, x, on_boundary): return on_boundary and fa.near(x[0], 1) class Bottom(fa.SubDomain): def inside(self, x, on_boundary): return on_boundary and fa.near(x[1], 0) class Top(fa.SubDomain): def inside(self, x, on_boundary): return on_boundary and fa.near(x[1], 1) class Interior(fa.SubDomain): def inside(self, x, on_boundary): return on_boundary and (x[0] > 0.1 and x[0] < 0.9 and x[1] > 0.1 and x[1] < 0.9) self.exteriors_dic = { 'left': Left(), 'right': Right(), 'bottom': Bottom(), 'top': Top() } self.exterior = Exterior() self.interior = Interior() self.V = fa.FunctionSpace(self.mesh, 'P', 1) self.source = da.Expression(("100*sin(2*pi*x[0])"), degree=3) # self.source = da.Expression("k*100*exp( (-(x[0]-x0)*(x[0]-x0) -(x[1]-x1)*(x[1]-x1)) / (2*0.01*l) )", # k=1, l=1, x0=0.9, x1=0.1, degree=3) # self.source = da.Constant(10) self.source = da.interpolate(self.source, self.V) boundary_fn_ext = da.Constant(1.) boundary_fn_int = da.Constant(1.) boundary_bc_ext = da.DirichletBC(self.V, boundary_fn_ext, self.exterior) boundary_bc_int = da.DirichletBC(self.V, boundary_fn_int, self.interior) self.bcs = [boundary_bc_ext, boundary_bc_int]
def test_strain(isochoric=True): mesh = dolfin_adjoint.UnitCubeMesh(3, 3, 3) x_dir = dolfin_adjoint.Constant([1.0, 0.0, 0.0]) y_dir = dolfin_adjoint.Constant([0.0, 1.0, 0.0]) V = dolfin.VectorFunctionSpace(mesh, "CG", 2) # u = dolfin_adjoint.Function(V) # Make an incompressible deformation (with J = 1) x_strain = -0.1 y_strain = 1 / (1 + x_strain) - 1 u = dolfin_adjoint.interpolate( dolfin.Expression( ("x_strain * x[0]", "y_strain * x[1]", "0.0"), x_strain=x_strain, y_strain=y_strain, degree=2, ), V, ) x_strain_obs = StrainObservation(mesh=mesh, field=x_dir, isochoric=isochoric, strain_tensor="gradu") assert abs(x_strain_obs(u).vector().get_local()[0] - x_strain) < 1e-12 x_strain_obs = StrainObservation(mesh=mesh, field=x_dir, isochoric=isochoric, strain_tensor="E") assert (abs( x_strain_obs(u).vector().get_local()[0] - 0.5 * ((1 + x_strain)**2 - 1)) < 1e-12) y_strain_obs = StrainObservation(mesh=mesh, field=y_dir, isochoric=isochoric, strain_tensor="gradu") assert abs(y_strain_obs(u).vector().get_local()[0] - y_strain) < 1e-12 y_strain_obs = StrainObservation(mesh=mesh, field=y_dir, isochoric=isochoric, strain_tensor="E") assert (abs( y_strain_obs(u).vector().get_local()[0] - 0.5 * ((1 + y_strain)**2 - 1)) < 1e-12)
def friction_constraints(self, lower_bounds=0., upper_bounds=None): if (upper_bounds == None): upper_bounds = upper_bounds = self._turbine_specification.friction n_turbines = len(self.turbine_positions) if (self.n_time_steps == None): self.n_time_steps = 0 elif (not self._turbine_specification.controls.dynamic_friction): self.n_time_steps = 0 lower_bounds = (self.n_time_steps+1)*n_turbines*\ [dolfin_adjoint.Constant(lower_bounds)] upper_bounds = (self.n_time_steps+1)*n_turbines*\ [dolfin_adjoint.Constant(upper_bounds)] return lower_bounds, upper_bounds
def __init__(self, bcs, solver_parameters, pressure): self._init_pressures(bcs["pressure"], pressure["p_lv"], "lv") self.p_lv.assign(dolfin_adjoint.Constant(float(self.lv_pressure[0]))) if "p_rv" in pressure: self.has_rv = True self._init_pressures(bcs["rv_pressure"], pressure["p_rv"], "rv") self.p_rv.assign( dolfin_adjoint.Constant(float(self.rv_pressure[0]))) else: self.has_rv = False # Mechanical solver Active strain Holzapfel and Ogden self.solver = create_mechanics_problem(solver_parameters)
def __init__( self, mesh, field, strain_tensor="E", dmu=None, approx="original", F_ref=None, isochoric=True, displacement_space="CG_2", interpolation_space="CG_1", description="", ): ModelObservation.__init__(self, mesh, target_space="R_0", description=description) # Check that the given field is a vector of the same dim as the mesh dim = mesh.geometry().dim() # assert isinstance(field, (dolfin.Function, dolfin.Constant) assert field.ufl_shape[0] == dim approxs = ["project", "interpolate", "original"] msg = 'Expected "approx" for be one of {}, got {}'.format( approxs, approx) self.field = field assert approx in approxs, msg self._approx = approx self._F_ref = F_ref if F_ref is not None else dolfin.Identity(dim) self._isochoric = isochoric tensors = ["gradu", "E", "almansi"] msg = "Expected strain tensor to be one of {}, got {}".format( tensors, strain_tensor) assert strain_tensor in tensors, msg self._tensor = strain_tensor if dmu is None: dmu = dolfin.dx(domain=mesh) assert isinstance(dmu, dolfin.Measure) self._dmu = dmu self._vol = dolfin_adjoint.Constant( dolfin.assemble(dolfin.Constant(1.0) * dmu)) # These spaces are only used if you want to project # or interpolate the displacement before assigning it # Space for interpolating the displacement if needed family, degree = interpolation_space.split("_") self._interpolation_space = dolfin.VectorFunctionSpace( mesh, family, int(degree)) # Displacement space family, degree = displacement_space.split("_") self._displacement_space = dolfin.VectorFunctionSpace( mesh, family, int(degree))
def default_parameters(**values): """ Parameter values """ # Imports import dolfin import dolfin_adjoint # Param values # a=0.13, b=0.013, c_1=0.26, c_2=0.1, c_3=1.0, v_peak=40.0, v_rest=-85.0 param_values = [0.13, 0.013, 0.26, 0.1, 1.0, 40.0, -85.0] param_names = ["a", "b", "c", "c_1", "c_2", "c_3", "v_peak", "v_rest"] # Parameter indices and limit checker param_ind = dict(a=(0, Range()), b=(1, Range()), c_1=(2, Range()),\ c_2=(3, Range()), c_3=(4, Range()), v_peak=(5, Range()), v_rest=(6,\ Range())) for param_name, value in list(values.items()): if param_name not in param_ind: raise ValueError("{{0}} is not a param".format(param_name)) ind, range = param_ind[param_name] if value not in range: raise ValueError("While setting '{0}' {1}".format(param_name,\ range.format_not_in(value))) # Assign value param_values[ind] = value params = [] for (val, name) in zip(param_values, param_names): params.append(dolfin_adjoint.Constant(val, name=name)) return params
def validate(self): data_points = len(self.bcs[0].data) ref = self.bcs[0].bc for i in self.bcs: msg = ( "Number of data points for BC {} is {} which does not " "match with {} which has {} data points" "" ).format(i.bc, len(i.data), ref, data_points) if len(i.data) != data_points: raise ValueError(msg) for i in self.targets: msg = ( "Number of data points for target {} is {} which does not " "match with {} which has {} data point" "" ).format(i.model, len(i.observations), ref, data_points) if len(i.observations) != data_points: raise ValueError(msg) self.data_points = data_points try: meshvol = self.problem.geometry.meshvol except AttributeError: meshvol = compute_meshvolume(self.problem.geometry.mesh) self._meshvol = dolfin_adjoint.Constant(meshvol)
def test_boundary_observation(): bc = pulse.NeumannBC(traction=dolfin_adjoint.Constant(0.0), marker=0, name="test bc") pressures = (0.0, 0.1, 0.2) bcs = BoundaryObservation(bc=bc, data=pressures) for i, b in enumerate(bcs): b.assign_bc() assert abs(float(b.bc.traction) - pressures[i]) < 1e-12
def site_boundary_constraints(self): """Returns the site boundary constraints for a rectangular site. These constraints ensure that the turbine positions remain within the turbine site during optimisation. :raises: ValueError :returns: Tuple of lists of length equal to the twice the number of turbines. Each list contains dolfin_adjoint.Constant objects of the upper and lower bound coordinates. """ # Check we have deployed some turbines in the farm. n_turbines = len(self.turbine_positions) if (n_turbines < 1): raise ValueError("You must deploy turbines before computing " "position constraints.") radius = self._turbine_specification.radius # Get the lower and upper bounds. lower_x = self.site_x_start + radius lower_y = self.site_y_start + radius upper_x = self.site_x_end - radius upper_y = self.site_y_end - radius # Check the site is large enough. if upper_x < lower_x or upper_y < lower_y: raise ValueError("Lower bound is larger than upper bound. Is your " "domain large enough?") # The control variable is ordered as [t1_x, t1_y, t2_x, t2_y, t3_x, ...] lower_bounds = n_turbines * [ dolfin_adjoint.Constant(lower_x), dolfin_adjoint.Constant(lower_y) ] upper_bounds = n_turbines * [ dolfin_adjoint.Constant(upper_x), dolfin_adjoint.Constant(upper_y) ] return lower_bounds, upper_bounds
def set_bcs_staggered(self): self.upper.mark(self.boundaries, 1) self.presLoad = da.Expression((0, "t"), t=0.0, degree=1) BC_u_lower = da.DirichletBC(self.U, da.Constant((0., 0.)), self.lower) BC_u_upper = da.DirichletBC(self.U, self.presLoad, self.upper) BC_d_middle = fe.DirichletBC(self.W, fe.Constant(1.), self.middle, method='pointwise') self.BC_u = [BC_u_lower, BC_u_upper] self.BC_d = [BC_d_middle]
def form(self): if self.reg_type == "": return dolfin_adjoint.Constant(0.0) elif self.reg_type == "L2": return L2(self.f) elif self.reg_type == "H0": return H0(self.f) elif self.reg_type == "H1": return H1(self.f) elif self.reg_type == "regional": return regional(self.f) else: raise ValueError("Unknown regularization type {}".format( self.reg_type))
def __init__(self, observations, model_observation, weight=1.0, collect=True): assert isinstance(model_observation, ModelObservation) self.model = model_observation if np.isscalar(observations): self.observations = (observations, ) elif isinstance(observations, tuple): self.observations = observations else: self.observations = tuple(observations) self._V = dolfin.FunctionSpace(model_observation.mesh, "R", 0) # self._functional = dolfin_adjoint.Function( # self._V, name="functional {}".format(self.model) # ) # # self.model_function = dolfin_adjoint.Function( # self.model._V, name="model function {}".format(self.model) # ) # self.data_function = dolfin_adjoint.Function( # self.model._V, name="data function {}".format(self.model) # ) # Test and trial functions for the target space self._trial = dolfin.TrialFunction(self._V) self._test = dolfin.TestFunction(self._V) self.weight = dolfin_adjoint.Constant(weight, name="Weight {}".format( self.model)) self.__len__ = len(self.observations) self._load_observations() self.collector = dict(model=[], data=[], functional=[]) self.collect = collect self.reset()
def __init__( self, mesh, dmu, approx="project", displacement_space="CG_2", interpolation_space="CG_1", description="", ): ModelObservation.__init__(self, mesh, target_space="R_0", description=description) approxs = ["project", "interpolate", "original"] msg = 'Expected "approx" for be one of {}, got {}'.format( approxs, approx) assert approx in approxs, msg self._approx = approx # These spaces are only used if you want to project # or interpolate the displacement before assigning it # Space for interpolating the displacement if needed family, degree = interpolation_space.split("_") self._interpolation_space = dolfin.VectorFunctionSpace( mesh, family, int(degree)) # Displacement space family, degree = displacement_space.split("_") self._displacement_space = dolfin.VectorFunctionSpace( mesh, family, int(degree)) self._X = dolfin.SpatialCoordinate(mesh) self._N = dolfin.FacetNormal(mesh) assert isinstance(dmu, dolfin.Measure) self._dmu = dmu name = "EndoArea {}".format(self) self._endoarea = dolfin_adjoint.Constant(dolfin.assemble( dolfin.Constant(1.0) * dmu), name=name)
def test_strain(strains): mesh = dolfin_adjoint.UnitCubeMesh(3, 3, 3) x_dir = dolfin_adjoint.Constant([1.0, 0.0, 0.0]) V = dolfin.VectorFunctionSpace(mesh, "CG", 2) # Make an incompressible deformation (with J = 1) x_strain = -0.1 y_strain = 1 / (1 + x_strain) - 1 u = dolfin_adjoint.interpolate( dolfin.Expression( ("x_strain * x[0]", "y_strain * x[1]", "0.0"), x_strain=x_strain, y_strain=y_strain, degree=2, ), V, ) strain_obs = StrainObservation(mesh=mesh, field=x_dir) model_strain = strain_obs(u).vector().get_local()[0] weight = 0.1 target = OptimizationTarget(strains, strain_obs, weight=weight, collect=False) if np.isscalar(strains): strains = (strains, ) for t, s in zip(target, strains): fun = dolfin.project(t.assign(u), t._V) f = fun.vector().get_local()[0] g = (model_strain - s)**2 assert abs(f - weight * g) < 1e-12 # Nothing is collected for k, v in target.collector.items(): assert len(v) == 0
import fenics as fa import dolfin_adjoint as da import numpy as np import moola n = 64 alpha = da.Constant(0) mesh = da.UnitSquareMesh(n, n) # case_flag = 0 runs the default case by # http://www.dolfin-adjoint.org/en/latest/documentation/poisson-mother/poisson-mother.html # change to any other value runs the other case case_flag = 1 x = fa.SpatialCoordinate(mesh) w = da.Expression("sin(pi*x[0])*sin(pi*x[1])", degree=3) V = fa.FunctionSpace(mesh, "CG", 1) W = fa.FunctionSpace(mesh, "DG", 0) # g is the ground truth for source term # f is the control variable if case_flag == 0: g = da.interpolate( da.Expression("1/(1+alpha*4*pow(pi, 4))*w", w=w, alpha=alpha, degree=3), W) f = da.interpolate( da.Expression("1/(1+alpha*4*pow(pi, 4))*w", w=w, alpha=alpha, degree=3), W) else: g = da.interpolate(da.Expression(("sin(2*pi*x[0])"), degree=3), W)
def test_constant_regularization(value, reg_type, weight, expected_functional_value): f = da.Constant(value, cell="tetrahedron") reg = Regularization(f, weight=weight, reg_type=reg_type) assert abs(float(reg.functional) - expected_functional_value) < 1e-12
def reset(self) -> None: self._count = -1 name = "Start value Boundary {}".format(self.bc) self.bc.traction.assign(dolfin_adjoint.Constant(self._start_value, name=name))
def create_problem(): geometry = pulse.HeartGeometry.from_file( pulse.mesh_paths["simple_ellipsoid"]) activation = dolfin_adjoint.Function(dolfin.FunctionSpace( geometry.mesh, "R", 0), name="activation") activation.assign(dolfin_adjoint.Constant(0.0)) matparams = pulse.HolzapfelOgden.default_parameters() V = dolfin.FunctionSpace(geometry.mesh, "R", 0) control = dolfin_adjoint.Function(V) control.assign(dolfin_adjoint.Constant(1.0)) # control = dolfin_adjoint.Constant(1.0, name="matparam control (a)") matparams["a"] = control f0 = dolfin_adjoint.Function(geometry.f0.function_space()) f0.assign(geometry.f0) s0 = dolfin_adjoint.Function(geometry.s0.function_space()) s0.assign(geometry.s0) n0 = dolfin_adjoint.Function(geometry.n0.function_space()) n0.assign(geometry.n0) material = pulse.HolzapfelOgden(activation=activation, parameters=matparams, f0=f0, s0=s0, n0=n0) # LV Pressure lvp = dolfin_adjoint.Constant(0.0, name="lvp") lv_marker = geometry.markers["ENDO"][0] lv_pressure = pulse.NeumannBC(traction=lvp, marker=lv_marker, name="lv") neumann_bc = [lv_pressure] # Add spring term at the base with stiffness 1.0 kPa/cm^2 base_spring = 1.0 robin_bc = [ pulse.RobinBC( value=dolfin_adjoint.Constant(base_spring, name="base_spring"), marker=geometry.markers["BASE"][0], ) ] # Fix the basal plane in the longitudinal direction # 0 in V.sub(0) refers to x-direction, which is the longitudinal direction def fix_basal_plane(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) bc = dolfin_adjoint.DirichletBC( V.sub(0), dolfin.Constant(0.0, name="fix_base"), geometry.ffun, geometry.markers["BASE"][0], ) return bc dirichlet_bc = [fix_basal_plane] # Collect boundary conditions bcs = pulse.BoundaryConditions(dirichlet=dirichlet_bc, neumann=neumann_bc, robin=robin_bc) # Create the problem problem = pulse.MechanicsProblem(geometry, material, bcs) return problem, control
def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return da.DirichletBC(V, da.Constant((0.0, 0.0, 0.0)), left)
def __init__(self, f, weight=1.0, reg_type="L2"): self.f = f self.weight = dolfin_adjoint.Constant(weight, name="Regularization weight") self.reg_type = reg_type
def assign_bc(self): data = self.data[self.count] name = "Data ({}) Boundary {}".format(self.count, self.bc) dolfin_data = dolfin_adjoint.Constant(data, name=name) self.bc.traction.assign(dolfin_data)
def friction_constraints(self): n_turbines = len(self.turbine_positions) lower_bounds = n_turbines*[dolfin_adjoint.Constant(0)] upper_bounds = n_turbines*[dolfin_adjoint.Constant(self._turbine_specification.friction)] return lower_bounds, upper_bounds