def test_basegen_after_index(): '''Check that we can insert an object using "after_index"''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var1"])) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var2"])) sub.add(CommentGen(sub, " hello"), position=["after_index", 1]) # The code checked by line_number() *includes* the SUBROUTINE # statement (which is obviously not a child of the SubroutineGen # object) and therefore the index it returns is 1 greater than we # might expect. assert line_number(sub.root, "hello") == 3
def test_basegen_before_error(): '''Check that we raise an error when attempting to insert an object before another object that is not present in the tree''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var1"])) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var2"])) # Create an object but do not add it as a child of sub dgen = DeclGen(sub, datatype="real", entity_decls=["rvar1"]) # Try to add an object before the orphan dgen with pytest.raises(RuntimeError) as err: sub.add(CommentGen(sub, " hello"), position=["before", dgen]) assert "Failed to find supplied object" in str(err)
def gen_code(self, parent): if self.field_space == "every": from psyclone.f2pygen import DeclGen dim_var = DeclGen(parent, datatype="INTEGER", entity_decls=[self._variable_name]) parent.add(dim_var) # loop bounds self._start = "1" if self._loop_type == "inner": index = "1" elif self._loop_type == "outer": index = "2" self._stop = ("SIZE(" + self.field_name + ", " + index + ")") else: # one of our spaces so use values provided by the infrastructure # loop bounds if self._loop_type == "inner": self._start = self.field_space + "%istart" self._stop = self.field_space + "%istop" elif self._loop_type == "outer": self._start = self.field_space + "%jstart" self._stop = self.field_space + "%jstop" Loop.gen_code(self, parent)
def gen_code(self, parent): ''' Generates GOcean specific invocation code (the subroutine called by the associated invoke call in the algorithm layer). This consists of the PSy invocation subroutine and the declaration of its arguments.''' from psyclone.f2pygen import SubroutineGen, DeclGen, TypeDeclGen # create the subroutine invoke_sub = SubroutineGen(parent, name=self.name, args=self.psy_unique_var_names) parent.add(invoke_sub) self.schedule.gen_code(invoke_sub) # add the subroutine argument declarations for arrays if len(self.unique_args_arrays) > 0: my_decl_arrays = DeclGen(invoke_sub, datatype="REAL", intent="inout", kind="go_wp", entity_decls=self.unique_args_arrays, dimension=":,:") invoke_sub.add(my_decl_arrays) # add the subroutine argument declarations for scalars if len(self.unique_args_scalars) > 0: my_decl_scalars = \ TypeDeclGen(invoke_sub, datatype="scalar_field_type", entity_decls=self.unique_args_scalars, intent="inout") invoke_sub.add(my_decl_scalars)
def test_decl_no_replication_scalars(): '''Check that the same scalar variable will only get declared once in a module and a subroutine''' variable_name = "arg_name" datatype = "integer" module = ModuleGen(name="testmodule") module.add(DeclGen(module, datatype=datatype, entity_decls=[variable_name])) module.add(DeclGen(module, datatype=datatype, entity_decls=[variable_name])) subroutine = SubroutineGen(module, name="testsubroutine") module.add(subroutine) subroutine.add( DeclGen(subroutine, datatype=datatype, entity_decls=[variable_name])) subroutine.add( DeclGen(subroutine, datatype=datatype, entity_decls=[variable_name])) generated_code = str(module.root) assert generated_code.count(variable_name) == 2
def test_declgen_wrong_type(): ''' Check that we raise an appropriate error if we attempt to create a DeclGen for an unsupported type ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) with pytest.raises(RuntimeError) as err: _ = DeclGen(sub, datatype="complex", entity_decls=["rvar1"]) assert "Only integer and real are currently supported" in str(err)
def test_basegen_first(): '''Check that we can insert an object as the first child''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var1"])) sub.add(CommentGen(sub, " hello"), position=["first"]) cindex = line_number(sub.root, "hello") assert cindex == 1
def test_basegen_append(): '''Check that we can append an object to the tree''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var1"])) sub.add(CommentGen(sub, " hello"), position=["append"]) cindex = line_number(sub.root, "hello") assert cindex == 3
def gen_code(self, parent): ''' Generate the Fortran Loop and any associated code. :param parent: the node in the f2pygen AST to which to add content. :type parent: :py:class:`psyclone.f2pygen.SubroutineGen` ''' # Avoid circular dependency # pylint: disable=import-outside-toplevel from psyclone.psyGen import zero_reduction_variables, InvokeSchedule def is_unit_literal(expr): ''' Check if the given expression is equal to the literal '1'. :param expr: a PSyIR expression. :type expr: :py:class:`psyclone.psyir.nodes.Node` :returns: True if it is equal to the literal '1', false otherwise. ''' return isinstance(expr, Literal) and expr.value == '1' if not self.is_openmp_parallel(): calls = self.reductions() zero_reduction_variables(calls, parent) invoke = self.ancestor(InvokeSchedule) if invoke.opencl or (is_unit_literal(self.start_expr) and is_unit_literal(self.stop_expr)): # no need for a loop for child in self.loop_body: child.gen_code(parent) else: # Avoid circular dependency # pylint: disable=import-outside-toplevel from psyclone.psyir.backend.fortran import FortranWriter # start/stop/step_expr are generated with the FortranWriter # backend, the rest of the loop with f2pygen. fwriter = FortranWriter() if is_unit_literal(self.step_expr): step_str = None else: step_str = fwriter(self.step_expr) do_stmt = DoGen(parent, self.variable.name, fwriter(self.start_expr), fwriter(self.stop_expr), step_str) # need to add do loop before children as children may want to add # info outside of do loop parent.add(do_stmt) for child in self.loop_body: child.gen_code(do_stmt) my_decl = DeclGen(parent, datatype="integer", entity_decls=[self.variable.name]) parent.add(my_decl)
def test_imp_none_in_module_with_decs(): ''' test that implicit none is added before any declaration statements in a module when auto (the default) is used for insertion ''' module = ModuleGen(name="testmodule", implicitnone=False) module.add(DeclGen(module, datatype="integer", entity_decls=["var1"])) module.add(TypeDeclGen(module, datatype="my_type", entity_decls=["type1"])) module.add(ImplicitNoneGen(module)) in_idx = line_number(module.root, "IMPLICIT NONE") assert in_idx == 1
def test_declgen_missing_names(): ''' Check that we raise an error if we attempt to create a DeclGen without naming the variable(s) ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) with pytest.raises(RuntimeError) as err: _ = DeclGen(sub, datatype="integer") assert ("Cannot create a variable declaration without specifying " "the name" in str(err))
def test_declgen_multiple_use2(): '''Check that we don't correctly handle the case where data of a different type has already been delared. ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) # first declaration datanames = ["data1"] sub.add(DeclGen(sub, datatype="real", entity_decls=datanames)) gen = str(sub.root) # second declaration datanames = ["data1", "data2"] sub.add(DeclGen(sub, datatype="integer", entity_decls=datanames)) gen = str(sub.root) print gen expected = (" INTEGER data1, data2\n" " REAL data1") assert expected in gen # check input data is not modified assert datanames == ["data1", "data2"]
def test_basegen_append_default(): ''' Check if no position argument is supplied to BaseGen.add() then it defaults to appending ''' from psyclone.f2pygen import BaseGen module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) BaseGen.add(sub, DeclGen(sub, datatype="integer", entity_decls=["var1"])) BaseGen.add(sub, CommentGen(sub, " hello")) cindex = line_number(sub.root, "hello") assert cindex == 3
def test_imp_none_in_subroutine_with_use_and_decs(): ''' test that implicit none is added after any use statements and before any declarations in a subroutine when auto (the default) is used for insertion''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var1"])) sub.add(TypeDeclGen(sub, datatype="my_type", entity_decls=["type1"])) sub.add(UseGen(sub, "fred")) sub.add(ImplicitNoneGen(sub)) in_idx = line_number(sub.root, "IMPLICIT NONE") assert in_idx == 2
def test_imp_none_in_module_with_use_and_decs_and_comments(): ''' test that implicit none is added after any use statements and before any declarations in a module in the presence of comments when auto (the default) is used for insertion''' module = ModuleGen(name="testmodule", implicitnone=False) module.add(DeclGen(module, datatype="integer", entity_decls=["var1"])) module.add(TypeDeclGen(module, datatype="my_type", entity_decls=["type1"])) module.add(UseGen(module, "fred")) for idx in [0, 1, 2, 3]: module.add(CommentGen(module, " hello " + str(idx)), position=["before_index", 2 * idx]) module.add(ImplicitNoneGen(module)) in_idx = line_number(module.root, "IMPLICIT NONE") assert in_idx == 3
def test_subroutine_var_with_implicit_none(): ''' test that a variable is added after an implicit none statement in a subroutine''' module = ModuleGen(name="testmodule") subroutine = SubroutineGen(module, name="testsubroutine", implicitnone=True) module.add(subroutine) subroutine.add( DeclGen(subroutine, datatype="integer", entity_decls=["var1"])) idx_var = line_number(subroutine.root, "INTEGER var1") idx_imp_none = line_number(subroutine.root, "IMPLICIT NONE") print str(module.root) assert idx_var - idx_imp_none == 1, \ "variable declation must be after implicit none"
def test_subroutine_var_intent_in_with_directive(): ''' test that a variable declared as intent in is added before a directive in a subroutine''' module = ModuleGen(name="testmodule") subroutine = SubroutineGen(module, name="testsubroutine", implicitnone=False) module.add(subroutine) subroutine.add(DirectiveGen(subroutine, "omp", "begin", "parallel", "")) subroutine.add( DeclGen(subroutine, datatype="integer", intent="in", entity_decls=["var1"])) idx_par = line_number(subroutine.root, "!$omp parallel") idx_var = line_number(subroutine.root, "INTEGER, intent(in) :: var1") print str(module.root) assert idx_par - idx_var == 1, \ "variable declaration must be before directive"
def gen_code(self, parent): if self.field_space == "every": from psyclone.f2pygen import DeclGen from psyclone.psyir.nodes import BinaryOperation dim_var = DeclGen(parent, datatype="INTEGER", entity_decls=[self.variable.name]) parent.add(dim_var) # Update start loop bound self.start_expr = Literal("1", INTEGER_TYPE, parent=self) # Update stop loop bound if self._loop_type == "inner": index = "1" elif self._loop_type == "outer": index = "2" self.stop_expr = BinaryOperation(BinaryOperation.Operator.SIZE, parent=self) self.stop_expr.addchild( Reference(DataSymbol(self.field_name, INTEGER_TYPE), parent=self.stop_expr)) self.stop_expr.addchild( Literal(index, INTEGER_TYPE, parent=self.stop_expr)) else: # one of our spaces so use values provided by the infrastructure # loop bounds # TODO: Issue 440. Implement derive types in PSyIR if self._loop_type == "inner": self.start_expr = Reference(self.field_space + "%istart", parent=self) self.stop_expr = Reference(self.field_space + "%istop", parent=self) elif self._loop_type == "outer": self.start_expr = Reference(self.field_space + "%jstart", parent=self) self.stop_expr = Reference(self.field_space + "%jstop", parent=self) Loop.gen_code(self, parent)
def gen_code(self, parent): if self.field_space == "every": from psyclone.f2pygen import DeclGen dim_var = DeclGen(parent, datatype="INTEGER", entity_decls=[self._variable_name]) parent.add(dim_var) # Update start loop bound self.start_expr = Literal("1", parent=self) # Update stop loop bound if self._loop_type == "inner": index = "1" elif self._loop_type == "outer": index = "2" # TODO: Issue 440. Implement SIZE intrinsic in PSyIR self.stop_expr = Literal("SIZE(" + self.field_name + "," + index + ")", parent=self) else: # one of our spaces so use values provided by the infrastructure # loop bounds # TODO: Issue 440. Implement derive types in PSyIR if self._loop_type == "inner": self.start_expr = Reference( self.field_space + "%istart", parent=self) self.stop_expr = Reference( self.field_space + "%istop", parent=self) elif self._loop_type == "outer": self.start_expr = Reference( self.field_space + "%jstart", parent=self) self.stop_expr = Reference( self.field_space + "%jstop", parent=self) Loop.gen_code(self, parent)
def gen_code(self, parent): ''' Generates GOcean specific invocation code (the subroutine called by the associated invoke call in the algorithm layer). This consists of the PSy invocation subroutine and the declaration of its arguments.''' from psyclone.f2pygen import SubroutineGen, DeclGen, TypeDeclGen, \ CommentGen, AssignGen # create the subroutine invoke_sub = SubroutineGen(parent, name=self.name, args=self.psy_unique_var_names) parent.add(invoke_sub) # add declarations for the variables holding the upper bounds # of loops in i and j if self.schedule.const_loop_bounds: invoke_sub.add( DeclGen(invoke_sub, datatype="INTEGER", entity_decls=[ self.schedule.iloop_stop, self.schedule.jloop_stop ])) # Generate the code body of this subroutine self.schedule.gen_code(invoke_sub) # add the subroutine argument declarations for fields if len(self.unique_args_arrays) > 0: my_decl_arrays = TypeDeclGen(invoke_sub, datatype="r2d_field", intent="inout", entity_decls=self.unique_args_arrays) invoke_sub.add(my_decl_arrays) # add the subroutine argument declarations for real scalars if len(self.unique_args_rscalars) > 0: my_decl_rscalars = DeclGen(invoke_sub, datatype="REAL", intent="inout", kind="wp", entity_decls=self.unique_args_rscalars) invoke_sub.add(my_decl_rscalars) # add the subroutine argument declarations for integer scalars if len(self.unique_args_iscalars) > 0: my_decl_iscalars = DeclGen(invoke_sub, datatype="INTEGER", intent="inout", entity_decls=self.unique_args_iscalars) invoke_sub.add(my_decl_iscalars) if self._schedule.const_loop_bounds and \ len(self.unique_args_arrays) > 0: # Look-up the loop bounds using the first field object in the # list sim_domain = self.unique_args_arrays[0] +\ "%grid%simulation_domain%" position = invoke_sub.last_declaration() invoke_sub.add(CommentGen(invoke_sub, ""), position=["after", position]) invoke_sub.add(AssignGen(invoke_sub, lhs=self.schedule.jloop_stop, rhs=sim_domain + "ystop"), position=["after", position]) invoke_sub.add(AssignGen(invoke_sub, lhs=self.schedule.iloop_stop, rhs=sim_domain + "xstop"), position=["after", position]) invoke_sub.add(CommentGen(invoke_sub, " Look-up loop bounds"), position=["after", position]) invoke_sub.add(CommentGen(invoke_sub, ""), position=["after", position])
def create_driver(self, input_list, output_list): # pylint: disable=too-many-locals, too-many-statements '''This function creates a driver that can read the output created by the extraction code. This is a stand-alone program that will read the input data, calls the kernels/ instrumented region, and then compares the results with the stored results in the file. TODO: #644: we need type information here. :param input_list: list of variables that are input parameters. :type input_list: list of str :param output_list: list of variables that are output parameters. :type output_list: list or str ''' from psyclone.f2pygen import AllocateGen, AssignGen, CallGen,\ CommentGen, DeclGen, ModuleGen, SubroutineGen, UseGen, \ TypeDeclGen from psyclone.gocean1p0 import GOSymbolTable from psyclone.psyir.symbols import Symbol all_vars = list(set(input_list).union(set(output_list))) all_vars.sort() module_name, region_name = self.region_identifier module = ModuleGen(name=module_name) prog = SubroutineGen(parent=module, name=module_name+"_code", implicitnone=True) module.add(prog) use = UseGen(prog, self.add_psydata_class_prefix("psy_data_mod"), only=True, funcnames=[self.add_psydata_class_prefix("PSyDataType")]) prog.add(use) # Use a symbol table to make sure all variable names are unique sym_table = GOSymbolTable() sym = Symbol("PSyDataType") sym_table.add(sym) psy_data = sym_table.new_symbol_name(self.add_psydata_class_prefix ("psy_data")) sym_table.add(Symbol(psy_data)) var_decl = TypeDeclGen(prog, datatype=self.add_psydata_class_prefix ("PSyDataType"), entity_decls=[psy_data]) prog.add(var_decl) call = CallGen(prog, "{0}%OpenRead(\"{1}\", \"{2}\")" .format(psy_data, module_name, region_name)) prog.add(call) post_suffix = self._post_name # Variables might need to be renamed in order to guarantee unique # variable names in the driver: An example of this would be if the # user code contains a variable 'dx', and the kernel takes a # property 'dx' as well. In the original code that is no problem, # since the property is used via field%grid%dx. But the stand-alone # driver renames field%grid%dx to dx, which can cause a name clash. # Similar problems can exist with any user defined type, since all # user defined types are rewritten to just use the field name. # We use a mapping to support renaming of variables: it takes as # key the variable as used in the original program (e.g. 'dx' from # an expression like field%grid%dx), and maps it to a unique local # name (e.g. dx_0). rename_variable = {} for var_name in all_vars: # TODO #644: we need to identify arrays!! # Support GOcean properties, which are accessed via a # derived type (e.g. 'fld%grid%dx'). In this stand-alone # driver we don't have the derived type, instead we create # variable based on the field in the derived type ('dx' # in the example above), and pass this variable to the # instrumented code. last_percent = var_name.rfind("%") if last_percent > -1: # Strip off the derived type, and only leave the last # field, which is used as the local variable name. local_name = var_name[last_percent+1:] else: # No derived type, so we can just use the # variable name directly in the driver local_name = var_name unique_local_name = sym_table.new_symbol_name(local_name) rename_variable[local_name] = unique_local_name sym_table.add(Symbol(unique_local_name)) local_name = unique_local_name # TODO: #644 - we need to identify arrays!! # Any variable used needs to be defined. We also need # to handle the kind property better and not rely on # a hard-coded value. decl = DeclGen(prog, "real", [local_name], kind="8", dimension=":,:", allocatable=True) prog.add(decl) is_input = var_name in input_list is_output = var_name in output_list if is_input and not is_output: # We only need the pre-variable, and we can read # it from the file (this call also allocates space for it). call = CallGen(prog, "{0}%ReadVariable(\"{1}\", {2})" .format(psy_data, var_name, local_name)) prog.add(call) elif is_input: # Now must be input and output: # First read the pre-variable (which also allocates it): call = CallGen(prog, "{0}%ReadVariable(\"{1}\", {2})" .format(psy_data, var_name, local_name)) prog.add(call) # Then declare the post variable, and and read its values # (ReadVariable will also allocate it) sym = Symbol(local_name+post_suffix) sym_table.add(sym) decl = DeclGen(prog, "real", [local_name+post_suffix], dimension=":,:", kind="8", allocatable=True) prog.add(decl) call = CallGen(prog, "{0}%ReadVariable(\"{1}{3}\", {2}{3})" .format(psy_data, var_name, local_name, post_suffix)) prog.add(call) else: # Now the variable is output only. We need to read the # post variable in, and create and allocate a pre variable # with the same size as the post sym = Symbol(local_name+post_suffix) sym_table.add(sym) decl = DeclGen(prog, "real", [local_name+post_suffix], dimension=":,:", kind="8", allocatable=True) prog.add(decl) call = CallGen(prog, "{0}%ReadVariable(\"{1}{3}\", {2}{3})" .format(psy_data, var_name, local_name, post_suffix)) prog.add(call) decl = DeclGen(prog, "real", [local_name], kind="8", dimension=":,:", allocatable=True) prog.add(decl) alloc = AllocateGen(prog, [var_name], mold="{0}".format(local_name + post_suffix)) prog.add(alloc) # Initialise the variable with 0, since it might contain # values that are not set at all (halo regions, or a # kernel might not set all values). This way the array # comparison with the post value works as expected # TODO #644 - create the right "0.0" type here (e.g. # 0.0d0, ...) assign = AssignGen(prog, local_name, "0.0d0") prog.add(assign) # Now add the region that was extracted here: prog.add(CommentGen(prog, "")) prog.add(CommentGen(prog, " RegionStart")) # For the driver we have to re-create the code of the # instrumented region, but in this stand-alone driver the # arguments are not dl_esm_inf fields anymore, but simple arrays. # Similarly, for properties we cannot use e.g. 'fld%grid%dx' # anymore, we have to use e.g. a local variable 'dx' that has # been created. Since we are using the existing way of creating # the code for the instrumented region, we need to modify how # these variables are created. We do this by temporarily # modifying the properties in the config file. api_config = Config.get().api_conf("gocean1.0") all_props = api_config.grid_properties # Keep a copy of the original values, so we can restore # them later orig_props = dict(all_props) # 1) A grid property is defined like "{0}%grid%dx". This is # changed to be just 'dx', i.e. the final component of # the current value (but we also take renaming into account, # so 'dx' might become 'dx_0'). # If a property is not used, it doesn't matter if we modify # its definition, so we just change all properties. for name, prop in all_props.items(): last_percent = prop.fortran.rfind("%") if last_percent > -1: # Get the last field name, which will be the # local variable name local_name = prop.fortran[last_percent+1:] unique_name = rename_variable.get(local_name, local_name) all_props[name] = GOceanConfig.make_property( unique_name, prop.type, prop.intrinsic_type) # 2) The property 'grid_data' is a reference to the data on the # grid (i.e. the actual field) , and it is defined as "{0}%data". # This just becomes {0} ('a_fld%data' in the original program # becomes just 'a_fld', and 'a_fld' is declared to be a plain # Fortran 2d-array) all_props["go_grid_data"] = GOceanConfig.make_property( "{0}", "array", "real") # Each kernel caches the argument code, so we also # need to clear this cached data to make sure the new # value for "go_grid_data" is actually used. from psyclone.psyGen import CodedKern for kernel in self.psy_data_body.walk(CodedKern): kernel.clear_cached_data() # Recreate the instrumented region. Due to the changes in the # config files, fields and properties will now become local # plain arrays and variables: for child in self.psy_data_body: child.gen_code(prog) # Now reset all properties back to the original values: for name in all_props.keys(): all_props[name] = orig_props[name] prog.add(CommentGen(prog, " RegionEnd")) prog.add(CommentGen(prog, "")) for var_name in output_list: prog.add(CommentGen(prog, " Check {0}".format(var_name))) code = str(module.root) with open("driver-{0}-{1}.f90". format(module_name, region_name), "w") as out: out.write(code)
def gen_code(self, parent): ''' Generates dynamo version 0.1 specific psy code for a call to the dynamo kernel instance. ''' from psyclone.f2pygen import CallGen, DeclGen, AssignGen, UseGen # TODO: we simply choose the first field as the lookup for the moment field_name = self.arguments.args[0].name # add a dofmap lookup using first field. # TODO: This needs to be generalised to work for multiple dofmaps parent.add( CallGen(parent, field_name + "%vspace%get_cell_dofmap", ["cell", "map"])) parent.add(DeclGen(parent, datatype="integer", entity_decls=["cell"])) parent.add( DeclGen(parent, datatype="integer", pointer=True, entity_decls=["map(:)"])) # create the argument list on the fly so we can also create # appropriate variables and lookups arglist = [] arglist.append("nlayers") arglist.append("ndf") arglist.append("map") found_gauss_quad = False gauss_quad_arg = None for arg in self._arguments.args: if arg.requires_basis: basis_name = arg.function_space + "_basis_" + arg.name arglist.append(basis_name) new_parent, position = parent.start_parent_loop() new_parent.add(CallGen(new_parent, field_name + "%vspace%get_basis", [basis_name]), position=["before", position]) parent.add( DeclGen(parent, datatype="real", kind="dp", pointer=True, entity_decls=[basis_name + "(:,:,:,:,:)"])) if arg.requires_diff_basis: raise GenerationError("differential basis has not yet " "been coded") if arg.requires_gauss_quad: if found_gauss_quad: raise GenerationError("found more than one gaussian " "quadrature in this kernel") found_gauss_quad = True gauss_quad_arg = arg dataref = "%data" arglist.append(arg.name + dataref) if found_gauss_quad: gq_name = "gaussian_quadrature" arglist.append(gauss_quad_arg.name + "%" + gq_name) # generate the kernel call and associated use statement parent.add(CallGen(parent, self._name, arglist)) if not self.module_inline: parent.add( UseGen(parent, name=self._module_name, only=True, funcnames=[self._name])) # declare and initialise the number of layers and the number # of degrees of freedom. Needs to be generalised. parent.add( DeclGen(parent, datatype="integer", entity_decls=["nlayers", "ndf"])) new_parent, position = parent.start_parent_loop() new_parent.add(AssignGen(new_parent, lhs="nlayers", rhs=field_name + "%get_nlayers()"), position=["before", position]) new_parent.add(AssignGen(new_parent, lhs="ndf", rhs=field_name + "%vspace%get_ndf()"), position=["before", position])