def __init__(self, kin, materials, incompr_2field, mat_growth=None, mat_remodel=None): self.kin = kin self.matmodels = [] for i in range(len(materials.keys())): self.matmodels.append(list(materials.keys())[i]) self.matparams = [] for i in range(len(materials.values())): self.matparams.append(list(materials.values())[i]) self.mat_growth = mat_growth self.mat_remodel = mat_remodel self.incompr_2field = incompr_2field if self.mat_growth: # growth & remodeling parameters self.gandrparams = materials['growth'] self.growth_dir = self.gandrparams['growth_dir'] self.growth_trig = self.gandrparams['growth_trig'] if self.mat_remodel: self.matmodels_remod = [] for i in range(len(self.gandrparams['remodeling_mat'].keys())): self.matmodels_remod.append(list(self.gandrparams['remodeling_mat'].keys())[i]) self.matparams_remod = [] for i in range(len(self.gandrparams['remodeling_mat'].values())): self.matparams_remod.append(list(self.gandrparams['remodeling_mat'].values())[i]) # identity tensor self.I = Identity(3)
def compute_force(self, velocity, pressure, t): self.tc.start('errorForce') I = Identity(3) # Identity tensor def T(p, v): return -p * I + 2.0 * self.nu * sym(grad(v)) error_force = sqrt( assemble( inner((T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal, (T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal) * self.dsWall)) an_force = sqrt( assemble( inner( T(self.sol_p, self.solution) * self.normal, T(self.sol_p, self.solution) * self.normal) * self.dsWall)) an_f_normal = sqrt( assemble( inner( inner( T(self.sol_p, self.solution) * self.normal, self.normal), inner( T(self.sol_p, self.solution) * self.normal, self.normal)) * self.dsWall)) error_f_normal = sqrt( assemble( inner( inner( (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal), inner( (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal)) * self.dsWall)) an_f_shear = sqrt( assemble( inner( (I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal, (I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal) * self.dsWall)) error_f_shear = sqrt( assemble( inner((I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, (I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal) * self.dsWall)) self.listDict['a_force_wall']['list'].append(an_force) self.listDict['a_force_wall_normal']['list'].append(an_f_normal) self.listDict['a_force_wall_shear']['list'].append(an_f_shear) self.listDict['force_wall']['list'].append(error_force) self.listDict['force_wall_normal']['list'].append(error_f_normal) self.listDict['force_wall_shear']['list'].append(error_f_shear) print(' Relative force error:', error_force / an_force) self.tc.end('errorForce')
def __init__(self, fib_funcs=None, F_hist=None): # fibers self.fib_funcs = fib_funcs # history deformation gradient (for prestressing) self.F_hist = F_hist # identity tensor self.I = Identity(3)
def __init__(self, kin, materials): self.kin = kin self.matmodels = [] for i in range(len(materials.keys())): self.matmodels.append(list(materials.keys())[i]) self.matparams = [] for i in range(len(materials.values())): self.matparams.append(list(materials.values())[i]) # identity tensor self.I = Identity(3)
def sigma(self, u): n = u.geometric_dimension() lmbda = self.lmbda3D(0) mu = self.mu3D(0) return lmbda * tr(self.eps(u)) * Identity(n) + 2 * mu * self.eps(u)
def sigma0(self): n = self.dim lmbda = self.lmbda3D(0) mu = self.mu3D(0) return lmbda * tr(self.eps0()) * Identity(n) + 2 * mu * self.eps0()
# Create CG Krylov solver and turn convergence monitoring on solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setFromOptions() # Set matrix operator solver.setOperators(A) # Compute solution solver.setMonitor(lambda ksp, its, rnorm: print( "Iteration: {}, rel. residual: {}".format(its, rnorm))) solver.solve(b, u.vector) solver.view() u.x.scatter_forward() # Compute von Mises stress via interpolation sigma_deviatoric = sigma(u) - (1 / 3) * tr(sigma(u)) * Identity(len(u)) sigma_von_mises = sqrt((3 / 2) * inner(sigma_deviatoric, sigma_deviatoric)) W = FunctionSpace(mesh, ("Discontinuous Lagrange", 0)) sigma_von_mises_expression = Expression(sigma_von_mises, W.element.interpolation_points) sigma_von_mises_h = Function(W) sigma_von_mises_h.interpolate(sigma_von_mises_expression) # Save solution to XDMF format with XDMFFile(MPI.COMM_WORLD, "displacements.xdmf", "w") as file: file.write_mesh(mesh) file.write_function(u) # Save solution to XDMF format with XDMFFile(MPI.COMM_WORLD, "von_mises_stress.xdmf", "w") as file:
uflSpace1 = Space((2, 2), 1) u1 = TrialFunction(uflSpace1) v1 = TestFunction(uflSpace1) uflSpace2 = Space((2, 2), 2) u2 = TrialFunction(uflSpace2) v2 = TestFunction(uflSpace2) if test_21: a = div(u2) * v1[0] * dx model = create.model("integrands", grid, a) space = "lagrange" if test_fem: test(model, space, 2, 1, "fem") if test_istl: test(model, space, 2, 1, "istl") if test_petsc: test(model, space, 2, 1, "petsc") if test_12: a = -inner(u1[0] * Identity(2), grad(v2)) * dx model = create.model("integrands", grid, a) space = "lagrange" if test_fem: test(model, space, 1, 2, "fem") if test_istl: test(model, space, 1, 2, "istl") if test_petsc: test(model, space, 1, 2, "petsc")
def compute_total_jacobian(displacement): return det(Identity(len(displacement)) + grad(displacement))
def compute_growth_induced_strain(conc_field, coupling_constant, dim): return conc_field * coupling_constant * Identity(dim)
def holzapfel_ogden(mesh, q, p, nf=0): # Based on https://gist.github.com/meg-simula/3ab3fd63264c8cf1912b # # Original credit note: # "Original implementation by Gabriel Balaban, # modified by Marie E. Rognes" from ufl import (Constant, VectorConstant, Identity, Coefficient, TestFunction, conditional, det, diff, dot, exp, grad, inner, tr, variable) # Define some random parameters a = Constant(mesh) b = Constant(mesh) a_s = Constant(mesh) b_s = Constant(mesh) a_f = Constant(mesh) b_f = Constant(mesh) a_fs = Constant(mesh) b_fs = Constant(mesh) # For more fun, make these general vector fields rather than # constants: e_s = VectorConstant(mesh) e_f = VectorConstant(mesh) # Define the isochoric energy contribution def isochoric(C): I_1 = tr(C) I4_f = dot(e_f, C*e_f) I4_s = dot(e_s, C*e_s) I8_fs = dot(e_s, C*e_f) def heaviside(x): return conditional(x < 1, 0, 1) def scaled_exp(a, b, x): return a/(2*b)*(exp(b*x) - 1) E_1 = scaled_exp(a, b, I_1 - 3) E_f = heaviside(I4_f)*scaled_exp(a_f, b_f, (I4_f - 1)**2) E_s = heaviside(I4_s)*scaled_exp(a_s, b_s, (I4_s - 1)**2) E_3 = scaled_exp(a_fs, b_fs, I8_fs**2) E = E_1 + E_f + E_s + E_3 return E # Define mesh and function space V = ufl.FunctionSpace(mesh, ufl.VectorElement("CG", mesh.ufl_cell(), q)) u = Coefficient(V) v = TestFunction(V) # Misc elasticity related tensors and other quantities I = Identity(mesh.ufl_cell().topological_dimension()) F = grad(u) + I F = variable(F) J = det(F) Cbar = J**(-2/3)*F.T*F # Define energy Psi = isochoric(Cbar) # Find first Piola-Kirchhoff tensor P = diff(Psi, F) # Define the variational formulation it = inner(P, grad(v)) P = ufl.FunctionSpace(mesh, ufl.VectorElement('P', mesh.ufl_cell(), p)) f = [ufl.Coefficient(P) for _ in range(nf)] return ufl.derivative(reduce(ufl.inner, list(map(ufl.div, f)) + [it])*ufl.dx, u)
ldot * timeVals[:len(timeVals) // 2], ldot * (-timeVals[len(timeVals) // 2:] + 2 * timeVals[len(timeVals) // 2]), )) svals = np.zeros_like(stretchVals) plt.plot(timeVals, stretchVals) plt.savefig("stretchesVisco.png") plt.close() # stabilization parameters h = FacetArea(mesh) h_avg = avg(h) # new variable name to take derivatives FF = Identity(3) + grad(u) CC = FF.T * FF Fv = variable(FF) S = diff(freeEnergy(Fv.T * Fv, CCv), Fv) # first PK stress dl_interp(CC, C) dl_interp(CC, Cn) my_identity = grad(SpatialCoordinate(mesh)) dl_interp(my_identity, CCv) dl_interp(my_identity, Cvn) dl_interp(my_identity, C_quart) dl_interp(my_identity, C_thr_quart) dl_interp(my_identity, C_half) a_uv = (derivative(freeEnergy(CC, CCv), u, v) * dx +
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace( mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(find_geometric_dimension(u)) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{ 'type': 'v', 'time': -dt }, { 'type': 'v', 'time': 0.0 }, { 'type': 'p', 'time': 0.0 }]) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity p_ = Function( self.Q ) # current pressure or pressure help function from rotation scheme p_mod = Function( self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5 * u0 - 0.5 * u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) if self.args.cbc_tau: # used in Simula cbcflow project tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h) else: # proposed in R. Codina: On stabilized finite element methods for linear systems of # convection-diffusion-reaction equations. tau = Constant(self.stabCoef) * k * h**2 / ( 2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) + h**2) # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0 if self.use_full_SUPG: v1 = v + tau * 0.5 * dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v def nonlinearity(function): if self.args.ema: return 2 * inner(dot(sym(grad(function)), u_ext), v1 ) * dx + inner(div(function) * u_ext, v1) * dx else: return inner(dot(grad(function), u_ext), v1) * dx def diffusion(fce): if self.useLaplace: return nu * inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': return form if self.bcv == 'LAP': return form - inner(nu * dot(grad(fce).T, n), v1 ) * problem.get_outflow_measure_form() if self.bcv == 'DDN': return form # additional term must be added to non-constant part def pressure_rhs(): if self.args.bc == 'outflow': return inner(p0, div(v1)) * dx else: return inner(p0, div(v1)) * dx - inner( p0 * n, v1) * problem.get_outflow_measure_form() a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u) a1_change = nonlinearity(0.5 * u) if self.bcv == 'DDN': # does not penalize influx for current step, only for the next one # this can lead to oscilation: # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5 * min_value(Constant(0.), inner( u_ext, n)) * inner(u, v1) * problem.get_outflow_measure_form() # NT works only with uflacs compiler L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity( 0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner( u0, v1) * problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot( grad(v), u_ext)) * dx(None, {'quadrature_degree': 6}) # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed: # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx else: # Projection, solve to p_ if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx for m in problem.get_outflow_measures(): F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m else: F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx else: F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0), v) * dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure F4 = (p - p0 - p_ + nu * div(u_)) * q * dx a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble( a1_const ) # must be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy( ) # copy to get matrix with same sparse structure (data will be overwritten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy( ) # copy to get matrix with same sparse structure (data will be overwritten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('mumps') if self.useRotationScheme: self.solver_rot = LUSolver('mumps') else: # NT 2016-1 KrylovSolver >> PETScKrylovSolver # not needed, chosen not to use hypre_parasails: # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = PETScKrylovSolver( 'gmres', self.args.precV) # nonsymetric > gmres # cannot use 'ilu' in parallel self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC) self.solver_p = PETScKrylovSolver( self.args.solP, self.args.precP) # almost (up to BC) symmetric > CG if self.useRotationScheme: self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg') # setup Krylov solvers if self.solvers == 'krylov': # Get the nullspace if there are no pressure boundary conditions foo = Function( self.Q) # auxiliary vector for setting pressure nullspace if self.args.bc == 'nullspace': null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers solver_options = { 'monitor_convergence': True, 'maximum_iterations': 10000, 'nonzero_initial_guess': True } # 'nonzero_initial_guess': True with solver.solve(A, u, b) means that # Solver will use anything stored in u as an initial guess for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 if self.args.solP == 'richardson': self.solver_p.parameters['monitor_convergence'] = False self.solver_vel_tent.parameters['relative_tolerance'] = 10**( -self.args.prv1) self.solver_vel_tent.parameters['absolute_tolerance'] = 10**( -self.args.pav1) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10**( -self.args.prp) self.solver_p.parameters['absolute_tolerance'] = 10**( -self.args.pap) if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10E-10 self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.args.Vrestart > 0: self.solver_vel_tent.parameters['gmres'][ 'restart'] = self.args.Vrestart if self.args.solP == 'gmres' and self.args.Prestart > 0: self.solver_p.parameters['gmres'][ 'restart'] = self.args.Prestart # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 # debug function if problem.args.debug_rot: plot_cor_v = Function(self.V) while t < (ttime + dt / 2.0): self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble( a1_change, tensor=A1_change ) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble( a1_stab, tensor=A1_stab ) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow - out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.args.bc == 'nullspace': self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) foo.assign(p_ + p0) if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(True, foo) else: foo = Function(self.Q) foo.assign(p_) # we do not want to change p_ by averaging if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.args.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step and not onlyVel: problem.averaging_pressure(p_mod) problem.save_pressure(False, p_mod) end() if problem.args.debug_rot: # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step) # see comment next to argument definition plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V)) problem.fileDict['grad_cor']['file'].write(plot_cor_v, t) # compute functionals (e. g. forces) problem.compute_functionals( u_cor, p_mod if self.useRotationScheme else p_, t, step) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign( u_cor) # use corrected velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0
def sigma(v): return (2.0 * mu * epsilon(v) + lmbda * tr(epsilon(v)) * Identity(len(v)))
def T(u: Expr, p: Expr, mu: Expr): return 2 * mu * sym_grad(u) - p * Identity(u.ufl_shape[0])
def tangential_proj(u: Expr, n: Expr): """ See for instance: https://link.springer.com/content/pdf/10.1023/A:1022235512626.pdf """ return (Identity(u.ufl_shape[0]) - outer(n, n)) * u
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('solve', 'Running nonlinear solver', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') mesh = self.problem.mesh # Define function spaces (P2-P1) self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.W = MixedFunctionSpace([self.V, self.Q]) self.PS = FunctionSpace( mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space # to assign solution in space W.sub(0) to Function(V) we need FunctionAssigner (cannot be assigned directly) fa = FunctionAssigner(self.V, self.W.sub(0)) velSp = Function(self.V) problem.initialize(self.V, self.Q, self.PS, self.D) # Define unknown and test function(s) NS v, q = TestFunctions(self.W) w = Function(self.W) dw = TrialFunction(self.W) u, p = split(w) # Define fields n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) theta = 0.5 # Crank-Nicholson k = Constant(self.metadata['dt']) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u0, p0] = self.problem.get_initial_conditions([{ 'type': 'v', 'time': 0.0 }, { 'type': 'p', 'time': 0.0 }]) if doSave: problem.save_vel(False, u0) # boundary conditions bcu, bcp = problem.get_boundary_conditions(False, self.W.sub(0), self.W.sub(1)) # Define steady part of the equation def T(u): return -p * I + 2.0 * nu * sym(grad(u)) def F(u, v, q): return (inner(T(u), grad(v)) - q * div(u)) * dx + inner( grad(u) * u, v) * dx # Define variational forms F_ns = (inner( (u - u0), v) / k) * dx + (1.0 - theta) * F(u0, v, q) + theta * F( u, v, q) J_ns = derivative(F_ns, w, dw) # J_ns = derivative(F_ns, w) # did not work # NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns, form_compiler_parameters=ffc_options) NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns) # (var. formulation, unknown, Dir. BC, jacobian, optional) NS_solver = NonlinearVariationalSolver(NS_problem) prm = NS_solver.parameters prm['newton_solver']['absolute_tolerance'] = 1E-08 prm['newton_solver']['relative_tolerance'] = 1E-08 # prm['newton_solver']['maximum_iterations'] = 45 # prm['newton_solver']['relaxation_parameter'] = 1.0 prm['newton_solver']['linear_solver'] = 'mumps' # prm['newton_solver']['lu_solver']['same_nonzero_pattern'] = True info(NS_solver.parameters, True) self.tc.end('init') # Time-stepping info("Running of direct method") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt / 2.0): self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # Compute begin("Solving NS ....") try: self.tc.start('solve') NS_solver.solve() self.tc.end('solve') except RuntimeError as inst: problem.report_fail(t) return 1 end() # Extract solutions: (u, p) = w.split() fa.assign(velSp, u) # we are assigning twice (now and inside save_vel), but it works with one method save_vel for direct and # projection (we could split save_vel to save one assign) if save_this_step: self.tc.start('saveVel') problem.save_vel(False, velSp) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u) problem.compute_err(False, u, t) problem.compute_div(False, u) # foo = Function(self.Q) # foo.assign(p) # problem.averaging_pressure(foo) # if save_this_step and not onlyVel: # problem.save_pressure(False, foo) if save_this_step and not onlyVel: problem.save_pressure(False, p) # compute functionals (e. g. forces) problem.compute_functionals(u, p, t, step) # Move to next time step self.tc.start('next') u0.assign(velSp) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: direct method") problem.report() return 0
def compliance(sigma, u, mu, lmbda): return sigma / (2 * mu) - lmbda / (4 * mu * (lmbda + mu)) * tr(sigma) * Identity( u.geometric_dimension())
def compliance(sigma): ' Returns the strain tensor as a function of sigma ' return sigma/(2*mu) - lmbda/(4*mu*(lmbda + mu))*tr(sigma)*Identity(u.geometric_dimension())
def compute_stress(displacement, mu, lmbda): return 2.0 * mu * compute_strain(displacement) + \ lmbda * tr(compute_strain(displacement)) * Identity(len(displacement))
# is assumed to be the same as the spatial dimension (in this case 3), # unless otherwise specified. # # Next, we will be needing functions for the boundary source ``B``, the # traction ``T`` and the displacement solution itself ``u``:: # Functions u = Coefficient(element) # Displacement from previous iteration # B = Coefficient(element) # Body force per unit volume # T = Coefficient(element) # Traction force on the boundary # Now, we can define the kinematic quantities involved in the model:: # Kinematics d = len(u) I = Identity(d) # Identity tensor F = variable(I + grad(u)) # Deformation gradient C = F.T*F # Right Cauchy-Green tensor # Invariants of deformation tensors Ic = tr(C) J = det(F) # Before defining the energy density and thus the total potential # energy, it only remains to specify constants for the elasticity # parameters:: # Elasticity parameters E = 10.0 nu = 0.3 mu = E/(2*(1 + nu))
def compute_growth_induced_jacobian(growth_induced_strain, dim): return det(Identity(dim) + growth_induced_strain)
def __init__(self, io_params, time_params, fem_params, constitutive_models, bc_dict, time_curves, io, comm=None): problem_base.__init__(self, io_params, time_params, comm) self.problem_physics = 'solid' self.simname = io_params['simname'] self.io = io # number of distinct domains (each one has to be assigned a own material model) self.num_domains = len(constitutive_models) self.order_disp = fem_params['order_disp'] try: self.order_pres = fem_params['order_pres'] except: self.order_pres = 1 self.quad_degree = fem_params['quad_degree'] self.incompressible_2field = fem_params['incompressible_2field'] self.fem_params = fem_params self.constitutive_models = constitutive_models # collect domain data self.dx_, self.rho0, self.rayleigh, self.eta_m, self.eta_k = [], [], [False]*self.num_domains, [], [] for n in range(self.num_domains): # integration domains self.dx_.append(dx(subdomain_data=self.io.mt_d, subdomain_id=n+1, metadata={'quadrature_degree': self.quad_degree})) # data for inertial and viscous forces: density and damping if self.timint != 'static': self.rho0.append(constitutive_models['MAT'+str(n+1)+'']['inertia']['rho0']) if 'rayleigh_damping' in constitutive_models['MAT'+str(n+1)+''].keys(): self.rayleigh[n] = True self.eta_m.append(constitutive_models['MAT'+str(n+1)+'']['rayleigh_damping']['eta_m']) self.eta_k.append(constitutive_models['MAT'+str(n+1)+'']['rayleigh_damping']['eta_k']) try: self.prestress_initial = fem_params['prestress_initial'] except: self.prestress_initial = False # type of discontinuous function spaces if str(self.io.mesh.ufl_cell()) == 'tetrahedron' or str(self.io.mesh.ufl_cell()) == 'triangle3D': dg_type = "DG" if (self.order_disp > 1 or self.order_pres > 1) and self.quad_degree < 3: raise ValueError("Use at least a quadrature degree of 3 or more for higher-order meshes!") elif str(self.io.mesh.ufl_cell()) == 'hexahedron' or str(self.io.mesh.ufl_cell()) == 'quadrilateral3D': dg_type = "DQ" if (self.order_disp > 1 or self.order_pres > 1) and self.quad_degree < 5: raise ValueError("Use at least a quadrature degree of 5 or more for higher-order meshes!") else: raise NameError("Unknown cell/element type!") # create finite element objects for u and p P_u = VectorElement("CG", self.io.mesh.ufl_cell(), self.order_disp) P_p = FiniteElement("CG", self.io.mesh.ufl_cell(), self.order_pres) # function spaces for u and p self.V_u = FunctionSpace(self.io.mesh, P_u) self.V_p = FunctionSpace(self.io.mesh, P_p) # Quadrature tensor, vector, and scalar elements Q_tensor = TensorElement("Quadrature", self.io.mesh.ufl_cell(), degree=1, quad_scheme="default") Q_vector = VectorElement("Quadrature", self.io.mesh.ufl_cell(), degree=1, quad_scheme="default") Q_scalar = FiniteElement("Quadrature", self.io.mesh.ufl_cell(), degree=1, quad_scheme="default") # not yet working - we cannot interpolate into Quadrature elements with the current dolfinx version currently! #self.Vd_tensor = FunctionSpace(self.io.mesh, Q_tensor) #self.Vd_vector = FunctionSpace(self.io.mesh, Q_vector) #self.Vd_scalar = FunctionSpace(self.io.mesh, Q_scalar) # Quadrature function spaces (currently not properly functioning for higher-order meshes!!!) self.Vd_tensor = TensorFunctionSpace(self.io.mesh, (dg_type, self.order_disp-1)) self.Vd_vector = VectorFunctionSpace(self.io.mesh, (dg_type, self.order_disp-1)) self.Vd_scalar = FunctionSpace(self.io.mesh, (dg_type, self.order_disp-1)) # functions self.du = TrialFunction(self.V_u) # Incremental displacement self.var_u = TestFunction(self.V_u) # Test function self.dp = TrialFunction(self.V_p) # Incremental pressure self.var_p = TestFunction(self.V_p) # Test function self.u = Function(self.V_u, name="Displacement") self.p = Function(self.V_p, name="Pressure") # values of previous time step self.u_old = Function(self.V_u) self.v_old = Function(self.V_u) self.a_old = Function(self.V_u) self.p_old = Function(self.V_p) # a setpoint displacement for multiscale analysis self.u_set = Function(self.V_u) self.p_set = Function(self.V_p) self.tau_a_set = Function(self.Vd_scalar) # initial (zero) functions for initial stiffness evaluation (e.g. for Rayleigh damping) self.u_ini, self.p_ini, self.theta_ini, self.tau_a_ini = Function(self.V_u), Function(self.V_p), Function(self.Vd_scalar), Function(self.Vd_scalar) self.theta_ini.vector.set(1.0) self.theta_ini.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # growth stretch self.theta = Function(self.Vd_scalar, name="theta") self.theta_old = Function(self.Vd_scalar) self.growth_thres = Function(self.Vd_scalar) # initialize to one (theta = 1 means no growth) self.theta.vector.set(1.0), self.theta_old.vector.set(1.0) self.theta.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD), self.theta_old.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # active stress self.tau_a = Function(self.Vd_scalar, name="tau_a") self.tau_a_old = Function(self.Vd_scalar) self.amp_old, self.amp_old_set = Function(self.Vd_scalar), Function(self.Vd_scalar) self.amp_old.vector.set(1.0), self.amp_old_set.vector.set(1.0) self.amp_old.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD), self.amp_old_set.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # prestressing history defgrad and spring prestress if self.prestress_initial: self.F_hist = Function(self.Vd_tensor, name="Defgrad_hist") self.u_pre = Function(self.V_u) else: self.F_hist = None self.u_pre = None self.internalvars = {"theta" : self.theta, "tau_a" : self.tau_a} self.internalvars_old = {"theta" : self.theta_old, "tau_a" : self.tau_a_old} # reference coordinates self.x_ref = Function(self.V_u) self.x_ref.interpolate(self.x_ref_expr) if self.incompressible_2field: self.ndof = self.u.vector.getSize() + self.p.vector.getSize() else: self.ndof = self.u.vector.getSize() # initialize solid time-integration class self.ti = timeintegration.timeintegration_solid(time_params, fem_params, time_curves, self.t_init, self.comm) # check for materials that need extra treatment (anisotropic, active stress, growth, ...) have_fiber1, have_fiber2 = False, False self.have_active_stress, self.active_stress_trig, self.have_frank_starling, self.have_growth = False, 'ode', False, False self.mat_active_stress, self.mat_growth, self.mat_remodel, self.mat_growth_dir, self.mat_growth_trig, self.mat_growth_thres = [False]*self.num_domains, [False]*self.num_domains, [False]*self.num_domains, [None]*self.num_domains, [None]*self.num_domains, []*self.num_domains self.localsolve, growth_dir = False, None self.actstress = [] for n in range(self.num_domains): if 'holzapfelogden_dev' in self.constitutive_models['MAT'+str(n+1)+''].keys() or 'guccione_dev' in self.constitutive_models['MAT'+str(n+1)+''].keys(): have_fiber1, have_fiber2 = True, True if 'active_fiber' in self.constitutive_models['MAT'+str(n+1)+''].keys(): have_fiber1 = True self.mat_active_stress[n], self.have_active_stress = True, True # if one mat has a prescribed active stress, all have to be! if 'prescribed_curve' in self.constitutive_models['MAT'+str(n+1)+'']['active_fiber']: self.active_stress_trig = 'prescribed' if 'prescribed_multiscale' in self.constitutive_models['MAT'+str(n+1)+'']['active_fiber']: self.active_stress_trig = 'prescribed_multiscale' if self.active_stress_trig == 'ode': act_curve = self.ti.timecurves(self.constitutive_models['MAT'+str(n+1)+'']['active_fiber']['activation_curve']) self.actstress.append(activestress_activation(self.constitutive_models['MAT'+str(n+1)+'']['active_fiber'], act_curve)) if self.actstress[-1].frankstarling: self.have_frank_starling = True if self.active_stress_trig == 'prescribed': self.ti.funcs_to_update.append({self.tau_a : self.ti.timecurves(self.constitutive_models['MAT'+str(n+1)+'']['active_fiber']['prescribed_curve'])}) if 'active_iso' in self.constitutive_models['MAT'+str(n+1)+''].keys(): self.mat_active_stress[n], self.have_active_stress = True, True # if one mat has a prescribed active stress, all have to be! if 'prescribed_curve' in self.constitutive_models['MAT'+str(n+1)+'']['active_iso']: self.active_stress_trig = 'prescribed' if 'prescribed_multiscale' in self.constitutive_models['MAT'+str(n+1)+'']['active_iso']: self.active_stress_trig = 'prescribed_multiscale' if self.active_stress_trig == 'ode': act_curve = self.ti.timecurves(self.constitutive_models['MAT'+str(n+1)+'']['active_iso']['activation_curve']) self.actstress.append(activestress_activation(self.constitutive_models['MAT'+str(n+1)+'']['active_iso'], act_curve)) if self.active_stress_trig == 'prescribed': self.ti.funcs_to_update.append({self.tau_a : self.ti.timecurves(self.constitutive_models['MAT'+str(n+1)+'']['active_iso']['prescribed_curve'])}) if 'growth' in self.constitutive_models['MAT'+str(n+1)+''].keys(): self.mat_growth[n], self.have_growth = True, True self.mat_growth_dir[n] = self.constitutive_models['MAT'+str(n+1)+'']['growth']['growth_dir'] self.mat_growth_trig[n] = self.constitutive_models['MAT'+str(n+1)+'']['growth']['growth_trig'] # need to have fiber fields for the following growth options if self.mat_growth_dir[n] == 'fiber' or self.mat_growth_trig[n] == 'fibstretch': have_fiber1 = True if self.mat_growth_dir[n] == 'radial': have_fiber1, have_fiber2 = True, True # in this case, we have a theta that is (nonlinearly) dependent on the deformation, theta = theta(C(u)), # therefore we need a local Newton iteration to solve for equilibrium theta (return mapping) prior to entering # the global Newton scheme - so flag localsolve to true if self.mat_growth_trig[n] != 'prescribed' and self.mat_growth_trig[n] != 'prescribed_multiscale': self.localsolve = True self.mat_growth_thres.append(self.constitutive_models['MAT'+str(n+1)+'']['growth']['growth_thres']) else: self.mat_growth_thres.append(as_ufl(0)) # for the case that we have a prescribed growth stretch over time, append curve to functions that need time updates # if one mat has a prescribed growth model, all have to be! if self.mat_growth_trig[n] == 'prescribed': self.ti.funcs_to_update.append({self.theta : self.ti.timecurves(self.constitutive_models['MAT'+str(n+1)+'']['growth']['prescribed_curve'])}) if 'remodeling_mat' in self.constitutive_models['MAT'+str(n+1)+'']['growth'].keys(): self.mat_remodel[n] = True else: self.mat_growth_thres.append(as_ufl(0)) # full linearization of our remodeling law can lead to excessive compiler times for ffcx... :-/ # let's try if we might can go without one of the critial terms (derivative of remodeling fraction w.r.t. C) try: self.lin_remod_full = fem_params['lin_remodeling_full'] except: self.lin_remod_full = True # growth threshold (as function, since in multiscale approach, it can vary element-wise) if self.have_growth and self.localsolve: growth_thres_proj = project(self.mat_growth_thres, self.Vd_scalar, self.dx_) self.growth_thres.vector.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) self.growth_thres.interpolate(growth_thres_proj) # read in fiber data if have_fiber1: fibarray = ['fiber'] if have_fiber2: fibarray.append('sheet') # fiber function space - vector defined on quadrature points V_fib = self.Vd_vector self.fib_func = self.io.readin_fibers(fibarray, V_fib, self.dx_) else: self.fib_func = None # for multiscale G&R analysis self.tol_stop_large = 0 # initialize kinematics class self.ki = solid_kinematics_constitutive.kinematics(fib_funcs=self.fib_func, F_hist=self.F_hist) # initialize material/constitutive class self.ma = [] for n in range(self.num_domains): self.ma.append(solid_kinematics_constitutive.constitutive(self.ki, self.constitutive_models['MAT'+str(n+1)+''], self.incompressible_2field, mat_growth=self.mat_growth[n], mat_remodel=self.mat_remodel[n])) # initialize solid variational form class self.vf = solid_variationalform.variationalform(self.var_u, self.du, self.var_p, self.dp, self.io.n0, self.x_ref) # initialize boundary condition class self.bc = boundaryconditions.boundary_cond_solid(bc_dict, self.fem_params, self.io, self.ki, self.vf, self.ti) if self.prestress_initial: # initialize prestressing history deformation gradient Id_proj = project(Identity(len(self.u)), self.Vd_tensor, self.dx_) self.F_hist.interpolate(Id_proj) self.bc_dict = bc_dict # Dirichlet boundary conditions if 'dirichlet' in self.bc_dict.keys(): self.bc.dirichlet_bcs(self.V_u) self.set_variational_forms_and_jacobians()
def compute_deviatoric_stress_tensor(stress_tensor, dim): return stress_tensor - (1. / 3.) * tr(stress_tensor) * Identity(dim)
x = SpatialCoordinate(cell) mu = Constant(0, "mu") nu = Constant(0, "nu") u = TrialFunction(spcU) v = TestFunction(spcU) p = TrialFunction(spcP) q = TestFunction(spcP) exact_u = as_vector([x[1] * (1. - x[1]), 0]) exact_p = as_vector([(-2 * x[0] + 2) * mu]) f = as_vector([ 0, ] * grid.dimension) f += nu * exact_u mainModel = (nu * dot(u, v) + mu * inner(grad(u) + grad(u).T, grad(v)) - dot(f, v)) * dx gradModel = -inner(p[0] * Identity(grid.dimension), grad(v)) * dx divModel = -div(u) * q[0] * dx massModel = inner(p, q) * dx preconModel = inner(grad(p), grad(q)) * dx # can also use 'operator' everywhere mainOp = create.scheme("galerkin", (mainModel == 0, DirichletBC(spcU, exact_u, 1)), spcU) gradOp = create.operator("galerkin", gradModel, spcP, spcU) divOp = create.operator("galerkin", divModel, spcU, spcP) massOp = create.scheme("galerkin", massModel == 0, spcP) preconOp = create.scheme("galerkin", preconModel == 0, spcP) mainOp.model.mu = 0.1 mainOp.model.nu = 0.01
def sigma(v): return 2.0 * mu * sym(grad(v)) + lmbda * tr(sym( grad(v))) * Identity(2)
# Fiber field A = Coefficient(A_element) # External forces T = Coefficient(u_element) p0 = Coefficient(p_element) # Material parameters FIXME rho = Constant(cell) K = Constant(cell) c00 = Constant(cell) c11 = Constant(cell) c22 = Constant(cell) # Deformation gradient I = Identity(d) F = I + grad(u) F = variable(F) Finv = inv(F) J = det(F) # Left Cauchy-Green deformation tensor B = F * F.T I1_B = tr(B) I2_B = (I1_B**2 - tr(B * B)) / 2 I3_B = J**2 # Right Cauchy-Green deformation tensor C = F.T * F I1_C = tr(C) I2_C = (I1_C**2 - tr(C * C)) / 2