def converge_test(self): """ - generate code for convergence test - convergence test implemented by calculating L2 norm of the simulation against analytical solution - L2 norm of each field is calculated and output with printf() - return generated code as string """ result = [] if not self.converge: return cgen.Module(result) m = self.margin.value ti = self.ntsteps.value % 2 # last updated grid loop = [Symbol('_'+x.name) for x in self.index] # symbols for loop for i in range(len(self.spacing)): result.append(cgen.Statement('printf("%d\\n")' % self.spacing[i].value)) for field in self.fields: body = [] l2 = ccode(field.label)+'_l2' idx = [ti] + loop result.append(cgen.Initializer(cgen.Value(self.real_t, l2), 0.0)) # populate xvalue, yvalue zvalue code for d in range(self.dimension-1, -1, -1): i = loop[d] i0 = m i1 = ccode(self.dim[d]-m) expr = self.spacing[d]*(loop[d] - self.margin.value) pre = [cgen.Initializer(cgen.Value(self.real_t, self.index[d].name), ccode(expr))] if d == self.dimension-1: # inner loop tn = self.dt.value*self.ntsteps.value \ if not field.staggered[0] \ else self.dt.value*self.ntsteps.value \ + self.dt.value/2.0 body = [cgen.Statement('%s += %s' % (l2, ccode((field[idx] - (field.sol.subs(self.t, tn)))**2.0)))] body = pre+body body = [cgen.For(cgen.InlineInitializer(cgen.Value('int', i), i0), cgen.Line('%s<%s' % (i, i1)), cgen.Line('++%s' % i), cgen.Block(body))] result += body volume = 1.0 for i in range(len(self.spacing)): volume *= self.spacing[i].value l2_value = 'pow(' + l2 + '*' + ccode(volume) + ', 0.5)' result.append(cgen.Statement('conv->%s = %s' % (l2, l2_value))) return cgen.Module(result)
def save_field_block(self, filename, field): statements = [] statements.append(cgen.Initializer(cgen.Value("int", "dims[]"), "{dim1, dim1, dim1}")) statements.append(cgen.Initializer(cgen.Value("float", "spacing[]"), "{dx1, dx2, dx3}")) statements.append(cgen.Assign("std::string vtkfile", "\""+filename+"\" + std::to_string(_ti)")) statements.append(cgen.Statement("opesci_dump_field_vts_3d(vtkfile, dims, spacing, 2, &"+field+"["+ccode(self.time[len(self.time)-1])+"][0][0][0])")) return cgen.Module([cgen.Pragma("omp single"), cgen.Block(statements)])
def initialise(self): loop = [Symbol('_'+x.name) for x in self.index] # symbols for loop statements = [] for field in self.fields: body = [] if self.omp: statements.append(cgen.Pragma('omp for schedule(static,1)')) # populate xvalue, yvalue zvalue code for d in range(self.dimension-1, -1, -1): i = loop[d] i0 = 0 i1 = ccode(self.dim[d]) pre = [] #velocity_initialisation = cgen.Assign(ccode()) post = [] if d == self.dimension-1: # inner loop # first time step t0 = 0 sol = field.sol.subs(self.t, t0) for idx in self.index: sol = sol.subs(idx, '_'+idx.name) body = [cgen.Assign(ccode(field[[0]+loop]), ccode(sol))] body = pre + body + post body = [cgen.For(cgen.InlineInitializer(cgen.Value('int', i), i0), cgen.Line('%s<%s' % (i, i1)), cgen.Line('++%s' % i), cgen.Block(body))] statements.append(body[0]) statements += self.generate_second_initialisation() return cgen.Module(statements)
def generate_loop(self, fields): """ The functions to generate stress loops and velocity loops are identical, save for a single parameter. Moved the common code to this function to reduce repetition of code. """ if self.eval_const: self.create_const_dict() m = self.margin.value body = [] for d in range(self.dimension-1, -1, -1): i = self.index[d] i0 = m i1 = ccode(self.dim[d]-m) if d == self.dimension-1: # inner loop if not self.fission: body = self.simple_kernel(fields, [d, i, i0, i1]) else: body = self.fission_kernel(fields, [d, i, i0, i1]) if not d == self.dimension-1: body = [cgen.For(cgen.InlineInitializer(cgen.Value('int', i), i0), cgen.Line('%s<%s' % (i, i1)), cgen.Line('++%s' % i), cgen.Block(body))] if not self.pluto and self.omp: body.insert(0, cgen.Pragma('omp for schedule(static,1)')) return cgen.Module(body)
def time_stepping(self): """ generate time index variable for time stepping e.g. for 2nd order time-accurate scheme, varibales are t0, t1 for 4th order time-accurate scheme, variables are t0, t1, t2, t3 the variables are used to address the field arrays e.g. in 2nd order scheme, U[t1] will be updated using U[t0] the variables are calculated by taking mod with time periodicity return generated code as string """ _ti = Symbol('_ti') body = [] for i in range(len(self.time)): lhs = self.time[i].name if i == 0: rhs = ccode(_ti % self.tp) else: rhs = ccode((self.time[i-1]+1) % self.tp) body.append(cgen.Assign(lhs, rhs)) body = cgen.Block(body) body = cgen.Module([cgen.Pragma('omp single'), body]) return body
def declare_fields(self): """ - generate code for declaring fields - the generated code first declare fields as std::vector of size=vec_size, then cast to multidimensional array - return the generated code as string """ result = [] arr = '' # = [dim1][dim2][dim3]... for d in self.dim: arr += '[' + d.name + ']' vsize = 1 for d in self.dim: vsize *= d.value vsize *= len(self.time) statements = [] for field in self.fields: vec = "_%s_vec" % ccode(field.label) vec_value = cgen.Pointer(cgen.Value(self.real_t, vec)) # alloc aligned memory (on windows and linux) statements.append(vec_value) ifdef = cgen.IfDef('_MSC_VER', [cgen.Assign(vec, '(%s*) _aligned_malloc(%s*sizeof(%s), %s)' % (self.real_t, str(vsize), self.real_t, str(self.alignment)))], [cgen.Statement('posix_memalign((void **)(&%s), %d, %d*sizeof(%s))' % (vec, self.alignment, vsize, self.real_t))]) statements.append(ifdef) # cast pointer to multidimensional array cast_pointer = cgen.Initializer(cgen.Value(self.real_t, "(*%s)%s" % (ccode(field.label), arr)), '(%s (*)%s) %s' % (self.real_t, arr, vec)) statements.append(cast_pointer) vec = "_%s_vec" % ccode("m") vec_value = cgen.Pointer(cgen.Value(self.real_t, vec)) statements.append(vec_value) result += statements return cgen.Module(result)
def store_fields(self): """Code fragment that stores field arrays to 'grid' struct""" result = [] for f in self.fields: assignment = cgen.Assign('grid->%s' % ccode(f.label), '(%s*) %s' % (self.real_t, ccode(f.label))) # There must be a better way of doing this. This hardly seems better than string manipulation result.append(assignment) return cgen.Module(result)
def define_fields(self): """Code fragment that defines field arrays""" result = [] for f in self.fields: var = cgen.Pointer(cgen.Value(self.real_t, ccode(f.label))) result.append(var) return cgen.Module(result)
def define_papi_events(self): """Code fragment that starts PAPI counters for specified events""" code = [] code.append(cgen.Initializer(cgen.Value('int', 'numevents'), self.numevents_papi)) code.append(cgen.ArrayOf(cgen.Value('int', 'events'), self.numevents_papi)) code.append(cgen.ArrayOf(cgen.Value('long long', 'counters'), self.numevents_papi)) code += [cgen.Statement('opesci_papi_name2event("%s", &(events[%d]))' % (e, i)) for i, e in enumerate(self._papi_events)] return cgen.Module(code)
def load_fields(self): """Code fragment that loads field arrays from 'grid' struct""" idxs = ''.join(['[%d]' % d.value for d in self.dim]) result = [] for f in self.fields: back_assign = cgen.Initializer(cgen.Value(self.real_t, "(*%s)%s" % (ccode(f.label), idxs)), '(%s (*)%s) grid->%s' % (self.real_t, idxs, ccode(f.label))) # Another hackish attempt. result.append(back_assign) return cgen.Module(result)
def free_memory(self): """ - generate code for free allocated memory - return the generated code as string """ statements = [] for field in self.fields: # alloc aligned memory (on windows and linux) ifdef = cgen.IfDef('_MSC_VER', [cgen.Statement('_aligned_free(grid->%s)' % (ccode(field.label)))], [cgen.Statement('free(grid->%s)' % (ccode(field.label)))]) statements.append(ifdef) return cgen.Module(statements)
def define_constants(self): """ - generate code for declaring variables - return the generated code as string """ result = [] variables = self.get_all_variables() for v in variables: if v.constant: line = cgen.Initializer(cgen.Const(cgen.Value(v.type, v.name)), v.value) else: line = cgen.Initializer(cgen.Value(v.type, v.name), v.value) result.append(line) return cgen.Module(result)
def print_convergence(self): """Code fragment that prints convergence norms""" statements = [cgen.Statement('printf("%s %s\\n", conv.%s_l2)' % (ccode(f.label), '\t%.10f', ccode(f.label))) for f in self.fields] return cgen.Module(statements)
def define_convergence(self): """Code fragment that defines convergence norms""" result = [] for f in self.fields: result.append(cgen.Value(self.real_t, '%s_l2' % ccode(f.label))) return cgen.Module(result)
def define_profiling(self): """Code fragment that defines global PAPI counters and events""" code = [cgen.Value('float', 'g_%s' % v) for v in ['rtime', 'ptime', 'mflops']] code += [cgen.Value('long long', 'g_%s' % e) for e in self._papi_events] return cgen.Module(code)
def sum_papi_events(self): """Code fragment that reads PAPI counters for specified events""" code = [cgen.Statement('profiling->g_%s += counters[%d]' % (e, i)) for i, e in enumerate(self._papi_events)] return cgen.Module(code)
def init_profiling(self): """Code fragment that initialises global PAPI counters and events""" code = [cgen.Assign('profiling->g_%s' % v, 0.0) for v in ['rtime', 'ptime', 'mflops']] code += [cgen.Assign('profiling->g_%s' % e, 0) for e in self._papi_events] return cgen.Module(code)