def __init__(self, romDict): self.numericalJacob = catchInput(romDict, "numericalJacob", False) if self.numericalJacob: self.fdStep = catchInput(romDict, "fdStep", fdStepDefault) self.encoder = catchInput(romDict, "encoderROM", False) # load models # check model dimensions against solDomain # do a test run just to make sure everything works okay pass
def __init__(self, paramDict): super().__init__(paramDict) self.timeType = "implicit" self.subiterMax = catchInput(paramDict, "subiterMax", const.subiterMaxImpDefault) self.resTol = catchInput(paramDict, "resTol", const.l2ResTolDefault) # dual time-stepping, robustness controls self.dualTime = catchInput(paramDict, "dualTime", True) self.dtau = catchInput(paramDict, "dtau", const.dtauDefault) if (self.dualTime): self.adaptDTau = catchInput(paramDict, "adaptDTau", False) else: self.adaptDTau = False self.CFL = catchInput( paramDict, "CFL", const.CFLDefault) # reference CFL for advective control of dtau self.VNN = catchInput( paramDict, "VNN", const.VNNDefault ) # von Neumann number for diffusion control of dtau self.refConst = catchInput(paramDict, "refConst", [None]) # constants for limiting dtau self.relaxConst = catchInput(paramDict, "relaxConst", [None]) #
def __init__(self, visID, solDomain, solver): self.visType = "probe" self.visID = visID self.xLabel = "t (s)" self.probeNum = catchInput(solver.paramDict, "probeNum"+str(self.visID), -2) - 1 assert (self.probeNum >= 0 ), "Must provide positive integer probe number for probe"+str(self.visID) assert (self.probeNum < solver.numProbes), "probeNum"+str(self.visID)+" must correspond to a valid probe" super().__init__(solDomain, solver) # image file on disk visName = "" for visVar in self.visVar: visName += "_"+visVar figName = "probe" + visName + "_" + str(self.probeNum) + "_" + solver.simType + ".png" self.figFile = os.path.join(const.imageOutputDir, figName) # check that requested variables are being probed for visVar in self.visVar: assert (visVar in solver.probeVars), "Must probe "+visVar+" to plot it"
def __init__(self): # input parameters from solverParams.inp paramFile = os.path.join(const.workingDir, const.paramInputs) paramDict = readInputFile(paramFile) self.paramDict = paramDict # spatial domain meshFile = str(paramDict["meshFile"]) # mesh properties file (string) meshDict = readInputFile(meshFile) self.mesh = mesh.mesh(meshDict) # TODO: selection for different meshes, when implemented # meshType = str(meshDict["meshType"]) # if (meshType == "uniform"): # self.mesh = mesh.uniformMesh(meshDict) # else: # raise ValueError("Invalid choice of meshType: " + meshType) # initial condition file try: self.initFile = str(paramDict["initFile"]) except: self.initFile = None # temporal discretization self.dt = float(paramDict["dt"]) # physical time step self.timeScheme = str(paramDict["timeScheme"]) self.runSteady = catchInput(paramDict, "runSteady", False) # run "steady" simulation self.numSteps = int( paramDict["numSteps"]) # total number of physical time iterations self.iter = 1 # iteration number for current run self.solTime = 0.0 # physical time self.timeIter = 1 # physical time iteration number if self.runSteady: self.steadyTol = catchInput( paramDict, "steadyTol", const.l2SteadyTolDefault) # threshold on convergence # spatial discretization parameters self.spaceScheme = catchInput( paramDict, "spaceScheme", "roe") # spatial discretization scheme (string) self.spaceOrder = catchInput( paramDict, "spaceOrder", 1) # spatial discretization order of accuracy (int) self.gradLimiter = catchInput( paramDict, "gradLimiter", "") # gradient limiter for higher-order face reconstructions self.viscScheme = catchInput(paramDict, "viscScheme", 0) # 0 for inviscid, 1 for viscous # restart files # TODO: could move this to solutionDomain, not terribly necessary self.saveRestarts = catchInput(paramDict, "saveRestarts", False) # whether to save restart files if self.saveRestarts: self.restartInterval = catchInput( paramDict, "restartInterval", 100) # number of steps between restart file saves self.numRestarts = catchInput( paramDict, "numRestarts", 20) # number of restart files to keep saved self.restartIter = 1 # file number counter self.initFromRestart = catchInput(paramDict, "initFromRestart", False) if ((self.initFile == None) and (not self.initFromRestart)): try: self.icParamsFile = str(paramDict["icParamsFile"]) except: raise KeyError( "If not providing IC profile or restarting from restart file, must provide icParamsFile" ) # unsteady output self.outInterval = catchInput( paramDict, "outInterval", 1) # iteration interval to save data (int) self.primOut = catchInput( paramDict, "primOut", True) # whether to save the primitive variables self.consOut = catchInput( paramDict, "consOut", False) # whether to save the conservative variables self.sourceOut = catchInput( paramDict, "sourceOut", False) # whether to save the species source term self.RHSOut = catchInput(paramDict, "RHSOut", False) # whether to save the RHS vector self.numSnaps = int(self.numSteps / self.outInterval) # misc self.velAdd = catchInput(paramDict, "velAdd", 0.0) self.resNormPrim = catchInput(paramDict, "steadyNorm", [None]) self.sourceOn = catchInput(paramDict, "sourceOn", True) self.solveFailed = False # visualization self.numProbes = 0 self.probeVars = [] # ROM flag self.calcROM = catchInput(paramDict, "calcROM", False) if not self.calcROM: self.simType = "FOM" else: self.simType = "ROM" self.romInputs = os.path.join(const.workingDir, const.romInputs)
def loadHyperReduc(self, solDomain, solver): """ Loads direct sampling indices and determines cell indices for calculating fluxes and gradients """ raise ValueError( "Hyper-reduction temporarily out of commission for development") # TODO: add some explanations for what each index array accomplishes # load and check sample points sampFile = catchInput(self.romDict, "sampFile", "") assert (sampFile != ""), "Must supply sampFile if performing hyper-reduction" sampFile = os.path.join(self.modelDir, sampFile) assert (os.path.isfile(sampFile)), ("Could not find sampFile at " + sampFile) # NOTE: assumed that sample indices are zero-indexed solDomain.directSampIdxs = np.load(sampFile).flatten() solDomain.directSampIdxs = (np.sort(solDomain.directSampIdxs)).astype( np.int32) solDomain.numSampCells = len(solDomain.directSampIdxs) assert (solDomain.numSampCells <= solver.mesh.numCells ), "Cannot supply more sampling points than cells in domain." assert (np.amin(solDomain.directSampIdxs) >= 0), "Sampling indices must be non-negative integers" assert ( np.amax(solDomain.directSampIdxs) < solver.mesh.numCells ), "Sampling indices must be less than the number of cells in the domain" assert (len(np.unique( solDomain.directSampIdxs)) == solDomain.numSampCells ), "Sampling indices must be unique" # TODO: should probably shunt these over to a function for when indices get updated in adaptive method # compute indices for inviscid flux calculations # NOTE: have to account for fact that boundary cells are prepended/appended solDomain.fluxSampLIdxs = np.zeros(2 * solDomain.numSampCells, dtype=np.int32) solDomain.fluxSampLIdxs[0::2] = solDomain.directSampIdxs solDomain.fluxSampLIdxs[1::2] = solDomain.directSampIdxs + 1 solDomain.fluxSampRIdxs = np.zeros(2 * solDomain.numSampCells, dtype=np.int32) solDomain.fluxSampRIdxs[0::2] = solDomain.directSampIdxs + 1 solDomain.fluxSampRIdxs[1::2] = solDomain.directSampIdxs + 2 # eliminate repeated indices solDomain.fluxSampLIdxs = np.unique(solDomain.fluxSampLIdxs) solDomain.fluxSampRIdxs = np.unique(solDomain.fluxSampRIdxs) solDomain.numFluxFaces = len(solDomain.fluxSampLIdxs) # Roe average if (solver.spaceScheme == "roe"): zerosProf = np.zeros( (solDomain.gasModel.numEqs, solDomain.numFluxFaces), dtype=const.realType) solDomain.solAve = solutionPhys(solDomain, zerosProf, zerosProf, solDomain.numFluxFaces, solver) # to slice flux when calculating RHS solDomain.fluxRHSIdxs = np.zeros(solDomain.numSampCells, np.int32) for i in range(1, solDomain.numSampCells): if (solDomain.directSampIdxs[i] == ( solDomain.directSampIdxs[i - 1] + 1)): solDomain.fluxRHSIdxs[i] = solDomain.fluxRHSIdxs[i - 1] + 1 else: solDomain.fluxRHSIdxs[i] = solDomain.fluxRHSIdxs[i - 1] + 2 # compute indices for gradient calculations # NOTE: also need to account for prepended/appended boundary cells # TODO: generalize for higher-order schemes if (solver.spaceOrder > 1): if (solver.spaceOrder == 2): solDomain.gradIdxs = np.concatenate( (solDomain.directSampIdxs + 1, solDomain.directSampIdxs, solDomain.directSampIdxs + 2)) solDomain.gradIdxs = np.unique(solDomain.gradIdxs) # exclude left neighbor of inlet, right neighbor of outlet if (solDomain.gradIdxs[0] == 0): solDomain.gradIdxs = solDomain.gradIdxs[1:] if (solDomain.gradIdxs[-1] == (solver.mesh.numCells + 1)): solDomain.gradIdxs = solDomain.gradIdxs[:-1] solDomain.numGradCells = len(solDomain.gradIdxs) # neighbors of gradient cells solDomain.gradNeighIdxs = np.concatenate( (solDomain.gradIdxs - 1, solDomain.gradIdxs + 1)) solDomain.gradNeighIdxs = np.unique(solDomain.gradNeighIdxs) # exclude left neighbor of inlet, right neighbor of outlet if (solDomain.gradNeighIdxs[0] == -1): solDomain.gradNeighIdxs = solDomain.gradNeighIdxs[1:] if (solDomain.gradNeighIdxs[-1] == (solver.mesh.numCells + 2)): solDomain.gradNeighIdxs = solDomain.gradNeighIdxs[:-1] # indices of gradIdxs in gradNeighIdxs _, _, solDomain.gradNeighExtract = np.intersect1d( solDomain.gradIdxs, solDomain.gradNeighIdxs, return_indices=True) # indices of gradIdxs in fluxSampLIdxs and fluxSampRIdxs, and vice versa _, solDomain.gradLExtract, solDomain.fluxLExtract = np.intersect1d( solDomain.gradIdxs, solDomain.fluxSampLIdxs, return_indices=True) _, solDomain.gradRExtract, solDomain.fluxRExtract = np.intersect1d( solDomain.gradIdxs, solDomain.fluxSampRIdxs, return_indices=True) else: raise ValueError( "Sampling for higher-order schemes not implemented yet") # copy indices for ease of use self.numSampCells = solDomain.numSampCells self.directSampIdxs = solDomain.directSampIdxs # paths to hyper-reduction files (unpacked later) hyperReducFiles = self.romDict["hyperReducFiles"] self.hyperReducFiles = [None] * self.numModels assert (len(hyperReducFiles) == self.numModels ), "Must provide hyperReducFiles for each model" for modelIdx in range(self.numModels): inFile = os.path.join(self.modelDir, hyperReducFiles[modelIdx]) assert (os.path.isfile(inFile) ), "Could not find hyper-reduction file at " + inFile self.hyperReducFiles[modelIdx] = inFile # load hyper reduction dimensions and check validity self.hyperReducDims = catchList(self.romDict, "hyperReducDims", [0], lenHighest=self.numModels) for i in self.hyperReducDims: assert (i > 0), "hyperReducDims must contain positive integers" if (self.numModels == 1): assert ( len(self.hyperReducDims) == 1 ), "Must provide only one value of hyperReducDims when numModels = 1" assert (self.hyperReducDims[0] > 0), "hyperReducDims must contain positive integers" else: if (len(self.hyperReducDims) == self.numModels): pass elif (len(self.hyperReducDims) == 1): print( "Only one value provided in hyperReducDims, applying to all models" ) sleep(1.0) self.hyperReducDims = [self.hyperReducDims[0]] * self.numModels else: raise ValueError( "Must provide either numModels or 1 entry in hyperReducDims" )
def __init__(self, solDomain, solver): romDict = readInputFile(solver.romInputs) self.romDict = romDict # load model parameters self.romMethod = str(romDict["romMethod"]) self.numModels = int(romDict["numModels"]) self.latentDims = catchList(romDict, "latentDims", [0], lenHighest=self.numModels) modelVarIdxs = catchList(romDict, "modelVarIdxs", [[-1]], lenHighest=self.numModels) # check model parameters for i in self.latentDims: assert (i > 0), "latentDims must contain positive integers" if (self.numModels == 1): assert ( len(self.latentDims) == 1 ), "Must provide only one value of latentDims when numModels = 1" assert (self.latentDims[0] > 0), "latentDims must contain positive integers" else: if (len(self.latentDims) == self.numModels): pass elif (len(self.latentDims) == 1): print( "Only one value provided in latentDims, applying to all models" ) sleep(1.0) self.latentDims = [self.latentDims[0]] * self.numModels else: raise ValueError( "Must provide either numModels or 1 entry in latentDims") # load and check modelVarIdxs for modelIdx in range(self.numModels): assert (modelVarIdxs[modelIdx][0] != -1 ), "modelVarIdxs input incorrectly, probably too few lists" assert (len(modelVarIdxs) == self.numModels ), "Must specify modelVarIdxs for every model" modelVarSum = 0 for modelIdx in range(self.numModels): modelVarSum += len(modelVarIdxs[modelIdx]) for modelVarIdx in modelVarIdxs[modelIdx]: assert (modelVarIdx >= 0), "modelVarIdxs must be non-negative integers" assert ( modelVarIdx < solDomain.gasModel.numEqs ), "modelVarIdxs must less than the number of governing equations" assert (modelVarSum == solDomain.gasModel.numEqs), ( "Must specify as many modelVarIdxs entries as governing equations (" + str(modelVarSum) + " != " + str(solDomain.gasModel.numEqs) + ")") modelVarIdxsOneList = sum(modelVarIdxs, []) assert (len(modelVarIdxsOneList) == len(set(modelVarIdxsOneList)) ), "All entries in modelVarIdxs must be unique" self.modelVarIdxs = modelVarIdxs # load and check model input locations self.modelDir = str(romDict["modelDir"]) modelFiles = romDict["modelFiles"] self.modelFiles = [None] * self.numModels assert (len(modelFiles) == self.numModels ), "Must provide modelFiles for each model" for modelIdx in range(self.numModels): inFile = os.path.join(self.modelDir, modelFiles[modelIdx]) assert (os.path.isfile(inFile) ), "Could not find model file at " + inFile self.modelFiles[modelIdx] = inFile # load standardization profiles, if they are required self.normSubConsIn = catchList(romDict, "normSubConsIn", [""]) self.normFacConsIn = catchList(romDict, "normFacConsIn", [""]) self.centConsIn = catchList(romDict, "centConsIn", [""]) self.normSubPrimIn = catchList(romDict, "normSubPrimIn", [""]) self.normFacPrimIn = catchList(romDict, "normFacPrimIn", [""]) self.centPrimIn = catchList(romDict, "centPrimIn", [""]) # load low-dimensional initial condition state, if desired self.loadInitCode(romDict) self.setModelFlags() self.adaptiveROM = catchInput(romDict, "adaptiveROM", False) # set up hyper-reduction, if necessary self.hyperReduc = catchInput(romDict, "hyperReduc", False) if (self.isIntrusive and self.hyperReduc): self.loadHyperReduc(solDomain, solver) # initialize models for domain self.modelList = [None] * self.numModels for modelIdx in range(self.numModels): if (self.romMethod == "linearGalerkinProj"): self.modelList[modelIdx] = linearGalerkinProj( modelIdx, self, solver, solDomain) elif (self.romMethod == "linearLSPGProj"): raise ValueError("linearLSPGProj ROM not implemented yet") elif (self.romMethod == "linearSPLSVTProj"): raise ValueError("linearSPLSVTProj ROM not implemented yet") elif (self.romMethod == "autoencoderGalerkinProjTF"): raise ValueError( "autoencoderGalerkinProjTF ROM not implemented yet") elif (self.romMethod == "autoencoderLSPGProjTF"): raise ValueError( "autoencoderLSPGProjTF ROM not implemented yet") elif (self.romMethod == "autoencoderSPLSVTProjTF"): raise ValueError( "autoencoderSPLSVTProjTF ROM not implemented yet") elif (self.romMethod == "liftAndLearn"): raise ValueError("liftAndLearn ROM not implemented yet") elif (self.romMethod == "tcnNonintrusive"): raise ValueError("tcnNonintrusive ROM not implemented yet") else: raise ValueError("Invalid ROM method name: " + self.romMethod) # initialize state if self.initROMFromFile[modelIdx]: self.modelList[modelIdx].initFromCode(self.code0[modelIdx], solDomain, solver) else: self.modelList[modelIdx].initFromSol(solDomain, solver) solDomain.solInt.updateState(fromCons=self.targetCons) # get time integrator, if necessary # TODO: timeScheme should be specific to the romDomain, not the solver if self.hasTimeIntegrator: self.timeIntegrator = getTimeIntegrator(solver.timeScheme, solver.paramDict) # initialize code history # TODO: this is necessary for non-time-integrated methods, e.g. TCN for model in self.modelList: model.codeHist = [model.code.copy() ] * (self.timeIntegrator.timeOrder + 1) else: self.timeIntegrator = None # TODO: this might be pointless
def setModelFlags(self): """ Set universal ROM method flags that dictate various execution behaviors """ self.hasTimeIntegrator = False # whether the model uses numerical time integration to advance the solution self.isIntrusive = False # whether the model requires access to the ODE RHS calculation self.targetCons = False # whether the ROM models the conservative variables self.targetPrim = False # whether the ROM models the primitive variables self.hasConsNorm = False # whether the model must load conservative variable normalization profiles self.hasConsCent = False # whether the model must load conservative variable centering profile self.hasPrimNorm = False # whether the model must load primitive variable normalization profiles self.hasPrimCent = False # whether the model must load primitive variable centering profile if (self.romMethod == "linearGalerkinProj"): self.hasTimeIntegrator = True self.isIntrusive = True self.targetCons = True self.hasConsNorm = True self.hasConsCent = True elif (self.romMethod == "linearLSPGProj"): self.hasTimeIntegrator = True self.isIntrusive = True self.targetCons = True self.hasConsNorm = True self.hasConsCent = True elif (self.romMethod == "linearSPLSVTProj"): self.hasTimeIntegrator = True self.isIntrusive = True self.targetPrim = True self.hasConsNorm = True self.hasPrimNorm = True self.hasPrimCent = True elif (self.romMethod == "autoencoderGalerkinProjTF"): self.hasTimeIntegrator = True self.isIntrusive = True self.targetCons = True self.hasConsNorm = True self.hasConsCent = True elif (self.romMethod == "autoencoderLSPGProjTF"): self.hasTimeIntegrator = True self.isIntrusive = True self.targetCons = True self.hasConsNorm = True self.hasConsCent = True elif (self.romMethod == "autoencoderSPLSVTProjTF"): self.hasTimeIntegrator = True self.isIntrusive = True self.targetPrim = True self.hasConsNorm = True self.hasPrimNorm = True self.hasPrimCent = True elif (self.romMethod == "liftAndLearn"): # TODO: the cons/prim dichotomy doesn't work for lifted variables self.hasTimeIntegrator = True raise ValueError( "Finalize settings of method parameters for lift and learn") elif (self.romMethod == "tcnNonintrusive"): # TODO: TCN does not **need** to target one or the other self.targetCons = catchInput(self.romDict, "targetCons", False) self.targetPrim = catchInput(self.romDict, "targetPrim", False) else: raise ValueError("Invalid ROM method name: " + self.romMethod) # TODO: not strictly true for the non-intrusive models assert ( self.targetCons != self.targetPrim ), "Model must target either the primitive or conservative variables"
def __init__(self, solDomain, solver): paramDict = solver.paramDict self.visShow = catchInput(paramDict, "visShow", True) self.visSave = catchInput(paramDict, "visSave", False) self.visInterval = catchInput(paramDict, "visInterval", 1) self.niceVis = catchInput(paramDict, "niceVis", False) # if not saving or showing, don't even draw the plots self.visDraw = True if ((not self.visShow) and (not self.visSave)): self.visDraw = False return # count number of visualizations requested self.numVisPlots = 0 plotCount = True while plotCount: try: keyName = "visType" + str(self.numVisPlots + 1) plotType = str(paramDict[keyName]) # TODO: should honestly just fail for incorrect input assert (plotType in [ "field", "probe", "residual" ]), (keyName + " must be either \"field\", \"probe\", or \"residual\"") self.numVisPlots += 1 except: plotCount = False if (self.numVisPlots == 0): print("WARNING: No visualization plots selected...") sleep(1.0) self.visList = [None] * self.numVisPlots # initialize each figure object for visIdx in range(1, self.numVisPlots + 1): visType = str(paramDict["visType" + str(visIdx)]) if (visType == "field"): self.visList[visIdx - 1] = fieldPlot(visIdx, self.visInterval, solDomain, solver) elif (visType == "probe"): self.visList[visIdx - 1] = probePlot(visIdx, solDomain, solver) elif (visType == "residual"): raise ValueError("Residual plot not implemented yet") else: raise ValueError("Invalid visualization selection: " + visType) # set plot positions/dimensions if (self.niceVis and self.visShow): try: self.movePlots() except: for vis in self.visList: vis.fig, vis.ax = plt.subplots(nrows=vis.numRows, ncols=vis.numCols, num=vis.visID, figsize=(figWidthDefault, figHeightDefault)) else: for vis in self.visList: vis.fig, vis.ax = plt.subplots(nrows=vis.numRows, ncols=vis.numCols, num=vis.visID, figsize=(figWidthDefault, figHeightDefault))