3232
3333# %%
3434# Geometry and mesh (2D)
35- L = 10 .0 # Length
35+ L = 1 .0 # Length
3636W = 1.0 # Height
37- nx , ny = 80 , 8
37+ nx , ny = 40 , 40
3838
3939# %%
4040domain = mesh .create_rectangle (MPI .COMM_WORLD , [[0.0 , 0.0 ], [L , W ]], [nx , ny ], cell_type = CellType .quadrilateral )
4444
4545
4646# %%
47- # Boundary markers for Dirichlet and Neumann BCs
48- def left (x ):
49- return np .isclose (x [0 ], 0.0 )
50- def right (x ):
51- return np .isclose (x [0 ], L )
47+ def bottom (x ):
48+ return np .isclose (x [1 ], 0.0 )
49+ def top (x ):
50+ return np .isclose (x [1 ], W )
5251
5352
54- # %%
55-
5653# %%
5754fdim = domain .topology .dim - 1
58- left_facets = mesh .locate_entities_boundary (domain , fdim , left )
59- right_facets = mesh .locate_entities_boundary (domain , fdim , right )
55+ bottom_facets = mesh .locate_entities_boundary (domain , fdim , bottom )
56+ top_facets = mesh .locate_entities_boundary (domain , fdim , top )
6057
6158# %%
62- # Mark facets: 1=left (fixed), 2=right (tension)
63- marked_facets = np .hstack ([left_facets , right_facets ])
64- marked_values = np .hstack ([np .full_like (left_facets , 1 ), np .full_like (right_facets , 2 )])
59+ marked_facets = np .hstack ([bottom_facets , top_facets ])
60+ marked_values = np .hstack ([np .full_like (bottom_facets , 1 ), np .full_like (top_facets , 2 )])
6561sorted_facets = np .argsort (marked_facets )
6662facet_tag = mesh .meshtags (domain , fdim , marked_facets [sorted_facets ], marked_values [sorted_facets ])
6763
68- # %%
69- # Dirichlet BC: fix left edge (all dofs)
70- u_bc = np .array ((0 ,) * domain .geometry .dim , dtype = default_scalar_type )
71- left_dofs = fem .locate_dofs_topological (V , facet_tag .dim , facet_tag .find (1 ))
72- bcs = [fem .dirichletbc (u_bc , left_dofs , V )]
64+ bottom_dofs = fem .locate_dofs_topological (V , facet_tag .dim , facet_tag .find (1 ))
65+ top_dofs = fem .locate_dofs_topological (V .sub (1 ), facet_tag .dim , facet_tag .find (2 ))
66+ u_D_bottom = np .array ((0 ,) * domain .geometry .dim , dtype = default_scalar_type )
67+ u_D_top = fem .Constant (domain , 0.0 )
68+
69+ bcs_u = [
70+ fem .dirichletbc (u_D_top , top_dofs , V .sub (1 )),
71+ fem .dirichletbc (u_D_bottom , bottom_dofs , V ),
72+ ]
7373
74- # %%
75- # Body force and traction (2D)
76- B = fem .Constant (domain , default_scalar_type ((0 , 0 )))
77- T = fem .Constant (domain , default_scalar_type ((0 , 0 )))
7874
7975# %%
80- # Solution and test functions
8176u = fem .Function (V )
8277v = ufl .TestFunction (V )
83-
84- # %%
8578d = len (u )
8679I = ufl .variable (ufl .Identity (d ))
8780F = ufl .variable (I + ufl .grad (u ))
@@ -95,25 +88,50 @@ def right(x):
9588nu = default_scalar_type (0.3 )
9689mu = fem .Constant (domain , E / (2 * (1 + nu )))
9790lmbda = fem .Constant (domain , E * nu / ((1 + nu ) * (1 - 2 * nu )))
98-
99- # %%
10091# Strain energy and first Piola-Kirchhoff stress
10192psi = (mu / 2 ) * (Ic - 2 ) - mu * ufl .ln (J ) + (lmbda / 2 ) * (ufl .ln (J )) ** 2
10293P = ufl .diff (psi , F )
94+ dP = ufl .diff (P , F )
10395
10496# %%
10597# Measures for integration
10698metadata = {"quadrature_degree" : 2 }
10799ds = ufl .Measure ("ds" , domain = domain , subdomain_data = facet_tag , metadata = metadata )
108100dx = ufl .Measure ("dx" , domain = domain , metadata = metadata )
109101
102+ # %%
103+ # from dolfinx.fem import Expression, functionspace, Function
104+ # import basix
105+
106+ # # Choose quadrature degree and get quadrature points for the cell type
107+ # quadrature_degree = 2
108+ # cell_name = domain.topology.cell_name()
109+ # quadrature_points, _ = basix.make_quadrature(getattr(basix.CellType, cell_name), quadrature_degree)
110+
111+ # # Create a quadrature element and function space for tensor-valued P
112+ # Qe = basix.ufl.quadrature_element(cell_name, value_shape=(d, d), degree=quadrature_degree, scheme="default")
113+ # Q = functionspace(domain, Qe)
114+
115+ # # Create Expression for P at quadrature points
116+ # P_expr = Expression(P, quadrature_points, dtype=default_scalar_type)
117+
118+ # # Evaluate P at all cells (including ghosts)
119+ # map_c = domain.topology.index_map(domain.topology.dim)
120+ # num_cells = map_c.size_local + map_c.num_ghosts
121+ # cells = np.arange(num_cells, dtype=np.int32)
122+ # P_eval = P_expr.eval(domain, cells)
123+
124+ # # Optionally, assemble into a Function for visualization/post-processing
125+ # P_func = Function(Q)
126+ # P_func.x.array[:] = P_eval.flatten()
127+
110128# %%
111129# Residual form (weak form)
112- F = ufl .inner (ufl .grad (v ), P ) * dx - ufl . inner ( v , B ) * dx - ufl . inner ( v , T ) * ds ( 2 )
130+ F = ufl .inner (ufl .grad (v ), P ) * dx
113131
114132# %%
115133# Nonlinear problem and Newton solver
116- problem = NonlinearProblem (F , u , bcs )
134+ problem = NonlinearProblem (F , u , bcs_u )
117135solver = NewtonSolver (domain .comm , problem )
118136solver .atol = 1e-8
119137solver .rtol = 1e-8
@@ -125,17 +143,17 @@ def right(x):
125143
126144# %%
127145# Apply a tensile load by incrementally increasing traction on the right edge
128- n_steps = 5
129- max_traction = 10 .0
146+ n_steps = 100
147+ max_traction = 1 .0
130148u .name = "displacement"
131149
132150for step in range (1 , n_steps + 1 ):
133- T .value [ 0 ] = step * max_traction / n_steps # Apply in x-direction
151+ u_D_top .value = step * max_traction / n_steps
134152 num_its , converged = solver .solve (u )
135153 assert converged , f"Newton solver did not converge at step { step } "
136154 u .x .scatter_forward ()
137155 if domain .comm .rank == 0 :
138- print (f"Step { step } : Traction { T .value [ 0 ] :.2f} , Newton its: { num_its } " )
156+ print (f"Step { step } : Traction { u_D_top .value :.2f} , Newton its: { num_its } " )
139157 # Save solution for each step
140158 with XDMFFile (domain .comm , "tensile2d_u.xdmf" , "a" ) as xdmf :
141159 xdmf .write_function (u , step )
@@ -153,7 +171,3 @@ def right(x):
153171# xdmf.write_mesh(domain)
154172# stresses.name = "von_mises"
155173# xdmf.write_function(stresses)
156-
157- # %%
158- if domain .comm .rank == 0 :
159- print ("2D simulation complete. Displacement and von Mises stress written to XDMF files." )
0 commit comments