def replace(e, mapping): """Replace subexpressions in expression. @param e: An Expr or Form. @param mapping: A dict with from:to replacements to perform. """ mapping2 = dict((k, as_ufl(v)) for (k, v) in mapping.items()) # Workaround for problem with delayed derivative evaluation # The problem is that J = derivative(f(g, h), g) does not evaluate immediately # So if we subsequently do replace(J, {g: h}) we end up with an expression: # derivative(f(h, h), h) # rather than what were were probably thinking of: # replace(derivative(f(g, h), g), {g: h}) # # To fix this would require one to expand derivatives early (which # is not attractive), or make replace lazy too. if has_exact_type(e, CoefficientDerivative): # Hack to avoid circular dependencies from ufl.algorithms.ad import expand_derivatives e = expand_derivatives(e) return map_integrand_dags(Replacer(mapping2), e)
def heat_subdomainbc(N, deg, butcher_tableau, splitting=AI): dt = Constant(1.0 / N) t = Constant(0.0) msh = UnitSquareMesh(N, N) V = FunctionSpace(msh, "CG", 2) x, y = SpatialCoordinate(msh) uexact = t*(x+y) rhs = expand_derivatives(diff(uexact, t)) - div(grad(uexact)) u = interpolate(uexact, V) v = TestFunction(V) n = FacetNormal(msh) F = inner(Dt(u), v)*dx + inner(grad(u), grad(v))*dx - inner(rhs, v)*dx - inner(dot(grad(uexact), n), v)*ds bc = DirichletBC(V, uexact, [1, 2]) luparams = {"mat_type": "aij", "snes_type": "ksponly", "ksp_type": "preonly", "pc_type": "lu"} stepper = TimeStepper(F, butcher_tableau, t, dt, u, bcs=bc, solver_parameters=luparams) while (float(t) < 1.0): if (float(t) + float(dt) > 1.0): dt.assign(1.0 - float(t)) stepper.advance() t.assign(float(t) + float(dt)) return norm(u-uexact)
def test_1d_heat_dirichletbc(butcher_tableau): # Boundary values u_0 = Constant(2.0) u_1 = Constant(3.0) N = 100 x0 = 0.0 x1 = 10.0 msh = IntervalMesh(N, x1) V = FunctionSpace(msh, "CG", 1) dt = Constant(10.0 / N) t = Constant(0.0) (x,) = SpatialCoordinate(msh) # Method of manufactured solutions copied from Heat equation demo. S = Constant(2.0) C = Constant(1000.0) B = (x - Constant(x0)) * (x - Constant(x1)) / C R = (x * x) ** 0.5 # Note end linear contribution uexact = ( B * atan(t) * (pi / 2.0 - atan(S * (R - t))) + u_0 + ((x - x0) / x1) * (u_1 - u_0) ) rhs = expand_derivatives(diff(uexact, t)) - div(grad(uexact)) u = interpolate(uexact, V) v = TestFunction(V) F = ( inner(Dt(u), v) * dx + inner(grad(u), grad(v)) * dx - inner(rhs, v) * dx ) bc = [ DirichletBC(V, u_1, 2), DirichletBC(V, u_0, 1), ] luparams = {"mat_type": "aij", "ksp_type": "preonly", "pc_type": "lu"} stepper = TimeStepper( F, butcher_tableau, t, dt, u, bcs=bc, solver_parameters=luparams ) t_end = 2.0 while float(t) < t_end: if float(t) + float(dt) > t_end: dt.assign(t_end - float(t)) stepper.advance() t.assign(float(t) + float(dt)) # Check solution and boundary values assert norm(u - uexact) / norm(uexact) < 10.0 ** -5 assert isclose(u.at(x0), u_0) assert isclose(u.at(x1), u_1)
def replace(e, mapping): """Replace terminal objects in expression. @param e: An Expr or Form. @param mapping: A dict with from:to replacements to perform. """ mapping2 = dict((k, as_ufl(v)) for (k, v) in mapping.items()) # Workaround for problem with delayed derivative evaluation if has_exact_type(e, CoefficientDerivative): # Hack to avoid circular dependencies from ufl.algorithms.ad import expand_derivatives e = expand_derivatives(e) return map_integrand_dags(Replacer(mapping2), e)
def RTCFtest(N, deg, butcher_tableau, splitting=AI): msh = UnitSquareMesh(N, N, quadrilateral=True) Ve = FiniteElement("RTCF", msh.ufl_cell(), 2) V = FunctionSpace(msh, Ve) dt = Constant(0.1 / N) t = Constant(0.0) x, y = SpatialCoordinate(msh) uexact = as_vector( [t + 2 * t * x + 4 * t * y, 7 * t + 5 * t * x + 6 * t * y]) rhs = expand_derivatives(diff(uexact, t)) + grad(div(uexact)) u = project(uexact, V) v = TestFunction(V) F = inner(Dt(u), v) * dx + inner(div(u), div(v)) * dx - inner(rhs, v) * dx bc = DirichletBC(V, uexact, "on_boundary") luparams = {"mat_type": "aij", "ksp_type": "preonly", "pc_type": "lu"} stepper = TimeStepper(F, butcher_tableau, t, dt, u, bcs=bc, solver_parameters=luparams, splitting=splitting) while (float(t) < 0.1): if (float(t) + float(dt) > 0.1): dt.assign(0.1 - float(t)) stepper.advance() print(float(t)) t.assign(float(t) + float(dt)) return norm(u - uexact)
def curltest(N, deg, butcher_tableau, splitting): msh = UnitSquareMesh(N, N) Ve = FiniteElement("N1curl", msh.ufl_cell(), 2, variant="integral") V = FunctionSpace(msh, Ve) dt = Constant(0.1 / N) t = Constant(0.0) x, y = SpatialCoordinate(msh) uexact = as_vector([t + 2*t*x + 4*t*y + 3*t*(y**2) + 2*t*x*y, 7*t + 5*t*x + 6*t*y - 3*t*x*y - 2*t*(x**2)]) rhs = expand_derivatives(diff(uexact, t)) + curl(curl(uexact)) u = interpolate(uexact, V) v = TestFunction(V) n = FacetNormal(msh) F = inner(Dt(u), v)*dx + inner(curl(u), curl(v))*dx - inner(curlcross(uexact, n), v)*ds - inner(rhs, v)*dx bc = DirichletBC(V, uexact, [1, 2]) luparams = {"mat_type": "aij", "ksp_type": "preonly", "pc_type": "lu"} stepper = TimeStepper(F, butcher_tableau, t, dt, u, bcs=bc, solver_parameters=luparams, splitting=splitting) while (float(t) < 0.1): if (float(t) + float(dt) > 0.1): dt.assign(0.1 - float(t)) stepper.advance() print(float(t)) t.assign(float(t) + float(dt)) return norm(u-uexact)
def preprocess(form, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input form to obtain form metadata, including a modified (preprocessed) form more easily manipulated by form compilers. The original form is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess') # TODO: Reposition tic calls after refactoring. # Check that we get a form ufl_assert(isinstance(form, Form), "Expecting Form.") original_form = form # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty form data form_data = FormData() # Store copies of preprocess input data, for future validation if called again... form_data._input_object_names = dict(object_names) form_data._input_element_mapping = dict(element_mapping) #form_data._input_common_cell = no need to store this # Store name of form if given, otherwise empty string # such that automatic names can be assigned externally form_data.name = object_names.get(id(form), "") # Extract common cell common_cell = extract_common_cell(form, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') form = expand_derivatives(form, common_cell.geometric_dimension()) # EXPR # Propagate restrictions of interior facet integrals to the terminal nodes form = propagate_restrictions(form) # INTEGRAL, EXPR # --- BEGIN DOMAIN SPLITTING AND JOINING # Store domain metadata per domain type form_data.domain_data = extract_domain_data(form) # Split up integrals and group by domain type and domain id, # adding integrands with same domain and compiler data sub_integral_data = integral_dict_to_sub_integral_data( form.integral_groups()) # TODO: Replace integral_data with this through ufl and ffc? if 0: print_sub_integral_data(sub_integral_data) # Reconstruct form from these integrals in a more canonical representation form = reconstruct_form_from_sub_integral_data(sub_integral_data, form_data.domain_data) # Represent in the way ffc expects TODO: Change ffc? Fine for now. form_data.integral_data = convert_sub_integral_data_to_integral_data( sub_integral_data) # --- END DOMAIN SPLITTING AND JOINING # --- BEGIN FUNCTION ANALYSIS # --- BEGIN SPLIT EXPR JOIN # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(form) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') # TODO: Remove renumbered ones? replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) # Note: This is the earliest point signature can be computed # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w, v) for (v, w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [ inv_replace_map[w] for w in renumbered_coefficients ] # TODO: Build mapping from object to position instead? But we need mapped elements as well anyway. #argument_positions = { v: i } #coefficient_positions = { w: i } # Store data extracted by preprocessing form_data.original_arguments = original_arguments form_data.original_coefficients = original_coefficients # --- END SPLIT INTEGRAL JOIN # Mappings from elements and functions (coefficients and arguments) # that reside in form to objects with canonical numbering as well as # completed cells and elements # TODO: Create additional function mappings per integral, # to have different counts? Depends on future UFC design. form_data.element_replace_map = element_mapping form_data.function_replace_map = replace_map # Store some useful dimensions form_data.rank = len(form_data.original_arguments) form_data.num_coefficients = len(form_data.original_coefficients) # Store argument names form_data.argument_names = \ [object_names.get(id(form_data.original_arguments[i]), "v%d" % i) for i in range(form_data.rank)] # Store coefficient names form_data.coefficient_names = \ [object_names.get(id(form_data.original_coefficients[i]), "w%d" % i) for i in range(form_data.num_coefficients)] # --- END FUNCTION ANALYSIS # --- BEGIN SIGNATURE COMPUTATION # TODO: Compute signatures of each INTEGRAL and EXPR as well, perhaps compute it hierarchially from integral_data? # Store signature of form tic('signature') # TODO: Remove signature() from Form, not safe to cache with a replacement map #form_data.signature = form.signature(form_data.function_replace_map) form_data.signature = compute_form_signature( form, form_data.function_replace_map) # --- END SIGNATURE COMPUTATION # --- BEGIN CONSISTENCY CHECKS # Check that we don't have a mixed linear/bilinear form or anything like that ufl_assert( len(compute_form_arities(form)) == 1, "All terms in form must have same rank.") # --- END CONSISTENCY CHECKS # --- BEGIN ELEMENT DATA # Store elements, sub elements and element map tic('extract_elements') form_data.argument_elements = tuple(f.element() for f in renumbered_arguments) form_data.coefficient_elements = tuple(f.element() for f in renumbered_coefficients) form_data.elements = form_data.argument_elements + form_data.coefficient_elements form_data.unique_elements = unique_tuple(form_data.elements) form_data.sub_elements = extract_sub_elements(form_data.elements) form_data.unique_sub_elements = unique_tuple(form_data.sub_elements) # --- END ELEMENT DATA # --- BEGIN DOMAIN DATA # Store element domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.domains = tuple( sorted(set(element.domain() for element in form_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.top_domains = tuple( sorted(set(domain.top_domain() for domain in form_data.domains))) # Store common cell form_data.cell = common_cell # Store data related to cell form_data.geometric_dimension = form_data.cell.geometric_dimension() form_data.topological_dimension = form_data.cell.topological_dimension() # Store number of domains for integral types form_data.num_sub_domains = extract_num_sub_domains(form) # --- END DOMAIN DATA # A coarse profiling implementation TODO: Add counting of nodes, Add memory usage tic.end() if preprocess.enable_profiling: print tic # Store preprocessed form and return form_data.preprocessed_form = form form_data.preprocessed_form._is_preprocessed = True # Attach signatures to original and preprocessed forms TODO: Avoid this? ufl_assert(form_data.preprocessed_form._signature is None, "") form_data.preprocessed_form._signature = form_data.signature ufl_assert(original_form._signature is None, "") original_form._signature = form_data.signature return form_data
def preprocess_expression(expr, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input expression to obtain expression metadata, including a modified (preprocessed) expression more easily manipulated by expression compilers. The original expression is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess_expression') # Check that we get an expression ufl_assert(isinstance(expr, Expr), "Expecting Expr.") # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty expression data expr_data = ExprData() # Store original expression expr_data.original_expr = expr # Store name of expr if given, otherwise empty string # such that automatic names can be assigned externally expr_data.name = object_names.get(id(expr), "") # TODO: Or default to 'expr'? # Extract common cell common_cell = extract_common_cell(expr, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') expr = expand_derivatives(expr, common_cell.geometric_dimension()) # Renumber indices #expr = renumber_indices(expr) # TODO: No longer needed? # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(expr) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) expr = replace(expr, replace_map) # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w, v) for (v, w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [ inv_replace_map[w] for w in renumbered_coefficients ] # Store data extracted by preprocessing expr_data.arguments = renumbered_arguments # TODO: Needed? expr_data.coefficients = renumbered_coefficients # TODO: Needed? expr_data.original_arguments = original_arguments expr_data.original_coefficients = original_coefficients expr_data.renumbered_arguments = renumbered_arguments expr_data.renumbered_coefficients = renumbered_coefficients tic('replace') # Mappings from elements and functions (coefficients and arguments) # that reside in expr to objects with canonical numbering as well as # completed cells and elements expr_data.element_replace_map = element_mapping expr_data.function_replace_map = replace_map # Store signature of form tic('signature') expr_data.signature = compute_expression_signature( expr, expr_data.function_replace_map) # Store elements, sub elements and element map tic('extract_elements') expr_data.elements = tuple( f.element() for f in chain(renumbered_arguments, renumbered_coefficients)) expr_data.unique_elements = unique_tuple(expr_data.elements) expr_data.sub_elements = extract_sub_elements(expr_data.elements) expr_data.unique_sub_elements = unique_tuple(expr_data.sub_elements) # Store element domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.domains = tuple( sorted(set(element.domain() for element in expr_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.top_domains = tuple( sorted(set(domain.top_domain() for domain in expr_data.domains))) # Store common cell expr_data.cell = common_cell # Store data related to cell expr_data.geometric_dimension = expr_data.cell.geometric_dimension() expr_data.topological_dimension = expr_data.cell.topological_dimension() # Store some useful dimensions expr_data.rank = len(expr_data.arguments) # TODO: Is this useful for expr? expr_data.num_coefficients = len(expr_data.coefficients) # Store argument names # TODO: Is this useful for expr? expr_data.argument_names = \ [object_names.get(id(expr_data.original_arguments[i]), "v%d" % i) for i in range(expr_data.rank)] # Store coefficient names expr_data.coefficient_names = \ [object_names.get(id(expr_data.original_coefficients[i]), "w%d" % i) for i in range(expr_data.num_coefficients)] # Store preprocessed expression expr_data.preprocessed_expr = expr tic.end() # A coarse profiling implementation # TODO: Add counting of nodes # TODO: Add memory usage if preprocess_expression.enable_profiling: print tic return expr_data
def preprocess(form, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input form to obtain form metadata, including a modified (preprocessed) form more easily manipulated by form compilers. The original form is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess') # TODO: Reposition tic calls after refactoring. # Check that we get a form ufl_assert(isinstance(form, Form), "Expecting Form.") original_form = form # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty form data form_data = FormData() # Store copies of preprocess input data, for future validation if called again... form_data._input_object_names = dict(object_names) form_data._input_element_mapping = dict(element_mapping) #form_data._input_common_cell = no need to store this # Store name of form if given, otherwise empty string # such that automatic names can be assigned externally form_data.name = object_names.get(id(form), "") # Extract common cell common_cell = extract_common_cell(form, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') form = expand_derivatives(form, common_cell.geometric_dimension()) # EXPR # Propagate restrictions of interior facet integrals to the terminal nodes form = propagate_restrictions(form) # INTEGRAL, EXPR # --- BEGIN DOMAIN SPLITTING AND JOINING # Store domain metadata per domain type form_data.domain_data = extract_domain_data(form) # Split up integrals and group by domain type and domain id, # adding integrands with same domain and compiler data sub_integral_data = integral_dict_to_sub_integral_data(form.integral_groups()) # TODO: Replace integral_data with this through ufl and ffc? if 0: print_sub_integral_data(sub_integral_data) # Reconstruct form from these integrals in a more canonical representation form = reconstruct_form_from_sub_integral_data(sub_integral_data, form_data.domain_data) # Represent in the way ffc expects TODO: Change ffc? Fine for now. form_data.integral_data = convert_sub_integral_data_to_integral_data(sub_integral_data) # --- END DOMAIN SPLITTING AND JOINING # --- BEGIN FUNCTION ANALYSIS # --- BEGIN SPLIT EXPR JOIN # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(form) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') # TODO: Remove renumbered ones? replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) # Note: This is the earliest point signature can be computed # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w,v) for (v,w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [inv_replace_map[w] for w in renumbered_coefficients] # TODO: Build mapping from object to position instead? But we need mapped elements as well anyway. #argument_positions = { v: i } #coefficient_positions = { w: i } # Store data extracted by preprocessing form_data.original_arguments = original_arguments form_data.original_coefficients = original_coefficients # --- END SPLIT INTEGRAL JOIN # Mappings from elements and functions (coefficients and arguments) # that reside in form to objects with canonical numbering as well as # completed cells and elements # TODO: Create additional function mappings per integral, # to have different counts? Depends on future UFC design. form_data.element_replace_map = element_mapping form_data.function_replace_map = replace_map # Store some useful dimensions form_data.rank = len(form_data.original_arguments) form_data.num_coefficients = len(form_data.original_coefficients) # Store argument names form_data.argument_names = \ [object_names.get(id(form_data.original_arguments[i]), "v%d" % i) for i in range(form_data.rank)] # Store coefficient names form_data.coefficient_names = \ [object_names.get(id(form_data.original_coefficients[i]), "w%d" % i) for i in range(form_data.num_coefficients)] # --- END FUNCTION ANALYSIS # --- BEGIN SIGNATURE COMPUTATION # TODO: Compute signatures of each INTEGRAL and EXPR as well, perhaps compute it hierarchially from integral_data? # Store signature of form tic('signature') # TODO: Remove signature() from Form, not safe to cache with a replacement map #form_data.signature = form.signature(form_data.function_replace_map) form_data.signature = compute_form_signature(form, form_data.function_replace_map) # --- END SIGNATURE COMPUTATION # --- BEGIN CONSISTENCY CHECKS # Check that we don't have a mixed linear/bilinear form or anything like that ufl_assert(len(compute_form_arities(form)) == 1, "All terms in form must have same rank.") # --- END CONSISTENCY CHECKS # --- BEGIN ELEMENT DATA # Store elements, sub elements and element map tic('extract_elements') form_data.argument_elements = tuple(f.element() for f in renumbered_arguments) form_data.coefficient_elements = tuple(f.element() for f in renumbered_coefficients) form_data.elements = form_data.argument_elements + form_data.coefficient_elements form_data.unique_elements = unique_tuple(form_data.elements) form_data.sub_elements = extract_sub_elements(form_data.elements) form_data.unique_sub_elements = unique_tuple(form_data.sub_elements) # --- END ELEMENT DATA # --- BEGIN DOMAIN DATA # Store element domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.domains = tuple(sorted(set(element.domain() for element in form_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.top_domains = tuple(sorted(set(domain.top_domain() for domain in form_data.domains))) # Store common cell form_data.cell = common_cell # Store data related to cell form_data.geometric_dimension = form_data.cell.geometric_dimension() form_data.topological_dimension = form_data.cell.topological_dimension() # Store number of domains for integral types form_data.num_sub_domains = extract_num_sub_domains(form) # --- END DOMAIN DATA # A coarse profiling implementation TODO: Add counting of nodes, Add memory usage tic.end() if preprocess.enable_profiling: print tic # Store preprocessed form and return form_data.preprocessed_form = form form_data.preprocessed_form._is_preprocessed = True # Attach signatures to original and preprocessed forms TODO: Avoid this? ufl_assert(form_data.preprocessed_form._signature is None, "") form_data.preprocessed_form._signature = form_data.signature ufl_assert(original_form._signature is None, "") original_form._signature = form_data.signature return form_data
def preprocess_expression(expr, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input expression to obtain expression metadata, including a modified (preprocessed) expression more easily manipulated by expression compilers. The original expression is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess_expression') # Check that we get an expression ufl_assert(isinstance(expr, Expr), "Expecting Expr.") # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty expression data expr_data = ExprData() # Store original expression expr_data.original_expr = expr # Store name of expr if given, otherwise empty string # such that automatic names can be assigned externally expr_data.name = object_names.get(id(expr), "") # TODO: Or default to 'expr'? # Extract common cell common_cell = extract_common_cell(expr, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') expr = expand_derivatives(expr, common_cell.geometric_dimension()) # Renumber indices #expr = renumber_indices(expr) # TODO: No longer needed? # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(expr) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) expr = replace(expr, replace_map) # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w,v) for (v,w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [inv_replace_map[w] for w in renumbered_coefficients] # Store data extracted by preprocessing expr_data.arguments = renumbered_arguments # TODO: Needed? expr_data.coefficients = renumbered_coefficients # TODO: Needed? expr_data.original_arguments = original_arguments expr_data.original_coefficients = original_coefficients expr_data.renumbered_arguments = renumbered_arguments expr_data.renumbered_coefficients = renumbered_coefficients tic('replace') # Mappings from elements and functions (coefficients and arguments) # that reside in expr to objects with canonical numbering as well as # completed cells and elements expr_data.element_replace_map = element_mapping expr_data.function_replace_map = replace_map # Store signature of form tic('signature') expr_data.signature = compute_expression_signature(expr, expr_data.function_replace_map) # Store elements, sub elements and element map tic('extract_elements') expr_data.elements = tuple(f.element() for f in chain(renumbered_arguments, renumbered_coefficients)) expr_data.unique_elements = unique_tuple(expr_data.elements) expr_data.sub_elements = extract_sub_elements(expr_data.elements) expr_data.unique_sub_elements = unique_tuple(expr_data.sub_elements) # Store element domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.domains = tuple(sorted(set(element.domain() for element in expr_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.top_domains = tuple(sorted(set(domain.top_domain() for domain in expr_data.domains))) # Store common cell expr_data.cell = common_cell # Store data related to cell expr_data.geometric_dimension = expr_data.cell.geometric_dimension() expr_data.topological_dimension = expr_data.cell.topological_dimension() # Store some useful dimensions expr_data.rank = len(expr_data.arguments) # TODO: Is this useful for expr? expr_data.num_coefficients = len(expr_data.coefficients) # Store argument names # TODO: Is this useful for expr? expr_data.argument_names = \ [object_names.get(id(expr_data.original_arguments[i]), "v%d" % i) for i in range(expr_data.rank)] # Store coefficient names expr_data.coefficient_names = \ [object_names.get(id(expr_data.original_coefficients[i]), "w%d" % i) for i in range(expr_data.num_coefficients)] # Store preprocessed expression expr_data.preprocessed_expr = expr tic.end() # A coarse profiling implementation # TODO: Add counting of nodes # TODO: Add memory usage if preprocess_expression.enable_profiling: print tic return expr_data
def heat(butcher_tableau): N = 4 dt = Constant(1.0 / N) t = Constant(0.0) msh = UnitSquareMesh(N, N) deg = 2 V = FunctionSpace(msh, "CG", deg) x, y = SpatialCoordinate(msh) uexact = t * (x + y) rhs = expand_derivatives(diff(uexact, t)) - div(grad(uexact)) sols = [] luparams = { "mat_type": "aij", "snes_type": "ksponly", "ksp_type": "preonly", "pc_type": "lu" } ranaLD = { "mat_type": "aij", "snes_type": "ksponly", "ksp_type": "gmres", "ksp_monitor": None, "pc_type": "python", "pc_python_type": "irksome.RanaLD", "aux": { "pc_type": "fieldsplit", "pc_fieldsplit_type": "multiplicative" } } per_field = {"ksp_type": "preonly", "pc_type": "gamg"} for s in range(butcher_tableau.num_stages): ranaLD["fieldsplit_%s" % (s, )] = per_field ranaDU = { "mat_type": "aij", "snes_type": "ksponly", "ksp_type": "gmres", "ksp_monitor": None, "pc_type": "python", "pc_python_type": "irksome.RanaDU", "aux": { "pc_type": "fieldsplit", "pc_fieldsplit_type": "multiplicative" } } for s in range(butcher_tableau.num_stages): ranaLD["fieldsplit_%s" % (s, )] = per_field params = [luparams, ranaLD, ranaDU] for solver_parameters in params: F, u, bc = Fubc(V, uexact, rhs) stepper = TimeStepper(F, butcher_tableau, t, dt, u, bcs=bc, solver_parameters=solver_parameters) stepper.advance() sols.append(u) errs = [errornorm(sols[0], uu) for uu in sols[1:]] return numpy.max(errs)
ns = ButcherTableau.num_stages N = 64 msh = UnitSquareMesh(N, N) V = FunctionSpace(msh, "CG", 1) x, y = SpatialCoordinate(msh) dt = Constant(10.0 / N) t = Constant(0.0) # This will give the exact solution at any time t. We just have # to t.assign(time_we_want) uexact = exp(-t) * cos(pi * x) * sin(pi * y) # MMS works on symbolic differentiation of true solution, not weak form rhs = expand_derivatives(diff(uexact, t)) - div(grad(uexact)) u = interpolate(uexact, V) # define the variational form once outside the loop h = CellSize(msh) n = FacetNormal(msh) beta = Constant(100.0) v = TestFunction(V) F = (inner(Dt(u), v) * dx + inner(grad(u), grad(v)) * dx - inner(rhs, v) * dx - inner(dot(grad(u), n), v) * ds - inner(dot(grad(v), n), u - uexact) * ds + beta / h * inner(u - uexact, v) * ds) params = { "mat_type": "aij", "snes_type": "ksponly",
def assemble_mixed_system(*args, **kwargs): "Assemble mixed variational problem a == L or F == 0" assert (len(args) > 0) assert (isinstance(args[0], ufl.classes.Equation)) # Extract arguments eq, u, bcs, J, tol, M, preconditioner, form_compiler_parameters, solver_parameters\ = _extract_args(*args, **kwargs) if u.num_sub_spaces() > 0: if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form): # Extract blocks from the variational formulation eq_lhs_forms = extract_blocks(eq.lhs) eq_rhs_forms = extract_blocks(eq.rhs) # Create problem problem = MixedLinearVariationalProblem( eq_lhs_forms, eq_rhs_forms, u._functions, bcs, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = MixedLinearVariationalSolver(problem) system = solver.assemble_system() return system else: raise RuntimeError( "assemble_mixed_system is not available for non-linear problems yet" ) # Extract blocks from the variational formulation eq_lhs_forms = extract_blocks(eq.lhs) if J is None: cpp.log.info( "No Jacobian form specified for nonlinear variational problem." ) cpp.log.info( "Differentiating residual form F to obtain Jacobian J = F'." ) # Give the list of jacobian for each eq_lhs Js = [] for Fi in eq_lhs_forms: for uj in u._functions: derivative = formmanipulations.derivative(Fi, uj) derivative = expand_derivatives(derivative) Js.append(derivative) problem = MixedNonlinearVariationalProblem( eq_lhs_forms, u._functions, bcs, Js, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = MixedNonlinearVariationalSolver(problem) # system = solver.assemble_system() # return system else: print( "Error : You're trying to use assemble_mixed_system on a single domain problem." )
def _solve_varproblem(*args, **kwargs): "Solve variational problem a == L or F == 0" # Extract arguments eq, u, bcs, J, tol, M, preconditioner, form_compiler_parameters, solver_parameters\ = _extract_args(*args, **kwargs) # Solve linear variational problem if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form): if u._functions is not None: # Extract blocks from the variational formulation eq_lhs_forms = extract_blocks(eq.lhs) eq_rhs_forms = extract_blocks(eq.rhs) # Create problem problem = MixedLinearVariationalProblem( eq_lhs_forms, eq_rhs_forms, u._functions, bcs, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = MixedLinearVariationalSolver(problem) solver.parameters.update(solver_parameters) solver.solve() else: # Create problem problem = LinearVariationalProblem( eq.lhs, eq.rhs, u, bcs, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = LinearVariationalSolver(problem) solver.parameters.update(solver_parameters) solver.solve() # Solve nonlinear variational problem else: if u._functions is not None: # Extract blocks from the variational formulation eq_lhs_forms = extract_blocks(eq.lhs) if J is None: cpp.log.info( "No Jacobian form specified for nonlinear mixed variational problem." ) cpp.log.info( "Differentiating residual form F to obtain Jacobian J = F'." ) # Give the list of jacobian for each eq_lhs Js = [] for Fi in eq_lhs_forms: for uj in u._functions: derivative = formmanipulations.derivative(Fi, uj) derivative = expand_derivatives(derivative) Js.append(derivative) problem = MixedNonlinearVariationalProblem( eq_lhs_forms, u._functions, bcs, Js, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = MixedNonlinearVariationalSolver(problem) solver.parameters.update(solver_parameters) solver.solve() else: # Create Jacobian if missing if J is None: cpp.log.info( "No Jacobian form specified for nonlinear variational problem." ) cpp.log.info( "Differentiating residual form F to obtain Jacobian J = F'." ) F = eq.lhs J = formmanipulations.derivative(F, u) # Create problem problem = NonlinearVariationalProblem( eq.lhs, u, bcs, J, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = NonlinearVariationalSolver(problem) solver.parameters.update(solver_parameters) solver.solve()