aboutsummaryrefslogtreecommitdiff
path: root/Tools/CodeGen/BAM.m
diff options
context:
space:
mode:
authorshusa <shusa>2005-11-10 19:18:29 +0000
committershusa <shusa>2005-11-10 19:18:29 +0000
commitaa73db4fd139fe7ad331e770733989b63263cf17 (patch)
treed277afac16ca7d60e11f7ea7c0b46c14fa89a51b /Tools/CodeGen/BAM.m
parent1e2d44f4d8089046fbd78869a4e45a06eb0cd3a8 (diff)
the bamification package
Diffstat (limited to 'Tools/CodeGen/BAM.m')
-rw-r--r--Tools/CodeGen/BAM.m1262
1 files changed, 1262 insertions, 0 deletions
diff --git a/Tools/CodeGen/BAM.m b/Tools/CodeGen/BAM.m
new file mode 100644
index 0000000..5f905e2
--- /dev/null
+++ b/Tools/CodeGen/BAM.m
@@ -0,0 +1,1262 @@
+
+(* $Id$ *)
+
+(* Copyright 2004 Sascha Husa, Ian Hinder, Christiane Lechner
+
+ This file is part of Kranc.
+
+ Kranc is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Kranc is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Foobar; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*)
+
+(* This package provides a set of functions to create the various
+ parts of a Cactus thorn and assemble them. *)
+
+BeginPackage["sym`"];
+
+(* These symbols are used in this file. Whenever this package is
+ used, the symbols will be added into the sym` context. You will
+ need to make sure the sym` context is on your ContextPath to use
+ the symbols without the sym` prefix. *)
+
+{AccumulatorBase, Name, Type, Extend, Default, Comment, Range, Implementation, Group,
+SchedulePoint, Language, SynchronizedGroups, StorageGroups,
+Timelevels, VariableType, GridType, Visibility, Variables,
+Implementations, Value, AllowedValues, UsedParameters, Description,
+ExtendedParameters, NewParameters, Directory, Interface, Param,
+Schedule, Sources, Makefile, Filename, Contents, ThornName,
+BaseImplementation, EvolvedGFs, PrimitiveGFs, Groups, Calculation,
+GridFunctions, Shorthands, Equations, Parameter, Value, UsesFunctions,
+ArgString, Conditional, D1, D2, D3, D11, D22, D33, D21, D31, D32,
+Textual, TriggerGroups, Include, RHSGroups};
+
+{ExcisionGFs};
+
+EndPackage[];
+
+BeginPackage["Thorn`", "CodeGen`", "CalculationFunction`", "MapLookup`", "KrancGroups`", "Helpers`"];
+
+(* These functions are externally visible, and comprise the public
+ interface to this package. *)
+CreateSchedule::usage = "Create the content of the schedule.ccl file.";
+CreateMakefile::usage = "Create the content of the Cactus make.code.defn file.";
+CreateInterface::usage = "Create the content of the interface.ccl file.";
+CreateParam::usage = "Create the content of the param.ccl file.";
+Quote::usage = ""; (* This should not be in this package *)
+CreateThorn::usage = "Create a general Cactus thorn from
+a thorn specification structure";
+CreateSymmetriesRegistrationSource::usage = "";
+CreateMoLRegistrationSource::usage = "";
+CreateMoLBoundariesSource::usage = "";
+CreateMoLExcisionSource::usage = "";
+CreateSetterSource::usage = "";
+CreateMPCharSource::usage = "";
+CreatePrecompMacros::usage = "";
+CreateStartupFile::usage = "";
+
+StartBAMProject::usage = "needs to be called before creating any BAM thorns";
+CloseBAMProject::usage = "needs to be called after all BAM thorns have been created";
+
+(* Ensure that we can refer to symbols without the `sym prefix *)
+$ContextPath = Join[{"sym`"}, $ContextPath];
+
+Begin["`Private`"];
+
+
+(* ------------------------------------------------------------------------
+ Miscellaneous definitions, could be moved elsewhere
+ ------------------------------------------------------------------------ *)
+
+(* date, user, etc. *)
+date[] := ToString[Date[][[3]]] <> "/" <>
+ ToString[Date[][[2]]] <> "/" <>
+ ToString[Date[][[1]]]
+
+
+dateLong[] := ToString[Date[][[3]]] <> "/" <>
+ ToString[Date[][[2]]] <> "/" <>
+ ToString[Date[][[1]]] <> "/" <> " " <>
+ ToString[Date[][[4]]] <> ":" <>
+ ToString[Date[][[5]]] <> ":" <>
+ ToString[Date[][[6]]];
+
+
+user[] := ToString[<< "!whoami"];
+
+
+whoWhen[lang_] := Module[{com1, com2},
+
+com1 = "UNRECOGNIZED LANGUAGE";
+com2 = "UNRECOGNIZED LANGUAGE";
+
+If[(lang == "C" || lang == "c"),
+ com1 = "/* "; com2 = " */";
+];
+
+If[(lang == "Fortran" || lang == "FORTRAN" || lang == "F90" || lang == "F95"),
+ com1 = "!"; com2 = "";
+];
+
+If[(lang == "F77"),
+ com1 = "C"; com2 = "";
+];
+
+If[(lang == "shell"),
+ com1 = "#"; com2 = "";
+];
+
+{com1 <> " file produced by user " <> user[] <> ", " <> date[] <> com2 <> "\n" <>
+ com1 <> " Produced with Mathematica Version " <> ToString[$Version] <> com2 <> "\n\n"<>
+ com1 <> " Mathematica script written by Ian Hinder and Sascha Husa" <> com2 <> "\n\n"<>
+ com1 <> " $Id" <> "$" <> com2 <> "\n\n"}
+
+];
+
+
+whoWhenSeparator[lang_] := Module[{com1, com2},
+
+com1 = "UNRECOGNIZED LANGUAGE";
+com2 = "UNRECOGNIZED LANGUAGE";
+
+If[(lang == "C" || lang == "c"),
+ com1 = "/* "; com2 = " */";
+ ];
+
+If[(lang == "Fortran" || lang == "FORTRAN" || lang == "F90" || lang == "F95"),
+ com1 = "!"; com2 = "";
+ ];
+
+If[(lang == "F77"),
+ com1 = "C"; com2 = "";
+ ];
+
+If[(lang == "shell"),
+ com1 = "#"; com2 = "";
+ ];
+
+{com1 <> " section produced by user " <> user[] <> ", " <> date[] <> com2 <> "\n\n"}
+];
+
+
+
+
+(* ------------------------------------------------------------------------
+ Makefile
+ ------------------------------------------------------------------------ *)
+
+(* Return a CodeGen block representing a makefile which refers to the
+ list of filenames sourceFiles *)
+CreateMakefile[sourceFiles_] :=
+ {whoWhenSeparator["shell"],
+ "SRCS = ", Map[{#, " "} &, sourceFiles]};
+
+(* ------------------------------------------------------------------------
+ Parameter file
+ ------------------------------------------------------------------------ *)
+
+(* parameterFileSpec = {Implementations -> {}, NewParameters -> {}}
+
+ implementation = {Name -> "",
+ UsedParameters (optional) -> {spec1, spec2, ...},
+ ExtendedParameters (optional) -> {spec1, spec2, ...}}
+
+ (optional) allowedValue = {Value -> "", Description -> ""}
+
+ parameter spec = {Name -> "", Type -> "", Default -> "",
+ Description -> "", Visibility -> "private",
+ AllowedValues -> {...}} *)
+
+(* Return a CodeGen block which represents the quoted value of x *)
+Quote[x_] := {"\"", x, "\""};
+
+(* To be used for parameters; will quote it if it is a keyword *)
+renderValue[type_, value_] :=
+ If[type == "KEYWORD",
+ Quote[value],
+ value];
+
+(* Return a block defining a parameter with the given
+ parameterSpec (defined above). This is used for defining new
+ parameters, as well as extending existing ones. *)
+parameterBlock[spec_] :=
+ {" AddPar(",
+ lookup[spec, Name], ", ",
+ Quote@lookup[spec, Default], ", ",
+ Quote@lookup[spec, Description],
+ ");\n"};
+
+(* Given a parameterFileSpec structure, return a CodeGen block file *)
+CreateParam[spec_] :=
+ {Map[{parameterBlock[#]} &, lookupDefault[spec, NewParameters, {}]]};
+
+(* ------------------------------------------------------------------------
+ Interface file
+ ------------------------------------------------------------------------ *)
+
+(* The following "group" structure defines a Cactus group of variables
+ to be included in an interface.ccl file.
+
+ group:
+
+ {Name -> "", VariableType -> "", Timelevels -> 2, GridType -> "GF",
+ Comment -> "", Visibility -> "public"
+ Variables -> {phi, h11, ...}} *)
+
+
+bamTensorType[group_] := Module[{comps, indices, type},
+
+ comps = Map[ToString, group ] ;
+
+ indices = Map[BreakTensorComponentName, comps];
+ indices = Map[#[[2]] &, indices];
+
+ type = "scalar"; (* the default *)
+
+ If [Sort@indices == Sort@{"11", "21", "22", "31", "32", "33", "12", "13", "23"}, type = "ij"];
+ If [Sort@indices == Sort@{"11", "21", "22", "31", "32", "33"}, type = "ij+ji"];
+ If [Sort@indices == Sort@{"33", "23", "22", "13", "12", "11"}, type = "IJ+JI"];
+
+ If [indices == {"1", "2", "3"}, type = "i"];
+ If [indices == {"3", "2", "1"}, type = "I"];
+
+ If[ValueQ@Global`UserSetTensorType[x], type = Global`UserSetTensorType[ Map[#[[1]] &, indices] ] ];
+
+ type
+ ];
+
+
+
+(* Given the specification of a group structure, return a CodeGen
+ block for the interface.ccl file to define that group *)
+interfaceGroupBlock[spec_] :=
+ {" AddVar(", lookup[spec, Name], ", ",
+ Quote@bamTensorType@lookup[spec, Variables], ", ",
+ Quote@lookup[spec, Comment], ");\n"};
+
+CreateInterface[implementation_, inheritedImplementations_, includeFiles_,
+ groups_, opts___] :=
+ {"/* Variable Parameter and Function declarations for ", implementation, " */\n",
+ NewlineSeparated[Map[FlattenBlock[interfaceGroupBlock[#]] &, groups]]};
+
+(* ------------------------------------------------------------------------
+ Scheduling
+ ------------------------------------------------------------------------ *)
+
+(* storage group
+
+ (represents the fact that we want to allocate storage for a Cactus
+ variable group with the given number of timelevels)
+
+ {Group -> "admbase::metric", Timelevels -> 3,
+ Conditional -> {Parameter -> "", Value -> ""},
+ Conditional -> {Textual -> "CCTK_EQUALS(name,value)"}}
+
+ A "conditional" structure looks like this: {Parameter -> "", Value -> ""}
+
+ scheduled function: (a function to be scheduled at a particular point)
+
+ {Name -> "ADM_BSSN_CalcRHS_fn", SchedulePoint -> "in POSTINITIAL before ExternalLapse",
+ Language -> "C", Comment -> "",
+ (optional) SynchronizedGroups -> {ADM_BSSN_gamma, ...},
+ (optional) Options -> {"meta", "level", ...},
+ (optional) StorageGroups -> {Group -> "mygroup", Timelevels -> 1},
+ (optional) Conditional -> {Parameter -> "", Value -> ""}}
+
+ scheduled group:
+
+ {... sameish}
+
+*)
+
+(* Given a function scheduling specification as defined above, return
+ a CodeGen block to schedule the function for the schedule.ccl file *)
+scheduleUnconditionalFunction[spec_] :=
+ {" AddFun(" <> lookup[spec,SchedulePoint] <> ", " <> lookup[spec, Name] <> ", " <> Quote[lookup[spec, Comment]], ");\n\n"};
+
+(* Handle the aspect of scheduling the function conditionally *)
+scheduleFunction[spec_] :=
+ Module[{condition, parameter, value, u},
+
+ u = scheduleUnconditionalFunction[spec];
+
+ If[mapContains[spec, Conditional],
+
+ (* Output the conditional structure *)
+ condition = lookup[spec, Conditional];
+
+ If[mapContains[condition, Textual],
+
+ ConditionalOnParameterTextual[lookup[condition, Textual], u],
+
+ If[mapContains[condition, Parameter],
+
+ parameter = lookup[condition, Parameter];
+ value = lookup[condition, Value];
+ ConditionalOnParameter[parameter, value, u],
+
+ If[condition != {},
+ Throw["Unrecognized conditional structure", condition],
+ u]]],
+ u]];
+
+(* Taking a list of group storage specifications for global storage,
+ and lists of scheduled function and scheduled group structures,
+ return a CodeGen block representing a schdule.ccl file. *)
+CreateSchedule[globalStorageGroups_, scheduledGroups_, scheduledFunctions_] :=
+ {Map[SeparatedBlock[scheduleFunction[#]] &, scheduledFunctions] };
+
+
+(* ------------------------------------------------------------------------
+ Setter
+ ------------------------------------------------------------------------ *)
+
+(* calculation = {Name -> "ClassicADM_Setter",
+ optional Before -> {functions},
+ optional After -> {functions},
+ Shorthands -> {gInv11, ...},
+ GridFunctions -> {g11rhs, K11},
+ CollectList -> {hInv11, hInv22, ...},
+ optional DeclarationIncludes -> {include file list},
+ optional LoopPreIncludes -> {include file list},
+ Equations -> {{K11_rhs -> 2 A K11, ...}...}} *)
+
+calculationMacros[] :=
+ CommentedBlock["Define macros used in calculations",
+ Map[{"#define ", #, "\n"} &,
+ {"INITVALUE (42)",
+ "INV(x) ((1.0) / (x))" ,
+ "SQR(x) ((x) * (x))" ,
+ "CUB(x) ((x) * (x) * (x))" ,
+ "QAD(x) ((x) * (x) * (x) * (x))"}]];
+
+(* Given a list of Calculation structures as defined above, create a
+ CodeGen representation of a source file that defines a function for
+ each Calculation. *)
+
+CreateSetterSource[calcs_, debug_, opts___] :=
+ Module[{include},
+ include = lookupDefault[{opts}, Include, {}];
+
+ If[!MatchQ[include, _List],
+ Throw["CreateSetterSource: Include should be a list but is in fact " <> ToString[include]]];
+
+ {whoWhen[CodeGen`SOURCELANGUAGE],
+
+ "#define KRANC_" <> ToUpperCase[CodeGen`SOURCELANGUAGE] <> "\n\n",
+
+ If[CodeGen`SOURCELANGUAGE == "C",
+ IncludeFile["math.h"],
+ "\n"
+ ],
+
+ Map[IncludeFile, Join[{"cctk.h", "cctk_Arguments.h", "cctk_Parameters.h",
+ "precomputations.h", "GenericFD.h", "Differencing.h"}, include]],
+ calculationMacros[],
+
+ (* For each function structure passed, create the function and
+ insert it *)
+ Map[CreateCalculationFunction[# , debug]& ,
+ calcs]}];
+
+
+
+(* ------------------------------------------------------------------------
+ Symmetries Registration
+ ------------------------------------------------------------------------ *)
+
+(* Symmetries registration spec = {{FullName -> "impl::GFname",
+ Sym -> {symX, symY, symZ}}, ...} *)
+
+SymmetriesBlock[spec_] :=
+
+ Module[{i, KrancDim},
+
+ KrancDim = 3;
+
+ sym = lookup[spec, Sym];
+
+ {Table["sym[" <> ToString[i - 1] <> "] = " <>
+ ToString@sym[[i]] <> ";\n", {i, 1, KrancDim}],
+
+ "SetCartSymVN(cctkGH, sym, \"" <> lookup[spec, FullName] <> "\");\n\n"
+}
+];
+
+
+calcSymmetry[gf_] := Module[{sym, q, string},
+
+sym = {1, 1, 1}; (* default *)
+
+string = ToString@gf;
+
+While[IntegerQ[q = ToExpression@StringTake[string, -1]],
+
+Module[{},
+ sym[[q]] = -sym[[q]];
+ string = StringDrop[string, -1]
+ ]
+];
+sym
+];
+
+
+(* Given a symmetries registration structure as defined above, return a
+ C CodeGen structure of a source file which will register the symmetries. *)
+CreateSymmetriesRegistrationSource[thornName_, implementationName_, GFs_, debug_] :=
+
+ Module[{spec, j, lang, tmp},
+
+ If[debug,
+ Print["Registering Symmetries for: ", GFs];
+ ];
+
+ lang = CodeGen`SOURCELANGUAGE;
+ CodeGen`SOURCELANGUAGE = "C";
+
+ spec = Table[{FullName -> implementationName <> "::" <> ToString@GFs[[j]],
+ Sym -> calcSymmetry[GFs[[j]] ]
+ }, {j, 1, Length@GFs}];
+
+ tmp = {whoWhen["C"],
+
+ Map[IncludeFile,
+ {"cctk.h", "cctk_Arguments.h", "cctk_Parameters.h", "Symmetry.h"}],
+
+ DefineCCTKFunction[ thornName <> "_RegisterSymmetries", "void",
+ {CommentedBlock["array holding symmetry definitions",
+
+ "CCTK_INT sym[3];\n\n"],
+
+ CommentedBlock["Register symmetries of grid functions",
+
+ Map[SymmetriesBlock, spec]]}
+]
+ };
+
+ CodeGen`SOURCELANGUAGE = lang;
+
+tmp
+];
+
+
+(* ------------------------------------------------------------------------
+ MoL Registration
+ ------------------------------------------------------------------------ *)
+
+(* MoL registration = {EvolvedGFs -> {h11, ...}, PrimitiveGFs -> {trK, ...},
+ BaseImplementation -> "ADMBase", ThornName -> "ADMMoL"} *)
+
+(* Given a MoL registration structure as defined above, return a
+ CodeGen structure of a source file which will register the
+ variables given with MoL. *)
+CreateMoLRegistrationSource[spec_, debug_] :=
+
+ Module[{tmp, lang},
+
+ If[debug,
+ Print["Registering for MoL:"];
+ Print[];
+ Print[" Evolved Gridfunctions: ", lookup[spec, EvolvedGFs] ];
+ Print[" Primitive Gridfunctions: ", lookup[spec, PrimitiveGFs] ];
+ ];
+
+ lang = CodeGen`SOURCELANGUAGE;
+ CodeGen`SOURCELANGUAGE= "C";
+
+ tmp = {whoWhen["C"],
+
+ Map[IncludeFile,
+ {"cctk.h", "cctk_Arguments.h", "cctk_Parameters.h"}],
+
+ DefineCCTKFunction[lookup[spec,ThornName] <> "_RegisterVars", "CCTK_INT",
+ {DefineVariable["ierr", "CCTK_INT", "0"],
+
+ CommentedBlock["Register all the evolved grid functions with MoL",
+
+ Map[{"ierr += MoLRegisterEvolved(CCTK_VarIndex(\"", #, "\"), CCTK_VarIndex(\"",
+ lookup[spec,BaseImplementation], "::", unqualifiedGroupName[#], "rhs\"));\n"} &,
+ lookup[spec, EvolvedGFs]]],
+
+ CommentedBlock["Register all the primitive grid functions with MoL",
+ Map[{"ierr += MoLRegisterConstrained(CCTK_VarIndex(\"",
+ lookup[spec,BaseImplementation], "::", #, "\"));\n"} &,
+ lookup[spec, PrimitiveGFs]]],
+ "return ierr;\n"}]};
+
+ CodeGen`SOURCELANGUAGE = lang;
+
+tmp
+];
+
+(* ------------------------------------------------------------------------
+ MoL Boundaries
+ ------------------------------------------------------------------------ *)
+
+(* boundaries spec = {Groups -> {trK, h11, ...},
+ BaseImplementation -> "ADMBase", ThornName -> "ADMMoL"} *)
+
+(* the boundary treatment is split into 3 steps:
+ 1. excision
+ 2. symmetries
+ 3. "other" boundary conditions, e.g. flat, radiative
+
+To simplify scheduling, testing, etc. the 3 steps are currently applied in separate functions!*)
+
+(* boundary conditions may have to be applied per GF or goup ; per group
+should be more efficient, but sometimes there will be a GF-dependent parameter,
+e.g. for radiation BCs *)
+
+cleanCPP[x_] := Map[StringReplace[FlattenBlock[#], " #" -> "#"]&, x];
+
+(* Given a BC registration structure as defined above, return a
+ CodeGen structure of a source file which does nothing yet! *)
+CreateMoLBoundariesSource[spec_] :=
+
+ Module[{gfs, groups, tmp, lang},
+
+ gfs = lookup[spec, EvolvedGFs];
+ groups = Map[unqualifiedGroupName, lookup[spec, Groups]];
+
+ listBCparfileEntry[gforgroup_] := Module[{prefix, unqualName},
+ (* include a comment block with template parameter file entries *)
+ prefix = "#$bound$#" <> lookup[spec, ThornImplementation] <> "::";
+ unqualName = unqualifiedGroupName@ToString@gforgroup;
+
+ {
+ prefix <> unqualName <> "_bound = \"skip\"\n",
+ prefix <> unqualName <> "_bound_speed = 1.0\n",
+ prefix <> unqualName <> "_bound_limit = 0.0\n",
+ prefix <> unqualName <> "_bound_scalar = 0.0\n\n"
+ }];
+
+ trivialBCGroup[group_] := Module[{boundpar, fullgroupname},
+ (* boundary conditions that do not have parameters besides their name *)
+
+ boundpar = unqualifiedGroupName@group <> "_bound";
+ fullgroupname = qualifyGroupName[ToString@group, lookup[spec, BaseImplementation]];
+
+ {"\n",
+ "if (CCTK_EQUALS(" <> boundpar <> ", \"none\" ) ||\n",
+ " CCTK_EQUALS(" <> boundpar <> ", \"static\") ||\n",
+ " CCTK_EQUALS(" <> boundpar <> ", \"flat\" ) ||\n",
+ " CCTK_EQUALS(" <> boundpar <> ", \"zero\" ) ) \n",
+ "{\n",
+
+ " ierr = Boundary_SelectGroupForBC(cctkGH, CCTK_ALL_FACES, 1, -1, \n",
+ " \"" <> fullgroupname <> "\", " <> boundpar <> ");\n",
+
+ " if (ierr < 0)\n",
+ " CCTK_WARN(-1, \"Failed to register "<>boundpar<>" BC for "<>fullgroupname<>"!\");\n",
+
+ "}\n"}];
+
+
+ trivialBCGF[gf_] := Module[{boundpar, fullgfname},
+ (* boundary conditions that do not have parameters besides their name *)
+
+ boundpar = unqualifiedGroupName@ToString@gf <> "_bound";
+ fullgfname = ToString@gf;
+
+ {"\n",
+ "if (CCTK_EQUALS(" <> boundpar <> ", \"none\" ) ||\n",
+ " CCTK_EQUALS(" <> boundpar <> ", \"static\") ||\n",
+ " CCTK_EQUALS(" <> boundpar <> ", \"flat\" ) ||\n",
+ " CCTK_EQUALS(" <> boundpar <> ", \"zero\" ) ) \n",
+ "{\n",
+
+ " ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, 1, -1, \n",
+ " \"" <> fullgfname <> "\", " <> boundpar <> ");\n",
+
+ " if (ierr < 0)\n",
+ " CCTK_WARN(-1, \"Failed to register "<>boundpar<>" BC for "<>fullgfname<>"!\");\n",
+
+ "}\n"}];
+
+ radiationBCGroup[group_] := Module[{boundpar, fullgroupname, myhandle},
+ (* a simple radiation boundary condition *)
+
+ boundpar = unqualifiedGroupName@ToString@group <> "_bound";
+ fullgroupname = qualifyGroupName[ToString@group, lookup[spec, BaseImplementation]];
+
+ myhandle = "handle_" <> boundpar;
+
+ {"\n",
+ "if (CCTK_EQUALS(" <> boundpar <> ", \"radiative\"))\n",
+ "{\n /* apply radiation boundary condition */\n ",
+
+ DefineVariable[myhandle, "CCTK_INT", "Util_TableCreate(UTIL_TABLE_FLAGS_CASE_INSENSITIVE)"],
+
+ " if ("<>myhandle<>" < 0) CCTK_WARN(-1, \"could not create table!\");\n",
+
+ " if (Util_TableSetReal("<>myhandle<>" , "<> boundpar <>"_limit, \"LIMIT\") < 0)\n",
+ " CCTK_WARN(-1, \"could not set LIMIT value in table!\");\n",
+
+ " if (Util_TableSetReal("<>myhandle<>" ," <> boundpar <> "_speed, \"SPEED\") < 0)\n",
+ " CCTK_WARN(-1, \"could not set SPEED value in table!\");\n",
+
+ "\n",
+ " ierr = Boundary_SelectGroupForBC(cctkGH, CCTK_ALL_FACES, 1, "<>myhandle<>", \n",
+ " \"" <> fullgroupname <> "\", \"Radiation\");\n\n",
+
+ " if (ierr < 0)\n",
+ " CCTK_WARN(-1, \"Failed to register Radiation BC for "<>fullgroupname<>"!\");\n",
+
+ "\n}\n"}];
+
+
+ radiationBCGF[gf_] := Module[{boundpar, fullgfname, myhandle},
+ (* a simple radiation boundary condition *)
+
+ boundpar = unqualifiedGroupName@ToString@gf <> "_bound";
+ fullgfname = ToString@gf;
+
+ myhandle = "handle_" <> boundpar;
+
+ {"\n",
+ "if (CCTK_EQUALS(" <> boundpar <> ", \"radiative\"))\n",
+ "{\n /* apply radiation boundary condition */\n ",
+
+ DefineVariable[myhandle, "CCTK_INT", "Util_TableCreate(UTIL_TABLE_FLAGS_CASE_INSENSITIVE)"],
+
+ " if ("<>myhandle<>" < 0) CCTK_WARN(-1, \"could not create table!\");\n",
+
+ " if (Util_TableSetReal("<>myhandle<>" , "<> boundpar <>"_limit, \"LIMIT\") < 0)\n",
+ " CCTK_WARN(-1, \"could not set LIMIT value in table!\");\n",
+
+ " if (Util_TableSetReal("<>myhandle<>" ," <> boundpar <> "_speed, \"SPEED\") < 0)\n",
+ " CCTK_WARN(-1, \"could not set SPEED value in table!\");\n",
+
+ "\n",
+ " ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, 1, "<>myhandle<>", \n",
+ " \"" <> fullgfname <> "\", \"Radiation\");\n\n",
+
+ " if (ierr < 0)\n",
+ " CCTK_WARN(-1, \"Failed to register Radiation BC for "<>fullgfname<>"!\");\n",
+
+ "\n}\n"}];
+
+ scalarBCGroup[group_] := Module[{boundpar, fullgroupnamei, myhandle},
+ (* simple dirichlet boundary condition *)
+
+ boundpar = unqualifiedGroupName@group <> "_bound";
+ fullgroupname = qualifyGroupName[ToString@group, lookup[spec, BaseImplementation]];
+ myhandle = "handle_" <> boundpar;
+
+ {"\n",
+ "if (CCTK_EQUALS(" <> boundpar <> ", \"scalar\"))\n",
+ "{\n /* apply scalar boundary condition */\n ",
+
+ DefineVariable[myhandle, "CCTK_INT", "Util_TableCreate(UTIL_TABLE_FLAGS_CASE_INSENSITIVE)"],
+
+ " if ("<>myhandle<>" < 0) CCTK_WARN(-1, \"could not create table!\");\n",
+
+ " if (Util_TableSetReal("<>myhandle<>" ," <> boundpar <> "_scalar, \"SCALAR\") < 0)\n",
+ " CCTK_WARN(-1, \"could not set SCALAR value in table!\");\n",
+
+ "\n",
+ " ierr = Boundary_SelectGroupForBC(cctkGH, CCTK_ALL_FACES, 1, "<>myhandle<>", \n",
+ " \"" <> fullgroupname <> "\", \"scalar\");\n\n",
+
+ " if (ierr < 0)\n",
+ " CCTK_WARN(-1, \"Failed to register Scalar BC for "<>fullgroupname<>"!\");\n",
+
+ "\n}\n"}];
+
+
+ scalarBCGF[gf_] := Module[{boundpar, fullgfname, myhandle},
+ (* simple dirichlet boundary condition *)
+
+ boundpar = unqualifiedGroupName@ToString@gf <> "_bound";
+ fullgfname = ToString@gf;
+ myhandle = "handle_" <> boundpar;
+
+ {"\n",
+ "if (CCTK_EQUALS(" <> boundpar <> ", \"scalar\"))\n",
+ "{\n /* apply scalar boundary condition */\n ",
+
+ DefineVariable[myhandle, "CCTK_INT", "Util_TableCreate(UTIL_TABLE_FLAGS_CASE_INSENSITIVE)"],
+
+ " if ("<>myhandle<>" < 0) CCTK_WARN(-1, \"could not create table!\");\n",
+
+ " if (Util_TableSetReal("<>myhandle<>" ," <> boundpar <> "_scalar, \"SCALAR\") < 0)\n",
+ " CCTK_WARN(-1, \"could not set SCALAR value in table!\");\n",
+
+ "\n",
+ " ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, 1, "<>myhandle<>", \n",
+ " \"" <> fullgfname <> "\", \"scalar\");\n\n",
+
+ " if (ierr < 0)\n",
+ " CCTK_WARN(-1, \"Error in registering Scalar BC for "<>fullgfname<>"!\");\n",
+
+ "\n}\n"}];
+
+
+ lang = CodeGen`SOURCELANGUAGE;
+ CodeGen`SOURCELANGUAGE = "C";
+
+ tmp = {whoWhen["C"],
+
+
+ Map[IncludeFile,
+ {"cctk.h", "cctk_Arguments.h", "cctk_Parameters.h",
+ "cctk_Faces.h", "util_Table.h"}],
+
+ {"\n\n",
+ "/* the boundary treatment is split into 3 steps: */\n",
+ "/* 1. excision */\n",
+ "/* 2. symmetries */\n",
+ "/* 3. \"other\" boundary conditions, e.g. radiative */\n\n",
+ "/* to simplify scheduling and testing, the 3 steps */\n",
+ "/* are currently applied in separate functions */\n\n"},
+
+
+ cleanCPP@DefineCCTKFunction[lookup[spec,ThornName] <> "_CheckBoundaries",
+ "CCTK_INT",
+ {"return 0;\n"}],
+
+
+ cleanCPP@DefineCCTKFunction[lookup[spec,ThornName] <> "_ApplyBoundConds",
+ "CCTK_INT",
+ {DefineVariable["ierr", "CCTK_INT", "0"],
+
+ Map[trivialBCGroup, groups],
+ Map[trivialBCGF, gfs],
+
+ Map[radiationBCGroup, groups],
+ Map[radiationBCGF, gfs],
+
+ Map[scalarBCGroup, groups],
+ Map[scalarBCGF, gfs],
+
+ "return ierr;\n"
+ }],
+
+ "\n\n\n",
+ "/* template for entries in parameter file:\n",
+ Map[listBCparfileEntry, groups],
+ Map[listBCparfileEntry, gfs],
+ "*/\n\n"
+ };
+
+ CodeGen`SOURCELANGUAGE = lang;
+tmp
+];
+
+CreateMoLExcisionSource[spec_] :=
+
+ Module[{gfs, currentlang, body, excisionExtrap},
+
+ gfs = lookup[spec, ExcisionGFs];
+
+ Print["Applying excision to GFs: ", gfs];
+
+ currentlang = CodeGen`SOURCELANGUAGE;
+ CodeGen`SOURCELANGUAGE = "Fortran";
+
+ excisionExtrap[gf_] := " call ExcisionExtrapolate(ierr, "
+ <> ToString@gf <> ", " <> ToString@gf
+ <> "_p, emask, exnormx, exnormy, exnormz, nx, ny, nz, "<> ToString@gf <> "_bound_limit)\n";
+
+ body = {whoWhen["Fortran"],
+
+ Map[IncludeFile,
+ {"cctk.h", "cctk_Arguments.h", "cctk_Parameters.h"}],
+
+ {"\n\n",
+ "! the boundary treatment is split into 3 steps: \n",
+ "! 1. excision \n",
+ "! 2. symmetries \n",
+ "! 3. \"other\" boundary conditions, e.g. radiative \n\n",
+ "! to simplify scheduling and testing, the 3 steps \n",
+ "! are currently applied in separate functions \n\n"},
+
+ cleanCPP@DefineCCTKSubroutine[lookup[spec,ThornName] <> "_FindBoundary",
+ {"! APPLY EXCISION\n\n",
+ DefineVariable["ierr", "CCTK_INT :: ", "0"],
+ "",
+
+ "integer :: nx, ny, nz\n\n",
+
+ "! grid parameters\n",
+
+ "nx = cctk_lsh(1)\n",
+ "ny = cctk_lsh(2)\n",
+ "nz = cctk_lsh(3)\n\n",
+
+ "if ( (excision .ne. 0).AND.(find_excision_boundary .ne. 0) ) then\n\n",
+
+ " call ExcisionFindBoundary(ierr, emask, nx, ny, nz)\n",
+ " if (ierr < 0) call CCTK_WARN(2, \"findboundary exited with an error\")\n\n",
+
+ "endif\n\n",
+ "return\n"}],
+
+ cleanCPP@DefineCCTKSubroutine[lookup[spec,ThornName] <> "_FindNormals",
+ {"! APPLY EXCISION\n\n",
+ DefineVariable["ierr", "CCTK_INT :: ", "0"],
+ "",
+
+ "integer :: nx, ny, nz\n\n",
+
+ "! grid parameters\n",
+
+ "nx = cctk_lsh(1)\n",
+ "ny = cctk_lsh(2)\n",
+ "nz = cctk_lsh(3)\n\n",
+
+ "if ( (excision .ne. 0).AND.(find_excision_normals .ne. 0) ) then\n\n",
+
+ " call ExcisionFindNormals(ierr, emask, exnormx, exnormy, exnormz, nx, ny, nz)\n",
+ " if (ierr < 0) call CCTK_WARN(2, \"findnormals exited with an error\")\n\n",
+
+ "endif\n\n",
+ "return\n"}],
+
+
+ cleanCPP@DefineCCTKSubroutine[lookup[spec,ThornName] <> "_ApplyExcision",
+ {"! APPLY EXCISION\n\n",
+ DefineVariable["ierr", "CCTK_INT :: ", "0"],
+ "",
+
+ "integer :: nx, ny, nz\n\n",
+
+ "! grid parameters\n",
+
+ "nx = cctk_lsh(1)\n",
+ "ny = cctk_lsh(2)\n",
+ "nz = cctk_lsh(3)\n\n",
+
+ "if (excision .ne. 0) then\n",
+
+ " call CCTK_INFO(\"Applying LegoExcision\")\n\n",
+
+ Map[excisionExtrap, gfs],
+ "endif\n\n",
+ "return\n"}]
+};
+
+CodeGen`SOURCELANGUAGE = currentlang;
+
+body
+];
+
+
+
+(* ------------------------------------------------------------------------ *)
+(* set Characteristic Info for MultiPatch *)
+(* ------------------------------------------------------------------------ *)
+
+(* boundaries spec = {TO BE DEFINED} *)
+
+charInfoFunction[type_, spec_, debug_]:= Module[{funcName, argString, headerComment1, headerComment2,
+ thornName, gfs, rhs, groups, tmp, lang, numvars, tab},
+
+ gfs = Map[ToString, lookup[spec, EvolvedGFs]];
+ rhs = Map[AddSuffix[#, "rhs"]&, gfs];
+
+ Print["createCharInfoFunction with type:\n", type];
+
+ thornName = lookup[spec, Name];
+
+ numvars = Length@gfs;
+
+ groups = Map[unqualifiedGroupName, lookup[spec, Groups]];
+
+ tab = "\t\t\t";
+
+If[type == "P2C",
+
+ funcName = lookup[spec,Name] <> "_MultiPatch_Prim2Char";
+ argString = "CCTK_POINTER_TO_CONST const cctkGH_,\n" <>
+ tab <> "CCTK_INT const dir,\n" <>
+ tab <> "CCTK_INT const face,\n" <>
+ tab <> "CCTK_REAL const * restrict const base,\n" <>
+ tab <> "CCTK_INT const * restrict const off,\n" <>
+ tab <> "CCTK_INT const * restrict const len,\n" <>
+ tab <> "CCTK_INT const rhs_flag,\n" <>
+ tab <> "CCTK_INT const num_modes,\n" <>
+ tab <> "CCTK_POINTER const * restrict const modes,\n" <>
+ tab <> "CCTK_POINTER const * restrict const speeds";
+
+ headerComment1 = "/* translate from primary to characteristic variables */\n";
+ headerComment2 = "/* Output: */\n" <>
+ "/* CCTK_POINTER ARRAY IN modes ... array if char. vars */\n" <>
+ "/* CCTK_POINTER ARRAY IN speeds ... array of char. speeds */\n\n";
+];
+
+
+If[type == "C2P",
+
+ funcName = lookup[spec,Name] <> "_MultiPatch_Char2Prim";
+ argString = "CCTK_POINTER_TO_CONST const cctkGH_,\n" <>
+ tab <> "CCTK_INT const dir,\n" <>
+ tab <> "CCTK_INT const face,\n" <>
+ tab <> "CCTK_REAL const * restrict const base,\n" <>
+ tab <> "CCTK_INT const * restrict const off,\n" <>
+ tab <> "CCTK_INT const * restrict const len,\n" <>
+ tab <> "CCTK_INT const rhs_flag,\n" <>
+ tab <> "CCTK_INT const num_modes,\n" <>
+ tab <> "CCTK_POINTER_TO_CONST const * restrict const modes";
+
+ headerComment1 = "/* translate from characteristic to primary variables */\n";
+ headerComment2 = "/* Output: */\n" <>
+ "/* CCTK_POINTER ARRAY IN modes ... array of char. vars */\n\n";
+];
+
+
+
+
+code = {
+
+DefineFunction[funcName, "CCTK_INT", argString,
+
+{headerComment1,
+"/* Input: */\n",
+"/* CCTK_POINTER_TO_CONST cctkGH ... CCTK grid hierarchy */\n",
+"/* CCTK_INT dir ... */\n",
+"/* CCTK_INT face ... */\n",
+"/* CCTK_REAL ARRAY base ... */\n",
+"/* CCTK_INT ARRAY lbnd ... */\n",
+"/* CCTK_INT ARRAY lsh ... */\n",
+"/* CCTK_INT rhs_flag ... */\n",
+"/* CCTK_INT num_modes... */\n",
+headerComment2,
+"{\n",
+" cGH const * restrict const cctkGH = cctkGH_;\n",
+" DECLARE_CCTK_ARGUMENTS;\n",
+" DECLARE_CCTK_PARAMETERS;\n\n",
+
+" CCTK_REAL const * restrict prims[" <> ToString@numvars <> "];\n",
+" CCTK_REAL * restrict chars[" <> ToString@numvars <> "];\n",
+" CCTK_REAL * restrict cspeeds[" <> ToString@numvars <> "];\n",
+
+" CCTK_REAL normal[3], normal_base[3];\n",
+" CCTK_REAL tangent[2][3];\n",
+
+ (* " CCTK_REAL gama[3][3], gamau[3][3], beta[3], alfa;\n", *)
+" CCTK_REAL lambda[" <> ToString@numvars <> "];\n",
+
+" CCTK_REAL xform[" <> ToString@numvars <> "][" <> ToString@numvars <> "];\n",
+" CCTK_REAL norm_normal;\n",
+" CCTK_REAL norm_tangent[2];\n",
+
+" int n, m; /* mode */\n",
+" int i, j, k; /* grid point indices */\n",
+" int d, e; /* dimension */\n\n",
+
+" /* Check arguments */\n",
+" assert (cctkGH);\n",
+" assert (cctk_dim == 3);\n",
+" assert (dir >= 0 && dir < cctk_dim);\n",
+" assert (face >= 0 && face < 2);\n",
+" assert (base);\n",
+" assert (off);\n",
+" assert (len);\n\n",
+
+" for (d = 0; d < 3; ++d) {\n",
+" assert (off[d] >= 0 && len[d] >= 0 && off[d] + len[d] <= cctk_lsh[d]);\n",
+" }\n\n",
+
+" assert (modes);\n",
+" assert (speeds);\n",
+
+" for (d = 0; d < 3; ++d) {\n",
+" normal_base[d] = base[d];\n",
+" tangent[ 0][d] = base[ cctk_dim+d];\n",
+" tangent[ 1][d] = base[2*cctk_dim+d];\n",
+" }\n\n",
+
+" {\n",
+" CCTK_REAL normal_length = 0;\n",
+" for (d = 0; d < 3; ++d) {\n",
+" normal_length += fabs(normal_base[d]);\n",
+" }\n",
+" assert (normal_length > 0);\n",
+" }\n\n",
+
+" assert (num_modes == " <> ToString@numvars <> ");\n",
+
+" for (n = 0; n < num_modes; ++n) {\n",
+" assert (modes[n]);\n",
+" }\n\n",
+
+" /* Get variable pointers */\n",
+" if (rhs_flag) {\n",
+Table[" rhs[" <> ToString[i-1] <> "] = CCTK_VarIndex(\"" <> ToString@rhs[[i]] <> "\");\n",
+ {i, 1, numvars}],
+" } else {\n",
+Table[" prim[" <> ToString[i-1] <> "] = CCTK_VarIndex(\"" <> gfs[[i]] <> "\");\n",
+ {i, 1, numvars}],
+" }\n\n",
+" for (n = 0; n < num_vars ; ++n) {\n",
+" chars[ n] = modes[ n];\n",
+" cspeeds[n] = speeds[n];\n",
+" }\n\n",
+"/* compute characteristic variables and speeds */\n",
+
+
+
+
+
+" /* Return 0 for Success! */\n",
+" return 0;\n"}]
+(* this was it, let`s go for a beer *)
+};
+
+code];
+
+CreateMPCharSource[spec_, debug_] :=
+
+ Module[{thornName, gfs, rhs, groups, tmp, lang, numvars},
+
+ gfs = Map[ToString, lookup[spec, EvolvedGFs]];
+ rhs = Map[AddSuffix[#, "rhs"]&, gfs];
+
+ Print["CreateMPCharSource uses RHS GFs:\n", rhs];
+
+ thornName = lookup[spec, Name];
+
+ numvars = Length@gfs;
+
+ groups = Map[unqualifiedGroupName, lookup[spec, Groups]];
+
+ lang = CodeGen`SOURCELANGUAGE;
+ CodeGen`SOURCELANGUAGE = "C";
+
+ tmp = {whoWhen["C"],
+
+
+ Map[IncludeFile,
+ {"assert.h", "math.h", "cctk.h", "cctk_Arguments.h", "cctk_Parameters.h"}],
+
+(* declare lapack function DGESV: compute solution to system of linear equations E * X = B *)
+{"\n/* declare lapack function DGESV for solving linear systems */\n",
+"void CCTK_FCALL\n",
+"CCTK_FNAME(dgesv) (int const * n,\n",
+" int const * nrhs,\n",
+" double * a,\n",
+" int const * lda,\n",
+" int * ipiv,\n",
+" double * b,\n",
+" int const * ldb,\n",
+" int * info);\n\n\n"},
+
+DefineFunction[lookup[spec,Name] <> "_MultiPatch_SystemDescription", "CCTK_INT",
+ "CCTK_POINTER_TO_CONST const cctkGH_, CCTK_INT const nvars,\n" <>
+ " CCTK_INT * restrict const prim, CCTK_INT * restrict const rhs,\n" <>
+ " CCTK_REAL * restrict const sigma",
+
+{
+"/* this function is called twice: */\n",
+"/* first to set the number of modes, then to set the rest of the information */\n",
+" cGH const * restrict const cctkGH = cctkGH_;\n",
+" DECLARE_CCTK_PARAMETERS;\n\n",
+" int n;\n\n",
+" /* Check arguments */\n",
+" assert (cctkGH);\n",
+" assert (nvars >= 0);\n\n",
+" /* Fill in return values on second call */\n",
+" if (nvars == " <> ToString@numvars <> ") {\n",
+" assert (prim);\n\n",
+Table[" prim[" <> ToString[i-1] <> "] = CCTK_VarIndex(\"" <> gfs[[i]] <> "\");\n", {i,1,numvars}],
+"\n",
+" for (n = 0; n < " <> ToString@numvars <> "; ++n) {\n",
+" assert (prim[n] >= 0);\n",
+" }\n\n",
+" assert (rhs);\n\n",
+Table[" rhs[" <> ToString[i-1] <> "] = CCTK_VarIndex(\"" <> ToString@rhs[[i]] <> "\");\n", {i,1,numvars}],
+"\n",
+" for (n = 0; n < " <> ToString@numvars <> "; ++n) {\n",
+" assert (rhs[n] >= 0);\n",
+" }\n\n",
+" }\n\n",
+" /* Coefficient for the scalar product via SummationByParts Thorn */\n",
+" *sigma = GetScalProdCoeff();\n\n",
+" /* Return the number of modes -- needed at first call! */\n",
+" return " <> ToString@numvars <> ";\n"}],
+
+(* *)
+charInfoFunction["P2C", spec, debug], "\n\n",
+charInfoFunction["C2P", spec, debug], "\n\n",
+charInfoFunction["WHATEVER", spec, debug]
+};
+
+CodeGen`SOURCELANGUAGE = lang;
+tmp
+];
+
+
+(* --------------------------------------------------------------------------
+ Precompmacros
+ -------------------------------------------------------------------------- *)
+
+(* Argument to this is the same as for CreateSetterSource. This is
+ not implemented currently because the precomputations are performed
+ in the setter file itself. We want to change this for readability
+ reasons. The change will be to have this precompMacros.h file
+ define a macro performing the precomputations for *each loop* in
+ each function. Then the setter source file just has a line
+ invoking the macro in each loop. *)
+CreatePrecompMacros[functions_] :=
+ Module[{},
+ {}];
+
+(* ------------------------------------------------------------------------
+ Startup file
+ ------------------------------------------------------------------------ *)
+
+CreateStartupFile[thornName_, bannerText_] :=
+ Module[{tmp, lang},
+
+ lang = CodeGen`SOURCELANGUAGE;
+ CodeGen`SOURCELANGUAGE = "C";
+
+ tmp = {whoWhen["C"],
+
+ IncludeFile["cctk.h"],
+ DefineFunction[thornName <> "_Startup", "int", "void",
+ {DefineVariable["banner", "const char *", Quote[bannerText]],
+ "CCTK_RegisterBanner(banner);\n",
+ "return 0;\n"}]};
+
+ CodeGen`SOURCELANGUAGE = lang;
+
+ tmp
+ ];
+
+(* ------------------------------------------------------------------------ *)
+ BAM 'Thorn' creation
+(* ------------------------------------------------------------------------ *)
+
+(* source = {Filename -> "MoLRegister.c", Contents -> "#include ..."} *)
+
+(* thorn = {Name -> "ClassicADMMolEvolve", Directory -> "ClassicADM",
+ Interface -> i, Schedule -> s, Param -> p, Makefile -> m,
+ Sources -> {s1, s2, ...} *)
+
+(* Given a thorn specification structure as defined above, create a
+ thorn. Note that if you specify a path to the thorn, then you are
+ responsible for making sure that the parent directory exists; this
+ function does not automatically create any parent directories. *)
+CreateThorn[thorn_] :=
+ Module[{thornName, project, directory, bamC, bamH},
+
+
+ lastDir[dir_] := StringDrop[dir, Last@Last@StringPosition[dir, "/"] ];
+
+ thornName = lookup[thorn, Name];
+ directory = lookup[thorn, Directory];
+ project = lastDir@directory;
+
+ Print["Creating thorns in directory ", directory];
+
+ EnsureDirectory[directory];
+
+ bamC = {"\n",
+ whoWhenSeparator["C"],
+ lookup[thorn, Interface],
+ lookup[thorn, Param],
+ lookup[thorn, Schedule]};
+
+
+ isFunEntry[x_]:= StringMatchQ[ToString@x, "*AddFun(*)*"];
+
+ bamH = {"\n/* here we declare functions we have added with AddFun */\n"};
+
+ AppendTo[bamH, Select[Flatten@bamC, isFunEntry]];
+
+ AddToFile[directory <> "/bam_" <> project <> ".c", bamC];
+ AddToFile[directory <> "/bam_" <> project <> ".h", bamH];
+
+ Map[GenerateFile[directory <> "/" <> lookup[#, Filename],
+ lookup[#, Contents]] &,
+ lookup[thorn, Sources]];
+
+ AddToFile[directory <> "/Makefile", lookup[thorn, Makefile]]];
+
+
+StartBAMProject[project_, directory_] :=
+ Module[{bamC, bamH, bamM, cname, hname, mname, projectRootFunction},
+
+ lastDir[dir_] := StringDrop[dir, Last@Last@StringPosition[dir, "/"] ];
+
+ Print["Starting project " <> project <> " in directory ", directory];
+
+ EnsureDirectory[directory];
+
+ cname = "bam_" <> project <> ".c";
+ hname = "bam_" <> project <> ".h";
+ mname = "Makefile";
+
+ projectRootFunction = "bam_" <> project;
+
+ bamC = {"/* " <> cname <> " */\n",
+ whoWhen["C"],
+ "#include \"bam.h\"\n",
+ "#include \"" <> hname <> "\"\n\n\n",
+ "void " <> projectRootFunction <> "();\n{\n",
+ " if (!Getv(\"physics\", \"" <> project <> "\")) return;\n",
+ " printf(\"Adding " <> project <> "\");\n"
+ };
+
+ bamH = {"/* " <> hname <> " */\n",
+ whoWhen["C"],
+ "void " <> projectRootFunction <> "();\n\n"};
+
+ bamM = {"# " <> project <> "Makefile\n",
+ whoWhen["SH"],
+ "\n",
+ "NAME := " <> project,
+ "\n",
+ "OBJS := bam_$(NAME).o",
+ "\n",
+ "include $(TOP)/Makefile.subdirs"
+ };
+
+ GenerateFile[directory <> "/" <> cname, bamC];
+ GenerateFile[directory <> "/" <> hname, bamH];
+ GenerateFile[directory <> "/" <> mname, bamM];
+ ];
+
+
+CloseBAMProject[project_, directory_] :=
+ Module[{bamC, bamH, bamM, cname, hname, mname, closingMsg},
+
+ lastDir[dir_] := StringDrop[dir, Last@Last@StringPosition[dir, "/"] ];
+
+ Print["Closing project in directory ", directory];
+ EnsureDirectory[directory];
+
+ cname = "bam_" <> project <> ".c";
+ hname = "bam_" <> project <> ".h";
+ mname = "Makefile";
+
+ closingMsg = "File closed by CloseBAMProject";
+
+ bamC = {"/* " <> closingMsg <> " */\n",
+ "}\n"
+ };
+
+ bamH = {"\n/* " <> closingMsg <> " */\n"};
+
+ bamM = {"\n#" <> closingMsg <> "\n"};
+
+ AddToFile[directory <> "/" <> cname, bamC];
+ AddToFile[directory <> "/" <> hname, bamH];
+ AddToFile[directory <> "/" <> mname, bamM];
+ ];
+
+End[];
+
+EndPackage[];