diff --git a/examples/Disk-Patch/GravityOnly/README b/examples/Disk-Patch/GravityOnly/README new file mode 100644 index 0000000000000000000000000000000000000000..5bf2638fc5ed48ebe248773223dde888af0c3bc8 --- /dev/null +++ b/examples/Disk-Patch/GravityOnly/README @@ -0,0 +1,10 @@ +Setup for a potential of a patch disk, see Creasey, Theuns & +Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 + +The density is given by +rho(z) = (Sigma/2b) / cosh^2(z/b) +where Sigma is the surface density, and b the scale height. + +The corresponding force is +dphi/dz = 2 pi G Sigma tanh(z/b), +which satifies d^2phi/dz^2 = 4 pi G rho. diff --git a/examples/Disk-Patch/GravityOnly/disk-patch.yml b/examples/Disk-Patch/GravityOnly/disk-patch.yml new file mode 100644 index 0000000000000000000000000000000000000000..78b42e78356f83e80eee8e7f5f91ad7dcf90c37f --- /dev/null +++ b/examples/Disk-Patch/GravityOnly/disk-patch.yml @@ -0,0 +1,43 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9885e33 # Grams + UnitLength_in_cgs: 3.0856776e18 # Centimeters + UnitVelocity_in_cgs: 1e5 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 480. # The end time of the simulation (in internal units). + dt_min: 1e-3 # The minimal time-step size of the simulation (in internal units). + dt_max: 1 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.0 # Time between statistics output + +# Parameters governing the snapshots +Snapshots: + basename: Disk-Patch # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 8. # Time difference between consecutive outputs (in internal units) + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2349 # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 1. # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + max_ghost_iterations: 30 # Maximal number of iterations allowed to converge towards the smoothing length. + max_smoothing_length: 40. # Maximal smoothing length allowed (in internal units). + +# Parameters related to the initial conditions +InitialConditions: + file_name: Disk-Patch.hdf5 # The file to read + +# External potential parameters +Disk-PatchPotential: + surface_density: 10. + scale_height: 100. + z_disk: 300. + timestep_mult: 0.03 diff --git a/examples/Disk-Patch/GravityOnly/makeIC.py b/examples/Disk-Patch/GravityOnly/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..135d1d4e34e0ec4c32d75a2c49d6e55f46db4e1c --- /dev/null +++ b/examples/Disk-Patch/GravityOnly/makeIC.py @@ -0,0 +1,161 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 John A. Regan (john.a.regan@durham.ac.uk) + # Tom Theuns (tom.theuns@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +import sys +import numpy +import math +import random + +# Generates N particles in a box of [0:BoxSize,0:BoxSize,-2scale_height:2scale_height] +# see Creasey, Theuns & Bower, 2013, for the equations: +# disk parameters are: surface density sigma +# scale height b +# density: rho(z) = (sigma/2b) sech^2(z/b) +# isothermal velocity dispersion = <v_z^2? = b pi G sigma +# grad potential = 2 pi G sigma tanh(z/b) +# potential = ln(cosh(z/b)) + const +# Dynamical time = sqrt(b / (G sigma)) +# to obtain the 1/ch^2(z/b) profile from a uniform profile (a glass, say, or a uniform random variable), note that, when integrating in z +# \int 0^z dz/ch^2(z) = tanh(z)-tanh(0) = \int_0^x dx = x (where the last integral refers to a uniform density distribution), so that z = atanh(x) +# usage: python makeIC.py 1000 + +# physical constants in cgs +NEWTON_GRAVITY_CGS = 6.672e-8 +SOLAR_MASS_IN_CGS = 1.9885e33 +PARSEC_IN_CGS = 3.0856776e18 +PROTON_MASS_IN_CGS = 1.6726231e24 +YEAR_IN_CGS = 3.154e+7 + +# choice of units +const_unit_length_in_cgs = (PARSEC_IN_CGS) +const_unit_mass_in_cgs = (SOLAR_MASS_IN_CGS) +const_unit_velocity_in_cgs = (1e5) + +print "UnitMass_in_cgs: ", const_unit_mass_in_cgs +print "UnitLength_in_cgs: ", const_unit_length_in_cgs +print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs + + +# parameters of potential +surface_density = 10. +scale_height = 100. + +# derived units +const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) +const_G = ((NEWTON_GRAVITY_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) +print 'G=', const_G +v_disp = numpy.sqrt(scale_height * math.pi * const_G * surface_density) +t_dyn = numpy.sqrt(scale_height / (const_G * surface_density)) +print 'dynamical time = ',t_dyn +print ' velocity dispersion = ',v_disp + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 600. # +Radius = 100. # maximum radius of particles [kpc] +G = const_G + +N = int(sys.argv[1]) # Number of particles + +# these are not used but necessary for I/O +rho = 2. # Density +P = 1. # Pressure +gamma = 5./3. # Gas adiabatic index +fileName = "Disk-Patch.hdf5" + + +#--------------------------------------------------- +numPart = N +mass = 1 +internalEnergy = P / ((gamma - 1.)*rho) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [0, numPart, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [0, numPart, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] + + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +# set seed for random number +numpy.random.seed(1234) + +#Particle group +#grp0 = file.create_group("/PartType0") +grp1 = file.create_group("/PartType1") + +#generate particle positions +r = numpy.zeros((numPart, 3)) +r[:,0] = numpy.random.rand(N) * boxSize +r[:,1] = numpy.random.rand(N) * boxSize +z = scale_height * numpy.arctanh(numpy.random.rand(2*N)) +gd = z < boxSize / 2 +r[:,2] = z[gd][0:N] +random = numpy.random.rand(N) > 0.5 +r[random,2] *= -1 +r[:,2] += 0.5 * boxSize + +#generate particle velocities +v = numpy.zeros((numPart, 3)) +v = numpy.zeros(1) +#v[:,2] = + + +ds = grp1.create_dataset('Velocities', (numPart, 3), 'f') +ds[()] = v + + +m = numpy.ones((numPart, ), dtype=numpy.float32) * mass +ds = grp1.create_dataset('Masses', (numPart,), 'f') +ds[()] = m +m = numpy.zeros(1) + + +ids = 1 + numpy.linspace(0, numPart, numPart, endpoint=False, dtype='L') +ds = grp1.create_dataset('ParticleIDs', (numPart, ), 'L') +ds[()] = ids + +ds = grp1.create_dataset('Coordinates', (numPart, 3), 'd') +ds[()] = r + + +file.close() diff --git a/examples/Disk-Patch/GravityOnly/run.sh b/examples/Disk-Patch/GravityOnly/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..a123ad24d7ca34105c22f5f31e75c688c681288f --- /dev/null +++ b/examples/Disk-Patch/GravityOnly/run.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e Isothermal.hdf5 ] +then + echo "Generating initial conditions for the disk-patch example..." + python makeIC.py 1000 +fi + +../../swift -g -t 2 disk-patch.yml diff --git a/examples/Disk-Patch/GravityOnly/test.pro b/examples/Disk-Patch/GravityOnly/test.pro new file mode 100644 index 0000000000000000000000000000000000000000..4bd0d001975d80b6729cf2ef7b95f81da5bc4fe8 --- /dev/null +++ b/examples/Disk-Patch/GravityOnly/test.pro @@ -0,0 +1,158 @@ +; +; test energy / angular momentum conservation of test problem +; + +iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare final and initial energy + +; set physical constants +@physunits + +indir = './' +basefile = 'Disk-Patch_' + +; set properties of potential +uL = phys.pc ; unit of length +uM = phys.msun ; unit of mass +uV = 1d5 ; unit of velocity + +; properties of patch +surface_density = 10. +scale_height = 100. + +; derived units +constG = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ; +pcentre = [0.,0.,300.] * pc / uL + +; +infile = indir + basefile + '*' +spawn,'ls -1 '+infile,res +nfiles = n_elements(res) + + +; choose: calculate change of energy and Lz, comparing first and last +; snapshots for all particles, or do so for a subset + +; compare all +ifile = 0 +inf = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5' +id = h5rd(inf,'PartType1/ParticleIDs') +nfollow = n_elements(id) + +; follow a subset +nfollow = 500 ; number of particles to follow + +; +if (iplot eq 1) then begin + nskip = 1 + nsave = nfiles +endif else begin + nskip = nfiles - 2 + nsave = 2 +endelse + +; +lout = fltarr(nfollow, nsave) ; Lz +xout = fltarr(nfollow, nsave) ; x +yout = fltarr(nfollow, nsave) ; y +zout = fltarr(nfollow, nsave) ; z +eout = fltarr(nfollow, nsave) ; energies +ekin = fltarr(nfollow, nsave) +epot = fltarr(nfollow, nsave) ; 2 pi G Sigma b ln(cosh(z/b)) + const +tout = fltarr(nsave) + + + +ifile = 0 +isave = 0 +for ifile=0,nfiles-1,nskip do begin + inf = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5' + time = h5ra(inf, 'Header','Time') + p = h5rd(inf,'PartType1/Coordinates') + v = h5rd(inf,'PartType1/Velocities') + id = h5rd(inf,'PartType1/ParticleIDs') + indx = sort(id) + +;; ; if you want to sort particles by ID +;; id = id[indx] +;; for ic=0,2 do begin +;; tmp = reform(p[ic,*]) & p[ic,*] = tmp[indx] +;; tmp = reform(v[ic,*]) & v[ic,*] = tmp[indx] +;; endfor + + +; calculate energy + dd = size(p,/dimen) & npart = dd[1] + ener = fltarr(npart) + dr = fltarr(npart) & dv = dr + for ic=0,2 do dr[*] = dr[*] + (p[ic,*]-pcentre[ic])^2 + for ic=0,2 do dv[*] = dv[*] + v[ic,*]^2 + xout[*,isave] = p[0,0:nfollow-1]-pcentre[0] + yout[*,isave] = p[1,0:nfollow-1]-pcentre[1] + zout[*,isave] = p[2,0:nfollow-1]-pcentre[2] + Lz = (p[0,*]-pcentre[0]) * v[1,*] - (p[1,*]-pcentre[1]) * v[0,*] + dz = reform(p[2,0:nfollow-1]-pcentre[2]) +; print,'time = ',time,p[0,0],v[0,0],id[0] + ek = 0.5 * dv + ep = fltarr(nfollow) + ep = 2 * !pi * constG * surface_density * scale_height * alog(cosh(abs(dz)/scale_height)) + ener = ek + ep + tout(isave) = time + lout[*,isave] = lz[0:nfollow-1] + eout(*,isave) = ener[0:nfollow-1] + ekin(*,isave) = ek[0:nfollow-1] + epot(*,isave) = ep[0:nfollow-1] + print,format='('' time= '',f7.1,'' E= '',f9.2,'' Lz= '',e9.2)', time,eout[0],lz[0] + isave = isave + 1 + +endfor + +x0 = reform(xout[0,*]) +y0 = reform(xout[1,*]) +z0 = reform(xout[2,*]) + +; calculate relative energy change +de = 0.0 * eout +dl = 0.0 * lout +nsave = isave +for ifile=1, nsave-1 do de[*,ifile] = (eout[*,ifile]-eout[*,0])/eout[*,0] +for ifile=1, nsave-1 do dl[*,ifile] = (lout[*,ifile] - lout[*,0])/lout[*,0] + + +; calculate statistics of energy changes +print,' relatve energy change: (per cent) ',minmax(de) * 100. +print,' relative Lz change: (per cent) ',minmax(dl) * 100. + +; plot enery and Lz conservation for some particles +if(iplot eq 1) then begin +; plot results on energy conservation for some particles + nplot = min(10, nfollow) + win,0 + xr = [min(tout), max(tout)] + yr = [-2,2]*1d-2 ; in percent + plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/nodata,xtitle='time',ytitle='dE/E, dL/L (%)' + for i=0,nplot-1 do oplot,tout,de[i,*] + for i=0,nplot-1 do oplot,tout,dl[i,*],color=red + legend,['dE/E','dL/L'],linestyle=[0,0],color=[black,red],box=0,/bottom,/left + screen_to_png,'e-time.png' + +; plot vertical oscillation + win,2 + xr = [min(tout), max(tout)] + yr = [-3,3]*scale_height + plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/iso,/nodata,xtitle='x',ytitle='y' + color = floor(findgen(nplot)*255/float(nplot)) + for i=0,nplot-1 do oplot,tout,zout[i,*],color=color(i) + screen_to_png,'orbit.png' + +; make histogram of energy changes at end + win,6 + ohist,de,x,y,-0.05,0.05,0.001 + plot,x,y,psym=10,xtitle='de (%)' + screen_to_png,'de-hist.png' + + +endif + +end + + diff --git a/examples/Disk-Patch/HydroStatic/README b/examples/Disk-Patch/HydroStatic/README new file mode 100644 index 0000000000000000000000000000000000000000..1bbb7e8a34f261e0feb1cab60d9d31519817b0b2 --- /dev/null +++ b/examples/Disk-Patch/HydroStatic/README @@ -0,0 +1,19 @@ +Generates and evolves a disk-patch, where gas is in hydrostatic +equilibrium with an imposed external gravitational force, using the +equations from Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, +Issue 3, p.1922-1948. + +To generate ICs ready for a scientific run: + +1) Recover a uniform glass file by running 'getGlass.sh'. + +2) Generate pre-ICs by running the 'makeIC.py' script. + +3) Run SWIFT with an isothermal EoS, no cooling nor feedback, and the +disk-patch potential switched on and using the parameters from +'disk-patch-icc.yml' + +4) The ICs are then ready to be run for a science problem. + +When running SWIFT with the parameters from 'disk-patch.yml' and an +ideal gas EoS on these ICs the disk should stay in equilibrium. diff --git a/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml b/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml new file mode 100644 index 0000000000000000000000000000000000000000..ebf04675852a7663119ed1ecfd33a05da6b7bb15 --- /dev/null +++ b/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml @@ -0,0 +1,44 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9885e33 # Grams + UnitLength_in_cgs: 3.0856776e18 # Centimeters + UnitVelocity_in_cgs: 1e5 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0 # The starting time of the simulation (in internal units). + time_end: 968. # The end time of the simulation (in internal units). + dt_min: 1e-4 # The minimal time-step size of the simulation (in internal units). + dt_max: 1. # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1 # Time between statistics output + +# Parameters governing the snapshots +Snapshots: + basename: Disk-Patch # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 12. # Time difference between consecutive outputs (in internal units) + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2349 # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + max_ghost_iterations: 30 # Maximal number of iterations allowed to converge towards the smoothing length. + max_smoothing_length: 70. # Maximal smoothing length allowed (in internal units). + +# Parameters related to the initial conditions +InitialConditions: + file_name: Disk-Patch.hdf5 # The file to read + +# External potential parameters +Disk-PatchPotential: + surface_density: 10. + scale_height: 100. + z_disk: 200. + timestep_mult: 0.03 + growth_time: 5. diff --git a/examples/Disk-Patch/HydroStatic/disk-patch.yml b/examples/Disk-Patch/HydroStatic/disk-patch.yml new file mode 100644 index 0000000000000000000000000000000000000000..55df81a08d16c6a4f39aa5e9e9205101dedaa3a9 --- /dev/null +++ b/examples/Disk-Patch/HydroStatic/disk-patch.yml @@ -0,0 +1,43 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9885e33 # Grams + UnitLength_in_cgs: 3.0856776e18 # Centimeters + UnitVelocity_in_cgs: 1e5 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 968 # The starting time of the simulation (in internal units). + time_end: 12000. # The end time of the simulation (in internal units). + dt_min: 1e-4 # The minimal time-step size of the simulation (in internal units). + dt_max: 1. # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1 # Time between statistics output + +# Parameters governing the snapshots +Snapshots: + basename: Disk-Patch-dynamic # Common part of the name of output files + time_first: 968. # Time of the first output (in internal units) + delta_time: 24. # Time difference between consecutive outputs (in internal units) + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2349 # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + max_ghost_iterations: 30 # Maximal number of iterations allowed to converge towards the smoothing length. + max_smoothing_length: 70. # Maximal smoothing length allowed (in internal units). + +# Parameters related to the initial conditions +InitialConditions: + file_name: Disk-Patch-dynamic.hdf5 # The file to read + +# External potential parameters +Disk-PatchPotential: + surface_density: 10. + scale_height: 100. + z_disk: 200. + timestep_mult: 0.03 diff --git a/examples/Disk-Patch/HydroStatic/dynamic.pro b/examples/Disk-Patch/HydroStatic/dynamic.pro new file mode 100644 index 0000000000000000000000000000000000000000..c02c65fe418e84cdd62978dbddcf5a641fa4c156 --- /dev/null +++ b/examples/Disk-Patch/HydroStatic/dynamic.pro @@ -0,0 +1,129 @@ +; +; test energy / angular momentum conservation of test problem +; + +iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare final and initial energy + +; set physical constants +@physunits + +indir = './' +basefile = 'Disk-Patch-dynamic_' + +; set properties of potential +uL = phys.pc ; unit of length +uM = phys.msun ; unit of mass +uV = 1d5 ; unit of velocity + +; properties of patch +surface_density = 10. +scale_height = 100. +z_disk = 200.; +gamma = 5./3. + +; derived units +constG = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ; +pcentre = [0.,0.,z_disk] * pc / uL +utherm = !pi * constG * surface_density * scale_height / (gamma-1.) +soundspeed = sqrt(gamma * (gamma-1.) * utherm) +t_dyn = sqrt(scale_height / (constG * surface_density)) + +; +infile = indir + basefile + '*' +spawn,'ls -1 '+infile,res +nfiles = n_elements(res) + + +; choose: calculate change of energy and Lz, comparing first and last +; snapshots for all particles, or do so for a subset + +; compare all +ifile = 0 +inf = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5' +id = h5rd(inf,'PartType0/ParticleIDs') +nfollow = n_elements(id) + + +; compute anlytic profile +nbins = 100 +zbins = findgen(nbins)/float(nbins-1) * 2 * scale_height +rbins = (surface_density/(2.*scale_height)) / cosh(abs(zbins)/scale_height)^2 + + +; plot analytic profile +wset,0 +plot,[0],[0],xr=[0,2*scale_height],yr=[0,max(rbins)],/nodata,xtitle='|z|',ytitle=textoidl('\rho') +oplot,zbins,rbins,color=blue + +ifile = 0 +nskip = nfiles - 1 +isave = 0 +nplot = 8192 ; randomly plot particles +color = floor(findgen(nfiles)/float(nfiles-1)*255) +;for ifile=0,nfiles-1,nskip do begin +tsave = [0.] +toplot = [1,nfiles-1] +for iplot=0,1 do begin + ifile = toplot[iplot] + inf = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5' + time = h5ra(inf, 'Header','Time') + tsave = [tsave, time] + print,' time= ',time + p = h5rd(inf,'PartType0/Coordinates') + v = h5rd(inf,'PartType0/Velocities') + id = h5rd(inf,'PartType0/ParticleIDs') + rho = h5rd(inf,'PartType0/Density') + h = h5rd(inf,'PartType0/SmoothingLength') + utherm = h5rd(inf,'PartType0/InternalEnergy') + indx = sort(id) + +; substract disk centre + for ic=0,2 do p[ic,*]=p[ic,*] - pcentre[ic] + + +;; ; if you want to sort particles by ID +;; id = id[indx] +;; rho = rho[indx] +;; utherm = utherm[indx] +;; h = h[indx] +;; for ic=0,2 do begin +;; tmp = reform(p[ic,*]) & p[ic,*] = tmp[indx] +;; tmp = reform(v[ic,*]) & v[ic,*] = tmp[indx] +;; endfor + + ip = floor(randomu(ifile+1,nplot)*n_elements(rho)) + color = red + if(ifile eq 1) then begin + color=black + endif else begin + color=red + endelse + oplot,abs(p[2,ip]), rho[ip], psym=3, color=color + + isave = isave + 1 + +endfor + +; time in units of dynamical time +tsave = tsave[1:*] / t_dyn + +label = [''] +for i=0,n_elements(tsave)-1 do label=[label,'time/t_dynamic='+string(tsave[i],format='(f8.0)')] +label = label[1:*] +legend,['analytic',label[0],label[1]],linestyle=[0,0,0],color=[blue,black,red],box=0,/top,/right + +; make histograms of particle velocities +xr = 1d-3 * [-1,1] +bsize = 1.d-5 +ohist,v[0,*]/soundspeed,x,vx,xr[0],xr[1],bsize +ohist,v[1,*]/soundspeed,y,vy,xr[0],xr[1],bsize +ohist,v[2,*]/soundspeed,z,vz,xr[0],xr[1],bsize +wset,2 +plot,x,vx,psym=10,xtitle='velocity/soundspeed',ytitle='pdf',/nodata,xr=xr,/xs +oplot,x,vx,psym=10,color=black +oplot,y,vy,psym=10,color=blue +oplot,z,vz,psym=10,color=red +legend,['vx/c','vy/c','vz/c'],linestyle=[0,0,0],color=[black,blue,red],box=0,/top,/right +end + + diff --git a/examples/Disk-Patch/HydroStatic/getGlass.sh b/examples/Disk-Patch/HydroStatic/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ffd92e88deae6e91237059adac2a6c2067caee46 --- /dev/null +++ b/examples/Disk-Patch/HydroStatic/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5 diff --git a/examples/Disk-Patch/HydroStatic/makeIC.py b/examples/Disk-Patch/HydroStatic/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..7404f76aeaa485037b19052d84636c1656847a2b --- /dev/null +++ b/examples/Disk-Patch/HydroStatic/makeIC.py @@ -0,0 +1,254 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 John A. Regan (john.a.regan@durham.ac.uk) + # Tom Theuns (tom.theuns@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +import sys +import numpy +import math +import random +import matplotlib.pyplot as plt + +# Generates a disk-patch in hydrostatic equilibrium +# see Creasey, Theuns & Bower, 2013, for the equations: +# disk parameters are: surface density sigma +# scale height b +# density: rho(z) = (sigma/2b) sech^2(z/b) +# isothermal velocity dispersion = <v_z^2? = b pi G sigma +# grad potential = 2 pi G sigma tanh(z/b) +# potential = ln(cosh(z/b)) + const +# Dynamical time = sqrt(b / (G sigma)) +# to obtain the 1/ch^2(z/b) profile from a uniform profile (a glass, say, or a uniform random variable), note that, when integrating in z +# \int 0^z dz/ch^2(z) = tanh(z)-tanh(0) = \int_0^x dx = x (where the last integral refers to a uniform density distribution), so that z = atanh(x) +# usage: python makeIC.py 1000 + +# physical constants in cgs +NEWTON_GRAVITY_CGS = 6.672e-8 +SOLAR_MASS_IN_CGS = 1.9885e33 +PARSEC_IN_CGS = 3.0856776e18 +PROTON_MASS_IN_CGS = 1.6726231e24 +YEAR_IN_CGS = 3.154e+7 + +# choice of units +const_unit_length_in_cgs = (PARSEC_IN_CGS) +const_unit_mass_in_cgs = (SOLAR_MASS_IN_CGS) +const_unit_velocity_in_cgs = (1e5) + +print "UnitMass_in_cgs: ", const_unit_mass_in_cgs +print "UnitLength_in_cgs: ", const_unit_length_in_cgs +print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs + + +# parameters of potential +surface_density = 10. +scale_height = 100. +gamma = 5./3. + +# derived units +const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) +const_G = ((NEWTON_GRAVITY_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) +print 'G=', const_G +utherm = math.pi * const_G * surface_density * scale_height / (gamma-1) +v_disp = numpy.sqrt(2 * utherm) +soundspeed = numpy.sqrt(utherm / (gamma * (gamma-1.))) +t_dyn = numpy.sqrt(scale_height / (const_G * surface_density)) +t_cross = scale_height / soundspeed +print 'dynamical time = ',t_dyn,' sound crossing time = ',t_cross,' sound speed= ',soundspeed,' 3D velocity dispersion = ',v_disp,' thermal_energy= ',utherm + + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 400. # [kpc] +Radius = 100. # maximum radius of particles [kpc] +G = const_G + +# File +fileName = "Disk-Patch.hdf5" + +#--------------------------------------------------- +mass = 1 + +#-------------------------------------------------- + + +# using glass ICs +# read glass file and generate gas positions and tile it ntile times in each dimension +ntile = 1 +inglass = 'glassCube_32.hdf5' +infile = h5py.File(inglass, "r") +one_glass_p = infile["/PartType0/Coordinates"][:,:] +one_glass_h = infile["/PartType0/SmoothingLength"][:] + +# scale in [-0.5,0.5]*BoxSize / ntile +one_glass_p[:,:] -= 0.5 +one_glass_p *= boxSize / ntile +one_glass_h *= boxSize / ntile +ndens_glass = (one_glass_h.shape[0]) / (boxSize/ntile)**3 +h_glass = numpy.amin(one_glass_h) * (boxSize/ntile) + +glass_p = [] +glass_h = [] +for ix in range(0,ntile): + for iy in range(0,ntile): + for iz in range(0,ntile): + shift = one_glass_p.copy() + shift[:,0] += (ix-(ntile-1)/2.) * boxSize / ntile + shift[:,1] += (iy-(ntile-1)/2.) * boxSize / ntile + shift[:,2] += (iz-(ntile-1)/2.) * boxSize / ntile + glass_p.append(shift) + glass_h.append(one_glass_h.copy()) + +glass_p = numpy.concatenate(glass_p, axis=0) +glass_h = numpy.concatenate(glass_h, axis=0) + +# random shuffle of glas ICs +numpy.random.seed(12345) +indx = numpy.random.rand(numpy.shape(glass_h)[0]) +indx = numpy.argsort(indx) +glass_p = glass_p[indx, :] +glass_h = glass_h[indx] + +# select numGas of them +numGas = 8192 +pos = glass_p[0:numGas,:] +h = glass_h[0:numGas] +numGas = numpy.shape(pos)[0] + +# compute furthe properties of ICs +column_density = surface_density * numpy.tanh(boxSize/2./scale_height) +enclosed_mass = column_density * boxSize * boxSize +pmass = enclosed_mass / numGas +meanrho = enclosed_mass / boxSize**3 +print 'pmass= ',pmass,' mean(rho) = ', meanrho,' entropy= ', (gamma-1) * utherm / meanrho**(gamma-1) + +# desired density +rho = surface_density / (2. * scale_height) / numpy.cosh(abs(pos[:,2])/scale_height)**2 +u = (1. + 0 * h) * utherm +entropy = (gamma-1) * u / rho**(gamma-1) +mass = 0.*h + pmass +entropy_flag = 0 +vel = 0 + 0 * pos + +# move centre of disk to middle of box +pos[:,:] += boxSize/2 + + +# create numPart dm particles +numPart = 0 + +# Create and write output file + +#File +file = h5py.File(fileName, 'w') + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numGas, numPart, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numGas, numPart, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [entropy_flag] + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + + +# write gas particles +grp0 = file.create_group("/PartType0") + +ds = grp0.create_dataset('Coordinates', (numGas, 3), 'f') +ds[()] = pos + +ds = grp0.create_dataset('Velocities', (numGas, 3), 'f') +ds[()] = vel + +ds = grp0.create_dataset('Masses', (numGas,), 'f') +ds[()] = mass + +ds = grp0.create_dataset('SmoothingLength', (numGas,), 'f') +ds[()] = h + +ds = grp0.create_dataset('InternalEnergy', (numGas,), 'f') +u = numpy.full((numGas, ), utherm) +if (entropy_flag == 1): + ds[()] = entropy +else: + ds[()] = u + +ids = 1 + numpy.linspace(0, numGas, numGas, endpoint=False, dtype='L') +ds = grp0.create_dataset('ParticleIDs', (numGas, ), 'L') +ds[()] = ids + +print "Internal energy:", u[0] + +# generate dark matter particles if needed +if(numPart > 0): + + # set seed for random number + numpy.random.seed(1234) + + grp1 = file.create_group("/PartType1") + + radius = Radius * (numpy.random.rand(N))**(1./3.) + ctheta = -1. + 2 * numpy.random.rand(N) + stheta = numpy.sqrt(1.-ctheta**2) + phi = 2 * math.pi * numpy.random.rand(N) + r = numpy.zeros((numPart, 3)) + + speed = vrot + v = numpy.zeros((numPart, 3)) + omega = speed / radius + period = 2.*math.pi/omega + print 'period = minimum = ',min(period), ' maximum = ',max(period) + + v[:,0] = -omega * r[:,1] + v[:,1] = omega * r[:,0] + + ds = grp1.create_dataset('Coordinates', (numPart, 3), 'd') + ds[()] = r + + ds = grp1.create_dataset('Velocities', (numPart, 3), 'f') + ds[()] = v + v = numpy.zeros(1) + + m = numpy.full((numPart, ),10) + ds = grp1.create_dataset('Masses', (numPart,), 'f') + ds[()] = m + m = numpy.zeros(1) + + ids = 1 + numpy.linspace(0, numPart, numPart, endpoint=False, dtype='L') + ds = grp1.create_dataset('ParticleIDs', (numPart, ), 'L') + ds[()] = ids + + +file.close() + +sys.exit() diff --git a/examples/Disk-Patch/HydroStatic/test.pro b/examples/Disk-Patch/HydroStatic/test.pro new file mode 100644 index 0000000000000000000000000000000000000000..31e027e3a308b04f1c59222e1a339786857061ac --- /dev/null +++ b/examples/Disk-Patch/HydroStatic/test.pro @@ -0,0 +1,142 @@ +; +; test energy / angular momentum conservation of test problem +; + +iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare final and initial energy + +; set physical constants +@physunits + +indir = './' +basefile = 'Disk-Patch_' + +; set properties of potential +uL = phys.pc ; unit of length +uM = phys.msun ; unit of mass +uV = 1d5 ; unit of velocity + +; properties of patch +surface_density = 10. +scale_height = 100. + +; derived units +constG = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ; +pcentre = [0.,0.,200.] * pc / uL + +; +infile = indir + basefile + '*' +spawn,'ls -1 '+infile,res +nfiles = n_elements(res) + + +; choose: calculate change of energy and Lz, comparing first and last +; snapshots for all particles, or do so for a subset + +; compare all +ifile = 0 +inf = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5' +id = h5rd(inf,'PartType0/ParticleIDs') +nfollow = n_elements(id) + +; follow a subset +; nfollow = min(4000, nfollow) ; number of particles to follow + +; +if (iplot eq 1) then begin + nskip = 1 + nsave = nfiles +endif else begin + nskip = nfiles - 2 + nsave = 2 +endelse + +; +lout = fltarr(nfollow, nsave) ; Lz +xout = fltarr(nfollow, nsave) ; x +yout = fltarr(nfollow, nsave) ; y +zout = fltarr(nfollow, nsave) ; z +vzout = fltarr(nfollow, nsave) ; z +rout = fltarr(nfollow, nsave) ; rho +hout = fltarr(nfollow, nsave) ; h +uout = fltarr(nfollow, nsave) ; thermal energy +eout = fltarr(nfollow, nsave) ; energies +ekin = fltarr(nfollow, nsave) +epot = fltarr(nfollow, nsave) ; 2 pi G Sigma b ln(cosh(z/b)) + const +tout = fltarr(nsave) + +ifile = 0 +isave = 0 +for ifile=0,nfiles-1,nskip do begin + inf = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5' + time = h5ra(inf, 'Header','Time') + p = h5rd(inf,'PartType0/Coordinates') + v = h5rd(inf,'PartType0/Velocities') + id = h5rd(inf,'PartType0/ParticleIDs') + rho = h5rd(inf,'PartType0/Density') + h = h5rd(inf,'PartType0/SmoothingLength') + utherm = h5rd(inf,'PartType0/InternalEnergy') + indx = sort(id) + +; if you want to sort particles by ID + id = id[indx] + rho = rho[indx] + utherm = utherm[indx] + h = h[indx] + for ic=0,2 do begin + tmp = reform(p[ic,*]) & p[ic,*] = tmp[indx] + tmp = reform(v[ic,*]) & v[ic,*] = tmp[indx] + endfor + +; calculate energy + dd = size(p,/dimen) & npart = dd[1] + ener = fltarr(npart) + dr = fltarr(npart) & dv = dr + for ic=0,2 do dr[*] = dr[*] + (p[ic,*]-pcentre[ic])^2 + for ic=0,2 do dv[*] = dv[*] + v[ic,*]^2 + xout[*,isave] = p[0,0:nfollow-1]-pcentre[0] + yout[*,isave] = p[1,0:nfollow-1]-pcentre[1] + zout[*,isave] = p[2,0:nfollow-1]-pcentre[2] + vzout[*,isave]= v[2,0:nfollow-1] + rout[*,isave] = rho[0:nfollow-1] + hout[*,isave] = h[0:nfollow-1] + uout[*,isave] = utherm[0:nfollow-1] + Lz = (p[0,*]-pcentre[0]) * v[1,*] - (p[1,*]-pcentre[1]) * v[0,*] + dz = reform(p[2,0:nfollow-1]-pcentre[2]) +; print,'time = ',time,p[0,0],v[0,0],id[0] + ek = 0.5 * dv + ep = fltarr(nfollow) + ep = 2 * !pi * constG * surface_density * scale_height * alog(cosh(abs(dz)/scale_height)) + ener = ek + ep + tout(isave) = time + lout[*,isave] = lz[0:nfollow-1] + eout(*,isave) = ener[0:nfollow-1] + ekin(*,isave) = ek[0:nfollow-1] + epot(*,isave) = ep[0:nfollow-1] + print,format='('' time= '',f7.1,'' E= '',f9.2,'' Lz= '',e9.2)', time,eout[0],lz[0] + isave = isave + 1 + +endfor + +x0 = reform(xout[0,*]) +y0 = reform(xout[1,*]) +z0 = reform(xout[2,*]) + + +; plot density profile and compare to analytic profile +nplot = nfollow + + ; plot density profile +wset,0 +xr = [0, 3*scale_height] +nbins = 100 +zpos = findgen(nbins)/float(nbins-1) * max(xr) +dens = (surface_density/(2.d0*scale_height)) * 1./cosh(zpos/scale_height)^2 +plot,[0],[0],xr=xr,/xs,yr=[0,max(dens)*1.4],/ys,/nodata,xtitle='|z|',ytitle='density' +oplot,zpos,dens,color=black,thick=3 +;oplot,abs(zout[*,1]),rout[*,1],psym=3 ; initial profile +oplot,abs(zout[*,nsave-1]),rout[*,nsave-1],psym=3,color=red + + +end + + diff --git a/examples/IsothermalPotential/GravityOnly/test.pro b/examples/IsothermalPotential/GravityOnly/test.pro index ac1f5e915c38c107408ffed9579c52944295f079..edfa50121d2e5adb7e039f3c38d6d4c0b4d5e34f 100644 --- a/examples/IsothermalPotential/GravityOnly/test.pro +++ b/examples/IsothermalPotential/GravityOnly/test.pro @@ -127,7 +127,7 @@ print,' relative Lz change: (per cent) ',minmax(dl) * 100. if(iplot eq 1) then begin ; plot results on energy conservation for some particles nplot = min(10, nfollow) - wset,0 + win,0 xr = [min(tout), max(tout)] yr = [-2,2]*1d-2 ; in percent plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/nodata,xtitle='time',ytitle='dE/E, dL/L (%)' @@ -137,7 +137,7 @@ if(iplot eq 1) then begin screen_to_png,'e-time.png' ; plot orbits of those particles - wset,2 + win,2 xr = [-100,100] yr = xr plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/iso,/nodata,xtitle='x',ytitle='y' @@ -146,7 +146,7 @@ if(iplot eq 1) then begin screen_to_png,'orbit.png' ; plot radial position of these particles - wset,4 + win,4 xr = [min(tout), max(tout)] yr = [0,80] plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/nodata,xtitle='t',ytitle='r' @@ -155,7 +155,7 @@ for i=0,nplot-1 do begin dr = sqrt(reform(xout[i,*])^2 + reform(yout[i,*])^2) & screen_to_png,'r-time.png' ; make histogram of energy changes at end - wset,6 + win,6 ohist,de,x,y,-0.05,0.05,0.001 plot,x,y,psym=10,xtitle='de (%)' screen_to_png,'de-hist.png' diff --git a/examples/main.c b/examples/main.c index efd40c3da9802ebfab72a51eb841c035c8490eba..c9253ca4dd40ce787b62b0c8ebf89eebbe08cb2c 100644 --- a/examples/main.c +++ b/examples/main.c @@ -332,7 +332,8 @@ int main(int argc, char *argv[]) { /* Initialise the external potential properties */ struct external_potential potential; - if (with_external_gravity) potential_init(params, &us, &potential); + if (with_external_gravity) + potential_init(params, &prog_const, &us, &potential); if (with_external_gravity && myrank == 0) potential_print(&potential); /* Read particles and space information from (GADGET) ICs */ diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index edb0885d621975850a09e4298b8e035ebb45a3cd..0a15f5e1628f70306cf021c4bd05bef699d78214 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -66,15 +66,22 @@ DomainDecomposition: # Point mass external potentials PointMass: - position_x: 50. # location of external point mass in internal units + position_x: 50. # location of external point mass (internal units) position_y: 50. position_z: 50. - mass: 1e10 # mass of external point mass in internal units - timestep_mult: 0.03 # Pre-factor for the time-step condition + mass: 1e10 # mass of external point mass (internal units) + timestep_mult: 0.03 # Dimensionless pre-factor for the time-step condition IsothermalPotential: - position_x: 100. # Location of centre of isothermal potential in internal units + position_x: 100. # Location of centre of isothermal potential (internal units) position_y: 100. position_z: 100. - vrot: 200. # Rotation speed of isothermal potential in internal units - timestep_mult: 0.03 # Pre-factor for the time-step condition + vrot: 200. # Rotation speed of isothermal potential (internal units) + timestep_mult: 0.03 # Dimensionless pre-factor for the time-step condition + +Disk-PatchPotential: + surface_density: 10. # Surface density of the disk (internal units) + scale_height: 100. # Scale height of the disk (internal units) + z_disk: 200. # Disk height (internal units) + timestep_mult: 0.03 # Dimensionless pre-factor for the time-step condition + growth_time: 5. # (Optional) Time for the disk to grow to its final size (multiple of the dynamical time) diff --git a/src/const.h b/src/const.h index 6710fe9e52dfdbede468282d84d4441b923a59fe..cbaa0054fe3bc9f829c59b19b71197264fd0c5af 100644 --- a/src/const.h +++ b/src/const.h @@ -36,6 +36,9 @@ /* Time integration constants. */ #define const_max_u_change 0.1f +/* Thermal energy per unit mass used as a constant for the isothermal EoS */ +#define const_isothermal_internal_energy 20.2615290634f + /* Dimensionality of the problem */ #define HYDRO_DIMENSION_3D //#define HYDRO_DIMENSION_2D @@ -72,6 +75,7 @@ /* External gravity properties */ #define EXTERNAL_POTENTIAL_POINTMASS //#define EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL +//#define EXTERNAL_POTENTIAL_DISK_PATCH /* Are we debugging ? */ //#define SWIFT_DEBUG_CHECKS diff --git a/src/equation_of_state.h b/src/equation_of_state.h index b4a36e8a3ef0bcc281d1f939f89fde08ecf00be9..af59d8a2cad1632c67b6d377b5ed9dfe9484b4aa 100644 --- a/src/equation_of_state.h +++ b/src/equation_of_state.h @@ -132,66 +132,93 @@ gas_soundspeed_from_internal_energy(float density, float u) { /** * @brief Returns the internal energy given density and entropy * - * @param density The density - * @param entropy The entropy + * Since we are using an isothermal EoS, the entropy value is ignored + * Computes \f$u = u_{cst}\f$. + * + * @param density The density \f$\rho\f$. + * @param entropy The entropy \f$S\f$. */ __attribute__((always_inline)) INLINE static float gas_internal_energy_from_entropy(float density, float entropy) { - error("Missing definition !"); - return 0.f; + return const_isothermal_internal_energy; } - /** * @brief Returns the pressure given density and entropy * - * @param density The density - * @param entropy The entropy + * Since we are using an isothermal EoS, the entropy value is ignored + * Computes \f$P = (\gamma - 1)u_{cst}\rho\f$. + * + * @param density The density \f$\rho\f$. + * @param entropy The entropy \f$S\f$. */ __attribute__((always_inline)) INLINE static float gas_pressure_from_entropy( float density, float entropy) { - error("Missing definition !"); - return 0.f; + return hydro_gamma_minus_one * const_isothermal_internal_energy * density; +} + +/** + * @brief Returns the sound speed given density and entropy + * + * Since we are using an isothermal EoS, the entropy value is ignored + * Computes \f$c = \sqrt{u_{cst} \gamma \rho^{\gamma-1}}\f$. + * + * @param density The density \f$\rho\f$. + * @param entropy The entropy \f$S\f$. + */ +__attribute__((always_inline)) INLINE static float gas_soundspeed_from_entropy( + float density, float entropy) { + + return sqrtf(const_isothermal_internal_energy * hydro_gamma * + hydro_gamma_minus_one); } /** * @brief Returns the entropy given density and internal energy * - * @param density The density - * @param u The internal energy + * Since we are using an isothermal EoS, the energy value is ignored + * Computes \f$S = \frac{(\gamma - 1)u_{cst}}{\rho^{\gamma-1}}\f$. + * + * @param density The density \f$\rho\f$ + * @param u The internal energy \f$u\f$ */ __attribute__((always_inline)) INLINE static float gas_entropy_from_internal_energy(float density, float u) { - error("Missing definition !"); - return 0.f; + return hydro_gamma_minus_one * const_isothermal_internal_energy * + pow_minus_gamma_minus_one(density); } /** * @brief Returns the pressure given density and internal energy * - * @param density The density - * @param u The internal energy + * Since we are using an isothermal EoS, the energy value is ignored + * Computes \f$P = (\gamma - 1)u_{cst}\rho\f$. + * + * @param density The density \f$\rho\f$ + * @param u The internal energy \f$u\f$ */ __attribute__((always_inline)) INLINE static float gas_pressure_from_internal_energy(float density, float u) { - error("Missing definition !"); - return 0.f; + return hydro_gamma_minus_one * const_isothermal_internal_energy * density; } /** * @brief Returns the sound speed given density and internal energy * - * @param density The density - * @param u The internal energy + * Since we are using an isothermal EoS, the energy value is ignored + * Computes \f$c = \sqrt{u_{cst} \gamma \rho^{\gamma-1}}\f$. + * + * @param density The density \f$\rho\f$ + * @param u The internal energy \f$u\f$ */ __attribute__((always_inline)) INLINE static float gas_soundspeed_from_internal_energy(float density, float u) { - error("Missing definition !"); - return 0.f; + return sqrtf(const_isothermal_internal_energy * hydro_gamma * + hydro_gamma_minus_one); } /* ------------------------------------------------------------------------- */ diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h index f9b67c96331e7572b0200093f0c32eee5d2391cd..da6b9aa01d7d382b1d6aa85f732f1fae1169ac34 100644 --- a/src/gravity/Default/gravity.h +++ b/src/gravity/Default/gravity.h @@ -48,7 +48,10 @@ gravity_compute_timestep_external(const struct external_potential* potential, dt = fminf(dt, external_gravity_isothermalpotential_timestep(potential, phys_const, gp)); #endif - +#ifdef EXTERNAL_POTENTIAL_DISK_PATCH + dt = fminf(dt, + external_gravity_disk_patch_timestep(potential, phys_const, gp)); +#endif return dt; } @@ -128,12 +131,13 @@ __attribute__((always_inline)) INLINE static void gravity_end_force( * * This function only branches towards the potential chosen by the user. * + * @param time The current time in internal units. * @param potential The properties of the external potential. * @param phys_const The physical constants in internal units. * @param gp The particle to act upon. */ __attribute__((always_inline)) INLINE static void external_gravity( - const struct external_potential* potential, + double time, const struct external_potential* potential, const struct phys_const* const phys_const, struct gpart* gp) { #ifdef EXTERNAL_POTENTIAL_POINTMASS @@ -142,6 +146,9 @@ __attribute__((always_inline)) INLINE static void external_gravity( #ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL external_gravity_isothermalpotential(potential, phys_const, gp); #endif +#ifdef EXTERNAL_POTENTIAL_DISK_PATCH + external_gravity_disk_patch_potential(time, potential, phys_const, gp); +#endif } /** diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 0e4eaf150c764e27156747bb01db20a71f03c7b5..815969975b4f5e6b39099e71bbbec4e43c875ddc 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -56,6 +56,15 @@ void hydro_props_init(struct hydro_props *p, void hydro_props_print(const struct hydro_props *p) { +#if defined(EOS_IDEAL_GAS) + message("Equation of state: Ideal gas."); +#elif defined(EOS_ISOTHERMAL_GAS) + message( + "Equation of state: Isothermal with internal energy " + "per unit mass set to %f.", + const_isothermal_internal_energy); +#endif + message("Adiabatic index gamma: %f.", hydro_gamma); message("Hydrodynamic scheme: %s in %dD.", SPH_IMPLEMENTATION, diff --git a/src/potentials.c b/src/potentials.c index 5cbe05e008b7de17310b1e738e032af90684c25e..dd7aed8712c01921a01462bf9de713433c81f5c1 100644 --- a/src/potentials.c +++ b/src/potentials.c @@ -29,11 +29,13 @@ * of units. * * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units * @param us The current internal system of units * @param potential The external potential properties to initialize */ void potential_init(const struct swift_params* parameter_file, - struct UnitSystem* us, + const struct phys_const* phys_const, + const struct UnitSystem* us, struct external_potential* potential) { #ifdef EXTERNAL_POTENTIAL_POINTMASS @@ -65,6 +67,22 @@ void potential_init(const struct swift_params* parameter_file, parameter_file, "IsothermalPotential:timestep_mult"); #endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */ +#ifdef EXTERNAL_POTENTIAL_DISK_PATCH + potential->disk_patch_potential.surface_density = parser_get_param_double( + parameter_file, "Disk-PatchPotential:surface_density"); + potential->disk_patch_potential.scale_height = parser_get_param_double( + parameter_file, "Disk-PatchPotential:scale_height"); + potential->disk_patch_potential.z_disk = + parser_get_param_double(parameter_file, "Disk-PatchPotential:z_disk"); + potential->disk_patch_potential.timestep_mult = parser_get_param_double( + parameter_file, "Disk-PatchPotential:timestep_mult"); + potential->disk_patch_potential.growth_time = parser_get_opt_param_double( + parameter_file, "Disk-PatchPotential:growth_time", 0.); + potential->disk_patch_potential.dynamical_time = + sqrt(potential->disk_patch_potential.scale_height / + (phys_const->const_newton_G * + potential->disk_patch_potential.surface_density)); +#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */ } /** @@ -78,7 +96,7 @@ void potential_print(const struct external_potential* potential) { message( "Point mass properties are (x,y,z) = (%e, %e, %e), M = %e timestep " - "multiplier = %e", + "multiplier = %e.", potential->point_mass.x, potential->point_mass.y, potential->point_mass.z, potential->point_mass.mass, potential->point_mass.timestep_mult); @@ -88,10 +106,25 @@ void potential_print(const struct external_potential* potential) { message( "Isothermal potential properties are (x,y,z) = (%e, %e, %e), vrot = %e " - "timestep multiplier= %e", + "timestep multiplier = %e.", potential->isothermal_potential.x, potential->isothermal_potential.y, potential->isothermal_potential.z, potential->isothermal_potential.vrot, potential->isothermal_potential.timestep_mult); #endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */ + +#ifdef EXTERNAL_POTENTIAL_DISK_PATCH + + message( + "Disk-patch potential properties are surface_density = %e disk height= " + "%e scale height = %e timestep multiplier = %e.", + potential->disk_patch_potential.surface_density, + potential->disk_patch_potential.z_disk, + potential->disk_patch_potential.scale_height, + potential->disk_patch_potential.timestep_mult); + + if (potential->disk_patch_potential.growth_time > 0.) + message("Disk will grow for %f dynamiccal times.", + potential->disk_patch_potential.growth_time); +#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */ } diff --git a/src/potentials.h b/src/potentials.h index 5373cc1f3f55b0c6d9876cbe6348fdcefbe242aa..f0de5b4dc784be2e613c634bf687300d57f40ecd 100644 --- a/src/potentials.h +++ b/src/potentials.h @@ -25,6 +25,7 @@ #include "../config.h" /* Some standard headers. */ +#include <float.h> #include <math.h> /* Local includes. */ @@ -53,13 +54,123 @@ struct external_potential { float timestep_mult; } isothermal_potential; #endif + +#ifdef EXTERNAL_POTENTIAL_DISK_PATCH + struct { + double surface_density; + double scale_height; + double z_disk; + double dynamical_time; + double growth_time; + double timestep_mult; + } disk_patch_potential; +#endif }; -/* Include external isothermal potential */ +/* ------------------------------------------------------------------------- */ + +/* external potential: disk_patch */ +#ifdef EXTERNAL_POTENTIAL_DISK_PATCH + +/** + * @brief Computes the time-step from the acceleration due to a hydrostatic + * disk. + * + * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 + * + * @param phys_cont The physical constants in internal units. + * @param gp Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_disk_patch_timestep(const struct external_potential* potential, + const struct phys_const* const phys_const, + const struct gpart* const g) { + + /* initilize time step to disk dynamical time */ + const float dt_dyn = potential->disk_patch_potential.dynamical_time; + float dt = dt_dyn; + + /* absolute value of height above disk */ + const float dz = fabs(g->x[2] - potential->disk_patch_potential.z_disk); + + /* vertical cceleration */ + const float z_accel = 2 * M_PI * phys_const->const_newton_G * + potential->disk_patch_potential.surface_density * + tanh(dz / potential->disk_patch_potential.scale_height); + + /* demand that dt * velocity < fraction of scale height of disk */ + float dt1 = FLT_MAX; + if (fabs(g->v_full[2]) > 0) { + dt1 = potential->disk_patch_potential.scale_height / fabs(g->v_full[2]); + if (dt1 < dt) dt = dt1; + } + + /* demand that dt^2 * acceleration < fraction of scale height of disk */ + float dt2 = FLT_MAX; + if (fabs(z_accel) > 0) { + dt2 = potential->disk_patch_potential.scale_height / fabs(z_accel); + if (dt2 < dt * dt) dt = sqrt(dt2); + } + + /* demand that dt^3 jerk < fraction of scale height of disk */ + float dt3 = FLT_MAX; + if (abs(g->v_full[2]) > 0) { + const float dz_accel_over_dt = + 2 * M_PI * phys_const->const_newton_G * + potential->disk_patch_potential.surface_density / + potential->disk_patch_potential.scale_height / + cosh(dz / potential->disk_patch_potential.scale_height) / + cosh(dz / potential->disk_patch_potential.scale_height) * + fabs(g->v_full[2]); + + dt3 = potential->disk_patch_potential.scale_height / fabs(dz_accel_over_dt); + if (dt3 < dt * dt * dt) dt = pow(dt3, 1. / 3.); + } + + return potential->disk_patch_potential.timestep_mult * dt; +} + +/** + * @brief Computes the gravitational acceleration along z due to a hydrostatic + * disk + * + * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 + * + * @param phys_cont The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void +external_gravity_disk_patch_potential( + const double time, const struct external_potential* potential, + const struct phys_const* const phys_const, struct gpart* g) { + + const float G_newton = phys_const->const_newton_G; + const float dz = g->x[2] - potential->disk_patch_potential.z_disk; + const float t_dyn = potential->disk_patch_potential.dynamical_time; + + float reduction_factor = 1.; + if (time < potential->disk_patch_potential.growth_time * t_dyn) + reduction_factor = + time / (potential->disk_patch_potential.growth_time * t_dyn); + + const float z_accel = + reduction_factor * 2 * G_newton * M_PI * + potential->disk_patch_potential.surface_density * + tanh(fabs(dz) / potential->disk_patch_potential.scale_height); + + /* Accelerations. Note that they are multiplied by G later on */ + if (dz > 0) g->a_grav[2] -= z_accel / G_newton; + if (dz < 0) g->a_grav[2] += z_accel / G_newton; +} +#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */ + +/* ------------------------------------------------------------------------- */ + #ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL /** - * @brief Computes the time-step due to the acceleration from a point mass + * @brief Computes the time-step due to the acceleration from an isothermal + * potential. * * @param potential The #external_potential used in the run. * @param phys_const The physical constants in internal units. @@ -73,6 +184,7 @@ external_gravity_isothermalpotential_timestep( const float dx = g->x[0] - potential->isothermal_potential.x; const float dy = g->x[1] - potential->isothermal_potential.y; const float dz = g->x[2] - potential->isothermal_potential.z; + const float rinv2 = 1.f / (dx * dx + dy * dy + dz * dz); const float drdv = dx * (g->v_full[0]) + dy * (g->v_full[1]) + dz * (g->v_full[2]); @@ -92,10 +204,10 @@ external_gravity_isothermalpotential_timestep( } /** - * @brief Computes the gravitational acceleration of a particle due to a point - * mass + * @brief Computes the gravitational acceleration from an isothermal potential. * - * Note that the accelerations are multiplied by Newton's G constant later on. + * Note that the accelerations are multiplied by Newton's G constant + * later on. * * @param potential The #external_potential used in the run. * @param phys_const The physical constants in internal units. @@ -107,7 +219,6 @@ external_gravity_isothermalpotential(const struct external_potential* potential, struct gpart* g) { const float G_newton = phys_const->const_newton_G; - const float dx = g->x[0] - potential->isothermal_potential.x; const float dy = g->x[1] - potential->isothermal_potential.y; const float dz = g->x[2] - potential->isothermal_potential.z; @@ -119,11 +230,12 @@ external_gravity_isothermalpotential(const struct external_potential* potential, g->a_grav[0] += term * dx; g->a_grav[1] += term * dy; g->a_grav[2] += term * dz; - // error(" %f %f %f %f", vrot, rinv2, dx, g->a_grav[0]); } #endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */ +/* ------------------------------------------------------------------------- */ + /* Include exteral pointmass potential */ #ifdef EXTERNAL_POTENTIAL_POINTMASS @@ -161,10 +273,11 @@ external_gravity_pointmass_timestep(const struct external_potential* potential, } /** - * @brief Computes the gravitational acceleration of a particle due to a point - * mass + * @brief Computes the gravitational acceleration of a particle due to a + * point mass * - * Note that the accelerations are multiplied by Newton's G constant later on. + * Note that the accelerations are multiplied by Newton's G constant later + * on. * * @param potential The proerties of the external potential. * @param phys_const The physical constants in internal units. @@ -187,9 +300,12 @@ __attribute__((always_inline)) INLINE static void external_gravity_pointmass( #endif /* EXTERNAL_POTENTIAL_POINTMASS */ +/* ------------------------------------------------------------------------- */ + /* Now, some generic functions, defined in the source file */ void potential_init(const struct swift_params* parameter_file, - struct UnitSystem* us, + const struct phys_const* phys_const, + const struct UnitSystem* us, struct external_potential* potential); void potential_print(const struct external_potential* potential); diff --git a/src/runner.c b/src/runner.c index 23fd820d119230a499a87a447562fc631142655e..eabc622e8c3aa41b5b06c604a4a540b8cf7c13b9 100644 --- a/src/runner.c +++ b/src/runner.c @@ -105,7 +105,7 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) { const int ti_current = r->e->ti_current; const struct external_potential *potential = r->e->external_potential; const struct phys_const *constants = r->e->physical_constants; - + const double time = r->e->time; TIMER_TIC; /* Recurse? */ @@ -128,7 +128,7 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) { /* Is this part within the time step? */ if (g->ti_end <= ti_current) { - external_gravity(potential, constants, g); + external_gravity(time, potential, constants, g); } }