class OM_DVGEOCOMP(om.ExplicitComponent): def initialize(self): self.options.declare("ffd_file", default=None) self.options.declare("vsp_file", default=None) self.options.declare("vsp_options", default=None) def setup(self): # create the DVGeo object that does the computations if self.options["ffd_file"] is not None: # we are doing an FFD-based DVGeo ffd_file = self.options["ffd_file"] self.DVGeo = DVGeometry(ffd_file) if self.options["vsp_file"] is not None: # we are doing a VSP based DVGeo vsp_file = self.options["vsp_file"] vsp_options = self.options["vsp_options"] self.DVGeo = DVGeometryVSP(vsp_file, comm=self.comm, **vsp_options) self.DVCon = DVConstraints() self.DVCon.setDVGeo(self.DVGeo) self.omPtSetList = [] def compute(self, inputs, outputs): # check for inputs thathave been added but the points have not been added to dvgeo for var in inputs.keys(): # check that the input name matches the convention for points if var[:2] == "x_": # trim the _in and add a "0" to signify that these are initial conditions initial var_out = var[:-3] + "0" if var_out not in self.omPtSetList: self.nom_addPointSet(inputs[var], var_out, add_output=False) # inputs are the geometric design variables self.DVGeo.setDesignVars(inputs) # ouputs are the coordinates of the pointsets we have for ptName in self.DVGeo.points: if ptName in self.omPtSetList: # update this pointset and write it as output outputs[ptName] = self.DVGeo.update(ptName).flatten() # compute the DVCon constraint values constraintfunc = dict() self.DVCon.evalFunctions(constraintfunc, includeLinear=True) comm = self.comm if comm.rank == 0: for constraintname in constraintfunc: outputs[constraintname] = constraintfunc[constraintname] # we ran a compute so the inputs changed. update the dvcon jac # next time the jacvec product routine is called self.update_jac = True def nom_add_discipline_coords(self, discipline, points=None): # TODO remove one of these methods to keep only one method to add pointsets if points is None: # no pointset info is provided, just do a generic i/o. We will add these points during the first compute self.add_input("x_%s_in" % discipline, distributed=True, shape_by_conn=True) self.add_output("x_%s0" % discipline, distributed=True, copy_shape="x_%s_in" % discipline) else: # we are provided with points. we can do the full initialization now self.nom_addPointSet(points, "x_%s0" % discipline, add_output=False) self.add_input("x_%s_in" % discipline, distributed=True, val=points.flatten()) self.add_output("x_%s0" % discipline, distributed=True, val=points.flatten()) def nom_addPointSet(self, points, ptName, add_output=True, **kwargs): # add the points to the dvgeo object self.DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs) self.omPtSetList.append(ptName) if add_output: # add an output to the om component self.add_output(ptName, distributed=True, val=points.flatten()) def nom_add_point_dict(self, point_dict): # add every pointset in the dict, and set the ptset name as the key for k, v in point_dict.items(): self.nom_addPointSet(v, k) def nom_addGeoDVGlobal(self, dvName, value, func): # define the input self.add_input(dvName, distributed=False, shape=len(value)) # call the dvgeo object and add this dv self.DVGeo.addGeoDVGlobal(dvName, value, func) def nom_addGeoDVLocal(self, dvName, axis="y", pointSelect=None): nVal = self.DVGeo.addGeoDVLocal(dvName, axis=axis, pointSelect=pointSelect) self.add_input(dvName, distributed=False, shape=nVal) return nVal def nom_addVSPVariable(self, component, group, parm, **kwargs): # actually add the DV to VSP self.DVGeo.addVariable(component, group, parm, **kwargs) # full name of this DV dvName = "%s:%s:%s" % (component, group, parm) # get the value val = self.DVGeo.DVs[dvName].value.copy() # add the input with the correct value, VSP DVs always have a size of 1 self.add_input(dvName, distributed=False, shape=1, val=val) def nom_addThicknessConstraints2D(self, name, leList, teList, nSpan=10, nChord=10): self.DVCon.addThicknessConstraints2D(leList, teList, nSpan, nChord, lower=1.0, name=name) comm = self.comm if comm.rank == 0: self.add_output(name, distributed=True, val=np.ones((nSpan * nChord, )), shape=nSpan * nChord) else: self.add_output(name, distributed=True, shape=(0, )) def nom_addThicknessConstraints1D(self, name, ptList, nCon, axis): self.DVCon.addThicknessConstraints1D(ptList, nCon, axis, name=name) comm = self.comm if comm.rank == 0: self.add_output(name, distributed=True, val=np.ones(nCon), shape=nCon) else: self.add_output(name, distributed=True, shape=(0)) def nom_addVolumeConstraint(self, name, leList, teList, nSpan=10, nChord=10): self.DVCon.addVolumeConstraint(leList, teList, nSpan=nSpan, nChord=nChord, name=name) comm = self.comm if comm.rank == 0: self.add_output(name, distributed=True, val=1.0) else: self.add_output(name, distributed=True, shape=0) def nom_add_LETEConstraint(self, name, volID, faceID, topID=None): self.DVCon.addLeTeConstraints(volID, faceID, name=name, topID=topID) # how many are there? conobj = self.DVCon.linearCon[name] nCon = len(conobj.indSetA) comm = self.comm if comm.rank == 0: self.add_output(name, distributed=True, val=np.zeros((nCon, )), shape=nCon) else: self.add_output(name, distributed=True, shape=0) return nCon def nom_addLERadiusConstraints(self, name, leList, nSpan, axis, chordDir): self.DVCon.addLERadiusConstraints(leList=leList, nSpan=nSpan, axis=axis, chordDir=chordDir, name=name) comm = self.comm if comm.rank == 0: self.add_output(name, distributed=True, val=np.ones(nSpan), shape=nSpan) else: self.add_output(name, distributed=True, shape=0) def nom_addCurvatureConstraint1D(self, name, start, end, nPts, axis, **kwargs): self.DVCon.addCurvatureConstraint1D(start=start, end=end, nPts=nPts, axis=axis, name=name, **kwargs) comm = self.comm if comm.rank == 0: self.add_output(name, distributed=True, val=1.0) else: self.add_output(name, distributed=True, shape=0) def nom_addLinearConstraintsShape(self, name, indSetA, indSetB, factorA, factorB): self.DVCon.addLinearConstraintsShape(indSetA=indSetA, indSetB=indSetB, factorA=factorA, factorB=factorB, name=name) lSize = len(indSetA) comm = self.comm if comm.rank == 0: self.add_output(name, distributed=True, val=np.zeros(lSize), shape=lSize) else: self.add_output(name, distributed=True, shape=0) def nom_addRefAxis(self, **kwargs): # we just pass this through return self.DVGeo.addRefAxis(**kwargs) def nom_setConstraintSurface(self, surface): # constraint needs a triangulated reference surface at initialization self.DVCon.setSurface(surface) def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): # only do the computations when we have more than zero entries in d_inputs in the reverse mode ni = len(list(d_inputs.keys())) if mode == "rev" and ni > 0: # this flag will be set to True after every compute call. # if it is true, we assume the design has changed so we re-run the sensitivity update # there can be hundreds of calls to this routine due to thickness constraints, # as a result, we only run the actual sensitivity comp once and save the jacobians # this might be better suited with the matrix-based API if self.update_jac: self.constraintfuncsens = dict() self.DVCon.evalFunctionsSens(self.constraintfuncsens, includeLinear=True) # set the flag to False so we dont run the update again if this is called w/o a compute in between self.update_jac = False for constraintname in self.constraintfuncsens: for dvname in self.constraintfuncsens[constraintname]: if dvname in d_inputs: dcdx = self.constraintfuncsens[constraintname][dvname] if self.comm.rank == 0: dout = d_outputs[constraintname] jvtmp = np.dot(np.transpose(dcdx), dout) else: jvtmp = 0.0 d_inputs[dvname] += jvtmp # OM does the reduction itself # d_inputs[dvname] += self.comm.reduce(jvtmp, op=MPI.SUM, root=0) for ptSetName in self.DVGeo.ptSetNames: if ptSetName in self.omPtSetList: dout = d_outputs[ptSetName].reshape( len(d_outputs[ptSetName]) // 3, 3) # only do the calc. if d_output is not zero on ANY proc local_all_zeros = np.all(dout == 0) global_all_zeros = np.zeros(1, dtype=bool) # we need to communicate for this check otherwise we may hang self.comm.Allreduce([local_all_zeros, MPI.BOOL], [global_all_zeros, MPI.BOOL], MPI.LAND) # global_all_zeros is a numpy array of size 1 if not global_all_zeros[0]: # TODO totalSensitivityTransProd is broken. does not work with zero surface nodes on a proc # xdot = self.DVGeo.totalSensitivityTransProd(dout, ptSetName) xdot = self.DVGeo.totalSensitivity(dout, ptSetName) # loop over dvs and accumulate xdotg = {} for k in xdot: # check if this dv is present if k in d_inputs: # do the allreduce # TODO reove the allreduce when this is fixed in openmdao # reduce the result ourselves for now. ideally, openmdao will do the reduction itself when this is fixed. this is because the bcast is also done by openmdao (pyoptsparse, but regardless, it is not done here, so reduce should also not be done here) xdotg[k] = self.comm.allreduce(xdot[k], op=MPI.SUM) # accumulate in the dict # TODO # because we only do one point set at a time, we always want the 0th # entry of this array since dvgeo always behaves like we are passing # in multiple objective seeds with totalSensitivity. we can remove the [0] # once we move back to totalSensitivityTransProd d_inputs[k] += xdotg[k][0]
class PlateComponentLHS(om.ExplicitComponent): """Robust Bump Flow Problem, with LHS Monte Carlo Samples""" def initialize(self): # Need to modify this dictionary when we change the SA constants #import pdb; pdb.set_trace() #sys.stdout = open(os.devnull, "w") self.aoptions = aeroOptions self.woptions = warpOptions self.ooptions = optOptions self.uoptions = uqOptions self.Pr = 0. self.P = self.uoptions['P'] self.NS0 = self.uoptions['NS0'] # Generate FFD and DVs if rank == 0: rank0dvg = pf.createFFD() else: rank0dvg = None self.DVGeo = comm.bcast(rank0dvg, root=0) # starting flat mesh meshname = self.aoptions['gridFile'] gridFile = meshname # flow characteristics alpha = 0.0 mach = self.ooptions['mach'] #0.95 Re = self.ooptions['Re'] #50000 Re_L = 1.0 temp = 540 arearef = 2.0 chordref = 1.0 # Spalart Allmaras model constants, to be changed in UQ (4 for now) saconstsm = [0.41, 0.1355, 0.622, 0.66666666667] self.saconstsb = [7.1, 0.3, 2.0, 1.0, 2.0, 1.2, 0.5, 2.0] self.saconsts = saconstsm + self.saconstsb self.aoptions['SAConsts'] = self.saconsts #self.gridSol = f'{meshname}_{saconstsm}_sol' solname = self.ooptions['prob_name'] self.gridSol = f'{solname}_sol' # Get a set of UQ sample points (LHS) #if self.ooptions['run_once']: # self.sample = self.uoptions['dist'] #else # Scatter samples, multi-point parallelism if self.uoptions['MCTimeBudget']: self.aps = [] self.solvers = [] self.meshes = [] self.current_samples = self.NS0 if rank == 0: rank0sam = plate_sa_lhs.genLHS(s=self.current_samples) else: rank0sam = None self.sample = comm.bcast(rank0sam, root=0) self.cases = divide_cases(self.NS0, size) # Scatter samples on each level, multi-point parallelism self.samplep = self.sample[self.cases[rank]] self.nsp = len(self.cases[rank]) # Create solvers for the preliminary data for i in range(self.nsp): namestr = self.gridSol + "_" + str(self.cases[rank][i]) # create meshes self.meshes.append( USMesh(options=self.woptions, comm=MPI.COMM_SELF)) # create aeroproblems self.aps.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=temp, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep(0.1) # this solves a few problems for some reason # create solvers self.solvers.append( ADFLOW(options=self.aoptions, comm=MPI.COMM_SELF)) saconstsm = self.samplep[i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[i].setOption('SAConsts', self.saconsts) self.solvers[i].setDVGeo(self.DVGeo) self.solvers[i].setMesh(self.meshes[i]) print("what up %i", str(rank)) coords = self.solvers[i].getSurfaceCoordinates( groupName=self.solvers[i].allWallsGroup) self.solvers[i].DVGeo.addPointSet(coords, 'coords') # start looping over mesh levels sumt = 0. sumtp = 0. Et = 0. funcs = {} a_init = self.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] dvdict = {'pnts': a_init['pnts']} for i in range(self.nsp): saconstsm = self.samplep[i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[i].setOption('SAConsts', self.saconsts) self.solvers[i].DVGeo.setDesignVars(dvdict) self.aps[i].setDesignVars(dvdict) pc0 = time.process_time() self.solvers[i](self.aps[i]) self.solvers[i].evalFunctions(self.aps[i], funcs) pc1 = time.process_time() astr = self.gridSol + "_" + str(self.cases[rank][i]) + "_cd" sumtp += (pc1 - pc0) sumt = comm.allreduce(sumtp) Et = sumt / self.NS0 self.NS = math.ceil(self.P / Et) self.Pr = self.NS * Et else: self.NS = self.uoptions['NS'] #import pdb; pdb.set_trace() if rank == 0: rank0sam = plate_sa_lhs.genLHS(s=self.NS) else: rank0sam = None self.sample = comm.bcast(rank0sam, root=0) self.cases = divide_cases(self.NS, size) self.nsp = len(self.cases[rank]) #int(ns/size) # samples per processor #import pdb; pdb.set_trace() self.samplep = self.sample[self.cases[ rank]] #self.sample[(rank*self.nsp):(rank*self.nsp+(self.nsp))] #shouldn't really need to "scatter" per se #import pdb; pdb.set_trace() #assert len(self.samplep) == self.nsp # Actually create solvers (and aeroproblems?) (and mesh?) now self.aps = [] self.solvers = [] self.meshes = [] #self.mesh = USMesh(options=self.woptions, comm=MPI.COMM_SELF) for i in range(self.nsp): namestr = self.gridSol + "_" + str(self.cases[rank][i]) # create meshes self.meshes.append( USMesh(options=self.woptions, comm=MPI.COMM_SELF)) # create aeroproblems self.aps.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=temp, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep(0.1) # this solves a few problems for some reason # create solvers self.solvers.append( ADFLOW(options=self.aoptions, comm=MPI.COMM_SELF)) # if not self.ooptions['run_once']: # saconstsm = self.samplep[i].tolist() # else: saconstsm = self.samplep[i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[i].setOption('SAConsts', self.saconsts) self.solvers[i].setDVGeo(self.DVGeo) self.solvers[i].setMesh(self.meshes[i]) print("what up %i", str(rank)) coords = self.solvers[i].getSurfaceCoordinates( groupName=self.solvers[i].allWallsGroup) self.solvers[i].DVGeo.addPointSet(coords, 'coords') # Set constraints, should only need one of those solvers, the meshes are all the same self.DVCon = DVConstraints() self.DVCon2 = DVConstraints() self.DVCon.setDVGeo(self.solvers[0].DVGeo.getFlattenedChildren()[1]) self.DVCon2.setDVGeo(self.solvers[0].DVGeo) self.DVCon.setSurface(self.solvers[0].getTriangulatedMeshSurface( groupName='allSurfaces')) # set extra group for surface area condition self.DVCon2.setSurface(self.solvers[0].getTriangulatedMeshSurface(), name='wall') # DV should be same into page (not doing anything right now) #import pdb; pdb.set_trace() lIndex = self.solvers[0].DVGeo.getFlattenedChildren()[1].getLocalIndex( 0) indSetA = [] indSetB = [] nXc = optOptions['NX'] self.NC = math.trunc( ((1.0 - self.ooptions['DVFraction']) * self.ooptions['NX'])) ind = [ int(nXc / 2) - int(self.NC / 2), int(nXc / 2) + int(self.NC / 2) ] for i in range(ind[0], ind[1]): indSetA.append(lIndex[i, 0, 1]) indSetB.append(lIndex[i, 1, 1]) # for i in range(lIndex.shape[0]): # indSetA.append(lIndex[i, 0, 1]) # indSetB.append(lIndex[i, 1, 1]) self.DVCon.addLinearConstraintsShape(indSetA, indSetB, factorA=1.0, factorB=-1.0, lower=0, upper=0, name='eqs') # Thickness constraints (one for each active DV) #import pdb; pdb.set_trace() # Maximum thickness of the domain, translates to minimum thickness of bump ub = 1.0 - self.ooptions['DCMinThick'] tcf = self.ooptions['DCThickFrac'] ra = self.ooptions['bumpBounds'] lim = self.ooptions['DCMinArea'] span = numpy.linspace(0, 1, nXc) xc = span * (ra[1] - ra[0]) + ra[0] #ind = range(int(nXc/2) - int(self.NC/2), int(nXc/2) + int(self.NC/2))) ind = [ int(nXc / 2) - int(tcf * self.NC / 2), int(nXc / 2) + int(tcf * self.NC / 2) ] ptList = numpy.zeros([2, 3]) ptList[:, 0] = xc[ind] ptList[:, 1] = 0.5 ptList[:, 2] = 0.5 if self.ooptions['use_area_con']: self.DVCon2.addSurfaceAreaConstraint(lower=lim, upper=10., name='sas', surfaceName='wall') else: self.DVCon2.addThicknessConstraints1D(ptList, self.NC, [0, 0, 1], lower=0.5, upper=ub, name='tcs') print("excuse me") dummy = rank dsum = comm.allgather(dummy) sys.stdout = sys.__stdout__ def setup(self): #initialize shape and set deformation points as inputs a_init = self.solvers[0].DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] # mult = numpy.linspace(1.0,1.5,num=int(0.5*len(a_init['pnts']))) # mult = numpy.concatenate((mult, mult)) #if self.ooptions['run_once']: # a_init['pnts'] = self.ooptions['ro_shape'] #a_init['pnts'] = numpy.multiply(mult, a_init['pnts']) self.add_input('a', a_init['pnts'], desc="Bump Shape Control Points") #self.add_input('a', 0.2, desc="Bump Shape Control Points") if self.ooptions['use_area_con']: self.add_output('SA', 1.0, desc='Surface Area Constraint') else: self.add_output('TC', numpy.zeros(self.NC), desc='Thickness Constraints') self.add_output('Cd_m', 0.0, desc="Mean Drag Coefficient") self.add_output('Cd_v', 0.0, desc="Variance Drag Coefficient") self.add_output('Cd_r', 0.0, desc="Robust Drag Objective") self.add_output('EQ', numpy.zeros(int(len(a_init['pnts']) / 2)), desc="Control Point Symmetry") self.add_output('N1', self.NS, desc="Number of samples used") self.add_output('Pr', self.Pr, desc="MFMC Samples at each level") def setup_partials(self): self.declare_partials('Cd_m', 'a', method='exact') self.declare_partials('Cd_v', 'a', method='exact') self.declare_partials('Cd_r', 'a', method='exact') if self.ooptions['use_area_con']: self.declare_partials('SA', 'a', method='exact') else: self.declare_partials('TC', 'a', method='exact') self.declare_partials('EQ', 'a', method='exact') def compute(self, inputs, outputs): # run the bump shape model #import pdb; pdb.set_trace() # evaluate each sample point #print("hello") dvdict = {'pnts': inputs['a']} funcs = {} ns = self.NS sump = 0. musp = numpy.zeros(self.nsp) #self.aps[0].setDesignVars(dvdict) for i in range(self.nsp): self.solvers[i].DVGeo.setDesignVars(dvdict) self.aps[i].setDesignVars(dvdict) self.solvers[i](self.aps[i]) self.solvers[i].evalFunctions(self.aps[i], funcs) astr = self.gridSol + "_" + str(self.cases[rank][i]) + "_cd" #import pdb; pdb.set_trace() musp[i] = funcs[astr] sump += funcs[astr] # compute mean and variance sum = comm.allreduce(sump) mus = comm.allgather(musp) #import pdb; pdb.set_trace() E = sum / ns sum2 = 0. for i in range(len(mus)): #range(size): for j in range(len(mus[i])): #range(self.nsp): sum2 += (mus[i][j] - E)**2 V = sum2 / ns self.DVCon.evalFunctions(funcs, includeLinear=True) self.DVCon2.evalFunctions(funcs, includeLinear=True) outputs['Cd_m'] = E outputs['Cd_v'] = math.sqrt(V) rho = self.uoptions['rho'] outputs['Cd_r'] = E + rho * math.sqrt(V) if self.ooptions['use_area_con']: outputs['SA'] = funcs['sas'] else: outputs['TC'] = funcs['tcs'] outputs['EQ'] = funcs['eqs'] outputs['N1'] = self.NS outputs['Pr'] = self.Pr #outputs['Cd'] = inputs['a']*inputs['a'] def compute_partials(self, inputs, J): dvdict = {'pnts': inputs['a']} funcs = {} funcSens = {} ns = self.NS sump = 0. musp = numpy.zeros(self.nsp) pmup = [] psump = numpy.zeros(len(inputs['a'])) self.aps[0].setDesignVars(dvdict) #import pdb; pdb.set_trace() for i in range(self.nsp): self.solvers[i].DVGeo.setDesignVars(dvdict) self.aps[i].setDesignVars(dvdict) self.solvers[i].evalFunctions(self.aps[i], funcs) self.solvers[i].evalFunctionsSens(self.aps[i], funcSens, ['cd']) astr = self.gridSol + "_" + str(self.cases[rank][i]) + "_cd" arr = [x * (1. / ns) for x in funcSens[astr]['pnts']] sump += funcs[astr] musp[i] = funcs[astr] pmup.append(arr[0]) psump += arr[0] sum = comm.allreduce(sump) mus = comm.allgather(musp) pmu = comm.allgather(pmup) psum = comm.allreduce(psump) #import pdb; pdb.set_trace() #import pdb; pdb.set_trace() # compute variance sensitivity E = sum / ns sum2 = 0. psum2 = numpy.zeros(len(inputs['a'])) for i in range(len(mus)): #range(size): for j in range(len(mus[i])): #range(self.nsp): sum2 += (mus[i][j] - E)**2 temp = pmu[i][j] * ns - psum arr2 = [x * 2 * (mus[i][j] - E) / ns for x in temp] psum2 += arr2 #import pdb; pdb.set_trace() V = sum2 / ns #import pdb; pdb.set_trace() self.DVCon.evalFunctionsSens(funcSens, includeLinear=True) self.DVCon2.evalFunctionsSens(funcSens, includeLinear=True) J['Cd_m', 'a'] = psum J['Cd_v', 'a'] = (1. / (2 * math.sqrt(V))) * psum2 rho = self.uoptions['rho'] J['Cd_r', 'a'] = psum + rho * (1. / (2 * math.sqrt(V))) * psum2 if self.ooptions['use_area_con']: J['SA', 'a'] = funcSens['sas']['pnts'] else: J['TC', 'a'] = funcSens['tcs']['pnts'] J['EQ', 'a'] = funcSens['eqs']['pnts']
class PlateComponentMLMC(om.ExplicitComponent): """Robust Bump Flow Problem, with LHS Multi Level Monte Carlo Samples""" def initialize(self): # Need to modify this dictionary when we change the SA constants #import pdb; pdb.set_trace() sys.stdout = open(os.devnull, "w") self.aoptions = aeroOptions self.woptions = warpOptions self.ooptions = optOptions self.uoptions = uqOptions # Generate FFD and DVs if rank == 0: rank0dvg = pf.createFFD() else: rank0dvg = None self.DVGeo = comm.bcast(rank0dvg, root=0) # Get a full list of every mesh name we have, assume they are ordered properly by level self.meshnames = self.uoptions['gridFileLevels'] self.Lmax = len(self.meshnames) #as many levels as we have meshes if self.Lmax < 3: raise ValueError('Not enough meshes available, ' + self.Lmax + ' < 3') # Spalart Allmaras model constants, to be changed in UQ (4 for now) saconstsm = [0.41, 0.1355, 0.622, 0.66666666667] self.saconstsb = [7.1, 0.3, 2.0, 1.0, 2.0, 1.2, 0.5, 2.0] self.saconsts = saconstsm + self.saconstsb self.aoptions['SAConsts'] = self.saconsts #self.gridSol = f'{meshname}_{saconstsm}_sol' solname = self.ooptions['prob_name'] self.gridSol = f'{solname}_sol' self.cases = [] self.nsp = [] #keep track per level self.samplep = [] self.naddedtot = [] for i in range(self.Lmax): self.naddedtot.append(0) self.aps = [] self.solvers = [] self.meshes = [] self.NS0 = self.uoptions['NS0'] self.current_samples = self.NS0 * self.Lmax self.N1 = None ########## # call distribute samples here? if not self.uoptions['use-predetermined-samples']: self.MLMC() else: self.N1 = self.uoptions['predet-N1'] self.dist_samples() ########## # Set constraints, should only need one of those solvers, the meshes are all the same if rank == 0: self.DVCon = DVConstraints() self.DVCon2 = DVConstraints() self.DVCon.setDVGeo( self.solvers[self.Lmax - 1][0].DVGeo.getFlattenedChildren()[1]) self.DVCon2.setDVGeo(self.solvers[self.Lmax - 1][0].DVGeo) self.DVCon.setSurface( self.solvers[self.Lmax - 1][0].getTriangulatedMeshSurface( groupName='allSurfaces')) # set extra group for surface area condition self.DVCon2.setSurface( self.solvers[self.Lmax - 1][0].getTriangulatedMeshSurface(), name='wall') # DV should be same into page (not doing anything right now) #import pdb; pdb.set_trace() lIndex = self.solvers[ self.Lmax - 1][0].DVGeo.getFlattenedChildren()[1].getLocalIndex(0) indSetA = [] indSetB = [] nXc = optOptions['NX'] self.NC = math.trunc( ((1.0 - self.ooptions['DVFraction']) * self.ooptions['NX'])) ind = [ int(nXc / 2) - int(self.NC / 2), int(nXc / 2) + int(self.NC / 2) ] for i in range(ind[0], ind[1]): indSetA.append(lIndex[i, 0, 1]) indSetB.append(lIndex[i, 1, 1]) # for i in range(lIndex.shape[0]): # indSetA.append(lIndex[i, 0, 1]) # indSetB.append(lIndex[i, 1, 1]) self.DVCon.addLinearConstraintsShape(indSetA, indSetB, factorA=1.0, factorB=-1.0, lower=0, upper=0, name='eqs') # Thickness constraints (one for each active DV) #import pdb; pdb.set_trace() # Maximum thickness of the domain, translates to minimum thickness of bump ub = 1.0 - self.ooptions['DCMinThick'] tcf = self.ooptions['DCThickFrac'] ra = self.ooptions['bumpBounds'] lim = self.ooptions['DCMinArea'] span = numpy.linspace(0, 1, nXc) xc = span * (ra[1] - ra[0]) + ra[0] #ind = range(int(nXc/2) - int(self.NC/2), int(nXc/2) + int(self.NC/2))) ind = [ int(nXc / 2) - int(tcf * self.NC / 2), int(nXc / 2) + int(tcf * self.NC / 2) ] ptList = numpy.zeros([2, 3]) ptList[:, 0] = xc[ind] ptList[:, 1] = 0.5 ptList[:, 2] = 0.5 if self.ooptions['use_area_con']: self.DVCon2.addSurfaceAreaConstraint(lower=lim, upper=10., name='sas', surfaceName='wall') else: self.DVCon2.addThicknessConstraints1D(ptList, self.NC, [0, 0, 1], lower=0.5, upper=ub, name='tcs') dummy = rank dsum = comm.allgather(dummy) sys.stdout = sys.__stdout__ def setup(self): #initialize shape and set deformation points as inputs a_init = self.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] # mult = numpy.linspace(1.0,1.5,num=int(0.5*len(a_init['pnts']))) # mult = numpy.concatenate((mult, mult)) #if self.ooptions['run_once']: # a_init['pnts'] = self.ooptions['ro_shape'] #a_init['pnts'] = numpy.multiply(mult, a_init['pnts']) self.add_input('a', a_init['pnts'], desc="Bump Shape Control Points") #self.add_input('a', 0.2, desc="Bump Shape Control Points") if self.ooptions['use_area_con']: self.add_output('SA', 1.0, desc='Surface Area Constraint') else: self.add_output('TC', numpy.zeros(self.NC), desc='Thickness Constraints') self.add_output('Cd_m', 0.0, desc="Mean Drag Coefficient") self.add_output('Cd_v', 0.0, desc="Variance Drag Coefficient") self.add_output('Cd_r', 0.0, desc="Robust Drag Objective") self.add_output('EQ', numpy.zeros(int(len(a_init['pnts']) / 2)), desc="Control Point Symmetry") self.add_output('N1', self.N1, desc="MLMC Samples at each level") def setup_partials(self): self.declare_partials('Cd_m', 'a', method='exact') self.declare_partials('Cd_v', 'a', method='exact') self.declare_partials('Cd_r', 'a', method='exact') if self.ooptions['use_area_con']: self.declare_partials('SA', 'a', method='exact') else: self.declare_partials('TC', 'a', method='exact') self.declare_partials('EQ', 'a', method='exact') def compute(self, inputs, outputs): # run the bump shape model #import pdb; pdb.set_trace() dvdict = {'pnts': inputs['a']} ntot = self.current_samples nslp = [] nslt = [] for k in range(self.Lmax): nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) funcs = {} sump = [] musp = [] sum1 = [] mus = [] sumpm = [] muspm = [] summ = [] musm = [] E = numpy.zeros(self.Lmax) V = numpy.zeros(self.Lmax) for k in range(self.Lmax): sump.append(0.0) musp.append(numpy.zeros(nslp[k])) sumpm.append(0.) muspm.append(numpy.zeros(nslp[k])) for i in range(nslp[k]): saconstsm = self.samplep[k][i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[k][i].setOption('SAConsts', self.saconsts) self.solvers[k][i].DVGeo.setDesignVars(dvdict) self.aps[k][i].setDesignVars(dvdict) self.solvers[k][i](self.aps[k][i]) self.solvers[k][i].evalFunctions(self.aps[k][i], funcs) astr = self.gridSol + "_" + str(self.cases[k][rank][i]) + "_cd" musp[k][i] = funcs[astr] sump[k] += funcs[astr] if k > 0: self.solvers[k][i + nslp[k]].setOption( 'SAConsts', self.saconsts) self.solvers[k][i + nslp[k]].DVGeo.setDesignVars(dvdict) self.aps[k][i + nslp[k]].setDesignVars(dvdict) self.solvers[k][i + nslp[k]](self.aps[k][i + nslp[k]]) self.solvers[k][i + nslp[k]].evalFunctions( self.aps[k][i + nslp[k]], funcs) astr = self.gridSol + "_" + str( self.cases[k][rank][i]) + "_m_cd" muspm[k][i] = -funcs[astr] sumpm[k] += -funcs[astr] # compute mean and variance for k in range(self.Lmax): sum1.append(comm.allreduce(sump[k])) mus.append(comm.allgather(musp[k])) summ.append(comm.allreduce(sumpm[k])) musm.append(comm.allgather(muspm[k])) #import pdb; pdb.set_trace() E[k] = (sum1[k] + summ[k]) / nslt[k] sum2 = 0. for i in range(len(mus[k])): #loop over processors for j in range(len( mus[k][i])): #loop over samples on processors if k > 0: sum2 += ((mus[k][i][j] + musm[k][i][j]) - E[k])**2 else: sum2 += (mus[k][i][j] - E[k])**2 V[k] = sum2 / nslt[k] import pdb pdb.set_trace() if rank == 0: self.DVCon.evalFunctions(funcs, includeLinear=True) self.DVCon2.evalFunctions(funcs, includeLinear=True) eq0 = funcs['eqs'] sa0 = funcs['sas'] else: eq0 = None sa0 = None eq = comm.bcast(eq0, root=0) sa = comm.bcast(sa0, root=0) outputs['Cd_m'] = sum(E) outputs['Cd_v'] = V[0] #by assumption rho = self.uoptions['rho'] outputs['Cd_r'] = sum(E) + rho * math.sqrt(V[0]) if self.ooptions['use_area_con']: outputs['SA'] = sa else: outputs['TC'] = funcs['tcs'] outputs['EQ'] = eq outputs['N1'] = self.N1 #outputs['Cd'] = inputs['a']*inputs['a'] def compute_partials(self, inputs, J): dvdict = {'pnts': inputs['a']} funcs = {} funcSens = {} nslp = [] nslt = [] for k in range(self.Lmax): nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) sump = [] musp = [] sum1 = [] sum2 = [] mus = [] sumpm = [] muspm = [] summ = [] musm = [] pmup = [] psump = [] #numpy.zeros(len(inputs['a'])) pmu = [] psum1 = [] pmupm = [] psumpm = [] #numpy.zeros(len(inputs['a'])) pmum = [] psumm = [] E = numpy.zeros(self.Lmax) V = numpy.zeros(self.Lmax) #import pdb; pdb.set_trace() for k in range(self.Lmax): sump.append(0.0) musp.append(numpy.zeros(nslp[k])) sumpm.append(0.) muspm.append(numpy.zeros(nslp[k])) pmup.append([]) psump.append(numpy.zeros(len(inputs['a']))) pmupm.append([]) psumpm.append(numpy.zeros(len(inputs['a']))) for i in range(nslp[k]): saconstsm = self.samplep[k][i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[k][i].setOption('SAConsts', self.saconsts) self.solvers[k][i].DVGeo.setDesignVars(dvdict) self.aps[k][i].setDesignVars(dvdict) self.solvers[k][i](self.aps[k][i]) self.solvers[k][i].evalFunctions(self.aps[k][i], funcs) self.solvers[k][i].evalFunctionsSens(self.aps[k][i], funcSens, ['cd']) astr = self.gridSol + "_" + str(self.cases[k][rank][i]) + "_cd" arr = [x * (1. / nslt[k]) for x in funcSens[astr]['pnts']] musp[k][i] = funcs[astr] sump[k] += funcs[astr] pmup[k].append(arr[0]) psump[k] += arr[0] if k > 0: self.solvers[k][i + nslp[k]].setOption( 'SAConsts', self.saconsts) self.solvers[k][i + nslp[k]].DVGeo.setDesignVars(dvdict) #import pdb; pdb.set_trace() self.aps[k][i + nslp[k]].setDesignVars(dvdict) self.solvers[k][i + nslp[k]](self.aps[k][i + nslp[k]]) self.solvers[k][i + nslp[k]].evalFunctions( self.aps[k][i + nslp[k]], funcs) self.solvers[k][i + nslp[k]].evalFunctionsSens( self.aps[k][i + nslp[k]], funcSens, ['cd']) astr = self.gridSol + "_" + str( self.cases[k][rank][i]) + "_m_cd" arr = [x * (1. / nslt[k]) for x in funcSens[astr]['pnts']] muspm[k][i] = -funcs[astr] sumpm[k] += -funcs[astr] pmupm[k].append(-arr[0]) psumpm[k] -= arr[0] # compute mean and variance psum2 = numpy.zeros(len(inputs['a'])) for k in range(self.Lmax): sum1.append(comm.allreduce(sump[k])) mus.append(comm.allgather(musp[k])) summ.append(comm.allreduce(sumpm[k])) musm.append(comm.allgather(muspm[k])) psum1.append(comm.allreduce(psump[k])) pmu.append(comm.allgather(pmup[k])) psumm.append(comm.allreduce(psumpm[k])) pmum.append(comm.allgather(pmupm[k])) E[k] = (sum1[k] + summ[k]) / nslt[k] sum2 = 0. for i in range(len(mus[0])): #loop over processors for j in range(len(mus[0][i])): #loop over samples on processors #import pdb; pdb.set_trace() sum2 += (mus[0][i][j] - E[0])**2 temp = pmu[0][i][j] * nslt[0] - psum1[0] arr2 = [x * 2 * (mus[0][i][j] - E[0]) / nslt[0] for x in temp] psum2 += arr2 V = sum2 / nslt[0] # compute variance sensitivity #import pdb; pdb.set_trace() if rank == 0: self.DVCon.evalFunctionsSens(funcSens, includeLinear=True) self.DVCon2.evalFunctionsSens(funcSens, includeLinear=True) eq0p = funcSens['eqs']['pnts'] sa0p = funcSens['sas']['pnts'] else: eq0p = None sa0p = None eqp = comm.bcast(eq0p, root=0) sap = comm.bcast(sa0p, root=0) J['Cd_m', 'a'] = sum(psum1) + sum(psumm) J['Cd_v', 'a'] = psum2 rho = self.uoptions['rho'] J['Cd_r', 'a'] = sum(psum1) + sum(psumm) + rho * (1. / (2 * math.sqrt(V))) * psum2 #import pdb; pdb.set_trace() if self.ooptions['use_area_con']: J['SA', 'a'] = sap else: J['TC', 'a'] = funcSens['tcs']['pnts'] J['EQ', 'a'] = eqp #J['Cd','a'][0] = 2*inputs['a'] def MLMC(self): # Use an MLMC algorithm to determine an optimal sample distribution between existing mesh levels # We do this once before optimization, then compute statistics with the same set of samples at every iteration # start with initial samples # Get a set of UQ sample points (LHS), enough for each level at the start #sys.stdout = open(os.devnull, "w") # flow characteristics alpha = 0.0 mach = self.ooptions['mach'] #0.95 Re = self.ooptions['Re'] #50000 Re_L = 1.0 tempR = 540 arearef = 2.0 chordref = 1.0 a_init = self.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] self.current_samples = self.NS0 * self.Lmax if rank == 0: rank0sam = plate_sa_lhs.genLHS(s=self.current_samples) else: rank0sam = None self.sample = comm.bcast(rank0sam, root=0) # Scatter samples on each level, multi-point parallelism for i in range(self.Lmax): self.cases.append(divide_cases(self.NS0, size)) for j in range(len(self.cases[i])): for k in range(len(self.cases[i][j])): self.cases[i][j][k] += i * self.NS0 #self.nsp.append(len(self.cases[i][rank]))#int(ns/size) # samples per processor self.samplep.append(self.sample[self.cases[i][rank]]) #import pdb; pdb.set_trace() #self.samplep = self.sample[self.cases[rank]]#self.sample[(rank*self.nsp):(rank*self.nsp+(self.nsp))] #shouldn't really need to "scatter" per se #import pdb; pdb.set_trace() # for i in range(self.Lmax): # assert len(self.samplep[i]) == self.nsp[i] # Actually create solvers (and aeroproblems?) (and mesh?) now nslp = [] nslt = [] for k in range(self.Lmax): alist = [] slist = [] mlist = [] alist2 = [] slist2 = [] mlist2 = [] nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) for i in range(nslp[k]): namestr = self.gridSol + "_" + str(self.cases[k][rank][i]) # create meshes leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[k] mlist.append(USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[k] alist.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep(0.1) # this solves a few problems for some reason # create solvers slist.append(ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) # if not self.ooptions['run_once']: # saconstsm = self.samplep[i].tolist() # else: saconstsm = self.samplep[k][i].tolist() self.saconsts = saconstsm + self.saconstsb slist[i].setOption('SAConsts', self.saconsts) slist[i].setDVGeo(self.DVGeo) slist[i].setMesh(mlist[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist[i].allWallsGroup) slist[i].DVGeo.addPointSet(coords, 'coords') if k > 0: #create additional solvers at higher levels for the estimators # create meshes namestr = self.gridSol + "_" + str( self.cases[k][rank][i]) + "_m" leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[k - 1] mlist2.append( USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[k - 1] alist2.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep( 0.1) # this solves a few problems for some reason # create solvers slist2.append(ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) slist2[i].setOption('SAConsts', self.saconsts) slist2[i].setDVGeo(self.DVGeo) slist2[i].setMesh(mlist2[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist2[i].allWallsGroup) slist2[i].DVGeo.addPointSet(coords, 'coords') self.aps.append(alist) self.solvers.append(slist) self.meshes.append(mlist) if k > 0: self.aps[k] += alist2 self.solvers[k] += slist2 self.meshes[k] += mlist2 #import pdb; pdb.set_trace() # start looping over mesh levels L = 0 M = 4.0 #0.5 #refinement factor? converged = 0 eps = self.uoptions['vartol'] sum1 = [] mus = [] sump = [] musp = [] sumpm = [] muspm = [] summ = [] musm = [] E = [] V = [] N1 = [] while ~converged & L < self.Lmax: # compute start up samples to estimate variance dvdict = {'pnts': a_init['pnts']} funcs = {} nslp = [] nslt = [] for k in range(self.Lmax): nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) sump.append(0.) musp.append(numpy.zeros(nslp[L])) sumpm.append(0.) muspm.append(numpy.zeros(nslp[L])) for i in range(nslp[L]): # just do this again in case saconstsm = self.samplep[L][i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[L][i].setOption('SAConsts', self.saconsts) self.solvers[L][i].DVGeo.setDesignVars(dvdict) self.aps[L][i].setDesignVars(dvdict) self.solvers[L][i](self.aps[L][i]) self.solvers[L][i].evalFunctions(self.aps[L][i], funcs) astr = self.gridSol + "_" + str(self.cases[L][rank][i]) + "_cd" musp[L][i] = funcs[astr] sump[L] += funcs[astr] #import pdb; pdb.set_trace() if L > 0: self.solvers[L][i + nslp[L]].setOption( 'SAConsts', self.saconsts) self.solvers[L][i + nslp[L]].DVGeo.setDesignVars(dvdict) self.aps[L][i + nslp[L]].setDesignVars(dvdict) self.solvers[L][i + nslp[L]](self.aps[L][i + nslp[L]]) self.solvers[L][i + nslp[L]].evalFunctions( self.aps[L][i + nslp[L]], funcs) astr = self.gridSol + "_" + str( self.cases[L][rank][i]) + "_m_cd" muspm[L][i] = -funcs[astr] sumpm[L] += -funcs[astr] # compute mean and variance estimate from start up samples sum1.append(comm.allreduce(sump[L])) mus.append(comm.allgather(musp[L])) summ.append(comm.allreduce(sumpm[L])) musm.append(comm.allgather(muspm[L])) #import pdb; pdb.set_trace() # mean at each level E = numpy.zeros(L + 1) for l in range(L + 1): E[l] = (sum1[l] + summ[l]) / nslt[l] # variance at each level V = numpy.zeros(L + 1) for l in range(L + 1): sum2 = 0. for i in range(len(mus[l])): #range(size): for j in range(len(mus[l][i])): #range(self.nsp): if l > 0: sum2 += ((mus[l][i][j] + musm[l][i][j]) - E[l])**2 else: sum2 += (mus[l][i][j] - E[l])**2 V[l] = sum2 / nslt[l] #import pdb; pdb.set_trace() # now determine the optimal number of samples at each level N1.append(0.) worksum = 0 for l in range(L + 1): worksum += numpy.sqrt(V[l] * (M**l)) for l in range(L + 1): nlf = 2 * numpy.sqrt(V[l] / (M**l)) * worksum / (eps * eps) nlfm = max(nslt[l], math.ceil(nlf)) N1[l] = nlfm # now compute and generate additional samples at each level # first partition samples NEVERMIND (just do it once at each level, no need to repeat) # create the extra number of solvers at each (the current) level # need to loop everything from here on for l in range(L + 1): alist = self.aps[l][0:nslp[l]] slist = self.solvers[l][0:nslp[l]] mlist = self.meshes[l][0:nslp[l]] if l > 0: alist2 = self.aps[l][nslp[l]:] slist2 = self.solvers[l][nslp[l]:] mlist2 = self.meshes[l][nslp[l]:] self.naddedtot[l] = N1[l] - nslt[l] self.current_samples += self.naddedtot[l] #import pdb; pdb.set_trace() if rank == 0: rank0sam = plate_sa_lhs.genLHS(s=self.current_samples) else: rank0sam = None self.sample = comm.bcast(rank0sam, root=0) if self.naddedtot[l] > 0: temp = divide_cases(self.naddedtot[l], size) for i in range(len(temp)): for j in range(len(temp[i])): temp[i][j] += self.current_samples - self.naddedtot[ l] #self.Lmax*self.NS0 + sum(self.naddedtot[0:L]) else: temp = [] if len(temp): for ns in range(size): self.cases[l][ns] += temp[ns] #append nslpnew = len(self.cases[l][rank]) nsltnew = sum([len(self.cases[l][x]) for x in range(size)]) #self.nsp[L] = len(self.cases[L][rank]) #int(ns/size) # samples per processor self.samplep[l] = self.sample[self.cases[l][rank]] for i in range(nslp[l], nslpnew): #need it to be just the extra cases #import pdb; pdb.set_trace() namestr = self.gridSol + "_" + str(self.cases[l][rank][i]) # create meshes leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[l] mlist.append( USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[l] alist.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep( 0.1) # this solves a few problems for some reason # create solvers slist.append(ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) saconstsm = self.samplep[l][i].tolist() self.saconsts = saconstsm + self.saconstsb slist[i].setOption('SAConsts', self.saconsts) slist[i].setDVGeo(self.DVGeo) slist[i].setMesh(mlist[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist[i].allWallsGroup) slist[i].DVGeo.addPointSet(coords, 'coords') time.sleep(0.1) if l > 0: #create additional solvers at higher levels for the estimators # create meshes #import pdb; pdb.set_trace() namestr = self.gridSol + "_" + str( self.cases[l][rank][i]) + "_m" leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[l - 1] mlist2.append( USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[l - 1] alist2.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep( 0.1) # this solves a few problems for some reason # create solvers slist2.append( ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) slist2[i].setOption('SAConsts', self.saconsts) slist2[i].setDVGeo(self.DVGeo) slist2[i].setMesh(mlist2[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist2[i].allWallsGroup) slist2[i].DVGeo.addPointSet(coords, 'coords') nslp[l] = nslpnew nslt[l] = nsltnew self.aps[l] = alist self.solvers[l] = slist self.meshes[l] = mlist if l > 0: self.aps[l] += alist2 self.solvers[l] += slist2 self.meshes[l] += mlist2 # compute remaining samples sump[l] = 0 sumpm[l] = 0 musp[l] = numpy.zeros(nslp[l]) muspm[l] = numpy.zeros(nslp[l]) for i in range(nslp[l]): # just do this again in case saconstsm = self.samplep[l][i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[l][i].setOption('SAConsts', self.saconsts) self.solvers[l][i].DVGeo.setDesignVars(dvdict) self.aps[l][i].setDesignVars(dvdict) self.solvers[l][i](self.aps[l][i]) self.solvers[l][i].evalFunctions(self.aps[l][i], funcs) astr = self.gridSol + "_" + str( self.cases[l][rank][i]) + "_cd" musp[l][i] = funcs[astr] sump[l] += funcs[astr] #import pdb; pdb.set_trace() if l > 0: self.solvers[l][i + nslp[l]].setOption( 'SAConsts', self.saconsts) self.solvers[l][i + nslp[l]].DVGeo.setDesignVars(dvdict) self.aps[l][i + nslp[l]].setDesignVars(dvdict) self.solvers[l][i + nslp[l]](self.aps[l][i + nslp[l]]) self.solvers[l][i + nslp[l]].evalFunctions( self.aps[l][i + nslp[l]], funcs) astr = self.gridSol + "_" + str( self.cases[l][rank][i]) + "_m_cd" muspm[l][i] = -funcs[astr] sumpm[l] += -funcs[astr] # compute mean and variance estimate from all samples sum1[l] = comm.allreduce(sump[l]) mus[l] = comm.allgather(musp[l]) summ[l] = comm.allreduce(sumpm[l]) musm[l] = comm.allgather(muspm[l]) # mean at each level E[l] = (sum1[l] + summ[l]) / nslt[l] # variance at each level sum2 = 0. for i in range(len(mus[l])): #range(size): for j in range(len(mus[l][i])): #range(self.nsp): if l > 0: sum2 += ((mus[l][i][j] + musm[l][i][j]) - E[l])**2 else: sum2 += (mus[l][i][j] - E[l])**2 V[l] = sum2 / nslt[l] # if L == 1: # import pdb; pdb.set_trace() L += 1 #import pdb; pdb.set_trace() #sys.stdout = sys.__stdout__ if rank == 0: print("MLMC Completed, Samples per level: ", N1) self.N1 = N1 #import pdb; pdb.set_trace() # test for convergence # don't actually need this, won't ever end early # range = -1:0 # if L > 1 & M**L >= 4: # con = M**(range.*suml(2,L+1+range)./suml(1,L+1+range)) # converged = (max(abs(con)) < (M-1)*eps/sqrt(2)) # once done, we have aps, solvers, meshes, which is all we need def dist_samples(self): # If we already have the number of samples, just create as many solvers as needed at each level # Just do this after running MLMC() anyway # flow characteristics alpha = 0.0 mach = self.ooptions['mach'] #0.95 Re = self.ooptions['Re'] #50000 Re_L = 1.0 tempR = 540 arearef = 2.0 chordref = 1.0 a_init = self.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] self.current_samples = sum(self.N1) if rank == 0: rank0sam = plate_sa_lhs.genLHS(s=self.current_samples) else: rank0sam = None self.sample = comm.bcast(rank0sam, root=0) #import pdb; pdb.set_trace() # Scatter samples on each level, multi-point parallelism self.cases = [] self.samplep = [] for i in range(self.Lmax): self.cases.append(divide_cases(self.N1[i], size)) for j in range(len(self.cases[i])): for k in range(len(self.cases[i][j])): self.cases[i][j][k] += sum(self.N1[0:i]) #self.nsp.append(len(self.cases[i][rank]))#int(ns/size) # samples per processor self.samplep.append(self.sample[self.cases[i][rank]]) # Actually create solvers (and aeroproblems?) (and mesh?) now self.aps = [] self.solvers = [] self.meshes = [] nslp = [] nslt = [] for k in range(self.Lmax): alist = [] slist = [] mlist = [] alist2 = [] slist2 = [] mlist2 = [] nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) for i in range(nslp[k]): namestr = self.gridSol + "_" + str(self.cases[k][rank][i]) # create meshes leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[k] mlist.append(USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[k] alist.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep(0.1) # this solves a few problems for some reason # create solvers slist.append(ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) # if not self.ooptions['run_once']: # saconstsm = self.samplep[i].tolist() # else: saconstsm = self.samplep[k][i].tolist() self.saconsts = saconstsm + self.saconstsb slist[i].setOption('SAConsts', self.saconsts) slist[i].setDVGeo(self.DVGeo) slist[i].setMesh(mlist[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist[i].allWallsGroup) slist[i].DVGeo.addPointSet(coords, 'coords') if k > 0: #create additional solvers at higher levels for the estimators # create meshes namestr = self.gridSol + "_" + str( self.cases[k][rank][i]) + "_m" leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[k - 1] mlist2.append( USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[k - 1] alist2.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep( 0.1) # this solves a few problems for some reason # create solvers slist2.append(ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) slist2[i].setOption('SAConsts', self.saconsts) slist2[i].setDVGeo(self.DVGeo) slist2[i].setMesh(mlist2[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist2[i].allWallsGroup) slist2[i].DVGeo.addPointSet(coords, 'coords') self.aps.append(alist) self.solvers.append(slist) self.meshes.append(mlist) if k > 0: self.aps[k] += alist2 self.solvers[k] += slist2 self.meshes[k] += mlist2
class PlateComponentMFMC(om.ExplicitComponent): """Robust Bump Flow Problem, with LHS Multi Level Monte Carlo Samples""" def initialize(self): # Need to modify this dictionary when we change the SA constants #import pdb; pdb.set_trace() #sys.stdout = open(os.devnull, "w") self.aoptions = aeroOptions self.woptions = warpOptions self.ooptions = optOptions self.uoptions = uqOptions self.Pr = 0. # Generate FFD and DVs if rank == 0: rank0dvg = pf.createFFD() else: rank0dvg = None self.DVGeo = comm.bcast(rank0dvg, root=0) # Get a full list of every mesh name we have, assume they are ordered properly by level self.meshnames = self.uoptions['gridFileLevels'] self.Lmax = len(self.meshnames) #as many levels as we have meshes self.mord = [*range(self.Lmax)] self.mord.reverse() if self.Lmax < 3: raise ValueError('Not enough meshes available, ' + self.Lmax + ' < 3') # Spalart Allmaras model constants, to be changed in UQ (4 for now) saconstsm = [0.41, 0.1355, 0.622, 0.66666666667] self.saconstsb = [7.1, 0.3, 2.0, 1.0, 2.0, 1.2, 0.5, 2.0] self.saconsts = saconstsm + self.saconstsb self.aoptions['SAConsts'] = self.saconsts #self.gridSol = f'{meshname}_{saconstsm}_sol' solname = self.ooptions['prob_name'] self.gridSol = f'{solname}_sol' self.cases = [] #self.nsp = [] #keep track per level self.samplep = [] self.naddedtot = [] for i in range(self.Lmax): self.naddedtot.append(0) self.aps = [] self.solvers = [] self.meshes = [] self.NS0 = self.uoptions['NS0'] self.current_samples = self.NS0 * self.Lmax self.N1 = None self.a1 = None self.P = self.uoptions['P'] ########## # call distribute samples here? if not self.uoptions['use-predetermined-samples']: self.MFMC() else: self.N1 = self.uoptions['predet-N1'] self.a1 = self.uoptions['predet-a1'] self.dist_samples() ########## # Set constraints, should only need one of those solvers, the meshes are all the same if rank == 0: self.DVCon = DVConstraints() self.DVCon2 = DVConstraints() self.DVCon.setDVGeo( self.solvers[0][0].DVGeo.getFlattenedChildren()[1]) self.DVCon2.setDVGeo(self.solvers[0][0].DVGeo) self.DVCon.setSurface( self.solvers[0][0].getTriangulatedMeshSurface( groupName='allSurfaces')) # set extra group for surface area condition self.DVCon2.setSurface( self.solvers[0][0].getTriangulatedMeshSurface(), name='wall') # DV should be same into page (not doing anything right now) #import pdb; pdb.set_trace() lIndex = self.solvers[0][0].DVGeo.getFlattenedChildren( )[1].getLocalIndex(0) indSetA = [] indSetB = [] nXc = optOptions['NX'] self.NC = math.trunc( ((1.0 - self.ooptions['DVFraction']) * self.ooptions['NX'])) ind = [ int(nXc / 2) - int(self.NC / 2), int(nXc / 2) + int(self.NC / 2) ] for i in range(ind[0], ind[1]): indSetA.append(lIndex[i, 0, 1]) indSetB.append(lIndex[i, 1, 1]) # for i in range(lIndex.shape[0]): # indSetA.append(lIndex[i, 0, 1]) # indSetB.append(lIndex[i, 1, 1]) self.DVCon.addLinearConstraintsShape(indSetA, indSetB, factorA=1.0, factorB=-1.0, lower=0, upper=0, name='eqs') # Thickness constraints (one for each active DV) #import pdb; pdb.set_trace() # Maximum thickness of the domain, translates to minimum thickness of bump ub = 1.0 - self.ooptions['DCMinThick'] tcf = self.ooptions['DCThickFrac'] ra = self.ooptions['bumpBounds'] lim = self.ooptions['DCMinArea'] span = numpy.linspace(0, 1, nXc) xc = span * (ra[1] - ra[0]) + ra[0] #ind = range(int(nXc/2) - int(self.NC/2), int(nXc/2) + int(self.NC/2))) ind = [ int(nXc / 2) - int(tcf * self.NC / 2), int(nXc / 2) + int(tcf * self.NC / 2) ] ptList = numpy.zeros([2, 3]) ptList[:, 0] = xc[ind] ptList[:, 1] = 0.5 ptList[:, 2] = 0.5 if self.ooptions['use_area_con']: self.DVCon2.addSurfaceAreaConstraint(lower=lim, upper=10., name='sas', surfaceName='wall') else: self.DVCon2.addThicknessConstraints1D(ptList, self.NC, [0, 0, 1], lower=0.5, upper=ub, name='tcs') dummy = rank dsum = comm.allgather(dummy) sys.stdout = sys.__stdout__ def setup(self): #initialize shape and set deformation points as inputs a_init = self.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] # mult = numpy.linspace(1.0,1.5,num=int(0.5*len(a_init['pnts']))) # mult = numpy.concatenate((mult, mult)) #if self.ooptions['run_once']: # a_init['pnts'] = self.ooptions['ro_shape'] #a_init['pnts'] = numpy.multiply(mult, a_init['pnts']) self.add_input('a', a_init['pnts'], desc="Bump Shape Control Points") #self.add_input('a', 0.2, desc="Bump Shape Control Points") if self.ooptions['use_area_con']: self.add_output('SA', 1.0, desc='Surface Area Constraint') else: self.add_output('TC', numpy.zeros(self.NC), desc='Thickness Constraints') self.add_output('Cd_m', 0.0, desc="Mean Drag Coefficient") self.add_output('Cd_v', 0.0, desc="Variance Drag Coefficient") self.add_output('Cd_r', 0.0, desc="Robust Drag Objective") self.add_output('EQ', numpy.zeros(int(len(a_init['pnts']) / 2)), desc="Control Point Symmetry") self.add_output('N1', self.N1, desc="MFMC Samples at each level") self.add_output('a1', self.a1, desc="MFMC Coeffs at each level") self.add_output('Pr', 0., desc="MFMC Samples at each level") def setup_partials(self): self.declare_partials('Cd_m', 'a', method='exact') self.declare_partials('Cd_v', 'a', method='exact') self.declare_partials('Cd_r', 'a', method='exact') if self.ooptions['use_area_con']: self.declare_partials('SA', 'a', method='exact') else: self.declare_partials('TC', 'a', method='exact') self.declare_partials('EQ', 'a', method='exact') def compute(self, inputs, outputs): # run the bump shape model #import pdb; pdb.set_trace() dvdict = {'pnts': inputs['a']} ntot = self.current_samples nslp = [] nslt = [] for k in range(self.Lmax): nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) funcs = {} sump = [] musp = [] sum1 = [] mus = [] sumpm = [] muspm = [] summ = [] musm = [] E = numpy.zeros(self.Lmax) V = numpy.zeros(self.Lmax) for k in range(self.Lmax): sump.append(0.0) musp.append(numpy.zeros(nslp[k])) sumpm.append(0.) if k > 0: muspm.append(numpy.zeros(nslp[k - 1])) else: muspm.append(numpy.zeros(nslp[k])) #not used for i in range(nslp[k]): saconstsm = self.samplep[self.Lmax - 1][i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[k][i].setOption('SAConsts', self.saconsts) self.solvers[k][i].DVGeo.setDesignVars(dvdict) self.aps[k][i].setDesignVars(dvdict) self.solvers[k][i](self.aps[k][i]) self.solvers[k][i].evalFunctions(self.aps[k][i], funcs) astr = self.gridSol + "_" + str(self.cases[k][rank][i]) + "_cd" musp[k][i] = funcs[astr] sump[k] += funcs[astr] if k > 0 and i < nslp[k - 1]: muspm[k][i] = -funcs[astr] sumpm[k] += -funcs[astr] # compute mean and variance for k in range(self.Lmax): sum1.append(comm.allreduce(sump[k])) mus.append(comm.allgather(musp[k])) summ.append(comm.allreduce(sumpm[k])) musm.append(comm.allgather(muspm[k])) #import pdb; pdb.set_trace() if k > 0: E[k] = (sum1[k] / nslt[k] + summ[k] / nslt[k - 1]) else: E[k] = (sum1[k] / nslt[k]) sum2 = 0. for i in range(len(mus[k])): #loop over processors for j in range(len( mus[k][i])): #loop over samples on processors if k > 0: #sum2 = 0 sum2 += ( (mus[k][i][j] - sum1[k] / nslt[k])**2) / nslt[k] if j < len(mus[k - 1][i]): sum2 -= ((musm[k][i][j] - summ[k] / nslt[k - 1])** 2) / nslt[k - 1] else: sum2 += ((mus[k][i][j] - E[k])**2) / nslt[k] V[k] = sum2 #import pdb; pdb.set_trace() if rank == 0: self.DVCon.evalFunctions(funcs, includeLinear=True) self.DVCon2.evalFunctions(funcs, includeLinear=True) eq0 = funcs['eqs'] sa0 = funcs['sas'] else: eq0 = None sa0 = None eq = comm.bcast(eq0, root=0) sa = comm.bcast(sa0, root=0) outputs['Cd_m'] = numpy.dot(self.a1, E) outputs['Cd_v'] = numpy.dot(self.a1, V) #by assumption rho = self.uoptions['rho'] outputs['Cd_r'] = numpy.dot(self.a1, E) + rho * math.sqrt(numpy.dot(self.a1, V)) #import pdb; pdb.set_trace() if self.ooptions['use_area_con']: outputs['SA'] = sa else: outputs['TC'] = funcs['tcs'] outputs['EQ'] = eq outputs['N1'] = self.N1 outputs['a1'] = self.a1 outputs['Pr'] = self.Pr #outputs['Cd'] = inputs['a']*inputs['a'] def compute_partials(self, inputs, J): dvdict = {'pnts': inputs['a']} funcs = {} funcSens = {} nslp = [] nslt = [] for k in range(self.Lmax): nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) sump = [] musp = [] sum1 = [] sum2 = [] mus = [] sumpm = [] muspm = [] summ = [] musm = [] pmup = [] psump = [] #numpy.zeros(len(inputs['a'])) pmu = [] psum1 = [] pmupm = [] psumpm = [] #numpy.zeros(len(inputs['a'])) pmum = [] psumm = [] E = numpy.zeros(self.Lmax) V = numpy.zeros(self.Lmax) #import pdb; pdb.set_trace() for k in range(self.Lmax): sump.append(0.0) musp.append(numpy.zeros(nslp[k])) sumpm.append(0.) if k > 0: muspm.append(numpy.zeros(nslp[k - 1])) else: muspm.append(numpy.zeros(nslp[k])) #not used pmup.append([]) psump.append(numpy.zeros(len(inputs['a']))) pmupm.append([]) psumpm.append(numpy.zeros(len(inputs['a']))) for i in range(nslp[k]): saconstsm = self.samplep[k][i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[k][i].setOption('SAConsts', self.saconsts) self.solvers[k][i].DVGeo.setDesignVars(dvdict) self.aps[k][i].setDesignVars(dvdict) self.solvers[k][i](self.aps[k][i]) self.solvers[k][i].evalFunctions(self.aps[k][i], funcs) self.solvers[k][i].evalFunctionsSens(self.aps[k][i], funcSens, ['cd']) astr = self.gridSol + "_" + str(self.cases[k][rank][i]) + "_cd" arr = [ x * (self.a1[k] / nslt[k]) for x in funcSens[astr]['pnts'] ] musp[k][i] = funcs[astr] sump[k] += funcs[astr] pmup[k].append(arr[0]) psump[k] += arr[0] if k > 0 and i < nslp[k - 1]: arrm = [ x * (self.a1[k] / nslt[k - 1]) for x in funcSens[astr]['pnts'] ] muspm[k][i] = -funcs[astr] sumpm[k] += -funcs[astr] pmupm[k].append(-arrm[0]) psumpm[k] -= arrm[0] # compute mean and variance psum2 = numpy.zeros(len(inputs['a'])) for k in range(self.Lmax): sum1.append(comm.allreduce(sump[k])) mus.append(comm.allgather(musp[k])) summ.append(comm.allreduce(sumpm[k])) musm.append(comm.allgather(muspm[k])) psum1.append(comm.allreduce(psump[k])) pmu.append(comm.allgather(pmup[k])) psumm.append(comm.allreduce(psumpm[k])) pmum.append(comm.allgather(pmupm[k])) if k > 0: E[k] = (sum1[k] / nslt[k] + summ[k] / nslt[k - 1]) else: E[k] = (sum1[k] / nslt[k]) sum2 = 0. for i in range(len(mus[k])): #loop over processors for j in range(len( mus[k][i])): #loop over samples on processors if k > 0: #sum2 = 0 sum2 += ( (mus[k][i][j] - sum1[k] / nslt[k])**2) / nslt[k] if j < len(mus[k - 1][i]): sum2 -= ((musm[k][i][j] - summ[k] / nslt[k - 1])** 2) / nslt[k - 1] else: sum2 += ((mus[k][i][j] - E[k])**2) / nslt[k] V[k] = sum2 # compute variance sensitivity psum2 = [] for k in range(self.Lmax): psum2.append(numpy.zeros(len(inputs['a']))) for i in range(len(mus[0])): #loop over processors for j in range(len( mus[0][i])): #loop over samples on processors if k > 0: temp = pmu[k][i][j] * nslt[k] - psum1[k] arr2 = [ x * 2 * (mus[k][i][j] - sum1[k]) / nslt[k] for x in temp ] psum2[k] += arr2 if j < len(mus[k - 1][i]): temp = pmu[k][i][j] * nslt[k - 1] - psum1[k] arr2 = [ x * 2 * (musm[k][i][j] - summ[k] / nslt[k - 1]) / nslt[k - 1] for x in temp ] psum2[k] -= arr2 else: temp = pmu[k][i][j] * nslt[k] - psum1[k] arr2 = [ x * 2 * (mus[k][i][j] - E[k]) / nslt[k] for x in temp ] psum2[k] += arr2 #import pdb; pdb.set_trace() if rank == 0: self.DVCon.evalFunctionsSens(funcSens, includeLinear=True) self.DVCon2.evalFunctionsSens(funcSens, includeLinear=True) eq0p = funcSens['eqs']['pnts'] sa0p = funcSens['sas']['pnts'] else: eq0p = None sa0p = None eqp = comm.bcast(eq0p, root=0) sap = comm.bcast(sa0p, root=0) J['Cd_m', 'a'] = sum(psum1) + sum(psumm) J['Cd_v', 'a'] = sum(psum2) rho = self.uoptions['rho'] J['Cd_r', 'a'] = sum(psum1) + sum(psumm) + rho * ( 1. / (2 * math.sqrt(numpy.dot(self.a1, V)))) * sum(psum2) if self.ooptions['use_area_con']: J['SA', 'a'] = sap else: J['TC', 'a'] = funcSens['tcs']['pnts'] J['EQ', 'a'] = eqp #J['Cd','a'][0] = 2*inputs['a'] def MFMC(self): # Use an MFMC algorithm to determine optimal sample distribution and coefficients among mesh levels # We do this once before optimization, then compute statistics with the same set of samples and coeffs at every iteration # start with initial samples # Get a set of UQ sample points (LHS), enough for each level at the start #sys.stdout = open(os.devnull, "w") # flow characteristics alpha = 0.0 mach = self.ooptions['mach'] #0.95 Re = self.ooptions['Re'] #50000 Re_L = 1.0 tempR = 540 arearef = 2.0 chordref = 1.0 a_init = self.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] self.current_samples = self.NS0 * self.Lmax if rank == 0: rank0sam = plate_sa_lhs.genLHS(s=self.current_samples) else: rank0sam = None self.sample = comm.bcast(rank0sam, root=0) N1 = [] a1 = numpy.zeros(self.Lmax) r1 = numpy.zeros(self.Lmax) # Scatter samples on each level, multi-point parallelism for i in range(self.Lmax): self.cases.append(divide_cases(self.NS0, size)) for j in range(len(self.cases[i])): for k in range(len(self.cases[i][j])): self.cases[i][j][k] += i * self.NS0 #self.nsp.append(len(self.cases[i][rank]))#int(ns/size) # samples per processor self.samplep.append(self.sample[self.cases[i][rank]]) #import pdb; pdb.set_trace() #self.samplep = self.sample[self.cases[rank]]#self.sample[(rank*self.nsp):(rank*self.nsp+(self.nsp))] #shouldn't really need to "scatter" per se #import pdb; pdb.set_trace() # for i in range(self.Lmax): # assert len(self.samplep[i]) == self.nsp[i] # Create solvers for the preliminary data nslp = [] nslt = [] for k in range(self.Lmax): alist = [] slist = [] mlist = [] nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) for i in range(nslp[k]): namestr = self.gridSol + "_" + str(self.cases[k][rank][i]) # create meshes leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[self.mord[k]] #import pdb; pdb.set_trace() mlist.append(USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[self.mord[k]] alist.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep(0.1) # this solves a few problems for some reason # create solvers slist.append(ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) # if not self.ooptions['run_once']: # saconstsm = self.samplep[i].tolist() # else: saconstsm = self.samplep[0][i].tolist() self.saconsts = saconstsm + self.saconstsb slist[i].setOption('SAConsts', self.saconsts) slist[i].setDVGeo(self.DVGeo) slist[i].setMesh(mlist[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist[i].allWallsGroup) slist[i].DVGeo.addPointSet(coords, 'coords') self.aps.append(alist) self.solvers.append(slist) self.meshes.append(mlist) # Solve the preliminary samples # start looping over mesh levels sumt = [] sumtp = [] nslp = [] nslt = [] sum1 = [] mus = [] sump = [] musp = [] sumpm = [] muspm = [] summ = [] musm = [] Et = numpy.zeros(self.Lmax) E = numpy.zeros(self.Lmax) V = numpy.zeros(self.Lmax) S = numpy.zeros(self.Lmax) N1 = [] for k in range(self.Lmax): nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) dvdict = {'pnts': a_init['pnts']} funcs = {} sumtp.append(0.0) sump.append(0.) musp.append(numpy.zeros(nslp[k])) sumpm.append(0.) muspm.append(numpy.zeros(nslp[k])) for i in range(nslp[k]): # just do this again in case saconstsm = self.samplep[0][i].tolist() self.saconsts = saconstsm + self.saconstsb self.solvers[k][i].setOption('SAConsts', self.saconsts) self.solvers[k][i].DVGeo.setDesignVars(dvdict) self.aps[k][i].setDesignVars(dvdict) pc0 = time.process_time() self.solvers[k][i](self.aps[k][i]) self.solvers[k][i].evalFunctions(self.aps[k][i], funcs) pc1 = time.process_time() astr = self.gridSol + "_" + str(self.cases[k][rank][i]) + "_cd" musp[k][i] = funcs[astr] sump[k] += funcs[astr] sumtp[k] += (pc1 - pc0) # compute mean and variance estimate from start up samples for k in range(self.Lmax): sumt.append(comm.allreduce(sumtp[k])) sum1.append(comm.allreduce(sump[k])) mus.append(comm.allgather(musp[k])) summ.append(comm.allreduce(sumpm[k])) musm.append(comm.allgather(muspm[k])) mus[k] = numpy.concatenate(mus[k][:]) musm[k] = numpy.concatenate(musm[k][:]) #import pdb; pdb.set_trace() # mean at each level Et[k] = sumt[k] / nslt[k] E[k] = (sum1[k]) / nslt[k] #+summ[k] sum2 = 0. for i in range(len(mus[k])): #loop over processors sum2 += (mus[k][i] - E[k])**2 V[k] = sum2 / nslt[k] S[k] = math.sqrt(V[k]) # compute correlation matrix and rearrange models if necessary ordered = False while not ordered: rho = numpy.corrcoef(mus) ordered = True # check if contradicted #tarr = rho[0,1:] for k in range(self.Lmax - 2): test = rho[0, 1 + k]**2 - rho[0, 2 + k]**2 if test < 0: ordered = False tarr = -rho[0, :]**2 if not ordered: sind = numpy.argsort(tarr) #import pdb; pdb.set_trace() self.mord[:] = [self.mord[i] for i in sind] E[:] = [E[i] for i in sind] Et[:] = [Et[i] for i in sind] V[:] = [V[i] for i in sind] S[:] = [S[i] for i in sind] mus[:] = [mus[i] for i in sind] # now compute N1 and a1 using sigma, rho, w, and p for k in range(self.Lmax): a1[k] = S[0] * rho[0, k] / S[k] if k == 0: r1[k] = 1 elif k == self.Lmax - 1: work = Et[0] * (rho[0, k]**2) work /= Et[k] * (1 - rho[0, 1]**2) r1[k] = math.sqrt(work) else: work = Et[0] * (rho[0, k - 1]**2 - rho[0, k]**2) work /= Et[k] * (1 - rho[0, 1]**2) r1[k] = math.sqrt(work) for k in range(self.Lmax): N1.append(0) nsf = self.P / numpy.dot(Et, r1) N1[0] = math.ceil(nsf) for k in range(self.Lmax): nsf = N1[0] * r1[k] N1[k] = math.ceil(nsf) # limit the number of samples on the last one to pass the sanity check, for debug sanity = numpy.dot(N1, Et) if sanity > 1.2 * self.P: N1n = (self.P - numpy.dot(N1[0:self.Lmax - 2], Et[0:self.Lmax - 2])) / Et[self.Lmax - 1] N1[self.Lmax - 1] = math.ceil(N1n) self.Pr = numpy.dot(N1, Et) self.N1 = N1 self.a1 = a1 #import pdb; pdb.set_trace() if rank == 0: print("MFMC Completed, Samples per level: ", N1) # call dist_samples after this def dist_samples(self): # If we already have the number of samples, just create as many solvers as needed at each level # Just do this after running MLMC() anyway # flow characteristics alpha = 0.0 mach = self.ooptions['mach'] #0.95 Re = self.ooptions['Re'] #50000 Re_L = 1.0 tempR = 540 arearef = 2.0 chordref = 1.0 a_init = self.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] self.current_samples = sum(self.N1) if rank == 0: rank0sam = plate_sa_lhs.genLHS(s=self.current_samples) else: rank0sam = None self.sample = comm.bcast(rank0sam, root=0) #import pdb; pdb.set_trace() # Scatter samples on each level, multi-point parallelism self.cases = [] self.samplep = [] for i in range(self.Lmax): self.cases.append(divide_cases(self.N1[i], size)) for j in range(len(self.cases[i])): for k in range(len(self.cases[i][j])): self.cases[i][j][k] += sum(self.N1[0:i]) #self.nsp.append(len(self.cases[i][rank]))#int(ns/size) # samples per processor self.samplep.append(self.sample[self.cases[i][rank]]) # Actually create solvers (and aeroproblems?) (and mesh?) now self.aps = [] self.solvers = [] self.meshes = [] nslp = [] nslt = [] for k in range(self.Lmax): alist = [] slist = [] mlist = [] nslp.append(len(self.cases[k][rank])) nslt.append(sum([len(self.cases[k][x]) for x in range(size)])) for i in range(nslp[k]): namestr = self.gridSol + "_" + str(self.cases[k][rank][i]) # create meshes leveloptions = self.woptions leveloptions['gridFile'] = self.meshnames[self.mord[k]] mlist.append(USMesh(options=leveloptions, comm=MPI.COMM_SELF)) # create aeroproblems aloptions = self.aoptions aloptions['gridFile'] = self.meshnames[self.mord[k]] alist.append( AeroProblem(name=namestr, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=tempR, areaRef=arearef, chordRef=chordref, evalFuncs=['cd'])) time.sleep(0.1) # this solves a few problems for some reason # create solvers slist.append(ADFLOW(options=aloptions, comm=MPI.COMM_SELF)) saconstsm = self.samplep[self.Lmax - 1][i].tolist() self.saconsts = saconstsm + self.saconstsb slist[i].setOption('SAConsts', self.saconsts) slist[i].setDVGeo(self.DVGeo) slist[i].setMesh(mlist[i]) coords = slist[i].getSurfaceCoordinates( groupName=slist[i].allWallsGroup) slist[i].DVGeo.addPointSet(coords, 'coords') self.aps.append(alist) self.solvers.append(slist) self.meshes.append(mlist)
class PlateComponent(om.ExplicitComponent): """Deterministic Bump Flow Problem""" def initialize(self): # Need to modify this dictionary when we change the SA constants sys.stdout = open(os.devnull, "w") self.aoptions = aeroOptions self.woptions = warpOptions self.ooptions = optOptions # Generate FFD and DVs self.DVGeo = pf.createFFD() # starting flat mesh meshname = self.aoptions['gridFile'] gridFile = meshname # flow characteristics alpha = 0.0 mach = self.ooptions['mach']#0.95 Re = self.ooptions['Re']#50000 Re_L = 1.0 temp = 540 arearef = 2.0 chordref = 1.0 # Spalart Allmaras model constants, to be changed in UQ saconstsm = [0.41, 0.1355, 0.622, 0.66666666667, 7.1, 0.3, 2.0] self.saconsts = saconstsm + [1.0, 2.0, 1.2, 0.5, 2.0] self.aoptions['SAConsts'] = self.saconsts #self.gridSol = f'{meshname}_{saconstsm}_sol' solname = self.ooptions['prob_name'] self.gridSol = f'{solname}_sol' # Aerodynamic problem description self.ap = AeroProblem(name=self.gridSol, alpha=alpha, mach=mach, reynolds=Re, reynoldsLength=Re_L, T=temp, areaRef=arearef, chordRef=chordref, evalFuncs=['cd']) # Create solver self.CFDSolver = ADFLOW(options=self.aoptions, comm=MPI.COMM_WORLD) self.CFDSolver.setDVGeo(self.DVGeo) # Set up mesh warping self.mesh = USMesh(options=self.woptions, comm=MPI.COMM_WORLD) self.CFDSolver.setMesh(self.mesh) # Try setting the DVGeo coordinates here coords = self.CFDSolver.getSurfaceCoordinates(groupName=self.CFDSolver.allWallsGroup) self.CFDSolver.DVGeo.addPointSet(coords, 'coords') self.CFDSolver.DVGeo.getFlattenedChildren()[1].writePlot3d("ffdp_opt_def.xyz") # Set constraints self.DVCon = DVConstraints() self.DVCon2 = DVConstraints() self.DVCon.setDVGeo(self.CFDSolver.DVGeo.getFlattenedChildren()[1]) self.DVCon2.setDVGeo(self.CFDSolver.DVGeo) self.DVCon.setSurface(self.CFDSolver.getTriangulatedMeshSurface(groupName='allSurfaces')) # set extra group for surface area condition self.DVCon2.setSurface(self.CFDSolver.getTriangulatedMeshSurface(), name='wall') # DV should be same into page (not doing anything right now) #import pdb; pdb.set_trace() lIndex = self.CFDSolver.DVGeo.getFlattenedChildren()[1].getLocalIndex(0) indSetA = [] indSetB = [] nXc = optOptions['NX'] self.NC = math.trunc(((1.0 - self.ooptions['DVFraction'])*self.ooptions['NX'])) ind = [int(nXc/2) - int(self.NC/2), int(nXc/2) + int(self.NC/2)] for i in range(ind[0], ind[1]): indSetA.append(lIndex[i, 0, 1]) indSetB.append(lIndex[i, 1, 1]) # for i in range(lIndex.shape[0]): # indSetA.append(lIndex[i, 0, 1]) # indSetB.append(lIndex[i, 1, 1]) self.DVCon.addLinearConstraintsShape(indSetA, indSetB, factorA=1.0, factorB=-1.0, lower=0, upper=0, name='eqs') # Thickness constraints (one for each active DV) #import pdb; pdb.set_trace() # Maximum thickness of the domain, translates to minimum thickness of bump ub = 1.0 - self.ooptions['DCMinThick'] tcf = self.ooptions['DCThickFrac'] ra = self.ooptions['bumpBounds'] lim = self.ooptions['DCMinArea'] span = numpy.linspace(0, 1, nXc) xc = span * (ra[1] - ra[0]) + ra[0] #ind = range(int(nXc/2) - int(self.NC/2), int(nXc/2) + int(self.NC/2))) ind = [int(nXc/2) - int(tcf*self.NC/2), int(nXc/2) + int(tcf*self.NC/2)] ptList = numpy.zeros([2, 3]) ptList[:,0] = xc[ind] ptList[:,1] = 0.5 ptList[:,2] = 0.5 if self.ooptions['use_area_con']: self.DVCon2.addSurfaceAreaConstraint(lower=lim, upper=10., name='sas', surfaceName='wall') else: self.DVCon2.addThicknessConstraints1D(ptList, self.NC, [0,0,1], lower=0.5, upper=ub, name='tcs') sys.stdout = sys.__stdout__ def setup(self): #initialize shape and set deformation points as inputs a_init = self.CFDSolver.DVGeo.getValues() a_init['pnts'][:] = self.ooptions['DVInit'] # mult = numpy.linspace(1.0,1.5,num=int(0.5*len(a_init['pnts']))) # mult = numpy.concatenate((mult, mult)) # a_init['pnts'] = numpy.multiply(mult, a_init['pnts']) #if self.ooptions['run_once']: # a_init['pnts'] = self.ooptions['ro_shape'] self.add_input('a', a_init['pnts'], desc="Bump Shape Control Points") #self.add_input('a', 0.2, desc="Bump Shape Control Points") if self.ooptions['use_area_con']: self.add_output('SA', 1.0, desc='Surface Area Constraint') else: self.add_output('TC', numpy.zeros(self.NC), desc='Thickness Constraints') self.add_output('Cd', 0.0, desc="Drag Coefficient") self.add_output('EQ', numpy.zeros(int(len(a_init['pnts'])/2)), desc="Control Point Symmetry") def setup_partials(self): self.declare_partials('Cd','a', method='exact') if self.ooptions['use_area_con']: self.declare_partials('SA','a', method='exact') else: self.declare_partials('TC','a', method='exact') self.declare_partials('EQ','a', method='exact') def compute(self, inputs, outputs): # run the bump shape model # move the mesh #import pdb; pdb.set_trace() dvdict = {'pnts':inputs['a']} self.CFDSolver.DVGeo.setDesignVars(dvdict) self.ap.setDesignVars(dvdict) #self.CFDSolver.DVGeo.update("coords") # Solve and evaluate functions funcs = {} self.CFDSolver(self.ap) self.DVCon.evalFunctions(funcs, includeLinear=True) self.DVCon2.evalFunctions(funcs, includeLinear=True) self.CFDSolver.evalFunctions(self.ap, funcs) str = self.gridSol + '_cd' outputs['Cd'] = funcs[str] if self.ooptions['use_area_con']: outputs['SA'] = funcs['sas'] else: outputs['TC'] = funcs['tcs'] outputs['EQ'] = funcs['eqs'] #outputs['Cd'] = inputs['a']*inputs['a'] def compute_partials(self, inputs, J): # move the mesh #import pdb; pdb.set_trace() dvdict = {'pnts':inputs['a']} self.CFDSolver.DVGeo.setDesignVars(dvdict) self.ap.setDesignVars(dvdict) #self.CFDSolver.DVGeo.update("coords") funcSens = {} #self.CFDSolver(self.ap) #ASSUME WE ALREADY COMPUTED THE SOLUTION self.DVCon.evalFunctionsSens(funcSens, includeLinear=True) self.DVCon2.evalFunctionsSens(funcSens, includeLinear=True) self.CFDSolver.evalFunctionsSens(self.ap, funcSens, ['cd']) str = self.gridSol + '_cd' J['Cd','a'] = funcSens[str]['pnts'] if self.ooptions['use_area_con']: J['SA','a'] = funcSens['sas']['pnts'] else: J['TC','a'] = funcSens['tcs']['pnts'] J['EQ','a'] = funcSens['eqs']['pnts']