def EigenCoord_Cart_to_xx(): # Step 1: Find the Eigen-Coordinate and set up the Eigen-Coordinate's reference metric: CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem") par.set_parval_from_str("reference_metric::CoordSystem",rfm.get_EigenCoord()) rfm.reference_metric() # Step 2: Output the Eigen-Coordinate mapping from Cartesian->xx: # Step 2.a: Sanity check: First make sure that rfm.Cart_to_xx has been set. Error out if not! if rfm.Cart_to_xx[0] == 0 or rfm.Cart_to_xx[1] == 0 or rfm.Cart_to_xx[2] == 0: print("ERROR: rfm.Cart_to_xx[], which maps Cartesian -> xx, has not been set for") print(" reference_metric::CoordSystem = "+par.parval_from_str("reference_metric::CoordSystem")) print(" Boundary conditions in curvilinear coordinates REQUiRE this be set.") sys.exit(1) # Step 2.b: Output C code for the Eigen-Coordinate mapping from Cartesian->xx: outstr = """ // Cart_to_xx for EigenCoordinate """+rfm.get_EigenCoord()+r""" (orig coord = """+CoordSystem_orig+");\n" outstr += outputC([rfm.Cart_to_xx[0],rfm.Cart_to_xx[1],rfm.Cart_to_xx[2]], ["Cart_to_xx0_inbounds","Cart_to_xx1_inbounds","Cart_to_xx2_inbounds"], filename="returnstring", params="preindent=2") # Step 3: Restore reference_metric::CoordSystem back to the original CoordSystem par.set_parval_from_str("reference_metric::CoordSystem",CoordSystem_orig) rfm.reference_metric() # Step 4: Return EigenCoord Cart_to_xx C code return outstr
def EigenCoord_xx_to_Cart(i012suffix = ""): # Step 1: Find the Eigen-Coordinate and set up the Eigen-Coordinate's reference metric: CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem") par.set_parval_from_str("reference_metric::CoordSystem",rfm.get_EigenCoord()) rfm.reference_metric() # Step 2: Output C code for the Eigen-Coordinate mapping from xx->Cartesian: outstr = """ { // xx_to_Cart for EigenCoordinate """+rfm.get_EigenCoord()+r""" (orig coord = """+CoordSystem_orig+r"""): REAL xx0 = xx[0][i0"""+i012suffix+"""]; REAL xx1 = xx[1][i1"""+i012suffix+"""]; REAL xx2 = xx[2][i2"""+i012suffix+"""];\n"""+ \ outputC([rfm.xx_to_Cart[0],rfm.xx_to_Cart[1],rfm.xx_to_Cart[2]], ["xCart[0]","xCart[1]","xCart[2]"], "returnstring", params="preindent=3")+" }\n" # Step 3: Restore reference_metric::CoordSystem back to the original CoordSystem par.set_parval_from_str("reference_metric::CoordSystem",CoordSystem_orig) rfm.reference_metric() # Step 4: Return EigenCoord xx_to_Cart C code return outstr
def Set_up_CurviBoundaryConditions(Ccodesdir, verbose=True, Cparamspath=os.path.join("../"), enable_copy_of_static_Ccodes=True, BoundaryCondition="QuadraticExtrapolation"): # Step P0: Check that Ccodesdir is not the same as CurviBoundaryConditions/boundary_conditions, # to prevent trusted versions of these C codes from becoming contaminated. if os.path.join(Ccodesdir) == os.path.join("CurviBoundaryConditions", "boundary_conditions"): print( "Error: Tried to output boundary conditions C code into CurviBoundaryConditions/boundary_conditions," " which is not allowed, to prevent trusted versions of these C codes from becoming contaminated." ) sys.exit(1) # Step P1: Create the C codes output directory & copy static CurviBC files # from CurviBoundaryConditions/boundary_conditions to Ccodesdir/ if enable_copy_of_static_Ccodes: cmd.mkdir(os.path.join(Ccodesdir)) # Choosing boundary condition drivers with in NRPy+ # - current options are Quadratic Polynomial Extrapolation for any coordinate system, # and the Sommerfeld boundary condition for only cartesian coordinates if str(BoundaryCondition) == "QuadraticExtrapolation": for file in [ "apply_bcs_curvilinear.h", "BCs_data_structs.h", "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h", "driver_bcstruct.h", "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h" ]: shutil.copy( os.path.join("CurviBoundaryConditions", "boundary_conditions", file), os.path.join(Ccodesdir)) with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"), "a") as file: file.write("\n#include \"apply_bcs_curvilinear.h\"") elif str(BoundaryCondition) == "Sommerfeld": for file in [ "apply_bcs_sommerfeld.h", "BCs_data_structs.h", "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h", "driver_bcstruct.h", "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h" ]: shutil.copy( os.path.join("CurviBoundaryConditions", "boundary_conditions", file), os.path.join(Ccodesdir)) with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"), "a") as file: file.write("\n#include \"apply_bcs_sommerfeld.h\"") elif str(BoundaryCondition) == "QuadraticExtrapolation&Sommerfeld": for file in [ "apply_bcs_curvilinear.h", "apply_bcs_sommerfeld.h", "BCs_data_structs.h", "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h", "driver_bcstruct.h", "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h" ]: shutil.copy( os.path.join("CurviBoundaryConditions", "boundary_conditions", file), os.path.join(Ccodesdir)) with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"), "a") as file: file.write("\n#include \"apply_bcs_sommerfeld.h\"" + "\n#include \"apply_bcs_curvilinear.h\"") else: print( "ERROR: Only Quadratic Polynomial Extrapolation (QuadraticExtrapolation) and Sommerfeld boundary conditions are currently supported\n" ) sys.exit(1) # Step P2: Output correct #include for set_Cparameters.h to # Ccodesdir/boundary_conditions/RELATIVE_PATH__set_Cparameters.h with open(os.path.join(Ccodesdir, "RELATIVE_PATH__set_Cparameters.h"), "w") as file: file.write( "#include \"" + Cparamspath + "/set_Cparameters.h\"\n" ) # #include's may include forward slashes for paths, even in Windows. # Step 0: Set up reference metric in case it hasn't already been set up. # (Doing it twice hurts nothing). rfm.reference_metric() # Step 1: Set unit-vector dot products (=parity) for each of the 10 parity condition types parity = ixp.zerorank1(DIM=10) UnitVectors_inner = ixp.zerorank2() xx0_inbounds, xx1_inbounds, xx2_inbounds = sp.symbols( "xx0_inbounds xx1_inbounds xx2_inbounds", real=True) for i in range(3): for j in range(3): UnitVectors_inner[i][j] = rfm.UnitVectors[i][j].subs( rfm.xx[0], xx0_inbounds).subs(rfm.xx[1], xx1_inbounds).subs(rfm.xx[2], xx2_inbounds) # Type 0: scalar parity[0] = sp.sympify(1) # Type 1: i0-direction vector or one-form # Type 2: i1-direction vector or one-form # Type 3: i2-direction vector or one-form for i in range(3): for Type in range(1, 4): parity[Type] += rfm.UnitVectors[Type - 1][i] * UnitVectors_inner[Type - 1][i] # Type 4: i0i0-direction rank-2 tensor # parity[4] = parity[1]*parity[1] # Type 5: i0i1-direction rank-2 tensor # Type 6: i0i2-direction rank-2 tensor # Type 7: i1i1-direction rank-2 tensor # Type 8: i1i2-direction rank-2 tensor # Type 9: i2i2-direction rank-2 tensor count = 4 for i in range(3): for j in range(i, 3): parity[count] = parity[i + 1] * parity[j + 1] count = count + 1 lhs_strings = [] for i in range(10): lhs_strings.append("parity[" + str(i) + "]") outputC( parity, lhs_strings, os.path.join(Ccodesdir, "parity_conditions_symbolic_dot_products.h")) # Step 2.a: Generate Ccodesdir/gridfunction_defines.h file, # containing human-readable gridfunction aliases evolved_variables_list, auxiliary_variables_list, auxevol_variables_list = gri.output__gridfunction_defines_h__return_gf_lists( Ccodesdir) # Step 2.b: set the parity conditions on all gridfunctions in gf_list, # based on how many digits are at the end of their names def set_parity_types(list_of_gf_names): parity_type = [] for name in list_of_gf_names: for gf in gri.glb_gridfcs_list: if gf.name == name: parity_type__orig_len = len(parity_type) if gf.DIM < 3 or gf.DIM > 4: print( "Error: Cannot currently specify parity conditions on gridfunctions with DIM<3 or >4." ) sys.exit(1) if gf.rank == 0: parity_type.append(0) elif gf.rank == 1: if gf.DIM == 3: parity_type.append( int(gf.name[-1]) + 1 ) # = 1 for e.g., beta^0; = 2 for e.g., beta^1, etc. elif gf.DIM == 4: parity_type.append( int(gf.name[-1]) ) # = 0 for e.g., b4^0; = 1 for e.g., beta^1, etc. elif gf.rank == 2: if gf.DIM == 3: # element of a list; a[-2] the # second-to-last element, etc. idx0 = gf.name[-2] idx1 = gf.name[-1] if idx0 == "0" and idx1 == "0": parity_type.append(4) elif (idx0 == "0" and idx1 == "1") or (idx0 == "1" and idx1 == "0"): parity_type.append(5) elif (idx0 == "0" and idx1 == "2") or (idx0 == "2" and idx1 == "0"): parity_type.append(6) elif idx0 == "1" and idx1 == "1": parity_type.append(7) elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"): parity_type.append(8) elif idx0 == "2" and idx1 == "2": parity_type.append(9) elif gf.DIM == 4: idx0 = gf.name[-2] idx1 = gf.name[-1] # g4DD00 = g_{tt} : parity type = 0 # g4DD01 = g_{tx} : parity type = 1 # g4DD02 = g_{ty} : parity type = 2 # g4DD0a = g_{ta} : parity type = a if idx0 == "0": parity_type.append(int(idx1)) elif idx1 == "0": parity_type.append(int(idx0)) if idx0 == "1" and idx1 == "1": parity_type.append(4) elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"): parity_type.append(5) elif (idx0 == "1" and idx1 == "3") or (idx0 == "3" and idx1 == "1"): parity_type.append(6) elif idx0 == "2" and idx1 == "2": parity_type.append(7) elif (idx0 == "2" and idx1 == "3") or (idx0 == "3" and idx1 == "2"): parity_type.append(8) elif idx0 == "3" and idx1 == "3": parity_type.append(9) if len(parity_type) == parity_type__orig_len: print( "Error: Could not figure out parity type for " + gf.gftype + " gridfunction: " + gf.name, gf.DIM, gf.name[-2], gf.name[-1], gf.rank) sys.exit(1) if len(parity_type) != len(list_of_gf_names): print( "Error: For some reason the length of the parity types list did not match the length of the gf list." ) sys.exit(1) return parity_type evol_parity_type = set_parity_types(evolved_variables_list) aux_parity_type = set_parity_types(auxiliary_variables_list) auxevol_parity_type = set_parity_types(auxevol_variables_list) # Step 2.c: Output all gridfunctions to Ccodesdir+"/gridfunction_defines.h" # ... then append to the file the parity type for each gridfunction. with open(os.path.join(Ccodesdir, "gridfunction_defines.h"), "a") as file: file.write("\n\n/* PARITY TYPES FOR ALL GRIDFUNCTIONS.\n") file.write( " SEE \"Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb\" FOR DEFINITIONS. */\n" ) if len(evolved_variables_list) > 0: file.write("const int8_t evol_gf_parity[" + str(len(evolved_variables_list)) + "] = { ") for i in range(len(evolved_variables_list) - 1): file.write(str(evol_parity_type[i]) + ", ") file.write( str(evol_parity_type[len(evolved_variables_list) - 1]) + " };\n") if len(auxiliary_variables_list) > 0: file.write("const int8_t aux_gf_parity[" + str(len(auxiliary_variables_list)) + "] = { ") for i in range(len(auxiliary_variables_list) - 1): file.write(str(aux_parity_type[i]) + ", ") file.write( str(aux_parity_type[len(auxiliary_variables_list) - 1]) + " };\n") if len(auxevol_variables_list) > 0: file.write("const int8_t auxevol_gf_parity[" + str(len(auxevol_variables_list)) + "] = { ") for i in range(len(auxevol_variables_list) - 1): file.write(str(auxevol_parity_type[i]) + ", ") file.write( str(auxevol_parity_type[len(auxevol_variables_list) - 1]) + " };\n") if verbose == True: import textwrap wrapper = textwrap.TextWrapper(initial_indent="", subsequent_indent=" ", width=75) def print_parity_list(gf_type, variable_names, parity_types): outstr = "" if len(variable_names) != 0: outstr += gf_type + " parity: ( " for i in range(len(variable_names)): outstr += variable_names[i] + ":" + str(parity_types[i]) if i != len(variable_names) - 1: outstr += ", " outstr += " )" print(wrapper.fill(outstr)) print_parity_list("Evolved", evolved_variables_list, evol_parity_type) print_parity_list("Auxiliary", auxiliary_variables_list, aux_parity_type) print_parity_list("AuxEvol", auxevol_variables_list, auxevol_parity_type) # Step 3: Find the Eigen-Coordinate and set up the Eigen-Coordinate's reference metric: CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem") par.set_parval_from_str("reference_metric::CoordSystem", rfm.get_EigenCoord()) rfm.reference_metric() # Step 4: Output C code for the Eigen-Coordinate mapping from xx->Cartesian: rfm.xxCart_h("EigenCoord_xxCart", os.path.join(Cparamspath, "set_Cparameters.h"), os.path.join(Ccodesdir, "EigenCoord_xxCart.h")) # Step 5: Output the Eigen-Coordinate mapping from Cartesian->xx: # Step 5.a: Sanity check: First make sure that rfm.Cart_to_xx has been set. Error out if not! if rfm.Cart_to_xx[0] == 0 or rfm.Cart_to_xx[1] == 0 or rfm.Cart_to_xx[ 2] == 0: print( "ERROR: rfm.Cart_to_xx[], which maps Cartesian -> xx, has not been set for" ) print(" reference_metric::CoordSystem = " + par.parval_from_str("reference_metric::CoordSystem")) print( " Boundary conditions in curvilinear coordinates REQUIRE this be set." ) sys.exit(1) # Step 5.b: Output C code for the Eigen-Coordinate mapping from Cartesian->xx: outputC([rfm.Cart_to_xx[0], rfm.Cart_to_xx[1], rfm.Cart_to_xx[2]], [ "Cart_to_xx0_inbounds", "Cart_to_xx1_inbounds", "Cart_to_xx2_inbounds" ], os.path.join(Ccodesdir, "EigenCoord_Cart_to_xx.h")) # Step 6: Restore reference_metric::CoordSystem back to the original CoordSystem par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem_orig) rfm.reference_metric()
def Set_up_CurviBoundaryConditions(outdir="CurviBoundaryConditions/", verbose=True): # Step 0: Set up reference metric in case it hasn't already been set up. # (Doing it twice hurts nothing). rfm.reference_metric() # Step 1: Set unit-vector dot products (=parity) for each of the 10 parity condition types parity = ixp.zerorank1(DIM=10) UnitVectors_inner = ixp.zerorank2() xx0_inbounds, xx1_inbounds, xx2_inbounds = sp.symbols( "xx0_inbounds xx1_inbounds xx2_inbounds", real=True) for i in range(3): for j in range(3): UnitVectors_inner[i][j] = rfm.UnitVectors[i][j].subs( rfm.xx[0], xx0_inbounds).subs(rfm.xx[1], xx1_inbounds).subs(rfm.xx[2], xx2_inbounds) # Type 0: scalar parity[0] = sp.sympify(1) # Type 1: i0-direction vector or one-form # Type 2: i1-direction vector or one-form # Type 3: i2-direction vector or one-form for i in range(3): for Type in range(1, 4): parity[Type] += rfm.UnitVectors[Type - 1][i] * UnitVectors_inner[Type - 1][i] # Type 4: i0i0-direction rank-2 tensor # parity[4] = parity[1]*parity[1] # Type 5: i0i1-direction rank-2 tensor # Type 6: i0i2-direction rank-2 tensor # Type 7: i1i1-direction rank-2 tensor # Type 8: i1i2-direction rank-2 tensor # Type 9: i2i2-direction rank-2 tensor count = 4 for i in range(3): for j in range(i, 3): parity[count] = parity[i + 1] * parity[j + 1] count = count + 1 lhs_strings = [] for i in range(10): lhs_strings.append("parity[" + str(i) + "]") outputC(parity, lhs_strings, outdir + "parity_conditions_symbolic_dot_products.h") # Step 2.a: Generate outdir+gridfunction_defines.h file, # containing human-readable gridfunction aliases evolved_variables_list, auxiliary_variables_list, auxevol_variables_list = gri.output__gridfunction_defines_h__return_gf_lists( outdir) # Step 2.b: set the parity conditions on all gridfunctions in gf_list, # based on how many digits are at the end of their names def set_parity_types(list_of_gf_names): parity_type = [] for name in list_of_gf_names: for gf in gri.glb_gridfcs_list: if gf.name == name: parity_type__orig_len = len(parity_type) if gf.DIM < 3 or gf.DIM > 4: print( "Error: Cannot currently specify parity conditions on gridfunctions with DIM<3 or >4." ) sys.exit(1) if gf.rank == 0: parity_type.append(0) elif gf.rank == 1: if gf.DIM == 3: parity_type.append( int(gf.name[-1]) + 1 ) # = 1 for e.g., beta^0; = 2 for e.g., beta^1, etc. elif gf.DIM == 4: parity_type.append( int(gf.name[-1]) ) # = 0 for e.g., b4^0; = 1 for e.g., beta^1, etc. elif gf.rank == 2: if gf.DIM == 3: # element of a list; a[-2] the # second-to-last element, etc. idx0 = gf.name[-2] idx1 = gf.name[-1] if idx0 == "0" and idx1 == "0": parity_type.append(4) elif (idx0 == "0" and idx1 == "1") or (idx0 == "1" and idx1 == "0"): parity_type.append(5) elif (idx0 == "0" and idx1 == "2") or (idx0 == "2" and idx1 == "0"): parity_type.append(6) elif idx0 == "1" and idx1 == "1": parity_type.append(7) elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"): parity_type.append(8) elif idx0 == "2" and idx1 == "2": parity_type.append(9) elif gf.DIM == 4: idx0 = gf.name[-2] idx1 = gf.name[-1] # g4DD00 = g_{tt} : parity type = 0 # g4DD01 = g_{tx} : parity type = 1 # g4DD02 = g_{ty} : parity type = 2 # g4DD0a = g_{ta} : parity type = a if idx0 == "0": parity_type.append(int(idx1)) elif idx1 == "0": parity_type.append(int(idx0)) if idx0 == "1" and idx1 == "1": parity_type.append(4) elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"): parity_type.append(5) elif (idx0 == "1" and idx1 == "3") or (idx0 == "3" and idx1 == "1"): parity_type.append(6) elif idx0 == "2" and idx1 == "2": parity_type.append(7) elif (idx0 == "2" and idx1 == "3") or (idx0 == "3" and idx1 == "2"): parity_type.append(8) elif idx0 == "3" and idx1 == "3": parity_type.append(9) if len(parity_type) == parity_type__orig_len: print( "Error: Could not figure out parity type for " + gf.gftype + " gridfunction: " + gf.name, gf.DIM, gf.name[-2], gf.name[-1], gf.rank) sys.exit(1) if len(parity_type) != len(list_of_gf_names): print( "Error: For some reason the length of the parity types list did not match the length of the gf list." ) sys.exit(1) return parity_type evol_parity_type = set_parity_types(evolved_variables_list) aux_parity_type = set_parity_types(auxiliary_variables_list) auxevol_parity_type = set_parity_types(auxevol_variables_list) # Step 2.c: Output all gridfunctions to outdir+"/gridfunction_defines.h" # ... then append to the file the parity type for each gridfunction. with open(outdir + "/gridfunction_defines.h", "a") as file: file.write("\n\n/* PARITY TYPES FOR ALL GRIDFUNCTIONS.\n") file.write( " SEE \"Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb\" FOR DEFINITIONS. */\n" ) if len(evolved_variables_list) > 0: file.write("const int8_t evol_gf_parity[" + str(len(evolved_variables_list)) + "] = { ") for i in range(len(evolved_variables_list) - 1): file.write(str(evol_parity_type[i]) + ", ") file.write( str(evol_parity_type[len(evolved_variables_list) - 1]) + " };\n") if len(auxiliary_variables_list) > 0: file.write("const int8_t aux_gf_parity[" + str(len(auxiliary_variables_list)) + "] = { ") for i in range(len(auxiliary_variables_list) - 1): file.write(str(aux_parity_type[i]) + ", ") file.write( str(aux_parity_type[len(auxiliary_variables_list) - 1]) + " };\n") if len(auxevol_variables_list) > 0: file.write("const int8_t auxevol_gf_parity[" + str(len(auxevol_variables_list)) + "] = { ") for i in range(len(auxevol_variables_list) - 1): file.write(str(auxevol_parity_type[i]) + ", ") file.write( str(auxevol_parity_type[len(auxevol_variables_list) - 1]) + " };\n") if verbose == True: for i in range(len(evolved_variables_list)): print("Evolved gridfunction \"" + evolved_variables_list[i] + "\" has parity type " + str(evol_parity_type[i]) + ".") for i in range(len(auxiliary_variables_list)): print("Auxiliary gridfunction \"" + auxiliary_variables_list[i] + "\" has parity type " + str(aux_parity_type[i]) + ".") for i in range(len(auxevol_variables_list)): print("AuxEvol gridfunction \"" + auxevol_variables_list[i] + "\" has parity type " + str(auxevol_parity_type[i]) + ".") # Step 3: Find the Eigen-Coordinate and set up the Eigen-Coordinate's reference metric: CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem") par.set_parval_from_str("reference_metric::CoordSystem", rfm.get_EigenCoord()) rfm.reference_metric() # Step 4: Output C code for the Eigen-Coordinate mapping from xx->Cartesian: rfm.xxCart_h("EigenCoord_xxCart", "../set_Cparameters.h", outdir + "EigenCoord_xxCart.h") # Step 5: Output the Eigen-Coordinate mapping from Cartesian->xx: # Step 5.a: Sanity check: First make sure that rfm.Cart_to_xx has been set. Error out if not! if rfm.Cart_to_xx[0] == 0 or rfm.Cart_to_xx[1] == 0 or rfm.Cart_to_xx[ 2] == 0: print( "ERROR: rfm.Cart_to_xx[], which maps Cartesian -> xx, has not been set for" ) print(" reference_metric::CoordSystem = " + par.parval_from_str("reference_metric::CoordSystem")) print( " Boundary conditions in curvilinear coordinates REQUIRE this be set." ) sys.exit(1) # Step 5.b: Output C code for the Eigen-Coordinate mapping from Cartesian->xx: outputC([rfm.Cart_to_xx[0], rfm.Cart_to_xx[1], rfm.Cart_to_xx[2]], [ "Cart_to_xx0_inbounds", "Cart_to_xx1_inbounds", "Cart_to_xx2_inbounds" ], outdir + "EigenCoord_Cart_to_xx.h") # Step 6: Restore reference_metric::CoordSystem back to the original CoordSystem par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem_orig) rfm.reference_metric()