def test_2_materials(): """FenicsPart instance initialized with only one instance of Material in the materials dictionnary""" L_x, L_y = 1, 1 mesh = fe.RectangleMesh(fe.Point(-L_x, -L_y), fe.Point(L_x, L_y), 20, 20) dimensions = np.array(((2 * L_x, 0.0), (0.0, 2 * L_y))) subdomains = fe.MeshFunction("size_t", mesh, 2) class Right_part(fe.SubDomain): def inside(self, x, on_boundary): return x[0] >= 0 - fe.DOLFIN_EPS subdomain_right = Right_part() subdomains.set_all(0) subdomain_right.mark(subdomains, 1) E_1, E_2, nu = 1, 3, 0.3 materials = {0: mat.Material(1, 0.3, "cp"), 1: mat.Material(3, 0.3, "cp")} rect_part = part.FenicsPart(mesh, materials, subdomains, dimensions) elem_type = "CG" degree = 2 strain_fspace = fe.FunctionSpace( mesh, fe.VectorElement(elem_type, mesh.ufl_cell(), degree, dim=3), ) strain = fe.project(fe.Expression(("1.0+x[0]*x[0]", "0", "1.0"), degree=2), strain_fspace) stress = mat.sigma(rect_part.elasticity_tensor, strain) energy = fe.assemble(fe.inner(stress, strain) * fe.dx(rect_part.mesh)) energy_theo = 2 * ((E_1 + E_2) / (1 + nu) * (1 + 28 / (15 * (1 - nu)))) assert energy == approx(energy_theo, rel=1e-13)
def test_mat_without_dictionnary(): """FenicsPart instance initialized with only one instance of Material""" L_x, L_y = 1, 1 mesh = fe.RectangleMesh(fe.Point(0.0, 0.0), fe.Point(L_x, L_y), 10, 10) dimensions = np.array(((L_x, 0.0), (0.0, L_y))) E, nu = 1, 0.3 material = mat.Material(E, nu, "cp") rect_part = part.FenicsPart( mesh, materials=material, subdomains=None, global_dimensions=dimensions, facet_regions=None, ) elem_type = "CG" degree = 2 strain_fspace = fe.FunctionSpace( mesh, fe.VectorElement(elem_type, mesh.ufl_cell(), degree, dim=3), ) strain = fe.project(fe.Expression(("1.0+x[0]*x[0]", "0", "1.0"), degree=2), strain_fspace) stress = mat.sigma(rect_part.elasticity_tensor, strain) energy = fe.assemble(fe.inner(stress, strain) * fe.dx(rect_part.mesh)) energy_theo = E / (1 + nu) * (1 + 28 / (15 * (1 - nu))) assert energy == approx(energy_theo, rel=1e-13)
def genericAuxiliaryProblem(self, Fload, Epsilon0): """ This function compute the auxiliary problem of order N given the sources (of auxiliary problem N-1). It returns new localizations """ U2 = [] S2 = [] E2 = [] for i in range(len(Fload)): logger.info(f"Progression : load {i+1} / {len(Fload)}") L = (fe.dot(Fload[i], self.v) + fe.inner( -sigma(self.rve.C_per, Epsilon0[i]), epsilon(self.v))) * fe.dx # u_s = fe.Function(self.V) res = fe.assemble(L) # self.bc.apply(res) #TODO à tester. Pas nécessaire pour le moment, la ligne # K,res = fe.assemble_system(self.a,L,self.bc) était commentée. # self.solver.solve(K, u_s.vector(), res) #* Previous method self.solver.solve(self.w.vector(), res) # * More info : https://fenicsproject.org/docs/dolfin/1.5.0/python/programmers-reference/cpp/la/PETScLUSolver.html u_only = fe.interpolate(self.w.sub(0), self.V) # ? Essayer de faire fe.assign(self.u_only, self.w.sub(0)) ? # ? Pour le moment u_only vit dans V et V n'est pas extrait # ? de l'espace fonctionnel mixte. Est-ce que cela marcherait si V # ? est extrait de M ? U2.append(u_only) eps = epsilon(u_only) + Epsilon0[i] E2.append(local_project(eps, self.W)) S2.append(local_project(sigma(self.rve.C_per, eps), self.W)) # TODO : Comparer les options : # TODO e2 = fe.Function(self.W); e2.assign(self.RVE.epsilon(u_s) + Epsilon0[i]) # TODO interpolation # TODO projection (locale) return U2, S2, E2
def LocalizationEGbis(self): """ return GradE stress/strain localization fields for the EG model $u^E\diads\delta$ (as function of macro GradE) """ try: out = self.localization["EGbis"] except KeyError: # h = self.lamination.total_thickness self.LocalizationE() Epsilon0 = self.Displacement2Epsilon0(self.localization["E"]["U"]) out = { "Sigma": [sigma(self.rve.C_per, Epsilon0[i]) for i in range(6)], "Epsilon": Epsilon0, } self.localization["EGbis"] = out return out
def LocalizationEbis(self): """ return E stress/strain localization fields $u^U\diads\delta$ (as function of macro E) """ try: out = self.localization["Ebis"] except KeyError: epsilon0 = [ fe.Constant((1, 0, 0)), fe.Constant((0, 1, 0)), fe.Constant((0, 0, 1)), ] Epsilon0 = [fe.interpolate(eps, self.W) for eps in epsilon0] out = { "Sigma": [sigma(self.rve.C_per, Epsilon0[i]) for i in range(3)], "Epsilon": Epsilon0, } self.localization["Ebis"] = out return out
def LocalizationEGGbis(self): """ return GradGradE stress/strain localization fields for the EGG model $u^EG\diads\delta$ (as function of macro GradGradE) >>> a = LaminatedComposite.paganosLaminate([1.0, 1.5, 1.0], [np.pi/3, -np.pi/3, np.pi/3]) >>> Lam = Laminate2D(a) >>> plt=plotTransverseDistribution(Lam.LocalizationEGGbis()['Sigma']) >>> show() """ try: out = self.localization["EGGbis"] except KeyError: # h = self.lamination.total_thickness self.LocalizationEG() Epsilon0 = self.Displacement2Epsilon0(self.localization["EG"]["U"]) out = { "Sigma": [sigma(self.rve.C_per, Epsilon0[i]) for i in range(12)], "Epsilon": Epsilon0, } self.localization["EGGbis"] = out return out
def __init__(self, fenics_2d_part, loads, boundary_conditions, element): """ The FacetFunction must be the same for all BC and facet loads. Its type of value must be 'size_t' (unsigned integers). Parameters ---------- fenics_2d_part : FEnicsPart [description] loads : [type] [description] boundary_conditions : list or tuple All the boundary conditions. Each of them is describe by a tuple or a dictionnary. Only one periodic BC can be prescribed. Format: if Periodic BC : {'type': 'Periodic', 'constraint': PeriodicDomain} or ('Periodic', PeriodicDomain) if Dirichlet BC : { 'type': 'Dirichlet', 'constraint': the prescribed value, 'facet_function': facet function, 'facet_idx': facet function index} or ('Dirichlet', the prescribed value, facet function, facet function index) or ('Dirichlet', the prescribed value, indicator function) where the indicator function is python function like : def clamped(x, on_boundary): return on_boundary and x[0] < tolerance element: tuple or dict Ex: ('CG', 2) or {'family':'Lagrange', degree:2} """ self.part = fenics_2d_part # * Boundary conditions self.per_bc = None self.Dirichlet_bc = list() for bc in boundary_conditions: if isinstance(bc, dict): bc_ = bc bc = [bc_["type"], bc_["constraint"]] try: bc.append(bc_["facet_function"]) bc.append(bc_["facet_idx"]) except KeyError: pass bc_type, *bc_data = bc if bc_type == "Periodic": if self.per_bc is not None: raise AttributeError( "Only one periodic boundary condition can be prescribed." ) self.per_bc = bc_data[0] elif bc_type == "Dirichlet": if len(bc_data) == 2 or len(bc_data) == 3: bc_data = tuple(bc_data) else: raise AttributeError( "Too much parameter for the definition of a Dirichlet BC." ) self.Dirichlet_bc.append(bc_data) self.measures = {self.part.dim: fe.dx, self.part.dim - 1: fe.ds} # * Function spaces try: self.elmt_family = family = element["family"] self.elmt_degree = degree = element["degree"] except TypeError: # Which means that element is not a dictionnary self.element_family, self.element_degree = element family, degree = element cell = self.part.mesh.ufl_cell() Voigt_strain_dim = int(self.part.dim * (self.part.dim + 1) / 2) strain_deg = degree - 1 if degree >= 1 else 0 strain_FE = fe.VectorElement("DG", cell, strain_deg, dim=Voigt_strain_dim) self.scalar_fspace = fe.FunctionSpace( self.part.mesh, fe.FiniteElement(family, cell, degree), constrained_domain=self.per_bc, ) self.displ_fspace = fe.FunctionSpace( self.part.mesh, fe.VectorElement(family, cell, degree, dim=self.part.dim), constrained_domain=self.per_bc, ) self.strain_fspace = fe.FunctionSpace(self.part.mesh, strain_FE, constrained_domain=self.per_bc) self.v = fe.TestFunction(self.displ_fspace) self.u = fe.TrialFunction(self.displ_fspace) self.a = (fe.inner( mat.sigma(self.part.elasticity_tensor, mat.epsilon(self.u)), mat.epsilon(self.v), ) * self.measures[self.part.dim]) self.K = fe.assemble(self.a) # * Create suitable objects for Dirichlet boundary conditions for i, bc_data in enumerate(self.Dirichlet_bc): self.Dirichlet_bc[i] = fe.DirichletBC(self.displ_fspace, *bc_data) # ? Vu comment les conditions aux limites de Dirichlet interviennent dans le problème, pas sûr que ce soit nécessaire que toutes soient définies avec la même facetfunction # * Taking into account the loads if loads: self.set_loads(loads) else: self.loads_data = None self.load_integrals = None # * Prepare attribute for the solver self.solver = None
def __init__(self, fenics_2d_rve, **kwargs): """[summary] Parameters ---------- object : [type] [description] fenics_2d_rve : [type] [description] element : tuple or dict Type and degree of element for displacement FunctionSpace Ex: ('CG', 2) or {'family':'Lagrange', degree:2} solver : dict Choose the type of the solver, its method and the preconditioner. An up-to-date list of the available solvers and preconditioners can be obtained with dolfin.list_linear_solver_methods() and dolfin.list_krylov_solver_preconditioners(). """ self.rve = fenics_2d_rve self.topo_dim = topo_dim = fenics_2d_rve.dim try: bottom_left_corner = fenics_2d_rve.bottom_left_corner except AttributeError: logger.warning( "For the definition of the periodicity boundary conditions," "the bottom left corner of the RVE is assumed to be on (0.,0.)" ) bottom_left_corner = np.zeros(shape=(topo_dim, )) self.pbc = periodicity.PeriodicDomain.pbc_dual_base( fenics_2d_rve.gen_vect, "XY", bottom_left_corner, topo_dim) solver = kwargs.pop("solver", {}) # {'type': solver_type, 'method': solver_method, 'preconditioner': preconditioner} s_type = solver.pop("type", None) s_method = solver.pop("method", SOLVER_METHOD) s_precond = solver.pop("preconditioner", None) if s_type is None: if s_method in DOLFIN_KRYLOV_METHODS.keys(): s_type = "Krylov" elif s_method in DOLFIN_LU_METHODS.keys(): s_type = "LU" else: raise RuntimeError("The indicated solver method is unknown.") self._solver = dict(type=s_type, method=s_method) if s_precond: self._solver["preconditioner"] = s_precond element = kwargs.pop("element", ("Lagrange", 2)) if isinstance(element, dict): element = (element["family"], element["degree"]) self._element = element # * Function spaces cell = self.rve.mesh.ufl_cell() self.scalar_FE = fe.FiniteElement(element[0], cell, element[1]) self.displ_FE = fe.VectorElement(element[0], cell, element[1]) strain_deg = element[1] - 1 if element[1] >= 1 else 0 strain_dim = int(topo_dim * (topo_dim + 1) / 2) self.strain_FE = fe.VectorElement("DG", cell, strain_deg, dim=strain_dim) # Espace fonctionel scalaire self.X = fe.FunctionSpace(self.rve.mesh, self.scalar_FE, constrained_domain=self.pbc) # Espace fonctionnel 3D : deformations, notations de Voigt self.W = fe.FunctionSpace(self.rve.mesh, self.strain_FE) # Espace fonctionel 2D pour les champs de deplacement # TODO : reprendre le Ve défini pour l'espace fonctionnel mixte. Par ex: V = FunctionSpace(mesh, Ve) self.V = fe.VectorFunctionSpace(self.rve.mesh, element[0], element[1], constrained_domain=self.pbc) # * Espace fonctionel mixte pour la résolution : # * 2D pour les champs + scalaire pour multiplicateur de Lagrange # "R" : Real element with one global degree of freedom self.real_FE = fe.VectorElement("R", cell, 0) self.M = fe.FunctionSpace( self.rve.mesh, fe.MixedElement([self.displ_FE, self.real_FE]), constrained_domain=self.pbc, ) # Define variational problem self.v, self.lamb_ = fe.TestFunctions(self.M) self.u, self.lamb = fe.TrialFunctions(self.M) self.w = fe.Function(self.M) # bilinear form self.a = ( fe.inner(sigma(self.rve.C_per, epsilon(self.u)), epsilon(self.v)) * fe.dx + fe.dot(self.lamb_, self.u) * fe.dx + fe.dot(self.lamb, self.v) * fe.dx) self.K = fe.assemble(self.a) if self._solver["type"] == "Krylov": self.solver = fe.KrylovSolver(self.K, self._solver["method"]) elif self._solver["type"] == "LU": self.solver = fe.LUSolver(self.K, self._solver["method"]) self.solver.parameters["symmetric"] = True try: self.solver.parameters.preconditioner = self._solver[ "preconditioner"] except KeyError: pass # fe.info(self.solver.parameters, True) self.localization = dict() # dictionary of localization field objects, # will be filled up when calling auxiliary problems (lazy evaluation) self.ConstitutiveTensors = dict()
def test_get_domains_gmsh(plots=False): """ Get subdomains and partition of the boundary from a .msh file. """ name = "test_domains" local_dir = Path(__file__).parent mesh_file = local_dir.joinpath(name + ".msh") gmsh.model.add(name) L_x, L_y = 2.0, 2.0 H = 1.0 vertices = [(0.0, 0.0), (0.0, L_y), (L_x, L_y), (L_x, 0.0)] contour = geo.LineLoop([geo.Point(np.array(c)) for c in vertices], False) surface = geo.PlaneSurface(contour) inclusion_vertices = list() for coord in [ (H / 2, -H / 2, 0.0), (H / 2, H / 2, 0.0), (-H / 2, H / 2, 0.0), (-H / 2, -H / 2, 0.0), ]: vertex = geo.translation(geo.Point((L_x / 2, L_y / 2)), coord) inclusion_vertices.append(vertex) inclusion = geo.PlaneSurface(geo.LineLoop(inclusion_vertices, False)) for s in [surface, inclusion]: s.add_gmsh() factory.synchronize() (stiff_s, ) = geo.surface_bool_cut(surface, inclusion) factory.synchronize() (soft_s, ) = geo.surface_bool_cut(surface, stiff_s) factory.synchronize() domains = { "stiff": geo.PhysicalGroup(stiff_s, 2), "soft": geo.PhysicalGroup(soft_s, 2), } boundaries = { "S": geo.PhysicalGroup(surface.ext_contour.sides[0], 1), "W": geo.PhysicalGroup(surface.ext_contour.sides[1], 1), "N": geo.PhysicalGroup(surface.ext_contour.sides[2], 1), "E": geo.PhysicalGroup(surface.ext_contour.sides[3], 1), } for group in domains.values(): group.add_gmsh() for group in boundaries.values(): group.add_gmsh() charact_field = mesh_tools.MathEvalField("0.05") mesh_tools.set_background_mesh(charact_field) geo.set_gmsh_option("Mesh.SaveAll", 0) model.mesh.generate(1) model.mesh.generate(2) gmsh.model.mesh.removeDuplicateNodes() gmsh.write(str(mesh_file)) E_1, E_2, nu = 1, 3, 0.3 materials = { domains["soft"].tag: mat.Material(E_1, nu, "cp"), domains["stiff"].tag: mat.Material(E_1, nu, "cp"), } test_part = part.FenicsPart.part_from_file(mesh_file, materials, subdomains_import=True) assert test_part.mat_area == approx(L_x * L_y) elem_type = "CG" degree = 2 V = fe.VectorFunctionSpace(test_part.mesh, elem_type, degree) W = fe.FunctionSpace( test_part.mesh, fe.VectorElement(elem_type, test_part.mesh.ufl_cell(), degree, dim=3), ) boundary_conditions = { boundaries["N"].tag: fe.Expression(("x[0]-1", "1"), degree=1), boundaries["S"].tag: fe.Expression(("x[0]-1", "-1"), degree=1), boundaries["E"].tag: fe.Expression(("1", "x[1]-1"), degree=1), boundaries["W"].tag: fe.Expression(("-1", "x[1]-1"), degree=1), } bcs = list() for tag, val in boundary_conditions.items(): bcs.append(fe.DirichletBC(V, val, test_part.facet_regions, tag)) ds = fe.Measure("ds", domain=test_part.mesh, subdomain_data=test_part.facet_regions) v = fe.TestFunctions(V) u = fe.TrialFunctions(V) F = (fe.inner(mat.sigma(test_part.elasticity_tensor, mat.epsilon(u)), mat.epsilon(v)) * fe.dx) a, L = fe.lhs(F), fe.rhs(F) u_sol = fe.Function(V) fe.solve(a == L, u_sol, bcs) strain = fe.project(mat.epsilon(u_sol), W) if plots: import matplotlib.pyplot as plt plt.figure() plot = fe.plot(u_sol) plt.colorbar(plot) plt.figure() plot = fe.plot(strain[0]) plt.colorbar(plot) plt.figure() plot = fe.plot(strain[1]) plt.colorbar(plot) plt.figure() plot = fe.plot(strain[2]) plt.colorbar(plot) plt.show() error = fe.errornorm( strain, fe.Expression(("1", "1", "0"), degree=0), degree_rise=3, mesh=test_part.mesh, ) assert error == approx(0, abs=1e-12) materials = { domains["soft"].tag: mat.Material(E_1, nu, "cp"), domains["stiff"].tag: mat.Material(E_2, nu, "cp"), } test_part = part.FenicsPart.part_from_file(mesh_file, materials, subdomains_import=True) V = fe.VectorFunctionSpace(test_part.mesh, elem_type, degree) W = fe.FunctionSpace( test_part.mesh, fe.VectorElement(elem_type, test_part.mesh.ufl_cell(), degree, dim=3), ) bcs = list() for tag, val in boundary_conditions.items(): bcs.append(fe.DirichletBC(V, val, test_part.facet_regions, tag)) v = fe.TestFunctions(V) u = fe.TrialFunctions(V) F = (fe.inner(mat.sigma(test_part.elasticity_tensor, mat.epsilon(u)), mat.epsilon(v)) * fe.dx) a, L = fe.lhs(F), fe.rhs(F) u_sol = fe.Function(V) fe.solve(a == L, u_sol, bcs) strain = mat.epsilon(u_sol) stress = mat.sigma(test_part.elasticity_tensor, strain) energy = 0.5 * fe.assemble( fe.inner(stress, strain) * fe.dx(test_part.mesh)) energy_abaqus = 12.8788939 assert energy == approx(energy_abaqus, rel=1e-3) geo.reset()
def sigma(self, eps): return mat.sigma(self.C_per, eps)