diff --git a/CMakeLists.txt b/CMakeLists.txt index c342e270cdbf091f67ca403443ad4792a7009dc5..ec130f661a7d716a7ad8c474bbb159802d10ceb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,11 +15,7 @@ # ---------------------------------------------------------------------------- PROJECT(DARTFlo) # ---------------------------------------------------------------------------- -CMAKE_MINIMUM_REQUIRED(VERSION 3.1) -IF(${CMAKE_VERSION} VERSION_GREATER "3.14.0") # we might want to update the project and use the NEW behavior here - cmake_policy(SET CMP0078 OLD) - cmake_policy(SET CMP0086 OLD) -ENDIF() +CMAKE_MINIMUM_REQUIRED(VERSION 3.14) # -- I/O # Lib/Exe dir @@ -60,17 +56,12 @@ ENDIF() # -- DEPENDENCIES # Python -IF (CMAKE_VERSION VERSION_LESS 3.12.0) - FIND_PACKAGE(PythonInterp 3.6 REQUIRED) - FIND_PACKAGE(PythonLibs 3.6 REQUIRED) -ELSE() - FIND_PACKAGE(Python3 COMPONENTS Interpreter Development) - # use Python3_ROOT_DIR if wrong python found (e.g. anaconda) - SET(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) - SET(PYTHON_LIBRARIES ${Python3_LIBRARIES}) - SET(PYTHON_INCLUDE_PATH ${Python3_INCLUDE_DIRS}) - SET(PYTHONLIBS_VERSION_STRING ${Python3_VERSION}) -ENDIF() +# use Python3_ROOT_DIR if wrong python found (e.g. anaconda) +FIND_PACKAGE(Python3 COMPONENTS Interpreter Development) +SET(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) +SET(PYTHON_LIBRARIES ${Python3_LIBRARIES}) +SET(PYTHON_INCLUDE_PATH ${Python3_INCLUDE_DIRS}) +SET(PYTHONLIBS_VERSION_STRING ${Python3_VERSION}) # SWIG FIND_PACKAGE(SWIG REQUIRED) diff --git a/dart/_src/CMakeLists.txt b/dart/_src/CMakeLists.txt index c78c46464f4be1f8f7788ca1f565e3e6e7a5e4ba..9cbe7fffcd239bac03021c362fee47f3b2c41b59 100644 --- a/dart/_src/CMakeLists.txt +++ b/dart/_src/CMakeLists.txt @@ -19,34 +19,17 @@ INCLUDE(${SWIG_USE_FILE}) FILE(GLOB SRCS *.h *.cpp *.inl *.swg) FILE(GLOB ISRCS *.i) -SET(CMAKE_SWIG_FLAGS "") SET_SOURCE_FILES_PROPERTIES(${ISRCS} PROPERTIES CPLUSPLUS ON) +SET(CMAKE_SWIG_FLAGS "-interface" "_dartw") # avoids "import _module_d" with MSVC/Debug +SWIG_ADD_LIBRARY(dartw LANGUAGE python SOURCES ${ISRCS} ${SRCS}) +SET_PROPERTY(TARGET dartw PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON) +MACRO_DebugPostfix(dartw) -SET(SWINCFLAGS --I${PROJECT_SOURCE_DIR}/dart/src --I${PROJECT_SOURCE_DIR}/ext/amfe/fwk/src --I${PROJECT_SOURCE_DIR}/ext/amfe/fwk/_src --I${PROJECT_SOURCE_DIR}/ext/amfe/tbox/src --I${PROJECT_SOURCE_DIR}/ext/amfe/tbox/_src -) -SET_SOURCE_FILES_PROPERTIES(${ISRCS} PROPERTIES SWIG_FLAGS "${SWINCFLAGS}") - -if (${CMAKE_VERSION} VERSION_LESS "3.8.0") - SWIG_ADD_MODULE(dartw python ${ISRCS} ${SRCS}) -else() - SWIG_ADD_LIBRARY(dartw LANGUAGE python SOURCES ${ISRCS} ${SRCS}) -endif() -MACRO_DebugPostfix(_dartw) - -TARGET_INCLUDE_DIRECTORIES(_dartw PRIVATE ${PROJECT_SOURCE_DIR}/dart/_src - ${PROJECT_SOURCE_DIR}/ext/amfe/fwk/_src - ${PROJECT_SOURCE_DIR}/ext/amfe/tbox/_src - ${PYTHON_INCLUDE_PATH} -) - -SWIG_LINK_LIBRARIES(dartw - dart tbox fwk ${PYTHON_LIBRARIES} -) +TARGET_INCLUDE_DIRECTORIES(dartw PRIVATE ${PROJECT_SOURCE_DIR}/dart/_src + ${PROJECT_SOURCE_DIR}/ext/amfe/tbox/_src + ${PROJECT_SOURCE_DIR}/ext/amfe/fwk/_src + ${PYTHON_INCLUDE_PATH}) +TARGET_LINK_LIBRARIES(dartw PRIVATE dart tbox fwk ${PYTHON_LIBRARIES}) INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/\${BUILD_TYPE}/dartw.py DESTINATION ${CMAKE_INSTALL_PREFIX}) -INSTALL(TARGETS _dartw DESTINATION ${CMAKE_INSTALL_PREFIX}) +INSTALL(TARGETS dartw DESTINATION ${CMAKE_INSTALL_PREFIX}) diff --git a/dart/api/core.py b/dart/api/core.py index 248a6a10a88877f11746837a59529bbc70d447f5..8359e35b75e2f6d53ff24ec03bb9c6cd26a4cee2 100644 --- a/dart/api/core.py +++ b/dart/api/core.py @@ -53,11 +53,10 @@ def initDart(cfg, scenario='aerodynamic', task='analysis'): Adjoint solver """ # Imports - import math - import fwk.wutils as wu + import dart import tbox import tbox.gmsh as gmsh - import dart + import math # Basic checks and config # dimension @@ -101,12 +100,11 @@ def initDart(cfg, scenario='aerodynamic', task='analysis'): nthrd = cfg['Threads'] else: nthrd = 1 - wu.initMKL(nthrd) # initialize threading layer and number of threads # verbosity if 'Verb' in cfg: verb = cfg['Verb'] else: - verb = 0 + verb = 1 # scenario and task type if scenario != 'aerodynamic' and scenario != 'aerostructural': raise RuntimeError('Scenario should be aerodynamic or aerostructural, but "' + scenario + '" was given!\n') @@ -124,17 +122,21 @@ def initDart(cfg, scenario='aerodynamic', task='analysis'): if _dim == 2: mshCrck = tbox.MshCrack(_msh, _dim) mshCrck.setCrack(cfg['Wake']) - mshCrck.addBoundaries([cfg['Fluid'], cfg['Farfield'][-1], cfg['Wing']]) + mshCrck.addBoundary(cfg['Fluid']) + mshCrck.addBoundary(cfg['Farfield'][-1]) + mshCrck.addBoundary(cfg['Wing']) mshCrck.run() else: for i in range(len(cfg['Wakes'])): mshCrck = tbox.MshCrack(_msh, _dim) mshCrck.setCrack(cfg['Wakes'][i]) - mshCrck.addBoundaries([cfg['Fluid'], cfg['Farfield'][-1], cfg['Wings'][i]]) + mshCrck.addBoundary(cfg['Fluid']) + mshCrck.addBoundary(cfg['Farfield'][-1]) + mshCrck.addBoundary(cfg['Wings'][i]) if 'Fuselage' in cfg: - mshCrck.addBoundaries([cfg['Fuselage']]) + mshCrck.addBoundary(cfg['Fuselage']) if 'Symmetry' in cfg: - mshCrck.addBoundaries([cfg['Symmetry']]) + mshCrck.addBoundary(cfg['Symmetry']) if 'WakeTips' in cfg: mshCrck.setExcluded(cfg['WakeTips'][i]) # 3D computations (not excluded for 2.5D) mshCrck.run() @@ -146,22 +148,24 @@ def initDart(cfg, scenario='aerodynamic', task='analysis'): # Mesh morpher creation if scenario == 'aerostructural' or task == 'optimization': - _mrf = tbox.MshDeform(_msh, tbox.Gmres(1, 1e-6, 30, 1e-8), _dim, nthrds=nthrd) + _mrf = tbox.MshDeform(_msh, tbox.Gmres(1, 1e-6, 30, 1e-8), _dim, nthrds=nthrd, vrb=verb) _mrf.setField(cfg['Fluid']) - _mrf.addFixed(cfg['Farfield']) + for tag in cfg['Farfield']: + _mrf.addFixed(tag) if _dim == 2: - _mrf.addMoving([cfg['Wing']]) - _mrf.addInternal([cfg['Wake'], cfg['Wake']+'_']) + _mrf.addMoving(cfg['Wing']) + _mrf.addInternal(cfg['Wake'], cfg['Wake']+'_') else: if 'Fuselage' in cfg: _mrf.addFixed(cfg['Fuselage']) - _mrf.setSymmetry(cfg['Symmetry'], 1) + if 'Symmetry' in cfg: + _mrf.setSymmetry(cfg['Symmetry'], 1) for i in range(len(cfg['Wings'])): if i == 0: - _mrf.addMoving([cfg['Wings'][i]]) # TODO body of interest (FSI/OPT) is always first given body + _mrf.addMoving(cfg['Wings'][i]) # TODO body of interest (FSI/OPT) is always first given body else: - _mrf.addFixed([cfg['Wings'][i]]) - _mrf.addInternal([cfg['Wakes'][i], cfg['Wakes'][i]+'_']) + _mrf.addFixed(cfg['Wings'][i]) + _mrf.addInternal(cfg['Wakes'][i], cfg['Wakes'][i]+'_') _mrf.initialize() else: _mrf = None diff --git a/dart/api/internal/polar.py b/dart/api/internal/polar.py index 69f5968bda90344dcc97352f842cf217c33ceb1b..f4355f3e0f4e35f1e2832cd6263982971a96a6e5 100644 --- a/dart/api/internal/polar.py +++ b/dart/api/internal/polar.py @@ -81,34 +81,33 @@ class Polar: """Compute the polar """ # initialize the loop - ac = 0 if self.alphas[0] == self.alphas[-1] else 1 self.Cls = [] self.Cds = [] self.Cms = [] print(ccolors.ANSI_BLUE + 'Sweeping AoA from', self.alphas[0], 'to', self.alphas[-1], ccolors.ANSI_RESET) - for alpha in self.alphas: + for i in range(len(self.alphas)): # define current angle of attack - alpha = alpha*math.pi/180 - # update problem + alpha = self.alphas[i]*math.pi/180 + acs = '_{:04d}'.format(i) + # update problem and reset ICs to improve robustness in transonic cases self.pbl.update(alpha) + self.sol.reset() # run the solver and save the results - print(ccolors.ANSI_BLUE + 'Running the solver for', alpha*180/math.pi, 'degrees', ccolors.ANSI_RESET) + print(ccolors.ANSI_BLUE + 'Running the solver for', self.alphas[i], 'degrees', ccolors.ANSI_RESET) self.tms["solver"].start() self.sol.run() self.tms["solver"].stop() - self.sol.save(self.wrtr, ac) + self.sol.save(self.wrtr, acs) # extract Cp if self.dim == 2: Cp = dU.extract(self.bnd.groups[0].tag.elems, self.sol.Cp) - tU.write(Cp, 'Cp_airfoil_'+str(ac)+'.dat', '%1.5e', ', ', 'alpha = '+str(alpha*180/math.pi)+' deg\nx, y, z, Cp', '') + tU.write(Cp, f'Cp_{self.msh.name}_airfoil{acs}.dat', '%1.5e', ', ', 'alpha = '+str(alpha*180/math.pi)+' deg\nx, y, z, Cp', '') elif self.dim == 3 and self.format == 'vtk' and self.slice: - dU.writeSlices(self.msh.name, self.slice, self.tag, ac) + dU.writeSlices(self.msh.name, self.slice, self.tag, acs) # extract force coefficients self.Cls.append(self.sol.Cl) self.Cds.append(self.sol.Cd) self.Cms.append(self.sol.Cm) - # end loop - ac+=1 def disp(self): """Display the results and draw the polar @@ -127,6 +126,6 @@ class Polar: print(self.tms) # plot results if self.alphas[0] != self.alphas[-1]: - tU.plot(self.alphas, self.Cls, "alpha", "Cl", "") - tU.plot(self.alphas, self.Cds, "alpha", "Cd", "") - tU.plot(self.alphas, self.Cms, "alpha", "Cm", "") + tU.plot(self.alphas, self.Cls, 'alpha', 'Cl', '') + tU.plot(self.alphas, self.Cds, 'alpha', 'Cd', '') + tU.plot(self.alphas, self.Cms, 'alpha', 'Cm', '') diff --git a/dart/api/internal/trim.py b/dart/api/internal/trim.py index 74c0025882f5de8f6be7732433c8500b8a5b109d..b277d9f8d85c2d371f6efeda3187d3017832c70f 100644 --- a/dart/api/internal/trim.py +++ b/dart/api/internal/trim.py @@ -86,8 +86,9 @@ class Trim: while True: # define current angle of attack print(ccolors.ANSI_BLUE + 'Setting AoA to', self.alpha*180/math.pi, ccolors.ANSI_RESET) - # update problem + # update problem and reset ICs to improve robustness in transonic cases self.pbl.update(self.alpha) + self.sol.reset() # run the solver self.tms["solver"].start() status = self.sol.run() @@ -112,7 +113,7 @@ class Trim: # extract Cp if self.dim == 2: Cp = dU.extract(self.bnd.groups[0].tag.elems, self.sol.Cp) - tU.write(Cp, "Cp_airfoil.dat", "%1.5e", ", ", "x, y, z, Cp", "") + tU.write(Cp, f'Cp_{self.msh.name}_airfoil.dat', '%1.5e', ', ', 'x, y, z, Cp', '') elif self.dim == 3 and self.format == 'vtk' and self.slice: dU.writeSlices(self.msh.name, self.slice, self.tag) diff --git a/dart/api/mphys.py b/dart/api/mphys.py index e59d6130a610c28eca75414bd025c3edf34aaa54..d23c81e436631b6d30747762b29c13479a8742a6 100644 --- a/dart/api/mphys.py +++ b/dart/api/mphys.py @@ -218,7 +218,7 @@ class DartSolver(om.ImplicitComponent): def initialize(self): self.options.declare('sol', desc='direct solver', recordable=False) self.options.declare('adj', desc='adjoint solver', recordable=False) - self.options.declare('raiseError', default=False, desc='raise AnalysisError when solver not fully converged') + self.options.declare('rerr', desc='raise AnalysisError when solver not fully converged') def setup(self): self.sol = self.options['sol'] @@ -239,7 +239,7 @@ class DartSolver(om.ImplicitComponent): # Compute self.sol.reset() # resets ICs (slows down convergence, but increases robustness for aero-structural) status = self.sol.run() - if (status == 1 and self.options['raiseError']) or status == 2: # max number of iterations exceeded or NaN in solution + if (status == 1 and self.options['rerr']) or status == 2: # max number of iterations exceeded or NaN in solution raise om.AnalysisError('DART solver failed to fully converge!\n') # Update outputs outputs['phi'] = self.sol.phi @@ -335,12 +335,9 @@ class DartLoads(om.ExplicitComponent): # Aerodynamic coefficients class DartCoefficients(om.ExplicitComponent): """Aerodynamic load coefficients for full aircraft - Also write solution to disk Attributes ---------- - iscn : int - ID of scenario (default: 0) sol : dart.Newton object Direct Newton sovler adj : dart.Adjoint object @@ -349,12 +346,8 @@ class DartCoefficients(om.ExplicitComponent): def initialize(self): self.options.declare('sol', desc='direct solver', recordable=False) self.options.declare('adj', desc='adjoint solver', recordable=False) - self.options.declare('wrtr', desc='data writer', recordable=False) - self.options.declare('iscn', default=0, desc='ID of the scenario') - self.options.declare('morph', default=False, desc='whether the mesh can deform or not') def setup(self): - self.iscn = self.options['iscn'] self.sol = self.options['sol'] self.adj = self.options['adj'] # I/O @@ -370,13 +363,6 @@ class DartCoefficients(om.ExplicitComponent): """Get the coefficients for the full aircraft """ # NB: inputs already up-to-date because DartSolver has already been run - # Write to disk - if self.options['morph'] and self.options['wrtr'].type() == 1: - if self.iscn == 0: - self.options['wrtr'].save(self.sol.pbl.msh.name) - else: - self.options['wrtr'].save(self.sol.pbl.msh.name + '_{0:d}'.format(self.iscn)) - self.sol.save(self.options['wrtr'], self.iscn) # Update outputs outputs['cl'] = self.sol.Cl outputs['cd'] = self.sol.Cd @@ -397,9 +383,53 @@ class DartCoefficients(om.ExplicitComponent): partials['cl', 'phi'] = dCfPhi[0] partials['cd', 'phi'] = dCfPhi[1] -# Aerodynamic group +# Aerodynamic writer +class DartWriter(om.ExplicitComponent): + """Write solution to disk + + Attributes + ---------- + sol : dart.Newton object + Direct Newton sovler + wrt : dart.MshExport object + Mesh and solution data writer + sname : string + Name of scenario + sall : bool + Whether to save results at each iteration to different files + it : int + Optimization iteration count + """ + def initialize(self): + self.options.declare('sol', desc='direct solver', recordable=False) + self.options.declare('wrt', desc='data writer', recordable=False) + self.options.declare('snam', desc='name of the scenario') + self.options.declare('sall', desc='whether to save results at each iteration to different files') + + def setup(self): + self.sol = self.options['sol'] + self.wrt = self.options['wrt'] + self.snam = self.options['snam'] if self.options['snam'] is not None else 'default' + self.sall = self.options['sall'] + self.it = 0 # optimization iteration count + + def compute(self, inputs, outputs): + """Write to disk + """ + # Create suffix + suffix = '_{:s}'.format(self.snam) + if self.sall: + suffix += '_{:04d}'.format(self.it) + self.it += 1 + # Write mesh to disk (.msh only) + if self.wrt.type() == 1: + self.wrt.save(self.sol.pbl.msh.name + suffix) + # Write solution to disk + self.sol.save(self.wrt, suffix) + +# Aerodynamic coupling group class DartGroup(om.Group): - """Aerodynamic group + """Aerodynamic coupling group Integrate the aerodynamic computations in an aero-structural coupling procedure Components @@ -414,17 +444,39 @@ class DartGroup(om.Group): self.options.declare('sol', desc='direct solver', recordable=False) self.options.declare('mrf', desc='mesh morpher', recordable=False) self.options.declare('adj', desc='adjoint solver', recordable=False) - self.options.declare('raiseError', default=False, desc='raise AnalysisError when solver not fully converged') + self.options.declare('rerr', desc='raise AnalysisError when solver not fully converged') def setup(self): # Components if self.options['mrf'] is None: - self.add_subsystem('morpher', DartDummyMorpher(dim = self.options['sol'].pbl.nDim, msh = self.options['sol'].pbl.msh), promotes_inputs=['x_aero'], promotes_outputs=['xv']) + self.add_subsystem('morpher', DartDummyMorpher(dim=self.options['sol'].pbl.nDim, msh=self.options['sol'].pbl.msh), promotes_inputs=['x_aero'], promotes_outputs=['xv']) else: - self.add_subsystem('morpher', DartMorpher(dim = self.options['sol'].pbl.nDim, bnd = self.options['bnd'], mrf = self.options['mrf'], adj = self.options['adj']), promotes_inputs=['x_aero'], promotes_outputs=['xv']) - self.add_subsystem('solver', DartSolver(sol = self.options['sol'], adj = self.options['adj'], raiseError = self.options['raiseError']), promotes_inputs=['aoa', 'xv'], promotes_outputs=['phi']) + self.add_subsystem('morpher', DartMorpher(dim=self.options['sol'].pbl.nDim, bnd=self.options['bnd'], mrf=self.options['mrf'], adj=self.options['adj']), promotes_inputs=['x_aero'], promotes_outputs=['xv']) + self.add_subsystem('solver', DartSolver(sol=self.options['sol'], adj=self.options['adj'], rerr=self.options['rerr']), promotes_inputs=['aoa', 'xv'], promotes_outputs=['phi']) if self.options['qinf'] is not None: - self.add_subsystem('loads', DartLoads(qinf = self.options['qinf'], bnd = self.options['bnd'], adj = self.options['adj']), promotes_inputs=['xv', 'phi'], promotes_outputs=['f_aero']) + self.add_subsystem('loads', DartLoads(qinf=self.options['qinf'], bnd=self.options['bnd'], adj=self.options['adj']), promotes_inputs=['xv', 'phi'], promotes_outputs=['f_aero']) + +# Aerodynamic post-coupling group +class DartPostGroup(om.Group): + """Aerodynamic post-coupling group + Update the aerodynamic load coefficients and write solution to disk + + Components + ---------- + - Coefficients + - Writer + """ + def initialize(self): + self.options.declare('sol', desc='direct solver', recordable=False) + self.options.declare('adj', desc='adjoint solver', recordable=False) + self.options.declare('wrt', desc='data writer', recordable=False) + self.options.declare('snam', desc='name of the scenario') + self.options.declare('sall', desc='whether to save results at each iteration to different files') + + def setup(self): + # Components + self.add_subsystem('coeff', DartCoefficients(sol=self.options['sol'], adj=self.options['adj']), promotes_inputs=['aoa', 'xv', 'phi'], promotes_outputs=['cl', 'cd']) + self.add_subsystem('writer', DartWriter(sol=self.options['sol'], wrt=self.options['wrt'], snam=self.options['snam'], sall=self.options['sall'])) # Builder class DartBuilder(Builder): @@ -436,8 +488,8 @@ class DartBuilder(Builder): Dimension of the problem __qinf : float Freestream dynamic pressure - __wrtr : tbox.MshExport object - Mesh writer + __wrt : tbox.MshExport object + Mesh and solution data writer __mrf : tbox.MshDeform object Mesh morpher __sol : dart.Newton object @@ -445,7 +497,7 @@ class DartBuilder(Builder): __adj : dart.Adjoint object Adjoint solver """ - def __init__(self, cfg, scenario='aerodynamic', task='analysis', scenarioId=0, raiseError=False): + def __init__(self, cfg, scenario='aerodynamic', task='analysis', saveAll=False, raiseError=False): """Instantiate the builder and save the parameters and options Parameters @@ -456,8 +508,8 @@ class DartBuilder(Builder): Type of scenario (available: aerodynamic, aerostructural) (default: aerodynamic) task : string, optional Type of task (available: analysis, optimization) (default: analysis) - scenarioId : int, optional - ID of the scenario, will be used in the output filename + saveAll : bool, optional + Whether to write results to different files at each iteration (default: False, results file will be overwritten) raiseError : bool, optional Whether to raise an openMDAO.AnalysisError when the solver does not fully converge (default: False) As of openMDAO V3.9.2, AnalysisError are only handled by pyOptSparse @@ -465,8 +517,8 @@ class DartBuilder(Builder): self.__cfg = cfg self.__scn = scenario self.__tsk = task - self.__sid = scenarioId - self.__raiseError = raiseError + self.__sall = saveAll + self.__rerr = raiseError def initialize(self, comm): """Instantiate and initialize all the solver components @@ -478,7 +530,7 @@ class DartBuilder(Builder): """ from .core import initDart def _init(): - self.__dim, self.__qinf, _, self.__wrtr, self.__mrf, _, self.__bnd, self.__sol, self.__adj = initDart(self.__cfg, scenario=self.__scn, task=self.__tsk) + self.__dim, self.__qinf, _, self.__wrt, self.__mrf, _, self.__bnd, self.__sol, self.__adj = initDart(self.__cfg, scenario=self.__scn, task=self.__tsk) # If n MPI processes, init DART on rank=0,...,n-1 sequentially to avoid issues with mesh IO # If mpi4py is not available or only 1 MPI process, init DART as usual try: @@ -500,20 +552,20 @@ class DartBuilder(Builder): except: _init() - def get_mesh_coordinate_subsystem(self): + def get_mesh_coordinate_subsystem(self, scenario_name=None): """Return openMDAO component to get the initial surface mesh coordinates """ - return DartMesh(dim = self.__dim, bnd = self.__bnd) + return DartMesh(dim=self.__dim, bnd=self.__bnd) - def get_coupling_group_subsystem(self): + def get_coupling_group_subsystem(self, scenario_name=None): """Return openMDAO group containing the morpher, solver and loads """ - return DartGroup(qinf = self.__qinf, bnd = self.__bnd, sol = self.__sol, mrf = self.__mrf, adj = self.__adj, raiseError = self.__raiseError) + return DartGroup(qinf=self.__qinf, bnd=self.__bnd, sol=self.__sol, mrf=self.__mrf, adj=self.__adj, rerr=self.__rerr) - def get_post_coupling_subsystem(self): - """Return openMDAO component that computes the aero coefficients and writes data to disk + def get_post_coupling_subsystem(self, scenario_name=None): + """Return openMDAO group that computes the aero coefficients and writes data to disk """ - return DartCoefficients(sol = self.__sol, adj = self.__adj, wrtr = self.__wrtr, iscn = self.__sid, morph = False if self.__mrf is None else True) + return DartPostGroup(sol=self.__sol, adj=self.__adj, wrt=self.__wrt, snam=scenario_name, sall=self.__sall) def get_number_of_nodes(self): """Return the number of surface nodes diff --git a/dart/benchmark/onera.py b/dart/benchmark/onera.py index b0f9c2157829c582e57c204ac18d53e18246d80d..36f048aa3666e342cda7650de1eddc6e49a00024 100644 --- a/dart/benchmark/onera.py +++ b/dart/benchmark/onera.py @@ -19,13 +19,13 @@ ## Compute flow around the Onera M6 wing at 3 degrees AOA and Mach 0.84 for benchmark # Adrien Crovato -import numpy as np import dart.default as floD import dart.utils as floU import tbox.utils as tbxU import fwk from fwk.testing import * from fwk.coloring import ccolors +import numpy as np def newton(pbl): from fwk.wutils import parseargs @@ -34,10 +34,10 @@ def newton(pbl): # Pardiso and GMRES should give similar perfs k = parseargs().k try: - newton = dart.Newton(pbl, tbox.Pardiso(), nthrds=k, vrb=2) + newton = dart.Newton(pbl, tbox.Pardiso(), nthrds=k, vrb=3) except: gmres = tbox.Gmres(2, 1e-6, 50, 1e-5) - newton = dart.Newton(pbl, gmres, nthrds=k, vrb=2) + newton = dart.Newton(pbl, gmres, nthrds=k, vrb=3) return newton def main(): diff --git a/dart/cases/coyote.py b/dart/cases/coyote.py index fc493e30324286c0bd5cf268fcb1b797bd2bda72..aa91bf51130009077de14cee8ecdb12e710b0328 100644 --- a/dart/cases/coyote.py +++ b/dart/cases/coyote.py @@ -30,7 +30,7 @@ def getParam(): # Arguments args = parseargs() p['Threads'] = args.k - p['Verb'] = args.verb + p['Verb'] = args.verb + 1 # Specific scl = 2 # scaling factor for lifting surface mesh size wrtems = scl*0.036 # wing root trailing edge mesh size diff --git a/dart/cases/n0012.py b/dart/cases/n0012.py index ed9f3dae1a3eca1532ab7c2b236c137d79d8173f..474b4516d82ecf7121462f0e23674b6aadbf0064 100644 --- a/dart/cases/n0012.py +++ b/dart/cases/n0012.py @@ -26,7 +26,7 @@ def getParam(): # Arguments args = parseargs() p['Threads'] = args.k - p['Verb'] = args.verb + p['Verb'] = args.verb + 1 # Input/Output p['File'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models/n0012.geo') # Input file containing the model p['Pars'] = {'xLgt' : 5, 'yLgt' : 5, 'msF' : 1.0, 'msTe' : 0.01, 'msLe' : 0.005} # Parameters for input file model diff --git a/dart/cases/n64A410.py b/dart/cases/n64A410.py index d033dcbdbec156fd160b65a4623c30d5c1ca1a53..b6a032343773fbbbe83e4630911d67c57505e12a 100644 --- a/dart/cases/n64A410.py +++ b/dart/cases/n64A410.py @@ -26,7 +26,7 @@ def getParam(): # Arguments args = parseargs() p['Threads'] = args.k - p['Verb'] = args.verb + p['Verb'] = args.verb + 1 # Input/Output p['File'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models/n64A410.geo') # Input file containing the model p['Pars'] = {'xLgt' : 5, 'yLgt' : 5, 'msF' : 1.0, 'msTe' : 0.01, 'msLe' : 0.005} # Parameters for input file model diff --git a/dart/cases/rae2822.py b/dart/cases/rae2822.py index 1b9c6cb4c07e7b9889e62c32c2365f655e930d1b..b18b12ea6cc7469c50a8245e09438f38186a0c62 100644 --- a/dart/cases/rae2822.py +++ b/dart/cases/rae2822.py @@ -26,7 +26,7 @@ def getParam(): # Arguments args = parseargs() p['Threads'] = args.k - p['Verb'] = args.verb + p['Verb'] = args.verb + 1 # Input/Output p['File'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models/rae2822.geo') # Input file containing the model p['Pars'] = {'xLgt' : 5, 'yLgt' : 5, 'msF' : 1.0, 'msTe' : 0.01, 'msLe' : 0.005} # Parameters for input file model diff --git a/dart/cases/wbht.py b/dart/cases/wbht.py index 73a8f1daaa682aca82263d5d7027baf1f2ecb3d8..0e6a5b00cfaed89cf70613da7d981863a7fe2bee 100644 --- a/dart/cases/wbht.py +++ b/dart/cases/wbht.py @@ -44,7 +44,7 @@ def getParam(): # Arguments args = parseargs() p['Threads'] = args.k - p['Verb'] = args.verb + p['Verb'] = args.verb + 1 # Input/Output p['File'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models/wbht.geo') # Input file containing the model p['Pars'] = {'msLeW0' : wrlems, 'msTeW0' : wrtems, diff --git a/dart/cases/wht.py b/dart/cases/wht.py index dd0fc7d10496d2bdafe0eacdc246bbcf7445c00e..1f216c56a6b8d90fc741d41b401c4474106da227 100644 --- a/dart/cases/wht.py +++ b/dart/cases/wht.py @@ -39,7 +39,7 @@ def getParam(): # Arguments args = parseargs() p['Threads'] = args.k - p['Verb'] = args.verb + p['Verb'] = args.verb + 1 # Input/Output p['File'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models/wht.geo') # Input file containing the model p['Pars'] = {'msLeW0' : wrlems, 'msTeW0' : wrtems, diff --git a/dart/default.py b/dart/default.py index 878ed741783c1c5dbea0eafad99b569a946e5027..c87cc94e64d8ab919264f7914284ce5b8c8eb2aa 100644 --- a/dart/default.py +++ b/dart/default.py @@ -23,7 +23,7 @@ from fwk.wutils import parseargs import dart import tbox -def mesh(dim, file, pars, bnd, wk = 'wake', wktp = None): +def mesh(dim, file, pars, bnds, wk = 'wake', wktp = None): """Initialize mesh and mesh writer Parameters @@ -34,7 +34,7 @@ def mesh(dim, file, pars, bnd, wk = 'wake', wktp = None): file contaning mesh (w/o extention) pars : dict parameters for mesh - bnd : array of str + bnds : array of str names of crack (wake) boundaries physical tag wk : str (optional) name of crack (wake) physical tag @@ -48,7 +48,8 @@ def mesh(dim, file, pars, bnd, wk = 'wake', wktp = None): # crack the mesh mshCrck = tbox.MshCrack(msh, dim) mshCrck.setCrack(wk) - mshCrck.addBoundaries(bnd) + for bnd in bnds: + mshCrck.addBoundary(bnd) if dim == 3 and wktp is not None: mshCrck.setExcluded(wktp) mshCrck.run() @@ -151,7 +152,7 @@ def picard(pbl): problem formulation """ args = parseargs() - solver = dart.Picard(pbl, tbox.Gmres(1, 1e-6, 30, 1e-8), nthrds=args.k, vrb=args.verb+1) + solver = dart.Picard(pbl, tbox.Gmres(1, 1e-6, 30, 1e-8), nthrds=args.k, vrb=args.verb+2) return solver def newton(pbl): @@ -164,7 +165,7 @@ def newton(pbl): """ from tbox.solvers import LinearSolver args = parseargs() - solver = dart.Newton(pbl, LinearSolver().pardiso(), nthrds=args.k, vrb=args.verb+1) + solver = dart.Newton(pbl, LinearSolver().pardiso(), nthrds=args.k, vrb=args.verb+2) return solver def morpher(msh, dim, mov, fxd = ['upstream', 'farfield', 'downstream'], fld = 'field', wk = 'wake', sym = 'symmetry'): @@ -190,11 +191,12 @@ def morpher(msh, dim, mov, fxd = ['upstream', 'farfield', 'downstream'], fld = ' args = parseargs() mshDef = tbox.MshDeform(msh, tbox.Gmres(1, 1e-6, 30, 1e-8), dim, nthrds=args.k) mshDef.setField(fld) - mshDef.addFixed(fxd) + for tag in fxd: + mshDef.addFixed(tag) mshDef.addMoving(mov) if dim == 3: mshDef.setSymmetry(sym, 1) - mshDef.addInternal([wk, wk+'_']) + mshDef.addInternal(wk, wk+'_') mshDef.initialize() return mshDef diff --git a/dart/src/wAdjoint.cpp b/dart/src/wAdjoint.cpp index 40b3b60b40afb7c74b3dbbc366e29bf1535737da..a404e630ee46fe79e5b79edce1f89932ce43240b 100644 --- a/dart/src/wAdjoint.cpp +++ b/dart/src/wAdjoint.cpp @@ -58,17 +58,23 @@ Adjoint::Adjoint(std::shared_ptr<Newton> _sol, std::shared_ptr<tbox::MshDeform> nthreads = sol->nthreads; verbose = sol->verbose; - // Linear solvers + // Linear solvers (if GMRES, use the same, but with a thighter tolerance) if (sol->linsol->type() == LSOLTYPE::GMRES_ILUT) { - // Use the same GMRES, but with a thighter tolerance std::shared_ptr<Gmres> gmres = std::make_shared<Gmres>(*dynamic_cast<Gmres *>(sol->linsol.get())); gmres->setTolerance(1e-8); alinsol = gmres; } else alinsol = sol->linsol; - mlinsol = morph->linsol; + if (morph->linsol->type() == LSOLTYPE::GMRES_ILUT) + { + std::shared_ptr<Gmres> gmres = std::make_shared<Gmres>(*dynamic_cast<Gmres *>(morph->linsol.get())); + gmres->setTolerance(1e-12); + mlinsol = gmres; + } + else + mlinsol = morph->linsol; // Update element gradients sol->pbl->initGradElems(); @@ -101,6 +107,14 @@ void Adjoint::run() // Init tbb::global_control control(tbb::global_control::max_allowed_parallelism, nthreads); + // Display current freestream conditions + if (verbose > 0) + std::cout << std::fixed << std::setprecision(2) + << "Computing gradients for Mach " << sol->pbl->M_inf << ", " + << sol->pbl->alpha * 180 / 3.14159 << "deg AoA, " + << sol->pbl->beta * 180 / 3.14159 << "deg AoS" + << std::endl; + // Compute partial gradients of flow residuals and solve flow adjoint tms["0-AdjFlo"].start(); this->linearizeFlow(); // dRu/dU, dRu/dA, dRu/dX @@ -180,7 +194,7 @@ void Adjoint::run() << std::setw(15) << std::right << Eigen::Map<Eigen::VectorXd>(lamCdMsh.data(), lamCdMsh.size()).norm() << std::endl; // Display timers - if (verbose > 1) + if (verbose > 2) std::cout << "Adjoint solver CPU" << std::endl << tms; std::cout << std::endl; @@ -415,7 +429,7 @@ void Adjoint::buildGradientLoadsFlow(Eigen::SparseMatrix<double, Eigen::RowMajor /** * @brief Build gradients of the load coefficients with respect to the flow variables on a given body - * + * * Note: this is essentially the same as buildGradientLoadsFlow, but with an additional projection performed inside the loop for performance */ void Adjoint::buildGradientCoefficientsFlow(Eigen::RowVectorXd &dCl, Eigen::RowVectorXd &dCd, Body const &bnd) @@ -661,7 +675,7 @@ void Adjoint::buildGradientLoadsMesh(Eigen::SparseMatrix<double, Eigen::RowMajor /** * @brief Build gradients of the loads with respect to mesh coordinates on a given body - * + * * Note: this is essentially the same as buildGradientLoadsMesh, but with an additional projection performed inside the loop for performance */ void Adjoint::buildGradientCoefficientsMesh(Eigen::RowVectorXd &dCl, Eigen::RowVectorXd &dCd, Body const &bnd) @@ -716,10 +730,15 @@ void Adjoint::buildGradientCoefficientsMesh(Eigen::RowVectorXd &dCl, Eigen::RowV /** * @brief Write the results */ -void Adjoint::save(MshExport *mshWriter, int n) +void Adjoint::save(MshExport *mshWriter, std::string const &suffix) { // Write files - std::cout << "Saving files... " << std::endl; + std::cout << "Saving files " + << std::setprecision(2) + << "(Mach " << sol->pbl->M_inf << ", " + << sol->pbl->alpha * 180 / 3.14159 << " deg AoA, " + << sol->pbl->beta * 180 / 3.14159 << " deg AoS)" + << std::endl; // setup results Results results; results.scalars_at_nodes["lambdaClPhi"] = &lamClFlo; @@ -727,18 +746,9 @@ void Adjoint::save(MshExport *mshWriter, int n) results.vectors_at_nodes["gradClMsh"] = &dClMsh; results.vectors_at_nodes["gradCdMsh"] = &dCdMsh; // save (whole mesh and bodies) - if (n > 0) - { - mshWriter->save(sol->pbl->msh->name + "_adjoint_" + std::to_string(n), results); - for (auto bnd : sol->pbl->bodies) - bnd->save(bnd->groups[0]->tag->name + "adjoint_" + std::to_string(n), results); - } - else - { - mshWriter->save(sol->pbl->msh->name + "_adjoint", results); - for (auto bnd : sol->pbl->bodies) - bnd->save(bnd->groups[0]->tag->name + "_adjoint", results); - } + mshWriter->save(sol->pbl->msh->name + "_adjoint" + suffix, results); + for (auto bnd : sol->pbl->bodies) + bnd->save(sol->pbl->msh->name + '_' + bnd->groups[0]->tag->name + "_adjoint" + suffix, results); } void Adjoint::write(std::ostream &out) const diff --git a/dart/src/wAdjoint.h b/dart/src/wAdjoint.h index a33efb11100d5775f064e882e91b57d56ea63de0..b2dc8ffd116cf3415e30bde0fef648e4fac4fb54 100644 --- a/dart/src/wAdjoint.h +++ b/dart/src/wAdjoint.h @@ -66,7 +66,7 @@ public: virtual ~Adjoint() { std::cout << "~Adjoint()\n"; } void run(); - void save(tbox::MshExport *mshWriter, int n = 0); + void save(tbox::MshExport *mshWriter, std::string const &suffix = ""); // Partial gradients of the mesh residuals void linearizeMesh(); diff --git a/dart/src/wF1Ct.h b/dart/src/wF1Ct.h index 010791b123483047afe138fbb9621992ed61baa5..3041a2fbf7edc24c2185044fc70d9a8ddf4c1e09 100644 --- a/dart/src/wF1Ct.h +++ b/dart/src/wF1Ct.h @@ -50,7 +50,7 @@ class DART_API F1CtDrag : public F1Ct public: F1CtDrag(int _nDim, double _sRef, double alpha, double _beta = 0); - void update(double alpha); + virtual void update(double alpha) override; virtual Eigen::Vector3d eval() const override; virtual Eigen::Vector3d evalGrad() const override; @@ -69,7 +69,7 @@ class DART_API F1CtSide : public F1Ct public: F1CtSide(int _nDim, double _sRef, double alpha, double _beta = 0); - void update(double alpha); + virtual void update(double alpha) override; virtual Eigen::Vector3d eval() const override; virtual Eigen::Vector3d evalGrad() const override; @@ -88,7 +88,7 @@ class DART_API F1CtLift : public F1Ct public: F1CtLift(int _nDim, double _sRef, double alpha, double _beta = 0); - void update(double alpha); + virtual void update(double alpha) override; virtual Eigen::Vector3d eval() const override; virtual Eigen::Vector3d evalGrad() const override; diff --git a/dart/src/wNewton.cpp b/dart/src/wNewton.cpp index 23ab5bf6689496c0918338882c5fb92d497ea847..1d53b14ba43af71ade78170de7c4fd284822956f 100644 --- a/dart/src/wNewton.cpp +++ b/dart/src/wNewton.cpp @@ -88,14 +88,12 @@ STATUS Newton::run() tbb::global_control control(tbb::global_control::max_allowed_parallelism, nthreads); // Display current freestream conditions - if (printOn) - { - std::cout << std::setprecision(2) - << "- Mach " << pbl->M_inf << ", " - << pbl->alpha * 180 / 3.14159 << " deg AoA, " - << pbl->beta * 180 / 3.14159 << " deg AoS" + if (verbose > 0) + std::cout << std::fixed << std::setprecision(2) + << "Solving flow for Mach " << pbl->M_inf << ", " + << pbl->alpha * 180 / 3.14159 << "deg AoA, " + << pbl->beta * 180 / 3.14159 << "deg AoS" << std::endl; - } // Initialize solver loop nIt = 0; // iteration counter @@ -121,7 +119,7 @@ STATUS Newton::run() ls.set(maxLsIt, lsTol, verbose); // Display residual - if (printOn) + if (verbose > 0) { std::cout << std::setw(8) << "N_Iter" << std::setw(8) << "L_Iter" @@ -204,7 +202,7 @@ STATUS Newton::run() Solver::computeLoad(); // Display residual (at each iteration) - if (verbose > 0 && printOn) + if (verbose > 1) { std::cout << std::fixed << std::setprecision(5); std::cout << std::setw(8) << nIt @@ -226,7 +224,7 @@ STATUS Newton::run() } while (nIt < maxIt); // Display residual (only last iteration) - if (verbose == 0 && printOn) + if (verbose == 1) { std::cout << std::fixed << std::setprecision(5); std::cout << std::setw(8) << nIt @@ -244,29 +242,35 @@ STATUS Newton::run() // Check the solution if (relRes < relTol || absRes < absTol) { - if (printOn) {std::cout << ANSI_COLOR_GREEN << "Newton solver converged!" << ANSI_COLOR_RESET << std::endl;} - if (verbose > 1) + if (verbose > 0) + std::cout << ANSI_COLOR_GREEN << "Newton solver converged!" << ANSI_COLOR_RESET << std::endl; + if (verbose > 2) std::cout << "Newton solver CPU" << std::endl << tms; - std::cout << std::endl; + if (verbose > 0) + std::cout << std::endl; return STATUS::CONVERGED; } else if (std::isnan(relRes)) { - std::cout << ANSI_COLOR_RED << "Newton solver diverged!" << ANSI_COLOR_RESET << std::endl; - if (verbose > 1) + if (verbose > 0) + std::cout << ANSI_COLOR_RED << "Newton solver diverged!" << ANSI_COLOR_RESET << std::endl; + if (verbose > 2) std::cout << "Newton solver CPU" << std::endl << tms; - std::cout << std::endl; + if (verbose > 0) + std::cout << std::endl; return STATUS::FAILED; } else { - if (printOn) {std::cout << ANSI_COLOR_YELLOW << "Newton solver not fully converged!" << ANSI_COLOR_RESET << std::endl;} - if (verbose > 1) + if (verbose > 0) + std::cout << ANSI_COLOR_YELLOW << "Newton solver not fully converged!" << ANSI_COLOR_RESET << std::endl; + if (verbose > 2) std::cout << "Newton solver CPU" << std::endl << tms; - std::cout << std::endl; + if (verbose > 0) + std::cout << std::endl; return STATUS::MAXIT; } } @@ -400,7 +404,7 @@ void Newton::buildJac(Eigen::SparseMatrix<double, Eigen::RowMajor> &J) J.prune(0.); J.makeCompressed(); - if (verbose > 2) + if (verbose > 3) std::cout << "J (" << J.rows() << "," << J.cols() << ") nnz=" << J.nonZeros() << "\n"; } @@ -510,7 +514,7 @@ void Newton::buildRes(Eigen::Map<Eigen::VectorXd> &R) for (auto nod : dBC->nodes) R(nod->row) = 0.; - if (verbose > 2) + if (verbose > 3) std::cout << "R (" << R.size() << ")\n"; } diff --git a/dart/src/wPicard.cpp b/dart/src/wPicard.cpp index 5d567514923b835699a796af894a5b5b32ace767..15177ebcb14e46fec7581fafc6daf94ae00b2bd7 100644 --- a/dart/src/wPicard.cpp +++ b/dart/src/wPicard.cpp @@ -81,11 +81,12 @@ STATUS Picard::run() tbb::global_control control(tbb::global_control::max_allowed_parallelism, nthreads); // Display current freestream conditions - std::cout << std::setprecision(2) - << "- Mach " << pbl->M_inf << ", " - << pbl->alpha * 180 / 3.14159 << " deg AoA, " - << pbl->beta * 180 / 3.14159 << " deg AoS" - << std::endl; + if (verbose > 0) + std::cout << std::fixed << std::setprecision(2) + << "Solving flow for Mach " << pbl->M_inf << ", " + << pbl->alpha * 180 / 3.14159 << "deg AoA, " + << pbl->beta * 180 / 3.14159 << "deg AoS" + << std::endl; // Initialize solver loop nIt = 0; @@ -95,11 +96,12 @@ STATUS Picard::run() Eigen::Map<Eigen::VectorXd> phi_(phi.data(), phi.size()), rPhi_(rPhi.data(), rPhi.size()); Eigen::VectorXd phiOld(phi.size()); - std::cout << std::setw(8) << "N_Iter" - << std::setw(12) << "Cl" - << std::setw(12) << "Cd" - << std::setw(15) << "Rel_Res[phi]" - << std::setw(15) << "Abs_Res[phi]" << std::endl; + if (verbose > 0) + std::cout << std::setw(8) << "N_Iter" + << std::setw(12) << "Cl" + << std::setw(12) << "Cd" + << std::setw(15) << "Rel_Res[phi]" + << std::setw(15) << "Abs_Res[phi]" << std::endl; do { @@ -122,7 +124,7 @@ STATUS Picard::run() Solver::computeLoad(); // Display residual (at each iteration) - if (verbose > 0 || nIt == 0) + if (verbose > 1 || (verbose == 1 && nIt == 0)) { std::cout << std::fixed << std::setprecision(5); std::cout << std::setw(8) << nIt @@ -152,7 +154,7 @@ STATUS Picard::run() } while (nIt < maxIt); // Display residual (only last iteration) - if (verbose == 0) + if (verbose == 1) { std::cout << std::fixed << std::setprecision(5); std::cout << std::setw(8) << nIt @@ -168,29 +170,35 @@ STATUS Picard::run() // Check the solution if (relRes < relTol || absRes < absTol) { - std::cout << ANSI_COLOR_GREEN << "Picard solver converged!" << ANSI_COLOR_RESET << std::endl; - if (verbose > 1) + if (verbose > 0) + std::cout << ANSI_COLOR_GREEN << "Picard solver converged!" << ANSI_COLOR_RESET << std::endl; + if (verbose > 2) std::cout << "Picard solver CPU" << std::endl << tms; - std::cout << std::endl; + if (verbose > 0) + std::cout << std::endl; return STATUS::CONVERGED; } else if (std::isnan(relRes)) { - std::cout << ANSI_COLOR_RED << "Picard sovler diverged!" << ANSI_COLOR_RESET << std::endl; - if (verbose > 1) + if (verbose > 0) + std::cout << ANSI_COLOR_RED << "Picard sovler diverged!" << ANSI_COLOR_RESET << std::endl; + if (verbose > 2) std::cout << "Picard solver CPU" << std::endl << tms; - std::cout << std::endl; + if (verbose > 0) + std::cout << std::endl; return STATUS::FAILED; } else { - std::cout << ANSI_COLOR_YELLOW << "Picard solver not fully converged!" << ANSI_COLOR_RESET << std::endl; - if (verbose > 1) + if (verbose > 0) + std::cout << ANSI_COLOR_YELLOW << "Picard solver not fully converged!" << ANSI_COLOR_RESET << std::endl; + if (verbose > 2) std::cout << "Picard solver CPU" << std::endl << tms; - std::cout << std::endl; + if (verbose > 0) + std::cout << std::endl; return STATUS::MAXIT; } } @@ -345,7 +353,7 @@ void Picard::build(Eigen::SparseMatrix<double, Eigen::RowMajor> &A, std::vector< A.prune(0.); A.makeCompressed(); - if (verbose > 2) + if (verbose > 3) { std::cout << "A (" << A.rows() << "," << A.cols() << ") nnz=" << A.nonZeros() << "\n"; std::cout << "b (" << b.size() << ")\n"; diff --git a/dart/src/wSolver.cpp b/dart/src/wSolver.cpp index 68d2b6119cc09b811844dcab4e8a681db8149d2f..32e8c70175ff8009f1450cf65714685ab0d8902c 100644 --- a/dart/src/wSolver.cpp +++ b/dart/src/wSolver.cpp @@ -161,10 +161,15 @@ STATUS Solver::run() /** * @brief Write the results */ -void Solver::save(MshExport *mshWriter, int n) +void Solver::save(MshExport *mshWriter, std::string const &suffix) { // Write files - std::cout << "Saving files... " << std::endl; + std::cout << "Saving files " + << std::fixed << std::setprecision(2) + << "(Mach " << pbl->M_inf << ", " + << pbl->alpha * 180 / 3.14159 << "deg AoA, " + << pbl->beta * 180 / 3.14159 << "deg AoS)" + << std::endl; // setup results Results results; results.scalars_at_nodes["phi"] = φ @@ -175,19 +180,9 @@ void Solver::save(MshExport *mshWriter, int n) results.scalars_at_nodes["Mach"] = &M; results.scalars_at_nodes["Cp"] = &Cp; // save (whole mesh and bodies) - if (n > 0) - { - mshWriter->save(pbl->msh->name + "_" + std::to_string(n), results); - for (auto bnd : pbl->bodies) - bnd->save(bnd->groups[0]->tag->name + "_" + std::to_string(n), results); - } - else - { - mshWriter->save(pbl->msh->name, results); - for (auto bnd : pbl->bodies) - bnd->save(bnd->groups[0]->tag->name, results); - } - std::cout << std::endl; + mshWriter->save(pbl->msh->name + suffix, results); + for (auto bnd : pbl->bodies) + bnd->save(pbl->msh->name + '_' + bnd->groups[0]->tag->name + suffix, results); } /** @@ -279,6 +274,6 @@ void Solver::computeFlow() }); // Check maximum Mach number - if (*std::max_element(M.begin(), M.end()) >= 1.25) + if (*std::max_element(M.begin(), M.end()) >= 1.25 && verbose > 0) std::cout << ANSI_COLOR_YELLOW << "Max. Mach greater than 1.25!" << ANSI_COLOR_RESET << std::endl; } diff --git a/dart/src/wSolver.h b/dart/src/wSolver.h index aab40ad17beb65aaee05537c73ab5bad693dc710..fdf41515f3278f2056eb7bf977f21dc0ecb18e10 100644 --- a/dart/src/wSolver.h +++ b/dart/src/wSolver.h @@ -78,7 +78,7 @@ public: void reset(); virtual STATUS run(); - void save(tbox::MshExport *mshWriter, int n = 0); + void save(tbox::MshExport *mshWriter, std::string const &suffix = ""); protected: void computeFlow(); diff --git a/dart/tests/adjoint.py b/dart/tests/adjoint.py index 5788fe27441057128a222cd330e4d421add55673..df03af2e7fec29ae787f34edacad6ee37b71cc28 100644 --- a/dart/tests/adjoint.py +++ b/dart/tests/adjoint.py @@ -25,13 +25,13 @@ # This test is provided to ensure that the solver works properly. # Mesh refinement may have to be performed to obtain physical results. -import numpy as np import dart.utils as floU import dart.default as floD import tbox.utils as tboxU import fwk from fwk.testing import * from fwk.coloring import ccolors +import numpy as np def main(): # timer @@ -50,7 +50,7 @@ def main(): pars = {'xLgt' : 5, 'yLgt' : 5, 'msF' : 1., 'msTe' : 0.0075, 'msLe' : 0.0075} msh, gmshWriter = floD.mesh(dim, 'models/n0012.geo', pars, ['field', 'airfoil', 'downstream']) # create mesh deformation handler - morpher = floD.morpher(msh, dim, ['airfoil']) + morpher = floD.morpher(msh, dim, 'airfoil') tms['msh'].stop() # set the problem and add fluid, initial/boundary conditions diff --git a/dart/tests/bli_Sep.py b/dart/tests/bli_Sep.py index 363809b55869ba500b58863197d547854a3def0f..cd35876dc54c6ecb9af7e497c4167792e9728dc2 100644 --- a/dart/tests/bli_Sep.py +++ b/dart/tests/bli_Sep.py @@ -46,6 +46,7 @@ import tbox.utils as tboxU import fwk from fwk.testing import * from fwk.coloring import ccolors +import math import math diff --git a/dart/tests/cylinder.py b/dart/tests/cylinder.py index 07d43117a1d25ebf38f3f0e24cdbb843fc383762..6a10c2b432dc9da509ccae0a388b300429fd1c30 100644 --- a/dart/tests/cylinder.py +++ b/dart/tests/cylinder.py @@ -25,11 +25,11 @@ # This test is provided to ensure that the solver works properly. # Mesh refinement may have to be performed to obtain physical results. -import math import dart.default as floD import fwk from fwk.testing import * from fwk.coloring import ccolors +import math def main(): # timer diff --git a/dart/tests/lift.py b/dart/tests/lift.py index daa9b6eb2ed31cdfbddb45f0e9b5448b7eb46210..76dea0dbbefff88d58075e3ef701866e3363e2d0 100644 --- a/dart/tests/lift.py +++ b/dart/tests/lift.py @@ -25,14 +25,13 @@ # This test is provided to ensure that the solver works properly. # Mesh refinement may have to be performed to obtain physical results. -import math import dart.utils as floU import dart.default as floD import tbox.utils as tboxU import fwk from fwk.testing import * from fwk.coloring import ccolors -from matplotlib import pyplot as plt +import math def main(): # timer diff --git a/dart/tests/morpher.py b/dart/tests/morpher.py index c82f27719227c722d27d4db4d80d0d17d546d4d3..5bca5c11e4ebff7b62457edeb725c770d69267ba 100644 --- a/dart/tests/morpher.py +++ b/dart/tests/morpher.py @@ -25,13 +25,13 @@ # This test is provided to ensure that the solver works properly. # Mesh refinement may have to be performed to obtain physical results. -import numpy as np import dart.utils as floU import dart.default as floD import tbox.utils as tboxU import fwk from fwk.testing import * from fwk.coloring import ccolors +import numpy as np def main(): # timer @@ -55,7 +55,7 @@ def main(): pars = {'xLgt' : 5, 'yLgt' : 5, 'msF' : 1., 'msTe' : 0.01, 'msLe' : 0.01} msh, gmshWriter = floD.mesh(dim, 'models/n0012.geo', pars, ['field', 'airfoil', 'downstream']) # create mesh deformation handler - morpher = floD.morpher(msh, dim, ['airfoil']) + morpher = floD.morpher(msh, dim, 'airfoil') # mesh the reference geometry pars = {'xLgt' : 5, 'yLgt' : 5, 'msF' : 1., 'msTe' : 0.01, 'msLe' : 0.01, 'angle' : alfa, 'xRot' : xc} msh_ref, gmshWriter_ref = floD.mesh(dim, 'models/n0012.geo', pars, ['field', 'airfoil', 'downstream']) diff --git a/dart/tests/nonlift.py b/dart/tests/nonlift.py index 223aa336b3ccd995c20e0bfa83e1c344e00ddded..4d6e911a825ddffc0321febeaee4fc4c69b62bbb 100644 --- a/dart/tests/nonlift.py +++ b/dart/tests/nonlift.py @@ -26,7 +26,6 @@ # Mesh refinement may have to be performed to obtain physical results. # The residual might not fully converge if this test is used with gmsh4 -import math import dart.utils as floU import dart.default as floD from matplotlib import pyplot as plt @@ -34,6 +33,7 @@ import tbox.utils as tboxU import fwk from fwk.testing import * from fwk.coloring import ccolors +import math def main(): # timer diff --git a/dart/tests/rae_25.py b/dart/tests/rae_25.py index cc656c11f7211fad86603d24ea9b253615b57698..95ab324502dc9d1614f0822c89e69e3e4e92001a 100644 --- a/dart/tests/rae_25.py +++ b/dart/tests/rae_25.py @@ -2,13 +2,13 @@ # -*- coding: utf-8 -*- # Copyright 2020 University of Liège -# +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,11 +25,11 @@ # This test is provided to ensure that the solver works properly. # Mesh refinement may have to be performed to obtain physical results. -import numpy as np import dart.default as floD import fwk from fwk.testing import * -from fwk.coloring import ccolors +from fwk.coloring import ccolors +import numpy as np def main(): # timer @@ -49,14 +49,14 @@ def main(): c_ref = 1.0 # reference length S_ref = spn # reference area fms = 1.0 # farfield mesh size - nms = 0.01 # nearfield mesh size + nms = 0.01 # nearfield mesh size # mesh the geometry print(ccolors.ANSI_BLUE + 'PyMeshing...' + ccolors.ANSI_RESET) tms["msh"].start() pars = {'spn' : spn, 'lgt' : lgt, 'wdt' : wdt, 'hgt' : hgt, 'msN' : nms, 'msF' : fms} msh, gmshWriter = floD.mesh(dim, 'models/rae_25.geo', pars, ['field', 'wing', 'symmetry', 'downstream']) - morpher = floD.morpher(msh, dim, ['wing']) + morpher = floD.morpher(msh, dim, 'wing') tms["msh"].stop() # set the problem and add fluid, initial/boundary conditions @@ -122,11 +122,11 @@ def main(): tests.add(CTest('CS', solver.Cs, 0.0000, 1e-3, forceabs=True)) tests.add(CTest('CM', solver.Cm, -0.113, 5e-2)) # 2D -0.117 tests.add(CTest('dCl/dAoA', adjoint.dClAoa, 12.7, 5e-2)) # 2D 12.7, FD 12.73 (1e-6) - tests.add(CTest('dCd/dAoA', adjoint.dCdAoa, 0.059, 5e-3, forceabs=True)) # 2D 0.051, FD 0.0557 (1e-6) + tests.add(CTest('dCd/dAoA', adjoint.dCdAoa, 0.055, 1e-2, forceabs=True)) # 2D 0.051, FD 0.0557 (1e-6) tests.add(CTest('dCl_dMsh', dClX, 22.0, 5e-2)) - tests.add(CTest('dCd_dMsh', dCdX, 1.0, 5e-2)) + tests.add(CTest('dCd_dMsh', dCdX, 1.0, 1e-1)) tests.add(CTest('dCl/dAoA (msh)', dClAoA, 12.9, 5e-2)) # 2D 12.9 - tests.add(CTest('dCd/dAoA (msh)', dCdAoA, 0.058, 5e-3, forceabs=True)) # 2D 0.051 + tests.add(CTest('dCd/dAoA (msh)', dCdAoA, 0.055, 1e-2, forceabs=True)) # 2D 0.051 tests.run() # eof diff --git a/dart/tests/rae_3.py b/dart/tests/rae_3.py index f1f8add3567e2643f5902b94731395bcd24b6f0c..6559ca1b1fa2d0d573213c17211a9c416d6e27b8 100644 --- a/dart/tests/rae_3.py +++ b/dart/tests/rae_3.py @@ -2,13 +2,13 @@ # -*- coding: utf-8 -*- # Copyright 2020 University of Liège -# +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,13 +25,13 @@ # This test is provided to ensure that the solver works properly. # Mesh refinement may have to be performed to obtain physical results. -import numpy as np import dart.default as floD import dart.utils as floU from matplotlib import pyplot as plt import fwk from fwk.testing import * -from fwk.coloring import ccolors +from fwk.coloring import ccolors +import numpy as np def main(): # timer @@ -51,7 +51,7 @@ def main(): c_ref = 1.0 # reference length S_ref = spn # reference area fms = 1.0 # farfield mesh size - nms = 0.02 # nearfield mesh size + nms = 0.02 # nearfield mesh size # parameters for mesh deformation alfa = 1*np.pi/180 @@ -62,8 +62,8 @@ def main(): print(ccolors.ANSI_BLUE + 'PyMeshing...' + ccolors.ANSI_RESET) tms["msh"].start() pars = {'spn' : spn, 'lgt' : lgt, 'wdt' : wdt, 'hgt' : hgt, 'msN' : nms, 'msF' : fms} - msh, sortElm, gmshWriter = floD.mesh(dim, 'models/rae_3.geo', pars, ['field', 'wing', 'symmetry', 'downstream'], wktp = 'wakeTip') - morpher = floD.morpher(msh, dim, ['wing']) + msh, gmshWriter = floD.mesh(dim, 'models/rae_3.geo', pars, ['field', 'wing', 'symmetry', 'downstream'], wktp = 'wakeTip') + morpher = floD.morpher(msh, dim, 'wing') tms["msh"].stop() # set the problem and add fluid, initial/boundary conditions diff --git a/dart/utils.py b/dart/utils.py index 9691f77b6a302af2dae89a2e4b80398af2bd8f63..03b98292724d1f3c9ec3fe13b5ddec30908b6045 100644 --- a/dart/utils.py +++ b/dart/utils.py @@ -111,7 +111,7 @@ def extract(vElems, vData): i += 1 return data -def writeSlices(mshName, ys, wId, n = 0): +def writeSlices(mshName, ys, wId, sfx=''): """Write slice data for each ys coordinate along the span (only works with VTK) Parameters @@ -122,8 +122,8 @@ def writeSlices(mshName, ys, wId, n = 0): y-coordinates of slices wId : int index of physical tag to slice (see gmsh) - n : int (optional, > 0) - index of mesh/solution file (will be appended to mesh file name) + sfx : string (optional) + suffix that will be appended to mesh/solution file name """ import numpy as np try: @@ -134,10 +134,7 @@ def writeSlices(mshName, ys, wId, n = 0): import tboxVtk.reader as vtkR import tboxVtk.cutter as vtkC reader = vtkR.Reader() - if n == 0: - reader.open(mshName) - else: - reader.open(mshName+'_'+str(n)) + reader.open(mshName+sfx) cutter = vtkC.Cutter(reader.reader.GetOutputPort()) for i in range(0, len(ys)): pts, elems, vals = cutter.extract(cutter.cut(wId, [0., ys[i], 0.], [0., 1., 0.]), 2, ['Cp']) @@ -147,7 +144,4 @@ def writeSlices(mshName, ys, wId, n = 0): data = np.hstack((data, vals['Cp'])) tU.sort(elems, data) data = np.vstack((data, data[0,:])) - if n == 0: - tU.write(data, 'slice_'+str(i)+'.dat', '%1.5e', ', ', 'x, y, z, x/c, Cp', '') - else: - tU.write(data, 'slice_'+str(n)+'_'+str(i)+'.dat', '%1.5e', ', ', 'x, y, z, x/c, Cp', '') + tU.write(data, mshName+sfx+'_slice_'+str(i)+'.dat', '%1.5e', ', ', 'x, y, z, x/c, Cp', '') diff --git a/dart/validation/agard.py b/dart/validation/agard.py index 86d5b9b767a88ab9107adba31809b85365a4d772..dc18dcb7da37bdbb618c3cf05c12763a1338a6f5 100644 --- a/dart/validation/agard.py +++ b/dart/validation/agard.py @@ -19,13 +19,13 @@ ## Compute flow around the Agard 445 wing at 1 degrees AOA and Mach 0.80 # Adrien Crovato -import numpy as np import dart.utils as floU import dart.default as floD import tbox.utils as tbxU import fwk from fwk.testing import * from fwk.coloring import ccolors +import numpy as np try: import tboxVtk @@ -84,7 +84,7 @@ def main(): tms['post'].start() if canPost: floU.writeSlices(msh.name,[0.01, 0.37, 0.75],5) - data = tbxU.read('slice_1.dat') + data = tbxU.read('agard445_slice_1.dat') tbxU.plot(data[:,3], data[:,4], 'x', 'Cp', 'Pressure distribution at MAC', True) tms['post'].stop() diff --git a/dart/validation/onera.py b/dart/validation/onera.py index 6957094a6f26428f4bc3814c8ca757320c9cc1eb..85f4e671a9b9e8164e463fdefa2fe6e7a69f5d5a 100644 --- a/dart/validation/onera.py +++ b/dart/validation/onera.py @@ -19,13 +19,13 @@ ## Compute flow around the Onera M6 wing at 3 degrees AOA and Mach 0.84 # Adrien Crovato -import numpy as np import dart.utils as floU import dart.default as floD import tbox.utils as tbxU import fwk from fwk.testing import * from fwk.coloring import ccolors +import numpy as np try: import tboxVtk @@ -82,7 +82,7 @@ def main(): tms['post'].start() if canPost: floU.writeSlices(msh.name,[0.01, 0.239, 0.526, 0.777, 0.957, 1.076, 1.136, 1.184],5) - data = tbxU.read('slice_2.dat') + data = tbxU.read('oneraM6_slice_2.dat') tbxU.plot(data[:,3], data[:,4], 'x', 'Cp', 'Pressure distribution at MAC', True) tms['post'].stop() diff --git a/ext/amfe b/ext/amfe index 8a20b0f157021fc286fa14bb9099fba81092da5c..e6eafbe78604a7a27c7ee21f098f4c1e3704b0dc 160000 --- a/ext/amfe +++ b/ext/amfe @@ -1 +1 @@ -Subproject commit 8a20b0f157021fc286fa14bb9099fba81092da5c +Subproject commit e6eafbe78604a7a27c7ee21f098f4c1e3704b0dc