def partial_to_rim(smbl): if smbl.head != Partial.head: return smbl.map_tails(partial_to_rim) body, direction = smbl.tail neighbour = lambda pos: body.map_variables(lambda name: neighbour_name(name, pos)) assert nFD == 2, "only nFD=2 implemented so far" return div(sum(neighbour(-1), neg(neighbour(+1)), dx[direction]))
def partial_to_support(partial_smbl): if partial_smbl.head != Partial.head: return partial_smbl.map_tails(partial_to_support) partial_body, direction = partial_smbl.tail def target(idx, offset, direction): t = list(idx) t[direction] += offset print(f" Computing from {idx} + {offset} in direction {direction} = {t}") return tuple(t) ### TODO: This is dumb as f**k. We need wildcard matching. ### Something such as map( Support(Symbol(str),builtin.int), mapper) ### or in this case as simple as map_deep(Support, mapper). def neighbour(offset): print(f"Working at {partial_body}...") def map_support(support_smbl): if support_smbl.head != Support.head: print(f" Ascending into {support_smbl}...") r = support_smbl.map_tails(map_support) print(f" ... resolved {support_smbl} -> {r}") return r var, *idx = support_smbl.tail r = Support(var, *target(idx, offset, direction)) print(f" ...mapped {support_smbl} -> {r}") return r r = partial_body.map_tails(map_support) print(f"... {partial_body}: neighbour({offset}) = {r}") return r assert nFD == 2, "only nFD=2 implemented so far" return div(sum(neighbour(-1), neg(neighbour(+1)), dx[direction]))
def add_sinusodial(s, name): # some sinosidual test signal y, my, mdy = dda.symbols(f"{name}_y, {name}_my, {name}_mdy") s[my] = dda.neg(y) s[y] = dda.int(mdy, dt, 0) s[mdy] = dda.int(my, dt, 1) return s
# Symbolic spatial partial derivative, expected usage: # partial(expression, i) with i the spatial dimension Partial = Symbol("Partial") state = State() dt = 0.1 # meaningless timestep size Q0 = [ 0., *(0 for j in rDim), 0 ] # initial conditions # analytical recovery of primitives and other auxilliaries state[rho] = state[D] # actually never really needed for i in rDim: state[v[i]] = div(state[S[i]], state[rho]) for j in rDim: state[vv[i][j]] = mult(v[i], v[j]) state[v2] = neg(sum(*(vv[j][j] for j in rDim))) state[eps] = neg(sum(E, neg(div(D, mult(2, v2))))) # Equation of state Gamma = 2 # polytropic constant in ideal fluid equation of state state[p] = mult(rho, mult(eps, Gamma-1)) # actual conserved flux system # D = -int_t partial_i D^i = - partial_i (rho * v^i) state[ D ] = int(*( Partial(S[i], i) for i in rDim), dt, Q0[iD]) for i in rDim: integrands = [ Partial(mult(S[i],v[j]),i) for j in rDim ] if i == j: integrands.append( Partial(neg(p),i) ) state[ S[i] ] = int(*integrands, dt, Q0[iS[i]])
from dda import symbols, State from dda.computing_elements import int, mult, neg from numpy import * # numbers of iterations and time step size iterations = 100_000_000 dt = 0.0005 x, y, z = symbols("x,y,z") a = [NaN, 5, 2, 0.5, 1, 4, 1] x0, y0, z0 = -5, 0, 5 state = State() state[x] = int(mult(a[1], x), neg(mult(mult(a[2], y), z)), dt, x0) state[y] = int(neg(mult(a[3], y)), mult(mult(a[4], x), z), dt, y0) state[z] = int(mult(a[5], z), neg(mult(mult(a[6], x), y)), dt, z0) if 0: # solve with naive explicit RK results = state.export(to="CppSolver").run(max_iterations=iterations, modulo_write=300, binary=True).as_recarray() else: # solve with scipy instead py_state = state.export("scipy") sol = py_state.solve(1000, dense_output=True) results = { field: sol.y[i] for i, field in enumerate(py_state.vars.evolved)