def process_scalar_bin(holder, label): assign = [] dassign = [] deplabels = np.zeros((num_equations, num_equations), 'O') deplabels[:] = 'none' for (i, x) in enumerate(holder): if x: xstr = "c[('%s', %d)][:] = %s" % (label, i, x) assign.append(xstr) for j, psi in enumerate(unk_scalar_fields): dx = differentiate(x, psi) if dx: deplabels[i][j] = classify_dep(dx) if deplabels[i][j] == 'nonlinear' \ or deplabels[i][j] == 'linear': dxstr = "c[('d%s', %d, %d)][:] = %s" % (label, i, j, dx) dassign.append(dxstr) else: pass return assign, dassign, deplabels
def generate_proteus_problem_file(bvp, clsnm): from ibvp.language import scalarize scalarized_system = scalarize(bvp) #import ibvp.sym as sym #print(sym.pretty(scalarized_system.pde_system)) distr_system = DistributeMapper()(scalarized_system.pde_system) scalar_unknowns = [v.name for v in scalarized_system.unknowns] num_equations = len(scalar_unknowns) ambient_dim = bvp.ambient_dim if len(set(scalar_unknowns)) != len(scalar_unknowns): raise ValueError("names of unknowns not unique " "after scalarization") # import ibvp.sym as sym # print sym.pretty(distr_system) tc_storage = TransportCoefficientStorage(scalarized_system, bvp.ambient_dim, scalar_unknowns) has_time_derivative = HasTimeDerivativeMapper() has_spatial_derivative = HasSpatialDerivativeMapper() for i, eqn_i in enumerate(distr_system): if isinstance(eqn_i, pp.Sum): terms = eqn_i.children else: terms = (eqn_i,) for term in terms: constant, term_without_constant = pick_off_constants(term) if isinstance(term_without_constant, p.OperatorBinding): op = term_without_constant.op if isinstance(op, p.TimeDerivativeOperator): if has_spatial_derivative(term_without_constant.argument): raise ValueError("no spatial derivatives allowed inside " "of time derivative") tc_storage.mass[i] += ( constant * term_without_constant.argument) continue if has_time_derivative(term_without_constant): raise ValueError("time derivatives found below " "root of tree of term '%s'" % pretty(term)) if isinstance(op, p.DerivativeOperator): outer_deriv_axis = term_without_constant.op.ambient_axis outer_deriv_argument = term_without_constant.argument if not has_spatial_derivative(outer_deriv_argument): tc_storage.advection[i, outer_deriv_axis] += ( constant * outer_deriv_argument) else: # diffusion coeff, inner_derivative = \ find_inner_deriv_and_coeff(outer_deriv_argument) pot_const, pot_expr = pick_off_constants( inner_derivative.argument) pot_index = tc_storage.register_potential(pot_expr) tc_storage.diffusion[ i, pot_index, outer_deriv_axis, inner_derivative.op.ambient_axis] \ += pot_const*coeff else: raise ValueError("unexpected operator: %s" % type(term_without_constant.op).__name__) else: if has_time_derivative(term_without_constant): raise ValueError("time derivatives found below " "root of tree of term '%s'" % pretty(term)) if has_spatial_derivative(term_without_constant): tc_storage.hamiltonian[i] += term else: tc_storage.reaction[i] += term # Python code we generate, we create references to the coefficient arrays # in the dictionary that will conveniently have the same name as our # pymbolic variables. This makes printing easy and has no major # performance penalty. defs_list = [" %s = c[('u', %d)]" % (str(v), i) for (i, v) in enumerate(scalar_unknowns)] defs = '\n'.join(defs_list) # noqa unk_scalar_fields = [p.Field(psi) for psi in scalar_unknowns] def process_scalar_bin(holder, label): assign = [] dassign = [] deplabels = np.zeros((num_equations, num_equations), 'O') deplabels[:] = 'none' for (i, x) in enumerate(holder): if x: xstr = "c[('%s', %d)][:] = %s" % (label, i, x) assign.append(xstr) for j, psi in enumerate(unk_scalar_fields): dx = differentiate(x, psi) if dx: deplabels[i][j] = classify_dep(dx) if deplabels[i][j] == 'nonlinear' \ or deplabels[i][j] == 'linear': dxstr = "c[('d%s', %d, %d)][:] = %s" % (label, i, j, dx) dassign.append(dxstr) else: pass return assign, dassign, deplabels mass_assigns, dmass_assigns, mass_deps \ = process_scalar_bin(tc_storage.mass, "m") for md in mass_deps.ravel(): if md == 'constant': raise Exception("Constant mass illegal") reaction_assigns, dreaction_assigns, reaction_deps \ = process_scalar_bin(tc_storage.reaction, "r") hamiltonian_assigns, dhamiltonian_assigns, hamiltonian_deps \ = process_scalar_bin(tc_storage.hamiltonian, "h") advect_assigns = [] dadvect_assigns = [] advect_deps_p = np.zeros((num_equations, num_equations, ambient_dim), 'O') advect_deps_p[:] = 'none' for i, bi in enumerate(tc_storage.advection): for j, bij in enumerate(bi): if bij: bstr = "c[('f', %d)][..., %d] = %s" % (i, j, bij) advect_assigns.append(bstr) for k, psi in enumerate(unk_scalar_fields): dbij = differentiate(bij, psi) if dbij: advect_deps_p[i, k, j] = classify_dep(dbij) dbstr = "c[('df', %d, %d)][...,%d] = %s" % (i, k, j, dbij) dadvect_assigns.append(dbstr) # now "reduce" over the vector component dependences and take the worst. dep2int = {'none': 0, 'constant': 1, 'linear': 2, 'nonlinear': 3} from pytools import reverse_dictionary int2dep = reverse_dictionary(dep2int) advect_deps = np.zeros((num_equations, num_equations), "O") for i in range(num_equations): for j in range(num_equations): advect_deps[i, j] = int2dep[ reduce(max, (dep2int[x] for x in advect_deps_p[i, j, :]), 0) ] diff_assigns = [] ddiff_assigns = [] diff_deps_p = np.zeros((num_equations, num_equations, num_equations, ambient_dim, ambient_dim), 'O') diff_deps_p[:] = 'none' for i, ai in enumerate(tc_storage.diffusion): for j, aij in enumerate(ai): for k, aijk in enumerate(aij): for ell, aijkell in enumerate(aijk): if aijkell: astr = "c[('a', %d, %d)][..., %d, %d] = %s" \ % (i, j, k, ell, aijkell) diff_assigns.append(astr) for q, psi in enumerate(unk_scalar_fields): da = differentiate(aijkell, psi) if da: diff_deps_p[i, j, q, k, ell] = classify_dep(da) dastr = "c[('da',%d,%d,%d)][...,%d,%d] = %s" \ % (i, j, q, k, ell, da) ddiff_assigns.append(dastr) else: diff_deps_p[i, j, q, k, ell] = 'constant' diff_deps = np.zeros((num_equations, num_equations, num_equations), 'O') ddp = diff_deps_p.reshape((num_equations, num_equations, num_equations, ambient_dim**2)) for i in range(num_equations): for j in range(num_equations): for k in range(num_equations): diff_deps[i, j, k] = int2dep[ reduce(max, (dep2int[x] for x in ddp[i, j, k]), 0)] # potential is a bit different from other scalars. potential_assigns = [] dpotential_assigns = [] phi_deps = np.zeros((num_equations, num_equations), 'O') for i, phi in enumerate(tc_storage.potential): for j, u in enumerate(unk_scalar_fields): if phi == u: phi_deps[i, j] = 'u' else: phi_str = "c[('phi', %d)] = %s" % (i, phi) potential_assigns.extend(phi_str) D = differentiate(phi, u) if D: phi_deps[i, j] = 'nonlinear' dphi_str = "c[('dphi', %d, %d)] = %s" % (i, j, D) dpotential_assigns.extend(dphi_str) def spacer(x): return " " + x assigns = "\n".join( map(spacer, reduce(lambda x, y: x+y, [mass_assigns, dmass_assigns, advect_assigns, dadvect_assigns, diff_assigns, ddiff_assigns, reaction_assigns, dreaction_assigns, hamiltonian_assigns, dhamiltonian_assigns]))) # we dict-ify the dependencies so we can repr them. def dictify(arr): if len(arr.shape) == 1: return dict((i, a) for (i, a) in enumerate(arr) if a and a != 'none') else: result = {} for i, a in enumerate(arr): da = dictify(a) if len(da) > 0: result[i] = da return result names = ["mass", "advection", "diffusion", "potential", "reaction", "hamiltonian"] deps = [mass_deps, advect_deps, diff_deps, phi_deps, reaction_deps, hamiltonian_deps] dep_stmnts = [] for (nm, d) in zip(names, deps): ddict = dictify(d) dep_stmnts.append(" %s = %s" % (nm, repr(ddict))) dep_st = "\n".join(dep_stmnts) # This is for creating, e.g. u = c[('u',0)] before we make assignments # in evaluate so that we have references into the c dictionary for our # data. This makes the pretty-printed code more readable. ref_list = [] for i, phi in enumerate(scalar_unknowns): ref_list.append("%s = c[('u',%d)]" % (phi, i)) refs = "\n".join((spacer(x) for x in ref_list)) tc_class_str = """ from proteus.TransportCoefficients import TC_base class %s(TC_base): def __init__(self): %s variableNames=%s TC_base.__init__(self, nc=%d, mass=mass, advection=advection, diffusion=diffusion, potential=potential, reaction=reaction, hamiltonian=hamiltonian, variableNames=variableNames) def evaluate(self, t, c): %s %s """ % (clsnm, dep_st, repr(scalar_unknowns), num_equations, refs, assigns) return(tc_class_str)