u = TrialFunction(V) v = TestFunction(V) a = inner(sigma(u), grad(v)) * dx L = inner(f, v) * dx u0 = Function(V) with u0.vector.localForm() as bc_local: bc_local.set(0.0) # Set up boundary condition on inner surface bc = DirichletBC(u0, locate_dofs_geometrical(V, boundary)) # Explicitly compile a UFL Form into dolfinx Form form = Form(a, jit_parameters={ "cffi_extra_compile_args": "-Ofast -march=native", "cffi_verbose": True }) # Assemble system, applying boundary conditions and preserving symmetry A = assemble_matrix(form, [bc]) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) # Create solution function u = Function(V)
values[0] = -args.displ return values displ_top = 0.0 J, F, expr_compiled =\ fecoda.main.compile_forms(intern_var0, intern_var1, w0, w1, f, g, heat_flux, water_flux, co2_flux, t, dt, [], [dx]) DMG = FunctionSpace(mesh, ("DG", 0)) dmg0 = Function(DMG, name="dmg") # Define variational problem for projection proj_rhs = Form(ufl.inner(intern_var0["dmg"], ufl.TestFunction(DMG)) * dx) mass_DMG = assemble_matrix( ufl.inner(ufl.TrialFunction(DMG), ufl.TestFunction(DMG)) * dx, []) mass_DMG.assemble() t0 = time() # Global time loop for k in range(len(dtimes)): t.value = times[k] dt.value = dtimes[k] if rank == 0: logger.info(79 * "#") logger.info("Time: {:2.2} days.".format(t.value))
def compile_forms(iv0, iv1, w0, w1, f, g, heat_flux, water_flux, co2_flux, t, dt, reb_dx, con_dx, damage_off=False, randomize=0.0): """Return Jacobian and residual forms""" t0 = time() mesh = w0["displ"].function_space.mesh # Prepare zero initial guesses, test and trial fctions, global w_displ_trial = ufl.TrialFunction(w0["displ"].function_space) w_displ_test = ufl.TestFunction(w0["displ"].function_space) w_temp_trial = ufl.TrialFunction(w0["temp"].function_space) w_temp_test = ufl.TestFunction(w0["temp"].function_space) w_phi_trial = ufl.TrialFunction(w0["phi"].function_space) w_phi_test = ufl.TestFunction(w0["phi"].function_space) w_co2_trial = ufl.TrialFunction(w0["co2"].function_space) w_co2_test = ufl.TestFunction(w0["co2"].function_space) # # Creep, shrinkage strains # # Autogenous shrinkage increment deps_sh_au = (mps.eps_sh_au(t + dt) - mps.eps_sh_au(t)) # Thermal strain increment deps_th = (misc.beta_C * (w1["temp"] - w0["temp"]) * ufl.Identity(3)) # Drying shrinkage increment deps_sh_dr = (mps.k_sh * (w1["phi"] - w0["phi"]) * ufl.Identity(3)) eta_dash_mid = mps.eta_dash(iv0["eta_dash"], dt / 2.0, w0["temp"], w0["phi"]) # Prepare creep factors beta_cr = mps.beta_cr(dt) lambda_cr = mps.lambda_cr(dt, beta_cr) creep_v_mid = mps.creep_v(t + dt / 2.0) if randomize > 0.0: # Randomize Young's modulus # This helps convergence at the point where crack initiation begins # Randomized E fluctuates uniformly in [E-eps/2, E+eps/2] rnd = Function(iv0["eta_dash"].function_space) rnd.vector.array[:] = 1.0 - randomize / 2 + np.random.rand( *rnd.vector.array.shape) * randomize else: rnd = 1.0 E_kelv = rnd * mps.E_kelv(creep_v_mid, lambda_cr, dt, t, eta_dash_mid) gamma0 = [] for i in range(mps.M): gamma0.append(iv0[f"gamma_{i}"]) deps_cr_kel = mps.deps_cr_kel(beta_cr, gamma0, creep_v_mid) deps_cr_dash = mps.deps_cr_dash(iv0["sigma"], eta_dash_mid, dt) deps_el = mech.eps_el(w1["displ"] - w0["displ"], deps_th, deps_cr_kel, deps_cr_dash, deps_sh_dr, deps_sh_au) # Water vapour saturation pressure p_sat = water.p_sat(0.5 * (w1["temp"] + w0["temp"])) water_cont = water.water_cont(0.5 * (w1["phi"] + w0["phi"])) dw_dphi = water.dw_dphi(0.5 * (w1["phi"] + w0["phi"])) # Rate of CaCO_3 concentration change dot_caco3 = co2.dot_caco3(dt * 24 * 60 * 60, iv0["caco3"], w1["phi"], w1["co2"], w1["temp"]) # # Balances residuals # sigma_eff = iv0["sigma"] + mech.stress(E_kelv, deps_el) eps_eqv = ufl.Max( damage_rankine.eps_eqv(sigma_eff, mps.E_static(creep_v_mid)), iv0["eps_eqv"]) f_c = mech.f_c(t) f_t = mech.f_t(f_c) G_f = mech.G_f(f_c) dmg = damage_rankine.damage(eps_eqv, mesh, mps.E_static(creep_v_mid), f_t, G_f) # Prepare stress increments if damage_off: dmg = 0.0 * dmg eps_eqv = iv0["eps_eqv"] sigma_rebar = mech.stress_rebar(mech.E_rebar, w1["displ"]) sigma = (1.0 - dmg) * sigma_eff _con_dx = [] for dx in con_dx: _con_dx += [dx(metadata={"quadrature_degree": quad_degree_stress})] _con_dx = ufl.classes.MeasureSum(*_con_dx) _reb_dx = [] for dx in reb_dx: _reb_dx += [dx(metadata={"quadrature_degree": quad_degree_stress})] _reb_dx = ufl.classes.MeasureSum(*_reb_dx) # Momentum balance for concrete material mom_balance = -ufl.inner(sigma, ufl.grad(w_displ_test)) * _con_dx # Momentum balance for rebar material if len(reb_dx) > 0: mom_balance += -ufl.inner(sigma_rebar, ufl.grad(w_displ_test)) * _reb_dx # Add volume body forces for measure, force in g.items(): mom_balance -= ufl.inner(force, w_displ_test) * measure # Add surface forces to mom balance for measure, force in f.items(): mom_balance += ufl.inner(force, w_displ_test) * measure _thc_dx = [] for dx in con_dx + reb_dx: _thc_dx += [dx(metadata={"quadrature_degree": quad_degree_thc})] _thc_dx = ufl.classes.MeasureSum(*_thc_dx) # Energy balance = evolution of temperature energy_balance = ( mech.rho_c * misc.C_pc / (dt * 24 * 60 * 60) * ufl.inner( (w1["temp"] - w0["temp"]), w_temp_test) * _thc_dx + misc.lambda_c * ufl.inner(ufl.grad(w1["temp"]), ufl.grad(w_temp_test)) * _thc_dx + water.h_v * water.delta_p * ufl.inner(ufl.grad( w1["phi"] * p_sat), ufl.grad(w_temp_test)) * _thc_dx + co2.alpha3 * dot_caco3 * w_temp_test * _thc_dx) # Water balance = evolution of humidity water_balance = (ufl.inner( dw_dphi * 1.0 / (dt * 24 * 60 * 60) * (w1["phi"] - w0["phi"]), w_phi_test) * _thc_dx + ufl.inner( dw_dphi * water.D_ws(water_cont) * ufl.grad(w1["phi"]) + water.delta_p * ufl.grad(w1["phi"] * p_sat), ufl.grad(w_phi_test)) * _thc_dx + co2.alpha2 * dot_caco3 * w_phi_test * _thc_dx) for measure, flux in water_flux.items(): water_balance -= ufl.inner(flux, w_phi_test) * measure co2_balance = ( ufl.inner(1.0 / (dt * 24 * 60 * 60) * (w1["co2"] - w0["co2"]), w_co2_test) * _thc_dx + ufl.inner(co2.D_co2 * ufl.grad(w1["co2"]), ufl.grad(w_co2_test)) * _thc_dx + co2.alpha4 * dot_caco3 * w_co2_test * _thc_dx) for measure, flux in co2_flux.items(): co2_balance -= ufl.inner(flux, w_co2_test) * measure J_mom = ufl.derivative(mom_balance, w1["displ"], w_displ_trial) J_energy_temp = ufl.derivative(energy_balance, w1["temp"], w_temp_trial) J_energy_hum = ufl.derivative(energy_balance, w1["phi"], w_phi_trial) J_energy_co2 = ufl.derivative(energy_balance, w1["co2"], w_co2_trial) J_energy = J_energy_hum + J_energy_temp + J_energy_co2 J_water_temp = ufl.derivative(water_balance, w1["temp"], w_temp_trial) J_water_hum = ufl.derivative(water_balance, w1["phi"], w_phi_trial) J_water_co2 = ufl.derivative(water_balance, w1["co2"], w_co2_trial) J_water = J_water_temp + J_water_hum + J_water_co2 J_co2_temp = ufl.derivative(co2_balance, w1["temp"], w_temp_trial) J_co2_hum = ufl.derivative(co2_balance, w1["phi"], w_phi_trial) J_co2_co2 = ufl.derivative(co2_balance, w1["co2"], w_co2_trial) J_co2 = J_co2_temp + J_co2_hum + J_co2_co2 # Put all Jacobians together J_all = J_mom + J_energy + J_water + J_co2 # Lower algebra symbols and apply derivatives up to terminals # This is needed for the Replacer to work properly preserve_geometry_types = (ufl.CellVolume, ufl.FacetArea) J_all = ufl.algorithms.apply_algebra_lowering.apply_algebra_lowering(J_all) J_all = ufl.algorithms.apply_derivatives.apply_derivatives(J_all) J_all = ufl.algorithms.apply_geometry_lowering.apply_geometry_lowering( J_all, preserve_geometry_types) J_all = ufl.algorithms.apply_derivatives.apply_derivatives(J_all) J_all = ufl.algorithms.apply_geometry_lowering.apply_geometry_lowering( J_all, preserve_geometry_types) J_all = ufl.algorithms.apply_derivatives.apply_derivatives(J_all) J = extract_blocks(J_all, [w_displ_test, w_temp_test, w_phi_test, w_co2_test], [w_displ_trial, w_temp_trial, w_phi_trial, w_co2_trial]) J[0][0]._signature = "full" if not damage_off else "dmgoff" # Just make sure these are really empty Forms assert len(J[1][0].arguments()) == 0 assert len(J[2][0].arguments()) == 0 assert len(J[3][0].arguments()) == 0 F = [-mom_balance, -energy_balance, -water_balance, -co2_balance] rank = MPI.COMM_WORLD.rank if rank == 0: logger.info("Compiling tangents J...") J_compiled = [ Form(J[0][0]), [[Form(J[i][j]) for j in range(1, 4)] for i in range(1, 4)] ] if rank == 0: logger.info("Compiling residuals F...") F_compiled = [Form(F[0]), [Form(F[i]) for i in range(1, 4)]] expr = OrderedDict() eta_dash1 = mps.eta_dash(iv0["eta_dash"], dt, w1["temp"], w1["phi"]) expr["eta_dash"] = (eta_dash1, iv0["eta_dash"].function_space.ufl_element()) expr["caco3"] = (iv0["caco3"] + dt * 24 * 60 * 60 * dot_caco3, iv0["caco3"].function_space.ufl_element()) expr["eps_cr_kel"] = (iv0["eps_cr_kel"] + deps_cr_kel, iv0["eps_cr_kel"].function_space.ufl_element()) expr["eps_cr_dash"] = (iv0["eps_cr_dash"] + deps_cr_dash, iv0["eps_cr_dash"].function_space.ufl_element()) expr["eps_sh_dr"] = (iv0["eps_sh_dr"] + deps_sh_dr, iv0["eps_sh_dr"].function_space.ufl_element()) expr["eps_th"] = (iv0["eps_th"] + deps_th, iv0["eps_th"].function_space.ufl_element()) expr["sigma"] = (iv0["sigma"] + mech.stress(E_kelv, deps_el), iv0["sigma"].function_space.ufl_element()) expr["eps_eqv"] = (eps_eqv, iv0["eps_eqv"].function_space.ufl_element()) expr["dmg"] = (dmg, iv0["dmg"].function_space.ufl_element()) for i in range(mps.M): expr[f"gamma_{i}"] = (lambda_cr[i] * (iv1["sigma"] - iv0["sigma"]) + (beta_cr[i]) * gamma0[i], gamma0[i].function_space.ufl_element()) expr_compiled = OrderedDict() for name, item in expr.items(): if rank == 0: logger.info(f"Compiling expressions for {name}...") expr_compiled[name] = CompiledExpression(item[0], item[1]) if rank == 0: logger.info(f"[Timer] UFL forms setup and compilation: {time() - t0}") return J_compiled, F_compiled, expr_compiled
# Controlling compilation parameters # ---------------------------------- # # Parameters which control FFCX and JIT compilation could be set # directly with the interface of :py:class:`Form <dolfinx.fem.Form>` or # via environmental variables. # # This demo shows a mixed approach, where C compilation # flags are set with environmental variables. # Some parameters which control FFCX compilation are passed directly to the ``Form``. # :: os.environ["DOLFINX_JIT_CFLAGS"] = "-Ofast -march=native" os.environ["FFCX_VERBOSITY"] = "20" form = Form(a, form_compiler_parameters={"quadrature_degree": 1}) # The use of such aggresive compiler flags (e.g. ``-Ofast`` violates IEEE floating point standard) # often results in a faster assembly code, but slower JIT compilation. # FFCX verbosity levels follow Python std logging library levels, https://docs.python.org/3/library/logging.html. # To see all available form compiler parameters run ``ffcx --help`` in the commandline. # # .. warning:: # Environmental variables override any other parameters passed to the ``Form``, or directly stated # in the metadata of an integral. Please make sure there are no environmental variables set # with side-effects. # # Assembly and solve # ------------------ # ::