diff --git a/dart/api/core.py b/dart/api/core.py new file mode 100644 index 0000000000000000000000000000000000000000..64cc2cf395825799606854cc9790c428a1bc574b --- /dev/null +++ b/dart/api/core.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +# -*- 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. +# See the License for the specific language governing permissions and +# limitations under the License. + + +## Initialize DART +# Adrien Crovato + +def initDart(params, scenario='aerodynamic', task='analysis'): + """Initlialize DART for various API + + Parameters + ---------- + params : dict + Dictionary of parameters to configure DART + scenario : string, optional + Type of scenario (available: aerodynamic, aerostructural) (default: aerodynamic) + task : string, optional + Type of task (available: analysis, optimization) (default: analysis) + + Returns + ------- + _dim : int + Dimension of the problem + _qinf : float + Freestream dynamic pressure + _msh : tbox.MshData object + Mesh data structure + _wrtr : tbox.MshExport object + Mesh writer + _mrf : tbox.MshDeform object + Mesh morpher + _pbl : dart.Probem object + Problem definition + _bnd : Dart.Body object + Body of interest + _sol : dart.Newton object + Direct (Newton) solver + _adj : dart.Adjoint object + Adjoint solver + """ + # Imports + import math + import tbox + import tbox.gmsh as gmsh + import dart + + # Basic checks and config + # dimension + if params['Dim'] != 2 and params['Dim'] != 3: + raise RuntimeError('Problem dimension should be 2 or 3, but ' + params['Dim'] + ' was given!\n') + else: + _dim = params['Dim'] + # aoa and aos + if 'AoA' in params: + alpha = params['AoA'] * math.pi / 180 + else: + alpha = 0 + if _dim == 2: + if 'AoS' in params and params['AoS'] != 0: + raise RuntimeError('Angle of sideslip (AoS) should be zero for 2D problems!\n') + else: + beta = 0 + if 'Symmetry' in params: + raise RuntimeError('Symmetry boundary condition cannot be used for 2D problems!\n') + else: + if 'AoS' in params: + if params['AoS'] != 0 and 'Symmetry' in params: + raise RuntimeError('Symmetry boundary condition cannot be used with nonzero angle of sideslip (AoS)!\n') + beta = params['AoS'] * math.pi / 180 + else: + beta = 0 + # save format + if params['Format'] == 'vtk': + try: + import tboxVtk + Writer = tboxVtk.VtkExport + print('VTK libraries found! Results will be saved in VTK format.\n') + except: + Writer = tbox.GmshExport + print('VTK libraries not found! Results will be saved in gmsh format.\n') + else: + Writer = tbox.GmshExport + print('Results will be saved in gmsh format.\n') + # number of threads + if 'Threads' in params: + nthrd = params['Threads'] + else: + nthrd = 1 + # verbosity + if 'Verb' in params: + verb = params['Verb'] + else: + verb = 0 + # scenario and task type + if scenario != 'aerodynamic' and scenario != 'aerostructural': + raise RuntimeError('Scenario should be aerodynamic or aerostructural, but "' + scenario + '" was given!\n') + if task != 'analysis' and task != 'optimization': + raise RuntimeError('Task should be analysis or optimization, but "' + task + '" was given!\n') + # dynamic pressure + if scenario == 'aerostructural': + _qinf = params['Q_inf'] + else: + _qinf = 0 + + # Mesh creation + _msh = gmsh.MeshLoader(params['File']).execute(**params['Pars']) + # add the wake + if _dim == 2: + mshCrck = tbox.MshCrack(_msh, _dim) + mshCrck.setCrack(params['Wake']) + mshCrck.addBoundaries([params['Fluid'], params['Farfield'][-1], params['Wing']]) + mshCrck.run() + else: + for i in range(len(params['Wakes'])): + mshCrck = tbox.MshCrack(_msh, _dim) + mshCrck.setCrack(params['Wakes'][i]) + mshCrck.addBoundaries([params['Fluid'], params['Farfield'][-1], params['Wings'][i]]) + if 'Fuselage' in params: + mshCrck.addBoundaries([params['Fuselage']]) + if 'Symmetry' in params: + mshCrck.addBoundaries([params['Symmetry']]) + if 'WakeTips' in params: + mshCrck.setExcluded(params['WakeTips'][i]) # 2.5D computations + mshCrck.run() + # save the updated mesh in native (gmsh) format and then set the writer to the requested format + tbox.GmshExport(_msh).save(_msh.name) + del mshCrck + _wrtr = Writer(_msh) + print('\n') + + # Mesh morpher creation + if scenario == 'aerostructural' or task == 'optimization': + _mrf = tbox.MshDeform(_msh, _dim) + _mrf.nthreads = nthrd + _mrf.setField(params['Fluid']) + _mrf.addFixed(params['Farfield']) + if _dim == 2: + _mrf.addMoving([params['Wing']]) + _mrf.addInternal([params['Wake'], params['Wake']+'_']) + else: + if 'Fuselage' in params: + _mrf.addFixed(params['Fuselage']) + _mrf.setSymmetry(params['Symmetry'], 1) + for i in range(len(params['Wings'])): + if i == 0: + _mrf.addMoving([params['Wings'][i]]) # TODO body of interest (FSI/OPT) is always first given body + else: + _mrf.addFixed([params['Wings'][i]]) + _mrf.addInternal([params['Wakes'][i], params['Wakes'][i]+'_']) + _mrf.initialize() + else: + _mrf = None + + # Problem creation + _pbl = dart.Problem(_msh, _dim, alpha, beta, params['M_inf'], params['S_ref'], params['c_ref'], params['x_ref'], params['y_ref'], params['z_ref']) + # add the field + _pbl.set(dart.Fluid(_msh, params['Fluid'], params['M_inf'], _dim, alpha, beta)) + # add the initial condition + _pbl.set(dart.Initial(_msh, params['Fluid'], _dim, alpha, beta)) + # add farfield boundary conditions (Neumann only, a DOF will be pinned automatically) + for i in range (len(params['Farfield'])): + _pbl.add(dart.Freestream(_msh, params['Farfield'][i], _dim, alpha, beta)) + # add solid boundaries + if _dim == 2: + bnd = dart.Body(_msh, [params['Wing'], params['Fluid']]) + _bnd = bnd + _pbl.add(bnd) + else: + _bnd = None + for bd in params['Wings']: + bnd = dart.Body(_msh, [bd, params['Fluid']]) + if _bnd is None: + _bnd = bnd # TODO body of interest (FSI/OPT) is always first given body + _pbl.add(bnd) + if 'Fuselage' in params: + bnd = dart.Body(_msh, [params['Fuselage'], params['Fluid']]) + _pbl.add(bnd) + # add Wake/Kutta boundary conditions + if _dim == 2: + _pbl.add(dart.Wake(_msh, [params['Wake'], params['Wake']+'_', params['Fluid']])) + _pbl.add(dart.Kutta(_msh, [params['Te'], params['Wake']+'_', params['Wing'], params['Fluid']])) + else: + for i in range(len(params['Wakes'])): + _pbl.add(dart.Wake(_msh, [params['Wakes'][i], params['Wakes'][i]+'_', params['Fluid'], params['TeTips'][i]])) + + # Direct (forward) solver creation + # NB: more solvers/options are available but we restrict the user's choice to the most efficient ones + # initialize the linear (inner) solver + if params['LSolver'] == 'PARDISO': + linsol = tbox.Pardiso() + elif params['LSolver'] == 'MUMPS': + linsol = tbox.Mumps() + elif params['LSolver'] == 'GMRES': + linsol = tbox.Gmres() + linsol.setFillFactor(params['G_fill']) if 'G_fill' in params else linsol.setFillFactor(2) + linsol.setRestart(params['G_restart']) if 'G_restart' in params else linsol.setRestart(50) + linsol.setTolerance(params['G_tol']) if 'G_tol' in params else linsol.setTolerance(1e-5) + else: + raise RuntimeError('Available linear solvers: PARDISO, MUMPS or GMRES, but ' + params['LSolver'] + ' was given!\n') + # initialize the nonlinear (outer) solver + _sol = dart.Newton(_pbl, linsol, rTol=params['Rel_tol'], aTol=params['Abs_tol'], mIt=params['Max_it'], nthrds=nthrd, vrb=verb) + + # Adjoint (reverse) solver creation + if task == 'optimization': + if params['LSolver'] == 'PARDISO': + alinsol = tbox.Pardiso() + elif params['LSolver'] == 'MUMPS': + alinsol = tbox.Mumps() + else: + alinsol = tbox.Gmres() + alinsol.setFillFactor(params['G_fill']) if 'G_fill' in params else alinsol.setFillFactor(2) + alinsol.setRestart(params['G_restart']) if 'G_restart' in params else alinsol.setRestart(50) + alinsol.setTolerance(1e-12) + _adj = dart.Adjoint(_sol, _mrf, alinsol) + else: + _adj = None + + # Return + return _dim, _qinf, _msh, _wrtr, _mrf, _pbl, _bnd, _sol, _adj diff --git a/dart/api/internal/config.py b/dart/api/internal/config.py deleted file mode 100644 index 7c40318b66d7c2b55419632469bb8f8edf8d713b..0000000000000000000000000000000000000000 --- a/dart/api/internal/config.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 -# -*- 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. -# See the License for the specific language governing permissions and -# limitations under the License. - - -## Base class for configuration -# Adrien Crovato - -import math -import dart -import tbox -import tbox.gmsh as gmsh -import fwk -from tbox.solvers import LinearSolver -from fwk.wutils import parseargs -from fwk.coloring import ccolors - -class Config: - """Basic configurator - Configure DART to perform standard computations on standard configurations - - Attributes - --------- - tms : fwk.Timers object - dictionary of timers - msh : tbox.MshData object - mesh data structure - mshWriter : tbox.MshExport object - mesh data writer - pbl : dart.Probelm object - problem formulation - bnd : array of dart.Body objects - bodies of interest immersed in fluid - solver : dart.Solver object - full potential solver - """ - def __init__(self, p): - """Setup and configure - - Parameters - ---------- - p : dict - dictionary of parameters to configure DART - """ - # timer - self.tms = fwk.Timers() - self.tms["total"].start() - - # basic checks - if p['Dim'] != 2 and p['Dim'] != 3: - raise RuntimeError('Problem dimension should be 2 or 3, but ' + p['Dim'] + ' was given!\n') - if p['Dim'] == 2: - if 'AoS' in p and p['AoS'] != 0: - raise RuntimeError('Angle of sideslip (AoS) should be zero for 2D problems!\n') - beta = 0 - if 'Symmetry' in p: - raise RuntimeError('Symmetry boundary condition cannot be used for 2D problems!\n') - else: - if 'AoS' in p: - if p['AoS'] != 0 and 'Symmetry' in p: - raise RuntimeError('Symmetry boundary condition cannot be used with nonzero angle of sideslip (AoS)!\n') - beta = p['AoS']*math.pi/180 - else: - beta = 0 - # basic config - if p['Format'] == 'vtk': - try: - import tboxVtk - Writer = tboxVtk.VtkExport - print("Found VTK libraries! Results will be saved in VTK format.\n") - except: - Writer = tbox.GmshExport - print("VTK libraries not found! Results will be saved in gmsh format.\n") - else: - Writer = tbox.GmshExport - print("Results will be saved in gmsh format.\n") - - # mesh the airfoil - print(ccolors.ANSI_BLUE + 'Meshing the airfoil...' + ccolors.ANSI_RESET) - self.msh = gmsh.MeshLoader(p['File']).execute(**p['Pars']) - if p['Dim'] == 2: - mshCrck = tbox.MshCrack(self.msh, p['Dim']) - mshCrck.setCrack(p['Wake']) - mshCrck.addBoundaries([p['Fluid'], p['Farfield'][-1], p['Wing']]) - mshCrck.run() - else: - for i in range(0, len(p['Wakes'])): - mshCrck = tbox.MshCrack(self.msh, p['Dim']) - mshCrck.setCrack(p['Wakes'][i]) - mshCrck.addBoundaries([p['Fluid'], p['Farfield'][-1], p['Wings'][i]]) - if 'Fuselage' in p: - mshCrck.addBoundaries([p['Fuselage']]) - if 'Symmetry' in p: - mshCrck.addBoundaries([p['Symmetry']]) - mshCrck.setExcluded(p['WakeTips'][i]) - mshCrck.run() - tbox.GmshExport(self.msh).save(self.msh.name) - del mshCrck - self.mshWriter = Writer(self.msh) - print('\n') - - # initialize the problem - self.pbl = dart.Problem(self.msh, p['Dim'], 0., beta, p['M_inf'], p['S_ref'], p['c_ref'], p['x_ref'], p['y_ref'], p['z_ref']) - # add a fluid "air" - self.pbl.set(dart.Fluid(self.msh, p['Fluid'], p['M_inf'], p['Dim'], 0., beta)) - # add initial condition - self.pbl.set(dart.Initial(self.msh, p['Fluid'], p['Dim'], 0., beta)) - # add farfield boundary conditions (Neumann only, a DOF will be pinned automatically) - for i in range (0, len(p['Farfield'])): - self.pbl.add(dart.Freestream(self.msh, p['Farfield'][i], p['Dim'], 0., beta)) - # add solid boundaries - if p['Dim'] == 2: - self.bnd = dart.Body(self.msh, [p['Wing'], p['Fluid']]) - self.pbl.add(self.bnd) - else: - self.bnd = [] - for bd in p['Wings']: - bnd = dart.Body(self.msh, [bd, p['Fluid']]) - self.bnd.append(bnd) - self.pbl.add(bnd) - if 'Fuselage' in p: - bnd = dart.Body(self.msh, [p['Fuselage'], p['Fluid']]) - self.bnd.append(bnd) - self.pbl.add(bnd) - # add Wake/Kutta boundary conditions - if p['Dim'] == 2: - self.pbl.add(dart.Wake(self.msh, [p['Wake'], p['Wake']+'_', p['Fluid']])) - self.pbl.add(dart.Kutta(self.msh, [p['Te'], p['Wake']+'_', p['Wing'], p['Fluid']])) - else: - for i in range(0, len(p['Wakes'])): - self.pbl.add(dart.Wake(self.msh, [p['Wakes'][i], p['Wakes'][i]+'_', p['Fluid'], p['TeTips'][i]])) - - # initialize the linear (inner) solver - print(ccolors.ANSI_BLUE + 'Initializing the solver...' + ccolors.ANSI_RESET) - if p['LSolver'] == 'Pardiso': - linsol = LinearSolver().pardiso() - elif p['LSolver'] == 'GMRES': - linsol = tbox.Gmres() - linsol.setFillFactor(p['G_fill']) - linsol.setRestart(p['G_restart']) - linsol.setTolerance(p['G_tol']) - elif p['LSolver'] == 'MUMPS': - linsol = LinearSolver().mumps() - elif p['LSolver'] == 'SparseLU': - linsol = tbox.SparseLu() - else: - raise RuntimeError('Available linear solvers: Pardiso, GMRES, MUMPS or SparseLU, but ' + p['LSolver'] + ' was given!\n') - # initialize the nonlinear (outer) solver - args = parseargs() - if p['NSolver'] == 'Picard': - self.solver = dart.Picard(self.pbl, linsol, rTol=p['Rel_tol'], aTol=p['Abs_tol'], mIt=p['Max_it'], nthrds=args.k, vrb=args.verb+1) - self.solver.relax = p['Relaxation'] - elif p['NSolver'] == 'Newton': - self.solver = dart.Newton(self.pbl, linsol, rTol=p['Rel_tol'], aTol=p['Abs_tol'], mIt=p['Max_it'], nthrds=args.k, vrb=args.verb+1) - self.solver.lsTol = p['LS_tol'] - self.solver.maxLsIt = p['Max_it_LS'] - self.solver.avThrsh = p['AV_thrsh'] - else: - raise RuntimeError('Available nonlinear solvers: Picard or Newton, but ' + p['NSolver'] + ' was given!\n') diff --git a/dart/api/internal/polar.py b/dart/api/internal/polar.py index 75644e5cde0b313276aecb63c148544b7fc0da47..69f5968bda90344dcc97352f842cf217c33ceb1b 100644 --- a/dart/api/internal/polar.py +++ b/dart/api/internal/polar.py @@ -19,19 +19,22 @@ ## Polar # Adrien Crovato # -# Compute lift and polar curves +# Compute aerodynamic load coefficients as a function of angle of attack import math import dart.utils as dU import tbox.utils as tU +import fwk from fwk.coloring import ccolors -from .config import Config +from ..core import initDart -class Polar(Config): +class Polar: """Utility to compute the polar of a lifting surface Attributes ---------- + tms : fwk.Timers object + dictionary of timers dim : int problem dimensions format : str @@ -59,8 +62,11 @@ class Polar(Config): p : dict dictionary of parameters to configure DART """ + # basic init + self.tms = fwk.Timers() + self.tms["total"].start() + self.dim, _, self.msh, self.wrtr, _, self.pbl, self.bnd, self.sol, _ = initDart(p, scenario='aerodynamic', task='analysis') # save some parameters for later use - self.dim = p['Dim'] self.format = p['Format'] try: self.slice = p['Slice'] @@ -70,8 +76,6 @@ class Polar(Config): self.tag = None self.mach = p['M_inf'] self.alphas = tU.myrange(p['AoA_begin'], p['AoA_end'], p['AoA_step']) - # basic init - Config.__init__(self, p) def run(self): """Compute the polar @@ -90,20 +94,19 @@ class Polar(Config): # run the solver and save the results print(ccolors.ANSI_BLUE + 'Running the solver for', alpha*180/math.pi, 'degrees', ccolors.ANSI_RESET) self.tms["solver"].start() - self.solver.run() + self.sol.run() self.tms["solver"].stop() - self.solver.save(self.mshWriter, ac) - print('\n') + self.sol.save(self.wrtr, ac) # extract Cp if self.dim == 2: - Cp = dU.extract(self.bnd.groups[0].tag.elems, self.solver.Cp) + 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', '') elif self.dim == 3 and self.format == 'vtk' and self.slice: dU.writeSlices(self.msh.name, self.slice, self.tag, ac) # extract force coefficients - self.Cls.append(self.solver.Cl) - self.Cds.append(self.solver.Cd) - self.Cms.append(self.solver.Cm) + self.Cls.append(self.sol.Cl) + self.Cds.append(self.sol.Cd) + self.Cms.append(self.sol.Cm) # end loop ac+=1 diff --git a/dart/api/internal/trim.py b/dart/api/internal/trim.py index 34b3d0b6e6e6c80f9a510d0fd3e55fdb6c4bc482..2555218013c5e20a211ed44fe66de850b985924a 100644 --- a/dart/api/internal/trim.py +++ b/dart/api/internal/trim.py @@ -24,15 +24,18 @@ import math import dart.utils as dU import tbox.utils as tU +import fwk from fwk.coloring import ccolors -from .config import Config +from ..core import initDart -class Trim(Config): +class Trim: """Utility to perform the trim analysis of a given lifting configuration Find the angle of attack to reach a desired lift coefficient Attributes ---------- + tms : fwk.Timers object + dictionary of timers dim : int problem dimensions format : str @@ -51,8 +54,18 @@ class Trim(Config): initial (estimated) slope of lift curve """ def __init__(self, p): + """Setup and configure + + Parameters + ---------- + p : dict + dictionary of parameters to configure DART + """ + # basic init + self.tms = fwk.Timers() + self.tms["total"].start() + self.dim, _, self.msh, self.wrtr, _, self.pbl, self.bnd, self.sol, _ = initDart(p, scenario='aerodynamic', task='analysis') # save some parameters for later use - self.dim = p['Dim'] self.format = p['Format'] try: self.slice = p['Slice'] @@ -64,8 +77,6 @@ class Trim(Config): self.clTgt = p['CL'] self.alpha = p['AoA']*math.pi/180 self.dCl = p['dCL'] - # basic init - Config.__init__(self, p) def run(self): """Perform the trim analysis @@ -79,26 +90,25 @@ class Trim(Config): self.pbl.update(self.alpha) # run the solver self.tms["solver"].start() - success = self.solver.run() + success = self.sol.run() if not success: raise RuntimeError('Flow solver diverged!\n') self.tms["solver"].stop() # update slope if it != 0: - self.dCl = (self.solver.Cl - Cl_) / (self.alpha - alpha_) + self.dCl = (self.sol.Cl - Cl_) / (self.alpha - alpha_) # break or store values and update AoA - if abs(self.clTgt - self.solver.Cl) < 0.005 or math.isnan(self.solver.Cl): + if abs(self.clTgt - self.sol.Cl) < 0.005 or math.isnan(self.sol.Cl): break else: - Cl_ = self.solver.Cl + Cl_ = self.sol.Cl alpha_ = self.alpha - dAlpha = (self.clTgt - self.solver.Cl) / self.dCl + dAlpha = (self.clTgt - self.sol.Cl) / self.dCl self.alpha += dAlpha it += 1 # save results - self.solver.save(self.mshWriter) - print('\n') + self.sol.save(self.wrtr) # extract Cp if self.dim == 2: Cp = dU.extract(self.bnd.groups[0].tag.elems, self.solver.Cp) @@ -107,12 +117,12 @@ class Trim(Config): dU.writeSlices(self.msh.name, self.slice, self.tag) def disp(self): - """DIsplay the results + """Display the results """ # display results print(ccolors.ANSI_BLUE + 'Trim analysis' + ccolors.ANSI_RESET) print(" M alpha dCl Cl Cd Cm") - print("{0:8.2f} {1:8.1f} {2:8.4f} {3:8.4f} {4:8.4f} {5:8.4f}".format(self.mach, self.alpha*180/math.pi, self.dCl, self.solver.Cl, self.solver.Cd, self.solver.Cm)) + print("{0:8.2f} {1:8.1f} {2:8.4f} {3:8.4f} {4:8.4f} {5:8.4f}".format(self.mach, self.alpha*180/math.pi, self.dCl, self.sol.Cl, self.sol.Cd, self.sol.Cm)) print('\n') # display timers self.tms["total"].stop() diff --git a/dart/api/mphys.py b/dart/api/mphys.py index 06f0ca06d1b5598cdd47d6b1ec57f7f0197cc40f..2edba1ebe352dbcaa57bf879dec24690cdaec229 100644 --- a/dart/api/mphys.py +++ b/dart/api/mphys.py @@ -433,14 +433,10 @@ class DartBuilder(Builder): Dimension of the problem __qinf : float Freestream dynamic pressure - __msh : tbox.MshData object - Mesh data structure __wrtr : tbox.MshExport object Mesh writer __mrf : tbox.MshDeform object Mesh morpher - __pbl : dart.Probem object - Problem definition __sol : dart.Newton object Direct Newton solver __adj : dart.Adjoint object @@ -453,192 +449,15 @@ class DartBuilder(Builder): Parameters ---------- params : dict - Dictonnary of parameters for the mesh, solver, ... + Dictonnary of parameters to configure DART scenario : string, optional Type of scenario (available: aerodynamic, aerostructural) (default: aerodynamic) task : string, optional Type of task (available: analysis, optimization) (default: analysis) """ - # Initialize print('*'*31 + 'mphys.DartBuilder' + '*'*31 + '\n') - - # Imports - from dartflo import tbox - from dartflo.tbox import gmsh - from dartflo import dart - - # Basic checks and config - # dimension - if params['Dim'] != 2 and params['Dim'] != 3: - raise RuntimeError('Problem dimension should be 2 or 3, but ' + params['Dim'] + ' was given!\n') - else: - self.__dim = params['Dim'] - # aoa and aos - if 'AoA' in params: - alpha = params['AoA'] * np.pi / 180 - else: - alpha = 0 - if self.__dim == 2: - if 'AoS' in params and params['AoS'] != 0: - raise RuntimeError('Angle of sideslip (AoS) should be zero for 2D problems!\n') - else: - beta = 0 - if 'Symmetry' in params: - raise RuntimeError('Symmetry boundary condition cannot be used for 2D problems!\n') - else: - if 'AoS' in params: - if params['AoS'] != 0 and 'Symmetry' in params: - raise RuntimeError('Symmetry boundary condition cannot be used with nonzero angle of sideslip (AoS)!\n') - beta = params['AoS'] * np.pi / 180 - else: - beta = 0 - # save format - if params['Format'] == 'vtk': - try: - import tboxVtk - Writer = tboxVtk.VtkExport - print('VTK libraries found! Results will be saved in VTK format.\n') - except: - Writer = tbox.GmshExport - print('VTK libraries not found! Results will be saved in gmsh format.\n') - else: - Writer = tbox.GmshExport - print('Results will be saved in gmsh format.\n') - # number of threads - if 'Threads' in params: - nthrd = params['Threads'] - else: - nthrd = 1 - # verbosity - if 'Verb' in params: - verb = params['Verb'] - else: - verb = 0 - # scenario and task type - if scenario != 'aerodynamic' and scenario != 'aerostructural': - raise RuntimeError('Scenario should be aerodynamic or aerostructural, but "' + scenario + '" was given!\n') - if task != 'analysis' and task != 'optimization': - raise RuntimeError('Task should be analysis or optimization, but "' + scenario + '" was given!\n') - # dynamic pressure - if scenario == 'aerostructural': - self.__qinf = params['Q_inf'] - else: - self.__qinf = 0 - - # Mesh creation - self.__msh = gmsh.MeshLoader(params['File']).execute(**params['Pars']) - # add the wake - if self.__dim == 2: - mshCrck = tbox.MshCrack(self.__msh, self.__dim) - mshCrck.setCrack(params['Wake']) - mshCrck.addBoundaries([params['Fluid'], params['Farfield'][-1], params['Wing']]) - mshCrck.run() - else: - for i in range(len(params['Wakes'])): - mshCrck = tbox.MshCrack(self.__msh, self.__dim) - mshCrck.setCrack(params['Wakes'][i]) - mshCrck.addBoundaries([params['Fluid'], params['Farfield'][-1], params['Wings'][i]]) - if 'Fuselage' in params: - mshCrck.addBoundaries([params['Fuselage']]) - if 'Symmetry' in params: - mshCrck.addBoundaries([params['Symmetry']]) - if 'WakeTips' in params: - mshCrck.setExcluded(params['WakeTips'][i]) # 2.5D computations - mshCrck.run() - # save the updated mesh in native (gmsh) format and then set the writer to the requested format - tbox.GmshExport(self.__msh).save(self.__msh.name) - del mshCrck - self.__wrtr = Writer(self.__msh) - print('\n') - - # Mesh morpher creation - if scenario == 'aerostructural' or task == 'optimization': - self.__mrf = tbox.MshDeform(self.__msh, self.__dim) - self.__mrf.nthreads = nthrd - self.__mrf.setField(params['Fluid']) - self.__mrf.addFixed(params['Farfield']) - if self.__dim == 2: - self.__mrf.addMoving([params['Wing']]) - self.__mrf.addInternal([params['Wake'], params['Wake']+'_']) - else: - if 'Fuselage' in params: - self.__mrf.addFixed(params['Fuselage']) - self.__mrf.setSymmetry(params['Symmetry'], 1) - for i in range(len(params['Wings'])): - if i == 0: - self.__mrf.addMoving([params['Wings'][i]]) # TODO body of interest (FSI/OPT) is always first given body - else: - self.__mrf.addFixed([params['Wings'][i]]) - self.__mrf.addInternal([params['Wakes'][i], params['Wakes'][i]+'_']) - self.__mrf.initialize() - else: - self.__mrf = None - - # Problem creation - self.__pbl = dart.Problem(self.__msh, self.__dim, alpha, beta, params['M_inf'], params['S_ref'], params['c_ref'], params['x_ref'], params['y_ref'], params['z_ref']) - # add the field - self.__pbl.set(dart.Fluid(self.__msh, params['Fluid'], params['M_inf'], self.__dim, alpha, beta)) - # add the initial condition - self.__pbl.set(dart.Initial(self.__msh, params['Fluid'], self.__dim, alpha, beta)) - # add farfield boundary conditions (Neumann only, a DOF will be pinned automatically) - for i in range (len(params['Farfield'])): - self.__pbl.add(dart.Freestream(self.__msh, params['Farfield'][i], self.__dim, alpha, beta)) - # add solid boundaries - if self.__dim == 2: - bnd = dart.Body(self.__msh, [params['Wing'], params['Fluid']]) - self.__bnd = bnd - self.__pbl.add(bnd) - else: - self.__bnd = None - for bd in params['Wings']: - bnd = dart.Body(self.__msh, [bd, params['Fluid']]) - if self.__bnd is None: - self.__bnd = bnd # TODO body of interest (FSI/OPT) is always first given body - self.__pbl.add(bnd) - if 'Fuselage' in params: - bnd = dart.Body(self.__msh, [params['Fuselage'], params['Fluid']]) - self.__pbl.add(bnd) - # add Wake/Kutta boundary conditions - if self.__dim == 2: - self.__pbl.add(dart.Wake(self.__msh, [params['Wake'], params['Wake']+'_', params['Fluid']])) - self.__pbl.add(dart.Kutta(self.__msh, [params['Te'], params['Wake']+'_', params['Wing'], params['Fluid']])) - else: - for i in range(len(params['Wakes'])): - self.__pbl.add(dart.Wake(self.__msh, [params['Wakes'][i], params['Wakes'][i]+'_', params['Fluid'], params['TeTips'][i]])) - - # Direct (forward) solver creation - # NB: more solvers/options are available but we restrict the user's choice to the most efficient ones - # initialize the linear (inner) solver - if params['LSolver'] == 'PARDISO': - linsol = tbox.Pardiso() - elif params['LSolver'] == 'MUMPS': - linsol = tbox.Mumps() - elif params['LSolver'] == 'GMRES': - linsol = tbox.Gmres() - linsol.setFillFactor(params['G_fill']) if 'G_fill' in params else linsol.setFillFactor(2) - linsol.setRestart(params['G_restart']) if 'G_restart' in params else linsol.setRestart(50) - linsol.setTolerance(params['G_tol']) if 'G_tol' in params else linsol.setTolerance(1e-5) - else: - raise RuntimeError('Available linear solvers: PARDISO, MUMPS or GMRES, but ' + params['LSolver'] + ' was given!\n') - # initialize the nonlinear (outer) solver - self.__sol = dart.Newton(self.__pbl, linsol, rTol=params['Rel_tol'], aTol=params['Abs_tol'], mIt=params['Max_it'], nthrds=nthrd, vrb=verb) - - # Adjoint (reverse) solver creation - if task == 'optimization': - if params['LSolver'] == 'PARDISO': - alinsol = tbox.Pardiso() - elif params['LSolver'] == 'MUMPS': - alinsol = tbox.Mumps() - else: - alinsol = tbox.Gmres() - alinsol.setFillFactor(params['G_fill']) if 'G_fill' in params else alinsol.setFillFactor(2) - alinsol.setRestart(params['G_restart']) if 'G_restart' in params else alinsol.setRestart(50) - alinsol.setTolerance(1e-12) - self.__adj = dart.Adjoint(self.__sol, self.__mrf, alinsol) - else: - self.__adj = None - - # Finalize + from .core import initDart + self.__dim, self.__qinf, _, self.__wrtr, self.__mrf, _, self.__bnd, self.__sol, self.__adj = initDart(params, scenario=scenario, task=task) print('*'*31 + 'mphys.DartBuilder' + '*'*31 + '\n') def get_mesh_coordinate_subsystem(self): diff --git a/dart/cases/coyote.py b/dart/cases/coyote.py index e23edac7696331c98e4ed432539fdc088367e4ca..6dda2e0d08de2e3f96136091afd2e83cb368361d 100644 --- a/dart/cases/coyote.py +++ b/dart/cases/coyote.py @@ -25,7 +25,12 @@ def getParam(): import os.path + from fwk.wutils import parseargs p = {} + # Arguments + args = parseargs() + p['Threads'] = args.k + p['Verb'] = args.verb # Specific scl = 2 # scaling factor for lifting surface mesh size wrtems = scl*0.036 # wing root trailing edge mesh size @@ -61,17 +66,13 @@ def getParam(): p['y_ref'] = 1.8 # Reference point for moment computation (y) p['z_ref'] = 0.8 # Reference point for moment computation (z) # Numerical - p['LSolver'] = 'GMRES' # Linear solver (Pardiso, GMRES, MUMPS or SparseLU) + p['LSolver'] = 'GMRES' # Linear solver (PARDISO, GMRES, or MUMPS) p['G_fill'] = 2 # Fill-in factor for GMRES preconditioner p['G_tol'] = 1e-5 # Tolerance for GMRES p['G_restart'] = 50 # Restart for GMRES - p['NSolver'] = 'Newton' # Noninear solver type (Picard or Newton) p['Rel_tol'] = 1e-6 # Relative tolerance on solver residual p['Abs_tol'] = 1e-8 # Absolute tolerance on solver residual p['Max_it'] = 15 # Solver maximum number of iterations - p['LS_tol'] = 1e-6 # Tolerance on linesearch residual - p['Max_it_LS'] = 10 # Linesearch maximum number of iterations - p['AV_thrsh'] = 1e-2 # Residual threshold below which the artificial viscosity is decreased return p def main(): diff --git a/dart/cases/n0012.py b/dart/cases/n0012.py index f984557d0a4ac95309b850fb30f77014a371cf3f..ed9f3dae1a3eca1532ab7c2b236c137d79d8173f 100644 --- a/dart/cases/n0012.py +++ b/dart/cases/n0012.py @@ -21,7 +21,12 @@ def getParam(): import os.path + from fwk.wutils import parseargs p = {} + # Arguments + args = parseargs() + p['Threads'] = args.k + p['Verb'] = args.verb # 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 @@ -45,12 +50,10 @@ def getParam(): p['y_ref'] = 0. # Reference point for moment computation (y) p['z_ref'] = 0. # Reference point for moment computation (z) # Numerical - p['LSolver'] = 'SparseLU' # Linear solver (Pardiso, GMRES, MUMPS or SparseLU) - p['NSolver'] = 'Picard' # Noninear solver type (Picard or Newton) + p['LSolver'] = 'GMRES' # Linear solver (PARDISO, GMRES, or MUMPS) p['Rel_tol'] = 1e-6 # Relative tolerance on solver residual p['Abs_tol'] = 1e-8 # Absolute tolerance on solver residual p['Max_it'] = 20 # Solver maximum number of iterations - p['Relaxation'] = 0.7 # Relaxation parameter for Picard solver return p def main(): diff --git a/dart/cases/n0012_3.py b/dart/cases/n0012_3.py index 11338607c8935df66e8a9c486a850780820e2b6f..57700c2e6cbe4b92065fcc678300cef2369fb776 100644 --- a/dart/cases/n0012_3.py +++ b/dart/cases/n0012_3.py @@ -21,9 +21,14 @@ def getParam(): import os.path + from fwk.wutils import parseargs p = {} # Specific span = 1. # span length + # Arguments + args = parseargs() + p['Threads'] = args.k + p['Verb'] = args.verb # Input/Output p['File'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models/n0012_3.geo') # Input file containing the model p['Pars'] = {'spn' : span, 'lgt' : 3., 'wdt' : 3., 'hgt' : 3., 'msLe' : 0.02, 'msTe' : 0.02, 'msF' : 1.} # Parameters for input file model @@ -51,14 +56,10 @@ def getParam(): p['y_ref'] = 0. # Reference point for moment computation (y) p['z_ref'] = 0. # Reference point for moment computation (z) # Numerical - p['LSolver'] = 'MUMPS' # Linear solver (Pardiso, GMRES, MUMPS or SparseLU) - p['NSolver'] = 'Newton' # Noninear solver type (Picard or Newton) + p['LSolver'] = 'MUMPS' # Linear solver (PARDISO, GMRES, or MUMPS) p['Rel_tol'] = 1e-6 # Relative tolerance on solver residual p['Abs_tol'] = 1e-8 # Absolute tolerance on solver residual p['Max_it'] = 10 # Solver maximum number of iterations - p['LS_tol'] = 1e-6 # Tolerance on linesearch residual - p['Max_it_LS'] = 10 # Linesearch maximum number of iterations - p['AV_thrsh'] = 1e-2 # Residual threshold below which the artificial viscosity is decreased return p def main(): diff --git a/dart/cases/n64A410.py b/dart/cases/n64A410.py index 0ef97fcdde17c9cb70329faa74ece232e62552ac..d033dcbdbec156fd160b65a4623c30d5c1ca1a53 100644 --- a/dart/cases/n64A410.py +++ b/dart/cases/n64A410.py @@ -21,7 +21,12 @@ def getParam(): import os.path + from fwk.wutils import parseargs p = {} + # Arguments + args = parseargs() + p['Threads'] = args.k + p['Verb'] = args.verb # 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 @@ -45,14 +50,10 @@ def getParam(): p['y_ref'] = 0. # Reference point for moment computation (y) p['z_ref'] = 0. # Reference point for moment computation (z) # Numerical - p['LSolver'] = 'Pardiso' # Linear solver (Pardiso, GMRES, MUMPS or SparseLU) - p['NSolver'] = 'Newton' # Noninear solver type (Picard or Newton) + p['LSolver'] = 'PARDISO' # Linear solver (PARDISO, GMRES, or MUMPS) p['Rel_tol'] = 1e-6 # Relative tolerance on solver residual p['Abs_tol'] = 1e-8 # Absolute tolerance on solver residual p['Max_it'] = 25 # Solver maximum number of iterations - p['LS_tol'] = 1e-6 # Tolerance on linesearch residual - p['Max_it_LS'] = 10 # Linesearch maximum number of iterations - p['AV_thrsh'] = 1e-2 # Residual threshold below which the artificial viscosity is decreased return p def main(): diff --git a/dart/cases/rae2822.py b/dart/cases/rae2822.py index cf515702b802afe84263d3dbe38362153024b9a6..1b9c6cb4c07e7b9889e62c32c2365f655e930d1b 100644 --- a/dart/cases/rae2822.py +++ b/dart/cases/rae2822.py @@ -21,7 +21,12 @@ def getParam(): import os.path + from fwk.wutils import parseargs p = {} + # Arguments + args = parseargs() + p['Threads'] = args.k + p['Verb'] = args.verb # 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 @@ -45,17 +50,13 @@ def getParam(): p['y_ref'] = 0. # Reference point for moment computation (y) p['z_ref'] = 0. # Reference point for moment computation (z) # Numerical - p['LSolver'] = 'GMRES' # Linear solver (Pardiso, GMRES, MUMPS or SparseLU) + p['LSolver'] = 'GMRES' # Linear solver (PARDISO, GMRES, or MUMPS) p['G_fill'] = 2 # Fill-in factor for GMRES preconditioner p['G_tol'] = 1e-5 # Tolerance for GMRES p['G_restart'] = 50 # Restart for GMRES - p['NSolver'] = 'Newton' # Noninear solver type (Picard or Newton) p['Rel_tol'] = 1e-6 # Relative tolerance on solver residual p['Abs_tol'] = 1e-8 # Absolute tolerance on solver residual p['Max_it'] = 10 # Solver maximum number of iterations - p['LS_tol'] = 1e-6 # Tolerance on linesearch residual - p['Max_it_LS'] = 10 # Linesearch maximum number of iterations - p['AV_thrsh'] = 1e-2 # Residual threshold below which the artificial viscosity is decreased return p def main(): diff --git a/dart/cases/wbht.py b/dart/cases/wbht.py index 4255fffd60dff15f1a0e5c7b763343f48a1a821e..4744d14e7fac451e54428a842c6e4b6e37acc4ee 100644 --- a/dart/cases/wbht.py +++ b/dart/cases/wbht.py @@ -24,6 +24,7 @@ def getParam(): import os.path + from fwk.wutils import parseargs p = {} # Specific scl = 2 # scaling factor for lifting surface mesh size @@ -40,6 +41,10 @@ def getParam(): fums = 0.15 # fuselage mesh size fucms = 0.01 # fuselage caps mesh size fams = 5. # farfield mesh size + # Arguments + args = parseargs() + p['Threads'] = args.k + p['Verb'] = args.verb # 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, @@ -73,17 +78,13 @@ def getParam(): p['y_ref'] = 0.66 # Reference point for moment computation (y) p['z_ref'] = -0.5 # Reference point for moment computation (z) # Numerical - p['LSolver'] = 'GMRES' # Linear solver (Pardiso, GMRES, MUMPS or SparseLU) + p['LSolver'] = 'GMRES' # Linear solver (PARDISO, GMRES, or MUMPS) p['G_fill'] = 2 # Fill-in factor for GMRES preconditioner p['G_tol'] = 1e-5 # Tolerance for GMRES p['G_restart'] = 50 # Restart for GMRES - p['NSolver'] = 'Newton' # Noninear solver type (Picard or Newton) p['Rel_tol'] = 1e-6 # Relative tolerance on solver residual p['Abs_tol'] = 1e-8 # Absolute tolerance on solver residual p['Max_it'] = 15 # Solver maximum number of iterations - p['LS_tol'] = 1e-6 # Tolerance on linesearch residual - p['Max_it_LS'] = 10 # Linesearch maximum number of iterations - p['AV_thrsh'] = 1e-2 # Residual threshold below which the artificial viscosity is decreased return p def main(): diff --git a/dart/cases/wht.py b/dart/cases/wht.py index c5f90f507ab3b8dd2615c4a156c5598ef7fcd127..ce7301700cb9d5967ab2f500b323740fe02a7d6d 100644 --- a/dart/cases/wht.py +++ b/dart/cases/wht.py @@ -21,6 +21,7 @@ def getParam(): import os.path + from fwk.wutils import parseargs p = {} # Specific scl = 2 # scaling factor for lifting surface mesh size @@ -35,6 +36,10 @@ def getParam(): ttlems = scl*0.0058 # tail tip leading mesh size tttems = scl*0.0058 # tail tip trailing mesh size fams = 5. # farfield mesh size + # Arguments + args = parseargs() + p['Threads'] = args.k + p['Verb'] = args.verb # 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, @@ -67,14 +72,10 @@ def getParam(): p['y_ref'] = 0.0 # Reference point for moment computation (y) p['z_ref'] = 0.0 # Reference point for moment computation (z) # Numerical - p['LSolver'] = 'Pardiso' # Linear solver (Pardiso, GMRES, MUMPS or SparseLU) - p['NSolver'] = 'Newton' # Noninear solver type (Picard or Newton) + p['LSolver'] = 'PARDISO' # Linear solver (PARDISO, GMRES, or MUMPS) p['Rel_tol'] = 1e-6 # Relative tolerance on solver residual p['Abs_tol'] = 1e-8 # Absolute tolerance on solver residual p['Max_it'] = 15 # Solver maximum number of iterations - p['LS_tol'] = 1e-6 # Tolerance on linesearch residual - p['Max_it_LS'] = 10 # Linesearch maximum number of iterations - p['AV_thrsh'] = 1e-2 # Residual threshold below which the artificial viscosity is decreased return p def main():