splash/000755 000766 000000 00000000000 13261626264 012765 5ustar00dpricewheel000000 000000 splash/INSTALL000644 000766 000000 00000013422 13261626263 014017 0ustar00dpricewheel000000 000000 To compile SPLASH v2.x you will need simply: - A Fortran 90/95/2003 compiler (e.g. gfortran) - X-Windows - cairo (As SPLASH is command-line driven, it is also assumed you have a basic knowledge of unix). The basic steps for installation are: 1) make sure you have a Fortran 90/95 compiler (such as gfortran). 2) make sure you have cairo on your system 3) compile SPLASH/giza and link with cairo. 4) if desired/necessary write a read_data subroutine so that SPLASH can read your data format. 5) make pretty pictures. As a first attempt, with gfortran as your Fortran compiler, just try "make SYSTEM=gfortran" and this may "just work". If not, read on... For troubleshooting of some common installation problems, have a look at the online FAQ. 1) ---------------- Fortran 9x/2003 compilers --------------------------- By now, many Fortran 90/95 compilers exist. In terms of free ones, both Intel and Sun have non-commercial versions available for Linux. Gfortran is the free GNU compiler, as of version 4.3.0, can be used to compile SPLASH. The latest version can be downloaded from: http://gcc.gnu.org/wiki/GFortran I strongly recommend downloading a more recent version of gfortran rather than relying on any pre-installed version (use gfortran -v to check the version number). In particular versions 4.2.0 and lower *do not* compile SPLASH2. Later versions also have openMP, so you can compile and run SPLASH in parallel ("make PARALLEL=yes") 2) ----------------- The Cairo Graphics Library -------------------- Cairo is a low-level system library used in many applications. Thus it is highly likely that you already have a copy on your system and already in your library path. Look for the header file cairo.h, e.g. using "locate cairo.h" or have a look in the usual places (e.g. /usr/include/cairo, /usr/X11/include). If not, you can usually use your inbuilt package manager to install cairo as follows: Debian/Ubuntu: sudo apt-get install libcairo2-dev Fedora/Red Hat/CentOS: sudo yum install cairo-devel OpenSUSE: zypper install cairo-devel MacPorts: sudo port install cairo Alternatively, use the script provided in the root-level splash directory: ./install-cairo.sh which downloads and installs both pixman and cairo into the giza/ subdirectory. Unlike the methods above, this does not require any admin/superuser permissions. 3) ----------------- Compiling SPLASH with the GIZA backend ---------------- Type: "make SYSTEM=xxx" Where xxx corresponds to a SYSTEM setting in build/Makefile. These are presets for most of the common Fortran compilers, the most useful of which are: gfortran -- settings for the gfortran/gcc compilers g95 -- settings for the g95/gcc compilers nagf95 -- settings for the NAG f95 compiler sunf95 -- settings for the Sun f95 compiler ifort -- settings for the Intel Fortran/C Compilers pgf90 -- settings for the Portland Group Fortran 90 compiler 4) -------------- reading your data format ------------------- The default binaries installed are as follows: splash : alias for asplash asplash : reads ascii formatted data files gsplash : reads data files from GADGET code (http://www.mpa-garching.mpg.de/galform/gadget/) dsplash : reads data files from DRAGON code nsplash : reads data files from NDSPMHD code (http://users.monash.edu.au/~dprice/ndspmhd/) tsplash : reads TIPSY files (ascii and binary) as used in GASOLINE rsplash : reads data files from MAGMA code ssplash : reads data files from sphNG code srsplash : reads data files from SEREN code vsplash : reads data files from VINE code Other formats implemented but not compiled by default include: h5splash : reads h5part formatted files bsplash : old Matthew Bate/Benz code format (see the userguide for a full list) The basic "splash" binary is quite general and will read any ascii or csv data file where columns correspond to different quantities and rows correspond to each particle (actually I use splash to plot graphs for nearly all data in this form, whether SPH or not) -- it will also sensibly skip header lines which do not have the same number of columns. However, it is ultimately desirable to use SPLASH to directly visualise the (binary) output of your code. If your format is not amongst those distributed, then BEFORE you start writing your own routine, please consider whether or not a routine to read your format would be of more general use (e.g. to other users of your code). If so, PLEASE email me to request a new read_data routine for your format, by sending an email attaching: a) an example dump and b) the source code from the routine which wrote the dump file. Then I can write a read for your format that can be added to the SPLASH repository and distributed in all future versions. Whilst I aim never to change the interface to the read_data routines, it is not impossible that some changes may occur somewhere down the line (or enhanced functionality -- for example the more advanced data reads are able to read only the required columns for a given plot from the file, rather than the whole file). If you *really* want to hack one yourself it is best to look at some of the other examples and change the necessary parts to suit your data files. Note that reading directly from unformatted data files is *much* faster than reading from formatted (ascii) data. If you do end up writing your own, again, please email me the end result so I can add it to the officially supported data reads. This also makes it much easier for you to upgrade to newer versions as you do not require a locally customised version. 5) ----- running splash/ making pretty pictures For detailed help on how to use SPLASH, refer to the (quite extensive) userguide on the splash web page. Have fun! And remember, if you get stuck you can always email me... (it doesn't hurt). Daniel Price daniel.price@monash.edu splash/INSTALL.macosx000644 000766 000000 00000006052 13261626263 015311 0ustar00dpricewheel000000 000000 On OS/X, the last stable version of SPLASH is available as a MacPorts package. Get MacPorts from: http://www.macports.org/ Then SPLASH can be installed using: sudo port install splash For troubleshooting of some common installation problems, have a look at the online FAQ. To install splash manually (recommended if you plan on modifying the source code) refer to the general INSTALL instructions. -------------- reading your data format ------------------- The basic "splash" binary is quite general and will read any data where columns correspond to different quantities and rows correspond to each particle (actually I use splash to plot graphs for nearly all data in this form, whether SPH or not) -- it will also sensibly skip header lines which do not have the same number of columns. However, it is ultimately desirable to use SPLASH to directly visualise the (binary) output of your code. If you are using a widely used SPH code (e.g. GADGET, GASOLINE, VINE, DRAGON), it is reasonably likely that I have already written a read data subroutine which will read your dumps. If your format is not amongst those distributed, then BEFORE you start writing your own routine, please consider whether or not a routine to read your format would be of more general use (e.g. to other users of your code). If so, PLEASE email me to request a new read_data routine for your format, by sending an email attaching: a) an example dump and b) the source code from the routine which wrote the dump file. Then I can write a read for your format that can be added to the SPLASH repository and distributed in all future versions. Whilst I aim never to change the interface to the read_data routines, it is not impossible that some changes may occur somewhere down the line (or enhanced functionality -- for example the more advanced data reads are able to read only the required columns for a given plot from the file, rather than the whole file). If you *really* want to hack one yourself it is best to look at some of the other examples and change the necessary parts to suit your data files. Note that reading directly from unformatted data files is *much* faster than reading from formatted (ascii) output. Just to get started you can use the read_data_ascii.f90 which reads from ascii files, but this will not enable the full rendering capabilities until you specify the location of the density, h and particle mass in the arrays (via the parameters ih, irho and ipmass in the set_labels subroutine which is part of the read_data file). If you do end up writing your own, again, please email me the end result so I can add it to the officially supported data reads. This also makes it much easier for you to upgrade to newer versions as you do not require a locally customised version. 5) ----- running splash/ making pretty pictures ----- For detailed help on how to use SPLASH, refer to the (quite extensive) userguide in the /docs directory or on the splash web page. Have fun! And remember, if you get stuck you can always email me... (it doesn't hurt). Daniel Price daniel.price@monash.edu splash/bin/000755 000766 000000 00000000000 13261626263 013534 5ustar00dpricewheel000000 000000 splash/ChangeLog000644 000766 000000 00001702621 13261626264 014550 0ustar00dpricewheel000000 000000 2018-04-06 Daniel Price * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.8.0 2018-04-06 Daniel Price * src/splash.f90: updated splash.f90 for v2.8.0 2018-04-06 Daniel Price * build/Makefile: (build) splash and giza builds are now SEPARATE; can still compile giza locally using make giza, but no longer done by default 2018-04-06 Daniel Price * docs/splash.tex: (docs) minor updates to user guide 2018-04-06 Daniel Price * build/Makefile: (h5part) build issues fixed 2018-04-06 Daniel Price * src/read_data_h5part.f90: (h5part) bug fix with h5part read 2018-03-23 Arnaud Vericel * : commit cd338bbc1967ec6fb5de9eb85a98f43503cb777d Author: Arnaud Vericel Date: Thu Mar 22 12:55:44 2018 +0100 2018-03-07 David Liptai * src/exact.f90: (exact bondi) changed min allowed value of entropy normalisation to zero 2018-03-02 Daniel Price * src/analysis.f90: further bug fix with record length in format string 2018-03-02 Daniel Price * : commit 074acfa764359c232ec3184f7b884a81d4437aac Author: Daniel Price Date: Fri Mar 2 16:00:11 2018 +1100 2018-03-01 David Liptai * src/exact_bondi.f90: (bondi exact) The exact solution for a sonic point flow wind is different than for accretion. Newton-Raphson thus needs different guesses in order to find the appropriate solution. 2018-02-26 David Liptai * src/exact_bondi.f90: didn’t saved fixed merged conflict 2018-02-26 David Liptai * : commit 8bd707af0bd2ad10f0e7646dc052924dea42aa86 Author: David Liptai Date: Mon Feb 26 14:42:43 2018 +1100 2018-02-20 Daniel Price * : commit 8422f1245d8c1b8ce1d05702cd2ef7ac008b0636 Author: Daniel Price Date: Tue Feb 20 14:50:23 2018 +1100 2018-02-20 Hayley Macpherson * : commit 35e2f6fdf173a62eb0d565911089c8e2a65eab40 Author: Hayley Macpherson Date: Tue Feb 20 10:05:44 2018 +0800 2018-02-16 Daniel Price * : commit 2232b1e35bc9d6af424fb6f8f5f9c566f2355c0c Author: JFG Date: Fri Feb 16 03:13:00 2018 +0100 2018-02-15 Daniel Price * src/options_render.f90: (360) colour bar vertical by default in 360 mode (as suggested by Chris Russell) 2018-02-15 Daniel Price * src/options_page.f90: (options_page) bug fix with page size settings not matching menu options (thanks to Chris Russell) 2018-02-13 Daniel Price * src/geometry.f90, src/interpolate3D_proj_geom.F90: (360) bug fix with missing poles in 4pi mode (thanks to Chris Russell) 2018-02-10 Daniel Price * src/splash.f90: updated changelog for v2.8.0 2018-02-10 Daniel Price * src/interpolate3D_proj_geom.F90, src/options_page.f90, src/splash.f90: (360) allow -4pi and -fourpi; use 1080 x 540 by default for interactive use; print walltime 2018-02-10 Daniel Price * src/shapes.f90: (shapes) can plot functions between some min,max 2018-02-05 Daniel Price * : commit 42b29fc98391509449e4f708c49f95e6541f49fa Author: Daniel Price Date: Mon Feb 5 12:40:15 2018 +1100 2017-12-07 Hayley Macpherson * src/read_data_cactus_hdf5_futils.f90: (cactus) better way to check for existence of rho data in find_metric 2017-12-04 Hayley Macpherson * : commit 79571fc26bda81eb3e3c8fd593c6ca7513816f9b Author: Hayley Macpherson Date: Mon Dec 4 12:10:45 2017 +0800 2017-11-30 Daniel Price * src/exact_bondi.f90: (bondi) more sensible if statement in bondi routine 2017-11-30 Daniel Price * src/read_data_ndspmhd.f90: (ndspmhd) distinguish between gas boundary and dust boundary particles in data read 2017-11-30 Daniel Price * src/colours.f90: (colours) added two colour schemes from CASA 2017-11-23 Daniel Price * src/colourbar.f90: (colourbar) bug fix with margins for icolourbar=10 2017-11-23 Daniel Price * : commit 9acedb64e3a26a4487a6e381909d9611adc0fbf5 Author: Daniel Price Date: Thu Nov 23 17:01:19 2017 +1100 2017-11-23 David Liptai * src/interpolate3D_proj_geom.F90, src/plotstep.f90: interpolation in other coordinate systems (e.g. spherical) is now from user specified origin 2017-11-23 David Liptai * src/colours.f90: typo in ocean colour scheme, blue array 2017-11-23 Daniel Price * src/options_page.f90, src/setpage.f90: added axis option for box, major ticks and numbers but no labels 2017-11-23 Daniel Price * src/colourbar.f90: (colourbar) bug fix with horizontal colour bar position when on top of plot 2017-11-23 Daniel Price * : commit 67288423882df4d1303f80d7aa57879b975cfc70 Author: Daniel Price Date: Thu Nov 23 12:22:45 2017 +1100 2017-11-17 David Liptai * src/colours.f90: new colours: ocean 2017-11-13 Daniel Price * scripts/cpfiles.sh: (cpfiles) updated cpfiles script to handle .func and .spirals files 2017-11-13 Daniel Price * src/read_data_sphNG.f90: (sphNG/Phantom) quieter warnings; unused variable warnings fixed 2017-11-13 Daniel Price * src/asciiutils.f90, src/exact.f90, src/exact_planetdisc.f90: (exact_planetdisc) added spiral arm fitting formula; reads parameters from .spirals file 2017-11-09 Daniel Price * src/read_data_cactus_hdf5_futils.f90: (cactus) split find_metric routine off so can be called externally 2017-11-09 Daniel Price * build/Makefile, src/read_data_cactus_hdf5.f90, src/read_data_cactus_hdf5_futils.f90: (cactus) Fortran utility routines moved to new module so can be compiled separate to splash 2017-11-03 Daniel Price * src/read_data_cactus_hdf5.f90: (cactus) added computation of trK as extra column 2017-11-03 Daniel Price * src/read_data_cactus_hdf5.f90, src/read_data_cactus_hdf5_utils.c: (cactus) bug fixes with read of checkpoint files; added CSPLASH_IGNORE_TIME_LEVELS option to ignore different tl grids 2017-11-02 Daniel Price * : commit 644bcab424f3b4a5bf4787823f089d9c9ac4c3f3 Author: David Liptai Date: Thu Nov 2 13:21:23 2017 +1100 2017-10-23 Daniel Price * src/defaults.f90, src/options_page.f90, src/options_render.f90, src/splash.f90: (360 video) added -360 command line option for defaults appropriate to 360 video (with thanks to Chris Russell) 2017-10-20 Daniel Price * src/geometry.f90, src/geomutils.f90, src/interactive.f90, src/interpolate3D_proj_geom.F90, src/particleplot.f90, src/plotstep.f90: (360) various issues with rendering in non-cartesian coordinates fixed (in particular for theta-phi plots) 2017-10-20 Daniel Price * src/discplot.f90: (surface density) set minimum sigma to epsilon(0.), so appears small when logged, rather than zero 2017-10-12 Daniel Price * src/interpolate3D_proj_geom.F90: bug fix in theta-phi rendering 2017-10-11 Daniel Price * src/discplot.f90, src/labels.f90, src/plotstep.f90: column density plots now in g/cm^2 when physical units on 2017-10-11 Daniel Price * src/units.f90: (units) better unit selection; uses error in log space to find nearest appropriate unit 2017-10-05 Kieran Hirsh * src/globaldata.f90: a trailing full stop in fileprefix now ignored 2017-09-22 Daniel Price * src/asciiutils.f90, src/plotstep.f90: bug fix: filenames with underscores now appear correctly in legend 2017-09-22 Daniel Price * src/exact.f90: exact solution from file option prompts in more logical order 2017-09-22 Daniel Price * src/colourbar.f90: BUG FIX: workaround for Xlib errors due to MIT-SHM extensions in cairo; ensure colour bar no bigger than pixel buffer 2017-09-21 Daniel Price * src/interpolation.f90: bug fix: interpolation with no particle masses set works with different particle types 2017-09-21 Daniel Price * src/read_data_cactus_hdf5_utils.c: bug fix with seg faults in cactus data read 2017-09-21 Daniel Price * src/read_data_cactus_hdf5.f90, src/read_data_cactus_hdf5_utils.c: cactus data read works with multiple variables per file 2017-09-07 Daniel Price * src/read_data_sphNG.f90, src/units.f90: (units) sphNG/phantom read automatically chooses closest time unit 2017-08-30 Daniel Price * src/asciiutils.f90: bug fix with label recognition: added sanity checks for sensible labels 2017-08-30 David Liptai * build/Makefile, src/exact.f90, src/exact_bondi.f90: Added exact solution for Bondi flow. Velocity only at present 2017-08-23 Daniel Price * src/interpolate3D_projection.F90: reverted reduction-on-arrays in interpolate3D_projection: causing too many seg faults 2017-08-23 Daniel Price * src/interpolate3D_projection.F90: reverted reduction-on-arrays in interpolate3D_projection: causing too many seg faults 2017-08-22 Daniel Price * : commit 8c228d2861796c2b29b71d0b53ab96b2fe2173fa Author: Daniel Price Date: Tue Aug 22 22:08:42 2017 +1000 2017-08-21 Daniel Price * : Merge pull request #6 from danieljprice/colours new colours: viridis and inferno 2017-08-16 Daniel Price * : commit ada8928504c86e2752b4541c97b186f393c94ab5 Author: Daniel Price Date: Wed Aug 16 12:46:12 2017 +0200 2017-08-16 Conrad Chan * src/read_data_gadget_hdf5.f90: support arepo snapshots 2017-08-14 Daniel Price * src/convert.f90: BUG FIX with splash to ascii and similar for data with multiple steps per file 2017-08-14 Daniel Price * src/read_data_ascii.f90: bug fix with column label read - now recognise particle type column and use it correctly; also read time even if line starts with # 2017-08-13 Daniel Price * src/read_data_cactus_hdf5.f90, src/read_data_cactus_hdf5_utils.c, src/sort.f90: improved cactus hdf5 read: now MUCH faster and puts timesteps in correct order 2017-08-10 Daniel Price * src/interpolate3D_projection.F90: 3D projection faster by factor of 2 (use reduction-on-arrays instead of openMP atomic) 2017-08-10 Daniel Price * src/read_data_cactus_hdf5.f90, src/read_data_cactus_hdf5_utils.c: optimisation of cactus read: do not reopen file every time 2017-08-10 Daniel Price * src/interpolate3D_projection.F90: reduced verboseness on non-interactive devices 2017-08-10 Daniel Price * src/plotstep.f90, src/render.f90, src/setpage.f90: reduced verboseness on non-interactive devices 2017-08-10 Daniel Price * src/read_data_cactus_hdf5.f90, src/read_data_cactus_hdf5_utils.c: further cleanup and optimisation of cactus data read 2017-08-10 Daniel Price * src/get_data.f90, src/globaldata.f90, src/read_data_cactus_hdf5.f90, src/timestepping.f90: bug fix: do not unnecessarily re-read step for data with multiple steps per file 2017-08-10 Daniel Price * scripts/movie.sh: cut unnecessary extra lines in movie.sh 2017-08-10 Daniel Price * src/read_data_cactus_hdf5.f90, src/read_data_cactus_hdf5_utils.c: cactus hdf5 read now working 2017-08-08 Daniel Price * : commit 9072857479fd8954ee77c22d062bc4be6d1b6e2e Author: Daniel Price Date: Tue Aug 8 23:24:56 2017 +1000 2017-08-08 Daniel Price * src/dataread_utils.f90: added utility for counting npartoftype 2017-08-08 Daniel Price * src/asciiutils.f90, src/read_data_ascii.f90: bug fixes with reading column labels: handle NaNs; trim spaces; allow nlabels > ncolumns 2017-08-07 Kieran Hirsh * scripts/movie.sh: movie.sh can now take infile_prefix and outfile_prefix as arguments, still works as before by default 2017-08-04 Daniel Price * src/asciiutils.f90, src/read_data_ascii.f90: bug fix with handling of unformatted files by ascii data read 2017-08-03 Daniel Price * src/shapes.f90: (shapes) added ability to annotate with plot markers; can also change character height 2017-08-03 Daniel Price * src/setpage.f90: bug fix with second y axis when plot limits have equal scales 2017-08-02 Daniel Price * src/asciiutils.f90, src/read_data_ascii.f90: (ascii) ascii data read automagically reads column labels from header lines in ascii files 2017-08-01 Daniel Price * src/colourbar.f90, src/options_render.f90, src/plotstep.f90: added options for colour bar on top or left of figure 2017-07-31 Daniel Price * : commit 42c2b88d724956be0028ecd9eda90e812799d069 Author: Daniel Price Date: Mon Jul 31 09:58:53 2017 +1000 2017-07-25 Mark Andrew Hutchison * : commit 6e40709eab7760a38ca565add2cf7266ebb543d5 Author: Mark Andrew Hutchison Date: Tue Jul 25 21:25:11 2017 +0200 2017-07-25 Daniel Price * src/menu.f90: bug fix causing seg fault in multiplot when in cylindrical coords (thanks to J-F Gonzalez) 2017-07-21 Daniel Price * : commit c88b1cf9c0dd7e5a8151e00f27a58811cf00bbd7 Author: Daniel Price Date: Fri Jul 21 15:12:40 2017 +1000 2017-07-21 Daniel Price * src/menu.f90: save as (SA) also saves .units file 2017-07-19 Mark Andrew Hutchison * src/calc_quantities.f90, src/options_particleplots.f90: (bugfix) reset verbose=.true. after rendering dust phase to allow future edits in extra calc list 2017-07-19 Mark Andrew Hutchison * src/options_particleplots.f90: (bugfix) calc quantities don't disappear when changing the rendered dust phase 2017-06-05 Terrence Tricco * src/menu.f90: Menu now works when no file was read 2017-06-05 Terrence Tricco * src/plotstep.f90: Rendered pixmaps now allow for adaptive limits 2017-05-11 Daniel Price * : commit 2b99f42cc250fa789a94bf0d36b54c3db1ed2942 Author: Daniel Price Date: Thu May 11 17:59:29 2017 +1000 2017-05-09 Mark Hutchison * : commit 239dc00d2d15eb3916d44e125cf7adff36876268 Author: Mark Hutchison Date: Tue May 9 00:14:11 2017 +1000 2017-05-08 Daniel Price * src/asciiutils.f90, src/interpolate2D.f90, src/menu.f90, src/options_particleplots.f90, src/plotstep.f90: implemented smoothing of particle plots (o menu, option 4) 2017-05-08 Daniel Price * src/kernels.f90: kernels functions declared pure 2017-05-08 Mark Hutchison * src/menu.f90: fixed menu to print column numbers correctly when >=100 columns present in file 2017-05-04 Daniel Price * src/splash.f90: year updated 2017-05-03 Daniel Price * src/splash.f90: year updated 2017-05-03 Daniel Price * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.7.0 2017-05-03 Daniel Price * src/splash.f90: updated splash.f90 for v2.7.0 2017-05-03 Daniel Price * : merge 2017-05-03 Daniel Price * src/interpolate3D_proj_geom.F90, src/labels.f90, src/menu.f90, src/plotstep.f90: implemented cross section rendering in r-phi plots; also works for contour plots 2017-05-02 Daniel Price * scripts/time_average_pdfs.f90: obsolete scripts removed 2017-05-02 Daniel Price * scripts/{cpfiles.bash => cpfiles.sh}, scripts/mapletofortran.pl: obsolete scripts removed 2017-05-02 Daniel Price * scripts/fixpgplotnames.bash, scripts/makemovie.sh, scripts/png2theora.sh, scripts/ppm2gif.bash: obsolete scripts removed 2017-04-28 Mark Hutchison * : commit 4d1ba9fe882c2cf22e6a05db17cb4ccaf65f2f3f Author: Mark Hutchison Date: Fri Apr 28 16:29:18 2017 +1000 2017-04-28 Daniel Price * : commit 0df892385cc9a2176fd3e88f4aba5d2f82299581 Author: Daniel Price Date: Fri Apr 28 12:17:36 2017 +1000 2017-04-28 Mark Hutchison * : commit eb55297ca2c953abd0788a14f42cb44b40a7f083 Author: Mark Hutchison Date: Fri Apr 28 11:41:28 2017 +1000 2017-04-27 Daniel Price * : commit 61c4a530c3fd6c16e6223780f11663163b3a331d Author: Daniel Price Date: Thu Apr 27 18:28:44 2017 +1000 2017-04-27 Mark Hutchison * src/exact.f90: (sphNG) for dustyshock, dtg can be read from the .setup file 2017-04-26 Mark Hutchison * src/read_data_sphNG.f90: (sphNG) BUG FIX with creation of one-fluid fake dust data 2017-04-26 Mark Hutchison * src/adjust_data.f90, src/calc_quantities.f90, src/options_particleplots.f90: Solved conflict between fake multigrain particles and calculated extra quantities 2017-04-25 Mark Hutchison * src/adjust_data.f90, src/calc_quantities.f90, src/options_particleplots.f90: Partially resolved conflict between fake multigrain particles and calculated extra quantities 2017-04-24 Mark Hutchison * build/Makefile, src/adjust_data.f90, src/labels.f90, src/options_particleplots.f90: Fake dust particles for ndusttypes>1 now correct 2017-04-20 Mark Hutchison * src/read_data_sphNG.f90: (sphNG) BUG FIX with plotting multiple files with multiple dust phases 2017-04-19 Daniel Price * : commit 4e0f89bdaa3a20939bcdf8a5a98f6488c9df66df Author: Daniel Price Date: Wed Apr 19 10:22:24 2017 +1000 2017-04-12 Mark Hutchison * : commit d8876cbe89cb10d424ece9597548ee1b40ddaeaf Author: Mark Hutchison Date: Tue Apr 11 13:49:39 2017 +1000 2017-04-11 Mark Hutchison * src/read_data_sphNG.f90: sphNG: Removed check of ndusttypes because causing problems when plotting from multiple files 2017-04-07 Mark Hutchison * src/read_data_sphNG.f90: sphNG: now handles onefluid_dust when dust arrays are omitted 2017-04-07 Mark Hutchison * src/read_data_sphNG.f90: sphNG: fixed bug in setting icolumn for dustfracsum 2017-04-07 Mark Hutchison * src/calc_quantities.f90, src/globaldata.f90, src/labels.f90, src/read_data_sphNG.f90: Modified sphNG to handle multiple dust phases 2017-04-07 Mark Hutchison * build/Makefile: Swapped read_data_maddison to read_data_mhutch 2017-03-30 Daniel Price * src/interactive.f90: added help for flip command in interactive mode 2017-03-01 Daniel Price * src/plotstep.f90: bug fix: must read dustfrac and deltav from file if plotting velocities in one-fluid dust 2017-02-17 Daniel Price * src/read_data_tipsy.F90: fixed tipsy format: now works if splash compiled in double precision 2017-02-12 Daniel Price * src/read_data_sphNG.f90: (read_sphNG) recognises deltav in Phantom dump files 2017-02-12 Daniel Price * src/adjust_data.f90: added warning if deltav not found in file for one fluid dust 2017-02-03 Daniel Price * build/Makefile: build failure with exact_planetdisc fixed 2017-02-03 Daniel Price * src/exact_planetdisc.f90: (planetdisc) better rmax for planetdisc exact solution 2017-02-02 Daniel Price * src/shapes.f90: (shapes) BUG FIX with plotting of square shapes 2017-01-19 Daniel Price * src/exact_planetdisc.f90: (exact_planetdisc) added Ogilvie/Lubow exact solution for planet-disc interaction 2017-01-19 Daniel Price * src/exact.f90: (exact) only prompt for type of error norm if compute error norms is true 2017-01-19 Daniel Price * src/fparser.f90: (fparser) changed gamma function to gamf in function parser to avoid conflict with variables named gamma 2017-01-19 Daniel Price * src/geometry.f90: labels for cyl/sph coords use \phi,\theta 2017-01-18 Daniel Price * src/interpolate3D_proj_geom.F90: cleanup of unused variables from previous commit 2017-01-18 Daniel Price * : commit af666311a2b43f8127e0815ba4a70e2c51deaee0 Author: dprice Date: Wed Jan 18 02:07:58 2017 +0000 2017-01-18 Daniel Price * src/geometry.f90, src/interpolate3D_proj_geom.F90: BUG FIX with interpolation of column density in r-phi plane: also MUCH faster 2017-01-16 dprice * src/adjust_data.f90: skip fake two fluid if dust particles are present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5306 cab04810-efc7-4a10-8ecf-f366c833a2ad 2017-01-16 dprice * src/shapes.f90: (shapes) better rectangle plotting; defines sides rather than edge + offset git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5305 cab04810-efc7-4a10-8ecf-f366c833a2ad 2017-01-16 dprice * src/fparser.f90: (fparser) BUG FIX with seg fault in fparser if string length too short git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5304 cab04810-efc7-4a10-8ecf-f366c833a2ad 2017-01-16 dprice * src/read_data_sphNG.f90: (read_sphNG) bug fix with reading fixed binary position/masses from file; also read dustfrac whenever present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5303 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-12-19 dprice * src/adjust_data.f90: error checking added while creating fake dust particles - avoids seg fault git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5302 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-10-19 dprice * src/exact_function.f90, src/fparser.f90: (fparser) added Fortran 2008 intrinsics to function parser (erf, erfc, erfcs, gamma, besj0, besj1, besy0, besy1) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5301 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-10-19 dprice * src/read_data_sphNG.f90: (sphNG) moves dustfrac to same location in small/full dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5300 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-09-25 dprice * src/exact.f90, src/exact_shock.f90: (exact_shock) allow custom setting of initial shock position git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5299 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-09-19 dprice * src/colours.f90: (colours) added blue-black-red colour map with black = 0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5298 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-05-22 dprice * src/convert_grid.f90, src/write_griddata.F90: (splash to grid) splash to gridascii2 writes all grid columns (rho,vx,vy,vz) in the same file (thanks to Seba Perez) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5297 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-04-15 dprice * src/exact.f90: exact solution errors in dustywave computed separately for each particle type git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5296 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-03-30 dprice * src/adjust_data.f90, src/read_data_ndspmhd.f90: added generic fake_twofluids routine independent of the data read; allows fake dust particles from Phantom simulations git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5295 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-03-30 dprice * src/exact.f90: bug fix with read of Kdrag from old-style ndspmhd input files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5294 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-03-23 dprice * src/interpolate2D.f90: (interp2D_pixels) preliminary work on rendering routine for arbitrary 2D data git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5293 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-03-23 dprice * src/options_page.f90, src/plotstep.f90: (options_page) page margins can be adjusted from menu instead of via environment variables; also added page options for MNRAS columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5292 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-03-23 dprice * src/exact.f90: (dustywave) drag coefficient read automatically from phantom input files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5291 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-03-21 dprice * src/colourbar.f90, src/interactive.f90, src/options_render.f90: Hollywood mode implemented: press ctrl-m in interactive mode git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5290 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-02-29 dprice * src/get_data.f90, src/globaldata.f90, src/read_data_UCLA.f90, src/read_data_VINE.f90, src/read_data_aly.f90, src/read_data_amuse_hdf5.f90, src/read_data_ascii.f90, src/read_data_bauswein.f90, src/read_data_dansph_old.f90, src/read_data_dragon.f90, src/read_data_egaburov.f90, src/read_data_falcON_hdf5.f90, src/read_data_flash_hdf5.f90, src/read_data_foulkes.f90, src/read_data_gadget.f90, src/read_data_gadget_hdf5.f90, src/read_data_gadget_jsb.f90, src/read_data_h5part.f90, src/read_data_jjm.f90, src/read_data_jjm_multiphase.f90, src/read_data_jules.f90, src/read_data_kitp.f90, src/read_data_maddison.f90, src/read_data_mbate.f90, src/read_data_mbate_hydro.f90, src/read_data_mbate_mhd.f90, src/read_data_ndspmhd.f90, src/read_data_oilonwater.f90, src/read_data_pbob.f90, src/read_data_rsph.f90, src/read_data_scw.f90, src/read_data_seren.f90, src/read_data_silo.f90, src/read_data_snsph.f90, src/read_data_sphNG.f90, src/read_data_sphysics.f90, src/read_data_spyros.f90, src/read_data_sro.f90, src/read_data_tipsy.F90, src/read_data_urban.f90, src/read_data_vanaverbeke.f90, src/timestepping.f90: (falcON) splash now buffers one step at a time from files with multiple timesteps per file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5289 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-02-29 dprice * src/read_data_ndspmhd.f90: bug fix with labelling of one fluid dust in ndspmhd read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5288 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-02-19 dprice * src/read_data_falcON_hdf5.f90: (falcON) use correct upper/lower case naming for falcON git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5287 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-02-19 dprice * .gitignore: (.gitignore) more files added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5286 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-02-19 dprice * build/Makefile: BUG FIX with c++ library flags for falcON data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5285 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-01-20 dprice * src/exact.f90: added option to normalise errors during exact solution calculation, or not git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5284 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-01-20 dprice * src/plotlib_giza.f90: bug fix with setting page size in cm git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5283 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-01-20 dprice * src/read_data_ndspmhd.f90: recognise del^2 and grad divv in ndspmhd output git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5282 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-01-20 dprice * src/fieldlines.f90: (3D fieldlines) various experimental options commented out git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5281 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-01-20 dprice * docs/splash.tex: docs updated with new calc options + new data reads git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5280 cab04810-efc7-4a10-8ecf-f366c833a2ad 2016-01-20 dprice * src/geometry.f90: added rotated cartesian coordinate system option git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5279 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-11-09 dprice * build/Makefile: BUILD FAILURE fixed with splash build + new giza distro git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5278 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-11-01 dprice * build/Makefile: unnecessary -lX11 removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5277 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-11-01 dprice * build/Makefile: build works with new giza install git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5276 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-11-01 dprice * build/Makefile: added giza/src to build path git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5275 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-22 dprice * docs/version_history, docs/version_history_tex.tex: version 2.6.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5274 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-22 dprice * docs/figs/hyperbolic.ps: deleted .ps file from docs git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5273 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-21 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.6.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5272 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-21 dprice * src/splash.f90: v2.6.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5271 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-21 dprice * src/timestepping.f90: bug fix with axes labelling decisions git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5270 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-20 dprice * src/read_data_ndspmhd.f90: (ndspmhd) label del2v and grad divv correctly in ndspmhd output git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5269 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-20 dprice * src/plotstep.f90: less verbose log output when plotting cross sections git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5268 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-20 dprice * src/menu.f90: do not allow rendering of cross-sections in non-cartesian coords (not implemented) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5267 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-20 dprice * src/read_data_pbob.f90, src/read_data_pbob_utils.c: (pbob) updated pbob data read for new format; reads all time slices git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5266 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-14 dprice * src/discplot.f90, src/plotstep.f90: bug fix with Toomre Q calculation if physical units set git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5265 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-14 dprice * src/read_data_sphNG.f90: BUG FIX with sphNG data read if precision of density array changed; now allows density and h to be any precision in any block if using tagged format (thanks to Ben Lewis) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5264 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-10-14 dprice * build/Makefile: updated X11 libs so compiles out-of-box on Mac git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5263 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-09-10 dprice * src/exact.f90: (exact_shock) automatically reads shock parameters from phantom .setup file if it exists git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5262 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-09-07 dprice * src/read_data_pbob.f90, src/read_data_pbob_utils.c: assume 2D not 3D in pbob read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5261 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-09-07 dprice * build/Makefile, src/read_data_pbob.f90, src/read_data_pbob_utils.c: added PBOB data read for David Brown git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5260 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-09-04 dprice * src/plotstep.f90: debugging statements removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5259 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-09-04 dprice * src/exact.f90, src/plotstep.f90: (exact) residual and error calculation only uses particles actually plotted git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5258 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-08-25 dprice * build/Makefile, src/dataread_utils.f90, src/read_data_falcON_hdf5.f90, src/read_data_falcON_hdf5_utils.cc: implemented falcON hdf5 data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5257 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-08-17 dprice * src/options_powerspec.f90: bug fix with previous commit; use 1/lambda not 2*pi/lambda for default frequency values git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5256 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-08-17 dprice * src/options_powerspec.f90, src/plotstep.f90: powerspectrum takes min, max freq as options; less confusing git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5255 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-08-04 dprice * src/analysis.f90: added splash calc delta option; computes 0.5*(max-min)/mean as function of time git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5254 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-31 dprice * src/read_data_sphNG.f90: BUG FIX with nunknown causing seg fault with mixed Phantom/sphNG data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5253 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-31 dprice * src/options_render.f90: allow up to 100000 pixels in x git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5252 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-23 dprice * src/exact.f90, src/exact_sedov.f90: bug fix: gamma can now be set manually for exact solutions (thanks to Tim Waters) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5251 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-21 dprice * src/plotstep.f90: bug fix with adaptive plot limits (after previous bug fix) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5250 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-21 dprice * src/read_data_sphNG.f90: BUG FIX with bulge particle types in phantom read (thanks to Alex Pettitt) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5249 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-16 dprice * src/read_data_sphNG.f90: BUG FIX with SSPLASH_RESET_CM with tagged data format (thanks to Chris Nixon) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5248 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-16 dprice * src/splash.f90: version info updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5247 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-16 dprice * src/options_page.f90: bug fix: tracking offset limits now saved to splash.defaults (thanks to Chris Nixon for reporting) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5246 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-16 dprice * src/timestepping.f90: bug fix with adaptive plot limits + multiple plots per page git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5245 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-16 dprice * src/plotstep.f90: BUG FIX with adaptive plot limits + multiple steps-per-page (thanks to Chris Nixon) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5244 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-07-16 dprice * build/Makefile: removed obsolete -i_dynamic from SYSTEM=ifort git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5243 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-06-20 dprice * src/read_data_ascii.f90: BUG FIX with out-of-bounds error if particle type read from ascii file (thanks to Carrie Elliott) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5242 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-06-05 dprice * src/calc_quantities.f90: dust-to-gas ratio added to list of pre-calculated quantities git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5241 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-06-04 dprice * src/read_data_sphNG.f90: recognises dustfrac in phantom output git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5240 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-05-27 dprice * src/write_sphdata.f90: splash to ascii preserves precision of data if splash compiled in double precision git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5239 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-05-25 dprice * src/write_data_phantom.f90: bug fix with checking of unequal masses in splash to phantom git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5238 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-05-03 dprice * src/exact.f90: bug fix with read of Kdrag from ndspmhd input file for dustywave exact solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5237 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-05-03 dprice * src/splash.f90: better error checking on command line options git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5236 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-27 dprice * src/plotstep.f90, src/setpage.f90: bug fix with axes drawing decisions if last row but page not complete git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5235 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-27 dprice * src/exact_torus.f90: minor update to torus exact solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5234 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-23 dprice * install-cairo.sh: typo in install-cairo script fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5233 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-23 dprice * src/options_particleplots.f90: bug fix with linestyle prompt for line joining particles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5232 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-20 dprice * src/read_data_ascii.f90: seg fault fixed in ascii read with particle types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5231 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-17 dprice * install-cairo.sh: updated install-cairo script to latest releases; also installs unxz automatically if tar Jxz fails git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5230 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-14 dprice * src/read_data_ascii.f90: ascii data read recognises different particle types if type column is labelled appropriately (thanks to Juan Pablo Farias) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5229 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-04-01 dprice * docs/splash.tex: updated docs on toroidal coordinate transforms git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5228 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-03-31 dprice * src/geometry.f90: (geometry) formatting fixes from Phantom; less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5227 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-03-31 dprice * src/geometry.f90: (geometry) BUG FIX with theta transformation in toroidal coordinates git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5226 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-03-27 dprice * src/read_data_silo_utils.c: added DBClose to read routine for silo data git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5225 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-03-27 dprice * src/geometry.f90: bug fix with labels in toroidal geometry git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5224 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-03-27 dprice * build/Makefile, src/read_data_silo.f90, src/read_data_silo_utils.c: implemented .silo data read (requires SILO libraries) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5223 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-03-24 dprice * src/read_data_gadget_hdf5_utils.c: BUG FIX with gadget-hdf5 reader after first step (thanks to Alex Pettitt) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5222 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-02-06 dprice * src/geomutils.f90: bug fix with dissappearing units labels in cylindrical/spherical coords (thanks to J-F Gonzalez) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5221 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-02-04 dprice * src/exact_shock.f90: bug fix in shock tube solution if density ratio is large (thanks to M. Hutchison) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5220 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.5.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5219 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * src/splash.f90: v2.5.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5218 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * src/globaldata.f90, src/splash.f90: v2.5.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5217 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * build/Makefile: GIZADIR->GIZA_DIR git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5216 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * src/get_data.f90: turn on particle colours automatically if no h/rho read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5215 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * src/menu.f90: allow rendering at all times if using colours not pixels git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5214 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * src/menu.f90: better default options if x,y,z not in first 3 columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5213 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-28 dprice * src/plotstep.f90: bug fix with out-of-bounds error if x,y,z not in first 3 columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5212 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-13 dprice * src/read_data_ndspmhd.f90: better labelling of dust fraction git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5211 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-13 dprice * src/menu.f90: minor bug fix with printout of render limits if rendered previously; also y=x avoided in prompt default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5210 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-08 dprice * src/exact.f90, src/geomutils.f90: verboseness reduced during exact solution plotting and coordinate system changes git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5209 cab04810-efc7-4a10-8ecf-f366c833a2ad 2015-01-06 dprice * src/menu.f90: minor change to comments git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5208 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-12-16 dprice * src/read_data_sphNG.f90: BUG FIX reading particle masses from stars/dark matter in phantom git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5207 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-12-10 dprice * src/read_data_amuse_hdf5.f90: (amuse) amuse-hdf5 read correctly recognises h_smooth git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5206 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-12-10 dprice * src/read_data_sphNG.f90: (ssplash) phantom read handles star/dark matter particle types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5205 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-12-10 dprice * src/read_data_sphNG.f90: bug fix with labelling of HI abundance in Phantom/sphNG read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5204 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-12-10 dprice * src/get_data.f90: added sanity check on size of dat allocation during data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5203 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-12-03 dprice * src/read_data_amuse_hdf5.f90: bug fix with array bounds error in amuse-hdf5 if h not read (thanks to Alessandro Atrani) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5202 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-11 dprice * src/legends.f90: improved line style legend git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5201 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-11 dprice * src/options_page.f90, src/plotlib_giza.f90: allow up to 6 line styles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5200 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-11 dprice * src/convert_grid.f90: splash to grid: range restrictions are now applied git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5199 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-07 dprice * src/plotstep.f90: bug fix with slice settings used in vector plots in cross sections when no h is present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5198 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-07 dprice * src/interpolate_vec.f90, src/plotstep.f90: vector plotting without h now works in 2D and for cross sections git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5197 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-06 dprice * src/interpolate_vec.f90, src/menu.f90, src/plotstep.f90: implemented plotting of vectors when smoothing length not read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5196 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-06 dprice * src/options_page.f90: 5K display size added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5195 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-11-06 dprice * scripts/movie.sh: higher quality movie options in movie.sh by default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5194 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-09-08 dprice * src/defaults.f90: bug fix with formatting overflow in labeltype (thanks to S. Toupin) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5193 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-25 dprice * src/options_particleplots.f90, src/particleplot.f90: added option to change error bar style git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5192 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-25 dprice * src/options_particleplots.f90: bug fix with selection of column for error bars git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5191 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-22 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.5.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5190 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-22 dprice * src/splash.f90: v2.5.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5189 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-22 dprice * src/colourbar.f90, src/options_render.f90: added preset options for floating colour bar (top left/top right/bottom left/bottom right) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5188 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-20 dprice * src/read_data_sphNG.f90, src/splash.f90: sphNG read handles Phantom dumps in tagged format also git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5187 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-08 dprice * src/shapes.f90: added opacity property to shapes; can now adjust each shape opacity separately git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5186 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-08 dprice * src/exact_ringspread.f90: BUG FIX with ring spreading exact solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5185 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-08 dprice * src/read_data_ascii.f90: BUG FIX with incorrect time from first line of ascii files if string too long git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5184 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * src/geometry.f90: compiler warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5183 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * src/analysis.f90: BUG FIX with splash calc: now applies coordinate transformations to data git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5182 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * src/exact_Cshock.f90: spurious comma removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5181 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * src/asciiutils.f90, src/exact.f90: bug fix: option to not apply units to exact solution from file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5180 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * build/Makefile, src/asciiutils.f90, src/read_data_amuse_hdf5.f90, src/read_data_amuse_hdf5_utils.c: added AMUSE HDF5 data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5179 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * src/read_data_gadget.f90: bug fixes with reading gadget files if splash is compiled in double precision git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5178 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * scripts/movie.sh: movie script uses slower frame rate git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5177 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-08-06 dprice * src/splash.f90: updated version info git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5176 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-07-08 dprice * src/read_data_sphNG.f90: complete rewrite of sphNG/Phantom read, added support for tagged data format git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5175 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-07-08 dprice * src/geomutils.f90: use _{} instead of \d in alternative coordinate system labels git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5174 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-07-02 dprice * build/Makefile: splash builds with openMP by default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5173 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-17 dprice * src/labels.f90: bug fix with calculated quantities: now ignores _ and \ when recognising labels git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5172 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-17 dprice * src/calc_quantities.f90: bug fix with plasma beta and T_rad in default calculated quantities list git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5171 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-17 dprice * src/options_page.f90: cave-2 resolution corrected git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5170 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-17 dprice * scripts/movie.sh: added movie.sh to make movies from splash images with ffmpeg git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5169 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-16 dprice * src/interactive.f90: bug fix with possible use of uninitialised variables git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5168 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-16 dprice * src/interactive.f90: seg fault in interactive mode (hopefully) fixed (npts=1 by default) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5167 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-16 dprice * src/read_data_sphNG.f90: BUG FIX with seg fault in sphNG/Phantom read if mhd+sinks git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5166 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-06-05 dprice * src/options_xsecrotate.f90, src/plotstep.f90: screen position adjusts during animation sequence adjustment of z observer position git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5165 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-28 dprice * src/exact_Cshock.f90: updated C-shock, now gives correct solution for Choi test (thanks to J. Wurster) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5164 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-28 dprice * src/particleplot.f90: BUG FIX with fast particle plotting with multiple types if particles on top of each other git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5163 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-28 dprice * src/interpolate3D_projection.F90: unused variable warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5162 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-20 dprice * src/exact_Cshock.f90: updated C-shock solution to plot vx, vy for neutrals git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5161 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-19 dprice * src/exact.f90, src/exact_dustywaves.f90: L2 and L1 errors computed for each solution in case where multiple exact solutions are plotted; also dustywave now standalone routine, does not do plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5160 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-16 dprice * src/exact.f90: computes L1, L2 errors only if 1 exact solution file; minor formatting changes git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5159 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-14 dprice * src/plotstep.f90, src/setpage.f90: bug fix with labelling of x axis if last plot on page and nplots < ndown git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5158 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-12 dprice * src/menu.f90: added instant-multiplot option; can now set up and plot a multiplot by specifying multiple columns on the command line for the y axis git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5157 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-12 dprice * src/menu.f90: removed obsolete labelled format statements git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5156 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-09 dprice * src/exact.f90: can now read and plot exact solutions from multiple files on the same plot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5155 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-09 dprice * src/exact_fromfile.f90: minor improvement to formatting of info line during exact_fromfile plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5154 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-09 dprice * src/calc_quantities.f90: calc quantities less verbose; obsolete function removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5153 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-06 dprice * src/parsetext.f90: bug fix with double minus sign if time is negative in legend git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5152 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-06 dprice * build/Makefile: debugging flags for gfortran does not catch fpes git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5151 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-05-06 dprice * src/interactive.f90, src/limits.f90, src/plotstep.f90: better handling of Infs and NaNs in plot limits git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5150 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-28 dprice * src/menu.f90: obsolete powerspectrum plot option hidden by default (use SPLASH_TURB=yes to get this option) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5149 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-28 dprice * src/read_data_ndspmhd.f90: bug fix with NSPLASH_BARYCENTRIC env. variable in ndspmhd data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5148 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-28 dprice * src/write_sphdata.f90: splash to phantom only writes gas particles but does do something if ntypes > 1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5147 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-28 dprice * src/write_data_phantom.f90: can write phantom mhd dumps using splash to phantom git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5146 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-11 dprice * src/exact_dustywaves.f90: minor cleanup of dustywave solution; avoids too many line continuations; now standards-conforming; also less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5145 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-11 dprice * src/read_data_ndspmhd.f90: less verbose header printing in ndspmhd read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5144 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-02 dprice * src/exact_Cshock.f90: C-shock solution: bug fix with shock width; vs calculated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5143 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-01 dprice * src/read_data_sphNG.f90: BUG FIX with seg fault reading phantom small dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5142 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-01 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.4.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5141 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-01 dprice * src/exact.f90, src/exact_Cshock.f90: added more C-shock solutions git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5140 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-01 dprice * docs/version: version v_2_4_1--01-04-14 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5139 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-04-01 dprice * src/splash.f90: v2.4.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5138 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-03-31 dprice * src/exact_Cshock.f90, src/read_data_ndspmhd.f90: time printing does not overflow git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5137 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-03-31 dprice * src/read_data_sphNG.f90: can read single precision Phantom/sphNG files in double precision splash git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5136 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-03-18 dprice * src/allocate.f90, src/analysis.f90, src/globaldata.f90, src/timestepping.f90: splash calc now gives time=0 in output if time not read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5135 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-03-18 dprice * src/interpolation.f90: BUG FIX with setting weights for dark matter rendering (thanks to Phil Sutton) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5134 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-03-11 dprice * src/splash.f90: updated version info git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5133 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-03-11 dprice * src/adjust_data.f90, src/exact.f90, src/exact_rochelobe.f90, src/partutils.f90, src/plotstep.f90: vastly improved Roche-lobe plotting; ability to track first two sinks git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5132 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-03-11 dprice * src/discplot.f90: number of bins used for surface density/toomre Q plots depends on number of particles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5131 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-25 dprice * src/splash.f90: bumped version number git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5130 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-25 dprice * src/read_data_sphNG.f90: BUG FIX with reading sink particle velocities from phantom data (thanks to F. Dai & S. Facchini) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5129 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-25 dprice * src/fieldlines.f90, src/write_data_gadget.f90: compiler warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5128 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-25 dprice * src/read_data_mbate.f90, src/read_data_oilonwater.f90: broken builds fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5127 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-25 dprice * src/asciiutils.f90: newunit= removed; breaks support for gfortran v4.4 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5126 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-21 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.4.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5125 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-21 dprice * src/splash.f90: version info for 2.4.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5124 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-21 dprice * docs/splash.tex: updated docs with barycentric stuff git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5123 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-21 dprice * src/read_data_ndspmhd.f90: onefluid->fake two fluids done by default; now use NSPLASH_BARYCENTRIC=yes to turn this off git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5122 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-21 dprice * src/asciiutils.f90, src/exact.f90, src/exact_sedov.f90: more robust way of getting line containing Kdrag for dustywave exact solution; also sedov exact solution plots circle again git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5121 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-12 dprice * src/exact.f90, src/exact_Cshock.f90: allow mach numbers to be input parameters to C-shock exact solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5120 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-12 dprice * build/Makefile: exact_Cshock added to build git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5119 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-12 dprice * src/exact.f90, src/exact_Cshock.f90: C-shock exact solution added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5118 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-12 dprice * src/exact.f90, src/exact_shock.f90: added solution for deltav and dustfrac in shock tube solution with no drag git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5117 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-02-11 dprice * src/exact.f90, src/exact_polytrope.f90: polytrope exact solution works with arbitrary mass, polyk git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5116 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-01-24 dprice * src/read_data_sphNG.f90: further bug fix with accreted particles appearing in pngs from phantom dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5115 cab04810-efc7-4a10-8ecf-f366c833a2ad 2014-01-24 dprice * src/labels.f90: bug fix with overflow in print_types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5114 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-12-10 dprice * src/read_data_ascii.f90: bug fix with seg fault if file cannot be opened in ascii read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5113 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-12-03 dprice * src/exact_shock.f90: gamma assumed to be 5/3 if < 1 in shock tube exact solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5112 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-27 dprice * src/read_data_ndspmhd.f90: labelling of deltav fixed -> \Deltav git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5111 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-27 dprice * src/read_data_ndspmhd.f90: bug fix in fake two-fluid stuff with quantities not set to zero git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5110 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-18 dprice * src/convert_grid.f90: bug fix with interface to convert_grid after render sink stuff git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5109 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-14 dprice * src/plotlib_giza.f90: giza interface updated to work with giza v0.8 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5108 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-14 dprice * src/shapes.f90: year updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5107 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-14 dprice * src/splash.f90: version history updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5106 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-14 dprice * src/splash.f90: version updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5105 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-14 dprice * src/options_page.f90: whitespace removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5104 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-14 dprice * src/interpolate3D_opacity.f90, src/interpolation.f90, src/options_xsecrotate.f90, src/plotstep.f90: added option to include sink particles in opacity rendering; hiding them behind dense gas git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5103 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * src/options_page.f90: added CAVE-2 page size option (27000 x 3000); also better defaults for custom page sizes, min is 1x1 in any units git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5102 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * src/legends.f90, src/options_page.f90, src/parsetext.f90: can now use functions in time legend, e.g. Time = %(t + 1000), %(t*100) etc. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5101 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * src/prompting.f90: added warning for strings that will be truncated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5100 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * build/Makefile, src/asciiutils.f90, src/parsetext.f90: added parsetext module to handle parsing of arbitrary functions in text strings git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5099 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * src/read_data_ndspmhd.f90: unused module variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5098 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * src/analysis.f90: compiler warnings fixed with real=0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5097 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * Makefile: added phony targets for all subdirectories git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5096 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-13 dprice * build/Makefile: bug fix with compilation of test modules git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5095 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-12 dprice * build/Makefile, src/read_data_maddison.f90: added data read for Sarah Maddison/Mark Hutchison code git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5094 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-11 dprice * docs/version_history, docs/version_history_tex.tex: version 2.3.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5093 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-11 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.3.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5092 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-11 dprice * src/splash.f90: v2.3.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5091 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-10 dprice * src/particleplot.f90: bug fix with previous commit: only do fast particle plotting if more than 100 particles of a given type git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5090 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-10 dprice * src/particleplot.f90: fast particle plotting used for cross-section + 3D perspective particle plots; further code tidying in particleplot routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5089 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-11-06 dprice * src/interpolation.f90: BUG FIX with out-of-bounds error in splash to grid if multiple particle types but not rendering additional types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5088 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * src/read_data_sphNG.f90: BUG FIX with mapping of phantom types to splash types, fixes problem with identifying boundary particles; also removed obsolete SSPLASH_CENTRE_ON_SINK option git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5087 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * src/sort.f90: sort routine moved to separate module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5086 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * build/Makefile, src/fieldlines.f90, src/interpolate3D_opacity.f90: sort routine moved to separate module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5085 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * src/labels.f90, src/read_data_sphNG.f90: phantom data read handles boundary particles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5084 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * src/particleplot.f90: further variable renames to simplify/shorten particleplot routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5083 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * src/particleplot.f90: further cleanups of particleplot routine; less repeated code git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5082 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * src/particleplot.f90, src/plotstep.f90: compiler warning fixed with unused dummy variables in particleplot routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5081 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-29 dprice * src/particleplot.f90: minor cleanups of particle plotting routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5080 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-23 dprice * src/adjust_data.f90: minor comment removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5079 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-23 dprice * src/adjust_data.f90: SPLASH_COROTATE: rotation of particle velocities implemented, seems to work git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5078 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-23 dprice * src/adjust_data.f90, src/partutils.f90: SPLASH_COROTATE setting implemented; works in conjunction with SPLASH_CENTRE_ON_SINK also git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5077 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-22 dprice * src/read_data_sphNG.f90: bug fix with printing of number of unknown/dead particles; also error->warning for iphase not present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5076 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-22 dprice * src/read_data_sphNG.f90: BUG FIX with dead/accreted particles appearing in plots after partial data read (ssplash) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5075 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-22 dprice * src/adjust_data.f90, src/partutils.f90: cleaned up adjust_data routine to use utility functions for finding sink locations git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5074 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-22 dprice * build/Makefile, src/get_data.f90, src/labels.f90: adjust_data routine moved into separate module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5073 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-14 dprice * src/units.f90: bug fix with unit settings for time prompting to set coordinates also git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5072 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-10-04 dprice * src/get_data.f90, src/read_data_ascii.f90: verboseness of ascii read reduced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5071 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-28 dprice * src/plotstep.f90: pixel map filenames from -o ascii now clearly indicate whether logged or not git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5070 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-28 dprice * src/asciiutils.f90: number of the beast bug: too much use of 666 leading to incorrect ncolumns from pixel maps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5069 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-28 dprice * src/analysis.f90: bug fix with 0/0 in splash calc ratio: now gives 0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5068 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-28 dprice * src/menu.f90: setting up a multiplot only changes nacross, ndown if mod(nacross*ndown,nplot)=0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5067 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-28 dprice * src/plotstep.f90: bug fix with handling of log(0) in -readpix pixel map plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5066 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-28 dprice * src/calc_quantities.f90: plasma \beta (giza) not plasma \gb (pgplot) in example quantities git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5065 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-28 dprice * src/calc_quantities.f90: bug fix with formatting of example calc quantities git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5064 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-23 dprice * src/plotstep.f90: BUG with previous error check fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5063 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-23 dprice * src/plotstep.f90, src/splash.f90: do not continue with splash if X11 device not opened git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5062 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-08 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.3.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5061 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-08 dprice * src/splash.f90: v2.3.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5060 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-08 dprice * src/fieldlines.f90: 3D vec plot routine restores original line width on exit git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5059 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-08-07 dprice * src/kernels.f90: BUG FIX/workaround for gfortran 4.8 issue with pure attribute on procedure pointer git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5058 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-28 dprice * src/fieldlines.f90: 3D field line plotting much improved git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5057 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-25 dprice * src/prompting.f90: prompt prints exponentials with scientific notation 1.e8 instead of 0.1e9 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5056 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-25 dprice * src/colourbar.f90: floating/inset colour bars now same width as usual colour bars git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5055 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-25 dprice * src/particleplot.f90: error bars use translucent shading by default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5054 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/plotstep.f90: hacked label only if icolpixmap=irender git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5053 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/shapes.f90: warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5052 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/plotstep.f90: BUG FIX with z not being read during partial data read for multiplot+rotation git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5051 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/plotstep.f90: hack: use z and label pixmap for Bpol/Bphi plots; bug fix with tiling + pixmap plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5050 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/analysis.f90: minor bug fixes with splash calc ratio git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5049 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/menu.f90: x column is 1 for pixmap plots by default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5048 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/splash.f90: version incremented git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5047 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-21 dprice * src/asciiutils.f90: | deleted from safe filenames git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5046 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-19 dprice * src/geomutils.f90, src/read_data_sphNG.f90: moved integrate_labels routine into labels module; required moving unitslabel and labelzintegration into this module also git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5045 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-19 dprice * src/asciiutils.f90, src/calc_quantities.f90, src/get_data.f90, src/labels.f90, src/options_data.f90, src/plotstep.f90, src/read_data_dragon.f90, src/read_data_seren.f90, src/read_data_sro.f90, src/units.f90, src/write_sphdata.f90: moved integrate_labels routine into labels module; required moving unitslabel and labelzintegration into this module also git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5044 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-19 dprice * src/menu.f90: bug fix with out-of-bounds error for 2D pixmap plots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5043 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-19 dprice * src/calc_quantities.f90, src/labels.f90, src/plotstep.f90, src/write_pixmap.f90: major improvements to read/write of pixel maps (ascii format) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5042 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/globaldata.f90: labels and part_utils modules moved into separate files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5041 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/asciiutils.f90: safename function improved; removes brackets and other stuff git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5040 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * build/Makefile: updated makefile with new files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5039 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/labels.f90, src/partutils.f90: labels and part_utils modules moved into separate files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5038 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/fieldlines.f90: increased length of lines in 3D field line plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5037 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/analysis.f90: added calc ratio option to compute ratio between two or more files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5036 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/get_data.f90: files are allowed to contain more than 64 columns for the purpose of calc/analysis routines git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5035 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/convert.f90: added calc ratio option to compute ratio between two or more files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5034 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/write_pixmap.f90: unused variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5033 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/read_data_ascii.f90: end-of-file error message toned down git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5032 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/asciiutils.f90: allow recognition of up to 5000 chars/1000 reals when determining number of columns in ascii files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5031 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/plotstep.f90: device resolution printout silenced (unless debug mode turned on) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5030 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/write_griddata.F90: gridtest output format now gridascii2; now documented git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5029 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/plotstep.f90: exact solution panel selection uses new ipanelselect routine; less repeated code git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5028 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/write_pixmap.f90: ppm and ascii pixmap output uses tagline from filenames module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5027 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/read_data_sphNG.f90: bug fix with labelling of eta git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5026 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/asciiutils.f90, src/write_pixmap.f90: splash can now read back its own pixmaps (produced via -o ascii) via the -readpix option git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5025 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/get_data.f90: better info in print statement git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5024 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/interactive.f90: bug fix with saving vector plot limits in interactive mode + multiple plots per page git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5023 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/get_data.f90, src/read_data_sphNG.f90: verbosity reduced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5022 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-18 dprice * src/units.f90: formatting in print statement git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5021 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-17 dprice * src/interpolate3D_projection.F90: less verbose printing of interpolation; prints wall time, not cpu time git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5020 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-17 dprice * src/plotstep.f90: less verbose printing of rotation angles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5019 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-17 dprice * src/shapes.f90: shape printing less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5018 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/limits.f90: less verbose read of limits file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5017 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/read_data_sphNG.f90: sphNG+MPI read much less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5016 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/calc_quantities.f90, src/get_data.f90, src/limits.f90: verboseness reduced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5015 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/units.f90: verboseness reduced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5014 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * LICENSE => LICENCE: LICENSE->LICENCE git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5013 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/kernels.f90, src/read_data_sphNG.f90, src/splash.f90: verboseness reduced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5012 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/defaults.f90, src/splash.f90: welcome message less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5011 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/splash.f90: updated version info git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5010 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-14 dprice * src/timing.f90: timing info does not include (=blah s) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5009 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * src/plotstep.f90, src/shapes.f90: text shapes can include time git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5008 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * src/legends.f90, src/options_page.f90, src/plotstep.f90: can adjust number of significant figures in time legend git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5007 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * src/legends.f90, src/plotstep.f90: time legend on top of vector plots drawn with translucent box git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5006 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * src/legends.f90, src/options_page.f90, src/plotstep.f90: added ability to customise position of time and units labels in time legend git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5005 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * build/Makefile, src/read_data_sphysics.f90: preliminaries added for sphysics data read (placeholder routines only at present) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5004 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * src/plotstep.f90: debugging line removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5003 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * build/Makefile, src/legends.f90, src/options_vecplot.f90, src/plotstep.f90, src/render.f90: added ability to select panel for vector plot legend git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5002 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * src/fieldlines.f90, src/rotate.f90: year updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5001 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-06-12 dprice * src/exact_mhdshock_other.f90: obsolete file removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5000 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-13 dprice * src/read_data_jjm.f90: changes to read jjm format from .mve files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4997 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-10 dprice * docs/version_history, docs/version_history_tex.tex: version 2.2.2 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4996 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-10 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.2.2 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4995 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-10 dprice * src/splash.f90: v2.2.2 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4994 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-10 dprice * docs/splash.tex: updated docs for v2.2.2 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4993 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-09 dprice * src/discplot.f90, src/globaldata.f90, src/plotstep.f90, src/read_data_vanaverbeke.f90: Toomre Q plots use actual speed of sound if read from dump file (if ispsound=column containing sound speed) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4992 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-09 dprice * src/exact.f90: unused variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4991 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-09 dprice * src/calc_quantities.f90: unused variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4990 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-01 dprice * src/calc_quantities.f90, src/globaldata.f90, src/read_data_ndspmhd.f90: one fluid stuff uses dust fraction instead of dust-to-gas ratio git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4980 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-01 dprice * src/setpage.f90: bug fix preventing compilation with gfortran 4.6 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4979 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-01 dprice * src/options_xsecrotate.f90: brightness correction NEVER applied for opacity-rendering (until I fix it) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4978 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-05-01 dprice * src/read_data_sphNG.f90: sphNG read a bit less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4977 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-23 dprice * build/Makefile, src/plotstep.f90, src/setpage.f90: bug fix with iaxis=4 (re-scaled y axis) if transformations applied; now works OK git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4974 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-23 dprice * build/Makefile: added extra -lX11 flag to link step; seems to solve issues with XFlush+Ubuntu git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4973 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-23 dprice * src/interactive.f90: preliminary work on interactive switch-to-movie mode (commented out) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4972 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-22 dprice * src/options_page.f90, src/plotstep.f90, src/setpage.f90: added alternative y-axis as option in axes menu git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4968 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-22 dprice * src/setpage.f90: bug fix with length of alt y label git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4967 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-22 dprice * src/setpage.f90: bug fix with alt y axis stuff git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4966 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-22 dprice * src/asciiutils.f90, src/setpage.f90: added ability to plot second y axis to setpage routines git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4965 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-18 dprice * src/plotstep.f90: BUG FIX with overlaid ticks not appearing on first panel pdf/eps/ps plots (thanks to M. Bate) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4964 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-18 dprice * src/read_data_gadget.f90: bug fix with reading gadget initial conditions files; also only looks for Nh, Ne if iFlagCool=1 instead of iFlagCool.ne.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4963 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-04-18 dprice * build/Makefile, src/read_data_aly.f90: added data read for Aly Reheam (splash-aly/make aly) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4962 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-28 bayliffe * src/analysis.f90: Added calc amp to give the amplitude rather than just the peak to peak difference; differs by factor of a half. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4949 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-28 dprice * src/analysis.f90: output from splash calc to screen to many more decimal places git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4948 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-25 dprice * src/read_data_ndspmhd.f90: added fictional dust particle creation for one-fluid dust visualisation (NSPLASH_TWOFLUID=yes) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4941 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-25 dprice * src/exact.f90: bug fix with read of Kdrag from ndspmhd .in file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4940 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-25 dprice * src/calc_quantities.f90, src/globaldata.f90, src/read_data_ndspmhd.f90: pre-calculation of one-fluid dust quantities added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4939 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-06 dprice * src/plotlib_giza.f90, src/plotlib_pgplot.f90: plot_pap interface includes optional units argument git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4911 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-01 dprice * src/analysis.f90: compiler warning (type conversion) fixed in calc diff git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4910 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-01 dprice * src/calc_quantities.f90, src/defaults.f90, src/geomutils.f90, src/globaldata.f90, src/interactive.f90, src/options_data.f90, src/options_limits.f90, src/plotstep.f90: Implemented particle tracking by type; can be saved to splash.defaults git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4909 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-01 dprice * docs/splash.tex: added some more docs about SPLASH_CENTRE_ON_SINK git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4908 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-01 dprice * docs/splash.tex: docs added for calc diff and SPLASH_CENTRE_ON_SINK git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4907 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-01 dprice * src/analysis.f90: minor change to help info git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4906 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-03-01 dprice * src/get_data.f90: SPLASH_CENTRE_ON_SINK environment variable added; works for ALL data reads git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4905 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-26 dprice * src/convert_grid.f90, src/system_utils.f90: SPLASH_TO_GRID env variable can be used to specify particular columns in splash to grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4904 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-26 dprice * src/exact_shock.f90: bug fix with dustyshock + isothermal eos git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4903 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-25 dprice * build/Makefile, src/{read_data_dansph.f90 => read_data_ndspmhd.f90}: renamed read_data_dansph->read_data_ndspmhd git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4902 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-25 dprice * src/read_data_dansph.f90: ndspmhd read handles one-fluid dust arrays (iformat=5) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4901 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-24 dprice * src/read_data_sphNG.f90: bug fix with reading real4s from sphNG files if splash compiled in double precision git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4895 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-22 bayliffe * src/read_data_sphNG.f90: Force single precision read of density from sphNG dumps (as it is stored) even when compiling splash as double precision; otherwise the values returned are junk. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4894 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-20 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.2.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4893 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-20 dprice * src/splash.f90: v2.2.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4892 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-20 dprice * src/globaldata.f90, src/splash.f90: v2.2.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4891 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-07 dprice * src/plotstep.f90: minor fix to comment git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4884 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-07 dprice * src/menu.f90, src/prompting.f90: bug fix if multiplot read from defaults file refers to columns not present: now re-prompts for this git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4883 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-07 dprice * src/read_data_ascii.f90: better recognition of density and pressure columns in ascii read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4882 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-02-07 dprice * src/plotstep.f90: bug fix with axes printing if >1 plot per page, now more transparently handled git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4881 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-01-30 dprice * build/Makefile, src/options_render.f90: bug fix with projections if kernel radius changed via menu git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4880 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-01-30 dprice * src/kernels.f90: added Wendland kernels as options; reverted to usual definition of m5 kernel git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4879 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-01-30 dprice * src/plotstep.f90: bug fix with particle tracking of dark matter particles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4878 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-01-18 dprice * src/plotstep.f90: implemented exact solution plotting on top of surface density/toomre Q/pdf plots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4877 cab04810-efc7-4a10-8ecf-f366c833a2ad 2013-01-18 dprice * src/options_page.f90: added page size option for 4KTV/Ultra HD git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4876 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-12-13 dprice * src/exact.f90: bug fix with exact solutions if time not read from file (now assumes t=0); also can specify shock position in mhd shock solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4875 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-12-13 dprice * src/exact_mhdshock.f90: Brio-Wu solution fixed! git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4874 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-12-02 bayliffe * src/analysis.f90: Added calculation of difference between maximum and minimum of the properties, called using calc diff. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4873 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-11-16 dprice * src/splash.f90: v2.2.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4871 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-11-16 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.2.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4870 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-11-16 dprice * src/splash.f90: v2.2.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4869 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-11-16 dprice * docs/splash.bbl, docs/splash.tex: docs updated for 2.2.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4868 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-11-16 dprice * src/interpolate3D_proj_geom.F90: rendering in non-cartesian coords uses normalised interpolation if 3rd dimension is not a length git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4867 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-11-16 dprice * src/shapes.f90: maxshapes increased to 32 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4866 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-26 dprice * src/plotstep.f90: bug fix with axes being redrawn with multiple steps per page (looks odd in eps) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4852 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-26 dprice * build/Makefile, src/exact.f90, src/exact_gresho.f90: added gresho vortex exact solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4850 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-26 dprice * src/plotstep.f90: titles continue on next page if ntitles > nacross*ndown git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4849 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-26 dprice * src/exact.f90: bug fix with axes colours in residual error plots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4848 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-15 dprice * src/read_data_gadget_hdf5.f90: bug fix: GSPLASH_DARKMATTER_HSOFT now works with gadget hdf5 read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4818 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-15 dprice * src/exact_function.f90: minor changes to comments git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4817 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-15 dprice * build/Makefile, src/geomutils.f90, src/get_data.f90, src/menu.f90, src/options_particleplots.f90, src/splash.f90: automatically turns on plotting of dark matter particles if no gas particles read; dependencies rejigged to enable this (set_coordlabels moved; also does not reread coord labels when resetting coord system) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4816 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-15 dprice * src/read_data_gadget_hdf5.f90, src/read_data_gadget_hdf5_utils.c: bug fix with gadget HDF5 read with dark matter only + no gas git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4815 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-03 dprice * src/geometry.f90, src/interpolate3D_proj_geom.F90, src/plotstep.f90: much faster r-z and non-cartesian rendering git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4814 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-03 dprice * src/menu.f90: bug fix with allowed range of columns in render prompts if extra quantities present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4813 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * src/fparser.f90: commented out cruft removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4812 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * src/write_sphdata.f90: splash to ascii uses origin settings if cyl/sph coords are used git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4811 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * src/calc_quantities.f90, src/geomutils.f90, src/write_sphdata.f90: bug fix with velocities in cylindrical coords + calc quantities if radius is relative to tracked particle git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4810 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * src/geomutils.f90, src/plotstep.f90: changecoords and changeveccoords routines moved to geomutils module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4809 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * src/interpolate3D_projection.F90: integrated kernel table setup does not print anything git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4808 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * src/geomutils.f90: BUG FIX with splash to ascii if coordinate transform set (now works) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4807 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * build/Makefile, src/calc_quantities.f90, src/write_sphdata.f90: BUG FIX with splash to ascii if coordinate transform set (now works) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4806 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-02 dprice * src/discplot.f90: bug fix with weird limit-changing behaviour with a) on surface density/toomre Q plots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4805 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-01 dprice * src/options_page.f90: minor bug with string indexing fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4802 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-01 dprice * docs/splash.tex: footnote fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4801 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-01 dprice * src/options_render.f90: compiler warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4800 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-10-01 dprice * src/kernels.f90: compile error in gfortran 4.7 fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4799 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-30 dprice * build/Makefile: Makefile uses FC, FFLAGS instead of F90C, F90FLAGS; also version number checked for gfortran compatibility git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4797 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-28 dprice * src/plotstep.f90: consistent definition of lastplot done in page_setup; printing of plot limits less verbose for multiple steps-per-page git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4796 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-28 dprice * src/read_data_ascii.f90: ascii data read prints column assignment info only for first file read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4795 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-28 dprice * src/asciiutils.f90: get_columns no longer prints number of columns (less verbosity) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4794 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-28 dprice * src/interactive.f90: line break before interactive help (looks nicer) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4793 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-19 dprice * src/kernels.f90: added kernel function for M5 quartic squashed to 2h git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4792 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-19 dprice * src/options_data.f90: better question about buffered data git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4791 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * src/defaults.f90, src/globaldata.f90, src/menu.f90, src/options_xsecrotate.f90, src/splash.f90: .anim files no longer used, animation sequence options now just saved to splash.defaults; much less confusing git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4782 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * src/shapes.f90: uses print_shapeinfo to pretty-print shapes during plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4781 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * src/splash.f90: updated version info git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4780 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * src/colourbar.f90: compiler warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4779 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * src/interactive.f90: unused variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4778 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * src/options_xsecrotate.f90: animation sequences menu uses new list prompting module: much less confusing git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4777 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * build/Makefile: added new prompt_list module for list-based menus; shapes menu now implemented using this git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4776 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-17 dprice * src/interactive.f90, src/promptlist.f90, src/shapes.f90: added new prompt_list module for list-based menus; shapes menu now implemented using this git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4775 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-14 dprice * src/interactive.f90, src/shapes.f90: better error handling with ctrl-t in interactive mode if hit array limits for text shapes git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4773 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-14 dprice * src/shapes.f90: vastly improved the menu interface for adding/editing shapes/annotation git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4772 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-14 dprice * src/colourbar.f90, src/interactive.f90, src/options_render.f90: implemented floating/inset and customisable colour bar options git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4771 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-14 dprice * src/interactive.f90: interactive mode prints help screen automatically on first call git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4770 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-12 dprice * src/kernels.f90: new kernel module added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4769 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-12 dprice * src/options_render.f90: kernel swapping implemented as menu option in r) menu; also choice saved to splash.defaults, overrides SPLASH_KERNEL setting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4768 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-12 dprice * src/interpolate3D.F90: bug fix with openMP in splash to grid + kernel swapping git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4767 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-12 dprice * src/convert_grid.f90: bug fix with memory allocation in splash to grid if npixels exceeds int4 limit git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4766 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-12 dprice * src/interpolate3D.F90, src/interpolate3D_proj_geom.F90, src/interpolate3D_projection.F90: openMP bugs fixed with kernel swapping stuff git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4765 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-12 dprice * build/Makefile, src/fieldlines.f90, src/interpolate1D.f90, src/interpolate2D.f90, src/interpolate3D.F90, src/interpolate3D_opacity.f90, src/interpolate3D_proj_geom.F90, src/interpolate3D_projection.F90, src/interpolate3D_xsec.f90, src/interpolate_vec.f90, src/splash.f90: kernel swapping implemented (using SPLASH_KERNEL environment variable): applies to ALL interpolation routines git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4764 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-11 dprice * build/Makefile: confusing/obsolete linkerror diagnostic stuff removed from Makefile git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4763 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-10 dprice * build/Makefile: build dependency fixed (thanks to Shazrene Mohamed) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4762 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-09 dprice * build/Makefile, src/write_data_gadget.f90, src/write_sphdata.f90: added splash to gadget option to convert files to basic gadget code format git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4761 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-09 dprice * src/convert_grid.f90, src/interpolate2D.f90, src/plotstep.f90, src/write_griddata.F90: implemented splash to grid in 2D git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4760 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-07 dprice * src/colours.f90: added CMRmap colour scheme: A colormap for effective black and white rendering of colour scale images git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4759 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-06 dprice * src/read_data_jules.f90: updated jules data read to handle his latest format git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4758 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-06 dprice * src/get_data.f90: catches problems with vector labelling in data reads git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4757 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-03 dprice * src/read_data_sphNG.f90: BUG FIX with sink particle read in phantom small dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4751 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-03 dprice * src/get_data.f90: less verbose warning if no gas particles (warns for first file only) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4750 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-09-03 dprice * src/globaldata.f90, src/read_data_sphNG.f90: BUG FIX/regression with int*8 and int*1 definitions in sphNG data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4748 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-31 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.1.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4732 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-31 dprice * src/splash.f90: v2.1.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4731 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-31 dprice * src/globaldata.f90: tagline update git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4730 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-31 dprice * docs/splash.tex: minor issues with docs fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4729 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-31 dprice * : added example shock tube figure to userguide git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4728 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-31 dprice * docs/splash.tex: userguide updated for new version/obsolete references to PGPLOT removed/updated appropriately for GIZA git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4727 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/interactive.f90: more concise/better formatted help for interactive mode git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4726 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/globaldata.f90, src/read_data_sphNG.f90: BUG FIX/regression with definition of int8 kind (sphNG read) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4725 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_sphNG.f90: handles ncolumns > maxplot error cleanly git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4724 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/get_data.f90: ncolumns set to zero if no data read to avoid out-of-bounds issues git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4723 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/calc_quantities.f90, src/exact_ringspread.f90, src/get_data.f90, src/interactive.f90, src/menu.f90, src/options_particleplots.f90, src/plotstep.f90, src/read_data_gadget.f90, src/read_data_seren.f90, src/write_data_phantom.f90: compiler warnings about unused module variables fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4722 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_VINE.f90: unused parameter warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4721 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/plotstep.f90, src/write_pixmap.f90: unused dummy variables removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4720 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/powerspectrums.f90: unused dummy variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4719 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/convert.f90, src/convert_grid.f90, src/write_griddata.F90: unused dummy variable removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4718 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_sro.f90: unused dummy variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4717 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_seren.f90: compiler warnings silenced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4716 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/analysis.f90, src/convert.f90: unused dummy variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4715 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_VINE.f90: compiler warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4714 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_sphNG.f90: compiler warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4713 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/exact_rochelobe.f90: compiler warning silenced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4712 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_gadget.f90, src/write_data_phantom.f90: compiler warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4711 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/exact_shock_sr.f90: compiler warnings (unused dummy variables, real*8 declarations) fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4710 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/read_data_gadget.f90: pedantic warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4709 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * build/Makefile: -pedantic and -Wextra added to debug flags for gfortran git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4708 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/options_limits.f90: unused variables removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4707 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-30 dprice * src/calc_quantities.f90: compiler warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4706 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/options_xsecrotate.f90: ifort remarks silenced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4702 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/analysis.f90, src/read_data_gadget.f90, src/read_data_sphNG.f90, src/read_data_sro.f90, src/read_data_tipsy.F90, src/timestepping.f90: ifort remarks silenced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4701 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/exact_rhoh.f90: ifort compiler remark silenced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4700 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/options_limits.f90, src/options_page.f90: prompts added to adjust plot limits to device aspect ratio when changing paper size and in limits menu git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4699 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/interpolate3D.F90: bug fix/regression with openMP in splash to grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4698 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/options_data.f90: ifort remark silenced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4697 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * build/Makefile: fixed dependency issue during compilation git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4696 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/fieldlines.f90: unused variable warning fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4684 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/calc_quantities.f90, src/get_data.f90, src/globaldata.f90, src/options_particleplots.f90: compilation/dependency issue fixed with iexact git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4683 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-29 dprice * src/convert_grid.f90, src/interpolate3D.F90, src/plotstep.f90, src/powerspectrums.f90, src/system_utils.f90: BUG FIX with wrapping to periodic boundaries in splash to grid; can also now specify periodicity in x,y and z boundaries separately in splash to grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4682 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-28 dprice * src/calc_quantities.f90, src/get_data.f90, src/options_particleplots.f90: BUG FIX with calculated quantities + change of coordinate systems git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4681 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-22 dprice * src/read_data_gadget.f90, src/read_data_gadget_hdf5.f90, src/read_data_gadget_hdf5_utils.c: BUGS FIXED with gadget HDF5+multiple files; seems to work now; better error handling in GADGET reads git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4663 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-22 dprice * src/read_data_gadget_hdf5_utils.c: preprocessing added so that gadget-hdf5 read compiles with both hdf5 1.8 as well as against earlier versions git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4662 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-21 dprice * src/fparser.f90, src/setpage.f90: compiler warnings under gfortran 4.6 fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4661 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-21 dprice * build/Makefile: added DOUBLEPRECISION=yes option to compile splash in double precision (currently only works for certain data reads) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4660 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-21 dprice * src/prompting.f90: minor bug fix in real prompt interface if min/max used git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4659 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-21 dprice * src/prompting.f90: better kind selection for single precision prompt routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4658 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-08-21 dprice * src/prompting.f90, src/timing.f90: fixed issues compiling in double precision git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4657 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-15 dprice * src/legends.f90, src/plotlib_pgplot.f90: vector plot legend uses rounded, semi-transparent rectangle for background (giza only) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4622 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-15 dprice * src/interactive.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90: implemented irregular shaped particle selection on shift-click in interactive mode (pgplot+giza), plus circular selection/marking of particles with middle click (giza backend only) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4621 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-15 dprice * src/interpolate3D_opacity.f90: contact details updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4620 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-15 dprice * src/allocate.f90, src/limits.f90: contact details updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4619 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-13 dprice * install-cairo.sh: install-cairo script works with cairo-1.12.2.tar.xz (xz not gz compression); plus gives correct hint for Darwin library path git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4618 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-13 dprice * src/shapes.f90: arrow shape takes justification parameter so either head or tail can be at specified position git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4617 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-12 dprice * src/read_data_h5part.f90: h5part read now recognises any vector quantity from _0, _1, _2 subscripts git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4616 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-08 dprice * src/read_data_dansph.f90: BUG FIX with itype warning when reading public ndspmhd code output; also ndspmhd read less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4610 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-08 dprice * src/read_data_h5part.f90: h5part read less verbose for second and subsequent files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4609 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-08 dprice * src/read_data_h5part.f90: h5part data read reads types from Phase if MatID is not present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4608 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-08 dprice * src/legends.f90: vector legend uses semi-transparent background git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4607 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-08 dprice * src/read_data_h5part.f90: bug fix with h5part read if smoothing length present in file, also with ndimV setting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4606 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-08 dprice * src/interactive.f90: pressing numbers now sets the timestep jump according to the digits typed (MUCH more sensible); also pressing 0 gives timestep jump of 10, 100 etc (thanks to Terry Tricco for the rather obvious suggestion) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4605 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-06-08 dprice * src/plotstep.f90: BUG FIX with exact solution not appearing on second and subsequent panels if multiplot is used git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4604 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-17 dprice * src/options_page.f90, src/setpage.f90: added axis=3 option to draw box, ticks and numbers but no labels git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4597 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-16 dprice * build/Makefile: fixed install target so that installs gsplash-hdf5, seren read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4596 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-16 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 2.1.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4595 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-16 dprice * src/splash.f90: version 2.1.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4594 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-16 dprice * install-cairo.sh: cairo/pixman versions updated in install-cairo script git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4593 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-14 dprice * src/read_data_gadget_hdf5.f90, src/read_data_gadget_hdf5_utils.c: GADGET hdf5 read now reads and uses particle ID to resort particles/track identities git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4589 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-14 dprice * src/interpolate3D_projection.F90: silenced h<0 warning in interpolate_projection (spurious when mixed particle types) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4588 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-14 dprice * src/read_data_gadget_hdf5.f90, src/read_data_gadget_hdf5_utils.c: implemented GADGET HDF5 data read (works on available test cases) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4587 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-14 dprice * src/read_data_sphNG.f90: phantom+cleaning labelling fixed in sphNG read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4586 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-05-06 dprice * src/get_data.f90: less verboseness git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4560 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-03-13 dprice * src/fieldlines.f90: minor parameter adjustments to fieldline rendering git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4547 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-03-13 dprice * src/options_xsecrotate.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/plotstep.f90, src/render.f90: plot_imag_alpha interfaces added; ppm stuff no longer used with opacity rendering (but new stuff not quite working yet) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4546 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-03-07 dprice * src/splash.f90: date updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4545 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-03-07 dprice * src/exact.f90, src/exact_fromfile.f90, src/plotstep.f90: exact solution from file can have arbitrary number of columns (prompts if ncols>2); can have one exact file per dump git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4544 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-03-07 dprice * src/read_data_seren.f90: bug fix with seren data read labelling of vector quantities in different coordinate systems git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4543 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-03-06 dprice * src/colours.f90: few more colour schemes added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4542 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-02-15 dprice * src/options_limits.f90: adjust limits to device OFF by default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4541 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-01-27 dprice * src/calc_quantities.f90: bug fix with newly added calculated quantities being skipped git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4540 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-01-26 dprice * src/calc_quantities.f90: BUG FIX with calculated quantities and alternative coordinate systems (thanks to Farzana Meru); also with printed output of nused git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4539 cab04810-efc7-4a10-8ecf-f366c833a2ad 2012-01-26 dprice * src/calc_quantities.f90: minor bug fix with identification of calc quantities/debug mode git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4538 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-20 dprice * src/read_data_sphNG.f90: added units for resistivity parameters to sphNG read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4537 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-19 dprice * src/plotstep.f90: minor debugging stuff added; bug partially fixed with no axes on multiplots + multiple steps per page git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4536 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-19 dprice * src/get_data.f90: endian information now only printed if BIG endian is found git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4535 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-19 dprice * src/legends.f90: header updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4534 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-19 dprice * src/read_data_sphNG.f90: fixed labelling for eta/psi/extra stuff in MHD dumps with sphNG git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4533 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-18 dprice * src/colours.f90: added Terrys Alice WBYR colour scheme git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4532 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-14 dprice * INSTALL: updated installation instructions to use install-cairo script + package info git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4530 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-14 dprice * install-cairo.sh: added script to install cairo and pixman if they are not already present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4529 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-13 dprice * src/fieldlines.f90: 3D field line plotting added; now default if streamlines are set and 3D projection plot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4528 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-13 dprice * src/plotstep.f90: bug fix with weights not being calculated for vector plots not drawn on top of another render plot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4527 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-13 dprice * src/interactive.f90: bug fix with effect of u,U,d,D in interactive mode for 3D perspective git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4526 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-09 dprice * src/fieldlines.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/plotstep.f90: new 3D field line plotting implemented (not yet live) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4525 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-08 dprice * src/convert.f90: bug fix with interface to analysis not having been committed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4524 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-08 dprice * src/analysis.f90: analysis does max/min/mean only for types being plotted; also calculates total ang mom for energies git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4523 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-12-06 dprice * src/interactive.f90: bug fix with vector limits not being saved in interactive mode on multiplots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4522 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-16 dprice * src/prompting.f90: minor fix to prompting module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4517 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-16 dprice * src/read_data_sphNG.f90: env variable for reading dust added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4516 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-14 dprice * src/plotlib_giza.f90: bug fix in giza interface git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4512 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-14 dprice * src/colourbar.f90, src/colours.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/render.f90: inverse greyscale colour bars now working in giza; also use new extend arguments to giza_render to determine padding of image (used for colour bars, not for main image) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4511 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-11 dprice * src/plotlib_giza.f90: added support for inverse colour tables with giza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4510 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-09 dprice * src/read_data_sphNG.f90: sphNG read less verbose git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4509 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-09 dprice * src/analysis.f90: ekiny/kh analysis added (undocumented) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4508 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-11-03 dprice * src/read_data_sphNG.f90: sphNG read gets curl v from .divv file if present for phantom dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4507 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-21 dprice * src/plotstep.f90: smarter plot tiling: can still tile plots with different colour bars as long as all colour bars in the row are the same git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4486 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-20 dprice * src/calc_quantities.f90: bug fix with dependency calculations in calculated quantities/partial data reads git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4485 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-19 dprice * src/calc_quantities.f90, src/get_data.f90, src/plotstep.f90: optimisation with calculated quantities: now only reads required quantities from file and only computes those actually being used git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4482 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-19 dprice * src/options_page.f90: bug fix with saving alphalegend to splash.defaults git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4481 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-19 dprice * src/read_data_ascii.f90: less verbose output git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4480 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-19 dprice * src/get_data.f90, src/globaldata.f90, src/options_data.f90, src/read_data_ascii.f90, src/units.f90: verboseness of splash calc reduced git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4479 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-19 dprice * src/analysis.f90: vrms/rhomach analyses hidden from documentation (as not recommended for general use) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4478 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-19 dprice * src/calc_quantities.f90: calculated quantities output less verbose when skipped git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4477 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-11 dprice * build/Makefile: make clean deletes bin/splash bin/ssplash etc git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4472 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-11 dprice * src/read_data_gadget.f90: minor changes to output in gadget read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4471 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-11 dprice * bin/.keep: added .keep to bin directory so it appears in svn git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4470 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-10-10 dprice * src/exact.f90, src/plotstep.f90: added option to plot exact solution only on selected panels; also bug fix with fixed npix + auto-adjusted limits git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4469 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-21 dprice * src/plotstep.f90: bug fix with non-cartesian rendering and auto-adjusted plot limits (also r-z gives square plot by default) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4465 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-15 dprice * src/interactive.f90: bug fix with uninitialised variable in getpanel git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4451 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-14 dprice * src/interactive.f90: bug fix with uninitialised variable git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4446 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-09 dprice * src/plotstep.f90: minor bug fix with round-off error in automatic pixel selection git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4442 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-09 dprice * src/options_limits.f90: auto-adjust is set by default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4441 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-09 dprice * src/options_limits.f90, src/options_page.f90, src/plotstep.f90, src/setpage.f90: added option to automatically adjust the plot limits to match the device aspect ratio git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4440 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-09 dprice * README: README file updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4439 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-09 dprice * src/options_page.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/plotstep.f90: page sizes can now be specified in pixels (giza only) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4438 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-09-09 dprice * src/plotlib_giza.f90: updated interface to giza_set_paper_size git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4437 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * src/setpage.f90: whitespace bug fixed in tiled non-square plots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4431 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * docs/version_history_tex.tex: version 2.0-beta git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4430 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * docs/version: version 2.0-beta git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4429 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * src/interactive.f90: bug fix with + in interactive mode for double rendering git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4428 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * build/Makefile: bug fixes with giza build with ifort/icc git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4427 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * build/Makefile: default backend now giza (for v2.0) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4426 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * INSTALL, INSTALL.macosx, INSTALLv1.x: updated install instructions for 2.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4425 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * src/splash.f90: more pgplot refs removed; version is 2.0beta git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4424 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * src/exact.f90, src/options_particleplots.f90, src/setpage.f90, src/splash.f90: PGPLOT references removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4423 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * build/Makefile: -Wall only done if debug flags set in SYSTEM=gfortran git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4412 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.15.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4411 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-29 dprice * docs/splash.tex: docs updated for 1.15.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4410 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-28 dprice * src/splash.f90: v1.15.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4409 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-28 dprice * src/exact.f90, src/options_page.f90, src/options_particleplots.f90, src/particleplot.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/shapes.f90, src/timestepping.f90: added plotlib_maxlinestyle, plotlib_maxlinecolour and plotlib_maxfillstyle; these are used where appropriate instead of hardwired values git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4408 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-26 dprice * build/Makefile: c compiler and flags passed to giza makefile git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4407 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-26 dprice * build/Makefile: added build target (libgiza) to just build giza, no splash git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4406 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-26 dprice * build/.depends, build/Makefile: .depends moved to separate file; other cleanups in Makefile git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4405 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-26 dprice * build/Makefile: removed unnecessary include stuff now we are using libgiza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4404 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-26 dprice * build/Makefile: new build process for giza; uses libgiza instead of direct use of object files; uses recursive make to build giza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4403 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-25 dprice * src/interactive.f90, src/plotstep.f90: bug fix with interactive mode on double-rendered multiplots; also better panel determination in colour bar click git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4402 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-25 dprice * src/plotstep.f90: bug fix with seg fault on double-rendering in multiplots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4401 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-25 dprice * src/particleplot.f90: line plotting for particle type 2 added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4400 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-25 dprice * src/interactive.f90, src/plotstep.f90: preliminaries for interactive setting of double-rendered colour bar on multiplots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4399 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-25 dprice * src/interactive.f90, src/plotstep.f90: interactive limits setting on double-rendered colour bar fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4398 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-24 dprice * src/exact_dustywaves.f90: bug fix in dustywave solution if density .ne. unity git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4397 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-24 dprice * src/exact.f90, src/exact_shock.f90: dustyshock solution added (modification of shock); also dustywave reads Kdrag from ndspmhd input file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4396 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-23 dprice * src/read_data_sphNG.f90: sphNG read handles batcode format git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4395 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-18 dprice * scripts/splash_parallel.pl: minor updates to splash_parallel from sun grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4394 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-17 dprice * src/system_utils.f90: valgrind error silenced in renvironment (does not attempt to read blank string) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4393 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-17 dprice * src/get_data.f90: bug fix in get_data if no data read (does not access ix if it is not set) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4392 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-15 dprice * src/allocate.f90, src/analysis.f90, src/calc_quantities.f90, src/colourbar.f90, src/colourparts.f90, src/colours.f90, src/convert.f90, src/convert_grid.f90, src/cubicsolve.f90, src/defaults.f90, src/discplot.f90, src/exact.f90, src/exact_densityprofiles.f90, src/exact_dustywaves.f90, src/exact_fromfile.f90, src/exact_function.f90, src/exact_mhdshock.f90, src/exact_mhdshock_other.f90, src/exact_polytrope.f90, src/exact_rhoh.f90, src/exact_ringspread.f90, src/exact_rochelobe.f90, src/exact_sedov.f90, src/exact_shock.f90, src/exact_shock_sr.f90, src/exact_torus.f90, src/exact_toystar1D.f90, src/exact_toystar2D.f90, src/exact_wave.f90, src/fieldlines.f90, src/fparser.f90, src/geometry.f90, src/get_data.f90, src/globaldata.f90, src/interactive.f90, src/interpolate1D.f90, src/interpolate2D.f90, src/interpolate3D_opacity.f90, src/interpolate3D_xsec.f90, src/interpolate_vec.f90, src/interpolation.f90, src/legends.f90, src/limits.f90, src/menu.f90, src/options_data.f90, src/options_limits.f90, src/options_page.f90, src/options_particleplots.f90, src/options_powerspec.f90, src/options_render.f90, src/options_vecplot.f90, src/options_xsecrotate.f90, src/pagecolours.f90, src/particleplot.f90, src/pdfs.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/plotstep.f90, src/plotutils.f90, src/powerspectrums.f90, src/prompting.f90, src/read_data_UCLA.f90, src/read_data_VINE.f90, src/read_data_ascii.f90, src/read_data_bauswein.f90, src/read_data_dansph.f90, src/read_data_dansph_old.f90, src/read_data_dragon.f90, src/read_data_egaburov.f90, src/read_data_flash_hdf5.f90, src/read_data_foulkes.f90, src/read_data_gadget.f90, src/read_data_gadget_hdf5.f90, src/read_data_gadget_jsb.f90, src/read_data_h5part.f90, src/read_data_jjm.f90, src/read_data_jjm_multiphase.f90, src/read_data_jules.f90, src/read_data_kitp.f90, src/read_data_mbate.f90, src/read_data_mbate_hydro.f90, src/read_data_mbate_mhd.f90, src/read_data_oilonwater.f90, src/read_data_rsph.f90, src/read_data_scw.f90, src/read_data_seren.f90, src/read_data_snsph.f90, src/read_data_sphNG.f90, src/read_data_spyros.f90, src/read_data_sro.f90, src/read_data_urban.f90, src/read_data_vanaverbeke.f90, src/render.f90, src/rotate.f90, src/setpage.f90, src/shapes.f90, src/splash.f90, src/system_f2003.f90, src/system_unix.f90, src/system_unix_NAG.f90, src/system_utils.f90, src/timestepping.f90, src/timing.f90, src/titles.f90, src/transform.f90, src/units.f90, src/write_data_phantom.f90, src/write_pixmap.f90, src/write_sphdata.f90: spurious whitespace at end of lines removed (script) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4390 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-15 dprice * src/read_data_sphNG.f90: less verbose output git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4389 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-15 dprice * build/Makefile: CC set to gcc only if not already set git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4388 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-15 dprice * src/options_particleplots.f90, src/particleplot.f90: implemented outlined solid marker types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4387 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-15 dprice * build/Makefile, src/contours.f90, src/options_render.f90, src/render.f90: implemented manual level/label setting for contour plots (thanks to Andy McLeod) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4386 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-15 dprice * src/asciiutils.f90: BUG FIX in read_asciifile_real; affects only splash calc massaboverho; added additional routine for contouring git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4385 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-11 dprice * src/get_data.f90, src/read_data_dansph.f90: reads multiple types from ndspmhd; bug fix with check_data_read and multiple types if get_label not called in read_data routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4382 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-05 dprice * build/Makefile: better giza compilation with icc git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@4368 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-04 dprice * src/menu.f90: allow rendering in alternative coordinate systems git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1675 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-04 dprice * src/interpolate3D_proj_geom.F90, src/plotstep.f90: minor tweaks to rendering in alternative coord systems git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1674 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-04 dprice * src/interpolate3D_proj_geom.F90, src/plotstep.f90: minor fixes to interp3D_geom, trying to get r-z to work git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1673 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-04 dprice * build/Makefile, src/interactive.f90, src/particleplot.f90: circles of interaction done in other coordinate systems in interactive mode git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1672 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-04 dprice * build/Makefile, src/geometry.f90, src/interpolate3D_proj_geom.F90, src/plotstep.f90: implemented rendering in alternative coordinate systems (works but slow!) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1671 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-02 dprice * src/geometry.f90: added parameter variables for each coordinate system to geometry module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1665 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-02 dprice * src/plotstep.f90, src/setpage.f90: interface to 2D interpolation fixed; plus redraw_axes now done with giza but only if rendering git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1664 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-02 dprice * src/interpolate2D.f90: 2D interpolation works with non-square pixels git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1663 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-02 dprice * src/read_data_sphNG.f90: better warning about lowmem+multiple types for phantom read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1662 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-02 dprice * src/analysis.f90: bug fix with momentum calculation in splash calc energies git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1661 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-02 dprice * src/read_data_sphNG.f90: bug fix causing seg fault in phantom+dust+low memory mode git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1660 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-08-02 dprice * src/globaldata.f90: BUG FIX with igettype routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1659 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/discplot.f90, src/globaldata.f90, src/interactive.f90, src/plotstep.f90: bug fix with interactive particle queries: now ignores particle types not plotted git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1653 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/read_data_sphNG.f90: bug fix with accreted particles + dust in phantom read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1652 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/discplot.f90, src/plotstep.f90: surface density/toomre Q plots respect particle types turned on/off git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1651 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolate3D.F90: use shared instead of firstprivate for hmin git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1650 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolate3D.F90: parallel interpolate3D_vec routine: so all 3D grid interpolations can be done in parallel git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1649 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * build/Makefile: bug fix with dependencies in Makefile git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1648 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolate3D_projection.F90: compiler warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1647 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolate3D.F90: parallelised interpolation to 3D grid (splash to grid) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1646 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/{interpolate3D.f90 => interpolate3D.F90}: .f90->.F90 for parallel 3D interpolation git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1645 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/get_data.f90: bug fix with ifort compilation git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1644 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolate3D.f90, src/interpolate3D_opacity.f90, src/interpolate3D_projection.F90: minor formatting of timings/header git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1643 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolate3D_projection.F90: timing is formatted better; with openmp clearly indicates cpu-s not s git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1642 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * build/Makefile, src/get_data.f90, src/timing.f90: timing module added; data read timing now walltime not cpu time git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1641 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolation.f90: bug fix in openmp git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1640 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interpolation.f90: parallel set_interpolation_weights git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1639 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-27 dprice * src/interactive.f90, src/plotstep.f90: bug fix with a in interactive mode: now adapts only to types being plotted git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1638 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-26 dprice * src/plotstep.f90: bug fix with adaptive limits + mixed particle types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1637 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-26 dprice * src/particleplot.f90: minor amendment to header git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1636 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-15 dprice * src/read_data_sphNG.f90: labels fixed on H2 chemistry small dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1597 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-14 dprice * src/read_data_sphNG.f90: labelling of H2 chemistry more robust (works with mhd) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1596 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-14 dprice * src/units.f90: bug fix/more robust write/read of units files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1595 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-12 dprice * src/read_data_sphNG.f90: fix for corrupt sphNG dump files (now checks that RT=on in file header - assumes block 3 not present if not) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1588 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-11 dprice * src/plotstep.f90: colour bar plotted as expected (for 2nd quantity) if double rendering is used git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1587 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-11 dprice * src/render.f90: minor formatting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1586 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-11 dprice * src/colourbar.f90: uses plot_gray if icolours=1 for colour bar git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1585 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-11 dprice * src/read_data_sphNG.f90: labelling of H2 chemistry stuff implemented (for Phantom) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1583 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-07-01 dprice * src/plotlib_pgplot.f90: bug fix with plotlib_pgplot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1552 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-30 dprice * src/discplot.f90, src/plotstep.f90: bug fix (for Ben): surface density and Toomre Q plots now use only particles turned on git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1551 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-30 dprice * src/plotlib_giza.f90: translates unit 3 to giza_units_pixels not giza_units_device git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1550 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-30 dprice * src/menu.f90, src/options_render.f90, src/plotstep.f90, src/splash.f90: implemented menu options for double rendering git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1549 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-30 dprice * src/colourbar.f90, src/options_page.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/plotstep.f90, src/setpage.f90: plotlib_is_pgplot etc are parameters not query functions git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1548 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-30 dprice * src/options_render.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/plotstep.f90, src/render.f90: double rendering implemented; seems to work git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1547 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-29 dprice * src/convert_grid.f90: safer default grid size for splash to grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1546 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-27 dprice * src/exact_rochelobe.f90: bug fix with ifort compilation git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1545 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-23 dprice * src/interpolate3D_projection.F90, src/interpolate3D_xsec.f90, src/plotstep.f90: non-square axes implemented for cross section slices git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1518 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-23 dprice * build/Makefile, src/exact.f90, src/exact_rochelobe.f90: added roche lobe exact solution (somewhat restricted at present) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1515 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-21 dprice * scripts/makemovie.sh: movie script from Ben Ayliffe added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1487 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-21 dprice * src/options_data.f90: minor header change git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1486 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-17 dprice * src/calc_quantities.f90: only print info in debug mode for column identification git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1483 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-17 dprice * src/get_data.f90: BUG FIX with labels not being set on first call to calc_quantities causing calculated quantities to be inactive git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1482 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-17 dprice * src/calc_quantities.f90: calc_quantities module identifies calculated quantities so they can be used in the exact solutions (in particular, the radius) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1481 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-14 dprice * src/get_data.f90: less verbose endian information git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1480 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-10 dprice * src/defaults.f90: file unit is a parameter rather than hard coded in defaults file read/write git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1479 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-10 dprice * src/plotstep.f90: extra spaces removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1478 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-10 dprice * src/asciiutils.f90: header updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1477 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-10 dprice * src/setpage.f90: A long-standing bug with numbers being chopped in half at the edge of the viewport fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1476 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-02 dprice * src/allocate.f90: dat array zeroed when memory first allocated; avoids uninitialised variable problems (valgrind) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1475 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-02 dprice * src/limits.f90: header info updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1474 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-02 dprice * src/limits.f90, src/units.f90: checks if limits/units files exist before trying to open (removes valgrind warning) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1473 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-06-01 dprice * src/colourbar.f90: minor cleanups in colourbar routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1472 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-31 dprice * src/plotstep.f90: minor debugging output added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1471 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-26 dprice * src/calc_quantities.f90: added h*divB/B and plasma beta to list of examples/prefilled calculated quantities git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1470 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-26 dprice * src/calc_quantities.f90: arithmetic operators removed from labels git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1469 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-26 dprice * src/calc_quantities.f90: implemented pre-filling of calculated quantities list with *all* of the known examples (requested by Ben Ayliffe) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1468 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-18 dprice * src/plotstep.f90: enters single plot interactive mode if only one step exists (suggested by Ben Ayliffe) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1467 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-17 dprice * src/splash.f90: BUG FIX with global SPLASH_DEFAULTS variable setting/use git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1464 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-16 dprice * src/exact.f90, src/exact_sedov.f90: added vr solution for Sedov blast wave git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1459 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-15 dprice * scripts/png2theora.sh: png2theora script added (contributed by Pau Amari-Seoane) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1458 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-13 dprice * src/interpolation.f90: minor change to header git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1457 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-13 dprice * src/plotstep.f90: BUG FIX with non-interactive multiplot and partial data reads (required) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1456 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-09 dprice * src/defaults.f90, src/options_particleplots.f90: minor change to header git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1455 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-09 dprice * src/defaults.f90, src/globaldata.f90, src/menu.f90, src/plotstep.f90, src/prompting.f90: allow multiplots consisting of different particle types: seems to work git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1454 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-05-04 dprice * src/cubicsolve.f90: bug fix with seg fault in cubic solver for dustywave solution git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1448 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-31 dprice * src/calc_quantities.f90, src/interactive.f90: radius in calculated quantities is now computed relative to tracked particle if x0,y0 and z0 are used; calculated quantities are recomputed when necessary (that is, when t is set and quantities use x0,y0 or z0 in the calculation) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1439 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-31 dprice * src/colours.f90: added debug info in colour_set git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1438 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-24 dprice * src/read_data_sphNG.f90: better warning if end of file reached (explicitly pauses and asks user to continue) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1436 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-17 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.14.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1433 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-17 dprice * src/splash.f90: v1.14.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1432 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-17 dprice * INSTALL.macosx: updated install instructions for OS/X git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1431 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-17 dprice * src/splash.f90: v1.14.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1430 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-17 dprice * docs/splash.tex: docs updated for 1.14.1; more compiler info added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1429 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-17 dprice * src/cubicsolve.f90: replaced cubicsolve_complex with version that does not require sinh or asinh (for maximum portability, these are F2008 features) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1428 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-17 dprice * src/interpolate3D_projection.F90: spurious debug line removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1427 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-14 dprice * src/plotstep.f90: uninitialised variable bug fix (valgrind) with gotcontours git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1424 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-07 dprice * src/splash.f90: version info for 1.14.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1416 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-07 dprice * src/read_data_gadget.f90, src/read_data_gadget_hdf5.f90: BUG fix with massoftype in header if ntypes > 6 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1415 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-07 dprice * src/read_data_gadget.f90: bug fix with new maxpartypes setting and gadget read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1414 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-07 dprice * src/calc_quantities.f90: better formatting on write statement git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1413 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-07 dprice * src/plotstep.f90: labeltimeunits has length lenunitslabel git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1412 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-03 dprice * src/calc_quantities.f90: added magnitude of all vector quantities to example calculated quantities list; also magnetic pressure git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1411 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-03 dprice * src/plotlib_giza.f90: scir and qcir implemented in giza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1410 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-02 dprice * src/plotlib_giza.f90: updated plotlib_giza with new routines git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1409 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-03-02 dprice * src/globaldata.f90: year bumped to 2011 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1408 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-28 dprice * src/read_data_sphNG.f90: reads generalised euler potentials from phantom dump git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1407 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/plotstep.f90: minor formatting change git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1406 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/plotstep.f90: minor change git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1405 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/plotstep.f90: minor change git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1404 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/plotstep.f90: minor change to resolve conflict git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1403 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/globaldata.f90: max number of particle types increased to 12 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1402 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/units.f90: units labels actually *use* lenunitslabel git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1401 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * build/Makefile, src/read_data_seren.f90: seren data read added, now included in default build (thanks to Andrew McLeod) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1400 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/read_data_dragon.f90: added updated dragon read (thanks to Andrew McLeod) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1399 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/calc_quantities.f90: use lenlabel instead of fixed length for calculated quantity string git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1398 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-27 dprice * src/interpolation.f90: unit_interp moved to after the rescaling git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1397 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-18 dprice * src/colourbar.f90: save added to declaration git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1396 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-18 dprice * utils/grid2pdf.f90: updated grid2pdf utility git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1395 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-18 dprice * src/legends.f90, src/options_page.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90, src/plotstep.f90: implemented transparency for legend text (with giza only) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1394 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-18 dprice * src/convert_grid.f90, src/globaldata.f90, src/interpolation.f90, src/units.f90: added unit_interp for SEREN data read, for Andrew McLeod git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1393 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-02-01 dprice * src/exact.f90, src/exact_dustywaves.f90: dustywaves solution takes rhogas and dust-to-gas ratio as parameters git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1380 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-01-25 dprice * scripts/mapletofortran.pl: script for converting maple .tex to Fortran added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1363 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-01-25 dprice * src/exact_dustywaves.f90: bug fixes with dusty wave exact solution: now seems to work OK git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1362 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-01-24 dprice * build/Makefile, src/cubicsolve.f90, src/exact.f90, src/exact_dustywaves.f90: exact solution for two fluid dust/gas waves added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1361 cab04810-efc7-4a10-8ecf-f366c833a2ad 2011-01-24 dprice * src/exact.f90: changed order of exact solutions so that arbitrary function is #1, read from file is #2, rest come later git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1360 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-22 dprice * src/read_data_gadget.f90: bug fix if h not present in block-labelled gadget format (i.e. ICs files) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1359 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * build/Makefile, src/read_data_flash_hdf5.f90: bug fixes in flash hdf5 read compilation git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1338 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * build/Makefile: pgplot makefile check first checks if PGPLOT_DIR/makefile exists; cuts out spurious errors git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1337 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * src/splash.f90: version date bumped git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1336 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * build/Makefile: use of DESTDIR and PREFIX complies with gnu coding standards git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1335 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * Makefile, build/Makefile: bug fixes with make install target git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1334 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * build/Makefile: uses DESTDIR instead of DEST for make install; for compatibility with macports; portfile uses SYSTEM=gfortran git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1333 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * build/Makefile: working on macports build git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1332 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * build/Makefile: added SYSTEM=macports git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1331 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-08 dprice * docs/splash.tex: some more minor config for HeVeA git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1330 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-07 dprice * docs/splash.bbl: splash userguide modified to work with HEVEA translation to html; build for htmldocs added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1329 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-07 dprice * .gitignore, build/Makefile, docs/splash.tex: splash userguide modified to work with HEVEA translation to html; build for htmldocs added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1328 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-07 dprice * : arrow files updated (oops) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1327 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-07 dprice * : added html docs dir git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1326 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * docs/version_history, docs/version_history_tex.tex: version 1.14.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1325 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * docs/version_history, docs/version_history_tex.tex: version 1.14.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1324 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.14.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1323 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * src/splash.f90: v1.14.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1322 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * docs/splash.tex: updated docs with env variable options + f/F git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1321 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * src/globaldata.f90, src/read_data_gadget.f90: F. Buerzle: minor changes to gadget read to handle poloidal/toroidal field reading git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1320 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * src/plotstep.f90: BUG fix with -666 and 10^38 plot limits if NaNs in rendered quantity; also if rendermin=rendermax git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1319 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * src/plotstep.f90: added SPLASH_MARGIN_XMAX/SPLASH_MARGIN_XMIN etc env. variables for setting page margins git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1318 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-12-06 dprice * src/plotlib_giza.f90: added set_text_background stuff to giza interface git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1317 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-11-25 dprice * src/plotlib_giza.f90: plotlib uses new giza_format_number routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1316 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-11-05 dprice * src/read_data_sphNG.f90: reads external binary masses from phantom header (backwards compatible with old format) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1279 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-11-05 dprice * src/read_data_sphNG.f90: bug fix in memory allocation for iphase with phantom dumps + external binary git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1278 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * src/read_data_sphNG.f90: MORE bug fixes with phantom+sinks; this time if idim=idimptmass git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1272 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * src/get_data.f90: checks that number of particles from iamtype is consistent with npartoftype git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1271 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * src/plotstep.f90: minor bug fix with debug mode if npart < 10 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1270 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * src/setpage.f90: allow slightly bigger offsets for title and labels (to look OK with giza) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1267 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * build/Makefile: debug flags updated for gfortran 4.5 (uses -fcheck=all) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1266 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * src/read_data_sphNG.f90: bug fix with memory allocation and sinks git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1265 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * src/colours.f90: added Dolag colour schemes back in git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1264 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-15 dprice * src/read_data_sphNG.f90: bugs fixed with read of sink particle only phantom dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1262 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-14 dprice * src/read_data_sphNG.f90: no colouring of particles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1261 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-14 dprice * src/read_data_sphNG.f90: read of sink particles in phantom dumps added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1260 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-11 dprice * src/read_data_sphNG.f90: bug fix with sinks + phantom read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1227 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-10-07 dprice * src/read_data_sphNG.f90: bug fix with stepping backwards + accreted particles in phantom dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1222 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-28 dprice * src/read_data_sphNG.f90: bug fix with iphase + blank plots from phantom dumps git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1207 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-28 dprice * src/read_data_sphNG.f90: bug fix with phantom dumps + accreted particles, now treated much better git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1206 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-27 dprice * src/plotlib_giza.f90: added contouring routines to giza interface git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1205 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-27 dprice * docs/splash.tex, src/read_data_sphNG.f90: expanded SSPLASH_TIMEUNITS settings; added to docs git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1204 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-27 dprice * src/read_data_sphNG.f90: SSPLASH_TIMEUNITS env variable added to change default time units git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1203 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-21 dprice * src/read_data_sphNG.f90: debug statements added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1201 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-21 dprice * src/read_data_sphNG.f90: reads iphase from phantom dumps and assigns types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1194 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-09-16 dprice * src/read_data_sphNG.f90: bug fix with reading dust+gas files from phantom git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1189 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-27 dprice * src/plotlib_giza.f90: better calls to routines specifying units; units are converted from pgplot to giza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1188 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-27 dprice * build/Makefile: nsplash added to the default make git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1187 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-17 dprice * build/Makefile: build order bug fix (thanks to Robert Thompson) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1181 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-16 dprice * src/shapes.f90: text field in shapes can now be up to 120 characters git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1180 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-12 dprice * src/shapes.f90: arbitrary function can also be plotted as a shape: more flexible in terms of line style, colour etc git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1179 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-05 dprice * src/particleplot.f90: bug fixes with lines + 3D perspective git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1178 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-05 dprice * src/plotstep.f90: bug fixes with lines + rotation; no prompt for unit magnification distance; rotated axes: bug fix if coords not in first ndim columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1177 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-05 dprice * src/plotlib_giza.f90: plotlib_giza cleaned up; few more things added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1176 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-08-05 dprice * src/read_data_ascii.f90: asplash recognises coordinates even if they are not in the first ndim columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1172 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/interactive.f90: bug fix with F/f in interactive mode git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1168 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * build/Makefile: bug fix with build order git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1167 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * docs/splash.tex: docs updated for SPLASH_DEFAULTS git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1166 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/menu.f90: removed PDF option from the main menu unless SPLASH_TURB=yes is set git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1165 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/splash.f90: Monash email address updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1164 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/splash.f90: implemented SPLASH_DEFAULTS environment variable that can be used to set a system-wide defaults file used if no local files are present git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1163 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/analysis.f90: splash calc energies now only uses particle types that are turned on git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1162 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/asciiutils.f90, src/read_data_ascii.f90: improved treatment of blank lines in header of ascii data git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1161 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * docs/splash.tex, src/read_data_ascii.f90: added several more environment variable for getting the time and gamma from the header in the ascii data read; added to docs also git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1160 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/get_data.f90: warning added if set ix but number of dimensions not set correctly from data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1159 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/plotstep.f90: .le.ndim -> is_coords; now done everywhere so in principle everything should work if coords are not in the first ndim columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1158 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/interactive.f90: .le.ndim -> is_coord git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1157 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/particleplot.f90: uses is_coord instead of .le.ndim git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1156 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/particleplot.f90: neatened up source code git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1155 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/limits.f90: coords not assumed in 1:ndim => uses ix() explicitly git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1154 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/options_limits.f90: uses is_coord instead of .le.ndim; neatened up formatting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1153 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/exact.f90: .le.ndim --> is_coord in exact solution plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1152 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * tests/test_interpolate3D.f90: test uses new interface to interpolate3D_projection git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1151 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/render.f90: render accepts non-square pixels git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1150 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-28 dprice * src/interpolate3D_opacity.f90, src/interpolate3D_projection.F90, src/plotstep.f90: changes to 3D interpolation interfaces: can use different pixel width in the y direction; plus 3D opacity rendering now ignores particles with zero or negative weights git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1149 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-23 dprice * build/Makefile: build added for prompt and slicer tests git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1148 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-23 dprice * src/calc_quantities.f90: editing of individual calc quantities entries now works; plus uses same routine to print the quantities at startup git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1147 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-23 dprice * src/calc_quantities.f90: edit option in calc quantities setting: can now choose which column git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1145 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-23 dprice * src/read_data_egaburov.f90: read by E.Gaburov added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1144 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-23 dprice * src/prompting.f90: integer prompt with min2:max2 range tested and working git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1142 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-22 dprice * src/prompting.f90: integer prompt handles two ranges (not checked yet) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1141 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-22 dprice * src/prompting.f90: integer prompt handles two ranges (not checked yet) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1140 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-22 dprice * src/plotlib_giza.f90: bug fix with translation of tr to affine; added new utility routine for this git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1139 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-22 dprice * src/exact.f90, src/exact_mhdshock.f90: all MHD shock tube solutions are labelled and work for all times git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1138 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-21 dprice * src/exact_function.f90: less confusing naming of internal routines in exact_function git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1137 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-21 dprice * src/exact_function.f90: exact solution can be f(x,t) not just f(x) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1136 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-21 dprice * src/exact.f90: exact solution can be f(x,t) not just f(x) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1135 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-21 dprice * src/exact.f90, src/exact_function.f90: exact solution can be f(x,t) not just f(x) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1134 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-21 dprice * src/exact_mhdshock.f90: added time scaling for mshk7 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1133 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-21 dprice * src/exact_mhdshock.f90: Brio/Wu problem scaled with time git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1132 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-17 dprice * src/read_data_gadget.f90, src/read_data_gadget_hdf5.f90, src/read_data_gadget_jsb.f90: added correct labels for gadget particle types 3 and 4 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1131 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-17 dprice * src/plotlib_giza.f90: implemented more of plot_qinf in giza interface git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1130 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-17 dprice * .gitignore: added some more files to .gitignore git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1129 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-17 dprice * src/read_data_gadget_hdf5.f90, src/read_data_gadget_hdf5_utils.c: gadget HDF5 read: so far just reads the header git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1128 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-17 dprice * build/Makefile: gadget hdf5 read added to Makefile; tests build correctly git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1127 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-17 dprice * src/read_data_flash_hdf5_utils.c: bug fix with number of datasets read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1126 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-17 dprice * src/plotlib_giza.f90: plot_numb interface calls giza_format_number -- seems to work OK git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1125 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-07-06 dprice * src/read_data_sphNG.f90: plots sink particles from phantom with binary potential git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1099 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-20 dprice * src/plotlib_giza.f90: Added the interface for plot_circ and plot_arro. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1015 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-17 dprice * src/read_data_gadget.f90: minor bug fix with uninitialised hsoft variable in gadget read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1005 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-17 dprice * src/read_data_gadget.f90: multi-file gadget read tested with block-labelled format and seems to work OK; info about neighbour number added; slight bug fix with this git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@1004 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/get_data.f90: bug fix with minimum h setting (now not applied to -ve smoothing lengths) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@999 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/read_data_gadget.f90: some minor cleanups to gadget read: better errors/warnings git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@998 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/read_data_gadget.f90: particle id read code neatened; plus bug fix with rescaling of h git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@997 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/read_data_gadget.f90: cleanup after multiple file read stuff; commented out sections removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@996 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/options_xsecrotate.f90: trailing whitespace fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@995 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/options_xsecrotate.f90: slightly more user-friendly prompts when setting animation sequences git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@994 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/interpolate3D_projection.F90: minor update to warning about h <= 0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@993 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-13 dprice * src/read_data_gadget.f90: GADGET read across multiple files: bugs fixed, tested and seems to work OK git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@992 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-12 dprice * src/read_data_gadget.f90: neighbour warning happens only once git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@991 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-12 dprice * src/read_data_gadget.f90: neighbour warning happens only once git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@990 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-12 dprice * src/read_data_gadget.f90: clean up of multiple file read; should work with all formats + dm smoothing lengths; friendly neighbour warning added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@989 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-12 dprice * docs/splash.tex: minor doc changes git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@988 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * src/read_data_gadget.f90: gadget read seems to work across multiple files; a few issues remaining git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@987 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * src/convert_grid.f90: parallel min/max/mean on grid commented (reduction on arrays does not work with ifort 10) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@986 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * docs/splash.tex: docs updated with splash to grid + splash_vzero_codeunits git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@985 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * src/splash.f90: date updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@984 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * scripts/time_average_pdfs.f90: bug with checking bin positions fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@983 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * src/convert_grid.f90: SPLASH_TO_GRID_DENSITY_ONLY; plus counts how many empty grid cells there are git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@982 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * src/pdfs.f90: bug with normalisation fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@981 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-11 dprice * src/get_data.f90: environment variable SPLASH_VZERO_CODEUNITS added to subtract mean velocity field git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@980 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-10 dprice * src/plotlib_giza.f90: bug fix in plot_vect interface to giza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@979 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-10 dprice * src/read_data_h5part.f90: minor changes to format statements git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@978 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-07 dprice * src/plotlib_giza.f90: Added vectors to interface. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@977 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-05 dprice * src/plotstep.f90: debug statements added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@973 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-05 dprice * src/options_powerspec.f90, src/pdfs.f90, src/plotstep.f90: pdf calculation more obvious: always does raw quantity (not transformed); pdf options moved to iotions_powerspec, will be saved; means pdf is a more standalone module git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@972 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-05 dprice * src/convert.f90: bug fix with low memory mode + splash to grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@971 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-05 dprice * src/convert_grid.f90: env variable for density only in splash to grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@970 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-05 dprice * src/analysis.f90: uses tagline git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@969 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-05 dprice * src/write_griddata.F90: minor git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@968 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/calc_quantities.f90: default prompt reverts to q after first entry in calc_quantities git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@967 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/allocate.f90, src/exact_shock_sr.f90, src/interpolate3D.f90, src/plotlib_giza.f90, src/write_data_phantom.f90: compiler warnings fixed (g95) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@966 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * build/Makefile: read_data_sphNG_otherendian now builds; does not leave .f90 files in build dir git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@965 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/convert_grid.f90: compiled warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@964 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * build/Makefile, utils/grid2pdf.f90: added grid2pdf utility that calculates PDF from output of splash to gridbinary git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@963 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/splash.f90: new module names git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@962 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/pdfs.f90, src/plotstep.f90: pdf module restructured so more standalone; not dependent on plotting library; calc separate to write git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@961 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/globaldata.f90: tagline updated git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@960 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/convert.f90, src/convert_grid.f90, src/write_griddata.F90: write_griddata module renamed readwrite_griddata; read routines added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@959 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-05-03 dprice * src/write_griddata.F90: gridbinary format explicitly explained in the command line options git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@958 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-29 dprice * src/read_data_VINE.f90: VINE read ignores ipindx array if there are values < 0 or > ntot; other minor changes git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@957 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-27 dprice * src/analysis.f90: rhomach calculation volume setting affects mean only, not variance git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@956 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-23 dprice * docs/splash.tex: page size larger; manual is smaller git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@955 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-23 dprice * src/legends.f90: bug fix with set/unset line cap/style for legends with giza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@954 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-22 dprice * build/Makefile: giza objects only defined if BACKEND=giza git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@953 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-22 dprice * src/options_particleplots.f90, src/particleplot.f90, src/plotstep.f90: implemented more general error bars; can be for x and y axis; error bar location for each column stored; also bug fix/regression with interactive change of irender git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@952 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-22 dprice * src/read_data_ascii.f90: docs added to header git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@951 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-22 dprice * docs/splash.tex: updated endian section; details of h5part read added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@950 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-22 dprice * src/plotlib_pgplot.f90: minor change git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@949 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-12 dprice * build/Makefile: h5part compiles in munich git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@948 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-12 dprice * src/splash.f90: bumped version number/date git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@947 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-12 dprice * src/options_xsecrotate.f90: sets default rotation if none is set when 3D perspective turned on git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@946 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-12 dprice * build/Makefile: H5partAttrib added to build git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@945 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-12 dprice * src/interactive.f90, src/plotstep.f90: f/F flips rendered quantity to next one (only for 1-plot-per-page interactive call) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@944 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-08 dprice * src/plotstep.f90: debugging of step legend, minor formatting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@943 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-08 dprice * src/H5Part/{H5Pt.F90 => H5Part.F90}: .F90 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@942 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-08 dprice * src/H5Part/{H5Part.f90 => H5Pt.F90}: rename to .F90 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@941 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-08 dprice * src/H5Part/H5Part.f90, src/H5Part/H5PartAttrib.f90, src/H5Part/H5PartAttribF.c, src/H5Part/H5PartF.c, src/read_data_h5part.f90: finished the new H5Part interface (phew!); h5part read now seems to work OK git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@940 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-08 dprice * src/interactive.f90, src/plotlib_pgplot.f90, src/plotstep.f90, src/shapes.f90: Changed plot_curs and plot_band to functinos, all calls modified accordingly. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@939 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-08 dprice * build/Makefile, src/interactive.f90, src/plotlib_giza.f90, src/plotlib_pgplot.f90: Added plot_left_click to the plot libs. Changed giza-fortran.f90 to giza-fortran.F90. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@938 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-04-07 dprice * src/plotlib_giza.f90: Modified the call to giza_render. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@937 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-31 dprice * src/plotstep.f90: legend and shapes only plotted once per page (avoids multiple antialiasing in giza) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@926 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-31 dprice * src/shapes.f90: text shape editing starts from first character (no need to delete -click to edit- first) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@925 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-31 dprice * src/plotlib_giza.f90: more warnings added for unimplemented functionality git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@924 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-31 dprice * src/plotlib_pgplot.f90: minor formatting changes; comments added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@923 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-31 dprice * src/plotlib_giza.f90: added error bar interface and get/set fill style; comments added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@922 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-29 dprice * src/plotstep.f90: paper size set in plot_init; plus axes only drawn once if multiple-steps-on-page git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@921 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-29 dprice * src/plotlib_giza.f90, src/plotlib_pgplot.f90: plot_init handles optional paper size git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@920 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-29 dprice * src/setpage.f90: only redraw axes if using PGPLOT git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@919 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-29 dprice * src/pagecolours.f90: bug fix with page colours the wrong way around! git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@918 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-28 dprice * src/plotstep.f90: bug fix with openMP statements in plotstep git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@917 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * src/read_data_h5part.f90: reads particle IDs and distinguishes between types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@916 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * src/read_data_h5part.f90: reads particle IDs and distinguishes between types git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@915 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * src/plotstep.f90: plotstep works even if coords are not in the first ndim columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@914 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * src/H5Part/H5Part.f90, src/H5Part/H5PartAttribF.c, src/H5Part/H5PartF.c: removed unnecessary underscoring business from h5part interface; uses bind(C) instead git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@913 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * src/H5Part/H5Part.f90: version without the bind(C) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@912 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * build/Makefile: h5part reader added; better linking to HDF5 libraries git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@911 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * src/globaldata.f90, src/menu.f90: is_coord function added; used so that coords can be anywhere in data git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@910 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-25 dprice * src/H5Part/COPYING, src/H5Part/H5Part.f90, src/H5Part/H5PartAttribF.c, src/H5Part/H5PartF.c, src/H5Part/README, src/read_data_h5part.f90: h5part reader added; with modified Fortran interface git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@909 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-17 dprice * src/plotlib_giza.f90: added plot_circ, qch and sfs/qfs to giza interface git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@908 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-15 dprice * src/plotstep.f90: minor formatting changes; additional debugging output added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@907 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-15 dprice * src/particleplot.f90: bug fix with commented-out line in particleplot.f90 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@906 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-15 dprice * src/calc_quantities.f90: maximum number of calculated quantities increased to 35; plus bugs fixed + message printed when this limit is reached git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@905 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-05 dprice * build/Makefile: h5part depends on H5part.o git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@904 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-05 dprice * build/Makefile: working on giza backend compilation; compiles ok, new giza-fortran.f90; also started h5part read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@903 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-04 dprice * build/Makefile: bug fix with auto library adding if compiler does not match PGPLOT git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@902 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-04 dprice * src/read_data_tipsy.F90: velocity labels fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@901 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-04 dprice * build/Makefile: Makefile is able to compile with the giza backend; plus much more user-friendly for PGPLOT; ensures PGPLOT_DIR is set plus adds compiler libraries for g77, g95 and gfortran-compiled PGPLOT automatically git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@900 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * bin/tmp: removed tmp git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@899 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * bin/tmp: bin dir added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@898 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * src/plotlib_cpl.f90: removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@897 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * src/plotlib_giza.f90: fixed a few things with the interface [intent(out) variables set, renamed to giza] git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@896 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * src/isosurface.f90: obsolete file removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@895 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * Makefile, build/Makefile: build works with subdirs git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@894 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * Makefile, {src => build}/Makefile: build works with subdirs git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@893 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * Makefile => src/Makefile, allocate.f90 => src/allocate.f90, analysis.f90 => src/analysis.f90, asciiutils.f90 => src/asciiutils.f90, calc_quantities.f90 => src/calc_quantities.f90, colourbar.f90 => src/colourbar.f90, colourparts.f90 => src/colourparts.f90, colours.f90 => src/colours.f90, convert.f90 => src/convert.f90, convert_grid.f90 => src/convert_grid.f90, defaults.f90 => src/defaults.f90, discplot.f90 => src/discplot.f90, exact.f90 => src/exact.f90, exact_densityprofiles.f90 => src/exact_densityprofiles.f90, exact_fromfile.f90 => src/exact_fromfile.f90, exact_function.f90 => src/exact_function.f90, exact_mhdshock.f90 => src/exact_mhdshock.f90, exact_mhdshock_other.f90 => src/exact_mhdshock_other.f90, exact_polytrope.f90 => src/exact_polytrope.f90, exact_rhoh.f90 => src/exact_rhoh.f90, exact_ringspread.f90 => src/exact_ringspread.f90, exact_sedov.f90 => src/exact_sedov.f90, exact_shock.f90 => src/exact_shock.f90, exact_shock_sr.f90 => src/exact_shock_sr.f90, exact_torus.f90 => src/exact_torus.f90, exact_toystar1D.f90 => src/exact_toystar1D.f90, exact_toystar2D.f90 => src/exact_toystar2D.f90, exact_wave.f90 => src/exact_wave.f90, fieldlines.f90 => src/fieldlines.f90, fparser.f90 => src/fparser.f90, geometry.f90 => src/geometry.f90, get_data.f90 => src/get_data.f90, globaldata.f90 => src/globaldata.f90, interactive.f90 => src/interactive.f90, interpolate1D.f90 => src/interpolate1D.f90, interpolate2D.f90 => src/interpolate2D.f90, interpolate3D.f90 => src/interpolate3D.f90, interpolate3D_opacity.f90 => src/interpolate3D_opacity.f90, interpolate3D_projection.F90 => src/interpolate3D_projection.F90, interpolate3D_xsec.f90 => src/interpolate3D_xsec.f90, interpolate_vec.f90 => src/interpolate_vec.f90, interpolation.f90 => src/interpolation.f90, isosurface.f90 => src/isosurface.f90, legends.f90 => src/legends.f90, limits.f90 => src/limits.f90, menu.f90 => src/menu.f90, options_data.f90 => src/options_data.f90, options_limits.f90 => src/options_limits.f90, options_page.f90 => src/options_page.f90, options_particleplots.f90 => src/options_particleplots.f90, options_powerspec.f90 => src/options_powerspec.f90, options_render.f90 => src/options_render.f90, options_vecplot.f90 => src/options_vecplot.f90, options_xsecrotate.f90 => src/options_xsecrotate.f90, pagecolours.f90 => src/pagecolours.f90, particleplot.f90 => src/particleplot.f90, pdfs.f90 => src/pdfs.f90, plotlib_cpl.f90 => src/plotlib_cpl.f90, plotlib_pgplot.f90 => src/plotlib_pgplot.f90, plotstep.f90 => src/plotstep.f90, plotutils.f90 => src/plotutils.f90, powerspectrums.f90 => src/powerspectrums.f90, prompting.f90 => src/prompting.f90, read_data_UCLA.f90 => src/read_data_UCLA.f90, read_data_VINE.f90 => src/read_data_VINE.f90, read_data_ascii.f90 => src/read_data_ascii.f90, read_data_bauswein.f90 => src/read_data_bauswein.f90, read_data_dansph.f90 => src/read_data_dansph.f90, read_data_dansph_old.f90 => src/read_data_dansph_old.f90, read_data_dragon.f90 => src/read_data_dragon.f90, read_data_flash_hdf5.f90 => src/read_data_flash_hdf5.f90, read_data_flash_hdf5_utils.c => src/read_data_flash_hdf5_utils.c, read_data_foulkes.f90 => src/read_data_foulkes.f90, read_data_gadget.f90 => src/read_data_gadget.f90, read_data_gadget_jsb.f90 => src/read_data_gadget_jsb.f90, read_data_jjm.f90 => src/read_data_jjm.f90, read_data_jjm_multiphase.f90 => src/read_data_jjm_multiphase.f90, read_data_jules.f90 => src/read_data_jules.f90, read_data_kitp.f90 => src/read_data_kitp.f90, read_data_mbate.f90 => src/read_data_mbate.f90, read_data_mbate_hydro.f90 => src/read_data_mbate_hydro.f90, read_data_mbate_mhd.f90 => src/read_data_mbate_mhd.f90, read_data_oilonwater.f90 => src/read_data_oilonwater.f90, read_data_rsph.f90 => src/read_data_rsph.f90, read_data_scw.f90 => src/read_data_scw.f90, read_data_snsph.f90 => src/read_data_snsph.f90, read_data_snsph_utils.c => src/read_data_snsph_utils.c, read_data_sphNG.f90 => src/read_data_sphNG.f90, read_data_spyros.f90 => src/read_data_spyros.f90, read_data_sro.f90 => src/read_data_sro.f90, read_data_tipsy.F90 => src/read_data_tipsy.F90, read_data_urban.f90 => src/read_data_urban.f90, read_data_vanaverbeke.f90 => src/read_data_vanaverbeke.f90, render.f90 => src/render.f90, rotate.f90 => src/rotate.f90, setpage.f90 => src/setpage.f90, shapes.f90 => src/shapes.f90, splash.f90 => src/splash.f90, system_f2003.f90 => src/system_f2003.f90, system_unix.f90 => src/system_unix.f90, system_unix_NAG.f90 => src/system_unix_NAG.f90, system_utils.f90 => src/system_utils.f90, timestepping.f90 => src/timestepping.f90, titles.f90 => src/titles.f90, transform.f90 => src/transform.f90, units.f90 => src/units.f90, write_data_phantom.f90 => src/write_data_phantom.f90, write_griddata.F90 => src/write_griddata.F90, write_pixmap.f90 => src/write_pixmap.f90, write_sphdata.f90 => src/write_sphdata.f90: files moved to sub-directory src/ git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@892 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * README: updated contact details git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@891 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-03-03 dprice * scripts/ppm2gif.bash: uses splash filenames not supersphplot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@890 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * docs/version_history, docs/version_history_tex.tex: version 1.13.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@889 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.13.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@888 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * splash.f90: v1.13.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@887 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * splash.f90: v1.13.1 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@886 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * interpolate3D_projection.F90: subgrid warning if nsubgrid > 10% of particles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@885 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * interactive.f90: better message when pressing s git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@884 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * interpolate3D_projection.F90: added warning about subgrid rendering; info about resolution required git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@883 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * options_data.f90: better menu behaviour if calcquantities is initially off git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@882 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-26 dprice * calc_quantities.f90: calc_quantities compiles with ifort; bugs with subsequent variables and label settings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@881 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * splash.f90: version changed to -svn git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@880 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * plotstep.f90: page colour schemes handled slighly differently to avoid use of pgscrn; also simpler; explicit refs to PGPLOT removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@879 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * Makefile, options_page.f90, pagecolours.f90: page colour schemes handled slighly differently to avoid use of pgscrn; also simpler; explicit refs to PGPLOT removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@878 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * calc_quantities.f90: bug fix with physical unit rescaling (ncalc not icalc) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@877 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * render.f90: render_opacity removed (obsolete) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@876 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * plotlib_cpl.f90, plotlib_pgplot.f90, plotstep.f90: bug fix with auto pixel selection (min now 1); check for PGPLOT bugs only done if lib is pgplot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@875 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * interactive.f90: bug fix with a) on multipanel plots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@874 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * interactive.f90: bug fix with a) on multipanel plots git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@873 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * Makefile: resolved conflict git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@872 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * plotlib_pgplot.f90: added header to plotlib routine git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@871 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * splash.f90: added credit to james git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@870 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-25 dprice * Makefile, colourbar.f90, colourparts.f90, colours.f90, discplot.f90, exact.f90, exact_toystar1D.f90, exact_toystar2D.f90, fieldlines.f90, interactive.f90, legends.f90, options_page.f90, particleplot.f90, plotstep.f90, plotutils.f90, render.f90, rotate.f90, setpage.f90, shapes.f90, timestepping.f90: PGPLOT calls replaced by calls to generic plot interface (work by James Wetter) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@869 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * Makefile: permissions fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@868 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * read_data_flash_hdf5_utils.c: permissions fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@867 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * LICENSE, plotlib_cpl.f90, plotlib_pgplot.f90: permissions fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@866 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.13.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@865 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * splash.f90: v1.13.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@864 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * calc_quantities.f90, defaults.f90, options_data.f90: function parser implemented for calc_quantities; tested and seems to work git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@863 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * globaldata.f90: lenunitslabel added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@862 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * menu.f90: print statement removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@861 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * fparser.f90: verboseness optional argument to checkf and parsef git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@860 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * docs/splash.tex: docs updated for 1.13.0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@859 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * calc_quantities.f90: bug fix with units label git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@858 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * read_data_sphNG.f90: bug fix with uninitialised variables git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@857 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * prompting.f90: updated contact details git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@856 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * prompting.f90: string prompt does not accept blank string if noblank=.true. git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@855 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * read_data_ascii.f90: nicer formatting in file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@854 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * INSTALL, INSTALL.macosx, README: permissions changed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@853 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * : figs added to repository git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@852 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * read_data_VINE.f90, read_data_ascii.f90, read_data_bauswein.f90, read_data_dansph.f90, read_data_dansph_old.f90, read_data_gadget_jsb.f90, read_data_jjm.f90, read_data_kitp.f90, read_data_mbate.f90, read_data_mbate_hydro.f90, read_data_mbate_mhd.f90, read_data_oilonwater.f90, read_data_rsph.f90, read_data_scw.f90, read_data_snsph.f90, read_data_sphNG.f90, read_data_spyros.f90, read_data_sro.f90, read_data_urban.f90: format printed at the start of all data reads git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@851 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * read_data_timli.f90: renamed to jjm_multiphase git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@850 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * read_data_jjm_multiphase.f90: renamed to jjm_multiphase git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@849 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * plotlib_pgplot.f90: plotlib added for pgplot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@848 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * plotlib_cpl.f90: added plotlib for cairoplot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@847 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * get_data.f90: more error checks on data read added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@846 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * get_data.f90: debugging statements added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@845 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * menu.f90: bug fix if ncolumns=0 from data read + menu git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@844 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * read_data_urban.f90: bug fix with urban data read if no columns found git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@843 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-24 dprice * Makefile: the splash binary is now the same as asplash git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@842 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-10 dprice * menu.f90: re-formatting; plus no extra columns if ncolumns=0 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@833 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-10 dprice * get_data.f90, read_data_urban.f90: bug fix with ncol<0 in data reads: added check for this git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@832 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-10 dprice * read_data_urban.f90: reads time; fixed bug with not closing file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@831 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-05 dprice * read_data_urban.f90: sink particle file name substitutes _number for _S git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@830 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-02 dprice * Makefile, docs/splash.tex: urban binary called usplash git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@829 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-02-02 dprice * docs/splash.tex: andrea urban format added to docs git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@828 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-27 dprice * read_data_ascii.f90: vector labels are set by default git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@827 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-27 dprice * prompting.f90: optional list argument to string prompt git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@826 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-27 dprice * get_data.f90: units labels always applied if iRescale is true git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@825 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-27 dprice * fparser.f90: checks for -ve numbers to fractional powers and zero to negative power git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@824 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-27 dprice * Makefile, read_data_urban.f90: data read added for Andrea Urban git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@823 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-27 dprice * plotstep.f90: uninitialised variable errors fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@822 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-25 dprice * plotstep.f90: bug fix with unset variables if irotate but not coord plot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@821 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-21 dprice * get_data.f90: nicer endian info printout git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@820 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-21 dprice * prompting.f90: uses len_trim instead of len(trim()) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@819 cab04810-efc7-4a10-8ecf-f366c833a2ad 2010-01-21 dprice * plotstep.f90: catches incorrect setting of iplotx,iploty to avoid seg fault if internal error git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@818 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-12-23 dprice * options_page.f90, plotstep.f90: panels can be plotted in column-major or row-major order git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@817 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-12-23 dprice * interactive.f90: extra safety on the panel determination git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@816 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-12-23 dprice * plotstep.f90: iplotxtemp and iplotytemp given default values for safety (prevents seg fault even if plotting page is screwed up) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@815 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-12-15 dprice * options_page.f90, timestepping.f90: allows max colour index setting and mod for line styles git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@814 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-12-15 dprice * globaldata.f90, read_data_gadget.f90: added iax label + sink particle type for gadget read (from Florian Buerzle) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@813 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-12-04 dprice * setpage.f90: bug fix with labelling of the y-axis and tiling git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@812 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-04 dprice * get_data.f90, menu.f90, options_data.f90, splash.f90: single routine used for resetting coordinate and vector labels after coordinate system change; fixes bugs with labels not being set properly under some circumstances git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@780 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-04 dprice * convert_grid.f90: skips interpolation if array is zero git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@779 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-03 dprice * convert_grid.f90: does velocity components one at a time if large grid used git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@778 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-03 dprice * convert_grid.f90, interpolate3D.f90, write_griddata.F90: added gridtest format; sub-grid interpolation implemented on 3D interpolation, gives much better results git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@777 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-03 dprice * write_griddata.F90: returns ierr = 0 if not set git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@776 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-03 dprice * write_sphdata.f90: created by git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@775 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-03 dprice * write_griddata.F90: jumps out if error in write git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@774 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-03 dprice * globaldata.f90, write_griddata.F90, write_sphdata.f90: implemented ascii grid format (also default) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@773 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-03 dprice * convert.f90, convert_grid.f90, interpolate3D.f90, write_griddata.F90: bug fixes in splash to grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@772 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * Makefile: new files added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@771 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * convert.f90, convert_grid.f90, interpolate3D.f90, plotstep.f90, powerspectrums.f90, splash.f90, write_griddata.F90: splash to grid feature implemented; seems to work ok (limited formats at present) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@770 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * get_data.f90: better checks on labels (checks ix.ne.0 if ndim>0) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@769 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * analysis.f90: minor change to log output for rhomach git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@768 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * INSTALL: comments on old gfortran added to install instructions git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@767 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * Makefile, interpolation.f90, plotstep.f90: set_interpolation_weights moved to separate module so can be called for sph to grid conversion git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@766 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * Makefile: added SYSTEM for old gfortran; new gfortran uses f2003 system file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@765 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-11-02 dprice * read_data_mbate.f90: reads all headers first to allocate memory properly; fixes seg fault git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@764 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-19 dprice * analysis.f90: variance and mean of ln(rho) computed in calc rhomach git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@760 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-19 dprice * particleplot.f90: line colour but not style used for error bars git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@759 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-09 dprice * analysis.f90: rhomach spits out b values also git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@52 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-09 dprice * Makefile: build order fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@51 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-09 dprice * analysis.f90: calc rhomach fixed; option to override volume with environment variable; spits out more to file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@50 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-09 dprice * analysis.f90: added calc rms and vrms, plus rhomach calculation using Knuth-style variance git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@49 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-08 dprice * Makefile: added build for gadget dual endian read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@48 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-08 dprice * get_data.f90: commented out stuff about read_data_otherendian git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@47 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-08 dprice * interpolate3D_opacity.f90: added commented-out parallelisation from zen git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@46 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-08 dprice * calc_quantities.f90: minor changes from zen (to dudtrad) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@45 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-10-08 dprice * Makefile: merged changes from zen into makefile: read_data_otherendian for sphNG stuff added git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@44 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-28 dprice * docs/splash.tex: docs added for splash calc timeaverage git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@43 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-28 dprice * interactive.f90, shapes.f90: ctrl-t adds a text shape at current location; seems to work ok git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@42 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-28 dprice * exact.f90, globaldata.f90, read_data_dansph.f90: sr shock tube solution for rho* plotted; plus memory explicitly allocated/deallocated for exact soln plotting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@41 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-28 dprice * get_data.f90: less verbose: timing only printed if takes > 1s for data read git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@40 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-28 dprice * exact_shock_sr.f90: special rel shock tube zooms correctly, instead of fixed x grid git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@39 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-17 dprice * read_data_sphNG.f90: out-of-bounds error in low mem sphNG read fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@38 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * analysis.f90, convert.f90: added ability to compute time average of a whole sequence of files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@37 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * read_data_ascii.f90: reads splash.columns file in preference to columns git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@36 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * fparser.f90: BUG FIX: exits after calling ParseErrMsg; avoids array-out-of-bounds error git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@35 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * exact.f90: uses read_asciifile to read .func files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@34 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * asciiutils.f90: bug fix if comments at the end of real columns: now works ok (gets n columns correctly) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@33 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * asciiutils.f90: bug fixes with error returned (0 by default) on read_asciifile; also spurious warning if nlines=nmax removed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@32 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * exact_function.f90: allow less verbose checking git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@31 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * exact.f90: allow up to 10 functions; functions can be read from .func file (dumpfile.func or splash.func) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@30 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * Makefile: added cleanall and distclean target to remove installed files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@29 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * get_data.f90: endian info printed first time; neatened up formatting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@28 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * legends.f90: blank legend entries are not printed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@27 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-09-04 dprice * interactive.f90: less verbose if non-interactive device git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@26 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * plotstep.f90: legend entries continue over multiple panels if there are enough lines in the step legend file git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@25 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * get_data.f90: unused variables removed from only clauses git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@24 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * Makefile: added install target; controlled by DEST= setting git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@23 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * fparser.f90: commented out unused routines/calls git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@22 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * exact.f90: allows up to five functions to be plotted git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@21 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * plotstep.f90: bug fix with log labels if iaxis=20 git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@20 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * fparser.f90: function parser recognises mathematical constants (currently just pi) and parses them (not using substitution) git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@19 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-27 dprice * exact.f90, exact_function.f90: implemented sub-functions in call to function parser git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@18 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-10 dprice * exact_function.f90: minor change git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@17 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-07 dprice * write_data_phantom.f90: compiler warnings fixed git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@16 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-07 dprice * asciiutils.f90: string_replace does all occurrences git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@15 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-07 dprice * Makefile: function parser added; new exact solution plots arbitrary function git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@14 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-07 dprice * exact.f90, exact_function.f90, fparser.f90: function parser added; new exact solution plots arbitrary function git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@13 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-08-07 dprice * allocate.f90, analysis.f90, asciiutils.f90, calc_quantities.f90, colourbar.f90, colourparts.f90, colours.f90, convert.f90, defaults.f90, discplot.f90, exact.f90, exact_densityprofiles.f90, exact_fromfile.f90, exact_mhdshock.f90, exact_mhdshock_other.f90, exact_polytrope.f90, exact_rhoh.f90, exact_ringspread.f90, exact_sedov.f90, exact_shock.f90, exact_shock_sr.f90, exact_torus.f90, exact_toystar1D.f90, exact_toystar2D.f90, exact_wave.f90, fieldlines.f90, geometry.f90, get_data.f90, globaldata.f90, interactive.f90, interpolate1D.f90, interpolate2D.f90, interpolate3D.f90, interpolate3D_opacity.f90, interpolate3D_projection.F90, interpolate3D_xsec.f90, interpolate_vec.f90, isosurface.f90, legends.f90, limits.f90, menu.f90, options_data.f90, options_limits.f90, options_page.f90, options_particleplots.f90, options_powerspec.f90, options_render.f90, options_vecplot.f90, options_xsecrotate.f90, particleplot.f90, pdfs.f90, plotstep.f90, plotutils.f90, powerspectrums.f90, prompting.f90, read_data_UCLA.f90, read_data_VINE.f90, read_data_ascii.f90, read_data_bauswein.f90, read_data_dansph.f90, read_data_dansph_old.f90, read_data_dragon.f90, read_data_flash_hdf5.f90, read_data_foulkes.f90, read_data_gadget.f90, read_data_gadget_jsb.f90, read_data_jjm.f90, read_data_jules.f90, read_data_kitp.f90, read_data_mbate.f90, read_data_mbate_hydro.f90, read_data_mbate_mhd.f90, read_data_oilonwater.f90, read_data_rsph.f90, read_data_scw.f90, read_data_snsph.f90, read_data_sphNG.f90, read_data_spyros.f90, read_data_sro.f90, read_data_timli.f90, read_data_tipsy.F90, read_data_vanaverbeke.f90, render.f90, rotate.f90, setpage.f90, shapes.f90, splash.f90, system_f2003.f90, system_unix.f90, system_unix_NAG.f90, system_utils.f90, timestepping.f90, titles.f90, transform.f90, units.f90, write_data_phantom.f90, write_pixmap.f90, write_sphdata.f90: header notice added to all .f90 files git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@12 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-24 dprice * plotstep.f90: prints limits for non-interactive plots for the first plot git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@11 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-23 dprice * read_data_dansph.f90: added labels for vector potential git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@10 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-17 dprice * Makefile: adds openMP flags if OPENMP=yes or if PARALLEL=yes git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@9 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-17 dprice * : commit 523eb26e751b3fa84eb22b5e6cb92d307dd20ed0 Author: dprice Date: Fri Jul 17 12:34:51 2009 +0000 2009-07-17 dprice * : deleted empty dirs git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@6 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-17 Daniel Price * docs/splash.tex: updated thanks to users 2009-07-17 dprice * trunk/Makefile: test of svn commit git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@5 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-17 dprice * Makefile: test of svn commit git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash/trunk@5 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-17 dprice * : 328 0 trunk/INSTALL 112 0 trunk/INSTALL.macosx 280 0 trunk/LICENSE 692 0 trunk/Makefile 23 0 trunk/README 250 0 trunk/allocate.f90 471 0 trunk/analysis.f90 374 0 trunk/asciiutils.f90 433 0 trunk/calc_quantities.f90 335 0 trunk/colourbar.f90 29 0 trunk/colourparts.f90 410 0 trunk/colours.f90 91 0 trunk/convert.f90 269 0 trunk/defaults.f90 167 0 trunk/discplot.f90 1181 0 trunk/docs/bibstyle.bst 8488 0 trunk/docs/figs/colourschemes.ps 22724 0 trunk/docs/figs/hyperbolic.ps - - trunk/docs/figs/sedov_example.png - - trunk/docs/figs/starpart1.png - - trunk/docs/figs/starpart2.png - - trunk/docs/figs/starpart3.png - - trunk/docs/figs/starpart4.png - - trunk/docs/figs/starpart5.png - - trunk/docs/figs/starpart6.png - - trunk/docs/figs/starpartfinal.png - - trunk/docs/figs/surfdens.pdf - - trunk/docs/figs/surfpart1.png - - trunk/docs/figs/surfpart2.png - - trunk/docs/figs/surfpart3.png - - trunk/docs/figs/surfpart4.png - - trunk/docs/figs/surfpart5.png - - trunk/docs/figs/surfpart6.png - - trunk/docs/figs/surfpartfinal.png 551 0 trunk/docs/figs/xsec2D.eps 61 0 trunk/docs/figs/xsec2D.fig 177 0 trunk/docs/figs/xsec3D.eps 33 0 trunk/docs/figs/xsec3D.fig 44 0 trunk/docs/splash.bbl 2576 0 trunk/docs/splash.tex 1 0 trunk/docs/version 98 0 trunk/docs/version_history 38 0 trunk/docs/version_history_tex.tex 936 0 trunk/exact.f90 100 0 trunk/exact_densityprofiles.f90 48 0 trunk/exact_fromfile.f90 446 0 trunk/exact_mhdshock.f90 261 0 trunk/exact_mhdshock_other.f90 104 0 trunk/exact_polytrope.f90 47 0 trunk/exact_rhoh.f90 321 0 trunk/exact_ringspread.f90 247 0 trunk/exact_sedov.f90 400 0 trunk/exact_shock.f90 850 0 trunk/exact_shock_sr.f90 122 0 trunk/exact_torus.f90 414 0 trunk/exact_toystar1D.f90 465 0 trunk/exact_toystar2D.f90 49 0 trunk/exact_wave.f90 319 0 trunk/fieldlines.f90 531 0 trunk/geometry.f90 441 0 trunk/get_data.f90 185 0 trunk/globaldata.f90 2510 0 trunk/interactive.f90 147 0 trunk/interpolate1D.f90 580 0 trunk/interpolate2D.f90 227 0 trunk/interpolate3D.f90 456 0 trunk/interpolate3D_opacity.f90 790 0 trunk/interpolate3D_projection.F90 327 0 trunk/interpolate3D_xsec.f90 155 0 trunk/interpolate_vec.f90 89 0 trunk/isosurface.f90 279 0 trunk/legends.f90 356 0 trunk/limits.f90 662 0 trunk/menu.f90 219 0 trunk/options_data.f90 238 0 trunk/options_limits.f90 410 0 trunk/options_page.f90 312 0 trunk/options_particleplots.f90 81 0 trunk/options_powerspec.f90 194 0 trunk/options_render.f90 145 0 trunk/options_vecplot.f90 750 0 trunk/options_xsecrotate.f90 613 0 trunk/particleplot.f90 237 0 trunk/pdfs.f90 3561 0 trunk/plotstep.f90 123 0 trunk/plotutils.f90 307 0 trunk/powerspectrums.f90 511 0 trunk/prompting.f90 188 0 trunk/read_data_UCLA.f90 481 0 trunk/read_data_VINE.f90 351 0 trunk/read_data_ascii.f90 250 0 trunk/read_data_bauswein.f90 403 0 trunk/read_data_dansph.f90 519 0 trunk/read_data_dansph_old.f90 659 0 trunk/read_data_dragon.f90 270 0 trunk/read_data_flash_hdf5.f90 304 0 trunk/read_data_flash_hdf5_utils.c 245 0 trunk/read_data_foulkes.f90 975 0 trunk/read_data_gadget.f90 343 0 trunk/read_data_gadget_jsb.f90 197 0 trunk/read_data_jjm.f90 232 0 trunk/read_data_jules.f90 180 0 trunk/read_data_kitp.f90 368 0 trunk/read_data_mbate.f90 314 0 trunk/read_data_mbate_hydro.f90 339 0 trunk/read_data_mbate_mhd.f90 393 0 trunk/read_data_oilonwater.f90 342 0 trunk/read_data_rsph.f90 289 0 trunk/read_data_scw.f90 172 0 trunk/read_data_snsph.f90 115 0 trunk/read_data_snsph_utils.c 1280 0 trunk/read_data_sphNG.f90 230 0 trunk/read_data_spyros.f90 781 0 trunk/read_data_sro.f90 234 0 trunk/read_data_timli.f90 443 0 trunk/read_data_tipsy.F90 256 0 trunk/read_data_vanaverbeke.f90 260 0 trunk/render.f90 293 0 trunk/rotate.f90 36 0 trunk/scripts/cpfiles.bash 59 0 trunk/scripts/fixpgplotnames.bash 37 0 trunk/scripts/getav.pl 9 0 trunk/scripts/ppm2gif.bash 200 0 trunk/scripts/splash_parallel.pl 90 0 trunk/scripts/time_average_pdfs.f90 562 0 trunk/setpage.f90 422 0 trunk/shapes.f90 619 0 trunk/splash.f90 38 0 trunk/system_f2003.f90 39 0 trunk/system_unix.f90 39 0 trunk/system_unix_NAG.f90 145 0 trunk/system_utils.f90 281 0 trunk/tests/test_fieldlines.f90 387 0 trunk/tests/test_interpolate3D.f90 339 0 trunk/timestepping.f90 66 0 trunk/titles.f90 699 0 trunk/transform.f90 276 0 trunk/units.f90 294 0 trunk/write_data_phantom.f90 355 0 trunk/write_pixmap.f90 239 0 trunk/write_sphdata.f90 create mode 100755 trunk/INSTALL create mode 100755 trunk/INSTALL.macosx create mode 100755 trunk/LICENSE create mode 100755 trunk/Makefile create mode 100755 trunk/README create mode 100755 trunk/allocate.f90 create mode 100644 trunk/analysis.f90 create mode 100644 trunk/asciiutils.f90 create mode 100755 trunk/calc_quantities.f90 create mode 100644 trunk/colourbar.f90 create mode 100755 trunk/colourparts.f90 create mode 100755 trunk/colours.f90 create mode 100644 trunk/convert.f90 create mode 100755 trunk/defaults.f90 create mode 100644 trunk/discplot.f90 create mode 100755 trunk/docs/bibstyle.bst create mode 100755 trunk/docs/figs/colourschemes.ps create mode 100755 trunk/docs/figs/hyperbolic.ps create mode 100644 trunk/docs/figs/sedov_example.png create mode 100644 trunk/docs/figs/starpart1.png create mode 100644 trunk/docs/figs/starpart2.png create mode 100644 trunk/docs/figs/starpart3.png create mode 100644 trunk/docs/figs/starpart4.png create mode 100644 trunk/docs/figs/starpart5.png create mode 100644 trunk/docs/figs/starpart6.png create mode 100644 trunk/docs/figs/starpartfinal.png create mode 100644 trunk/docs/figs/surfdens.pdf create mode 100644 trunk/docs/figs/surfpart1.png create mode 100644 trunk/docs/figs/surfpart2.png create mode 100644 trunk/docs/figs/surfpart3.png create mode 100644 trunk/docs/figs/surfpart4.png create mode 100644 trunk/docs/figs/surfpart5.png create mode 100644 trunk/docs/figs/surfpart6.png create mode 100644 trunk/docs/figs/surfpartfinal.png create mode 100755 trunk/docs/figs/xsec2D.eps create mode 100755 trunk/docs/figs/xsec2D.fig create mode 100755 trunk/docs/figs/xsec3D.eps create mode 100755 trunk/docs/figs/xsec3D.fig create mode 100644 trunk/docs/splash.bbl create mode 100755 trunk/docs/splash.tex create mode 100644 trunk/docs/version create mode 100644 trunk/docs/version_history create mode 100644 trunk/docs/version_history_tex.tex create mode 100755 trunk/exact.f90 create mode 100755 trunk/exact_densityprofiles.f90 create mode 100755 trunk/exact_fromfile.f90 create mode 100755 trunk/exact_mhdshock.f90 create mode 100755 trunk/exact_mhdshock_other.f90 create mode 100755 trunk/exact_polytrope.f90 create mode 100755 trunk/exact_rhoh.f90 create mode 100644 trunk/exact_ringspread.f90 create mode 100755 trunk/exact_sedov.f90 create mode 100755 trunk/exact_shock.f90 create mode 100755 trunk/exact_shock_sr.f90 create mode 100755 trunk/exact_torus.f90 create mode 100755 trunk/exact_toystar1D.f90 create mode 100755 trunk/exact_toystar2D.f90 create mode 100755 trunk/exact_wave.f90 create mode 100755 trunk/fieldlines.f90 create mode 100755 trunk/geometry.f90 create mode 100755 trunk/get_data.f90 create mode 100755 trunk/globaldata.f90 create mode 100755 trunk/interactive.f90 create mode 100755 trunk/interpolate1D.f90 create mode 100755 trunk/interpolate2D.f90 create mode 100755 trunk/interpolate3D.f90 create mode 100755 trunk/interpolate3D_opacity.f90 create mode 100644 trunk/interpolate3D_projection.F90 create mode 100755 trunk/interpolate3D_xsec.f90 create mode 100755 trunk/interpolate_vec.f90 create mode 100755 trunk/isosurface.f90 create mode 100755 trunk/legends.f90 create mode 100755 trunk/limits.f90 create mode 100755 trunk/menu.f90 create mode 100755 trunk/options_data.f90 create mode 100755 trunk/options_limits.f90 create mode 100755 trunk/options_page.f90 create mode 100755 trunk/options_particleplots.f90 create mode 100755 trunk/options_powerspec.f90 create mode 100755 trunk/options_render.f90 create mode 100755 trunk/options_vecplot.f90 create mode 100755 trunk/options_xsecrotate.f90 create mode 100755 trunk/particleplot.f90 create mode 100644 trunk/pdfs.f90 create mode 100755 trunk/plotstep.f90 create mode 100644 trunk/plotutils.f90 create mode 100755 trunk/powerspectrums.f90 create mode 100755 trunk/prompting.f90 create mode 100755 trunk/read_data_UCLA.f90 create mode 100755 trunk/read_data_VINE.f90 create mode 100755 trunk/read_data_ascii.f90 create mode 100644 trunk/read_data_bauswein.f90 create mode 100755 trunk/read_data_dansph.f90 create mode 100755 trunk/read_data_dansph_old.f90 create mode 100644 trunk/read_data_dragon.f90 create mode 100755 trunk/read_data_flash_hdf5.f90 create mode 100755 trunk/read_data_flash_hdf5_utils.c create mode 100755 trunk/read_data_foulkes.f90 create mode 100755 trunk/read_data_gadget.f90 create mode 100755 trunk/read_data_gadget_jsb.f90 create mode 100755 trunk/read_data_jjm.f90 create mode 100755 trunk/read_data_jules.f90 create mode 100644 trunk/read_data_kitp.f90 create mode 100755 trunk/read_data_mbate.f90 create mode 100755 trunk/read_data_mbate_hydro.f90 create mode 100755 trunk/read_data_mbate_mhd.f90 create mode 100755 trunk/read_data_oilonwater.f90 create mode 100755 trunk/read_data_rsph.f90 create mode 100755 trunk/read_data_scw.f90 create mode 100644 trunk/read_data_snsph.f90 create mode 100644 trunk/read_data_snsph_utils.c create mode 100755 trunk/read_data_sphNG.f90 create mode 100755 trunk/read_data_spyros.f90 create mode 100755 trunk/read_data_sro.f90 create mode 100755 trunk/read_data_timli.f90 create mode 100644 trunk/read_data_tipsy.F90 create mode 100644 trunk/read_data_vanaverbeke.f90 create mode 100755 trunk/render.f90 create mode 100755 trunk/rotate.f90 create mode 100755 trunk/scripts/cpfiles.bash create mode 100755 trunk/scripts/fixpgplotnames.bash create mode 100755 trunk/scripts/getav.pl create mode 100755 trunk/scripts/ppm2gif.bash create mode 100755 trunk/scripts/splash_parallel.pl create mode 100644 trunk/scripts/time_average_pdfs.f90 create mode 100755 trunk/setpage.f90 create mode 100755 trunk/shapes.f90 create mode 100644 trunk/splash.f90 create mode 100755 trunk/system_f2003.f90 create mode 100755 trunk/system_unix.f90 create mode 100755 trunk/system_unix_NAG.f90 create mode 100644 trunk/system_utils.f90 create mode 100644 trunk/tests/test_fieldlines.f90 create mode 100755 trunk/tests/test_interpolate3D.f90 create mode 100755 trunk/timestepping.f90 create mode 100755 trunk/titles.f90 create mode 100755 trunk/transform.f90 create mode 100644 trunk/units.f90 create mode 100644 trunk/write_data_phantom.f90 create mode 100644 trunk/write_pixmap.f90 create mode 100644 trunk/write_sphdata.f90 2009-07-17 dprice * : making trunk,tags,branches dirs git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@3 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-07-16 Daniel Price * Makefile: minor reformatting 2009-07-15 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.12.2 2009-07-15 dprice * exact_wave.f90: does not multiply by ymean if it is zero; warns if period -ve 2009-07-15 dprice * splash.f90: v1.12.2 description, plus uses argument 0 in usage 2009-07-15 dprice * docs/splash.bbl, docs/splash.tex: docs updated for v1.12.2 2009-07-15 dprice * options_particleplots.f90, particleplot.f90: variable marker sizes implemented; can plot particles as filled or outlined circles with radius proportional to h 2009-07-15 dprice * Makefile: added write_data_phantom.f90 to source files 2009-07-15 dprice * convert.f90, write_data_phantom.f90, write_sphdata.f90: convert to phantom dump format implemented 2009-07-15 dprice * globaldata.f90: single precision defined; bug fix with int8 definition 2009-07-15 dprice * system_utils.f90: lenvironment accepts 1 as true 2009-07-15 dprice * plotstep.f90: bug fix with opacity-based rendering + dark matter 2009-07-15 dprice * read_data_gadget.f90: BUG FIX: creates h and rho columns when using GSPLASH_DARKMATTER_HSOFT with block-labelled data read; also read from .hsml and .dens files implemented better -- dark matter rendering now works OK in this case 2009-07-14 dprice * menu.f90, options_render.f90: bug fix with contour plotting and icolours=0; behaviour better defined; contour prompt does not appear in this case 2009-07-14 dprice * plotstep.f90: interpolation weights only set if needed; avoids pointless warnings, also bug fix with contour plotting and icolours=0 2009-07-14 dprice * read_data_VINE.f90: implemented read of star/point mass particles from VINE dumps 2009-07-14 dprice * read_data_ascii.f90: does not override vector labels 2009-07-13 dprice * defaults.f90, get_data.f90, globaldata.f90, menu.f90, options_data.f90: does not override coordinate and vector labels by default (unless not set) 2009-07-13 dprice * read_data_sphNG.f90: speed improvements for low memory mode - skips faster if no columns need to be read 2009-07-13 dprice * read_data_ascii.f90: r and z recognised as coordinates 2009-07-13 dprice * interactive.f90: cursor position now saved relative to viewport, stops weird cursor jumping 2009-07-13 dprice * allocate.f90: spaces removed 2009-06-25 dprice * read_data_sphNG.f90: BUG FIX with B field read in sphNG 2009-06-11 dprice * plotstep.f90: debug mode; temporary arrays allocated one at a time; low memory mode does not allocate temp arrays if not needed 2009-06-11 dprice * menu.f90: minor bug fix with irendermulti resetting before prompt (icolpixmap related) 2009-06-11 dprice * write_pixmap.f90: oops 2009-06-11 dprice * write_pixmap.f90: better warning if cant find .pix files 2009-06-11 dprice * particleplot.f90: allow arbitrary sizes for input arrays; gracefully warns if z array size too small 2009-06-11 dprice * read_data_sro.f90, setpage.f90: compiler warnings fixed 2009-06-11 dprice * globaldata.f90, splash.f90: debug mode added 2009-06-10 dprice * plotstep.f90: bug fix with tiling decision for pixmap plotting 2009-06-10 dprice * globaldata.f90, menu.f90, plotstep.f90, splash.f90, write_pixmap.f90: -readpix option added for plotting grid based data alongside SPH, limited formats at present 2009-06-10 dprice * asciiutils.f90: basename function added for stripping directory names 2009-06-01 dprice * read_data_dansph.f90: bug fix with garbled geomfile in header printing 2009-05-25 dprice * read_data_timli.f90: better memory reallocation 2009-05-21 dprice * plotstep.f90: shocking bug with saving of number of pixels and automatic pixel numbers fixed 2009-05-20 dprice * globaldata.f90, read_data_oilonwater.f90: units in rsun and msun for oilonwater; module physcon added 2009-05-20 dprice * plotstep.f90: temporary arrays are explicitly allocated instead of being left up to the compiler 2009-05-19 dprice * Makefile, read_data_oilonwater.f90: added new oilonwater read for Ross Church, Melvyn Davies 2009-05-19 dprice * analysis.f90, plotstep.f90: bug fixes blocking compilation with ifort8 2009-05-14 dprice * read_data_sphNG.f90: bug fix with reading Bens small dumps 2009-05-14 dprice * read_data_sphNG.f90: few more debugging statements added 2009-05-11 dprice * read_data_gadget.f90: lcase in asciiutils module 2009-05-11 dprice * plotstep.f90: bug fix with plot tiling; buffer for line width changes removed 2009-05-08 dprice * INSTALL: added some extra bits; simplified others 2009-05-07 dprice * Makefile, read_data_timli.f90: added timli data read 2009-05-07 dprice * options_particleplots.f90, particleplot.f90, plotstep.f90: error bar plotting implemented for particle plots 2009-04-30 dprice * read_data_ascii.f90: guessing of column identities from columns file is case-insensitive 2009-04-30 dprice * analysis.f90, asciiutils.f90, convert.f90, get_data.f90, prompting.f90: functions ucase, lcase moved from prompting module into asciiutils module 2009-04-29 dprice * particleplot.f90: memory only allocated for temporary arrays when required (when plotting circles of interaction as error bars) 2009-04-27 dprice * asciiutils.f90, docs/splash.bbl: minor changes 2009-04-22 dprice * read_data_ascii.f90: better error handling on bad lines - carries on reading but gives warning 2009-04-21 dprice * Makefile, read_data_snsph.f90, read_data_snsph_utils.c: snsph data read added 2009-04-21 dprice * read_data_tipsy.F90: works with ifort 10.0.0 stream access which is still old style 2009-04-20 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.12.1 2009-04-20 dprice * docs/version_history, docs/version_history_tex.tex: version 1.12.1 2009-04-20 dprice * splash.f90: v1.12.1 2009-04-20 dprice * docs/splash.tex: extra GSPLASH env variables added to docs 2009-04-20 dprice * read_data_gadget.f90: added GSPLASH_IGNORE_IFLAGCOOL and GSPLASH_HSML_COLUMN 2009-04-20 dprice * INSTALL, INSTALL.macosx: updated install instructions 2009-04-20 dprice * docs/splash.tex: docs updated for v1.12.1 2009-04-20 dprice * setpage.f90: bug fix with round-off error on exact pixel boundary selection 2009-04-20 dprice * analysis.f90: BUG FIXES with analysis mode -- thanks to Florian Buerzle. Format statements now work with arbitrary numbers of columns (up to 999), also massaboverho analysis works with masses in dump file headers 2009-04-08 dprice * colourbar.f90: bug fix with separation in colour bar label 2009-03-30 dprice * write_sphdata.f90: bug fix with convert to rsph format 2009-03-27 dprice * write_sphdata.f90: convert to Steinars rsph file format added 2009-03-25 dprice * calc_quantities.f90: dudtrad added 2009-03-23 dprice * plotstep.f90, render.f90: removed need to render twice when contour limits differ from render limits 2009-03-23 dprice * splash.f90: date/version bumped 2009-03-23 dprice * limits.f90: allow contour max=min 2009-03-23 dprice * interactive.f90, limits.f90, menu.f90, plotstep.f90: ability to set contour limits separately from render limits for same quantity implemented 2009-03-23 dprice * render.f90: bug fix with formatting of contour limits 2009-03-19 dprice * splash.f90: bug fix with broken -d and -l command line options 2009-03-11 dprice * splash.f90: version 1.12.1beta 2009-03-11 dprice * docs/figs/colourschemes.ps: new colour schemes added to figure 2009-03-11 dprice * colourbar.f90, interactive.f90, shapes.f90: can interactively edit text shapes and the colour bar label 2009-03-11 dprice * asciiutils.f90, options_render.f90, plotstep.f90: option to customize label on projection plots implemented 2009-03-11 dprice * Makefile: dependencies added to Makefile properly, still try to get files in the right order 2009-03-06 dprice * plotutils.f90, render.f90: formatreal utility added; used for contour level labels; also character height of contour labels increased 2009-03-06 dprice * get_data.f90: label check done after every get_labels call; added SPLASH_HMIN_CODEUNITS environment variable to set minimum h 2009-03-06 dprice * options_render.f90, plotstep.f90, render.f90: option added for numeric labelling of contours 2009-03-06 dprice * render.f90: contour levels set better: exactly on min and max apart from with nc=1 which uses min; also prints out full list of levels 2009-02-11 pgdixon * : new dir splash git-svn-id: https://svn-vre.its.monash.edu.au/mathsci/splash@2 cab04810-efc7-4a10-8ecf-f366c833a2ad 2009-02-07 dprice * plotstep.f90: bug fix with separate types in contours 2009-02-06 dprice * read_data_sphNG.f90: tabs removed 2009-02-06 dprice * exact_shock_sr.f90: compiler warnings (unused variables, no intents) fixed 2009-02-06 dprice * exact_sedov.f90, exact_toystar1D.f90, exact_toystar2D.f90, interactive.f90, interpolate3D.f90, interpolate3D_projection.F90, menu.f90, options_data.f90, plotstep.f90, prompting.f90, splash.f90, system_utils.f90: compiler warnings (unused variables, no intents) fixed 2009-02-06 dprice * docs/splash.tex: docs updated with command line options 2009-02-06 dprice * read_data_dansph.f90: minor change to the way it prints geometry info 2009-02-06 dprice * prompting.f90: mask= added to print_logical 2009-02-06 dprice * Makefile, options_particleplots.f90, plotstep.f90: contours of different particle type to rendered quantity implemented 2009-02-05 dprice * read_data_sphNG.f90: bug fix with seg fault in low memory mode 2009-02-05 dprice * read_data_sphNG.f90: location of u in mhd small dumps known 2009-02-05 dprice * read_data_sphNG.f90: new location for u in small dumps 2009-02-05 dprice * read_data_sphNG.f90: fixed labelling on pure RT small dumps 2009-02-05 dprice * read_data_sphNG.f90: ilocpmassinitial is 23 for small sphNG dumps; 15 for phantom 2009-02-05 dprice * splash.f90: updated contact details, year 2009-01-29 dprice * read_data_sphNG.f90: bug fix with MHD_RT small dumps; debug as environment variable 2009-01-15 dprice * system_utils.f90: BUG FIX with integer environment variable read 2008-12-22 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.12.0 2008-12-22 dprice * splash.f90: v1.12.0 2008-12-22 dprice * read_data_tipsy.F90: bug fix with preprocessing of ifort checks 2008-12-22 dprice * read_data_tipsy.f90 => read_data_tipsy.F90: added preprocessor option for older ifort compilers + stream access 2008-12-22 dprice * splash.f90: no longer prompts for filenames (obsolete), instead prints usage; also bug fix with command-line plot invocation - if interactive device is invoked, will enter menu instead of quitting 2008-12-16 dprice * read_data_tipsy.f90: access=stream for ifort 9 added (will comment later) 2008-12-10 dprice * plotstep.f90, shapes.f90: BUG FIX: transforms applied to shape coordinates 2008-12-09 dprice * read_data_sphNG.f90: bug fix with Phantom MPI read (missing particles) 2008-12-08 dprice * scripts/time_average_pdfs.f90: bug fix with time averaged pdf calculation 2008-12-08 dprice * plotstep.f90: bug fix with required array for PDF plots 2008-12-07 dprice * pdfs.f90, plotstep.f90: ability to manually set the number of bins for pdf plots 2008-12-07 dprice * scripts/time_average_pdfs.f90: f90 script to produce time averaged pdfs from a bunch of SPLASH PDF files 2008-12-05 dprice * transform.f90: ln transformation added 2008-12-05 dprice * transform.f90: ln transformation added 2008-12-05 dprice * read_data_flash_hdf5_utils.c: spaces removed from SPH density dataset name 2008-12-05 dprice * asciiutils.f90: uses tiny instead of arbitrary number 2008-12-05 dprice * pdfs.f90: bug fix if normalisation is zero in PDF 2008-12-05 dprice * menu.f90: bug fix with PDFs in multiplot 2008-12-05 dprice * read_data_flash_hdf5.f90, read_data_flash_hdf5_utils.c: updated flash tracer particle read/write (reads SPH density dataset if present) 2008-12-01 dprice * docs/splash.tex, splash.f90: documentation for -x -y -render -vec -cont -dev added 2008-12-01 dprice * read_data_flash_hdf5.f90, read_data_flash_hdf5_utils.c: partial data reading+low memory mode implemented for FLASH tracer particles format 2008-12-01 dprice * Makefile: typo in HDF5INCLUDE fixed 2008-12-01 dprice * read_data_flash_hdf5.f90, read_data_flash_hdf5_utils.c: puts particles in id order, uses bind(c) for linking with c (more portable) 2008-12-01 dprice * Makefile: FLASH code +HDF5 compiling and linking; also LDFLAGS always put at the end of the compile-time line (seems to be more standard) 2008-12-01 dprice * read_data_flash_hdf5.f90, read_data_flash_hdf5_utils.c: read data routine added for FLASH code tracer particles (hdf5 format). HDF5 requires linking with HDF5 libraries and compiling/calling c routine 2008-11-30 dprice * asciiutils.f90: uses len not len_trim, should be more portable 2008-11-27 dprice * interactive.f90: ctrl-C in interactive window kills it 2008-11-27 dprice * asciiutils.f90: function to convert a fortran string into a c string added 2008-11-27 dprice * limits.f90: formatted output 2008-11-27 dprice * plotstep.f90, shapes.f90: shapes can be plotted on only some panels, or first row, first column etc 2008-11-26 dprice * read_data_gadget.f90: oops! now bug really fixed in block-labelled read 2008-11-26 dprice * system_utils.f90: more robust integer environment variable read 2008-11-26 dprice * menu.f90: bug fix with allowrendering if mass not present 2008-11-26 dprice * read_data_gadget.f90: BUG FIX with block labelled gadget read if BFLD present 2008-11-26 dprice * options_xsecrotate.f90, plotstep.f90: BUG FIX: cross section slice thickness is saved - no longer resets in prompt 2008-11-21 dprice * calc_quantities.f90: calculates gas temperature in K for barytropic EOS runs 2008-11-21 dprice * read_data_sphNG.f90: bug fix with reading of B from sphNG files 2008-11-21 dprice * read_data_sphNG.f90: minor bug fix with labelling 2008-11-20 dprice * read_data_ascii.f90: bug fix with auto-labelling: ndimV = 0 if ndim = 0 2008-11-13 dprice * options_render.f90: nicer printing of logical options after they have been set 2008-11-12 dprice * defaults.f90, globaldata.f90, menu.f90, plotstep.f90, splash.f90, timestepping.f90, transform.f90: contour plotting of second quantity (ie. different to rendered quantity) implemented; tested and seems to work OK 2008-11-12 dprice * units.f90: bug fix with units for calculated quantities not being read properly from units file 2008-11-12 dprice * limits.f90: improved formatting on min=max warning after reading limits file; less repeated code 2008-11-12 dprice * options_data.f90: bug fix if only changing unit labels: now prompts to write units file 2008-11-11 dprice * interpolate3D_projection.F90: atomic statements added to openMP parallelism - fixes "black dots" bug 2008-11-11 dprice * options_data.f90: prints info about z integration unit 2008-11-11 dprice * plotstep.f90: bug fix with normalised column integrated plots if unit for z integration set 2008-11-11 dprice * scripts/cpfiles.bash: cpfiles script added for renaming .defaults, .limits files etc 2008-11-10 dprice * plotstep.f90: can plot step legend on panels other than the first one 2008-11-10 dprice * options_page.f90, timestepping.f90: options for finer control of line-style changing / line colour changing added 2008-11-10 dprice * plotstep.f90, splash.f90: bug fixes with command line plot/device specification 2008-11-10 dprice * defaults.f90, menu.f90, options_page.f90, plotstep.f90, splash.f90, timestepping.f90: command line plotting implemented: -x, -y, -render and -vecplot can be used to specify the plot from the command line (ie. no prompts needed), also -dev to specify the pgplot device 2008-11-07 dprice * asciiutils.f90: bug fix in format statement 2008-11-07 dprice * Makefile, analysis.f90, convert.f90, splash.f90: "splash calc" command line utility implemented; calculates energies, min,max,mean of all columns and mass above rho vs time using all the dump files on the command ine 2008-11-07 dprice * write_sphdata.f90: minor changes to formatting 2008-11-07 dprice * asciiutils.f90: read_asciifile now generic interface that can return either array of strings or array of reals from an ascii file 2008-11-07 dprice * get_data.f90: blank filenames not allowed in prompt 2008-10-30 dprice * read_data_sphNG.f90: debug flag set back to false 2008-10-21 dprice * read_data_sphNG.f90: bug fixes with RT+MHD small dump read 2008-10-21 dprice * read_data_sphNG.f90: minor change to low memory mode 2008-10-20 dprice * interactive.f90: backspace key added to interactive mode - progressively removes annotation from the plot (ie. axes and colour bar, legends, titles, shapes, scale) - legends and titles can be restored by pressing G, T and H for each 2008-10-16 dprice * system_utils.f90: BUG FIX with envlist routine (cause of seg fault in GADGET read): thanks to Daniel Cunnama 2008-10-13 dprice * docs/version_history, docs/version_history_tex.tex: version 1.11.1 2008-10-13 dprice * docs/version_history, docs/version_history_tex.tex: version 1.11.1 2008-10-13 dprice * docs/version_history_tex.tex: version 1.11.1 2008-10-13 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.11.1 2008-10-13 dprice * splash.f90: v1.11.1 2008-10-13 dprice * docs/splash.tex: docs updated for v1.11.1 2008-10-13 dprice * transform.f90: function interface; not used yet 2008-10-13 dprice * plotstep.f90: sends brightness into particleplot for opacity rendering; also origin sent into interactive routines 2008-10-13 dprice * interpolate2D.f90, particleplot.f90: sets brightness to 1. if present for writing sinks direct to ppm 2008-10-13 dprice * read_data_foulkes.f90: bugs fixed; only reads up to smoothing length by default 2008-10-13 dprice * colourbar.f90: only does split-colourbar workaround if > 1024 pixels actually needed 2008-10-13 dprice * setpage.f90: bug fix: tol reduced when finding exact pixel boundaries 2008-10-10 dprice * Makefile, read_data_foulkes.f90: read_data added for steve foulkes 2008-10-10 dprice * interactive.f90: o in interactive mode recentres plot on origin (ie. not zero if origin set to something different 2008-10-07 dprice * colourbar.f90: one-sided colour bars implemented as both vertical or horizontal 2008-10-07 dprice * plotstep.f90: npix = 1024/nacross in auto mode for pixel devices 2008-10-07 dprice * plotstep.f90: automatic resolution determination limited to 800/nacross on vector devices 2008-10-07 dprice * plotstep.f90: automatic resolution determination limited to 400/nacross on vector devices 2008-10-07 dprice * plotstep.f90: bug fix with new colour bar plot order: now uses foreground colour again 2008-10-02 dprice * read_data_sphNG.f90: less verbose header printout for MPI dumps 2008-10-02 dprice * shapes.f90: shapes can be plotted relative to viewport for each panel as well as using plot coords; also longer text string allowed 2008-09-26 dprice * prompting.f90: blank="blank" in prompt, not null= 2008-09-26 dprice * Makefile, colourbar.f90, setpage.f90: colour bar lies on exact pixel boundaries; calls same routine as setpage to determine this 2008-09-26 dprice * plotstep.f90: frame changes on a per-page basis, not per dump file; does not quite work with interactive mode step navigation 2008-09-25 dprice * plotstep.f90: small page buffer if box is drawn to allow for line width changes 2008-09-25 dprice * plotstep.f90, titles.f90: lensteplegend a parameter to avoid pgf90 bug 2008-09-25 dprice * plotstep.f90: colour bar now plotted AFTER renderings, so can in principle plot colour bar on top of rendered plot 2008-09-25 dprice * plotstep.f90: lower stacksize footprint; renderplot now only allocated when necessary (ie. for rendering with particle colouring instead of pixels) 2008-09-24 dprice * read_data_sphNG.f90: parallelisation uses default(none) 2008-09-24 dprice * Makefile: pgf90 flags updated 2008-09-24 dprice * get_data.f90: compiles with pgf90 2008-09-24 dprice * globaldata.f90, plotstep.f90: workaround for crash in pgf90 compiler; now runs OK 2008-09-23 dprice * options_render.f90: auto npix back on by default 2008-09-23 dprice * read_data_sphNG.f90: ssplash_omegat environment variable added 2008-09-18 dprice * menu.f90, splash.f90: v1.11.1alpha; contact details updated to Monash 2008-09-18 dprice * options_render.f90: npix=200 by default for 1.11.1alpha 2008-09-18 dprice * read_data_gadget.f90: BUG FIX with block-labelled GADGET read if mass array not present; also labelling added for every column that appears in io.c in GADGET-3 2008-09-18 dprice * plotstep.f90: minor bug fixes with interpolation weights when mass not read from dump file 2008-09-17 dprice * exact_shock_sr.f90: minor formatting 2008-09-10 dprice * Makefile, exact.f90, exact_shock_sr.f90, riemann_sr.f: special relativistic shock tube exact solution added 2008-09-07 dprice * plotstep.f90: BUG fix with particle coordinates in different coordinate systems 2008-09-05 dprice * options_page.f90: line width allowed to be >5 2008-09-03 dprice * get_data.f90: rendering works if mass not read as column (just as a single number for each type); works only for sphNG read at the moment in low-memory mode; endian check added but commented 2008-09-03 dprice * menu.f90, read_data_sphNG.f90: rendering works if mass not read as column (just as a single number for each type); works only for sphNG read at the moment in low-memory mode 2008-09-03 dprice * plotstep.f90: bug fix with circles of interaction on particle plots 2008-09-03 dprice * Makefile: changes committed by mistake reverted 2008-09-03 dprice * options_render.f90, setpage.f90: automatic pixel numbers re-activated (will appear in v1.11.1); bug fix with multiple panels 2008-09-02 dprice * Makefile, read_data_jjm.f90, read_data_jules.f90: jules data read added 2008-08-15 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.11.0 2008-08-15 dprice * splash.f90: v1.11.0 2008-08-15 dprice * plotstep.f90: bug fix with auto pixel selection 2008-08-15 dprice * docs/splash.tex: extra info re: data reads/environment variables added 2008-08-15 dprice * read_data_gadget.f90: BUG FIX with reading masses from gadget files if particle mass < 1.e-8 (thanks to Thomas Grief); also GSPLASH_CHECKIDS added 2008-08-15 dprice * read_data_VINE.f90: minor changes to VINE format: VSPLASH_MHD and VINE_MHD both work; VSPLASH_HFAC added as option for compatibility with older vine output 2008-08-15 dprice * options_render.f90: automatic pixel numbers disabled for v1.11.0 2008-08-15 dprice * setpage.f90: exact pixel boundaries improved; disabled for v1.11.0 2008-08-15 dprice * plotstep.f90: bug fixes, improvements to automatic pixel selection 2008-08-15 dprice * Makefile, read_data_UCLA.f90: data read for Sky King (UCLA) added 2008-08-15 dprice * options_render.f90: bug fix: prompt for label distance appears for plot-hugging colour bars 2008-08-07 dprice * plotstep.f90: bug fix with frames not changing 2008-08-06 dprice * exact_sedov.f90: out-of-bounds error fixed at t=0 2008-07-28 dprice * plotstep.f90: BUG FIX with velocity components in different coordinate systems (VPHI WAS WRONG!) - thanks to Giuseppe Lodato 2008-07-22 dprice * exact_shock_sr.f90, riemann_sr.f: exact solution for special relativistic riemann problem 2008-07-21 dprice * options_render.f90, plotstep.f90: AUTO-SELECT npix so pixels exactly match those on device (MUCH SMOOTHER PLOTS WITH NO ARTIFACTS); rendering of sinks to ppm implemented 2008-07-21 dprice * interpolate2D.f90, particleplot.f90: rendering of sinks to ppm files implemented (slightly inflexible at the moment) 2008-07-21 dprice * setpage.f90: viewport automatically adjusted to lie exactly on pixel boundaries (no blank spaces) 2008-07-10 dprice * interactive.f90: bug fix with C 2008-07-10 dprice * interactive.f90: C key re-centres plot on cursor in interactive mode 2008-07-10 dprice * allocate.f90: bug fix with reallocation of iamtype variable 2008-07-10 dprice * read_data_sphNG.f90: divvcol read only if required 2008-07-10 dprice * read_data_sphNG.f90: star particle type added for sphNG read 2008-07-04 dprice * read_data_sphNG.f90: bug fix with reading phantom small dumps + MPI 2008-06-25 dprice * docs/splash.tex, options_page.f90: documentation added for shapes; better scale menu option 2008-06-25 dprice * shapes.f90: bug fixes with shape plotting 2008-06-24 dprice * read_data_sphNG.f90: prints RK2, tff, dtmax, rhozero as read from sphNG header 2008-06-24 dprice * exact_polytrope.f90: bug fix: handles gamma 4/3 case now 2008-06-24 dprice * exact.f90: errors in residuals collected into one warning 2008-06-19 dprice * options_render.f90: no prompt for label if no colour bar plotted 2008-06-05 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.11.0beta 2008-06-05 dprice * splash.f90: v1.11.0beta 2008-06-05 dprice * read_data_sphNG.f90: check for zero particle mass in Phantom dumps 2008-06-05 dprice * read_data_ascii.f90: local file called columns takes precedence over ASPLASH_COLUMNSFILE environment variable 2008-06-05 dprice * interactive.f90, plotstep.f90: v,V,w and H implemented in interactive mode on multiple-steps-per-page/multiple panels 2008-06-05 dprice * colourbar.f90, options_render.f90: vertical and horizontal plot-hugging colour bars implemented; option to turn off colour bar label added 2008-06-05 dprice * menu.f90: BUG FIX with multiplots made in 10.5.2 2008-06-03 dprice * read_data_sphNG.f90: correct labelling for MHD+RT dumps 2008-06-02 dprice * plotstep.f90: bug fix with interactive mode + PDF plotting 2008-05-14 dprice * Makefile: plotutils added to makefile 2008-05-13 dprice * transform.f90: utility function to convert log to ln in transforms 2008-05-13 dprice * calc_quantities.f90: imri bug fix + better label for ipmag 2008-05-13 dprice * asciiutils.f90, pdfs.f90: pdf file name removes slashes, spaces and escape characters from label 2008-05-13 dprice * read_data_sphNG.f90: uninitialised bug fix in phantom reads 2008-05-12 dprice * read_data_sphNG.f90: reads .divv file if present (Phantom) 2008-05-12 dprice * pdfs.f90, plotstep.f90: write_pdf routine improved 2008-05-12 dprice * plotutils.f90: plotutils beginning of plot api for generic backend; also line plotting with blanking 2008-05-12 dprice * transform.f90: generic interface added; interface routines to handle single real numbers added 2008-05-12 dprice * read_data_kitp.f90: minor change 2008-05-09 dprice * globaldata.f90: ipdf label 2008-05-09 dprice * Makefile, menu.f90, pdfs.f90, plotstep.f90: PDF plotting implemented 2008-05-09 dprice * defaults.f90, interactive.f90, limits.f90, options_limits.f90, plotstep.f90, splash.f90: parameter range restriction implemented; saves to limits file; also in interactive mode using x, y and r keys after click (R to reset) 2008-05-09 dprice * menu.f90: noblank on fileprefix setting 2008-05-09 dprice * prompting.f90: string prompts accept blank to set null string, noblank optional argument 2008-05-09 dprice * asciiutils.f90: ncolumnsline public function instead of subroutine 2008-05-08 dprice * plotstep.f90: shape plotting call uncommented 2008-05-08 dprice * options_page.f90: plot shapes option added to g) menu 2008-05-08 dprice * defaults.f90: shape options read from defaults file; other changes 2008-05-08 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.10.2 2008-05-08 dprice * docs/splash.tex, splash.f90: v1.10.2 2008-05-08 dprice * options_particleplots.f90, particleplot.f90: option to change plotting order of particle types implemented 2008-05-08 dprice * plotstep.f90: bug fix with array-out-of-bounds in low memory mode + not rendering 2008-05-08 dprice * splash.f90: date updated 2008-05-08 dprice * read_data_vanaverbeke.f90: read_data update for sigfried; new header info read 2008-05-08 dprice * read_data_vanaverbeke.f90: read_data update for sigfried; new header info read 2008-05-08 dprice * discplot.f90, plotstep.f90: toomre Q now correct for ideal gas equation of state (uses gamma from dump file) 2008-05-08 dprice * docs/splash.tex: updated docs with surface density plot info 2008-05-08 dprice * get_data.f90: bug fix with units resetting when turning on/off even if units file present 2008-05-08 dprice * plotstep.f90: bug fix with a) on multiple-plots per page/surface density plots 2008-05-07 dprice * write_sphdata.f90: simple unformatted binary format added as sph conversion option 2008-05-07 dprice * write_pixmap.f90: pixmap write in ascii format uses format statement to prevent ifort rubbish 2008-05-01 dprice * convert.f90, write_sphdata.f90: ascii file conversion writes header with dump file information in it 2008-05-01 dprice * shapes.f90: new shapes module; not yet added as a feature 2008-05-01 dprice * discplot.f90, menu.f90, plotstep.f90: bug fixes with disc quantity plotting (interactive mode, multiple plots per page, zoom) 2008-04-30 dprice * splash.f90: bumped version number to 1.10.2beta 2008-04-30 dprice * Makefile: discplot added to Makefile 2008-04-30 dprice * discplot.f90, globaldata.f90, menu.f90, plotstep.f90: calculation of surface density and Toomre parameter implemented 2008-04-30 dprice * get_data.f90, options_data.f90: splash.columns file can be used to override default column labels 2008-04-30 dprice * Makefile, read_data_vanaverbeke.f90: read data routine for sigfried vanaverbeke added 2008-04-15 dprice * colours.f90: colour schemes from flash code added 2008-04-01 dprice * docs/splash.bbl: updated refs 2008-04-01 dprice * read_data_sphNG.f90: ugly common block removed; bug fixes with labelling of phantom MHD dumps 2008-03-30 dprice * read_data_ascii.f90: ASPLASH_COLUMNSFILE environment variable added 2008-03-17 dprice * INSTALL, INSTALL.macosx: comment about stacksize added to install instructions 2008-03-12 dprice * menu.f90: nacross and ndown not changed if OK when re-setting multiplot 2008-03-11 dprice * docs/splash.tex: version history spans page 2008-03-11 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.10.1 2008-03-11 dprice * read_data_sphNG.f90: reads all velocities if any required 2008-03-11 dprice * read_data_ascii.f90: bug fix with header lines by environment variable 2008-03-11 dprice * splash.f90: v1.10.1 2008-03-11 dprice * plotstep.f90: only velocities relative to tracked particle in different coordinate systems (not B) 2008-03-11 dprice * read_data_gadget.f90: block labelled GADGET data read implemented (GSPLASH_FORMAT=2) 2008-03-11 dprice * docs/splash.tex: updated docs for v1.10.1 2008-03-11 dprice * options_particleplots.f90: default marker for type 5 is * 2008-03-07 dprice * asciiutils.f90, read_data_ascii.f90: number of header lines can be overwritten by environment variable ASPLASH_NHEADERLINES 2008-03-03 dprice * read_data_gadget.f90: gsplash will read additional .hsml and/or .dens files if present containing smoothing length and density for dark matter particles 2008-02-29 dprice * read_data_mbate.f90: bug fix with single/double precision detection 2008-02-28 giuseppe * Makefile: frecord-marker issue 2008-02-27 dprice * read_data_sphNG.f90: more robust single/double precision determination 2008-02-27 dprice * Makefile: exact_ringspread added to Makefile 2008-02-26 dprice * exact.f90, exact_ringspread.f90: Lynden-Bell & Pringle ring spreading exact solution added 2008-02-26 dprice * plotstep.f90: bug fix with mem allocation/size checking with recent vector+rotation fix 2008-02-20 dprice * options_particleplots.f90: itrans resets when changing coordinate systems 2008-02-15 dprice * plotstep.f90: minor bug fixes in previous changes/openmp 2008-02-15 dprice * docs/splash.tex: docs updated for gadget environment variables 2008-02-15 dprice * system_utils.f90: envlist subroutine for extracting list of strings from an environment variable 2008-02-15 dprice * read_data_gadget.f90: extra columns environment variable for gadget read, also star particle information can be read 2008-02-15 dprice * plotstep.f90: BUG FIX with rotation + vector plots: vector components now rotate correctly 2008-02-15 dprice * exact_shock.f90: tabs removed 2008-02-12 dprice * interactive.f90, options_limits.f90, plotstep.f90: other coordinate system radii/velocities are now calculated relative to tracked particle (if particle tracking set) 2008-02-12 dprice * read_data_kitp.f90: read format for KITP comparison project 2008-01-24 dprice * convert.f90: bug fix: convert does calculated quantities now also 2008-01-24 dprice * read_data_sphNG.f90: better labelling of columns for rt and mhd : now always gets start of block right 2008-01-22 dprice * Makefile, convert.f90, splash.f90, write_sphdata.f90: implemented command line SPH dump file conversion (splash to ascii only at present) 2008-01-22 dprice * defaults.f90, globaldata.f90: reset_columnids routine added; minor 2008-01-18 dprice * interpolate3D_projection.F90: catches error causing segfault if NaNs or Infs for q2 in interpolation 2007-12-28 dprice * read_data_sphNG.f90: bug fix with int*8 skipping; reads MPI-Phantom dumps 2007-12-19 dprice * calc_quantities.f90: bug fix in mach number calculation if ivx not set (causes seg fault) 2007-12-13 dprice * docs/splash.tex: updated docs with square xy limits option 2007-12-13 dprice * options_page.f90, plotstep.f90: option for non-square coordinate axes implemented 2007-12-10 dprice * read_data_sphNG.f90: bug fix with sinks in MPI dump read 2007-12-05 dprice * particleplot.f90: bug fix: blocks compilation on NAG: thanks to Andrew McLeod 2007-12-05 dprice * particleplot.f90: bug fix: blocks compilation on NAG: thanks to Andrew McLeod 2007-11-29 dprice * docs/version_history_tex.tex: version 1.10.0 2007-11-29 dprice * globaldata.f90, options_data.f90, transform.f90: compiler warnings fixed 2007-11-29 dprice * calc_quantities.f90: minor bug fix 2007-11-29 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.10.0 2007-11-29 dprice * splash.f90: v1.10 2007-11-29 dprice * docs/splash.tex: userguide updated for version 1.10 2007-11-28 dprice * plotstep.f90: will tile if rendered + adaptive limits but no colour bar plotted 2007-11-28 dprice * render.f90: bug fixes in interface 2007-11-27 dprice * colours.f90: Bate original replaced by reinstated IDL blue-white 2007-11-25 dprice * docs/splash.tex: menu options updated 2007-11-25 dprice * globaldata.f90, splash.f90: better fileprefix changing; implemented save-as from main menu 2007-11-25 dprice * Makefile: intelmac variable added 2007-11-25 dprice * menu.f90, options_page.f90: legend menu made separate; cut-down menu if ndim<2 2007-11-25 dprice * colourbar.f90, plotstep.f90, setpage.f90: bugs with horizontal colour bars fixed 2007-11-25 dprice * write_pixmap.f90: prints usage for -o format 2007-11-25 dprice * INSTALL, INSTALL.macosx: updated and improved install instructions 2007-11-22 dprice * get_data.f90: does not call set_labels if no data read 2007-11-22 dprice * allocate.f90: bug fix in itype allocation 2007-11-20 dprice * read_data_dragon.f90: working data read for dragon code (both binary and ascii formats) 2007-11-20 dprice * allocate.f90, globaldata.f90, particleplot.f90, plotstep.f90, read_data_sphNG.f90, timestepping.f90: disordered particle type storage implemented, used in dragon and sphNG reads at present; remains optional 2007-11-20 dprice * write_pixmap.f90: ppm write implemented as -o option; same routine also used for opacity rendered .ppm 2007-11-20 dprice * get_data.f90: catches error if ix set incorrectly 2007-11-20 dprice * fieldlines.f90: minor conflict resolved; fewer warnings 2007-11-20 dprice * powerspectrums.f90: no warnings 2007-11-20 dprice * options_limits.f90, options_xsecrotate.f90: minor changes to only statements 2007-11-20 dprice * interactive.f90, interpolate3D_opacity.f90, interpolate3D_projection.F90: long subroutine names shortened to be strict f95 compatible 2007-11-20 dprice * defaults.f90: minor cleanup 2007-11-20 dprice * exact.f90, exact_rhoh.f90: only clauses improved; rhoh does rho vs h and h vs rho 2007-11-20 dprice * Makefile: -lg2c removed by default; reshuffle of some routines 2007-11-19 dprice * limits.f90: unused module removed 2007-11-15 dprice * plotstep.f90: BUG FIX on nag compiler; thanks to Sumedh 2007-11-15 dprice * fieldlines.f90: bug with r8 fixed 2007-11-15 dprice * Makefile, read_data_dragon.f90: dragon read implemented; not yet itype 2007-11-08 dprice * calc_quantities.f90, globaldata.f90: radiative transfer stuff calculated 2007-11-08 dprice * read_data_sphNG.f90: radiative transfer stuff labelled/units done 2007-11-02 dprice * colourbar.f90: improvements to horizontal colour bar 2007-10-30 dprice * Makefile: slight change to ukaff1a build 2007-10-30 dprice * defaults.f90: safer default filename opening 2007-10-30 dprice * globaldata.f90: max label length increased 2007-10-30 dprice * legends.f90: text no longer opaque in step legend 2007-10-30 dprice * splash.f90: bug fix on ukaff compiler 2007-10-30 dprice * read_data_sphNG.f90: bug fix in phantom read 2007-10-30 dprice * docs/splash.tex, splash.f90: citation updated 2007-10-30 dprice * read_data_sphNG.f90: bug fix in phantom read 2007-10-26 dprice * read_data_tipsy.f90: temperature labelled properly 2007-10-24 dprice * get_data.f90: catches errors in ndim; prevents seg faulting for strange data reads 2007-10-24 dprice * read_data_tipsy.f90: tipsy read handles both binary and ascii files 2007-10-24 dprice * read_data_gadget.f90: massoftype name clash fixed 2007-10-24 dprice * Makefile: comments and DEV option added 2007-10-24 dprice * read_data_sphNG.f90: seg fault bug fixed 2007-10-24 dprice * options_data.f90: bug fix in initial limits setting for calculated quantities 2007-10-24 dprice * calc_quantities.f90: bug fix in radius calculation 2007-10-23 dprice * read_data_sphNG.f90: compiles on ukaff 2007-10-23 dprice * read_data_sphNG.f90: sphNG read updated for MPI code 2007-10-17 dprice * setpage.f90: minor bug fix for aspect ratios very close to 1 2007-10-16 dprice * options_render.f90: bug fix 2007-10-16 dprice * Makefile, colourbar.f90, interactive.f90, options_render.f90, plotstep.f90, render.f90: option for horizontal colour bar implemented 2007-10-16 dprice * interpolate3D.f90: BUG FIX! 2007-10-15 dprice * plotstep.f90: space left for legend text in margins also 2007-10-15 dprice * tests/test_fieldlines.f90: updated test routine 2007-10-15 dprice * fieldlines.f90: improved fieldlines routine: uses Simpsons rule not trapezoidal; still issues with v.large gradients 2007-10-15 dprice * read_data_sphNG.f90: reads dust mass properly 2007-10-11 dprice * interpolate_vec.f90, plotstep.f90, render.f90: hide arrows where no particles also applies to streamline plotting 2007-10-11 dprice * calc_quantities.f90, plotstep.f90: better log output 2007-10-05 dprice * read_data_sphNG.f90: phantom+dust read 2007-10-05 dprice * Makefile: finds libX11 better on 64 bit systems; stuff added to pgf90 2007-10-05 dprice * read_data_sphNG.f90: dead particles in phantom treated better 2007-10-05 dprice * menu.f90: rendering by colouring particles allowed in different co-ordinate systems 2007-10-05 dprice * plotstep.f90: colour table set if particles have been previously coloured; also bug with a) and particle colouring fixed 2007-10-05 dprice * read_data_ascii.f90: better guess for mass column 2007-10-05 dprice * calc_quantities.f90: info about radius printed 2007-10-05 dprice * plotstep.f90: rotation is about tracked particle 2007-10-05 dprice * interactive.f90: o in interactive mode centres on tracked particle 2007-10-05 dprice * read_data_tipsy.f90: creates smoothing lengths if only softening lengths are dumped 2007-10-04 dprice * read_data_tipsy.f90: fixed tipsy read 2007-10-04 dprice * read_data_tipsy.f90: reads tipsy ascii files properly 2007-10-04 dprice * read_data_tipsy.f90: minor improvements 2007-09-26 dprice * Makefile, read_data_tipsy.f90: tipsy read added 2007-09-25 dprice * read_data_sphNG.f90: minus sign fixed 2007-09-25 dprice * read_data_sphNG.f90: corotating velocity subtraction implemented as environment variable 2007-09-24 dprice * write_pixmap.f90: minor compilation bugs fixed 2007-09-24 dprice * plotstep.f90: better prompt for dscreen 2007-09-24 dprice * particleplot.f90: fast particle plotting implemented for coloured subsets also 2007-09-21 dprice * options_render.f90, plotstep.f90: density weighted interpolation implemented 2007-09-21 dprice * Makefile, plotstep.f90, splash.f90, write_pixmap.f90: command line option to write pixel map to file implemented; only ascii format at present 2007-09-21 dprice * interpolate3D_projection.F90, plotstep.f90, tests/test_interpolate3D.f90: normalised projected plots implemented 2007-09-21 dprice * splash.f90: description of command line options added 2007-09-21 dprice * get_data.f90, globaldata.f90, options_data.f90, options_page.f90, splash.f90, titles.f90: ability to change file prefix for ALL files written by splash (ie. .defaults, .limits, .units, .anim, .titles and .legend) 2007-09-21 dprice * defaults.f90: minor internal change 2007-09-14 dprice * splash.f90: -p command line option added 2007-09-14 dprice * defaults.f90: minor formatting adjustments 2007-09-12 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.9.2 2007-09-12 dprice * splash.f90: v1.9.2 2007-09-12 dprice * docs/splash.bbl, docs/splash.tex: docs updated for v1.9.2 2007-09-12 dprice * options_vecplot.f90, render.f90: minor touch-ups to fixed arrow length option 2007-09-12 dprice * read_data_bauswein.f90: finishing touches added 2007-09-12 dprice * Makefile, read_data_bauswein.f90: read routine for Andreas Bauswein added 2007-09-11 dprice * interpolate3D_opacity.f90: bug fix with integer overflow on progress counter for > 100m particles 2007-09-10 dprice * plotstep.f90: renamed variables to avoid confusion 2007-09-10 dprice * plotstep.f90: smart background/foreground colour changing for legends, titles and overlaid ticks 2007-09-10 dprice * options_page.f90: better prompt for background-colour for axes/text option 2007-09-10 dprice * setpage.f90: slight adjustment to previous improvement 2007-09-10 dprice * plotstep.f90, setpage.f90: better margin determination when title is plotted above page 2007-09-10 dprice * setpage.f90: room left for title on tiled plots 2007-09-10 dprice * splash.f90: updated citation info 2007-09-10 dprice * options_vecplot.f90, render.f90: option to plot all arrows of same length implemented 2007-09-10 dprice * globaldata.f90: module variables declared public by default 2007-09-04 dprice * titles.f90: unused variable removed 2007-08-18 dprice * plotstep.f90: minor bug fix with previous 2007-08-17 dprice * plotstep.f90: axes numbers/labels turn off when rotating/using perspective 2007-08-17 dprice * interactive.f90: bug fix: distance to nearest particle now calculated much better on plots with different axes scaling 2007-08-14 dprice * asciiutils.f90: improved header skipping in ascii data read 2007-08-09 dprice * plotstep.f90: round-off problems fixed with v. large/v. small units (dobbs) 2007-08-09 dprice * interactive.f90: bug fix with a) only zooming on one axis when should zoom on both 2007-08-08 dprice * get_data.f90, globaldata.f90, read_data_sphNG.f90, splash.f90: beginnings of low memory mode implemented (memory allocated up to last required column -- applies to sphNG only at the moment); command line + env variable option added 2007-08-08 dprice * limits.f90: more error catches added 2007-08-08 dprice * allocate.f90: returns having done nothing if array sizes unchanged; also now accepts ncolumns=0 as valid input (with a warning) 2007-07-31 dprice * Makefile: fixed ukaff1b system 2007-07-31 dprice * geometry.f90: bug fix 2007-07-31 dprice * geometry.f90: non-standard declarations fixed which was blocking compilation on pathf95 2007-07-31 dprice * Makefile: new system options for pathf95 and ukaff1b 2007-07-31 dprice * docs/splash.tex: bit about ASPLASH_NCOLUMNS added to docs 2007-07-31 dprice * allocate.f90, globaldata.f90, plotstep.f90, timestepping.f90: changed interfaces to exact and interpolate_opacity; also catches min=max before PGPLOT does; massoftype implemented (but not yet in data reads) 2007-07-31 dprice * interpolate3D_opacity.f90: can send only one mass in via interface 2007-07-31 dprice * system_utils.f90: ienvironment function added 2007-07-31 dprice * read_data_ascii.f90: ASPLASH_NCOLUMNS can be used to override ncolumns settings 2007-07-31 dprice * defaults.f90, splash.f90: asplash -e option (evsplash) for plotting .ev files (ie. energy vs time) 2007-07-31 dprice * options_page.f90, options_particleplots.f90: default options for -ev option added 2007-07-31 dprice * exact.f90: circle on sedov plots; commented out by default 2007-07-31 dprice * exact_sedov.f90: plots circle at shock location 2007-07-26 dprice * calc_quantities.f90: mri deltav added 2007-07-24 dprice * exact_shock.f90: bug fix with rarefaction position in isothermal solution 2007-07-23 dprice * Makefile: spectrum SYSTEM added 2007-07-16 dprice * Makefile: zen system changes 2007-07-11 dprice * splash.f90: v1.9.1 2007-07-11 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.9.1 2007-07-11 dprice * splash.f90: v1.9.1 2007-07-11 dprice * docs/splash.tex, read_data_gadget.f90: info about environment variables added 2007-07-11 dprice * docs/splash.tex: = changed to default= 2007-07-11 dprice * read_data_gadget.f90: GSPLASH_USE_Z environment variable added to use redshift in the legend instead of time 2007-07-11 dprice * read_data_gadget.f90: GSPLASH_USE_Z environment variable added to use redshift in the legend instead of time 2007-07-06 dprice * Makefile: changes to zen system 2007-07-06 dprice * interpolate3D_projection.F90: openMP bug fixed 2007-07-06 dprice * interpolate3D_opacity.f90, options_powerspec.f90: no compiler warnings 2007-07-06 dprice * get_data.f90: warning if no SPH particles in data 2007-07-06 dprice * splash.f90: citation info added 2007-07-06 dprice * read_data_VINE.f90, read_data_gadget.f90, read_data_sphNG.f90, read_data_sro.f90: better memory use in gadget read; also various environment variables implemented 2007-07-06 dprice * Makefile, system_f2003.f90, system_unix.f90, system_unix_NAG.f90, system_utils.f90: system utils separate from system files 2007-07-06 dprice * defaults.f90: errors caught when opening defaults file 2007-07-06 dprice * prompting.f90: functions for upper and lower casing of strings added 2007-06-22 dprice * legends.f90: time is rounded up and down (not just down as previously) 2007-06-22 dprice * splash.f90: excludes blank arguments from command line 2007-06-22 dprice * timestepping.f90: better time display if time not read 2007-06-20 dprice * colours.f90: two more Bate colour schemes added 2007-06-20 dprice * options_page.f90: minor bug fix with formatting 2007-06-20 dprice * splash.f90: v1.9+ 2007-06-20 dprice * plotstep.f90: option for legend only on first row/column/nth plot implemented 2007-06-20 dprice * titles.f90: filenames for titles and legend changed to splash.titles and splash.legend to be consistent with elsewhere 2007-06-20 dprice * options_page.f90: neatened up legend+title menu plus option for legend only on first row/column/nth plot added 2007-06-20 dprice * prompting.f90: prompting now uses default= not = plus quotes around character string default 2007-06-20 dprice * options_page.f90: bug fix with constraints on nacross,ndown 2007-06-19 dprice * colours.f90: black-blue-cyan-yellow colour scheme added (suggested by Vid Irsic) 2007-06-19 dprice * powerspectrums.f90: fftw stuff commented out 2007-06-18 dprice * Makefile, interpolate3D.f90, interpolate3D_xsec.f90, plotstep.f90, powerspectrums.f90: interpolate3D now a separate module so can be optimised 2007-06-18 dprice * interpolate3D_xsec.f90: fastsqrt stuff taken out of interpolate3D 2007-06-18 dprice * plotstep.f90, powerspectrums.f90: power spectrum routines for 3D added (requires library function -- and 3D interpolation which is VERY slow) 2007-06-18 dprice * interpolate3D_projection.F90: normalisation option added to 3D vector projection routine 2007-06-18 dprice * interpolate3D_projection.F90: bug fix with column density units if 3D perspective on 2007-06-18 dprice * interpolate3D_xsec.f90: optimised 3D interpolation routine 2007-05-24 dprice * particleplot.f90: fast particle plot applies only if plotting > 100 particles 2007-05-24 dprice * plotstep.f90, rotate.f90: slightly faster rotation (+done in parallel) 2007-05-22 dprice * colours.f90, render.f90: bug fix with inverse greyscale: greyscale now behaves like other colour schemes 2007-05-22 dprice * read_data_sphNG.f90: bug fixes with phantom read 2007-05-21 dprice * docs/splash.tex: star formation tutorial added 2007-05-21 dprice * : star formation tutorial figures 2007-05-21 dprice * read_data_gadget.f90: better label for density on gadget read 2007-05-21 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.9.0 2007-05-21 dprice * : sedov example 2007-05-21 dprice * : figs for surface rendering tutorial 2007-05-21 dprice * splash.f90: v1.9 2007-05-21 dprice * docs/splash.bbl, docs/splash.tex: more improvements to user guide 2007-05-03 dprice * splash.f90: v1.9beta 2007-05-03 dprice * read_data_VINE.f90: vine data read now works for 2D, also added MHD read based on VINE_MHD environment variable 2007-05-03 dprice * plotstep.f90: bug fix in declarations 2007-05-03 dprice * Makefile, read_data_VINE.f90: bug fixes with VINE 2007-05-03 dprice * interpolate_vec.f90: minor change to comment 2007-05-03 dprice * options_page.f90, plotstep.f90: automatic line width selection implemented 2007-05-03 dprice * docs/splash.tex: more added to tutorials 2007-05-03 dprice * read_data_sphNG.f90: minor bug fix with declarations 2007-04-30 dprice * docs/splash.tex: more userguide updates 2007-04-30 dprice * Makefile: asplash also builds universal binary on maccluster 2007-04-30 dprice * plotstep.f90: minor bug fix with terminal log for 2D xsec 2007-04-30 dprice * plotstep.f90: x axis label reads x if 2D cross section is not oblique 2007-04-30 dprice * plotstep.f90: allocation clean-up + minor change to 2D xsec setup 2007-04-30 dprice * read_data_sphNG.f90: reset centre of mass option added for sphNG read 2007-04-26 dprice * options_xsecrotate.f90: log steps used for optical depth in animation sequences if tauend> 1000.*taustart or tauend<0.001*tauend 2007-04-26 dprice * interactive.f90: minor bug fix with zoom and - on colour bar in interactive mode 2007-04-26 dprice * colours.f90: bate colour scheme name change 2007-04-26 dprice * options_particleplots.f90, options_xsecrotate.f90: better menu labels 2007-04-26 dprice * plotstep.f90: bug fix with opacity setting if h limit very close to zero 2007-04-26 dprice * docs/splash.tex: vast amount added to userguide 2007-04-24 dprice * titles.f90: bug fix with log output if title/legend files do not exist 2007-04-24 dprice * splash.f90: alternative to command line filename specification is to create a file called splash.filenames (only read if nothing on command line) -- this is a workaround for a problem on the ukaff (xlf) compiler with lots of command line arguments 2007-04-24 dprice * Makefile, asciiutils.f90, read_data_ascii.f90, titles.f90: new utility module for reading ascii files; neatened up other routines which now use this module instead of repeating code 2007-04-24 dprice * interactive.f90: help displays options even if not applicable to current plot 2007-04-24 dprice * Makefile, calc_quantities.f90, globaldata.f90, interactive.f90, options_data.f90, options_limits.f90, plotstep.f90: radius is calculated relative to tracked particle if particle tracking limits are set 2007-04-24 dprice * Makefile, calc_quantities.f90, globaldata.f90, options_data.f90, options_xsecrotate.f90, plotstep.f90: origin settings now also apply in transformation to cylindrical and spherical coords 2007-04-24 dprice * options_xsecrotate.f90: bug fix with origin not being saved 2007-04-24 dprice * Makefile: dependency order fixed 2007-04-24 dprice * calc_quantities.f90: radius calculation uses origin settings 2007-04-13 dprice * docs/splash.tex: more added to vastly-improved user guide 2007-04-12 dprice * get_data.f90, globaldata.f90, limits.f90, menu.f90, options_particleplots.f90, plotstep.f90: initial limits setting also applies coordinate transformation: icoordsnew now in settings_data 2007-04-12 dprice * geometry.f90: limits also transformed for toroidal coordinate system 2007-04-12 dprice * options_page.f90, options_xsecrotate.f90: improved menu option descriptions 2007-04-12 dprice * setpage.f90: minor print statement changed 2007-04-12 dprice * interpolate3D_xsec.f90: skips particles with h < 0 and carries on 2007-04-05 dprice * interpolate3D_opacity.f90: optimisations + just skips parts if h<0 2007-04-04 dprice * plotstep.f90: bug fix: colour bar off for 2D cross section 2007-04-04 dprice * plotstep.f90: bug fix: rotation only allowed in cartesian coords 2007-04-04 dprice * plotstep.f90: gives warning about rotation+vectors 2007-04-04 dprice * options_xsecrotate.f90: bug fix with nframes not being set for interactive sequence setting 2007-04-04 dprice * interactive.f90, options_xsecrotate.f90, plotstep.f90: bug fix with sequences with logged axes; also sequences can now be set interactively using e) in interactive mode 2007-04-04 dprice * globaldata.f90, interactive.f90, menu.f90, options_xsecrotate.f90, plotstep.f90, splash.f90: animation sequences implemented 2007-04-04 dprice * options_page.f90: tiling on by default; titles off by default 2007-03-28 dprice * scripts/supersphplot_parallel.pl: obsolete 2007-03-28 dprice * docs/version, docs/version_history, docs/version_history_tex.tex: version 1.8.1 2007-03-28 dprice * docs/version: version 1.8.1 2007-03-28 dprice * splash.f90: version 1.8.1 2007-03-28 dprice * docs/splash.tex: some improvements to the manual (still kind of outdated, but slightly less so) 2007-03-26 dprice * interpolate_vec.f90, options_vecplot.f90, plotstep.f90: minimum number of particles for arrow to be plotted can be specified 2007-03-26 dprice * interpolate_vec.f90: comments added 2007-03-26 dprice * interpolate_vec.f90, options_vecplot.f90, plotstep.f90: option to hide vector arrows where there are no particles implemented; seems to work 2007-03-23 dprice * interpolate3D_projection.F90: info about uthermcutoff printed in synchrotron plots 2007-03-23 dprice * plotstep.f90: integrated vector plots use z in code units, not unit of z integration (avoids round-off issues with physical units) 2007-03-23 dprice * interpolate3D_projection.F90, options_vecplot.f90, plotstep.f90: utherm cutoff added to synchrotron options 2007-03-23 dprice * plotstep.f90: minor bug fix with npixy calculation for vector plots 2007-03-23 dprice * plotstep.f90: better synchrotron plotting sequence (arrows appear before intensity is calculated) 2007-03-23 dprice * interpolate3D_projection.F90: openmp bug fixes 2007-03-23 dprice * interpolate3D_projection.F90, plotstep.f90: better contours on synchrotron map (uses more pixels) 2007-03-23 dprice * calc_quantities.f90: minor change to tiny() fixing compiler issue (g95) with debug flags set 2007-03-23 dprice * options_render.f90: can set number of contours to zero 2007-03-23 dprice * plotstep.f90: label changed back to int B dz for vector plots in 3D 2007-03-23 dprice * interpolate3D_projection.F90, plotstep.f90: minimum resolution length implemented: equal to pixel size - means plots are much smoother 2007-03-20 dprice * interactive.f90: z(Z) key in interactive mode now goes up to x10^6 2007-03-20 dprice * interpolate_vec.f90: bug with interface 2007-03-20 dprice * interpolate_vec.f90, plotstep.f90: vector plot reverted to averaging routine (smoother plots where field is highly disordered) 2007-03-20 dprice * Makefile, plotstep.f90: interpolate_vec used (trial) 2007-03-20 dprice * interpolate3D_opacity.f90, plotstep.f90, read_data_dansph.f90, read_data_gadget.f90: only clauses improved; fewer compiler warnings 2007-03-19 dprice * plotstep.f90: bug fixes with a) on multiple-plots per page; some internal restructuring required 2007-03-19 dprice * interactive.f90: more information given when hitting a) 2007-03-19 dprice * options_xsecrotate.f90: writeppm now optional for opacity-rendered plots 2007-03-19 dprice * interpolate3D_opacity.f90: writeppm now separate routine 2007-03-15 dprice * interpolate3D_projection.F90: corrections for particles entirely within a single pixel removed 2007-03-15 dprice * interpolate3D_projection.F90: corrections for particles entirely within a single pixel added 2007-03-15 dprice * plotstep.f90: bug fix with labels for vector plot legend in physical units 2007-03-15 dprice * Makefile: openmp flag added properly for gfortran 2007-03-15 dprice * interpolate3D_projection.F90: parallel versions of vector interpolations added 2007-03-15 dprice * interpolate3D_projection.F90: bug fix with synchrotron stuff 2007-03-15 dprice * calc_quantities.f90, plotstep.f90: bad bug fixes in yesterdays build (2 seg faults fixed) 2007-03-15 dprice * Makefile: debug flags added for ifort 2007-03-15 dprice * Makefile, interpolate3D_projection.F90: bug fix with _openmp 2007-03-15 dprice * Makefile: Makefile handles new preprocessed parallel routine 2007-03-15 dprice * interpolate3D_projection.F90: new unified, preprocessed parallel routine 2007-03-15 dprice * interpolate3D_projection.f90, interpolate3D_projection_P.f90: obsolete 2007-03-14 dprice * interpolate3D_projection.f90, interpolate3D_projection_P.f90, options_vecplot.f90, plotstep.f90: implemented synchrotron map calculation 2007-03-14 dprice * splash.f90: version 1.8 2007-03-14 dprice * calc_quantities.f90: much neater calc_quantities routine (subroutine addcolumn added) 2007-03-13 dprice * options_vecplot.f90, render.f90: option to turn arrow heads off implemented 2007-03-13 dprice * interactive.f90, plotstep.f90: bug fixes with a) implementation for multiple plots per page 2007-03-12 dprice * plotstep.f90: bug fix with seg fault if multiplot settings > ncolumns (e.g. in defaults file) 2007-03-12 dprice * interactive.f90, plotstep.f90: implemented a) in interactive mode for multiple-plots-per-page (restricted at present to max,min over all panels) 2007-03-08 dprice * plotstep.f90: opacity rendering uses hmin and pmassmin (easier to get kappa right) 2007-03-08 dprice * INSTALL, INSTALL.macosx, README: name changed to splash 2007-03-08 dprice * Makefile: debug flags added for ukaff 2007-03-08 dprice * units.f90: bug fix with z integration units reading (causing seg fault) 2007-03-08 dprice * interactive.f90: bug fix with o) in interactive mode for y axis 2007-03-08 dprice * calc_quantities.f90: v. minor bug fix 2007-03-08 dprice * defaults.f90, options_data.f90, units.f90: units defaults now set in units module 2007-03-07 dprice * options_data.f90, plotstep.f90, read_data_sphNG.f90, units.f90: implemented unit for z integration (means can have different units for x, y, z like kpc but still have column density in g/cm^2 for example 2007-03-07 dprice * plotstep.f90: bug fix with background foreground colour on vector plots if axes are overlaid in background colour 2007-03-07 dprice * interactive.f90: g)radient option implemented for interactive mode with multiple-plots-per-page 2007-03-07 dprice * read_data_ascii.f90: minor typo fixed in warning 2007-02-26 dprice * Makefile: astromac system variable added 2007-02-21 dprice * splash.f90: version uses same string in both places; 1.8beta 2007-02-21 dprice * read_data_sphNG.f90: reads dumps from phantom 2007-02-21 dprice * exact_shock.f90: bug fix with left going shock wave 2007-02-21 dprice * interactive.f90, interpolate1D.f90, interpolate2D.f90, interpolate3D_opacity.f90, interpolate3D_projection.f90, interpolate3D_projection_P.f90, interpolate3D_xsec.f90, plotstep.f90: hidden particles now also not used in rendering; means can isolate portions of flow; can select particles even on render plots in interactive mode 2007-02-20 dprice * read_data_sro.f90: hfact can be set via RSPLASH_HFACT environment variable 2007-02-20 dprice * interpolate3D_opacity.f90: minor change to log output 2007-02-20 dprice * interpolate1D.f90, interpolate2D.f90, interpolate3D_xsec.f90: log output indicates whether normalised or not 2007-02-19 dprice * read_data_sphNG.f90: bug fix with small dump reads with extra quantities 2007-02-19 dprice * read_data_sphNG.f90: bug fix with small dump reads with extra quantities; preliminary stuff for phantom data read 2007-02-19 dprice * interactive.f90, plotstep.f90: x option for interactively setting cross section can be used in column density mode (ie. changes automatically from projection to cross section) 2007-02-19 dprice * splash.f90: v1.7.2 2007-02-19 dprice * docs/splash.tex, geometry.f90: overhaul of geometry module: vector components are now all physical components (have same dimensions as original vector, ie. vphi = r\dot{\phi} etc; done now for all co-ordinate systems; docs updated accordingly 2007-02-19 dprice * menu.f90, options_data.f90, options_page.f90, options_particleplots.f90, options_render.f90, options_vecplot.f90, options_xsecrotate.f90: menu shortcuts implemented 2007-02-19 dprice * options_limits.f90: menu shortcuts implemented; help mode obsolete 2007-02-19 dprice * docs/splash.tex: v minor bug fix in manual 2007-02-19 dprice * Makefile: v minor debugflag changes 2007-02-19 dprice * interactive.f90: z) also zooms (same as Z) in interactive mode 2007-02-19 dprice * calc_quantities.f90, limits.f90: bug fix if no data read and calc_quantities called 2007-02-08 dprice * exact.f90, exact_torus.f90: exact solution added for Jphi in tokamak torus 2007-02-08 dprice * exact.f90, exact_torus.f90: exact solution for Btheta in torus added 2007-02-08 dprice * menu.f90, options_particleplots.f90: menu shortcuts implemented (trial only with o menu at present) 2007-02-07 dprice * docs/splash.tex, geometry.f90: bug fixes to angular vector components in toroidal co-ordinates 2007-02-07 dprice * calc_quantities.f90: rho*u calculated for hydro 2007-02-07 dprice * exact_torus.f90: fixed exact solution for nu=2 torus 2007-02-06 dprice * plotstep.f90: BUG fix with units label showing on vector plots even when in code units 2007-02-06 dprice * geometry.f90: BUG FIX with phi component of vectors in cylindrical geometry 2007-02-05 dprice * geometry.f90: v\phi really v\phi for cylindricals 2007-02-03 dprice * options_limits.f90: more minor improvements to menu prompt 2007-02-02 dprice * read_data_sphNG.f90: more robust data read: fixes Clares problem with single precision reads 2007-02-02 dprice * options_limits.f90: slight improvements to user prompts 2007-02-02 dprice * read_data_ascii.f90: minor change only 2007-02-02 dprice * read_data_ascii.f90: minor change only 2007-02-02 dprice * read_data_ascii.f90: minor change only 2007-02-02 dprice * tests/test_fieldlines.f90: new test for fieldlines added (currently fails dismally) 2007-02-02 dprice * interactive.f90: o) option works better in interactive mode + zooming now always centred (not relative to cursor position) 2007-02-02 dprice * allocate.f90, plotstep.f90: time not plotted if has not been read from file; also better replotting behaviour when nplots < npanels 2007-01-25 dprice * read_data_sro.f90: no longer makes assumptions about file names 2007-01-25 dprice * read_data_sphNG.f90: BUG FIX with MHD reads for columns > 13 2007-01-18 dprice * scripts/splash_parallel.pl: improved ssh/xgrid farming utility 2007-01-18 dprice * tests/test_fieldlines.f90: interface to streamlines updated 2007-01-18 dprice * fieldlines.f90, plotstep.f90: bug fixes with streamline plotting 2007-01-18 dprice * options_page.f90: restrictions on page size removed; titles back on by default 2007-01-17 dprice * get_data.f90: bug fix with limits file reading with buffered data 2007-01-15 dprice * timestepping.f90: regression/bug with colour-by-type 2007-01-15 dprice * splash.f90: -v valid command line option; no compiler warnings 2007-01-15 dprice * options_particleplots.f90: label particles option removed (but not yet deleted) 2007-01-15 dprice * options_particleplots.f90, timestepping.f90: improved colour-particle-by-type option; makes clear what is going on; overrides interactive settings 2007-01-15 dprice * Makefile: minor changes to comments, debugflags, also universal binary build added for maccluster settings 2007-01-15 dprice * interactive.f90, particleplot.f90, plotstep.f90: bug fix/regression in sink particle plotting (again): icolour now = -1 for non-plotted particles; only checks z * particleplot.f90, plotstep.f90: BUG FIX/regression with particle plotting (sinks not appearing...) 2007-01-10 dprice * Makefile: minor changes for sun compiler 2007-01-10 dprice * splash.f90: spits out number of filenames read from command line 2007-01-10 dprice * options_limits.f90: obsolete options removed 2007-01-04 dprice * splash.f90: version 1.7.1 2007-01-04 ayliffe * get_data.f90: bug fix with character declaration 2007-01-04 ayliffe * read_data_sphNG.f90: bug fix with iphase read 2006-12-15 dprice * read_data_sro.f90: units for WD read implemented 2006-12-15 dprice * defaults.f90: bug fix with new command line options 2006-12-14 dprice * splash.f90: bug fix with command line read 2006-12-14 dprice * defaults.f90, globaldata.f90, menu.f90, splash.f90: command line options for defaults and limits files implemented 2006-12-13 dprice * read_data_sro.f90: minor changes; better labels for abundances; also bug fix with dump file number getting 2006-12-12 dprice * splash.f90: version 1.7.0 2006-12-12 dprice * read_data_sro.f90: nearly there with abun read 2006-12-12 dprice * read_data_sro.f90: bug fix with abun read 2006-12-12 dprice * read_data_sro.f90: reads abundance files 2006-12-12 dprice * read_data_sro.f90: hydro read implemented, plus environment variable settings 2006-12-12 dprice * system_f2003.f90, system_unix.f90, system_unix_NAG.f90: environment variable reading added to system commands 2006-12-11 dprice * interpolate3D_projection.f90, interpolate3D_projection_P.f90, particleplot.f90, plotstep.f90: bug fix with 3D projections (particles with z > zobserver no longer plotted) 2006-12-07 dprice * Makefile, defaults.f90, docs/{supersphplot.bbl => splash.bbl}, docs/{supersphplot.tex => splash.tex}, get_data.f90, interpolate3D_opacity.f90, menu.f90, options_data.f90, options_limits.f90, read_data_gadget.f90, supersphplot.f90 => splash.f90, transform.f90: name changed to splash for version 1.7 2006-12-06 dprice * plotstep.f90, timestepping.f90: BUG FIX with vector components in partial data reads 2006-12-05 dprice * calc_quantities.f90, get_data.f90, units.f90: improved units setting prompts and units labelling of calculated quantities 2006-11-24 dprice * units.f90: minor changes to warnings/ label suggestions 2006-11-24 dprice * calc_quantities.f90: automatically sets unit label for vector magnitudes 2006-11-24 dprice * Makefile: minor flag changes for maccluster 2006-11-24 dprice * interactive.f90, plotstep.f90: BUG FIX with panel determination in interactive mode on multiplots if nsteps < nacross*ndown 2006-11-23 dprice * read_data_ascii.f90: attempts to read gamma from ascii file header 2006-11-23 dprice * read_data_ascii.f90: attempts to read gamma from ascii file header 2006-11-21 dprice * read_data_ascii.f90: minor changes; warns about NaNs and Infs and recognises B field in columns file 2006-11-21 dprice * exact_shock.f90: solution implemented for left-going and right-going shocks and rarefactions 2006-11-20 dprice * Makefile: various presets added/changed 2006-11-20 dprice * docs/supersphplot.tex, geometry.f90: bug fix with spherical vector transforms; also toroidal coordinate system implemented for vectors; documentation added for these 2006-11-20 dprice * docs/supersphplot.tex: preliminary docs for geometry module 2006-11-17 dprice * plotstep.f90: bug fix with partial data reads 2006-11-17 dprice * exact.f90, exact_torus.f90: exact solution for tokamak torus added 2006-11-17 dprice * geometry.f90: toroidal co-ordinates added 2006-11-10 dprice * get_data.f90, options_data.f90, units.f90: working units file save 2006-11-09 dprice * Makefile, calc_quantities.f90, get_data.f90, globaldata.f90, menu.f90, options_data.f90, plotstep.f90, read_data_mbate.f90, read_data_sphNG.f90, read_data_sro.f90, units.f90: new units module; preliminary implementation of units file saving 2006-11-09 dprice * scripts/supersphplot_parallel.pl: minor changes 2006-11-09 dprice * read_data_sphNG.f90: Bfield units implemented 2006-11-09 dprice * interactive.f90: minor bug fix with inexact exception with uninitialised xpt2,ypt2 2006-11-09 dprice * menu.f90: BUG FIX if nplots set to 1 in multiplot options 2006-11-09 dprice * options_page.f90: minor bug fix with menu options 2006-11-09 dprice * options_particleplots.f90: bug fix with menu option > 10 not working 2006-11-09 dprice * read_data_ascii.f90: more column labels recognised 2006-11-09 dprice * calc_quantities.f90, get_data.f90, globaldata.f90, plotstep.f90, read_data_gadget.f90, read_data_sphNG.f90, timestepping.f90: partial data read implemented in gadget and sphNG reads (factor of ~2-3 speedup in read from disk for >1 files) 2006-11-09 dprice * interpolate2D.f90: comments only 2006-11-09 dprice * read_data_rsph.f90: bug fixes with rsph read 2006-11-09 dprice * colours.f90: very minor change to label 2006-11-09 dprice * interpolate3D_xsec.f90: very minor optimisation 2006-11-08 dprice * colours.f90: Bate red-yellow-white colour scheme added 2006-11-07 dprice * read_data_sphNG.f90: temporary hack to catch corrupt small dump files 2006-11-07 dprice * options_limits.f90, options_render.f90, options_xsecrotate.f90: new style menus 2006-11-07 dprice * options_data.f90, options_vecplot.f90: new menu format 2006-11-07 dprice * options_page.f90, setpage.f90: iaxis=-4 option added 2006-11-06 dprice * plotstep.f90: improved behaviour for interactive mode with nstepsperpage > 1 (uses interactive_multi) 2006-11-06 dprice * options_particleplots.f90: bug fix with formatting for ntypes<=1 2006-11-02 dprice * legends.f90: minor cleanup, documentation added for legend_scale subroutine 2006-11-01 dprice * options_particleplots.f90, timestepping.f90: option for default colours for particle types implemented; some issues remain 2006-11-01 dprice * read_data_sphNG.f90: time units are free-fall times; various minor clean ups 2006-10-31 dprice * options_page.f90, plotstep.f90: option to use background colour for overlaid axes and text added 2006-10-31 dprice * legends.f90, options_page.f90, plotstep.f90: option to plot scale on co-ordinate plots implemented; also units label on column densities works for sphNG 2006-10-31 dprice * prompting.f90: added function for printing logical variables in menus as ON or OFF 2006-10-31 dprice * options_page.f90, plotstep.f90: improvements to legend and title option settings; also option to plot time legend only for first row added 2006-10-24 dprice * supersphplot.f90: version 1.6.2 2006-10-24 dprice * read_data_sphNG.f90: bug fix with units in new read 2006-10-20 dprice * read_data_sphNG.f90: reads both full and small dumps and can mix the two 2006-10-16 dprice * scripts/supersphplot_parallel.pl: working ssh version 2006-10-16 dprice * scripts/supersphplot_parallel.pl: minor changes 2006-10-13 dprice * scripts/supersphplot_parallel.pl: working xgrid version 2006-10-12 dprice * scripts/supersphplot_parallel.pl: parallel farming utility to do one-frame-per-processor 2006-10-12 dprice * Makefile: make all option 2006-10-12 dprice * tests/test_fieldlines.f90: error check added 2006-10-12 dprice * options_particleplots.f90, particleplot.f90: fast particle plotting implemented 2006-10-12 dprice * plotstep.f90: bug fix with colour bar limits changing for multiple plots per page; also much neater 2006-10-12 dprice * interactive.f90: bug fix with colour bar limits changing for multiple plots per page 2006-10-12 dprice * menu.f90: bug fix with default values of vector plotting prompt for multiplots 2006-10-06 dprice * tests/test_fieldlines.f90: added test for fieldlines module 2006-10-06 dprice * Makefile: fieldlines test added; gfortran system type 2006-10-06 dprice * interpolate3D_xsec.f90: minor optimisations 2006-10-05 dprice * docs/supersphplot.tex: bug fix with L2 definition in manual 2006-10-05 dprice * fieldlines.f90: minor changes 2006-10-05 dprice * fieldlines.f90, options_vecplot.f90, plotstep.f90: plot streamlines option implemented; works OK in 2D; a bit funny in 3D 2006-09-28 dprice * setpage.f90: spurious print statement removed 2006-09-21 dprice * scripts/getav.pl: bug fix with error parsing script 2006-09-21 dprice * scripts/getav.pl: minor improvements 2006-09-19 dprice * options_page.f90: default line width is now 1 (looks better on pixel images e.g. gif/png) 2006-09-19 dprice * plotstep.f90: bug fix with adaptive limits changing on logged plots (-666) 2006-09-15 dprice * interactive.f90: bug fix with zoom on colourbar for multiple-plots-per-page 2006-09-15 dprice * plotstep.f90: bug fix with floating exception with optimisation on sun compiler 2006-09-14 dprice * render.f90: no labels printed on contour plots 2006-09-14 dprice * options_particleplots.f90: menu arranged better 2006-09-12 dprice * globaldata.f90: maxfile is now 10001 2006-09-11 dprice * read_data_sphNG.f90: bug fix with sink particles/dead particles 2006-09-07 dprice * colours.f90: haze colour scheme improved 2006-09-07 dprice * exact.f90, exact_densityprofiles.f90: solution for two-component plummer/hernquist spheres 2006-08-24 dprice * supersphplot.f90: version 1.6.1 2006-08-24 dprice * Makefile: dependencies removed 2006-08-24 dprice * interactive.f90: log output for particle tracking 2006-08-23 dprice * options_page.f90: minor changes to prompts 2006-08-23 dprice * interactive.f90: cursor stays put on multiplots 2006-08-23 dprice * plotstep.f90: bug fix with particle tracking+rotation 2006-08-18 dprice * setpage.f90: bug fix with margins/aspect ratios under some circumstances on tiled plots 2006-08-18 dprice * setpage.f90: buffer removed from y axis label 2006-08-18 dprice * interactive.f90, plotstep.f90: bug fixes with interactive colour bar changing on tiled plots 2006-08-18 dprice * interactive.f90: improved timestep changing for interactive on multiplot 2006-08-17 dprice * read_data_sro.f90: option to reset centre of mass 2006-08-17 dprice * plotstep.f90: outputs centre of mass 2006-08-17 dprice * calc_quantities.f90: error catches for floating exceptions in some cases 2006-08-17 dprice * interactive.f90: bug fixes with interactive on multiplot 2006-08-17 dprice * interactive.f90: improved panel finding 2006-08-16 dprice * exact.f90: ishk saved for mhd shock solution 2006-08-16 dprice * interactive.f90: better panel determination on multiplot-interactive mode 2006-08-16 dprice * exact.f90, exact_mhdshock.f90: minor bug fixes with mhd shock selection 2006-08-15 dprice * exact.f90, plotstep.f90: bug fixes with residual plots 2006-08-15 dprice * exact.f90, plotstep.f90: residual plots fixed for tiled panels 2006-08-15 dprice * options_data.f90: only clauses 2006-08-14 dprice * Makefile, danpgutils.f90, supersphplot.f90: danpgutils obsolete 2006-08-14 dprice * options_page.f90, particleplot.f90, plotstep.f90: bug fix with character height changing between devices; danpgsch now obsolete anyway 2006-08-14 dprice * allocate.f90: more informative error message 2006-08-14 dprice * read_data_dansph.f90: better error catching 2006-08-11 dprice * interactive.f90, plotstep.f90: bad regressions : seg faults if no rendering 2006-08-11 dprice * calc_quantities.f90: bug fix with labels if vector quantity 2006-08-11 dprice * plotstep.f90: bad bug fix with seg fault if ih/irho=0 2006-08-11 dprice * get_data.f90: error check on irho,ipmass,ih after data read 2006-08-10 dprice * supersphplot.f90: version 1.6 2006-08-10 dprice * plotstep.f90: bug fix with log10(1.0) giving blank pixels 2006-08-10 dprice * Makefile: bug fix with 0s instead of Os 2006-08-10 dprice * transform.f90: uses errval for errors instead of zero, can be input by calling routine - default is still zero 2006-08-10 dprice * transform.f90: v.minor 2006-08-10 dprice * Makefile: only -O4 on ukaff1a 2006-08-10 dprice * interpolate3D_projection_P.f90: no preprocessing 2006-08-10 dprice * INSTALL, INSTALL.macosx, Makefile: new Makefile with preset options for various compilers; corresponding changes to install instructions 2006-08-10 dprice * Makefile: new Makefile with preset options for various compilers; corresponding changes to install instructions 2006-08-10 dprice * Makefile, danpgsch.f, danpgtile.f, danpgutils.f90, danpgwedg.f, particleplot.f90, plotstep.f90, render.f90: .f routines now obsolete; code now f90 only; colour bar routine now draws the colour bar itself 2006-08-10 dprice * options_page.f90: minor changes to output text 2006-08-10 dprice * geometry.f90: some changes; need test for this routine 2006-08-09 dprice * options_render.f90: default number of pixels now 200 2006-08-09 dprice * interpolate3D_projection.f90, interpolate3D_projection_P.f90: message about accelerated rendering included 2006-08-09 dprice * get_data.f90: log messages improved for changed ncolumns 2006-08-09 dprice * scripts/fixpgplotnames.bash: first filename fixed 2006-08-09 dprice * interactive.f90, plotstep.f90: implemented vastly improved interactive mode on multiple-plots-per-page; seems to work 2006-08-09 dprice * setpage.f90: new setpage seems to work 2006-08-09 dprice * options_render.f90, render.f90: bug fixes with colour bar positioning 2006-08-09 dprice * defaults.f90, docs/supersphplot.tex, get_data.f90, options_page.f90, titles.f90: improved multiple-timestep-per-page legend labels/docs 2006-08-09 dprice * timestepping.f90, transform.f90: v. minor changes (public/private) 2006-08-01 dprice * danpgtile.f: bug fix causing seg fault on tiled plot zooming 2006-08-01 dprice * get_data.f90: minor bug fix with warning about ncolumns change 2006-07-31 dprice * interpolate3D_projection_P.f90: updated parallel version 2006-07-27 dprice * read_data_sphNG.f90: closes file on error condition 2006-07-26 dprice * interpolate3D_projection.f90, interpolate3D_xsec.f90: highly optimised (at least for my mac), factors of >3 speedup in interpolation 2006-07-25 dprice * scripts/fixgifs.bash, scripts/fixpgplotnames.bash, scripts/fixpngs.bash: new script for fixing pgplot filenames (more general - handles both png and gif and can offset file numbers also) 2006-07-25 dprice * read_data_sro.f90: bug fix/regression with error catch 2006-07-24 dprice * setpage.f90: bug fix with NOPGBOX 2006-07-24 dprice * calc_quantities.f90: minor changes; particle volume added but commented out 2006-07-24 dprice * read_data_ascii.f90: v. minor changes to output warnings 2006-07-24 dprice * menu.f90: better help message 2006-07-24 dprice * read_data_rsph.f90: general clean-up 2006-07-24 dprice * INSTALL, INSTALL.macosx, README: minor changes 2006-07-24 dprice * read_data_sro.f90: error catches for npart=0 2006-07-21 dprice * INSTALL.macosx: detailed instructions for mac os/x 2006-07-21 dprice * read_data_rsph.f90: bug fix with plot labels 2006-07-20 dprice * read_data_rsph.f90: revised rsph format; includes plot labels 2006-07-20 dprice * menu.f90: gwaves not there by default 2006-07-20 dprice * get_data.f90: warning if ih,ipmass,irho not present 2006-07-20 dprice * read_data_rsph.f90: updated rsph read 2006-07-20 dprice * options_page.f90, options_particleplots.f90, options_render.f90, options_vecplot.f90: toggles removed 2006-07-20 dprice * options_limits.f90, transform.f90: integer overflow bug fix with transform_label 2006-07-20 dprice * Makefile, read_data_rsph.f90: rsph data read 2006-07-18 dprice * setpage.f90: unified setpage routine added 2006-07-18 dprice * colours.f90: four new colour schemes added; bug fix with rainbow II=rainbowIII 2006-07-13 dprice * menu.f90, plotstep.f90: quick hack for plotting gravitational waves 2006-07-06 dprice * supersphplot.f90: version 1.5.4 2006-07-06 dprice * docs/supersphplot.tex: docs for multiple types added 2006-07-06 dprice * read_data_sphNG.f90: bug fix with nghost 2006-07-06 dprice * plotstep.f90: comment removed 2006-07-06 dprice * defaults.f90: defaults for UseTypeInRendering 2006-07-06 dprice * globaldata.f90, options_particleplots.f90, plotstep.f90, read_data_VINE.f90, read_data_ascii.f90, read_data_dansph.f90, read_data_dansph_old.f90, read_data_gadget.f90, read_data_gadget_jsb.f90, read_data_jjm.f90, read_data_mbate.f90, read_data_mbate_hydro.f90, read_data_mbate_mhd.f90, read_data_scw.f90, read_data_sphNG.f90, read_data_spyros.f90, read_data_sro.f90: UseTypeInRendering now set in set_labels routine, not as an option 2006-07-06 dprice * particleplot.f90: bug fix in case of npartoftype=0 for some intermediate type 2006-07-06 dprice * options_particleplots.f90: minor bug fix with plotonrenderings settings 2006-07-06 dprice * options_particleplots.f90, plotstep.f90: other particle types can now contribute to renderings (if turned on by menu option) 2006-07-06 dprice * interpolate1D.f90: uses dimensionless weights; comments updated; carries on (just skips particle) if h <= 0 or weight <= 0 2006-07-06 dprice * interpolate2D.f90: comments updated; carries on (just skips particle) if h <= 0 or weight <= 0 2006-07-06 dprice * plotstep.f90: floating exception if h or rho=0 in weights fixed 2006-07-06 dprice * plotstep.f90, setpage.f90: axes re-drawn on top of particle plots/renderings - tick marks now show up etc 2006-07-03 dprice * Makefile: exact_torus in Makefile 2006-07-03 dprice * exact_torus.f90: exact solution for pap-pringle torus 2006-07-03 dprice * read_data_sphNG.f90: bug fix with end-of-file errors 2006-06-27 dprice * supersphplot.f90: v1.5.3 2006-06-27 dprice * options_render.f90: accelerated rendering off by default 2006-06-27 dprice * exact.f90, plotstep.f90: units sent into exact solution routine - used on exact_fromfile only at present 2006-06-26 dprice * menu.f90, plotstep.f90: bug fixes/ improvements in multiplot prompting, x-sections in multiplots 2006-06-23 dprice * read_data_sro.f90: more error tolerant 2006-06-20 dprice * danpgtile.f, plotstep.f90, render.f90: colour bar spans all rows on tiled plots 2006-06-20 dprice * danpgtile.f: bug fix/regression with margins 2006-06-20 dprice * danpgtile.f, plotstep.f90, setpage.f90: bug fix with colour bar on tiled plots; also with page setup with axis ratios < 1.0 and plot tiling with colour bars 2006-06-20 dprice * danpgtile.f: bug fix with axis ratios < 1.0 (now plots top to bottom not bottom to top) 2006-06-19 dprice * plotstep.f90: bug fix/regression with legend plotting (must be done after rendering) 2006-06-19 dprice * interactive.f90: no auto change of justification 2006-06-06 dprice * plotstep.f90: bug fix with titles; a bit of cleanup also 2006-06-06 dprice * plotstep.f90: no log on label for iaxis=10,20,30 2006-06-06 dprice * read_data_ascii.f90: bug fix with reallocate if ncolumns changes 2006-06-05 dprice * options_page.f90: log labelling allowed on x and y axes 2006-06-01 dprice * scripts/getav.pl: uses argv; can do multiple files 2006-06-01 dprice * scripts/getav.pl: script for getting average of L2 errors 2006-06-01 dprice * read_data_dansph.f90: columns labelled correctly 2006-05-24 dprice * plotstep.f90: regression/bug fix with 2D mem allocation/deallocation 2006-05-24 dprice * menu.f90: continue statement removed; also dies gracefully if reaches end of stdin (ie. when scripted) 2006-05-24 dprice * limits.f90: only prints eof warning when important 2006-05-22 dprice * exact_shock.f90: improved isothermal solution 2006-05-19 dprice * legends.f90: bug fix with vecmax=0 in vector plot legend 2006-05-16 dprice * interpolate3D_projection.f90: further optimisation (less work for particles which do not contribute) 2006-05-16 dprice * interpolate3D_projection.f90: integer overflow fixed for counter on > 20m particles 2006-05-16 dprice * read_data_sphNG.f90: bug fix with accreted particle removal; time units added 2006-05-16 dprice * timestepping.f90: time formatting fixed (handles large t) 2006-05-16 dprice * legends.f90: bug fix/improved time label formatting (handles large, -ve t) 2006-05-12 dprice * interpolate3D_projection.f90, options_render.f90, plotstep.f90, tests/test_interpolate3D.f90: accelerated rendering implemented (3D projections >2x faster) 2006-05-11 dprice * supersphplot.f90: v1.5.2 2006-05-11 dprice * interactive.f90, plotstep.f90: no unnecessary re-rendering (MUCH faster limits changing etcfor large datasets); also bug fix with a) on render plots 2006-05-09 dprice * docs/supersphplot.bbl: updated bibliography 2006-05-09 dprice * colours.f90: demo plot can be to any device 2006-05-09 dprice * docs/supersphplot.tex, menu.f90, supersphplot.f90: (S) option which saves both defaults and plot limits; docs for this 2006-05-09 dprice * read_data_sphNG.f90: formatting only (I think) 2006-04-26 dprice * docs/figs/colourschemes.ps, supersphplot.f90: v1.5.1 2006-04-26 dprice * docs/supersphplot.tex: docs updated for version 1.5 2006-04-26 dprice * read_data_gadget.f90: memory allocated for exact number of particles only (unless reallocated) 2006-04-26 dprice * allocate.f90, globaldata.f90, interactive.f90, options_particleplots.f90, particleplot.f90: maximum of 100 circles of interaction can be saved (to save memory) - now allocated statically also 2006-04-21 dprice * plotstep.f90: bug fix (kind of): uses ghosts in interpolation even if not plotting them 2006-03-22 dprice * read_data_gadget.f90: v. minor bug fix (uninitialised variable) 2006-03-22 dprice * interpolate3D_opacity.f90: minor bug fix (z now must be < zobserver) 2006-03-21 dprice * legends.f90: further vector legend fiddling 2006-03-21 dprice * legends.f90: small refinements to vector legend 2006-03-20 dprice * interactive.f90, legends.f90, options_page.f90, plotstep.f90: justification of time legend adjustable 2006-03-17 dprice * legends.f90, options_vecplot.f90: bug fixes with vector legend settings 2006-03-17 dprice * supersphplot.f90: version 1.5 beta 2006-03-17 dprice * supersphplot.f90: version 1.5 2006-03-16 dprice * tests/test_interpolate3D.f90: more tests added 2006-03-16 dprice * interpolate3D_opacity.f90: bug fix with opacity rendering (no new colours generated) 2006-03-16 dprice * legends.f90, options_vecplot.f90, plotstep.f90, render.f90: better vector legend : bug fix with arrow outside of viewport + units added 2006-03-16 dprice * globaldata.f90: bigger maxplot 2006-02-21 dprice * read_data_sphNG.f90: bug fix with dead particle/sink reshuffling 2006-02-15 dprice * scripts/ppm2gif.bash: script to convert ppms to gifs 2006-02-15 dprice * read_data_sphNG.f90: mem allocated better for dead particles 2006-02-15 dprice * options_xsecrotate.f90: options triggered appropriately for surface rendering 2006-02-15 dprice * plotstep.f90: better prompt for surface rendering 2006-02-14 dprice * read_data_sphNG.f90: (should) handle sink/dead particles 2006-02-13 dprice * get_data.f90: bug fix: now handles ncolumn changes during data read (gives warnings) 2006-02-13 dprice * options_particleplots.f90, plotstep.f90: bug fix: limits only changed when different coord system is selected 2006-02-13 dprice * menu.f90: vecprompt bigger 2006-02-13 dprice * calc_quantities.f90: automatic calculation of vector magnitudes 2006-01-23 dprice * interactive.f90: Z) option for accelerated zoom/timestepping 2006-01-23 dprice * plotstep.f90: new interfaces 2006-01-17 dprice * get_data.f90, globaldata.f90, interactive.f90, options_data.f90, timestepping.f90: complete rewrite of timestepping -- now error correcting (cuts out files with nstepsinfile = 0) 2006-01-17 dprice * options_limits.f90: minor bug fix wth set_limits call, also new stepping variables 2006-01-12 dprice * plotstep.f90: bug fix with 3D perspective if iadvance=0 2006-01-12 dprice * plotstep.f90: bug fix with unset log variable 2006-01-12 dprice * interpolate3D_projection.f90: bug fix (NaN in kernel table) 2006-01-12 dprice * plotstep.f90: bug fix (3D perspective non-zero after it has been turned off) 2006-01-12 dprice * docs/supersphplot.tex: docs for 3D perspective added 2006-01-05 dprice * get_data.f90: bug fix in unit labelling (after coord change) 2006-01-05 dprice * get_data.f90: bug fix in unit labelling (after coord change) 2006-01-05 dprice * read_data_sro.f90: bug fix in particle mass setting: now handles unequal mass particle minidumps 2006-01-05 dprice * interpolate3D_opacity.f90, interpolate3D_projection.f90, plotstep.f90: speed enhancements (> factor of 2 faster) (sqrt eliminated) 2006-01-04 dprice * interpolate3D_projection.f90, interpolate3D_xsec.f90, plotstep.f90, tests/test_interpolate3D.f90: projections work with large numbers (uses dimensionless weights) 2006-01-04 dprice * options_data.f90: more on calculated quantity units 2006-01-04 dprice * get_data.f90, options_data.f90: more on calculated quantity units 2006-01-04 dprice * calc_quantities.f90, options_data.f90: units labels set for calculated quantities where possible 2006-01-04 dprice * options_data.f90: oops 2006-01-04 dprice * options_data.f90: improved units menu interface 2006-01-04 dprice * get_data.f90, options_data.f90: improved units menu interface 2006-01-04 dprice * options_data.f90: improved units menu interface 2006-01-03 dprice * tests/test_interpolate3D.f90: minor additions, works with new interfaces 2005-12-21 dprice * options_page.f90: minor bug fix 2005-12-21 dprice * get_data.f90, globaldata.f90, legends.f90, options_data.f90, plotstep.f90, read_data_sro.f90: time rescaling implemented 2005-12-21 dprice * interpolate2D.f90, interpolate3D_xsec.f90, options_render.f90, plotstep.f90: normalisation implemented as an option 2005-12-21 dprice * render.f90: minor improvements 2005-12-21 dprice * options_page.f90: option to change line width added 2005-12-21 dprice * colours.f90: bug fix with red colour table 2005-12-21 dprice * read_data_sro.f90: first attempt at corotating frame vels 2005-12-21 dprice * read_data_sro.f90: units added 2005-12-21 dprice * interpolate2D.f90, interpolate3D_xsec.f90, plotstep.f90: normalised interpolations (2D and 3D xsec) 2005-12-21 dprice * supersphplot.f90: new version label 2005-12-21 dprice * interactive.f90, interpolate3D_opacity.f90, options_xsecrotate.f90, plotstep.f90: cross section of opacity rendering implemented 2005-12-21 dprice * interpolate1D.f90, interpolate2D.f90: bugs fixed (from other interpolation routines) 2005-12-20 dprice * calc_quantities.f90: Bmag calculated 2005-12-20 dprice * colours.f90: gamma no fade fades has some blue shading at bottom 2005-12-20 dprice * get_data.f90, options_data.f90, read_data_mbate.f90, read_data_sphNG.f90: bug fixes with units on calculated quantities 2005-12-16 dprice * colours.f90: gamma with no black added 2005-12-15 dprice * interpolate3D_opacity.f90, plotstep.f90: further bug fixes with opacity rendering 2005-12-15 dprice * options_xsecrotate.f90: comments about rotation added 2005-12-15 dprice * rotate.f90: less confusing rotation angles (z first, then y, then x) 2005-12-15 dprice * colours.f90, options_render.f90: demo plot restored; minor bug fix in red map 2005-12-15 dprice * Makefile, interpolate3D_opacity.f90, options_xsecrotate.f90, plotstep.f90: opacity rendering implemented as an option with sensible defaults 2005-12-15 dprice * docs/supersphplot.tex: docs improved for projections 2005-12-15 dprice * defaults.f90: error if cannot write defaults file 2005-12-15 dprice * read_data_sro.f90: minor change to warnings 2005-12-15 dprice * read_data_dansph.f90, read_data_dansph_old.f90: new format (handles my r-z code) 2005-12-15 dprice * danpgtile.f: aspect ratio adjusted if > 1.0 also 2005-12-15 dprice * colours.f90: lots of new, decent colour tables 2005-12-13 dprice * calc_quantities.f90: bug fix with calculation of MHD quantities if no pressure 2005-12-12 dprice * interpolate3D_projection.f90, interpolate3D_xsec.f90, tests/test_interpolate3D.f90: unit test for 3D interpolations written; minor bugs fixed in these routines 2005-12-06 dprice * interpolate3D_projection.f90, plotstep.f90: ppm files written with numbers 2005-12-06 dprice * plotstep.f90: bug fix: rotation does not use 3D perspective if not set 2005-12-06 dprice * interpolate3D_projection.f90: uses function to interpolate from table 2005-12-06 dprice * colours.f90, interactive.f90, options_render.f90, plotstep.f90, render.f90: ability to invert colour table added (although not currently working for greyscale) 2005-12-06 dprice * colours.f90: minor cleanup 2005-12-06 dprice * plotstep.f90: interface to opacity rendering 2005-12-06 dprice * interpolate3D_projection.f90: intermediate version of opacity rendering (writes ppm) (+BUG fix in kernel, not yet fixed in other routines) 2005-12-02 dprice * read_data_sro.f90: bug fix -- array bounds if iutherm=0 2005-12-02 dprice * menu.f90: no vector prompt if no vectors in data 2005-12-02 dprice * interpolate3D_projection.f90, plotstep.f90: more on opacity rendering 2005-12-01 dprice * colours.f90, interpolate3D_projection.f90, plotstep.f90, supersphplot.f90: intermediate version (no opacity renderings but with 3D perspective) 2005-11-30 dprice * options_page.f90: default is off for plot titles 2005-11-30 dprice * interactive.f90, plotstep.f90, timestepping.f90: annoying issues with timestepping resolved: MUCH better behaviour. Non-interactive devices automatically go through all timesteps, no unnecessary extra plotting 2005-11-30 dprice * legends.f90: bug fix with box size on vector plot legend 2005-11-30 dprice * read_data_sro.f90: labels + read to column 27 2005-11-24 dprice * colours.f90, interactive.f90, interpolate3D_projection.f90, options_xsecrotate.f90, plotstep.f90, rotate.f90: preliminary implementations of 3D plotting using perspective, opacities 2005-10-27 dprice * plotstep.f90: BUG FIX if ipmass,irho,ih=ndataplots 2005-10-25 dprice * interactive.f90: non-interactive devices same as non-interactive mode 2005-10-24 dprice * scripts/fixgifs.bash, scripts/fixpngs.bash: scripts for fixing weird pgplot output names for gifs/pngs 2005-10-20 dprice * read_data_nina.f90: obsolete 2005-10-20 dprice * docs/supersphplot.tex: minor update 2005-10-20 dprice * INSTALL, docs/supersphplot.tex, exact_shock.f90: install instructions updated; things work on pgf90; docs updated 2005-10-13 dprice * interactive.f90, legends.f90, options_vecplot.f90, plotstep.f90, render.f90: vector plot legend; arrow size can be adjusted interactively 2005-10-10 dprice * exact.f90: exact_fromfile checks if file exists at menu prompt 2005-09-29 dprice * read_data_ascii.f90: comments removed 2005-09-28 dprice * supersphplot.f90: version 1.0.5 2005-09-28 dprice * read_data_ascii.f90: better header skipping, makes intelligent guesses about time, also columns automatically set rho, h positions if labels are set 2005-09-27 dprice * plotstep.f90, read_data_mbate.f90: bug fixes 2005-09-27 dprice * read_data_mbate.f90: units added, single/double precision detection 2005-09-27 dprice * get_data.f90, globaldata.f90, menu.f90, options_data.f90: rescale data can be turned on/off independently of units=1 (means you can set units in the read_data routine) 2005-09-27 dprice * exact.f90: bug fix 2005-09-27 dprice * plotstep.f90: interface to exact solution 2005-09-27 dprice * options_render.f90: colour scheme prompt altered slightly 2005-09-27 dprice * docs/figs/colourschemes.ps, docs/supersphplot.tex: colour scheme plot, error norms described 2005-09-27 dprice * exact_densityprofiles.f90: minor changes 2005-09-27 dprice * exact.f90: error plots use particle markers 2005-09-16 dprice * read_data_mbate_mhd.f90: step allocation is 1 2005-09-16 dprice * read_data_sphNG.f90: units 2005-09-16 dprice * plotstep.f90: labels integral of rho as column density 2005-09-16 dprice * menu.f90: unitslabel used properly on vectors/co-ordinates 2005-09-13 dprice * colours.f90: better colour demo 2005-09-13 dprice * exact.f90, exact_densityprofiles.f90, plotstep.f90: exact solution error calculation, residual plotting + potential/force solutions for exact_densityprofiles 2005-09-09 dprice * colours.f90: more/better colour schemes 2005-09-05 dprice * geometry.f90: minor changes (spherical limits, labels) 2005-09-05 dprice * Makefile, exact.f90, exact_densityprofiles.f90: exact density profiles added 2005-09-01 dprice * geometry.f90: spherical vector transforms implemented (not tested) 2005-09-01 dprice * rotate.f90: warping implemented 2005-09-01 dprice * defaults.f90, get_data.f90, legends.f90, options_page.f90, titles.f90: legend for multiple steps per page 2005-09-01 dprice * plotstep.f90: hopefully the last bug with page changing!! 2005-09-01 dprice * timestepping.f90: bug fix 2005-08-25 dprice * options_limits.f90: iadapt set properly when manually setting limits 2005-08-25 dprice * exact.f90, exact_mhdshock.f90: Bx solution added to MHD shocks 2005-08-25 dprice * setpage.f90: bug fix with title offset 2005-08-17 dprice * supersphplot.f90: header v1.0.4 2005-08-17 dprice * docs/supersphplot.tex: updated for v1.0.4 2005-08-17 dprice * supersphplot.f90: version 1.0.4 2005-08-17 dprice * options_page.f90, options_particleplots.f90, timestepping.f90: marker style changing 2005-08-11 dprice * globaldata.f90: more files allowed 2005-08-11 dprice * read_data_sphNG.f90: mem allocation fixed 2005-08-03 dprice * options_limits.f90: zoom not saved 2005-08-03 dprice * defaults.f90, exact.f90, menu.f90, options_limits.f90, options_particleplots.f90, options_xsecrotate.f90, read_data_ascii.f90: unused module variables 2005-08-03 dprice * exact_polytrope.f90: minor changes 2005-07-29 dprice * Makefile, read_data_sphNG.f90: read for new sphNG format 2005-07-29 dprice * interactive.f90: bug fix with a) on y limits 2005-07-28 dprice * supersphplot.f90: intermediate version 2005-07-28 dprice * geometry.f90: minor clean up 2005-07-15 dprice * colours.f90, interactive.f90, plotstep.f90: interactive colour map changing; also added heat colour map 2005-07-15 dprice * colours.f90: two new colour schemes 2005-07-05 dprice * supersphplot.f90: version number on splash screen 2005-07-05 dprice * supersphplot.f90: comments for v1.0.3 2005-07-05 dprice * read_data_sro.f90: minor changes 2005-07-05 dprice * interactive.f90: improved zooming + cursor position saved 2005-07-05 dprice * exact.f90, exact_toystar2D.f90: more work on toystar exact solution in 2D 2005-07-05 dprice * docs/supersphplot.tex: new format; small amendments 2005-06-24 dprice * exact.f90, exact_fromfile.f90: bug fix: exact solution from file now working properly 2005-06-23 dprice * get_data.f90, interpolate3D_projection.f90, limits.f90, supersphplot.f90: print statements formatted slightly better 2005-06-23 dprice * interpolate3D_projection_P.f90: openMP statements compile 2005-06-23 dprice * read_data_sro.f90: bug fix with single precision read 2005-06-23 dprice * docs/supersphplot.tex: some info about specific compilers 2005-06-22 dprice * read_data_sro.f90: double precision detection (full dumps + hydro minidumps) 2005-06-22 dprice * read_data_sro.f90: double precision detection (minidump only so far) 2005-06-22 dprice * interpolate3D_projection_P.f90: upper case + no output 2005-06-22 dprice * interpolate3D_projection_P.f90: updated parallel version 2005-06-21 dprice * interactive.f90, plotstep.f90: interactive setting of particle tracking 2005-06-21 dprice * menu.f90: better formatting (esp. on crap intel compiler) 2005-06-21 dprice * Makefile: whoops 2005-06-21 dprice * Makefile, exact_shock.f95 => exact_shock.f90: .f95 reverted to .f90: problems on the ifort compiler fixed 2005-06-17 dprice * exact_toystar2D.f90: A-C plane in 2D (not working) 2005-06-17 dprice * calc_quantities.f90: delta rho for toy star 2005-06-17 dprice * limits.f90: bug catch if col>maxcol 2005-06-16 dprice * plotstep.f90: bug fix with titles on top of rendered plots 2005-06-13 dprice * get_data.f90, globaldata.f90, options_data.f90: rescale data option 2005-06-13 dprice * plotstep.f90: no title on power spectrum 2005-06-13 dprice * defaults.f90: minor changes 2005-06-06 dprice * menu.f90: cleverer nacross/ndown guess 2005-06-06 dprice * options_particleplots.f90: bug fix with plot-on-renderings prompt 2005-06-06 dprice * options_powerspec.f90, plotstep.f90, read_data_ascii.f90, timestepping.f90: minor changes/bug fixes 2005-06-06 dprice * menu.f90: bug fix with tiling decisions if nextra in multiplot 2005-06-06 dprice * setpage.f90: improved label spaces depending on options 2005-06-06 dprice * setpage.f90: page setup takes account of whether space for title is needed 2005-06-06 dprice * options_page.f90, plotstep.f90: titles/legends can be turned on/off 2005-06-06 dprice * options_powerspec.f90, plotstep.f90: improved power spectrum settings 2005-06-06 dprice * calc_quantities.f90: bug fix if ndim=0 (no radius calculated) 2005-06-06 dprice * options_data.f90: calc quantities is off by default 2005-06-06 dprice * menu.f90: comments 2005-06-06 dprice * options_data.f90: default ndim=0 2005-06-02 dprice * calc_quantities.f90: no longer sets numplot 2005-06-01 dprice * get_data.f90, plotstep.f90: bug fix with unbuffered data + power spectrums 2005-06-01 dprice * docs/supersphplot.tex: latex2html falls over on \date: removed 2005-06-01 dprice * Makefile, docs/supersphplot.tex, supersphplot.f90: version 1.0.2 2005-06-01 dprice * Makefile, exact_toystar2D.f90, plotstep.f90, rotate.f90, setpage.f90: compiler warnings fixed 2005-06-01 dprice * Makefile, exact_shock.f90 => exact_shock.f95: needs to be f95 to compile properly on some compilers 2005-06-01 dprice * get_data.f90, globaldata.f90, options_data.f90, timestepping.f90: calculate quantities can be turned on/off; also bug fix with buffered/unbuffered data switching 2005-06-01 dprice * limits.f90: bug fix: limits reset only for columns that are set 2005-05-26 dprice * options_powerspec.f90, plotstep.f90, powerspectrums.f90, supersphplot.f90: much improved power spectrum plots for 1D and ascii (e.g. time evolution) data (zoom works properly) 2005-05-26 dprice * Makefile: added make docs option to compile documentation 2005-05-26 dprice * plotstep.f90: bug fix with adaptive limits if only plotting line 2005-05-24 dprice * docs/supersphplot.tex, options_particleplots.f90, particleplot.f90, plotstep.f90, read_data_ascii.f90, timestepping.f90: better line-plotting (useful for ascii data) 2005-05-24 dprice * : no longer kept in repository; done at build time 2005-05-24 dprice * docs/supersphplot.tex: updated sink particle plotting in docs 2005-05-24 dprice * options_particleplots.f90, particleplot.f90, plotstep.f90: sink/star particles can be plotted on top of renderings 2005-05-24 dprice * plot_kernel_gr.f90: obsolete 2005-05-24 dprice * options_powerspec.f90, plotstep.f90: handles ndim=0 and ih, irho, ipmass = 0; also improved power spectrum plotting 2005-05-24 dprice * read_data_VINE.f90, read_data_gadget.f90, read_data_mbate.f90, read_data_mbate_hydro.f90, read_data_mbate_mhd.f90, read_data_nina.f90, read_data_spyros.f90, read_data_sro.f90: better error output 2005-05-24 dprice * read_data_gadget_jsb.f90: jamies modified gadget read 2005-05-24 dprice * geometry.f90: bug fix for ndim=1 2005-05-24 dprice * menu.f90: handles ndim=0 2005-05-24 dprice * danpgwedg.f, render.f90: compiler warnings fixed 2005-05-24 dprice * read_data_ascii.f90: vastly improved ascii read (reads columns file, handles files with non-real columns, uses ndim=0) 2005-05-17 dprice * Makefile, supersphplot.f90: standard Makefile 2005-05-17 dprice * docs/supersphplot.tex: updated docs -- uses version number and version updates 2005-05-12 dprice * docs/supersphplot.tex: version number updated 2005-05-10 dprice * Makefile, calc_quantities.f90, danpgtile.f, get_data.f90, plotstep.f90, read_data_mbate.f90, read_data_nina.f90, render.f90, supersphplot.f90: read_data_nina.f90 2005-05-10 dprice * Makefile, defaults.f90, legends.f90, options_page.f90, read_data_mbate.f90: namelist issues sorted 2005-05-10 dprice * plotstep.f90, setpage.f90: bug fix with page setup 2005-05-04 dprice * legends.f90, options_page.f90: legend text no longer contains equals sign (NAG compiler screws this up in namelist read) 2005-05-04 dprice * options_render.f90, plotstep.f90, render.f90, setpage.f90: page setup now leaves correct amount of space for colour bar 2005-05-04 dprice * danpgwedg.f, render.f90: bug fix with colour bar widths on multiplots 2005-05-04 dprice * particleplot.f90: particle plots faster (buffered) 2005-05-04 dprice * interactive.f90: character height reset properly 2005-05-04 dprice * read_data_dansph.f90: better log 2005-04-25 dprice * Makefile: NAG-mbate Makefile 2005-04-21 dprice * Makefile, supersphplot.f90: version 1.0 ? 2005-04-21 dprice * read_data_gadget.f90, read_data_mbate.f90: gadget serial code data read (not tested) 2005-04-21 dprice * menu.f90: bug fix with array out of bounds 2005-04-21 dprice * colours.f90: new red-blue-green colour scheme 2005-04-21 dprice * INSTALL: updated install instructions 2005-04-19 dprice * INSTALL: comments 2005-04-19 dprice * INSTALL, Makefile, read_data_ascii.f90, read_data_mbate.f90: ascii data read 2005-04-19 dprice * menu.f90: conditions on when you can render 2005-04-14 dprice * Makefile, read_data_dansph.f90, supersphplot.f90, system_f2003.f90, system_unix.f90, system_unix_NAG.f90: f2003 system calls 2005-04-12 dprice * Makefile, titles_read.f90: renamed 2005-04-12 dprice * Makefile, read_data_mbate_mhd.f90, titles.f90: titles module 2005-04-12 dprice * plotstep.f90: bug fix with spurious powerspectrum prompt 2005-04-12 dprice * options_page.f90: saves foreground/background colours 2005-04-11 dprice * read_data_spyros.f90: no compile warnings 2005-04-11 dprice * docs/supersphplot.tex: updated docs 2005-04-10 dprice * INSTALL, docs/supersphplot.tex, options_data.f90, options_xsecrotate.f90, supersphplot.f90: better documentation 2005-04-10 dprice * Makefile, read_data_dansph.f90, read_data_spyros.f90: spyros format 2005-04-09 dprice * Makefile, calc_quantities.f90, get_data.f90, menu.f90, particleplot.f90, plotstep.f90, supersphplot.f90, timestepping.f90: everything finally in modules 2005-04-09 dprice * read_data_dansph.f90: automatic reading of single/double precision 2005-04-08 dprice * interactive.f90: interactive application of log axes 2005-04-08 dprice * fieldlines.f90: comments 2005-04-08 dprice * docs/supersphplot.tex, render.f90: minor changes 2005-04-08 dprice * setpage.f90: pagechanging on multiplots bug fix 2005-04-07 dprice * read_data_VINE.f90: cleaned up comments etc 2005-04-07 dprice * legends.f90, options_page.f90: changeable legend text 2005-04-07 dprice * Makefile, legends.f90, read_data_dansph.f90, read_data_mbate.f90: crap 2005-04-07 dprice * read_data_VINE.f90: read format for VINE code 2005-04-01 dprice * plotstep.f90, render.f90: better colour bar behaviour on multiple plots per page 2005-03-24 dprice * particleplot.f90: bug fix with colours applying to axes by mistake 2005-03-24 dprice * calc_quantities.f90: calculates valfven 2005-03-24 dprice * interactive.f90: a)dapt option applies to renderings 2005-03-24 dprice * plotstep.f90: iadaptcoords implemented properly - adaptive plot limits now do less strange things 2005-03-24 dprice * menu.f90: no render option if transformations applied 2005-03-23 dprice * interactive.f90, plotstep.f90: bug fix with string on intel compiler 2005-03-23 dprice * limits.f90, plotstep.f90: improved setting of log limits, uses huge/tiny instead of fixed values 2005-03-23 dprice * interactive.f90: interactive changing of log/unlog on render plots 2005-03-23 dprice * interpolate3D_projection.f90: ticker on for large npix 2005-03-22 dprice * calc_quantities.f90: bug fix with plasma beta (now uses tiny) 2005-03-22 dprice * read_data_sro.f90: full dump reading 2005-03-22 dprice * interactive.f90, plotstep.f90: bug fix with interactive xsec moving + colourpart render limit adjusting 2005-03-22 dprice * options_page.f90: opens null device before colour settings 2005-03-22 dprice * danpgwedg.f, options_render.f90, render.f90: colour bar issues resolved 2005-03-22 dprice * interactive.f90: cross section position set interactively 2005-03-22 dprice * Makefile, interpolate1D.f90, interpolate2D.f90, interpolate2D_xsec.f90, interpolate3D.f90, interpolate3D_fastxsec.f90, interpolate3D_projection.f90, interpolate3D_xsec.f90, interpolate3D_xsec_vec.f90, plotstep.f90: interpolations in modules + vector interpolations use kernel 2005-03-21 dprice * menu.f90, options_limits.f90: help text 2005-03-21 dprice * interactive.f90: changes adaptive to fixed limits 2005-03-18 dprice * get_data.f90, limits.f90, options_limits.f90: bug fixes with limits file reading 2005-03-18 dprice * read_data_mbate.f90, read_data_mbate_hydro.f90, read_data_mbate_mhd.f90: better error output 2005-03-18 dprice * exact_wave.f90: amplitude is percentage of mean 2005-03-18 dprice * menu.f90: rendering disallowed unless icoords=icoordsnew 2005-03-17 dprice * options_page.f90, plotstep.f90, timestepping.f90: ipagechange local variable only 2005-03-17 dprice * plotstep.f90: bug fix with floating exception caused by xplot,yplot>ntot 2005-03-17 dprice * interactive.f90: minor changes 2005-03-17 dprice * plotstep.f90: adaptive rendering limits behave correctly with interactive mode 2005-03-17 dprice * supersphplot.f90: unused module removed 2005-03-17 dprice * read_data_mbate.f90, read_data_mbate_hydro.f90: dgrav printed, also t_ff output in mbate version 2005-03-15 dprice * calc_quantities.f90: some clutter removed 2005-03-15 dprice * Makefile, docs/supersphplot.tex, globaldata.f90, integratedkernel.f90, interpolate3D_proj_vec.f90, interpolate3D_projection.f90, plotstep.f90, supersphplot.f90: interpolate3D_projection in self-contained module 2005-03-15 dprice * get_data.f90, menu.f90, supersphplot.f90: neater log output 2005-03-15 dprice * defaults.f90, limits.f90: prompts for limits file 2005-03-15 dprice * calc_quantities.f90, get_data.f90, options_particleplots.f90: bug fix: icoordsnew default set 2005-03-15 dprice * defaults.f90: unused vars removed 2005-03-11 dprice * Makefile, colours.f90, defaults.f90, exact.f90, exact_toystar1D.f90, exact_wave.f90, globaldata.f90, limits.f90, menu.f90, options_data.f90, options_limits.f90, options_page.f90, options_particleplots.f90, options_powerspec.f90, options_render.f90, options_vecplot.f90, options_xsecrotate.f90, plotstep.f90: submenus in modules which include their settings 2005-03-10 dprice * Makefile, legends.f90, plotstep.f90, render.f90, render_vec.f90: render module, only statements tightened, colour bar plotting and bug fix in particle colourings 2005-03-10 dprice * docs/supersphplot.tex: pdf version so appears when exported 2005-03-10 dprice * docs/supersphplot.bbl: bibliography file 2005-03-10 dprice * supersphplot.f90: new blurb 2005-03-08 dprice * plotstep.f90, rotate.f90: rotated axes use origin 2005-03-08 dprice * options_xsecrotate.f90, read_data_gadget.f90: bug fixes 2005-03-08 dprice * read_data_gadget.f90: jamies data read new version 2005-03-08 dprice * defaults.f90, globaldata.f90, options_render.f90, options_xsecrotate.f90, plotstep.f90, render.f90, rotate.f90: colour bar adjustable, rotated box has own limits, and zero values set to min in datpix if logged 2005-03-08 dprice * read_data_dansph.f90: bug fix 2005-03-08 dprice * supersphplot.f90: splash screen does legal bit 2005-03-08 dprice * INSTALL, LICENSE, README, docs/supersphplot.tex: docs added 2005-03-08 dprice * Makefile, allocate.f90, calc_quantities.f90, globaldata.f90, limits.f90, options_particleplots.f90, read_data_dansph.f90, read_data_gadget.f90, read_data_jjm.f90, read_data_mbate.f90, read_data_mbate_hydro.f90, read_data_mbate_mhd.f90, read_data_scw.f90, read_data_sro.f90: iam and ntot removed 2005-03-04 dprice * Makefile, defaults.f90, geometry.f90, globaldata.f90, options_limits.f90, plotstep.f90: iadaptcoords added, but not yet used/implemented 2005-03-04 dprice * read_data_mbate.f90, read_data_mbate_hydro.f90, read_data_mbate_mhd.f90: read datas renamed+mbate version added 2005-03-03 dprice * particleplot.f90: bug fix in xerrb 2005-03-02 dprice * exact.f90, exact_toystar2D.f90, get_data.f90, timestepping.f90: 2D toystar exact solution (linear) looks like it works 2005-03-02 dprice * interactive.f90: bug fix if non-interactive device 2005-03-02 dprice * plotstep.f90: bug fix (no istepprev or irenderprev any more as these should never be necessary) 2005-03-02 dprice * timestepping.f90: print statements removed 2005-03-02 dprice * read_data_dansph.f90: gradh read 2005-03-02 dprice * exact.f90: right names for exact parameter files 2005-03-02 dprice * calc_quantities.f90: dh/drho added 2005-03-01 dprice * Makefile, defaults.f90, get_data.f90, menu.f90, options_data.f90, supersphplot.f90, timestepping.f90: bug fix: does not set limits on unbuffered data unless on first data read 2005-03-01 dprice * timestepping.f90: bug fixes with timestepping 2005-03-01 dprice * exact.f90, exact_polytrope.f90, exact_toystar.f90 => exact_toystar1D.f90: renamings 2005-03-01 dprice * exact.f90: exact solution from file plotted untransformed 2005-03-01 dprice * Makefile, exact.f90, exact_mhdshock.f90: exact_mhdshock brought into the fold 2005-03-01 dprice * Makefile, print_header.f90, supersphplot.f90: print header moved into supersphplot.f90 as internal routine 2005-03-01 dprice * exact_polytrope.f, exact_toystar_ACplane.f, toystar2D_utils.f90: files cleaned up from exact solution revamp 2005-03-01 dprice * Makefile, defaults.f90, exact.f90, exact_fromfile.f90, exact_rhoh.f90, exact_sedov.f90, exact_shock.f90, exact_toystar2D.f90, exact_wave.f90, globaldata.f90, options_particleplots.f90, plotstep.f90: exact solutions completely revamped to use transformations (coord transforms not done yet) 2005-03-01 dprice * danpgtile.f: y label moved 2005-03-01 dprice * timestepping.f90: there are bugs in here 2005-03-01 dprice * transform.f90: zerolog parameter 2005-02-25 dprice * read_data_mbate_hydro.f90: uses units on density 2005-02-23 dprice * defaults.f90, globaldata.f90, options_page.f90, plotstep.f90: foreground/background colour changing within program 2005-02-23 dprice * colours.f90, defaults.f90, globaldata.f90, interactive.f90, options_data.f90, options_limits.f90, options_page.f90, plotstep.f90, timestepping.f90: plots selected timesteps + multiple steps per page (also some bug fixes with interactive setting of plot limits) 2005-02-23 dprice * transform.f90: log label instead of log10 2005-02-23 dprice * read_data_mbate_hydro.f90: uses free fall time 2005-02-23 dprice * read_data_mbate_hydro.f90: bug fixes 2005-02-21 dprice * docs/supersphplot.tex: polytrope exact solution notes updated 2005-02-18 dprice * timestepping.f90: more on timestepping across multiple files 2005-02-18 dprice * timestepping.f90: bug fix with multiple files, now goes forwards and backwards properly 2005-02-18 dprice * read_data_mbate_hydro.f90: doub prec 2005-02-17 dprice * timestepping.f90: crap 2005-02-16 dprice * interactive.f90, plotstep.f90, rotate.f90: various bug fixes 2005-02-16 dprice * Makefile, interactive.f90, plotstep.f90: bug fixes in iplotz 2005-02-16 dprice * read_data_mbate.f90, read_data_mbate_hydro.f90: added read format for hydro mbate+bug fixes 2005-02-11 dprice * interactive.f90: better log output 2005-02-11 dprice * plotstep.f90: bug fix if iplotx=iploty 2005-02-11 dprice * geometry.f90: better handling of r=0 in vec transform 2005-02-10 dprice * get_data.f90, globaldata.f90, timestepping.f90: bug fix in timestepping, gets first step properly when not buffered, also freezes on last step in interactive mode 2005-02-07 dprice * Makefile, globaldata.f90, menu.f90, mainloop.f90 => plotstep.f90, timestepping.f90: mainloop split into separate parts, timestepping separate to plotstep, more local variables, although some streamlining could still be done 2005-02-04 dprice * options_render.f90: more on particle colouring 2005-02-03 dprice * Makefile, colourparts.f90, colours.f90, globaldata.f90, mainloop.f90, options_render.f90: preliminary implementation of particle colouring 2005-01-31 dprice * calc_quantities.f90, defaults.f90, exact_rhoh.f90, get_data.f90, globaldata.f90, limits.f90, mainloop.f90, options_page.f90, particleplot.f90, read_data_dansph.f90: animate removed, better(less) log output to screen 2005-01-31 dprice * exact_rhoh.f90: better for variable masses, bug fix if xmin < 0 2005-01-27 dprice * menu.f90: bug fix in coord sys change 2005-01-27 dprice * mainloop.f90, transform.f90: transform2 uses size of array (smaller interface) 2005-01-27 dprice * defaults.f90, geometry.f90: reshape initialisation so that labelcoord is a parameter 2005-01-27 dprice * get_data.f90, menu.f90: bug fix in labelling for different coord sys 2005-01-26 dprice * toystar2D_utils.f90: bug fix in functions 2005-01-26 dprice * globaldata.f90: icoordsnew saved 2005-01-26 dprice * Makefile, toystar2D_utils.f90: toy star functions appear to work 2005-01-26 dprice * mainloop.f90: bug catch if ndimV>ndim and coord transforms 2005-01-25 dprice * Makefile, defaults.f90, geometry.f90, globaldata.f90, menu.f90, options_particleplots.f90, read_data_dansph.f90, read_data_gadget.f90, read_data_jjm.f90, read_data_mbate.f90, read_data_scw.f90, read_data_sro.f90: geometry module contains coord system labels + maxcoordsys 2005-01-25 dprice * interactive.f90, rotate.f90: experimenting with projections 2005-01-25 dprice * exact.f90, exact_toystar2D.f90, toystar2D_utils.f90: working on exact 2D toystars 2005-01-25 dprice * mainloop.f90: out of bounds bug fix 2005-01-24 dprice * globaldata.f90, mainloop.f90: ixsec bug fix 2005-01-24 dprice * mainloop.f90: bug fix with turning interactive mode off 2005-01-24 dprice * geometry.f90, mainloop.f90: coord transform applied to fixed limits 2005-01-21 dprice * allocate.f90: better output 2005-01-21 dprice * read_data_dansph.f90: less reallocation of memory 2005-01-21 dprice * mainloop.f90: bug fix with totmass 2005-01-21 dprice * exact_toystar2D.f90, mainloop.f90: bug fix with totmass 2005-01-20 dprice * Makefile, exact.f90: saves polyk 2005-01-20 dprice * globaldata.f90, read_data_dansph.f90: uses iformat 2005-01-20 dprice * Makefile, mainloop.f90: out-of-bounds bug fix 2005-01-19 dprice * interpolate3D_projection.f90: cpu timings added 2005-01-19 dprice * geometry.f90, mainloop.f90: vector transforms implemented + tested (cyl only) 2005-01-19 dprice * Makefile, interactive_part.f90 => interactive.f90: interactive_part renamed interactive 2005-01-19 dprice * Makefile, defaults_set.f90 => defaults.f90, defaults_read.f90, defaults_write.f90, menu.f90, read_data_dansph.f90, supersphplot.f90: defaults module 2005-01-19 dprice * rotate_axes.f90: obsolete 2005-01-19 dprice * menu.f90: bug fix in string length 2005-01-18 dprice * allocate.f90, particleplot.f90, read_data_dansph.f90, read_data_mbate.f90: bug fixes in memory allocation/reallocation 2005-01-18 dprice * read_data_mbate.f90: trying to catch memory bugs 2005-01-18 dprice * geometry.f90: bug fix in coord transform for x=0 2005-01-18 dprice * mainloop.f90: reverted to vector plots 2005-01-17 dprice * read_data_dansph.f90: new header format 2005-01-17 dprice * interactive_part.f90: bug fix with save circles 2005-01-15 dprice * interactive_part.f90: interactive resets angle if > 360 degrees 2005-01-15 dprice * mainloop.f90, options_xsecrotate.f90, rotate.f90: rotated 3D axes implemented 2005-01-13 dprice * allocate.f90, supersphplot.f90: deallocation of remaining memory 2004-12-23 dprice * Makefile, interpolate3D_proj_vec.f90: projection for vec plots (not tested) 2004-12-23 dprice * interactive_part.f90: comments 2004-12-23 dprice * interactive_part.f90, mainloop.f90: interactive selection in cross section slices 2004-12-23 dprice * Makefile, interactive_part.f90, mainloop.f90, particleplot.f90: plotting of selected particles only, general zrange interface to particleplot 2004-12-23 dprice * interpolate3D_projection.f90, mainloop.f90: mainloop split further into internal subroutines 2004-12-22 dprice * defaults_set.f90, globaldata.f90, options_xsecrotate.f90, rotate.f90: options for rotated axes (not yet implemented) 2004-12-21 dprice * Makefile, fieldlines.f90, mainloop.f90, render_vec.f90: preliminary field line plotting routine (2D only) 2004-12-21 dprice * allocate.f90, defaults_set.f90, globaldata.f90, interactive_part.f90, mainloop.f90, options_particleplots.f90: interactive setting of circles of interaction/allocatable 2004-12-20 dprice * legends.f90, render_vec.f90: more playing around 2004-12-20 dprice * interactive_part.f90: interactive setting of x-sec position 2004-12-20 dprice * mainloop.f90: interactive_step only on multiplots 2004-12-20 dprice * mainloop.f90: bug fix in timestepping 2004-12-20 dprice * read_data_mbate.f90: uses free fall time 2004-12-20 dprice * interactive_part.f90: better render limits changing 2004-12-17 dprice * options_vecplot.f90: comment moved 2004-12-17 dprice * mainloop.f90: bug fix in initialising colour schemes for multiplot 2004-12-17 dprice * menu.f90: bug fix in multiplot settings 2004-12-17 dprice * defaults_set.f90, interactive_part.f90, legends.f90, mainloop.f90, render.f90, render_vec.f90: mostly crap 2004-12-16 dprice * interactive_part.f90, mainloop.f90, transform.f90: inverse transforms for setting plot limits adaptively 2004-12-16 dprice * defaults_set.f90, legends.f90, render_vec.f90: working on vector plot legend 2004-12-16 dprice * Makefile, globaldata.f90, legend.f, legends.f90, mainloop.f90, options_vecplot.f90: legend module + preliminaries for vecplot legend 2004-12-16 dprice * read_data_mbate.f90: bug fix with incomplete data 2004-12-16 dprice * mainloop.f90, menu.f90: prompt for iplotpartvec 2004-12-15 dprice * Makefile, interactive_part.f90, mainloop.f90: better gradient plotting, better saving from interactive mode 2004-12-15 dprice * read_data_mbate.f90: single precision 2004-12-15 dprice * interactive_part.f90, mainloop.f90: interactive mode sets render limits, moves legend and title 2004-12-15 dprice * transform.f90: bug fix with multiple labels 2004-12-14 dprice * allocate.f90, particleplot.f90: bug fix with ntot.ne.sumoftypes, npartoftype=0 in allocate now 2004-12-14 dprice * mainloop.f90: bug fix with interactive iadapt changing 2004-12-14 dprice * Makefile, calc_quantities.f90, read_data_jjm.f90: read data for joes format 2004-12-12 dprice * interactive_part.f90, mainloop.f90: interactive zooms on powerspec plots 2004-12-10 dprice * mainloop.f90, transform.f90: transforms improved 2004-12-10 dprice * mainloop.f90: transforms on powerspec plots 2004-12-10 dprice * Makefile, mainloop.f90, powerspectrum_fourier1D.f90, powerspectrum_lomb1D.f90 => powerspectrums.f90: powerspectrums in module 2004-12-10 dprice * Makefile, exact.f90, int_from_string.f90, interactive_part.f90, menu.f90, read_data_gadget.f90: int_from_string removed, found better way 2004-12-09 dprice * mainloop.f90, plot_powerspectrum.f90, powerspectrum_lomb1D.f90: power spectrum plots better integrated 2004-12-09 dprice * interactive_part.f90: cut down version for extra plots 2004-12-09 dprice * read_data_dansph.f90: bug fixes with new formats 2004-12-07 dprice * Makefile, calc_quantities.f90, coord_transform.f90, geometry.f90, mainloop.f90, plot_kernel_gr.f90, read_data_dansph.f90, vector_transform.f90: basic cylindrical interpolation works (ie density calculation) + dumps/plotting works 2004-12-07 dprice * interpolate3D_projection_P.f90: openMP version (NOT TESTED) 2004-12-07 dprice * Makefile, limits.f90, options_limits.f90, vector_transform.f90: various tweaks/additions, nothing big 2004-12-06 dprice * Makefile, allocate.f90, calc_quantities.f90, read_data_dansph.f90, read_data_gadget.f90, read_data_mbate.f90, read_data_scw.f90, read_data_sro.f90, transform.f90: allocate in module 2004-12-06 dprice * options_limits.f90: better prompts 2004-12-03 dprice * Makefile, exact_toystar2D.f90, interactive_part.f90, mainloop.f90: circles of interaction in interactive mode, more on 2D toy stars 2004-12-02 dprice * Makefile, defaults_set.f90, docs/supersphplot.tex, exact.f90, exact_toystar2D.f90, mainloop.f90: working on toy star exact solution in 2D 2004-12-02 dprice * read_data_dansph.f90: for new format 2004-12-01 dprice * read_data_dansph.f90: reads div v also 2004-11-23 dprice * Makefile, read_data_sro.f90: read data for Stephans code (minidumps only at present) 2004-11-23 dprice * mainloop.f90: playing around with 1D interpolation 2004-11-20 dprice * Makefile, docs/supersphplot.tex, mainloop.f90, rotate.f90: rotation module added 2004-11-19 dprice * interactive_part.f90: checks for cursor 2004-11-19 dprice * Makefile, defaults_set.f90, globaldata.f90, mainloop.f90, options_particleplots.f90, plot_average.f: removed plot_average and associated options (was crap anyway) 2004-11-19 dprice * Makefile, exact.f90, get_data.f90, globaldata.f90, limits.f90, limits_read.f90, limits_save.f90, limits_set.f90, mainloop.f90, options_limits.f90, options_xsecrotate.f90: limits module enveloping limits_read, limits_set and limits_save : used appropriately 2004-11-19 dprice * options_render.f90: crap 2004-11-19 dprice * colours.f90: minor crap in colours 2004-11-19 dprice * colours.f90: minor crap in colours 2004-11-19 dprice * colours.f90, defaults_set.f90, globaldata.f90, mainloop.f90, options_render.f90: bug fixes with colours module, better colour scheme setting in menu 2004-11-19 dprice * Makefile, colour_demo.f, colour_set.f90 => colours.f90: colour schemes in module 2004-11-18 dprice * particleplot.f90, supersphplot.f90: bug catches for incomplete data 2004-11-18 dprice * get_data.f90, particleplot.f90: crap 2004-11-18 dprice * get_data.f90, globaldata.f90, mainloop.f90, menu.f90, options_data.f90, read_data_dansph.f90, read_data_gadget.f90, read_data_mbate.f90, read_data_scw.f90, supersphplot.f90: ivegotdata only in get_data + ihavereadfilename obsolete + get_nextstep in mainloop 2004-11-18 dprice * allocate.f90, globaldata.f90, interactive_part.f90, mainloop.f90, particleplot.f90: particle marking implemented (and works) 2004-11-17 dprice * Makefile: playing around with superfast cleaning 2004-11-17 dprice * get_data.f90: bug fix with single step reads and limits settings 2004-11-17 dprice * read_data_mbate.f90: catches header errors 2004-11-17 dprice * calc_quantities.f90: calculates ke 2004-11-17 dprice * Makefile, exact_toystar2D.f90, mainloop.f90: exact solution is plotted before call to interactive, call to pgsls in toy star version required 2004-11-17 dprice * particleplot.f90: colours of circles of interation changed + line width 2004-11-15 dprice * transform.f90: auto label lengths 2004-11-15 dprice * Makefile, calc_quantities.f90, interactive_part.f90: bug fixes with uninitialized vars 2004-11-15 dprice * read_data_mbate.f90, read_data_scw.f90: bug fixstat rhozero, RK2 declared real 2004-11-12 dprice * read_data_mbate.f90: crap 2004-11-11 dprice * Makefile: sys file moved 2004-11-11 dprice * Makefile, defaults_read.f90, defaults_set.f90, defaults_write.f90, exact.f90, get_data.f90, globaldata.f90, limits_read.f90, limits_save.f90, mainloop.f90, menu.f90, options_data.f90, options_limits.f90, options_page.f90, options_particleplots.f90, options_powerspec.f90, options_render.f90, options_vecplot.f90, options_xsecrotate.f90, particleplot.f90, read_data_dansph.f90, read_data_gadget.f90, read_data_mbate.f90, read_data_scw.f90, supersphplot.f90: settings module split into sub-modules 2004-11-11 dprice * calc_quantities.f90: clean up of unnecessary global vars 2004-11-09 dprice * mainloop.f90: some junk removed 2004-11-09 dprice * exact.f90, exact_toystar2D.f90: bug fixes in exact solution for static 2D toy star 2004-11-08 dprice * defaults_set.f90, globaldata.f90, mainloop.f90, options_particleplots.f90, particleplot.f90: circles of interaction cleaned up, no plotcircall, only on selected 2004-11-05 dprice * rotate.f90: mucking around 2004-11-05 dprice * defaults_set.f90, globaldata.f90, interactive_part.f90, mainloop.f90, options_xsecrotate.f90, rotate.f90: rotation works in 3D (although x appears to be y and vice versa??), better log in main loop 2004-11-04 dprice * Makefile, coord_transform.f90: uses ATAN2 -> much better! 2004-11-04 dprice * calc_quantities.f90, read_data_dansph.f90: bug fix with labelling 2004-11-04 dprice * Makefile, coord_transform.f90: quadrants on theta (not tested) 2004-11-03 dprice * coord_transform.f90, mainloop.f90: meddling with rotation 2004-11-03 dprice * Makefile, calc_quantities.f90, get_data.f90: calc quantities bugs fixed, more robust now 2004-11-03 dprice * read_data_mbate.f90: better memory allocation 2004-11-03 dprice * Makefile, docs/supersphplot.tex, read_data_mbate.f90: various crap 2004-11-03 dprice * options_render.f90: plot contours changes if icolours = 0 or set 2004-11-03 dprice * mainloop.f90: trying rotation on rendered plots 2004-11-03 dprice * coord_transform.f90, rotate.f90: tried to fix spherical transform and hence rotation simpler 2004-11-02 dprice * docs/supersphplot.tex: various additions 2004-11-02 dprice * Makefile, system_unix.f90, system_unix_NAG.f90: different system files 2004-11-02 dprice * defaults_write.f90: ierr removed 2004-11-02 dprice * Makefile, defaults_read.f90, defaults_write.f90, exact.f90, system_unix.f90: exact module and defaults neatened up 2004-10-27 dprice * mainloop.f90: file skipping in non-buffered read 2004-10-27 dprice * Makefile, defaults_set.f90, docs/supersphplot.tex, interactive_part.f90, mainloop.f90, read_data_dansph.f90, supersphplot.f90: keys for mouse functions 2004-10-27 dprice * interpolate3D_projection.f90, render.f90: exactly fits in plot window, progress bar revised 2004-10-27 dprice * read_data_dansph.f90, read_data_gadget.f90, read_data_mbate.f90, read_data_scw.f90: bug catches in set_labels 2004-10-20 dprice * Makefile, system_unix.f90: compiles and everything works on NAG compiler 2004-10-20 dprice * Makefile, allocate.f90, defaults_set.f90, docs/supersphplot.tex, get_data.f90, interpolate3D_projection.f90, limits_set.f90, mainloop.f90, menu.f90, options_limits.f90, read_data_scw.f90, supersphplot.f90, system_unix.f90, titles_read.f90: bug fixes with uninitialised variables 2004-10-19 dprice * Makefile, allocate.f90, get_data.f90, read_data_dansph.f90, read_data_gadget.f90, read_data_mbate.f90, read_data_scw.f90, system_unix.f90: scw data read works + various crap 2004-10-19 dprice * prompting.f90: bug fix with present(min) 2004-10-19 dprice * Makefile, exact.f90, exact_mhdshock.f90, exact_shock.f90, exact_toystar.f90, exact_toystar2D.f90, exact_toystar_ACplane.f, globaldata.f90, interpolate1D.f90, interpolate2D.f90, interpolate3D.f90, interpolate3D_fastxsec.f90, interpolate3D_projection.f90, interpolate3D_xsec_vec.f90, mainloop.f90, menu.f90, options_limits.f90, options_page.f90, particleplot.f90, plot_powerspectrum.f90, powerspectrum_lomb1D.f90, read_data_dansph.f90, render.f90, render_vec.f90, supersphplot.f90, transform.f90: unused variables removed 2004-10-19 dprice * read_data_scw.f90: not working, playing around with recl= 2004-10-19 dprice * Makefile, read_data_scw.f90: data read for Stuarts data 2004-10-19 dprice * calc_quantities.f90, colour_demo.f, colour_set.f90, exact_polytrope.f, exact_toystar.f90, exact_toystar2D.f90, exact_toystar_ACplane.f, interactive_part.f90, interpolate1D.f90, interpolate2D.f90, interpolate2D_xsec.f90, interpolate3D.f90, interpolate3D_fastxsec.f90, interpolate3D_projection.f90, interpolate3D_xsec_vec.f90, interpolate_vec.f90, legend.f, menu.f90, options_limits.f90, options_page.f90, options_particleplots.f90, options_render.f90, options_xsecrotate.f90, plot_average.f, plot_powerspectrum.f90, powerspectrum_lomb1D.f90, render.f90, render_vec.f90, setpage.f90: tabs removed 2004-10-19 dprice * Makefile, modules.f90 => globaldata.f90: modules.f90 renamed globaldata.f90 2004-10-19 dprice * read_data_dansph_ascii.f90: obsolete, no longer updated 2004-10-19 dprice * allocate.f90, limits_set.f90, mainloop.f90, modules.f90, read_data_dansph.f90, read_data_dansph_ascii.f90, read_data_gadget.f90, read_data_mbate.f90: npart, nghost removed altogether (not tested) 2004-10-18 dprice * read_data_dansph.f90: better error catches on incomplete data 2004-10-18 dprice * coord_transform.f90: tabs removed 2004-10-13 dprice * transform.f90: tabs removed 2004-10-13 dprice * transform.f90: floor now 10-12 if log(0) 2004-10-13 dprice * get_data.f90, read_data_dansph.f90, read_data_gadget.f90, read_data_mbate.f90: more robust data read -> returns nstepsread 2004-10-13 dprice * Makefile: added system.f90 2004-10-13 dprice * int_from_string.f90: tabs removed 2004-10-13 dprice * defaults_read.f90, defaults_set.f90, defaults_write.f90: defaults save rootnames, tabs removed, gotos removed 2004-10-13 dprice * supersphplot.f90, system_unix.f90: interface for system commands 2004-10-13 dprice * options_data.f90: tabs removed, ians=0 2004-10-13 dprice * docs/supersphplot.tex: small changes in docs 2004-10-13 dprice * danpgtile.f: tabs removed 2004-10-11 dprice * read_data_mbate.f90: tabs removed, reads sinks (but must reorder data to plot them) 2004-10-11 dprice * options_vecplot.f90: ians=0, tabs removed 2004-10-11 dprice * menu.f90: goto removed 2004-10-11 dprice * mainloop.f90: bug fix with adaptive plot limits 2004-10-11 dprice * exact.f90, exact_shock.f90: isothermal riemann solver 2004-10-11 dprice * exact.f90: bug fix in prompt for shock solution 2004-10-07 dprice * options_render.f90: ians =0 2004-10-07 dprice * get_data.f90: bug fix in get_data with call to set_limits 2004-10-05 dprice * allocate.f90: formatted print statements 2004-10-05 dprice * particleplot.f90: comments formatted 2004-10-05 dprice * Makefile, get_data.f90, options_limits.f90, read_data_dansph.f90: various bug catches to do with data read 2004-10-05 dprice * Makefile, main.f90 => mainloop.f90, menu.f90, print_header.f90, supersphplot.f90: main renamed mainloop, trying to get getarg to work 2004-10-04 dprice * Makefile, read_data_mbate.f90: various compilation problems fixed 2004-10-04 dprice * docs/bibstyle.bst, docs/figs/hyperbolic.ps, docs/figs/xsec2D.eps, docs/figs/xsec2D.fig, docs/figs/xsec3D.eps, docs/figs/xsec3D.fig, docs/supersphplot.tex: docs in CVS properly 2004-10-04 dprice * Makefile: updated make for ifc, no copyscripts any more 2004-10-04 dprice * Makefile: auto changing of Makefiles 2004-09-16 dprice * get_data.f90, main.f90: bug fixes if buffering 2004-09-14 dprice * Makefile, defaults_set.f90, get_data.f90, main.f90, menu.f90, modules.f90, options_data.f90, read_data_dansph.f90, read_data_dansph_ascii.f90, read_data_gadget.f90, read_data_mbate.f90, supersphplot.f90: working on buffering in data read (getting there...) 2004-09-14 dprice * read_data_mbate.f90, read_data_mbate_dump.f90, supersphplot.f90: formatted read from mbate obsolete : now only unformatted 2004-09-14 dprice * options_exact.f90, read_exactparams.f90: module interface for exact solution calling : removed old files 2004-09-14 dprice * Makefile, defaults_read.f90, defaults_set.f90, defaults_write.f90, exact.f90, exact_fromfile.f90, get_data.f90, main.f90, modules.f90, options_particleplots.f90, read_data_dansph.f90, read_data_mbate_dump.f90: module interface for exact solution calling 2004-09-14 dprice * read_data_dansph.f90, read_data_gadget.f90, read_data_mbate_dump.f90: set labels in separate subs 2004-09-13 dprice * menu.f90, supersphplot.f90: menu self-contained 2004-09-13 dprice * Makefile, calc_quantities.f90, get_data.f90, modules.f90, read_data_mbate_dump.f90: working on mbate data read 2004-09-13 dprice * main.f90: bug fix if ivectorplot=0 2004-09-13 dprice * allocate.f90: bug fix in reallocation 2004-08-30 dprice * menu.f90, supersphplot.f90: preliminary recursive menu 2004-08-27 dprice * read_data_gadget.f90: smoothing length x 0.5 2004-08-25 dprice * read_data_mbate_dump.f90: part types, vectors 2004-08-25 dprice * menu.f90: vector prompting 2004-08-25 dprice * defaults_set.f90, docs/supersphplot.tex, main.f90, menu.f90, modules.f90, options_xsecrotate.f90, read_data_gadget.f90: preliminary subscripting for vector quantities 2004-08-24 dprice * options_xsecrotate.f90: menu cleaned up a bit 2004-08-23 dprice * Makefile, main.f90, rotate_axes.f90: preliminary rotated axes/box 2004-08-23 dprice * read_data_dansph.f90: some changes for npartoftype 2004-08-20 dprice * Makefile, allocate.f90, defaults_set.f90, limits_set.f90, main.f90, modules.f90, options_particleplots.f90, particleplot.f90, read_data_gadget.f90: particle plotting in new sub, allows up to 6 particle types, removed refs to sinks, ghosts, works on gadget data 2004-08-20 dprice * interactive_part.f90, main.f90, rotate.f90: rotation about x seems to work, also interactive rotation 2004-08-20 dprice * Makefile, vectorplot.f90 => interpolate_vec.f90, main.f90, supersphplot.f90: vectorplot superseded by interpolate_vec, has been checked 2004-08-20 dprice * exact_rhoh.f90: does nothing if hfact not set 2004-08-20 dprice * interactive_part.f90: prints on exit 2004-08-20 dprice * get_data.f90, limits_set.f90: bug fix with numplot if no calc_quantities 2004-08-20 dprice * read_data_dansph.f90, read_data_dansph_ascii.f90, read_data_mbate.f90, read_data_mbate_dump.f90: dat array references fixed 2004-08-19 dprice * allocate.f90, calc_quantities.f90, limits_set.f90, main.f90, read_data_gadget.f90: changed ordering of indices on dat array for optimal performance (NOT YET FOR AALL FORMATS) 2004-08-19 dprice * Makefile, exact_shock.f90, riemannsolver.f90: riemannsolver moved inside exact_shock.f90 2004-08-19 dprice * limits_set.f90, main.f90, modules.f90, options_limits.f90: can reset limits, limits_set cleaned up 2004-08-19 dprice * interactive_part.f90, main.f90: limits changing not permanent in interactive 2004-08-19 dprice * Makefile, coord_transform.f90: coord transform moved from src, cylindrical transformations fixed 2004-08-19 dprice * defaults_set.f90, interpolate3D_fastxsec.f90, main.f90, modules.f90, options_xsecrotate.f90, rotate.f90: azimuthal rotation works 2004-08-19 dprice * read_data_gadget.f90: bug fix for ifile=0 2004-08-19 dprice * docs/supersphplot.tex: updated docs 2004-08-19 dprice * get_data.f90, read_data_gadget.f90: gadget data read working 2004-08-19 dprice * interpolate3D_projection.f90: progress counter added 2004-08-17 dprice * get_data.f90: THESIS SUBMITTED, so working code (haha) 2004-08-16 dprice * docs/supersphplot.tex: docs moved to plot dir 2004-08-05 dprice * Makefile, main.f90, modules.f90, options_xsecrotate.f90, rotate.f90: rotation added: at the moment around origin and only for particle plots 2004-08-04 dprice * interpolate2D_xsec.f90, main.f90: BIG BUG FIX in interpolate2D_xsec : now works 2004-08-03 dprice * main.f90, setpage.f90: more bug fixes in page changingstat I think this should always work properly now 2004-08-03 dprice * Makefile: improved Makefile - modules separate 2004-08-03 dprice * Makefile, get_data.f90, modules.f90, read_data_dansph.f90, read_data_dansph_ascii.f90, read_data_gadget.f90, read_data_mbate.f90, read_data_mbate_dump.f90: handles long filenames 2004-08-03 dprice * main.f90, menu.f90, options_limits.f90, transform.f90: transform is a module 2004-08-03 dprice * transform.f90: bug fix in transform limits for abs 2004-08-03 dprice * read_data_dansph.f90: bug fix with multiple files and nstep_max 2004-08-02 dprice * modules.f90: saves interactive setting 2004-08-02 dprice * menu.f90: adjustl 2004-07-29 dprice * main.f90: paper change only if set 2004-07-29 dprice * calc_quantities.f90: perp/parallel labels 2004-07-28 dprice * main.f90: bug fix with page changing - much more robust now 2004-07-28 dprice * defaults_set.f90, main.f90, modules.f90, options_exact.f90: bug fix in linear wave exact solution (iwaveplotx,y now) 2004-07-28 dprice * main.f90, setpage.f90: bug fix with page changing for 1 plot per page and ipagechange off 2004-07-28 dprice * Makefile, defaults_set.f90, interpolate2D_xsec.f90, main.f90, menu.f90, modules.f90, options_particleplots.f90, options_render.f90, options_xsecrotate.f90, setpage.f90, supersphplot.f90: 2D cross sections work, options_xsecrotate added 2004-07-27 dprice * Makefile, interpolate2D_xsec.f90, main.f90: interpolate2D_xsec bug fix: removed pixwidth<0 error catch 2004-07-27 dprice * read_data_dansph.f90: better error catches 2004-07-27 dprice * defaults_set.f90, get_data.f90: crap 2004-07-26 dprice * read_data_mbate_dump.f90: bug fix in setting npart etc 2004-07-26 dprice * main.f90: vector cross sections working in 3D 2004-07-26 dprice * Makefile: sending to ukaff 2004-07-24 dprice * Makefile, get_data.f90, read_data_mbate_dump.f90: reads mbate data direct from dump (needs improvement though) 2004-07-24 dprice * Makefile, interpolate3D_xsec_vec.f90, main.f90, render_vec.f90: vector cross sections (NOT YET TESTED) 2004-07-23 dprice * interactive_part.f90, main.f90: interactive zoom 2004-07-23 dprice * read_data_dansph.f90: labels fixed 2004-07-23 dprice * interpolate2D.f90, render.f90: playing around with renderings 2004-07-23 dprice * get_data.f90, read_data_dansph.f90, read_data_dansph_ascii.f90: reads binary dumps from my code 2004-07-23 dprice * colour_demo.f, colour_set.f90, supersphplot.f90: ice colour scheme improved 2004-07-16 dprice * Makefile, defaults_read.f90, main.f90, modules.f90, read_data_gadget.f90: reads gadget data 2004-07-14 dprice * Makefile, defaults_read.f90, defaults_set.f90, defaults_write.f90, main.f90, menu.f90, modules.f90, options_data.f90, options_render.f90, options_vecplot.f90, render.f90, supersphplot.f90: render options majorly restructured + vector options 2004-07-13 dprice * Makefile, danpgsch.f, defaults_set.f90, main.f90, modules.f90, options_page.f90, supersphplot.f90: title positions, danpgsch added for character height 2004-07-13 dprice * Makefile, main.f90, supersphplot.f90, titles_read.f90: reads plot titles from file 2004-07-13 dprice * read_data_dansph.f90: bug fix with reading multiple files 2004-07-13 dprice * interactive_part.f90: advance by n steps 2004-07-13 dprice * read_data_dansph.f90: more sensible reallocation of memory 2004-07-12 dprice * defaults_set.f90, legend.f, main.f90, modules.f90, options_page.f90: adjustable legend position 2004-06-24 dprice * plot_powerspectrum.f90, setpage.f90: crap 2004-06-23 dprice * danpgtile.f: bug fix : iplot now unchanged on output 2004-06-19 dprice * defaults_set.f90, main.f90, menu.f90, modules.f90, options_particleplots.f90, read_data_dansph.f90, read_data_mbate.f90, supersphplot.f90: particle coords -> new coord systems (not for vectors yet) 2004-06-19 dprice * exact_sedov.f90: crap 2004-06-16 dprice * exact_shock.f90: bug fix for t=0 2004-06-11 dprice * Makefile, defaults_set.f90, exact_polytrope.f, exact_shock.f, exact_shock.f90, get_data.f90, main.f90, modules.f90, options_exact.f90, read_exactparams.f90, riemannsolver.f90, supersphplot.f90: exact solution for 1D shock tubes 2004-06-11 dprice * exact_fromfile.f90: exact solution from a file 2004-06-10 dprice * legend.f: crap 2004-06-09 dprice * danpgtile.f: more frigging around with labels/margins 2004-06-09 dprice * main.f90: particle cross section initialisation works 2004-06-03 dprice * danpgtile.f: margins adjust with character height 2004-06-02 dprice * isosurface.f90: beginnings of isosurface routine (NOT WORKING) 2004-06-02 dprice * interactive_part.f90, main.f90, supersphplot.f90: interactive does forward/backward stepping, replotting 2004-06-01 dprice * Makefile, get_data.f90, int_from_string.f90, menu.f90, modules.f90, options.f90, print_menu.f90, supersphplot.f90: redesigned main menu - options use characters, compact form 2004-06-01 dprice * options_limits.f90: re-read limits file 2004-06-01 dprice * options_page.f90: animate moved to here 2004-06-01 dprice * print_menu.f90: last revision before killed 2004-06-01 dprice * Makefile, limits_read.f90, limits_save.f90, limits_set.f90, main.f90, modules.f90, options.f90, options_limits.f90, supersphplot.f90: saves/reads plot limits to/from files 2004-06-01 dprice * limits.f90 => limits_set.f90: renamed limits_set 2004-06-01 dprice * read_data_dansph.f90: labels for alpha u etc 2004-06-01 dprice * defaults_read.f90: better error catching 2004-06-01 dprice * main.f90: uses setgrid1D, also particle tracking for non-coord plots 2004-05-31 dprice * Makefile, main.f90, options_limits.f90, supersphplot.f90: particle tracking limits work 2004-05-28 dprice * defaults_set.f90, interpolate3D_xsec_vec.f90, main.f90, modules.f90, options_limits.f90: particle tracking (exeter) NOT YET WORKING 2004-05-27 dprice * exact_rhoh.f90, read_data_dansph.f90, read_data_mbate.f90: bug fixes in input, rhoh plot no text 2004-05-26 dprice * Makefile, allocate.f90, colour_demo.f, exact_sedov.f90, int_from_string.f90, main.f90, options.f90, read_data_mbate.f, read_data_mbate.f90, supersphplot.f90: works on Matthews code, several compiler bugs fixed 2004-05-20 dprice * Makefile, defaults_set.f90, exact_swave.f, exact_wave.f90, main.f90, modules.f90, options_exact.f90: exact_swave -> general sine wave plot 2004-05-18 dprice * danpgtile.f: y axis label position 2004-05-18 dprice * options_page.f90: more paper options 2004-05-18 dprice * options_page.f90: bug fix in paper size menu 2004-05-18 dprice * allocate.f90, read_data_dansph.f90: bug fix in mem allocation for multiple files 2004-05-18 dprice * calc_quantities.f90: cross helicity 2004-05-17 dprice * Makefile, danpgtile.f, defaults_set.f90, legend.f, main.f90, modules.f90, options_page.f90, setpage.f90, supersphplot.f90: plot tiling, axes options 2004-05-17 dprice * Makefile, main.f90, modules.f90, options.f90, read_data_dansph.f90, supersphplot.f90: reads from multiple files off command line 2004-05-17 dprice * legend.f: various crap 2004-05-17 dprice * print_menu.f90: no defaults on y prompt 2004-05-17 dprice * interpolate1D.f90, interpolate2D.f90, interpolate2D_xsec.f90, interpolate3D.f90, interpolate3D_fastxsec.f90, interpolate3D_projection.f90: error catches 2004-05-17 dprice * danpgtile.f: bug fix in just=0 2004-05-13 dprice * calc_quantities.f90: calculates magnitude of current 2004-05-13 dprice * exact_sedov.f90: attempted to make it work for <3D 2004-05-13 dprice * read_data_dansph.f90: alpha B 2004-05-13 dprice * exact_mhdshock.f90: added extra solution 2004-05-10 dprice * exact_rhoh.f90, main.f90: works for different particle masses 2004-05-06 dprice * plot_powerspectrum.f90, powerspectrum_lomb1D.f90: error catches if variance=0 2004-04-26 dprice * main.f90: bug fix in titles 2004-04-21 dprice * options.f90, options_exact.f90, options_page.f90, options_particleplots.f90, options_render.f90, print_menu.f90, supersphplot.f90: menu cleaned up some more 2004-04-21 dprice * int_from_string.f90: izero : more robust 2004-04-21 dprice * exact_shock_old.f, render_smooth_pgxtal.f: obsolete subroutines removed 2004-04-21 dprice * legend.f: fiddling 2004-04-21 dprice * smooth_pixels.f: obsolete 2004-04-21 dprice * supersphplot.f90: comments added 2004-04-21 dprice * Makefile, danpgtile.f, main.f90, setpage.f90: utilities for tiling on page/ page setup 2004-04-21 dprice * options.f90: minor change to prompt 2004-04-21 dprice * options_page.f90: new paper sizes 2004-04-13 dprice * Makefile, options.f90, options_page.f90, print_menu.f90, supersphplot.f90: page options 2004-04-13 dprice * options.f90: bug fix in multiplot options 2004-04-13 dprice * supersphplot.f90: comments 2004-04-13 dprice * render.f90: crap 2004-04-13 dprice * read_data_dansph.f90: new labels 2004-04-13 dprice * main.f90: icoords crap 2004-04-13 dprice * modules.f90: maxplot increased 2004-04-13 dprice * plot_kernel_gr.f90: print outs 2004-04-01 dprice * interactive_part.f90: line plotting 2004-03-26 dprice * options_limits.f90: prompting on limits 2004-03-26 dprice * Makefile, defaults_set.f90, main.f90, modules.f90, options.f90, options_limits.f90, options_particleplots.f90, print_menu.f90, supersphplot.f90: options split into submenus 2004-03-26 dprice * read_data_dansph.f90: labels 2004-03-16 dprice * defaults_set.f90, interactive_part.f90, main.f90, modules.f90, options.f90, print_menu.f90: interactive mode in menu, animate removed 2004-03-15 dprice * Makefile, defaults_set.f90, interactive_part.f90, main.f90, modules.f90: preliminary interactive mode 2004-03-15 dprice * exact_toystar.f90: comments added 2004-03-09 dprice * main.f90, read_data_dansph.f90: bug fix in transformed limits, reallocation more sensible 2004-03-08 dprice * defaults_set.f90, interpolate1D.f90, main.f90, modules.f90, options_powerspec.f90, plot_powerspectrum.f90, powerspectrum_fourier1D.f90, powerspectrum_lomb1D.f90: working power spectrums (not sure about fourier) 2004-03-08 dprice * read_data_dansph.f90: bug fix with reallocate 2004-03-05 dprice * Makefile, defaults_set.f90, exact_sedov.f, exact_sedov.f90, main.f90, modules.f90, options_exact.f90: sedov blast wave solution working 2004-03-05 dprice * Makefile, allocate.f90, calc_quantities.f90, defaults_read.f90, defaults_set.f90, limits.f90, modules.f90, options.f90, read_data_dansph.f, read_data_dansph.f90, supersphplot.f90: allocatable arrays 2004-03-04 dprice * Makefile, exact_toystar2D.f90, read_data_mbate.f: IoA makefiles, some printouts removed 2004-02-27 dprice * exact_toystar2D.f90: toy star 2D stuff 2004-02-26 dprice * Makefile, defaults_set.f90, exact_toystar2D.f90, main.f90: 2D toy star exact solutions (preliminary), also A,C sensible defaults 2004-02-26 dprice * main.f90: xminrender removed 2004-02-26 dprice * Makefile, main.f90, modules.f90, render_coarse.f, vectorplot.f90: improved vector plots, npix,npixy etc 2004-02-25 dprice * Makefile, exact_toystar.f, exact_toystar.f90: exact_toystar -> f90 2004-02-23 dprice * modules.f90, options.f90, read_data_dansph.f: more initialisation bugs 2004-02-23 dprice * read_data_dansph.f: crap removed 2004-02-23 dprice * defaults_set.f90: more compiler-found bugs 2004-02-23 dprice * Makefile, defaults_set.f90, modules.f90, options.f90, print_menu.f, print_menu.f90: print menu -> f90, magfield option removed 2004-02-23 dprice * Makefile, calc_quantities.f90, defaults_set.f90, interpolate2D.f90, main.f90, modules.f90, options.f, options.f90, options_render.f90, print_menu.f, read_data_dansph.f, read_data_mbate.f, supersphplot.f90, transform.f, transform.f90: bugs from monash compiler: uninitialised variables fixed, also prompts for irender in menu 2004-02-23 dprice * main.f90, transform.f90: f90, limits transformed 2004-02-20 dprice * main.f90, modules.f90: compiler bugs fixed 2004-02-20 dprice * calc_quantities.f90, exact_mhdshock.f90, interpolate1D.f, interpolate1D.f90, interpolate2D.f, interpolate2D.f90, interpolate2D_xsec.f, interpolate2D_xsec.f90, interpolate3D.f, interpolate3D.f90, interpolate3D_fastxsec.f, interpolate3D_fastxsec.f90, interpolate3D_projection.f, interpolate3D_projection.f90, main.f90, modules.f90, supersphplot.f90: f90 2004-02-20 dprice * read_data_dansph.f: crap 2004-02-20 dprice * Makefile: f90 crap 2004-02-20 dprice * Makefile, read_data_dansph.f: bugs found by swinburne compiler fixed 2004-02-18 dprice * Makefile, defaults_set.f90, main.f90, modules.f90, options.f, plot_kernel_gr.f90: gr kernel plots, icircpart now array 2004-02-17 dprice * Makefile, colour_demo.f, colour_set.f, colour_set.f90: .f90 2004-02-17 dprice * menu_actions_old.f, supersphplot_old.f: removed 2004-02-16 dprice * defaults_set.f90, interpolate2D_xsec.f, main.f90: 2D xsections 2004-02-16 dprice * Makefile, legend.f, modules.f90: random crap 2004-02-16 dprice * main.f90: toy star exact for r 2004-02-06 dprice * Makefile, colour_demo.f, setcolours.f => colour_set.f, read_defaults.f90 => defaults_read.f90, set_defaults.f => defaults_set.f90, write_defaults.f90 => defaults_write.f90, main.f90, menu_actions.f => options.f, supersphplot.f90: name changes 2004-02-06 dprice * render_smooth.f: obsolete 2004-02-06 dprice * Makefile, exact_toystar_ACplane.f, interpolate3D_projection.f, menu_actions.f, modules.f90, powerspectrum_lomb1D.f90, supersphplot.f90: bugs picked up by monash compiler fixed 2004-01-14 dprice * set_defaults.f: better defaults for renderings 2004-01-14 dprice * colour_demo.f, setcolours.f: uses max available colours on device (up to 256) 2004-01-14 dprice * danpgwedg.f: more pixels in wedge 2004-01-13 dprice * read_data_dansph.f: minor clean up 2004-01-13 dprice * powerspectrum_fourier1D.f90: crap at the moment 2004-01-13 dprice * options_powerspec.f90: power spec options 2004-01-13 dprice * interpolate1D.f: 1D interpolation 2004-01-12 dprice * main.f90: a,b,c,d plot labelling 2003-12-30 dprice * calc_quantities.f90: uses ntotal instead of npart 2003-12-30 dprice * main.f90: axes crap cont... 2003-12-30 dprice * main.f90: axes sets to 0.02 and 0.98 not 0.0 and 1.0 2003-12-29 dprice * main.f90, menu_actions.f, modules.f90, print_menu.f, set_defaults.f: axes on/off 2003-12-29 dprice * render.f90: uses pgcons 2003-12-23 dprice * exact_rhoh.f90, read_data_dansph.f: read data sets mhd according to no of columns 2003-12-22 dprice * calc_quantities.f90, main.f90, modules.f90, read_data_dansph.f, supersphplot.f90: vperp etc calculated in calc_quantities, no longer in data file 2003-12-19 dprice * supersphplot.f90: goto removed 2003-12-19 dprice * Makefile, main.f90, set_defaults.f, supersphplot.f90: main.f90 added 2003-12-19 dprice * calc_quantities.f90: sets labels 2003-12-19 dprice * supersphplot.f90: from .f format 2003-12-19 dprice * supersphplot.f: to .f90 format 2003-12-18 dprice * Makefile, menu_actions.f, modules.f90, plot_powerspectrum.f90, powerspectrum_lomb1D.f90, set_defaults.f, supersphplot.f: power spectrum plotting/options etc 2003-12-18 dprice * options_exact.f90: moved out of menu_actions into separate file 2003-12-18 dprice * options_render.f90: renamed get_render_options, converted to .f90 2003-12-18 dprice * get_render_options.f: renamed options_render 2003-12-18 dprice * lomb_powerspectrum1D.f90: renamed powerspectrum_lomb1D 2003-12-18 dprice * supersphplot.f: labels on particle cross sections 2003-12-18 dprice * modules.f90: update to namelists 2003-12-16 dprice * modules.f90: more defaults saved 2003-12-16 dprice * Makefile, supersphplot.f: bug fixes, main prog cleaned up substantially 2003-12-16 dprice * interpolate3D_fastxsec.f: bug fix z < 2h 2003-12-15 dprice * interpolate3D.f: some bug fixes/minor modifications 2003-12-15 dprice * render.f90: .f90 2003-12-15 dprice * render.f: to .f90 2003-12-15 dprice * render.f: uses ymin 2003-12-15 dprice * supersphplot.f: xsec limits bug fixed, but not completely 2003-12-15 dprice * supersphplot.f: bug fix for cross sectioning 2003-12-15 dprice * read_data_dansph.f: bug fix: sets nghost 2003-12-15 dprice * supersphplot.f: lower case 2003-12-15 dprice * supersphplot.f: lower case 2003-12-15 dprice * Makefile, supersphplot.f, write_defaults.f: namelists, .f90 source 2003-12-15 dprice * modules.f90: namelists, .f90 2003-12-15 dprice * plot_powerspectrum.f90: histogram 2003-12-15 dprice * read_defaults.f90, write_defaults.f90: namelist 2003-12-15 dprice * read_defaults.f: to .f90 2003-12-15 dprice * modules.f: freeform source -> to .f90 2003-12-15 dprice * modules.f: lower case 2003-12-09 dprice * Makefile, lomb_powerspectrum1D.f90, modules.f, plot_powerspectrum.f90, set_defaults.f, supersphplot.f: power spectrum of 1D data (some bugs still) 2003-12-08 dprice * modules.f: minor changes for spherical blast wave 2003-11-24 dprice * supersphplot.f: update 2003-11-24 dprice * Makefile, calc_quantities.f90, exact_rhoh.f90, menu_actions.f, plot_rhoh.f, supersphplot.f: calc_quantities added, rhoh moved 2003-10-30 dprice * supersphplot.f: fix bug when no data 2003-10-22 dprice * Initial revision splash/Makefile000644 000766 000000 00000001070 13261626263 014422 0ustar00dpricewheel000000 000000 #---------------------------------------------------------------- # Parent Makefile for SPLASH # This file is just a wrapper for the sub-make in the build # directory. Refer to build/Makefile for more details. # # (c) 2007-2013 Daniel Price # #---------------------------------------------------------------- .PHONY: splash install docs tests src bin splash: @cd build; ${MAKE} ${MAKECMDGOALS} %:: @cd build; ${MAKE} ${MAKECMDGOALS} install: @cd build; ${MAKE} ${MAKECMDGOALS} docs: @cd build; ${MAKE} ${MAKECMDGOALS} clean: @cd build; ${MAKE} clean splash/tests/000755 000766 000000 00000000000 13261626263 014126 5ustar00dpricewheel000000 000000 splash/utils/000755 000766 000000 00000000000 13261626263 014124 5ustar00dpricewheel000000 000000 splash/docs/000755 000766 000000 00000000000 13261626263 013714 5ustar00dpricewheel000000 000000 splash/README000644 000766 000000 00000002064 13261626263 013646 0ustar00dpricewheel000000 000000 SPLASH - an interactive visualisation tool for SPH data Copyright (C) 2005-2011 Daniel Price daniel.price@monash.edu For installation instructions see the INSTALL file. For detailed descriptions of menu options and algorithms, refer to the userguide in the /docs directory or online. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See the LICENSE file for details. splash/INSTALLv1.x000644 000766 000000 00000035030 13261626263 014533 0ustar00dpricewheel000000 000000 To compile SPLASH with the PGPLOT backend (the default in SPLASH v1.x.x, but still an option with SPLASH 2.x) you will need the following on your system, both of which are freely available: - The PGPLOT graphics subroutine library - A Fortran 95 compiler The basic steps for installation are: 1) make sure you have a Fortran 90/95 compiler (such as g95 or gfortran). 2) make sure you have the PGPLOT libraries installed. 3) compile SPLASH and link with PGPLOT. 4) if desired/necessary write a read_data subroutine so that SPLASH can read your data format. 5) make pretty pictures. For troubleshooting of some common installation problems, have a look at the online FAQ. 1) ---------------- Fortran 95 compilers --------------------------- By now, many Fortran 90/95 compilers exist. In terms of free ones, both Intel and Sun have non-commercial versions available for Linux and the g95 compiler, downloadable from: http://www.g95.org successfully compiles SPLASH and if necessary the PGPLOT libraries. Gfortran is also free and, as of version 4.2.0, works. The latest version can be downloaded from: http://gcc.gnu.org/wiki/GFortran I strongly recommend downloading a more recent version of gfortran rather than relying on any pre-installed version (use gfortran -v to check the version number). In particular versions 4.1.0 and lower *do not* compile splash. Later versions also have openMP, so you can compile and run SPLASH in parallel. 2) ------------------- PGPLOT ----------------------------------------- The PGPLOT graphics subroutine library is freely downloadable from http://www.astro.caltech.edu/~tjp/pgplot/ or by ftp from ftp://ftp.astro.caltech.edu/pub/pgplot/pgplot5.2.tar.gz however check to see if it is already installed on your system (if so, the libraries are usually located in /usr/local/pgplot). If PGPLOT is already installed, make sure that the environment variable PGPLOT_DIR is set to the location of the PGPLOT installation directory (e.g. /usr/local/pgplot). Check this by typing "echo $PGPLOT_DIR". If instead you are following the steps below, set the PGPLOT_DIR environment variable to the directory to which you will install PGPLOT (e.g. export PGPLOT_DIR=$HOME/pgplot). It is a good idea to add the setting of PGPLOT_DIR into your .profile/.bashrc or .tcshrc file along with a setting for PGPLOT_DEV (e.g. to "/xw" which sets the default device to be the X-windows device). --- installing PGPLOT yourself --- Whilst detailed installation instructions are given in the PGPLOT distribution, the general procedure for installing your own version (if necessary) is given below (otherwise skip to part 3). Note: to compile PGPLOT with the png and X-windows drivers may require some packages to be installed from your linux distribution. In Ubuntu these are "libpng-dev" and "libX11-dev" which can be installed with "sudo apt-get libpng-dev" and "sudo apt-get libX11-dev". Otherwise you will encounter errors regarding missing header files -- e.g. "cannot find png.h" and a whole bunch of errors. a) untar the pgplot5.2.tar.gz file (e.g. in your home space): "tar xvfz pgplot5.2.tar.gz" b) rename the directory pgplot to something else (e.g. "mv pgplot pgplotsrc"). c) make a directory called pgplot and enter it: "mkdir pgplot; cd pgplot" d) copy the drivers.list file from the pgplotsrc directory "cp ../pgplotsrc/drivers.list ." e) edit the drivers.list file and uncomment the following drivers: /NULL /XW /VPS /CPS /VCPS /PS and either /PNG /TPNG or /GIF /VGIF. Optionally also /PPM /VPPM. f) make a makefile using the makemake utility: "../pgplotsrc/makemake ../pgplotsrc linux g77_gcc" (I generally always use "linux" and "g77_gcc" regardless of the actual operating system). You should now have a file called "makefile" in your directory. g) edit the makefile, replacing the compiler "g77" with "gfortran" or "g95" (or any other compiler) as appropriate. For some compilers, you may need to change the FFLAGC= to just -O2. h) type "make": this should compile through all the .f files and may or may not compile the GIF drivers (if not, go back to step d and remove the /GIF drivers from the list), but will definitely die for the png driver with an error like: make: *** No rule to make target `png.h', needed by `pndriv.o'. Stop. i) edit the makefile and initially try just commenting out the line: pndriv.o : ./png.h ./pngconf.h ./zlib.h ./zconf.h by adding a preceding hash as follows: #pndriv.o : ./png.h ./pngconf.h ./zlib.h ./zconf.h If this does not work, try giving the correct paths for these files (use "locate png.h" to find where they are) -- usually this will be the following: pndriv.o : /usr/include/png.h /usr/include/pngconf.h /usr/include/zlib.h /usr/include/zconf.h Also add the above include path to the CFLAGC= variable, by amending the line "CFLAGC= xxx" to "CFLAGC= xxx -I/usr/include/" (e.g. on a mac you may need -I/sw/include/ or -I/opt/include/). Also, for the time being, comment out the line SHARED_LIB= (stuff) and replace it with a blank setting: SHARED_LIB= so that only the static library (libpgplot.a) is built. If you succeed with this you can try going back and uncommenting this line and typing "make" again to build the dynamic library. You may further need to add the path for the png library (libpng.a) to the LIBS= flag (e.g. LIBS=stuff -L/sw/lib/). j) now type "make" again, and you should obtain a successful build. Check that your installation is OK by running the demo programs pgdemo1, pgdemo2 etc. 3) ------------------- Compiling the code -------------------------------- Once you have a copy of PGPLOT installed, you can proceed with the SPLASH installation. Prior to doing so you should make sure that the PGPLOT_DIR environment variable is set (check using "echo $PGPLOT_DIR"). If not, add the following lines to your ~/.bashrc file (or the tcsh equivalent): export PGPLOT_DIR=/me/whereeveriputit/pgplot export PGPLOT_DEV=/xw (this sets the default PGPLOT device). export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PGPLOT_DIR Now, having untarred the SPLASH bundle: "tar xvfz splash-x.x.x.tar.gz", you should have a directory called splash/. Enter this directory: "cd splash" and have a look in the Makefile. Preset options for the most common Fortran compilers are given in the Makefile provided the variable SYSTEM is set appropriately (either on the command line or as an environment variable). On the command line, type "make SYSTEM=xxx BACKEND=pgplot" Where the SYSTEM corresponds to one of those listed in the Makefile, some of the most commonly used of which are: gfortran -- settings for the gfortran compiler g95 -- settings for the g95 compiler nagf95 -- settings for the NAG f95 compiler sunf95 -- settings for the Sun f95 compiler ifort -- settings for the Intel Fortran Compiler pgf90 -- settings for the Portland Group Fortran 90 compiler Options which compile and also link PGPLOT on specific machines are: mymac -- settings for a Mac using g95 with PGPLOT installed via fink ukaff1a -- settings for the ukaff1a supercomputer If you have the PGPLOT_DIR environment variable set then linking with PGPLOT and associated libraries (libpng, libX11) *might just work* and you should find a whole bunch of splash binaries (asplash, gsplash, ssplash etc.) have been created. If so, then you are done with compilation and can skip directly to step 4 or 5. If not, read on. ---- porting to a new SYSTEM ------ If none of the SYSTEM variables corresponds to your local Fortran compiler, it should be reasonably straightforward to add your own. For example you will need to set the Fortran compiler and flags to your local version, e.g.. F90C = g95 F90FLAGS = -O and importantly, on some compilers you will need to make sure that backslashes (\) are treated as normal characters. For example on the following compilers you should use: intel fortran compiler: F90C = ifc/ifort F90FLAGS = -O -nbs portland group fortran: F90C = pgf90 F90FLAGS = -O -Mbackslash Secondly, you will need to modify the system-dependent routines for your compiler. These are specified via the settings: SYSTEMFILE = system_f2003.f90 which uses Fortran 2003 standard calls (supported by almost all recent compilers). Alternatively the file system_unix.f90 should also work for older (and newer) unix-based compilers. A file system_unix_NAG is included for the NAG f95 compiler. The whole idea of SPLASH is that filenames should be read off the command line, though sometimes there can be library clashes (e.g. two libraries defining the same function) which make these calls not work. In this case there are some slightly convoluted workarounds given in the online FAQ. If you have ported to a new compiler, please send me an email with your new SYSTEM variable and I will add it to the SPLASH Makefile, both for you and for others (then you can just update directly). Remember it is always likely that someone else in the same department may download SPLASH one day... ------------------- linking with PGPLOT libraries --------------------- Secondly the compiler must be able to link to the PGPLOT and X11 libraries on your system. The settings for these are given at the top of the Makefile by the settings: X11LIBS= -lX11 PGPLOTLIBS= -lpgplot If that works at a first attempt, take a moment to think several happy thoughts about your system administrator. If these libraries are not found, you will need to enter the library paths by hand. On most systems this is something like: X11LIBS= -L/usr/X11R6/lib -lX11 PGPLOTLIBS= -L/usr/local/pgplot -lpgplot (assuming the PGPLOT libraries are in the /usr/local/pgplot directory and the X11 libraries are in /usr/X11R6/lib). If this does not work, try using the "locate" command to find the libraries on your system: user> locate libpgplot user> locate libX11 If, having found the PGPLOT and X11 libraries, the program still won't compile, it is usually because the PGPLOT on your system has been compiled with a different compiler to the one you are using, and the libraries from that compiler must also be linked. For g77-compiled PGPLOT (very common) the relevant library is g2c, so use: PGPLOTLIBS = -L/usr/local/pgplot -lpgplot -lg2c similarly for gfortran-compiled PGPLOT the appropriate library is libgfortran, so use -lgfortran, and for g95-compiled PGPLOT, libg95, so use -lg95 (where again you may need the -L flag to specify the location of the libxxx.a or libxxx.so file). If the PNG drivers are incorporated into the PGPLOT installation, the -lpng libraries must also be added. *** A good, failsafe way of working out exactly what flags are required to link to PGPLOT on your system is to look in the PGPLOT makefile itself, at exactly which flags were used to compile the pgdemo programs (pgdemo1, pgdemo2 -- you should also run these to check that PGPLOT has been installed correctly). For example in the PGPLOT makefile on my Mac (located in /sw/lib/pgplot), the flags are LIBS=-L/usr/X11R6/lib -lX11 -L/sw/lib -laquaterm -Wl,-framework -Wl,Foundation so these are the flags needed to link to PGPLOT, PLUS fink had used g77 to compile pgplot, so I also need to add the -lg2c flag (see above). Obviously a way round having to work out which compiler libraries to add is to simply make sure that PGPLOT has been compiled with the same compiler you are using to compile SPLASH. It is worth remembering also, that if you work in an Astronomy department, it is almost certain that there will be someone in your department who uses PGPLOT on your system and knows how to link to it, so it is worth asking around. *** Another last-resort option for linking to PGPLOT is to compile with the static libraries explictly on the command line. To do this simply set the variable STATICLIBS, e.g. STATICLIBS=/usr/local/libpgplot.a then no -lpgplot is needed and the library is treated like a normal .o file at compile time. Have a look at the online FAQ for some tips on common problems with linking to PGPLOT (e.g. font problems on 64-bit machines). 4) -------------- reading your data format ------------------- The basic "splash" binary is quite general and will read any data where columns correspond to different quantities and rows correspond to each particle (actually I use splash to plot graphs for nearly all data in this form, whether SPH or not) -- it will also sensibly skip header lines which do not have the same number of columns. However, it is ultimately desirable to use SPLASH to directly visualise the (binary) output of your code. If you are using a widely used SPH code (e.g. GADGET, GASOLINE, VINE, DRAGON), it is reasonably likely that I have already written a read data subroutine which will read your dumps. If your format is not amongst those distributed, then BEFORE you start writing your own routine, please consider whether or not a routine to read your format would be of more general use (e.g. to other users of your code). If so, PLEASE email me to request a new read_data routine for your format, by sending an email attaching: a) an example dump and b) the source code from the routine which wrote the dump file. Then I can write a read for your format that can be added to the SPLASH repository and distributed in all future versions. Whilst I aim never to change the interface to the read_data routines, it is not impossible that some changes may occur somewhere down the line (or enhanced functionality -- for example the more advanced data reads are able to read only the required columns for a given plot from the file, rather than the whole file). If you *really* want to hack one yourself it is best to look at some of the other examples and change the necessary parts to suit your data files. Note that reading directly from unformatted data files is *much* faster than reading from formatted (ascii) output. Just to get started you can use the read_data_ascii.f90 which reads from ascii files, but this will not enable the full rendering capabilities until you specify the location of the density, h and particle mass in the arrays (via the parameters ih, irho and ipmass in the set_labels subroutine which is part of the read_data file). If you do end up writing your own, again, please email me the end result so I can add it to the officially supported data reads. This also makes it much easier for you to upgrade to newer versions as you do not require a locally customised version. 5) ----- running splash/ making pretty pictures ----- For detailed help on how to use SPLASH, refer to the (quite extensive) userguide in the /docs directory or on the splash web page. Have fun! And remember, if you get stuck you can always email me... (it doesn't hurt). Daniel Price daniel.price@monash.edu splash/.gitignore000644 000766 000000 00000000426 13261626263 014756 0ustar00dpricewheel000000 000000 *.mod *.o ?splash docs/splash.aux docs/splash.log docs/splash.toc docs/splash.out docs/splash.blg docs/splash.haux docs/*.bib docs/html/*.png docs/html/*.html docs/html/splash.css cairo-* pixman-* bin ref *otherendian.f90 Portfile* .DS_Store releasenotes* ChangeLog giza* tests splash/VERSION000644 000766 000000 00000000075 13261626263 014036 0ustar00dpricewheel000000 000000 splash, version 2.8.0, built on Fri 6 Apr 2018 18:15:47 AEST splash/install-cairo.sh000755 000766 000000 00000010754 13261626263 016073 0ustar00dpricewheel000000 000000 #!/bin/bash # # Script for splash 2.x that retrieves and installs # both cairo and pixman # # (these are the only dependencies for the giza backend, # are often already present as system libraries but # may need to be installed by the user if not) # # An alternative is to use your inbuilt package manager to install cairo # e.g. # Debian/Ubuntu: # sudo apt-get install libcairo2-dev # Fedora/Red Hat/CentOS: # sudo yum install cairo-devel # OpenSUSE: # zypper install cairo-devel # MacPorts: # sudo port install cairo # cairodist=cairo-1.12.18.tar.xz; pixmandist=pixman-0.32.6.tar.gz; xzdist=xz-5.2.1.tar.gz; installprefix=$PWD/giza; url="http://cairographics.org/releases"; xzurl="http://tukaani.org/xz/"; # #--Check that the giza directory is present. # This is not strictly necessary, but it means we install cairo and # pixman to the same location as the giza libraries and linking of # giza with cairo will work automatically. # if [ ! -d $installprefix ]; then echo; echo " ERROR: directory $installprefix does not exist "; echo; echo " $0 should be run from the root-level splash directory"; echo " with giza already downloaded as a subdirectory of splash"; echo; exit 1; fi # #--if not already downloaded, retrieve the pixman and cairo tarballs using wget # if [ ! -f $cairodist ] || [ ! -f $pixmandist ]; then echo "cairo and/or pixman not downloaded"; if !(type -p wget); then echo "ERROR: $0 requires the \"wget\" command, which is not present on"; echo "your system. Instead, you will need to download the following files by hand:"; echo echo "$url/$cairodist"; echo "$url/$pixmandist"; echo; echo "To proceed, download these files, place them in the current directory and try again" exit; else wget $url/$pixmandist; wget $url/$cairodist; fi fi # #--proceed with installation # if [ ! -f $cairodist ] || [ ! -f $pixmandist ]; then echo; echo "ERROR: cairo and/or pixman download failed. Please try again"; echo; else echo "$pixmandist and $cairodist found in current dir"; # #--unpack the distribution files # echo "unpacking pixman..."; tar xfz $pixmandist; echo "unpacking cairo..."; tar -Jxf $cairodist; pixmandir=${pixmandist/.tar.gz/}; cairodir=${cairodist/.tar.xz/}; if [ ! -d $pixmandir ]; then echo; echo "ERROR: pixman failed to unpack (no directory $pixmandir)"; echo; exit $?; fi if [ ! -d $cairodir ]; then echo; echo "ERROR: cairo failed to unpack (no directory $cairodir)"; echo; # #--install xzutils if tar -Jxf fails... # echo "Attempting to download xzutils in order to unpack cairo..." wget $xzurl/$xzdist; tar xfz $xzdist; xzdir=${xzdist/.tar.gz/}; cd $xzdir; xzinstalldir=/tmp/xz-tmp/; ./configure --prefix=$xzinstalldir; make || ( echo; echo "ERROR during xzutils build"; echo; exit $? ); make install || ( echo; echo "ERROR installing xzutils into $xzinstalldir"; echo; exit $? ); cd ..; # #--now unpack cairo using xz utils # ${xzinstalldir}/bin/unxz $cairodist; tar xf ${cairodist/.xz/}; if [ ! -d $cairodir ]; then echo; echo "ERROR: cairo failed to unpack even with xz downloaded (no directory $cairodir)"; echo; exit $?; fi fi # #--install pixman # cd $pixmandir; ./configure --prefix=$installprefix || ( echo; echo "ERROR during pixman config"; echo; exit $? ); make || ( echo; echo "ERROR during pixman build"; echo; exit $? ); make install || ( echo; echo "ERROR installing pixman into $installdir"; echo; exit $? ); cd ..; # #--install cairo # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$installprefix; export PKG_CONFIG_PATH=$installprefix/lib/pkgconfig; cd $cairodir; ./configure --prefix=$installprefix || ( echo; echo "ERROR during cairo config"; echo; exit $? ); make || ( echo; echo "ERROR during cairo build"; echo; exit $? ); make install || ( echo; echo "ERROR installing cairo into $installdir"; echo; exit $? ); cd ..; #--cleanup #rm -rf $pixmandir; #rm -rf $cairodir; # #--finish # echo; echo "cairo and pixman installation successful"; echo "type \"make\" to compile SPLASH"; echo; echo "You should also add the following line to your .bashrc or equivalent:"; echo; if [[ `uname` =~ Darwin ]]; then echo "export DYLD_LIBRARY_PATH=\$DYLD_LIBRARY_PATH:$installprefix"; else echo "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$installprefix"; fi echo fi splash/scripts/000755 000766 000000 00000000000 13261626263 014453 5ustar00dpricewheel000000 000000 splash/build/000755 000766 000000 00000000000 13261626263 014063 5ustar00dpricewheel000000 000000 splash/LICENCE000644 000766 000000 00000035445 13261626263 013764 0ustar00dpricewheel000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS splash/src/000755 000766 000000 00000000000 13261626263 013553 5ustar00dpricewheel000000 000000 splash/src/read_data_scw.f90000644 000766 000000 00000021743 13261626263 016662 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM MATTHEW BATE'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns use mem_allocation implicit none integer, intent(IN) :: indexstart,ipos integer, intent(OUT) :: nstepsread character(LEN=*), intent(IN) :: rootname integer, parameter :: maxptmass = 100 integer :: i,j,ifile,ierr integer :: npart_max, nstep_max logical :: iexist character(LEN=3) :: fileno character(LEN=LEN(rootname)+10) :: dumpfile integer :: nprint, n1, n2, nptmass integer, dimension(:), allocatable :: isteps, iphase integer, dimension(maxptmass) :: listpm real(doub_prec), dimension(:,:), allocatable :: dattemp real(doub_prec), dimension(:), allocatable :: dummy real(doub_prec) :: udisti,umassi,utimei, timei, gammai real(doub_prec) :: escap,tkin,tgrav,tterm,trad real(doub_prec) :: dtmax, rhozero, RK2 nstepsread = 0 ierr = 0 nstep_max = 0 npart_max = 0 ifile = 0 ! !--for rootnames without the '00', read all files starting at #1 ! if (len_trim(rootname).lt.7) then ifile = 1 if (len_trim(rootname).eq.4) then write(fileno,"(i1,i1,i1)") ifile/100,mod(ifile,100)/10,mod(ifile,10) dumpfile = rootname(1:4)//fileno elseif (len_trim(rootname).eq.5) then write(fileno,"(i1,i1)") ifile/10,mod(ifile,10) dumpfile = rootname(1:5)//trim(fileno) endif else dumpfile = trim(rootname) endif ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: ',trim(dumpfile),' file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 ncolumns = 15 ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 print "(1x,a)",'reading Stuart Whitehouse''s modified Bate-code format' do while (iexist) write(*,"(23('-'),1x,a,1x,23('-'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,file=dumpfile,status='old',form='unformatted') ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,end=55,iostat=ierr) udisti,umassi,utimei,nprint if (ierr /= 0) then print "(a)",'*** ERROR reading timestep header ***' close(15) return endif print*,'nprint = ',nprint if (.not.allocated(dat) .or. nprint.gt.npart_max) then npart_max = max(npart_max,INT(1.1*nprint)) call alloc(npart_max,nstep_max,ncolumns) endif rewind(15) ! !--loop over the timesteps in this file ! over_steps_in_file: do npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+10,maxcol) endif ! !--allocate a temporary array for double precision variables ! if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(npart_max,ncolumns)) ! !--allocate a dummy arrays for data I want to throw away ! if (allocated(dummy)) deallocate(dummy) allocate(dummy(npart_max)) if (allocated(isteps)) deallocate(isteps) allocate(isteps(npart_max)) if (allocated(iphase)) deallocate(iphase) allocate(iphase(npart_max)) ! !--now read the timestep data in the dumpfile ! read(15,end=55,iostat=ierr) udisti, umassi, utimei, & nprint, n1, n2, timei, gammai, rhozero, RK2, & (dattemp(i,7), i=1, nprint), & escap, tkin, tgrav, tterm, trad, & (dattemp(i,1), i=1, nprint), (dattemp(i,2), i=1, nprint), & (dattemp(i,3), i=1, nprint), (dattemp(i,4), i=1, nprint), & (dattemp(i,5), i=1, nprint), (dattemp(i,6), i=1, nprint), & (dattemp(i,8), i=1, nprint), (dattemp(i,9), i=1, nprint), & (dattemp(i,10), i=1, nprint), (dattemp(i,11), i=1, nprint), & (dattemp(i,12), i=1, nprint), (dattemp(i,13), i=1, nprint), & (dattemp(i,14), i=1, nprint), (dattemp(i,15), i=1, nprint), & (dummy(i),i=1,nprint), & dtmax, (isteps(i), i=1,nprint), (iphase(i),i=1,nprint), & nptmass, (listpm(i), i=1,nptmass) if (ierr /= 0) then print "(a)",'*** ERROR READING TIMESTEP ***' cycle over_steps_in_file else nstepsread = nstepsread + 1 endif ! !--convert to single precision ! print *,'step ',j,': ntotal = ',nprint print "(a)",' converting to single precision... ' dat(1:nprint,1:ncolumns,j) = real(dattemp(1:nprint,1:ncolumns)) deallocate(dattemp) deallocate(dummy) deallocate(isteps) deallocate(iphase) npartoftype(1,j) = nprint npartoftype(2:maxparttypes,j) = 0 gamma(j) = real(gammai) time(j) = real(timei) j = j + 1 enddo over_steps_in_file 55 continue ! !--reached end of file ! close(15) print*,'>> end of dump file: nsteps =',j-1,'ntot = ',sum(npartoftype(:,j-1)) ! !--if just the rootname has been input, ! set next filename and see if it exists ! ifile = ifile + 1 if (len_trim(rootname).eq.4) then write(fileno,"(i1,i1,i1)") ifile/100,mod(ifile,100)/10,mod(ifile,10) dumpfile = rootname(1:4)//fileno inquire(file=dumpfile,exist=iexist) elseif (len_trim(rootname).eq.5) then write(fileno,"(i1,i1)") ifile/10,mod(ifile,10) dumpfile = rootname(1:5)//trim(fileno) inquire(file=dumpfile,exist=iexist) else iexist = .false. ! exit loop endif enddo return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ih = 7 ! smoothing length label(ih) = 'h' iutherm = 8 ! thermal energy label(iutherm) = 'u' label(9) = 'e' ipmass = 10 ! particle mass label(ipmass) = 'particle mass' label(11) = 'rkappa' label(12) = 'cv' irho = 13 ! location of rho in data array label(irho) = '\gr' label(14) = 'rlambda' label(15) = 'eddington factor' label(ix(1:ndim)) = labelcoord(1:ndim,1) do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 1 !!maxparttypes labeltype(1) = 'gas' labeltype(2) = 'ghost' labeltype(3) = 'sink' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_pbob_utils.c000644 000766 000000 00000011774 13261626263 020057 0ustar00dpricewheel000000 000000 /* * This subroutine performs the calls to the PBOB c routines * */ #include #include #include #include #include #include #include extern PBOB *ReadPBOB(char *file_name); extern PARTICLE *ReadParticle(char *file_name,int time_slice,int start_indx,int N); void set_blocklabel(int *icol, char *name); void read_pbob_data_fromc(int icol, int istep, int np, double temparr[np],int itype[np], char *tag); void read_pbob_header(char *filename, int *npart, int *ncol, int *nsteps, int *ndim, int *ndimV, double *time, int *ierr) { *npart = 0; *ierr = 0; *ncol = 0; *nsteps = 0; *time = 0.; *npart = 0; *ndim = 2; *ndimV = 2; *ncol = 15; /* hard wired */ PBOB *pbob = NULL; if ((pbob=ReadPBOB(filename))==NULL) { *ierr = 1; return; } /*printf(" cluster_size = %i \n",pbob->cluster_size); printf(" length_units = %s \n",pbob->length_units); printf(" mass_units = %s \n",pbob->mass_units); printf(" time_units = %s \n",pbob->time_units); printf(" energy_units = %s \n",pbob->internal_energy_units); */ printf(" short title = %s \n",pbob->short_title); printf(" total_particles = %llu \n",pbob->total_particles); printf(" nn_k = %i \n",pbob->nn_k); printf(" time slices = %i \n",pbob->number_of_time_slices); /* printf(" offset = %i \n",pbob->first_particle_byte_offset); printf(" length = %i \n",pbob->particle_length_bytes); */ printf(" endian = %s \n",pbob->endian_str); printf(" version = %s \n",pbob->pbob_version); *nsteps = pbob->number_of_time_slices; *npart = pbob->total_particles; *time = pbob->time; /* printf(" ascii_header: \n"); printf("%s\n",pbob.ascii_header); */ } void read_pbob_data(char *filename, int npart, int time_slice, double *timeval, int *ierr) { PARTICLE *particle = NULL; *ierr = 0; int start_indx = 0; int N = npart; int i; *timeval = -1.; /*printf(" time_slice = %i\n",time_slice);*/ if ((particle=ReadParticle(filename,time_slice,start_indx,N))==NULL) { *ierr = 2; return; } int species[npart]; for (i=0;i 0, reads only from the filename rootname(ireadfile) ! if = 0, no data read, just call labelling and exact_params ! module getdata implicit none public :: get_data, get_labels integer, private :: ncolumnsfirst private contains subroutine get_data(ireadfile,gotfilenames,firsttime,iposinfile) use asciiutils, only:ucase use exact, only:read_exactparams use filenames, only:rootname,nstepsinfile,nfiles,nsteps,maxfile,ifileopen,iposopen use limits, only:set_limits use settings_data, only:ncolumns,iendatstep,ncalc,ivegotdata, & DataisBuffered,iCalcQuantities,ndim,iverbose,ntypes, & iRescale,required,ipartialread,lowmemorymode,debugmode use settings_data, only:iexact,buffer_steps_in_file use particle_data, only:dat,time,npartoftype,maxcol use prompting, only:prompt use labels, only:labeltype use calcquantities, only:calc_quantities use settings_units, only:units use timing, only:wall_time,print_time use settings_part, only:iplotpartoftype use geomutils, only:set_coordlabels use adjustdata, only:adjust_data_codeunits implicit none integer, intent(in) :: ireadfile logical, intent(in) :: gotfilenames logical, intent(in), optional :: firsttime integer, intent(in), optional :: iposinfile logical :: setlimits,isfirsttime logical, parameter :: dotiming = .true. integer :: i,istart,ierr,itype,nplot,ipos,nsteps_read real :: t1,t2 if (.not.gotfilenames) then if (nfiles.le.0 .or. nfiles.gt.maxfile) nfiles = 1 call prompt('Enter number of files to read ',nfiles,1,maxfile) do i=1,nfiles call prompt('Enter filename to read',rootname(i),noblank=.true.) enddo endif ! !--set everything to zero initially ! ncolumns = 0 ncalc = 0 nsteps = 0 istart = 1 ivegotdata = .false. ifileopen = ireadfile DataIsBuffered = .false. ipartialread = .false. isfirsttime = .false. if (present(firsttime)) isfirsttime = firsttime if (isfirsttime) then iverbose = 1 else iverbose = 0 endif ! ipos is the offset for *which* timestep to read from files containing multiple steps ipos = 1 if (present(iposinfile)) then if (iposinfile > 0) ipos = iposinfile endif iposopen = 0 ! !--nstepsinfile is initialised to negative ! this is set progressively as files are read ! for non-buffered data file 1 is read and the rest are assumed to be the same ! then these files are corrected as they are read. By initialising nstepsinfile ! to negative, this means that if we get dud files (with nstepsinfile=0) we ! know that this is really the file contents (not just an initialised value of nstepsinfile) ! and can skip the file on the second encounter (see timestepping.f90) ! if (isfirsttime) then nstepsinfile(:) = -1 ncolumnsfirst = 0 required = .true. if (lowmemorymode) required = .false. call endian_info() endif if (ireadfile.le.0) then ! !--read all steps from the data file ! nstepsinfile(1:nfiles) = 0 required = .true. print "(/a)",' reading ALL dumpfiles into memory' !call endian_info() do i=1,nfiles call read_data(rootname(i),istart,ipos,nstepsinfile(i)) istart = istart + nstepsinfile(i) ! number of next step in data array if (nstepsinfile(i).gt.0 .and. ncolumnsfirst.eq.0 .and. ncolumns.gt.0) then ncolumnsfirst = ncolumns elseif (nstepsinfile(i).gt.0 .and. ncolumns.ne.ncolumnsfirst) then print "(a,i2,a,i2,a)",' WARNING: file contains ',ncolumns, & ' columns, which differs from ',ncolumnsfirst,' read previously' ncolumns = max(ncolumns,ncolumnsfirst) endif enddo nsteps = istart - 1 if (nsteps.gt.0) then ivegotdata = .true. DataIsBuffered = .true. else ncolumns = 0 endif print "(a,i6,a,i3)",' >> Finished data read, nsteps = ',nsteps,' ncolumns = ',ncolumns ! !--set labels (and units) for each column of data ! !print "(/a)",' setting plot labels...' if (ivegotdata .and. ncolumns.gt.0) then call get_labels call adjust_data_codeunits ! !--do some basic sanity checks ! call check_data_read() endif if (iRescale .and. any(abs(units(0:ncolumns)-1.0).gt.tiny(units))) then !write(*,"(/a)") ' rescaling data...' do i=1,ncolumns if (abs(units(i)-1.0).gt.tiny(units) .and. abs(units(i)).gt.tiny(units)) then dat(:,i,1:nsteps) = dat(:,i,1:nsteps)*units(i) endif enddo time(1:nsteps) = time(1:nsteps)*units(0) endif ! !--reset coordinate and vector labels (depending on coordinate system) ! Need to do this BEFORE calculating quantities ! if (ivegotdata) call set_coordlabels(ncolumns) ! !--calculate various additional quantities ! if (nsteps.ge.1 .and. iCalcQuantities) then call calc_quantities(1,nsteps) endif ! !--set plot limits ! if (ierr.gt.0 .and. ivegotdata .and. nstepsinfile(1).ge.1) then call set_limits(1,nsteps,1,ncolumns+ncalc) endif elseif (ireadfile.gt.0) then ! !--read from a single file only ! nstepsinfile(ireadfile) = 0 !if (isfirsttime) print "(/a)",' reading single dumpfile' if (dotiming) call wall_time(t1) call read_data(rootname(ireadfile),istart,ipos,nstepsinfile(ireadfile)) ! !--do some basic sanity checks ! if (debugmode) print "(a,i3)",' DEBUG: ncolumns from data read = ',ncolumns if (debugmode) print "(a,i3)",' DEBUG: nsteps in file = ',nstepsinfile(ireadfile) if (buffer_steps_in_file) then nsteps_read = nstepsinfile(ireadfile) else nsteps_read = 1 iposopen = ipos endif !--try different endian if failed the first time !if (nstepsinfile(ireadfile).eq.0) then ! print "(a)",' trying different endian' ! call read_data_otherendian(rootname(ireadfile),istart,nstepsinfile(ireadfile)) !endif if (dotiming) then call wall_time(t2) if (t2-t1.gt.1.) then if (ipartialread) then call print_time(t2-t1,'time for (partial) data read = ') print* else call print_time(t2-t1,'time for data read = ') print* endif endif ! do i=1,ncolumns+ncalc ! print*,' required(',i,') = ',required(i) ! enddo endif !!print*,'nsteps in file = ',nstepsinfile(ireadfile) if (ANY(nstepsinfile(1:ireadfile).gt.0)) ivegotdata = .true. if (.not.ivegotdata) ncolumns = 0 ! !--set ncolumns on first step only ! if (ivegotdata .and. ncolumnsfirst.eq.0 .and. ncolumns.gt.0) then ncolumnsfirst = ncolumns endif !--override ncolumns from file and warn if different to first file if (ncolumnsfirst.gt.0 .and. nstepsinfile(ireadfile).gt.0) then if (ncolumns.ne.ncolumnsfirst) then print "(1x,a,i2,a,i2,a)",'WARNING: file contains ',ncolumns, & ' columns, which differs from ',ncolumnsfirst,' read previously' if (ncolumns.lt.ncolumnsfirst) then print "(10x,a,i2,/)",'setting data = 0 for columns > ',ncolumns dat(:,ncolumns+1:min(ncolumnsfirst,maxcol),1:nstepsinfile(ireadfile)) = 0. elseif (ncolumns.gt.ncolumnsfirst) then print "(10x,a,i2,a)",'extra data beyond column ',ncolumnsfirst,' will be ignored' print "(10x,a,/)",'(read this file first to use this data)' endif ncolumns = ncolumnsfirst endif endif ! !--assume there are the same number of steps in the other files ! which have not been read ! do i=1,nfiles if (nstepsinfile(i).eq.-1) then nstepsinfile(i) = nstepsinfile(ireadfile) endif enddo nsteps = sum(nstepsinfile(1:nfiles)) ! !--set labels (and units) for each column of data ! allow this to be overridden by the presence of a splash.columns file ! !!print "(/a)",' setting plot labels...' if (ivegotdata .and. ncolumns.gt.0) then call get_labels call adjust_data_codeunits call check_data_read() endif if (iRescale .and. any(abs(units(0:ncolumns)-1.0).gt.tiny(units))) then if (debugmode) write(*,"(a)") ' rescaling data...' do i=1,min(ncolumns,maxcol) if (abs(units(i)-1.0).gt.tiny(units) .and. abs(units(i)).gt.tiny(units)) then dat(:,i,1:nsteps_read) = dat(:,i,1:nsteps_read)*units(i) endif enddo do i=1,nsteps_read if (time(i).gt.-0.5*huge(0.)) time(i) = time(i)*units(0) enddo endif ! !--reset coordinate and vector labels (depending on coordinate system) ! Need to do this BEFORE calculating quantities ! if (ivegotdata) call set_coordlabels(ncolumns) ! !--calculate various additional quantities ! if (nsteps_read.gt.0 .and. iCalcQuantities) then if (ipartialread .and. .not.any(required(ncolumns+1:))) then !--for partial data reads do a "pretend" call to calc quantities ! just to get ncalc and column labels right call calc_quantities(1,nsteps_read,dontcalculate=.true.) else call calc_quantities(1,nsteps_read) endif endif ! !--only set limits if reading the first file for the first time ! setlimits = (ireadfile.eq.1 .and. ivegotdata .and. nstepsinfile(1).ge.1) if (.not.present(firsttime)) then setlimits = .false. elseif (.not.firsttime) then setlimits = .false. endif if (setlimits) then call set_limits(1,nsteps_read,1,ncolumns+ncalc) !--also set iendatstep the first time around iendatstep = nsteps endif endif ! !--check for errors in data read / print warnings ! if (ndim.ne.0 .and. ncolumns.gt.0 .and. nsteps.gt.0 .and. iverbose.eq.1) then if (sum(npartoftype(:,1)).gt.0 .and. npartoftype(1,1).eq.0) then print "(a)",' WARNING! DATA APPEARS TO CONTAIN NO '//trim(ucase(labeltype(1)))//' PARTICLES' itype = 0 nplot = 0 do while (nplot.eq.0 .and. itype < ntypes) itype = itype + 1 if (npartoftype(itype,1).gt.0) then iplotpartoftype(itype) = .true. nplot = nplot + npartoftype(itype,1) print "(a)",' (plotting of '//trim(labeltype(itype))//' particles turned ON)' endif enddo print* endif endif ! !--read exact solution parameters from files if present ! if (iexact.ne.0) then if (ireadfile.lt.0) then call read_exactparams(iexact,rootname(1),ierr) else call read_exactparams(iexact,rootname(ireadfile),ierr) endif endif return end subroutine get_data !---------------------------------------------------------------------- ! ! The following is a wrapper routine for the call to set_labels which ! overrides the label setting from the splash.columns file if present. ! Also adds the units label if the data has been rescaled. ! !---------------------------------------------------------------------- subroutine get_labels use asciiutils, only:read_asciifile use filenames, only:fileprefix,unitsfile use labels, only:label,unitslabel use settings_data, only:ncolumns,iRescale,iverbose use settings_units, only:read_unitsfile use particle_data, only:maxcol use params, only:maxplot implicit none logical :: iexist integer :: nlabelsread,ierr,i call set_labels ! !--check that label settings are sensible, fix where possible ! call check_labels ! !--look for a .columns file to override the default column labelling ! inquire(file=trim(fileprefix)//'.columns',exist=iexist) nlabelsread = 0 if (iexist) then call read_asciifile(trim(fileprefix)//'.columns',nlabelsread,label(1:min(ncolumns,maxcol,maxplot))) if (nlabelsread.lt.ncolumns) & print "(a,i3)",' end of file in '//trim(fileprefix)//'.columns file: labels read to column ',nlabelsread endif ! !--read units file and change units if necessary ! call read_unitsfile(trim(unitsfile),ncolumns,ierr,iverbose) ! !--add units labels to labels ! if (iRescale) then do i=1,min(ncolumns,maxcol,maxplot) if (index(label(i),trim(unitslabel(i))).eq.0) label(i) = trim(label(i))//trim(unitslabel(i)) enddo endif end subroutine get_labels !---------------------------------------------------------------- ! ! utility to check that label settings are sensible ! !---------------------------------------------------------------- subroutine check_labels use settings_data, only:ndim,ndimV,ncolumns,iverbose use labels, only:ix,irho,ih,ipmass use particle_data, only:masstype use settings_render, only:icolour_particles implicit none integer :: i,ndimset if (ndim.ne.0 .and. ncolumns.gt.0) then if (ndim.lt.0 .or. ndim.gt.3) then print "(a)",' ERROR with ndim setting in data read, using ndim=3' ndim = 3 endif if (ndimV.lt.0 .or. ndimV.gt.3) then print "(a)",' ERROR with ndimV setting in data read, using ndimV=3' ndimV = 3 endif if (ndim.ge.2 .and. any(ix(2:ndim).eq.ix(1))) then print "(a)",' WARNING: error in ix setting in set_labels: fixing ' ix(1) = max(ix(1),1) do i=2,ndim ix(i) = i enddo endif if (ndim.ge.1) then do i=1,ndim if (ix(i).le.0) then ix(i) = i print "(a)",' WARNING: ndim > 0 but zero ix setting in set_labels: fixing ' endif enddo endif ndimset = 0 do i=1,3 if (ix(i).ne.0) ndimset = ndimset + 1 enddo if (ndimset.ne.ndim) then print "(2(a,i2))",' ERROR: labels for ',ndimset,& ' coordinates set but got ndim = ',ndim endif if (irho.gt.ncolumns .or. irho.lt.0) then print "(a)",' ERROR with irho setting in data read' irho = 0 endif if (ih.gt.ncolumns .or. ih.lt.0) then print "(a)",' ERROR with ih setting in data read ' ih = 0 endif if (ipmass.gt.ncolumns .or. ipmass.lt.0) then print "(a)",' ERROR with ipmass setting in data read' ipmass = 0 endif if (iverbose.ge.1) then if (irho.eq.0 .or. ih.eq.0) then print "(4(/,a))",' WARNING: Rendering capabilities cannot be enabled', & ' until positions of density, smoothing length and particle', & ' masses are known (specified using the integer variables ', & ' irho,ih and ipmass in the read_data routine)' icolour_particles = .true. elseif (irho.gt.0 .and. ih.gt.0 .and. ipmass.eq.0 .and. all(masstype(:,:).lt.tiny(0.))) then print "(2(/,a))",' WARNING: Particle masses not read as array but mass not set:', & ' RENDERING WILL NOT WORK! ' endif endif endif end subroutine check_labels !---------------------------------------------------------------- ! ! utility to check things about the data read ! !---------------------------------------------------------------- subroutine check_data_read use params, only:maxplot,maxparttypes use settings_data, only:ncolumns,ndim,ndimV,ntypes,ivegotdata use particle_data, only:npartoftype,iamtype,dat use labels, only:labeltype implicit none integer :: i,j,ntoti,nunknown,itype integer, dimension(maxparttypes) :: noftype if (ncolumns.lt.0) then print "(a)",' ERROR: ncolumns < 0 in data read' ncolumns = 0 elseif (ncolumns.gt.maxplot) then print "(/,71('*'),/,'*',a,i3,a,'*',/,71('*'))",& ' ERROR: ncolumns > ',maxplot,' in data read: cannot list all columns in menu ' ncolumns = maxplot endif if (ndim.gt.3) then; print "(a)",' ERROR: ndim > 3 in data read, setting ndim = 3'; ndim = 3; endif if (ndim.lt.0) then; print "(a)",' ERROR: ndim < 0 in data read, setting ndim = 0'; ndim = 0; endif if (ndimV.gt.3) then; print "(a)",' ERROR: ndimV > 3 in data read, setting ndimV = 3'; ndimV = 3; endif if (ndimV.lt.0) then; print "(a)",' ERROR: ndimV < 0 in data read, setting ndimV = 0'; ndimV = 0; endif if (ntypes.lt.0) then; print "(a)",' ERROR: ntypes < 0 in data read'; ntypes = 0; endif if (allocated(npartoftype)) then if (size(npartoftype(:,1)).lt.ntypes) then print "(a)",' ERROR: too many particle types for allocated array size in data read' ntypes = size(npartoftype(:,1)) endif do i=1,ntypes do j=1,size(npartoftype(i,:)) if (npartoftype(i,j).lt.0) then print "(a)",' ERROR: number of '//trim(labeltype(i))//' particles < 0 in data read' npartoftype(i,j) = 0 endif enddo enddo ntoti = sum(npartoftype(:,1)) if (ntoti > size(dat(:,1,1))) then print "(2(a,i10),a)",' ERROR: size of dat array (',size(dat(:,1,1)),& ') too small for number of particles (',ntoti,')' ivegotdata = .false. endif !if (debugmode) then ! !--for mixed type storage, check that the number of particles ! of each type adds up to npartoftype ! ntoti = sum(npartoftype(:,1)) noftype(:) = 0 nunknown = 0 if (size(iamtype(:,1)).ge.ntoti) then do i=1,ntoti itype = iamtype(i,1) if (itype.gt.0 .and. itype.le.ntypes) then noftype(itype) = noftype(itype) + 1 else nunknown = nunknown + 1 endif enddo do itype=1,ntypes if (npartoftype(itype,1).ne.noftype(itype)) then print "(a,i10,a,i10)",' ERROR in data read: got ',noftype(itype),' '//trim(labeltype(itype))// & ' particles from iamtype, but npartoftype = ',npartoftype(itype,1) endif enddo if (nunknown.gt.0) then print "(a,i10,a)",' ERROR in data read: got ',nunknown, & ' particles of unknown type in iamtype array from data read' endif endif !endif endif end subroutine check_data_read !------------------------------------- ! ! simple utility to spit out native ! endian-ness ! !------------------------------------- subroutine endian_info implicit none logical :: bigendian bigendian = IACHAR(TRANSFER(1,"a")) == 0 if (bigendian) then print "(a)",' native byte order on this machine is BIG endian' !--we no longer warn for little endian, as this is now most common !else ! print "(a)",' native byte order on this machine is LITTLE endian' endif end subroutine endian_info end module getdata splash/src/read_data_dragon.f90000644 000766 000000 00000064677 13261626263 017355 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2011 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR OUTPUT FROM THE DRAGON CODE ! HANDLES BOTH ASCII AND BINARY FILES ! ! THE FOLLOWING ENVIRONMENT VARIABLES AFFECT THIS FORMAT: ! ! DSPLASH_EXTRACOLS : set to number of extra columns to read (after itype) ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Partial data read implemented means that columns with ! the 'required' flag set to false are not read (read is therefore much faster) !------------------------------------------------------------------------- module unit_constants integer, parameter :: DP = selected_real_kind(p=15) ! double precision ! Length units in metres real(kind=DP),parameter :: r_pc = 3.08568E16_DP ! parsec real(kind=DP),parameter :: r_au = 1.49597870E11_DP ! astronomical unit real(kind=DP),parameter :: r_sun = 6.96E8_DP ! solar radius real(kind=DP),parameter :: r_earth = 6.371E6_DP ! Earth radius ! Mass units in kilograms real(kind=DP),parameter :: m_sun = 1.98892E30_DP ! solar mass real(kind=DP),parameter :: m_jup = 1.8986E27_DP ! Jupiter mass real(kind=DP),parameter :: m_earth = 5.9736E24_DP ! Earth mass ! Time units in seconds real(kind=DP),parameter :: myr = 3.1556952E13_DP ! megayear real(kind=DP),parameter :: yr = 3.1556952E7_DP ! year real(kind=DP),parameter :: day = 8.64E4_DP ! day end module unit_constants subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,iamtype,npartoftype,time,gamma,maxpart,maxcol,maxstep use params use settings_data, only:ndim,ndimV,ncolumns,ncalc,required,ipartialread,ntypes use settings_units, only:unitzintegration, unit_interp use mem_allocation, only:alloc use labels, only:label,labeltype,labelzintegration use system_utils, only:ienvironment implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile integer, parameter :: iunit = 16 integer :: i,j,icol,ierr,iambinaryfile,itype integer :: ncolstep,npart_max,nstep_max,ntoti,nlastcol,nextracols logical :: iexist,reallocate,doubleprec character(len=11) :: fmt character(len=50) :: string integer :: nei_want,nei_min,nmax integer, dimension(:), allocatable :: iparttype integer, dimension(20) :: idata real, dimension(50) :: rdata real(doub_prec), dimension(50) :: rdatadb real :: timetemp,gammatemp,runit,massunit,sinksoft,sinkrad nstepsread = 0 if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(datfile)//': file not found ***' return endif ! !--set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 ncolstep = ndim + ndimV + 4 ! pos x 3, vel x 3, temp, h, rho, mass nlastcol = ncolstep nextracols = ienvironment('DSPLASH_EXTRACOLS',0) if (nextracols.gt.0 .and. nextracols.le.99) then print "(a,i2,a)",' ASSUMING ',nextracols,' EXTRA COLUMNS BEYOND ITYPE' ncolstep = ncolstep + nextracols else nextracols = 0 endif ncolumns = ncolstep call set_labels ! !--read data from snapshots ! j = istepstart write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! ! !--determine whether file is binary or ascii, open it and read the header ! inquire(file=datfile,form=fmt) !print*,'fmt = ',fmt ! select case(trim(adjustl(fmt))) ! case('UNFORMATTED') ! iambinaryfile = 1 ! open(unit=iunit,file=datfile,status='old',form='unformatted',iostat=ierr) ! write (6,*) "Compiler identified as UNFORMATTED" ! case('FORMATTED') ! iambinaryfile = 0 ! open(unit=iunit,file=datfile,status='old',form='formatted',iostat=ierr) ! write (6,*) "Compiler identified as FORMATTED" ! case default !--if compiler cannot distinguish the two, try binary first, then ascii iambinaryfile = -1 open(unit=iunit,file=datfile,status='old',form='unformatted',iostat=ierr) ! end select if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(datfile)//' ***' return endif ! !--read the file header ! try binary format first, and if unsuccessful try ascii ! doubleprec = .true. if (iambinaryfile.eq.1) then print "(a)",' reading binary dragon format ' call read_dragonheader_binary(iunit,ierr) else if (iambinaryfile.eq.0) then print "(a)",' reading ascii dragon format ' call read_dragonheader_ascii(iunit,ierr,iambinaryfile) else call read_dragonheader_binary(iunit,ierr) if (ierr.eq.0) then !--if successful binary header read, file is doubleprec binary iambinaryfile = 1 print "(a)",' reading binary dragon format ' print "(a)",' Double precision file' else !--otherwise, close binary file, and assume file is single precision binary doubleprec = .false. close(unit=iunit) iambinaryfile = 1 open(unit=iunit,file=datfile,status='old',form='unformatted',iostat=ierr) call read_dragonheader_binary(iunit,ierr) if (ierr.eq.0) then print "(a)",' reading binary dragon format ' print "(a)",' Single precision file' else print "(a)",' reading ascii dragon format ' iambinaryfile = 0 close(unit=iunit) open(unit=iunit,file=datfile,status='old',form='formatted',iostat=ierr) call read_dragonheader_ascii(iunit,ierr,iambinaryfile) if (ierr/=0) then print "(a)",' ERROR reading ascii file header: wrong endian binary? ' close (iunit) ndim = 0 ncolumns = 0 return end if end if endif endif ! !--get values of quantities from the header ! ntoti = idata(1) nmax = idata(3) nei_want = idata(10) nei_min = idata(12) !--check for errors in integer header (either from corrupt file or wrong endian) if (ntoti.le.0 .or. ntoti.gt.1.e10 .or. nmax.lt.0 & .or. nei_want.lt.0 .or. nei_want.gt.1e6 .or. nei_min.lt.0) then if (iambinaryfile.eq.1) then print "(a)",' ERROR reading binary file header: wrong endian? ' else print "(a)",' ERROR reading ascii file header ' endif ndim = 0 ncolumns = 0 close(unit=iunit) return endif if (doubleprec) then timetemp = rdatadb(1) runit = rdatadb(21) massunit = rdatadb(22) gammatemp = rdatadb(26) sinksoft = rdatadb(27) sinkrad = rdatadb(38) else timetemp = rdata(1) runit = rdata(21) massunit = rdata(22) gammatemp = rdata(26) sinksoft = rdata(27) sinkrad = rdata(38) end if !--assume first that the file is single precision, check values are sensible, if not try double ! if (iambinaryfile.eq.1) then ! if (timetemp.lt.0. .or. runit.lt.0. .or. massunit.lt.0. .or. gammatemp.lt.0. & ! .or. gammatemp.gt.6.) then ! print "(a)",' double precision file' ! doubleprec = .true. ! rewind(iunit) ! call read_dragonheader_binary(iunit,ierr) ! else ! print "(a)",' single precision file' ! endif ! endif print*,'time : ',timetemp print*,'gamma : ',gammatemp print*,'n_total : ',ntoti if (ierr /= 0) then if (iambinaryfile.eq.1) then print "(a)",' ERROR reading real part of binary file header ' else print "(a)",' ERROR reading real part of ascii file header ' endif ndim = 0 ncolumns = 0 close(unit=iunit) return endif ! !--if successfully read header, increment the nstepsread counter ! nstepsread = nstepsread + 1 ! !-- now work out dimensionless weight unit and z integration unit ! call find_weights(unit_interp,unitzintegration,labelzintegration) ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntoti) else ! if first time, save on memory npart_max = int(ntoti) endif endif if (j.ge.maxstep .and. j.ne.1) then nstep_max = j + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncolstep+ncalc,maxcol),mixedtypes=.true.) endif ! !--copy header into header arrays ! npartoftype(:,j) = 0 npartoftype(1,j) = ntoti time(j) = timetemp gamma(j) = gammatemp ! !--read particle data ! if (ntoti.gt.0) then if (iambinaryfile.eq.1) then call read_dragonbody_binary(iunit,ierr) else call read_dragonbody_ascii(iunit,ierr) endif else ntoti = 0 npartoftype(1,i) = 0 dat(:,:,i) = 0. endif if (allocated(iamtype)) then !--relabel particle types call set_types(iamtype(:,j),ntoti,npartoftype(:,j)) endif if (any(npartoftype(2:,j).ne.0)) then do itype=1,ntypes if (npartoftype(itype,j).gt.0) then string = ' ' write(string,"(a)") 'n_'//trim(labeltype(itype)) write(string(18:len(string)),"(a)") ':' print*,trim(string),' ',npartoftype(itype,j) endif enddo endif ! !--set flag to indicate that only part of this file has been read ! if (.not.all(required(1:ncolstep))) ipartialread = .true. ! !--close data file and return ! if (allocated(iparttype)) deallocate(iparttype) close(unit=iunit) return contains !---------------------------------------------------- ! binary header read !---------------------------------------------------- subroutine read_dragonheader_binary(iunitb,ierr) implicit none integer, intent(in) :: iunitb integer, intent(out) :: ierr read(iunitb,end=55,iostat=ierr) idata if (doubleprec) then read(iunitb,end=55,iostat=ierr) rdatadb else read(iunitb,end=55,iostat=ierr) rdata endif return 55 continue !print "(a)",' ERROR: end of file in binary header read' ierr = -1 return end subroutine read_dragonheader_binary !---------------------------------------------------- ! ascii header read !---------------------------------------------------- subroutine read_dragonheader_ascii(iunita,ierr,iwarn) implicit none integer, intent(in) :: iunita,iwarn integer, intent(out) :: ierr do i=1,size(idata) read(iunita,*,end=55,iostat=ierr) idata(i) enddo do i=1,size(rdata) read(iunita,*,end=55,iostat=ierr) rdata(i) enddo doubleprec = .false. return 55 continue if (iwarn.ge.0) print "(a)",' ERROR: end of file in binary header read' ierr = -1 return end subroutine read_dragonheader_ascii !---------------------------------------------------- ! binary body read !---------------------------------------------------- subroutine read_dragonbody_binary(iunitb,ierr) implicit none integer, intent(in) :: iunitb integer, intent(out) :: ierr real(doub_prec), dimension(:,:), allocatable :: dummyx real(doub_prec), dimension(:), allocatable :: dummy integer, dimension(:), allocatable :: idumtype integer :: icol if (doubleprec .and. any(required(1:ndim+ndimV))) then allocate(dummyx(3,ntoti),stat=ierr) if (ierr /= 0) then print *,' ERROR allocating memory' goto 56 endif endif !--positions if (any(required(1:ndim))) then if (doubleprec) then read(iunitb,end=55,iostat=ierr) dummyx(1:ndim,1:ntoti) do i=1,ntoti dat(i,1:ndim,j) = real(dummyx(1:ndim,i)) enddo else read(iunitb,end=55,iostat=ierr) (dat(i,1:ndim,j),i=1,ntoti) endif if (ierr /= 0) print*,' WARNING: errors reading positions ' else read(iunitb,end=55,iostat=ierr) if (ierr /= 0) print*,' WARNING: error skipping positions ' endif !--velocities if (any(required(ndim+1:ndim+ndimV))) then if (doubleprec) then read(iunitb,end=55,iostat=ierr) dummyx(1:ndimV,1:ntoti) do i=1,ntoti dat(i,ndim+1:ndim+ndimV,j) = real(dummyx(1:ndimV,i)) enddo else read(iunitb,end=55,iostat=ierr) (dat(i,ndim+1:ndim+ndimV,j),i=1,ntoti) endif if (ierr /= 0) print*,' WARNING: errors reading velocities ' else read(iunitb,end=55,iostat=ierr) if (ierr /= 0) print*,' WARNING: error skipping velocities ' endif if (doubleprec .and. any(required(ndim+ndimV+1:ncolstep))) then allocate(dummy(ntoti),stat=ierr) if (ierr /= 0) then print*,' ERROR allocating memory' goto 56 endif endif !--the rest do icol = ndim+ndimV+1,nlastcol if (required(icol)) then if (doubleprec) then read(iunitb,end=55,iostat=ierr) dummy(1:ntoti) dat(1:ntoti,icol,j) = real(dummy(1:ntoti)) else read(iunitb,end=55,iostat=ierr) dat(1:ntoti,icol,j) endif if (ierr /= 0) print*,' WARNING: errors reading '//trim(label(icol)) else read(iunitb,end=55,iostat=ierr) if (ierr /= 0) print*,' WARNING: error skipping '//trim(label(icol)) endif enddo if (size(iamtype(:,j)).gt.1) then allocate(idumtype(ntoti),stat=ierr) if (ierr /= 0) then print*,'error reading type, assuming all gas' iamtype(1:ntoti,j) = 1 else read(iunitb,end=55,iostat=ierr) idumtype(1:ntoti) iamtype(1:ntoti,j) = idumtype(1:ntoti) endif deallocate(idumtype) if (ierr /= 0) print*,' WARNING: error reading itype' endif !--extra columns beyond itype do icol = nlastcol+1,nlastcol+nextracols if (required(icol)) then if (doubleprec) then read(iunitb,end=55,iostat=ierr) dummy(1:ntoti) dat(1:ntoti,icol,j) = real(dummy(1:ntoti)) else read(iunitb,end=55,iostat=ierr) dat(1:ntoti,icol,j) endif if (ierr /= 0) print*,' WARNING: errors reading '//trim(label(icol)) else read(iunitb,end=55,iostat=ierr) if (ierr /= 0) print*,' WARNING: error skipping '//trim(label(icol)) endif enddo if (allocated(dummyx)) deallocate(dummyx) if (allocated(dummy)) deallocate(dummy) return 55 continue print "(a)",' ERROR: end of file in binary read' 56 continue ierr = -1 if (allocated(dummyx)) deallocate(dummyx) if (allocated(dummy)) deallocate(dummy) return end subroutine read_dragonbody_binary !---------------------------------------------------- ! ascii body read !---------------------------------------------------- subroutine read_dragonbody_ascii(iunita,ierr) implicit none integer, intent(in) :: iunita integer, intent(out) :: ierr integer :: nerr,idumtype !--positions nerr = 0 do i=1,ntoti read(iunita,*,end=55,iostat=ierr) dat(i,1:ndim,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print*,' WARNING: ',nerr,' errors reading positions ' !--velocities nerr = 0 do i=1,ntoti read(iunita,*,end=55,iostat=ierr) dat(i,ndim+1:ndim+ndimV,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print*,' WARNING: ',nerr,' errors reading velocities ' !--the rest if (any(required(ndim+ndimV+1:nlastcol))) then do icol = ndim+ndimV+1,nlastcol nerr = 0 do i=1,ntoti read(iunita,*,end=55,iostat=ierr) dat(i,icol,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print*,' WARNING: ',nerr,' errors reading '//trim(label(icol)) enddo endif !--particle type if (size(iamtype(:,j)).gt.1) then nerr = 0 do i=1,ntoti read(iunita,*,end=55,iostat=ierr) idumtype iamtype(i,j) = idumtype if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print*,' WARNING: ',nerr,' errors reading itype' endif !--the rest if (any(required(nlastcol+1:nlastcol+nextracols))) then do icol = nlastcol+1,nlastcol+nextracols nerr = 0 do i=1,ntoti read(iunita,*,end=55,iostat=ierr) dat(i,icol,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print*,' WARNING: ',nerr,' errors reading '//trim(label(icol)) enddo endif return 55 continue print "(a)",' ERROR: end of file in ascii read' ierr = -1 return end subroutine read_dragonbody_ascii !---------------------------------------------------- ! translate types into order (for old dragon read) !---------------------------------------------------- subroutine set_types(itypei,ntotal,noftype) implicit none integer(kind=int1), dimension(:), intent(inout) :: itypei integer, intent(in) :: ntotal integer, dimension(:), intent(out) :: noftype integer :: ngas,nsink,nbnd,ncloud,nsplit,nunknown,nstar !--types ! 1 gas ! -1 sink ! -2 star ! 6 boundary (fixed) ! 9 intercloud (hydro only) ! 4 split particle (obsolete?) ! !--we translate these into ! 1 gas ! 2 boundary ! 3 sink ! 4 intercloud ! 5 split ! 6 unknown / the rest ! 7 star ! ngas = 0 nsink = 0 nbnd = 0 ncloud = 0 nsplit = 0 nunknown = 0 nstar = 0 do i=1,ntotal select case(itypei(i)) case(1) ngas = ngas + 1 itypei(i) = 1 case(-1) nsink = nsink + 1 itypei(i) = 3 case(6) nbnd = nbnd + 1 itypei(i) = 2 case(9) ncloud = ncloud + 1 itypei(i) = 4 case(4) nsplit = nsplit + 1 itypei(i) = 5 case(-2) nstar = nstar + 1 itypei(i) = 6 case default nunknown = nunknown + 1 ! itypei(i) = 6 write (6,*) "Unknown particle type ", itypei(i), "!!" stop end select enddo noftype(1) = ngas noftype(2) = nbnd noftype(3) = nsink noftype(4) = ncloud noftype(5) = nsplit noftype(6) = nstar if (sum(noftype(1:6)).ne.ntotal) then print "(a)",' INTERNAL ERROR setting number in each type in dragon read' endif return end subroutine set_types end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass,ih,irho use params use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = ndim+1 label(ivx+ndimV) = 'temperature' ih = ivx + ndimV + 1 irho = ih + 1 ! location of rho in data array ipmass = irho + 1 ! !--set labels of the quantities read in ! label(ix(1:ndim)) = labelcoord(1:ndim,1) label(irho) = 'density' !label(iutherm) = 'u' label(ipmass) = 'particle mass' label(ih) = 'h' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo !--set labels for each particle type ! ntypes = 6 labeltype(1) = 'gas' labeltype(2) = 'boundary' labeltype(3) = 'sink' labeltype(4) = 'cloud' labeltype(5) = 'split' labeltype(6) = 'star' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. UseTypeInRenderings(4) = .true. UseTypeInRenderings(5) = .true. UseTypeInRenderings(6) = .false. !----------------------------------------------------------- return end subroutine set_labels subroutine find_weights(out_unit_interp,out_unitzintegration,out_labelzintegration) use labels, only:ipmass,ih,irho use params use settings_data, only:ndim use unit_constants use system_commands, only:get_environment implicit none real(doub_prec), intent(out) :: out_unit_interp real, intent(out) :: out_unitzintegration character(len=20), intent(out) :: out_labelzintegration real(doub_prec) :: dm_unit, dh_unit, drho_unit, dr_unit logical :: do_dimweight, do_zintegration character(len=20) :: rho_length_label real(doub_prec) :: rho_length character(len=20) :: r_unit ! length unit character(len=20) :: m_unit ! mass unit character(len=20) :: rho_unit ! density unit character(len=20) :: h_unit ! smoothing length unit call get_environment("DRAGON_R_UNIT",r_unit) call get_environment("DRAGON_M_UNIT",m_unit) call get_environment("DRAGON_RHO_UNIT",rho_unit) call get_environment("DRAGON_H_UNIT",H_unit) out_unit_interp = 1.0 out_unitzintegration = 1.0 out_labelzintegration = "" do_dimweight = .TRUE. do_zintegration = .TRUE. ! Length unit in S.I. units (m) if (r_unit=="") then print*,'No positions or no position units!' print*,'Set environment variable DRAGON_R_UNIT to:' print*,' pc, au, r_sun, r_earth, km, m, cm or 1 (dimensionless)' do_zintegration = .FALSE. dr_unit = 1._DP else if (r_unit=="pc") then dr_unit = r_pc else if (r_unit=="au") then dr_unit = r_au else if (r_unit=="r_sun") then dr_unit = r_sun else if (r_unit=="r_earth") then dr_unit = r_earth else if (r_unit=="km") then dr_unit = 1000.0_DP else if (r_unit=="m") then dr_unit = 1.0_DP else if (r_unit=="cm") then dr_unit = 0.01_DP else if (r_unit=="1") then dr_unit = 1._DP else print*,'Unknown position unit ', r_unit, '!' do_zintegration = .FALSE. dr_unit = 1._DP end if ! Length unit in S.I. units (m) if (h_unit=="") then print*,'No smoothing lengths or no smoothing length units!' print*,'Set environment variable DRAGON_H_UNIT to: ' print*,' pc, au, r_sun, r_earth, km, m, cm or 1 (dimensionless)' do_dimweight = .FALSE. dh_unit = 1._DP else if (h_unit=="pc") then dh_unit = r_pc else if (h_unit=="au") then dh_unit = r_au else if (h_unit=="r_sun") then dh_unit = r_sun else if (h_unit=="r_earth") then dh_unit = r_earth else if (h_unit=="km") then dh_unit = 1000.0_DP else if (h_unit=="m") then dh_unit = 1.0_DP else if (h_unit=="cm") then dh_unit = 0.01_DP else if (h_unit=="1") then dh_unit = 1._DP else print*,'Unknown smoothing length unit ', h_unit, '!' do_dimweight = .FALSE. dh_unit = 1._DP end if ! Mass units in S.I. units (kg) if (m_unit=="") then print*,'No masses or no mass units!' print*,'Set environment variable DRAGON_M_UNIT to:' print*,' m_sun, m_jup, m_earth, kg, g or 1 (dimensionless)' do_dimweight = .FALSE. dm_unit = 1._DP else if (m_unit=="m_sun") then dm_unit = m_sun else if (m_unit=="m_jup") then dm_unit = m_jup else if (m_unit=="m_earth") then dm_unit = m_earth else if (m_unit=="kg") then dm_unit = 1._DP else if (m_unit=="g") then dm_unit = 1.0E-3_DP else if (m_unit=="1") then dm_unit = 1._DP else print*,'Unknown mass unit ', m_unit, '!' do_dimweight = .FALSE. dm_unit = 1._DP end if ! Density units in S.I. units (i.e. kg/m^3) if (rho_unit=="") then print*,'No densities or no density units!' print*,'Set environment variable DRAGON_RHO_UNIT to:' if (ndim==3) print*,' m_sun_pc3, kg_m3, g_cm3 or 1 (dimensionless)' if (ndim==2) print*,' m_sun_pc2, kg_m2, g_cm2 or 1 (dimensionless)' if (ndim==1) print*,' 1 (dimensionless)' do_dimweight = .FALSE. do_zintegration = .FALSE. rho_length = 1._DP else if (rho_unit=="m_sun_pc3") then drho_unit = m_sun / (r_pc**3) rho_length = r_pc rho_length_label = "pc" else if (rho_unit=="m_sun_pc2") then drho_unit = m_sun / (r_pc**2) rho_length = r_pc rho_length_label = "pc" else if (rho_unit=="kg_m3") then drho_unit = 1.0_DP rho_length = 1.0_DP rho_length_label = "m" else if (rho_unit=="kg_m2") then drho_unit = 1.0_DP rho_length = 1.0_DP rho_length_label = "m" else if (rho_unit=="g_cm3") then drho_unit = 1.0E3_DP rho_length = 0.01_DP rho_length_label = "cm" else if (rho_unit=="g_cm2") then drho_unit = 10.0_DP rho_length = 0.01_DP rho_length_label = "cm" else if (rho_unit=="1") then drho_unit = 1._DP rho_length = 1._DP rho_length_label = "" else print*,'Unknown density unit ', rho_unit, '!' do_dimweight = .FALSE. do_zintegration = .FALSE. rho_length = 1._DP end if if (do_dimweight) then out_unit_interp = dm_unit/(drho_unit*dh_unit**ndim) else print*,'Cannot create dimensionless weight' print*,'(unnormalised rendered plots may be incorrect)' end if if (do_zintegration) then out_unitzintegration = dr_unit / rho_length out_labelzintegration = rho_length_label else print*,'Cannot set unitzintegration' print*,'(column density plots may be incorrect)' end if return end subroutine find_weights splash/src/colourbar.f90000644 000766 000000 00000053646 13261626263 016101 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------ ! Module containing routines related to plotting the colour bar ! in various styles !------------------------------------------------------------------------ module colourbar implicit none integer, parameter, public :: maxcolourbarstyles = 12 character(len=28), dimension(0:maxcolourbarstyles), parameter, public :: & labelcolourbarstyles = (/'no colour bar ', & 'vertical (right hand side) ', & 'horizontal (underneath plot)', & 'plot-hugging vertical ', & 'plot-hugging horizontal ', & 'one-sided vertical ', & 'one-sided horizontal ', & 'floating/inset vertical ', & 'floating/inset horizontal ', & 'vertical (left hand side) ', & 'horizontal (on top) ', & 'custom vertical ', & 'custom horizontal '/) integer, parameter, public :: maxfloatingstyles = 5 character(len=*), dimension(maxfloatingstyles), parameter, public :: & labelfloatingstyles = (/' 1) Top left ', & ' 2) Top right ', & ' 3) Bottom left ', & ' 4) Bottom right', & ' 5) Custom '/) ! !--these are settings that have default values but can ! be changed if required ! real, public :: ColourBarDisp = 5.0 real, public :: ColourBarWidth = 2. ! width in character heights logical, public :: iplotcolourbarlabel = .true. public :: plotcolourbar,incolourbar,incolourbarlabel,barisvertical public :: get_colourbarmargins,isfloating,adjustcolourbar,iscustombar public :: set_floating_bar_style real, private, save :: xlabeloffsetsave = 0. real, parameter, private :: dispall = 0.25 real, public :: ColourBarPosx = 0.01 ! default x pos of short/fat bars real, public :: ColourBarPosy = 0.05 ! default y pos of short/fat bars real, public :: ColourBarLen = 0.25 ! default length of short/fat bars character(len=10), public :: ColourBarFmtStr = 'BCMSTV ' private contains !-------------------------------------------------------- ! this subroutine plots the colour bar in various styles !-------------------------------------------------------- subroutine plotcolourbar(istyle,icolours,datmin,datmax,label,log, & xlabeloffset,vptxminfull,vptxmaxfull,vptyminfull,vptymaxfull) use plotlib, only:plot_set_exactpixelboundaries,plotlib_is_pgplot use plotlib, only:plot_bbuf,plot_ebuf,plot_qwin,plot_qvp,plot_qcs,& plot_svp,plot_swin,plot_imag,plot_box,plot_annotate,plot_gray,& plotlib_extend_pad implicit none integer, intent(in) :: istyle,icolours real, intent(in) :: datmin,datmax,xlabeloffset character(len=*), intent(in) :: label logical, intent(in) :: log real, intent(in), optional :: vptxminfull,vptxmaxfull,vptyminfull,vptymaxfull integer, parameter :: maxpixwedg = 400 real, dimension(6), parameter :: trans = (/-0.5,1.0,0.0,-0.5,0.0,1.0/) real, dimension(1,maxpixwedg) :: sampley real, dimension(maxpixwedg,1) :: samplex integer :: i,npixwedg real :: disp,width,xch,ych,dx real :: xmin,xmax,ymin,ymax,vptxmin,vptxmax,vptymin,vptymax real :: vptxmini,vptxmaxi,vptymini,vptymaxi real :: vptxminp,vptxmaxp,vptyminp,vptymaxp real :: xmaxpix,xminpix,yminpix,ymaxpix ! !--return on style 0 ! if (istyle.le.0) return ! !--set colour bar displacement and width in character heights ! width = ColourBarWidth xlabeloffsetsave = xlabeloffset ! !--start buffering ! call plot_bbuf ! !--query and save current viewport, page and character height settings ! call plot_qwin(xmin,xmax,ymin,ymax) call plot_qvp(0,vptxmin,vptxmax,vptymin,vptymax) call plot_qcs(0,xch,ych) !--if colour bar stretches across multiple plots, ! override settings for vptymin and vptymax with input values if (present(vptxminfull) .and. present(vptxmaxfull) .and. & present(vptyminfull) .and. present(vptymaxfull)) then vptxmini = vptxminfull vptxmaxi = vptxmaxfull vptymini = vptyminfull vptymaxi = vptymaxfull else vptxmini = vptxmin vptxmaxi = vptxmax vptymini = vptymin vptymaxi = vptymax endif disp = dispall select case(istyle) !------------------------ ! horizontal colour bar !------------------------ case(2,4,6,8,10,12) if (istyle.eq.4) disp = 0. ! plot-hugging ! !--set viewport for the wedge ! if (isfloating(istyle)) then vptxminp = vptxmini ! to vptxmaxp = vptxmaxi ! avoid vptyminp = vptymini ! compiler vptymaxp = vptymaxi ! warnings call barlimits(vptxmini,vptxmaxi,vptxminp,vptxmaxp,ColourBarPosx,ColourBarLen) call barlimits(vptymini,vptymaxi,vptyminp,vptymaxp,ColourBarPosy,ColourBarLen) vptymaxi = vptymini + width*ych elseif (istyle.eq.10) then ! on top of plot vptymini = vptymaxi + (disp+0.1)*ych vptymaxi = vptymini + width*ych else vptymaxi = vptymini - (disp + xlabeloffset)*ych vptymini = vptymaxi - width*ych endif call plot_svp(vptxmini,vptxmaxi,vptymini,vptymaxi) call plot_set_exactpixelboundaries() !--check number of pixels in colour bar call plot_qvp(3,xminpix,xmaxpix,yminpix,ymaxpix) npixwedg = max(min(int(xmaxpix-xminpix),maxpixwedg),2) ! !--fill array with all values from datmin to datmax ! dx = (datmax-datmin)/real(npixwedg-1) do i=1,npixwedg samplex(i,:) = datmin + (i-1)*dx enddo ! !--draw colour bar, by cleverly setting window size ! call plot_swin(1.0,real(npixwedg),0.0,1.0) if (abs(icolours).gt.0) then ! colour !--check if the colour bar will be more than 1024 pixels if ((xmaxpix-xminpix).le.1024 .or. .not.plotlib_is_pgplot) then ! !--the standard way is to use the default line below ! if (icolours.eq.1) then call plot_gray(samplex,npixwedg,1,1,npixwedg,1,1,datmin,datmax,trans,iextend=plotlib_extend_pad) else call plot_imag(samplex,npixwedg,1,1,npixwedg,1,1,datmin,datmax,trans,iextend=plotlib_extend_pad) endif else ! !--if > 1024 pixels, we instead use the following: ! this is a workaround for a PGPLOT bug with large colour bars ! (> 1024 device pixels long) - plot colour bar in two halves. ! this works up to 2048 pixels, really should divide by n. ! call plot_svp(vptxmini,vptxmaxi-0.5*(vptxmaxi-vptxmini),vptymini,vptymaxi) call plot_swin(1.0,real(npixwedg/2),0.0,1.0) call plot_set_exactpixelboundaries() call plot_imag(samplex,npixwedg,1,1,npixwedg/2,1,1,datmin,datmax,trans) call plot_svp(vptxmaxi-0.5*(vptxmaxi-vptxmini)-0.001,vptxmaxi,vptymini,vptymaxi) call plot_swin(real(npixwedg/2 + 1),real(npixwedg),0.0,1.0) call plot_set_exactpixelboundaries() call plot_imag(samplex,npixwedg,1,npixwedg/2+1,npixwedg,1,1,datmin,datmax,trans) call plot_svp(vptxmini,vptxmaxi,vptymini,vptymaxi) call plot_set_exactpixelboundaries() endif endif call plot_swin(datmin,datmax,0.0,1.0) ! !--draw labelled frame around the wedge ! if (istyle.eq.12) then call plot_box(ColourBarFmtStr,0.0,0,'BC',0.0,0) elseif (istyle.eq.10) then call plot_box('BCSMT',0.0,0,'BC',0.0,0) elseif (istyle.eq.4 .or. istyle.eq.6 .or. istyle.eq.8) then call plot_box('BNST',0.0,0,'BC',0.0,0) if (istyle.eq.6 .or. istyle.eq.8) call plot_box('C',0.0,0,' ',0.0,0) else call plot_box('BCNST',0.0,0,'BC',0.0,0) endif ! !--write the units label: the position is relative to the bottom of ! the wedge because of the way we have defined the viewport. ! For the horizontal colour bar this never needs to change ! (0.25 space + 1 character height for numeric labels + 0.25 space ! + 1 character height for actual label = 2.5 character heights) ! if (len_trim(label).gt.0 .and. iplotcolourbarlabel) then if (istyle==10) then call plot_annotate('T',2.5,0.5,0.5,trim(label)) else call plot_annotate('B',2.5,0.5,0.5,trim(label)) endif endif !------------------------------- ! vertical colour bar (default) !------------------------------- case default if (istyle.eq.3) disp = 0. ! plot-hugging ! !--set viewport for the wedge ! if (isfloating(istyle)) then vptxminp = vptxmini ! to vptxmaxp = vptxmaxi ! avoid vptyminp = vptymini ! compiler vptymaxp = vptymaxi ! warnings call barlimits(vptxmini,vptxmaxi,vptxminp,vptxmaxp,ColourBarPosx,ColourBarLen) call barlimits(vptymini,vptymaxi,vptyminp,vptymaxp,ColourBarPosy,ColourBarLen) vptxmaxi = vptxmini + width*xch elseif (istyle==9) then vptxmaxi = vptxmini - disp*xch vptxmini = vptxmaxi - width*xch else vptxmini = vptxmaxi + disp*xch vptxmaxi = vptxmini + width*xch endif call plot_svp(vptxmini,vptxmaxi,vptymini,vptymaxi) call plot_set_exactpixelboundaries() !--check number of pixels in colour bar call plot_qvp(3,xminpix,xmaxpix,yminpix,ymaxpix) npixwedg = max(min(int(ymaxpix-yminpix),maxpixwedg),2) dx = (datmax-datmin)/real(npixwedg-1) do i=1,npixwedg sampley(:,i) = datmin + (i-1)*dx enddo ! !--draw colour bar, by cleverly setting window size ! call plot_swin(0.0,1.0,0.0,real(npixwedg)) if (icolours.eq.1) then ! greyscale call plot_gray(sampley,1,npixwedg,1,1,1,npixwedg,datmin,datmax,trans,iextend=plotlib_extend_pad) elseif (abs(icolours).gt.0) then ! colour call plot_imag(sampley,1,npixwedg,1,1,1,npixwedg,datmin,datmax,trans,iextend=plotlib_extend_pad) endif call plot_swin(0.0,1.0,datmin,datmax) ! !--draw labelled frame around the wedge ! if (istyle.eq.11) then call plot_box('BC',0.0,0,ColourBarFmtStr,0.0,0) elseif (istyle.eq.9) then call plot_box('BC',0.0,0,'BCNSTV',0.0,0) elseif (istyle.eq.3 .or. istyle.eq.5 .or. istyle.eq.7) then call plot_box('BC',0.0,0,'CMSTV',0.0,0) if (istyle.eq.5 .or. istyle.eq.7) call plot_box(' ',0.0,0,'B',0.0,0) else call plot_box('BC',0.0,0,'BCMSTV',0.0,0) endif ! !--write the units label: the position is relative to the edge of ! the wedge because of the way we have defined the viewport. ! For the vertical colour bar ColourBarDisp is a set by default to ! the maximum size for the numeric label (written horizontally) - ! this is about 4 character heights for something like "-5 x 10^10" ! We allow the user to adjust this parameter to bring the label ! closer where the numeric labels are smaller (e.g. "-5"). ! if (len_trim(label).gt.0 .and. iplotcolourbarlabel) then if (istyle.eq.9) then call plot_annotate('L',ColourBarDisp+0.75,1.0,1.0,trim(label)) else call plot_annotate('R',ColourBarDisp+0.75,1.0,1.0,trim(label)) endif endif end select ! !--reset window and viewport ! call plot_svp(vptxmin,vptxmax,vptymin,vptymax) call plot_swin(xmin,xmax,ymin,ymax) call plot_ebuf return end subroutine plotcolourbar !------------------------------------------------------- ! query function to see if colour bar is plotted ! vertically or horizontally for a given style !------------------------------------------------------- logical function barisvertical(istyle) implicit none integer, intent(in) :: istyle barisvertical = .true. if (istyle.le.0) return select case(istyle) case(2,4,6,8,10,12) barisvertical = .false. case default barisvertical = .true. end select end function barisvertical !------------------------------------------------------- ! query function to see if a given position on ! the plot should lie within the colour bar or not !------------------------------------------------------- logical function incolourbar(istyle,iunits,xpt,ypt,xmin,xmax,ymin,ymax) use plotlib, only:plot_qcs implicit none integer, intent(in) :: istyle,iunits real, intent(in) :: xpt,ypt,xmin,xmax,ymin,ymax real :: xminbar,xmaxbar,yminbar,ymaxbar,xch,ych,barwidth incolourbar = .false. if (istyle.le.0) return select case(istyle) case(8,12) call barlimits(xminbar,xmaxbar,xmin,xmax,ColourBarPosx,ColourBarLen) call barlimits(yminbar,ymaxbar,ymin,ymax,ColourBarPosy,ColourBarLen) call plot_qcs(iunits,xch,ych) ymaxbar = yminbar + 2.*ColourBarWidth*ych if (iplotcolourbarlabel) then yminbar = yminbar - 3.0*ych else yminbar = yminbar - 2.0*ych endif if ((xpt.ge.xminbar .and. xpt.le.xmaxbar) .and. & (ypt.ge.yminbar .and. ypt.le.ymaxbar)) then incolourbar = .true. endif case(7,11) call barlimits(xminbar,xmaxbar,xmin,xmax,ColourBarPosx,ColourBarLen) call barlimits(yminbar,ymaxbar,ymin,ymax,ColourBarPosy,ColourBarLen) call plot_qcs(iunits,xch,ych) if (iplotcolourbarlabel) then barwidth = (2.*ColourBarWidth+0.75 + max(ColourBarDisp+0.75,0.0))*xch else barwidth = (2.*ColourBarWidth+0.75 + 5.0)*xch endif xmaxbar = xminbar + barwidth if ((xpt.ge.xminbar .and. xpt.le.xmaxbar) .and. & (ypt.ge.yminbar .and. ypt.le.ymaxbar)) then incolourbar = .true. endif case(9) ! colour bar on left if (xpt.lt.xmin) incolourbar = .true. case(10) ! colour bar on top if (ypt.gt.ymax) incolourbar = .true. case(2,4,6) if (ypt.lt.ymin) incolourbar = .true. case default if (xpt.gt.xmax) incolourbar = .true. end select return end function incolourbar !------------------------------------------------------- ! query function to see if a given position on ! the plot should lie within the colour bar label or not !------------------------------------------------------- logical function incolourbarlabel(istyle,iunits,xpt,ypt,xmin,xmax,ymin,ymax) use plotlib, only:plot_qcs implicit none integer, intent(in) :: istyle,iunits real, intent(in) :: xpt,ypt,xmin,xmax,ymin,ymax real :: xch,ych,disp,xminbar,xmaxbar,yminbar,ymaxbar incolourbarlabel = .false. if (iplotcolourbarlabel) then call plot_qcs(iunits,xch,ych) disp = dispall if (istyle.eq.3 .or. istyle.eq.4) disp = 0. select case(istyle) case(8,12) call barlimits(xminbar,xmaxbar,xmin,xmax,ColourBarPosx,ColourBarLen) if (ypt.lt.(ymin-(disp + xlabeloffsetsave + ColourBarWidth+2.0)*ych) .and. & ypt.gt.(ymin-(disp + xlabeloffsetsave + ColourBarWidth+3.0)*ych) .and. & xpt.gt.xminbar .and. xpt.lt.xmaxbar) incolourbarlabel = .true. case(7,11) call barlimits(yminbar,ymaxbar,ymin,ymax,ColourBarPosy,ColourBarLen) if (xpt.gt.(xmax+(disp + ColourBarWidth-0.25 + max(ColourBarDisp-0.25,0.0))*xch) .and. & xpt.lt.(xmax+(disp + ColourBarWidth+0.75 + max(ColourBarDisp+0.75,0.0))*xch) .and. & ypt.gt.yminbar .and. ypt.lt.ymaxbar) incolourbarlabel = .true. case(2,4,6) if (ypt.lt.(ymin-(disp + xlabeloffsetsave + ColourBarWidth+2.0)*ych) .and. & ypt.gt.(ymin-(disp + xlabeloffsetsave + ColourBarWidth+3.0)*ych)) incolourbarlabel = .true. case(9) if (xpt.lt.(xmin-(disp + ColourBarWidth-0.25 + max(ColourBarDisp-0.25,0.0))*xch) .and. & xpt.gt.(xmin-(disp + ColourBarWidth+0.75 + max(ColourBarDisp+0.75,0.0))*xch)) incolourbarlabel = .true. case(10) if (ypt.gt.(ymax+(disp + xlabeloffsetsave + ColourBarWidth+2.0)*ych) .and. & ypt.lt.(ymax+(disp + xlabeloffsetsave + ColourBarWidth+3.0)*ych)) incolourbarlabel = .true. case default if (xpt.gt.(xmax+(disp + ColourBarWidth-0.25 + max(ColourBarDisp-0.25,0.0))*xch) .and. & xpt.lt.(xmax+(disp + ColourBarWidth+0.75 + max(ColourBarDisp+0.75,0.0))*xch)) incolourbarlabel = .true. end select endif return end function incolourbarlabel !------------------------------------------ ! utility function to avoid repeated code !------------------------------------------ subroutine barlimits(barmin,barmax,posmin,posmax,pos,barlen) implicit none real, intent(out) :: barmin,barmax real, intent(in) :: posmin,posmax,pos,barlen real :: dpos dpos = (posmax - posmin) ! in case posmin and barmin are same variable barmin = posmin + pos*dpos barmax = barmin + barlen*dpos end subroutine barlimits !------------------------------------------------------- ! query function to get margins which should ! be allowed on the page in order to later plot ! the colour bar !------------------------------------------------------- subroutine get_colourbarmargins(istyle,xminmargin,xmaxmargin,yminmargin,ymaxmargin,barwidth) use plotlib, only:plot_qcs,plot_qvp implicit none integer, intent(in) :: istyle real, intent(inout) :: xminmargin,xmaxmargin,yminmargin,ymaxmargin real, intent(out) :: barwidth real :: xch,ych,vptxmin,vptxmax,vptymin,vptymax barwidth = 0. if (istyle.le.0) return call plot_qcs(0,xch,ych) call plot_qvp(0,vptxmin,vptxmax,vptymin,vptymax) if (barisvertical(istyle)) then if (iplotcolourbarlabel) then barwidth = (ColourBarWidth+0.75 + max(ColourBarDisp+0.75,0.0))*xch else barwidth = (ColourBarWidth+0.75 + 5.0)*xch endif if (isfloating(istyle)) then barwidth = max((ColourBarPosx-1.) + barwidth,0.) endif if (istyle==9) then xminmargin = xminmargin + barwidth else xmaxmargin = xmaxmargin + barwidth endif else if (iplotcolourbarlabel) then barwidth = (ColourBarWidth+3.0)*ych ! ie. width + 2.5 + 0.5 margin else barwidth = (ColourBarWidth+2.0)*ych ! ie. width + 1.5 + 0.5 margin endif if (isfloating(istyle)) then barwidth = max(-(ColourBarPosy - (barwidth - ColourBarWidth*ych)),0.) endif if (istyle==10) then ymaxmargin = ymaxmargin + barwidth + 0.35*ych else yminmargin = yminmargin + barwidth endif endif return end subroutine get_colourbarmargins !------------------------------------------------------- ! query function for floating colour bar styles !------------------------------------------------------- logical function isfloating(istyle) integer, intent(in) :: istyle select case(istyle) case(7,8,11,12) isfloating = .true. case default isfloating = .false. end select end function isfloating !------------------------------------------------------- ! query function for custom colour bar styles !------------------------------------------------------- logical function iscustombar(istyle) integer, intent(in) :: istyle if (istyle.eq.12 .or. istyle.eq.11) then iscustombar = .true. else iscustombar = .false. endif end function iscustombar !--------------------------------------------------------------------- ! utility function used when interactively changing colour bar limits !--------------------------------------------------------------------- subroutine adjustcolourbar(istyle,xpt1,ypt1,xpt2,ypt2,& xmin,xmax,ymin,ymax,barmin,barmax) implicit none integer, intent(in) :: istyle real, intent(in) :: xpt1,ypt1,xpt2,ypt2,xmin,xmax,ymin,ymax real, intent(inout) :: barmin,barmax real :: dbar,xminbar,xmaxbar,yminbar,ymaxbar if (istyle.eq.8 .or. istyle.eq.12) then !--floating horizontal bar xminbar = xmin + ColourBarPosx*(xmax - xmin) xmaxbar = xminbar + ColourBarLen*(xmax - xmin) else xminbar = xmin xmaxbar = xmax endif if (istyle.eq.7 .or. istyle.eq.11) then !--floating vertical bar yminbar = ymin + ColourBarPosy*(ymax - ymin) ymaxbar = yminbar + ColourBarLen*(ymax - ymin) else yminbar = ymin ymaxbar = ymax endif if (barisvertical(istyle)) then if ((ymaxbar-yminbar).gt.0.) then dbar = (barmax-barmin)/(ymaxbar-yminbar) else dbar = 0. endif barmax = barmin + (max(ypt1,ypt2)-yminbar)*dbar barmin = barmin + (min(ypt1,ypt2)-yminbar)*dbar else if ((xmaxbar-xminbar).gt.0.) then dbar = (barmax-barmin)/(xmaxbar-xminbar) else dbar = 0. endif barmax = barmin + (max(xpt1,xpt2)-xminbar)*dbar barmin = barmin + (min(xpt1,xpt2)-xminbar)*dbar endif end subroutine adjustcolourbar subroutine set_floating_bar_style(iColourBarStyle,iColourBarPos) integer, intent(in) :: iColourBarStyle,iColourBarPos select case(iColourBarPos) case(1) if (barisvertical(iColourBarStyle)) then ColourBarPosx = 0.01 ColourBarPosy = 0.74 else ColourBarPosx = 0.01 ColourBarPosy = 0.95 endif case(2) if (barisvertical(iColourBarStyle)) then ColourBarPosx = 0.82 ! minus width in ch ColourBarPosy = 0.74 else ColourBarPosx = 0.73 ColourBarPosy = 0.95 endif case(3) if (barisvertical(iColourBarStyle)) then ColourBarPosx = 0.01 ColourBarPosy = 0.01 else ColourBarPosx = 0.015 ColourBarPosy = 0.075 endif case(4) if (barisvertical(iColourBarStyle)) then ColourBarPosx = 0.82 ! minus width in ch ColourBarPosy = 0.01 else ColourBarPosx = 0.73 ColourBarPosy = 0.075 endif end select end subroutine set_floating_bar_style end module colourbar splash/src/exact_torus.f90000644 000766 000000 00000010301 13261626263 016426 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! Plots solution for equilibrium torus of Papaloizou & Pringle ! strictly valid for the midplane only (uses spherical r) ! ! Added by D. Price 16.1.06 ! ---------------------------------------------------------------------- module torus implicit none contains subroutine exact_torus(iplot,itorus,Mstar,Rtorus,AA,distortion,gamma,xplot,yplot,ierr) implicit none integer, intent(in) :: iplot,itorus real, intent(in) :: Mstar,Rtorus,AA,gamma,distortion real, intent(in), dimension(:) :: xplot real, intent(out), dimension(size(xplot)) :: yplot real :: term,densi,rxy integer, intent(out) :: ierr integer :: i,mytorus real :: ra2 integer, parameter :: nu = 2 real, parameter :: atorus = 0.2, currj0 = 1.0 ! ! check for errors ! ierr = 0 if (Mstar.le.0.) then print*,'error: mass <= 0 in exact_torus' ierr = 2 return elseif (Rtorus.lt.0.) then print*,'error: rtorus < 0 in exact_torus' ierr = 3 return endif mytorus = 1 select case(mytorus) ! !--Tokamak torus (in torus 'r' co-ordinate) ! case(2) if (nu.le.0 .or. (iplot.lt.4 .and. nu.gt.2)) then print*,'error: solution not found for nu value in tokamak torus' ierr = 5 return endif print*,' plotting tokamak torus' do i=1,size(xplot) ra2 = xplot(i)**2/atorus**2 if (nu.eq.1) then term = currj0**2*atorus**2*(1. - ra2)*(7.*ra2**2 - 23.*ra2 + 13.)/96. elseif (nu.eq.2) then term = currj0**2*atorus**2*(47. - 12.*ra2**5 + 75.*ra2**4 - 200.*ra2**3 + 270.*ra2**2 - 180*ra2)/720. endif if (abs(ra2) < tiny(ra2)) print*,'rho0 = ',term select case(iplot) case(1) !--density yplot(i) = Mstar*term**gamma case(2) !--pressure yplot(i) = term case(3) !--thermal energy yplot(i) = term case(4) !--Btheta if (xplot(i).gt.tiny(xplot(i))) then yplot(i) = 0.5*currj0*atorus**2/(nu+1)* & (1.-(1.-ra2)**(nu+1))/xplot(i) else yplot(i) = 0. endif case(5) !--Jphi current yplot(i) = currj0*(1. - ra2)**nu end select enddo ! !--Papaloizou & Pringle equilibrium torus ! case default if ((gamma-1.).le.1e-4) then print*,'error: exact solution not valid for isothermal eos' ierr = 4 return endif do i=1,size(xplot) if (iplot.ne.4) then !--plot quantities vs spherical r (assume z = 0) term = Mstar/(AA*Rtorus)*(gamma-1.)/gamma* & (Rtorus/xplot(i) - 0.5*(Rtorus/xplot(i))**2 - 1./(2.*distortion)) else !--plots with z (assume cyl r = Rtorus) rxy = sqrt(Rtorus**2 + xplot(i)**2) term = Mstar/(AA*Rtorus)*(gamma-1.)/gamma* & (Rtorus/rxy - 0.5 - 1./(2.*distortion)) endif if (term.gt.tiny(term)) then densi = term**(1./(gamma-1.)) else densi = 0. endif select case(iplot) case(1) !--density yplot(i) = densi case(2,4) !--pressure yplot(i) = AA*densi**gamma case(3) !--thermal energy yplot(i) = AA/(gamma-1.)*densi**(gamma-1.) end select enddo end select return end subroutine exact_torus end module torus splash/src/plotlib_giza.f90000644 000766 000000 00000040117 13261626263 016555 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !--------------------------------------------------------------------------- ! The plotlib module in SPLASH provides a consistent API so that SPLASH ! can be compiled against different graphics libraries as the backend ! ! This version provides an interface to giza, a plotting ! library written by Daniel Price & James Wetter. ! ! Giza implements basic 2D plotting functionality ! on top of the cairo graphics library ! ! Interface written by James Wetter and Daniel Price (2010) !--------------------------------------------------------------------------- module plotlib use giza, only: & plot_arro=>giza_arrow, & plot_annotate=>giza_annotate, & plot_band=>giza_band, & plot_bbuf=>giza_begin_buffer,& plot_box=>giza_box, & plot_circ=>giza_circle, & plot_close=>giza_close_device, & plot_curs=>giza_get_key_press, & plot_ebuf=>giza_end_buffer, & plot_end=>giza_close_device, & plot_env=>giza_set_environment, & plot_errb=>giza_error_bars, & plot_funx=>giza_function_x, & plot_label=>giza_label, & plot_line=>giza_line, & plot_lcur=>giza_mark_line, & plot_olin=>giza_mark_points, & plot_ncur=>giza_mark_points_ordered, & plot_page=>giza_change_page, & plot_poly=>giza_polygon, & plot_pt1=>giza_single_point, & plot_pt=>giza_points, & plot_ptxt=>giza_ptext, & plot_qch=>giza_get_character_height, & plot_qci=>giza_get_colour_index, & plot_qcir=>giza_get_colour_index_range, & plot_qcr=>giza_get_colour_representation, & plot_qfs=>giza_get_fill, & plot_qlw=>giza_get_line_width,& plot_qls=>giza_get_line_style,& plot_qlc=>giza_get_line_cap, & plot_qtxt=>giza_qtext, & plot_qwin=>giza_get_window, & plot_rect=>giza_rectangle, & plot_sah=>giza_set_arrow_style, & plot_scf=>giza_set_font, & plot_sch=>giza_set_character_height, & plot_sci=>giza_set_colour_index, & plot_scir=>giza_set_colour_index_range, & plot_scr=>giza_set_colour_representation, & plot_set_palette=>giza_set_colour_palette, & plot_sfs=>giza_set_fill, & plot_slc=>giza_set_line_cap, & plot_sls=>giza_set_line_style,& plot_slw=>giza_set_line_width, & plot_stbg=>giza_set_text_background, & plot_svp=>giza_set_viewport, & plot_swin=>giza_set_window, & plot_text=>giza_text, & plot_wnad=>giza_set_window_equal_scale, & plot_qcur=>giza_device_has_cursor, & plot_rgb_from_table=>giza_rgb_from_table, & giza_contour, & giza_get_character_size, & giza_get_surface_size, & giza_get_viewport, & giza_open_device, & giza_open_device_size, & giza_render, & giza_render_gray, & giza_render_transparent, & giza_set_colour_table, & giza_stop_prompting, & giza_start_prompting, & plot_left_click=>giza_left_click_f, & plot_right_click=>giza_right_click_f, & plot_middle_click=>giza_middle_click_f, & plot_shift_click=>giza_shift_click_f, & plot_scroll_up=>giza_scroll_up_f, & plot_scroll_down=>giza_scroll_down_f, & plot_scroll_left=>giza_scroll_left_f, & plot_scroll_right=>giza_scroll_right_f, & giza_vector, & giza_format_number, & giza_query_device, & giza_draw_pixels, & giza_colour_index_min,& giza_colour_index_max,& giza_extend_pad,& giza_extend_repeat, & giza_extend_reflect, & giza_extend_none implicit none logical, parameter :: plotlib_is_pgplot = .false. logical, parameter :: plotlib_supports_alpha = .true. integer, parameter :: plotlib_maxlinestyle = 6 integer, parameter :: plotlib_maxfillstyle = 5 integer, parameter :: plotlib_maxlinecolour = 16 integer, parameter :: plotlib_maxpalette = 7 integer, parameter :: plotlib_extend_pad = giza_extend_pad integer, parameter :: plotlib_extend_repeat = giza_extend_repeat integer, parameter :: plotlib_extend_reflect = giza_extend_reflect integer, parameter :: plotlib_extend_none = giza_extend_none public contains !--------------------------------------------- ! initialise the plotting library !--------------------------------------------- subroutine plot_init(devicein, ierr, papersizex, aspectratio, paperunits) use giza, only:giza_units_inches,giza_units_pixels,giza_units_mm implicit none character(len=*),intent(in) :: devicein integer,intent(out) :: ierr real, intent(in), optional :: papersizex,aspectratio integer, intent(in), optional :: paperunits real :: width,height integer :: units, id if (present(papersizex)) then width = papersizex if (present(aspectratio)) then height = width*aspectratio else height = width/sqrt(2.) endif if (present(paperunits)) then select case(paperunits) case(0) units = giza_units_pixels case(1) units = giza_units_inches case(2) units = giza_units_mm width = 10.*width height = 10.*height end select else units = giza_units_inches endif id = giza_open_device_size(devicein, 'splash', width, height, units) else id = giza_open_device(devicein,'splash') endif ! id<0 should return an error, but +ve id is OK select case(id) case(:-1) ierr = id case default ierr = 0 end select if(ierr.eq.0) then call giza_stop_prompting endif end subroutine plot_init subroutine plot_gray(a, idim, jdim, i1, i2, j1, j2, a1, a2, tr, iextend) integer,intent(in) :: IDIM, JDIM, I1, I2, J1, J2 real,intent(in) :: A(IDIM,JDIM), A1, A2, TR(6) real :: affine(6) integer, intent(in), optional :: iextend call convert_tr_to_affine(tr,affine) if (present(iextend)) then call giza_render_gray(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,a1,a2,iextend,affine) else call giza_render_gray(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,a1,a2,0,affine) endif end subroutine plot_gray subroutine plot_imag(a, idim, jdim, i1, i2, j1, j2, a1, a2, tr, iextend) integer,intent(in) :: IDIM, JDIM, I1, I2, J1, J2 real,intent(in) :: A(IDIM,JDIM), A1, A2, TR(6) real :: affine(6) integer, intent(in), optional :: iextend call convert_tr_to_affine(tr,affine) if (present(iextend)) then call giza_render(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,a1,a2,iextend,affine) else call giza_render(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,a1,a2,0,affine) endif end subroutine plot_imag subroutine plot_imag_alpha(dat, alpha, idim, jdim, i1, i2, j1, j2, a1, a2, tr, iextend) integer,intent(in) :: IDIM, JDIM, I1, I2, J1, J2 real,intent(in) :: dat(IDIM,JDIM), alpha(IDIM,JDIM), A1, A2, TR(6) real :: affine(6) integer, intent(in), optional :: iextend call convert_tr_to_affine(tr,affine) if (present(iextend)) then call giza_render(idim,jdim,dat,alpha,i1-1,i2-1,j1-1,j2-1,a1,a2,iextend,affine) else call giza_render(idim,jdim,dat,alpha,i1-1,i2-1,j1-1,j2-1,a1,a2,0,affine) endif end subroutine plot_imag_alpha subroutine plot_imag_transparent(a, idim, jdim, i1, i2, j1, j2, a1, a2, tr) integer,intent(in) :: IDIM, JDIM, I1, I2, J1, J2 real,intent(in) :: A(IDIM,JDIM), A1, A2, TR(6) real :: affine(6) call convert_tr_to_affine(tr,affine) call giza_render_transparent(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,a1,a2,0,affine) end subroutine plot_imag_transparent subroutine plot_ctab(l,r,g,b,nc,contra,bright) implicit none integer,intent(in) :: nc real,intent(in) :: l(nc),r(nc),g(nc),b(nc),contra,bright call giza_set_colour_table(l,r,g,b,nc,contra,bright) end subroutine plot_ctab subroutine plot_qvsz(units,x1,x2,y1,y2) use giza, only:giza_get_paper_size,giza_units_device implicit none real, intent(out) :: x1,x2,y1,y2 integer, intent(in) :: units x1 = 0. y1 = 0. call giza_get_paper_size(units,x2,y2) end subroutine plot_qvsz subroutine plot_bins(nbin,x,data,centre) integer, intent(in) :: nbin real, dimension(nbin), intent(in) :: x, data logical, intent(in) :: centre print*,' WARNING: plot_bins not implemented in giza' end subroutine plot_bins subroutine plot_qvp(units, x1, x2, y1, y2) implicit none integer,intent(in) :: units real,intent(out) :: x1, x2, y1, y2 call giza_get_viewport(units_giza(units),x1,x2,y1,y2) end subroutine plot_qvp subroutine plot_qcs(units,xch,ych) implicit none integer,intent(in) :: units real,intent(out) :: xch,ych call giza_get_character_size(units_giza(units),xch,ych) end subroutine plot_qcs subroutine plot_qcol(icolmin,icolmax) integer,intent(out) :: icolmin,icolmax icolmin = giza_colour_index_min icolmax = giza_colour_index_max end subroutine plot_qcol subroutine plot_scrn(ci,name,ier) implicit none integer,intent(in) :: ci character(len=*),intent(in) :: name integer,intent(out) :: ier print*,' WARNING: plot_scrn not implemented in giza' ier = 1 end subroutine plot_scrn subroutine plot_qinf(item,value,length) implicit none character(len=*),intent(in) :: item character(len=*),intent(out) :: value integer,intent(out) :: length character(len=10) :: datestring,timestring select case(item) case('VERSION','version') value = 'giza-0.1' case('STATE','state') print*,' WARNING: query for STATE not yet implemented in giza' case('USER','user') print*,' WARNING: query for USER not yet implemented in giza' case('NOW','now') call date_and_time(datestring,timestring) value = datestring(7:8)//'-'//datestring(5:6)//'-'//datestring(1:4)// & ' '//timestring(1:2)//':'//timestring(3:4) case('DEVICE','device') print*,' WARNING: query for DEVICE not yet implemented in giza' case('FILE','file') print*,' WARNING: query for FILE not yet implemented in giza' case('TYPE','type') call giza_query_device('type',value) case('DEV/TYPE','dev/type') print*,' WARNING: query for DEV/TYPE not yet implemented in giza' case('HARDCOPY','hardcopy') call giza_query_device('hardcopy',value) case('TERMINAL','terminal') !--in giza the current device is never the terminal value = 'NO' case('CURSOR','cursor') call giza_query_device('cursor',value) case('SCROLL','scroll') !--no scroll capability in any current giza devices value = 'NO' case default value = ' ' end select length = len_trim(value) end subroutine plot_qinf subroutine plot_numb(m,pp,form,string,nc) implicit none integer,intent(in) :: m,pp,form character(len=*),intent(out) :: string integer,intent(out) :: nc call giza_format_number(m,pp,form,string) nc = len_trim(string) end subroutine plot_numb subroutine plot_set_opacity(alpha) implicit none real, intent(in) :: alpha integer :: ci real :: red,green,blue call plot_qci(ci) call plot_qcr(ci,red,green,blue) call plot_scr(ci,red,green,blue,alpha) end subroutine plot_set_opacity subroutine plot_err1(dir,x,y,e,t) implicit none integer,intent(in) :: dir real,intent(in) :: x,y,e real,intent(in) :: t real, dimension(1) :: xi,yi,ei xi(1) = x yi(1) = y ei(1) = e call plot_errb(dir,1,xi,yi,ei,t) end subroutine plot_err1 subroutine plot_conb(a,idim,jdim,i1,i2,j1,j2,c,nc,tr,blank) implicit none integer,intent(in) :: idim,jdim,i1,i2,j1,j2,nc real,intent(in) :: a(idim,jdim),c(*),tr(6),blank real :: affine(6) print*,' WARNING: blanking in contouring not implemented in giza' call convert_tr_to_affine(tr,affine) call giza_contour(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,nc,c,affine) end subroutine plot_conb subroutine plot_cons(a,idim,jdim,i1,i2,j1,j2,c,nc,tr) implicit none integer,intent(in) :: idim,jdim,i1,i2,j1,j2,nc real,intent(in) :: a(idim,jdim),c(*),tr(6) real :: affine(6) call convert_tr_to_affine(tr,affine) call giza_contour(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,nc,c,affine) end subroutine plot_cons subroutine plot_conl(a,idim,jdim,i1,i2,j1,j2,c,tr,label,intval,mininit) implicit none integer,intent(in) :: idim,jdim,i1,i2,j1,j2,intval,mininit real,intent(in) :: a(idim,jdim),c,tr(6) character(len=*),intent(in) :: label real :: affine(6) integer, parameter :: nc = 1 real, dimension(nc) :: clevel clevel(1) = c print*,' WARNING: labelled coutouring not implemented in giza' call convert_tr_to_affine(tr,affine) call giza_contour(idim,jdim,a,i1-1,i2-1,j1-1,j2-1,nc,clevel,affine) print*,'nc = ',nc end subroutine plot_conl subroutine plot_vect(a,b,idim,jdim,i1,i2,j1,j2,c,nc,tr,blank) implicit none integer,intent(in) :: idim,jdim,i1,i2,j1,j2,nc real,intent(in) :: a(idim,jdim),b(idim,jdim),tr(6),blank,c real :: affine(6) call convert_tr_to_affine(tr,affine) call giza_vector(idim,jdim,a,b,i1-1,i2-1,j1-1,j2-1,c,nc,affine,blank) end subroutine plot_vect subroutine plot_pixl(ia,idim,jdim,i1,i2,j1,j2,x1,x2,y1,y2) use giza, only:giza_draw_pixels implicit none integer,intent(in) :: idim,jdim,i1,i2,j1,j2 integer,intent(in) :: ia(idim,jdim) real,intent(in) :: x1,x2,y1,y2 call giza_draw_pixels(IDIM, JDIM, IA, I1-1, I2-1, J1-1, J2-1, X1, X2, Y1, Y2, 0) end subroutine plot_pixl subroutine plot_pap(widthin,aspect,paperunits) use giza, only:giza_set_paper_size use giza, only:giza_units_inches,giza_units_pixels,giza_units_mm implicit none real,intent(in) :: widthin,aspect integer, intent(in), optional :: paperunits integer :: units real :: width width = widthin units = giza_units_inches if (present(paperunits)) then select case(paperunits) case(0) units = giza_units_pixels case(1) units = giza_units_inches case(2) units = giza_units_mm width = 0.1*width end select endif call giza_set_paper_size(units,width,width*aspect) end subroutine plot_pap ! !--this subroutine can be called to ! make sure that the viewport lies exactly on ! pixel boundaries. ! ! unnecessary for giza ! subroutine plot_set_exactpixelboundaries() implicit none end subroutine plot_set_exactpixelboundaries !------------------------------------------------------------ ! Function to convert PGPLOT units value to giza units value !------------------------------------------------------------ integer function units_giza(pgplotunits) use giza, only:giza_units_normalized,giza_units_inches, & giza_units_mm,giza_units_pixels,giza_units_world implicit none integer, intent(in) :: pgplotunits select case(pgplotunits) case(0) units_giza = giza_units_normalized case(1) units_giza = giza_units_inches case(2) units_giza = giza_units_mm case(3) units_giza = giza_units_pixels case(4) units_giza = giza_units_world case default ! giza will give an error units_giza = pgplotunits end select end function units_giza subroutine convert_tr_to_affine(tr,affine) implicit none real, dimension(6), intent(in) :: tr real, dimension(6), intent(out) :: affine affine(1) = TR(2) affine(2) = TR(3) affine(3) = TR(5) affine(4) = TR(6) affine(5) = TR(1) + 0.5 * TR(2) affine(6) = TR(4) + 0.5 * TR(6) end subroutine convert_tr_to_affine end module plotlib splash/src/exact_planetdisc.f90000644 000766 000000 00000011777 13261626263 017422 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !--------------------------------------------------------------------------- ! compute exact solution for various spiral structures: ! ! 1) planet-disc interaction in phi-r plane ! (Ogilvie & Lubow (2002), MNRAS 330, 950) ! 2) planet-disc interaction from Rafikov (2002) ! 3) general parameterised spiral arms !--------------------------------------------------------------------------- module planetdisc implicit none public :: exact_planetdisc integer, parameter :: maxspirals = 2 integer, parameter :: maxcoeff = 5 character(len=*), dimension(maxspirals), parameter, public :: labelspiral = & (/'Ogilvie-Lubow (2002) planet-disc interaction ',& 'Spiral arm fitting formula r(phi) = sum(a_i*phi^i,i=1,4)'/) contains subroutine exact_planetdisc(iplot,ispiral,time,HonR,rplanet,narms,params,rplot,yplot,ierr) use plotlib, only:plot_line implicit none integer, intent(in) :: iplot,ispiral,narms integer, intent(out) :: ierr real, intent(in) :: time, HonR, rplanet, params(:,:) real, dimension(:), intent(inout) :: rplot real, dimension(size(rplot)), intent(out) :: yplot integer :: npts,iend,istart integer :: i,j,norbits,iarm real :: r,phase,dr,phi,rmin,rmax,phimin,phimax,dphi,coeff(maxcoeff) real, parameter :: pi = 4.*atan(1.) ierr = 0 npts = size(rplot) norbits = int(time/(2.*pi)) phase = time - (2.*pi*norbits) select case(ispiral) case(2) print "(a,i2)",' Spiral arm fitting formula r = sum(a_i*phi^i,i=1,4) narms =',narms case default print "(a,f6.2,a,f8.1,a)",' Ogilvie-Lubow planet-disc interaction: H/R=',HonR,' at ',time/(2.*pi),' orbits)' end select select case(iplot) case(2) ! in phi-r plane istart = 1 do i=1,npts r = rplot(i) if (r > rplanet) then yplot(i) = phase - 2./(3.*HonR)*(sqrt(r**3) - 1.5*log(r) - 1.) else yplot(i) = phase + 2./(3.*HonR)*(sqrt(r**3) - 1.5*log(r) - 1.) endif if (yplot(i) > pi) then phase = phase - 2.*pi if (i > 1) then iend = i call plot_line(iend-istart+1,rplot(istart:iend),yplot(istart:iend)) istart = i+1 endif endif if (yplot(i) <= -pi) then phase = phase + 2.*pi ! plot separate line segments every time we cross the phase boundary if (i > 1) then iend = i call plot_line(iend-istart+1,rplot(istart:iend),yplot(istart:iend)) istart = i+1 endif endif enddo ierr = 1 ! do not plot outside this routine return case default ! in x-y plane ! define npts outside planet orbit rmin = 1.e-3 rmax = min(max(maxval(rplot),abs(minval(rplot))),5.*rplanet) dr = (rmax - rmin)/npts do iarm=1,narms if (ispiral.eq.2) then phimin = params(1,iarm) + 90. ! add 90 deg for East of North convention phimax = params(2,iarm) + 90. coeff(:) = params(3:,iarm) dphi = (phimax - phimin)/npts !print*,' GOT RMIN = ',rmin,phimin,phimax, 'COEFFS=',coeff endif ! outside planet do i=1,npts select case(ispiral) case(2) ! ! Spiral arm fitting formula ! phi = phimin + (i-1)*dphi r = 0. ! rmin do j=1,maxcoeff r = r + coeff(j)*((phi-phimin)*pi/180.)**(j-1) enddo case default ! ! Ogilvie & Lubow (2002) ! r = rmin + (i-1)*dr if (r > rplanet) then phi = phase - 2./(3.*HonR)*(sqrt(r**3) - 1.5*log(r) - 1.) else phi = phase + 2./(3.*HonR)*(sqrt(r**3) - 1.5*log(r) - 1.) endif end select rplot(i) = r*cos(phi*pi/180.) yplot(i) = r*sin(phi*pi/180.) !print*,'r, phi = ',r,phi,' : x, y = ',rplot(i),yplot(i) enddo call plot_line(npts,rplot,yplot) enddo ierr = 1 ! do not plot outside this routine end select end subroutine exact_planetdisc end module planetdisc splash/src/splash.f90000644 000766 000000 00000110257 13261626263 015373 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2018 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! ! The plotting API for SPLASH 2.0 was written by James Wetter ! wetter.j@gmail.com ! !----------------------------------------------------------------- program splash !--------------------------------------------------------------------------------- ! ! SPLASH - a plotting utility for SPH data in 1, 2 and 3 dimensions ! Copyright (C) 2005-2018 Daniel Price ! daniel.price@monash.edu ! ! -------------------------------------------------------------------------- ! ! This program is free software; you can redistribute it and/or modify ! it under the terms of the GNU General Public License as published by ! the Free Software Foundation; either version 2 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 General Public License ! along with this program; if not, write to the Free Software ! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! ! ------------------------------------------------------------------------- ! Version history/ Changelog: ! 2.8.0 : (06/04/18) ! 360/4pi video mode added; automatically read labels from ascii file headers; ! nearest sensible unit (e.g. au or pc) used by default; cactus hdf5 data read; ! kernel-smoothed particle plots of arbitrary quantities; ! Viridis, Ocean and Inferno colour schemes; can customise line colours; ! Bondi flow exact solution; option for ticks but no labels; ! correct units in surface density plots; colour bar on top or left; ! support for multi-grain dust in Phantom; bug fix with NaNs in ascii files ! 2.7.0 : (03/05/17) ! Hollywood mode added (ctrl-m in interactive mode); better handling of dust/gas ! phantom data; added rotated cartesian geometry; rendering implemented in r-phi ! coordinates; added Fortran 2008 intrinsics to function parser; better rectangle ! plotting; better falcON data read; Ogilvie-Lubow exact solution for planet-disc ! interaction; tipsy read now works when splash compiled in double precision; ! splash to gridascii2 implemented; bugs with r-phi rendering fixed ! 2.6.0 : (22/10/15) ! SILO, falcON and .pbob data reads implemented; bug fixes in gadget-hdf5 reader; ! can recognise particle types in ascii read; more robust sphNG read; ! dust fraction recognised in phantom data read; Toomre Q works in physical units; ! bug fix with disappearing units labels; bug fix in shock tube exact solution; ! added splash calc delta; splash to ascii keeps precision; better power spectra ! 2.5.1 : (29/01/15) ! error bar style options; support for 5K displays; can plot vectors ! and render with colours if h not read; range restrictions apply during splash to grid; ! improved line-style legend; now up to 6 line styles; fixes to amuse-hdf5 read; ! phantom read handles star/dm particles; various bugs fixed ! 2.5.0 : (22/08/14) ! instant multiplots by giving multiple columns as y axis; ! ability to plot multiple exact solution files on same plot; ! compiles in parallel by default; support for tagged sphNG/Phantom format; ! AMUSE hdf5 format reader added; various bug fixes ! 2.4.1 : (01/04/14) ! Roche-lobe plotting vastly improved; newunit= issue fixed; ! bug fix with reading sink velocities from Phantom; other minor bug fixes. ! 2.4.0 : (21/02/14) ! time formatting in legend can include general functions like %(t + 1000); ! option to include sinks in opacity rendering; ! supports one-fluid dust visualisation; ! C-shock exact solution; better polytrope solution ! 2.3.1 : (11/11/13) ! SPLASH_COROTATE option to plot in frame corotating with sinks; ! bug fixes with handling of dead/accreted/boundary particles in sphNG/phantom; ! various other bugs fixed. ! 2.3.0 : (09/08/13) ! can customise time formatting in legend; improvements to legends; ! less verboseness; splash can read and plot pixel maps produced with -o ascii; ! 3D vector field plotting improved; bug fix with gfortran 4.8 ! 2.2.2 : (10/05/13) ! particle tracking by type implemented; ! can interpolate specific columns in splash to grid; ! SPLASH_CENTRE_ON_SINK option generic to all data reads; ! Aly Reheam format added; option for 2nd y axis on plots; ! bug fix with X11 linking on Ubuntu; can read gadget ICs files ! 2.2.1 : (21/02/13) ! minor bug with axes plotting fixed; ! Wendland kernels added; bugs with exact solution plotting fixed; ! bug fix with tracking of dark matter particles ! 2.2.0 : (16/11/12) ! option to use different kernels for interpolation; ! floating/inset colour bars added; ! splash to gadget conversion implemented; ! splash to grid works in 2D; ! improved interfaces to shapes and animation sequences ! automatically turns on dark matter particle plotting if no gas ! interactive mode help displayed automatically ! 2.1.1 : (31/08/12) ! irregular/circular particle selection using shift-left/middle click; ! improved h5part and GADGET HDF5 data reads; ! splash can be compiled in double precision; ! bug fixes with calculated quantities + change of coordinate systems; ! improved vector plot legend; option for box+numbers but no labels added ! 2.1.0 : (16/05/12) ! 3D vector field visualisation added; ! GADGET HDF5 read implemented; ! page sizes can be specified in pixels; ! limits can auto-adapt to device aspect ratio; ! more general exact solution from file option; ! tiling works with one colour bar per row; ! splash calc handles different particle types ! 2.0 : (29/08/11) ! new giza backend - antialiased lines; real fonts; pdf, eps and svg drivers; ! fewer build dependencies (only cairo, X11); ! support for semi-transparent text; ! Double rendering (with transparent background) implemented. ! 1.15.0 : (29/08/11) ! Multiplot with different particle types implemented; calculated quantities ! list is now pre-filled automatically; preliminary support for r-phi and r-z ! rendering; outlined solid markers implemented; better handling of multiple types; ! manual contour levels can be specified in splash.contours; parallel splash to grid; ! better support for non-square pixels; clipping of numbers at edge of viewport fixed ! 1.14.1 : (17/03/11) ! SEREN data read added; dragon read updated; build follows Gnu conventions ! on DEST and DESTDIR (needed for macports build); can have up to 12 particle types; ! exact solutions re-ordered; dusty wave exact solution added ! 1.14.0 : (06/12/10) ! Can flip between rendered quantities in interactive mode using 'f/F'; ! SPLASH_DEFAULTS variable can be set for system-wide defaults; ! can plot arbitrary functions of x,t as exact solution; asplash better ! handles blank lines in header and can specify time, gamma location with ! env. variables; added data read for the H5PART format; GADGET read ! across multiple files implemented; VINE read works with particle injection; ! error bars can be plotted for both x and y axis simultaneously; ! default rotation angles are set if 3D perspective turned on; ! new directory layout and more helpful error messages during build; ! PGPLOT linking is easier to get right. ! 1.13.1 : (26/02/10) ! bugs with new calc_quantities module fixed; generic library interface ! implemented so backend can be changed easily; bug fix with auto pixel selection; ! simpler foreground/background colour setting; added subgrid interpolation warning ! 1.13.0 : (25/02/10) ! function parser incorporated; calculated quantities can now be specified ! at runtime, arbitrary function plotting implemented as an exact ! solution; command-line SPH->grid conversion ("splash to grid") ! implemented; ctrl-t in interactive mode adds arbitrary text box; ! better line style/colour changing; bug fix with tiling and y-axis labels; ! various other bug fixes. ! 1.12.2 : (15/07/09) ! Variable marker sizes added, can plot particles as circles with ! size proportional to h; dark matter rendering with block-labelled ! GADGET format fixed; VINE read handles star particles; TIPSY read ! with ifort10.0.0 works; snsph read added; splash to phantom added; ! does not override labels for coords, vectors by default; bug fixes ! with contouring options; stability bug fixes with older compilers; ! more robust memory handling; bug fix with automatic pixel selection ! causing seg fault. ! 1.12.1 : (20/04/09) ! Can edit/delete text shapes interactively, also the colour bar label; can customise ! the label on projection plots; contour levels better defined; SPLASH_HMIN_CODEUNITS added; ! option for numeric labelling of contours; contour limits can be set separately ! to render limits for same quantity; minor bug fixes. ! 1.12.0 : (22/12/08) ! Command-line plotting implemented; ln transform added; bug fixes in GADGET read; ! Backspace over annotation (legends,titles,axes,colour bar) in interactive mode ! removes it; "splash calc" command line utility calculates time sequences of ! global quantities from a sequence of dump files; bug fix causing seg fault. ! 1.11.1 : (13/10/08) ! automatic number of pixels and exact pixel boundaries implemented; ! mass does not have to be read from dump file; frame changes are per-page ! not per-dump file for animation sequences; lower stacksize footprint; ! bug fix with circles of interaction; bug fixes with block-labelled GADGET read; ! Steve Foulkes data read added. ! 1.11.0 : (15/08/08) ! ability to use subset of particles in restricted parameter range(s); ! probability density function plot option; plot-hugging colour bars added; ! ability to annotate plot with a range of shapes; v,V,w and H implemented ! in interactive mode for >1 panel; various bug fixes (including one with vphi). ! 1.10.2 : (08/05/08) ! disc surface density / toomre q parameter plotting added; flash colour ! schemes added; splash to binary convert option, can change order in ! which particle types are plotted; splash.columns file overrides ! column label settings; vanaverbeke format read; various bug fixes. ! 1.10.1 : (11/03/08) ! "splash to" command line option converts binary dumps to ascii format; ! vector plots + rotation now implemented; block labelled GADGET format read; ! ring-spreading exact solution added. ! 1.10.0 : (28/11/07) ! horizontal colour bars implemented; -p, -o command line options; ! can have mixed types in data reads; TIPSY and DRAGON data reads; ! density weighted rendering; normalisation applies to column ! density plots; improved particle tracking; save as option; various bug fixes ! 1.9.2 : (12/09/07) ! improvements to ascii read including asplash -e option; ! smarter foreground/background colour changing for titles; ! min=max problem fixed (caught by splash not pgplot); ! fixed vector arrow length option; other minor changes and bug fixes ! 1.9.1 : (11/07/07) ! environment variables + improvements to gadget data read; better ! prompting; 3 new colour schemes; improved legend/title options; ! other minor changes ! 1.9.0 : (21/05/07) ! animation sequences implemented; origin settings now affect radius ! calculation and are relative to tracked particle; automatic line ! width choice for postscript devices; w key adapts vector arrows; ! vastly improved userguide ! 1.8.1 : (28/03/07) ! option to hide vector arrows where there are no particles added; ! smoother 3D plotting at low pixel numbers; ! (smoother vector plots); bug fixes with a); issues with ! round-off error with z integration of vectors fixed. ! 1.8.0 : (14/03/07) ! hidden particles not used in rendering; units for z integration added; ! a) & g) implemented in interactive mode for multiple-plots-per-page; ! improved cross section using x in interactive mode ! 1.7.2 : (19/02/07) ! Menu shortcuts implemented; bug fix/ more sensible transformation ! of angular vector components in different co-ordinate systems; ! improvements to interactive zoom and origin recentreing; ! improved colour-by-type option; restrictions on page size removed; ! minor bug fixes ! 1.7.1 : (04/01/07) ! command line options for defaults and limits files added; ! minor bug fixes ! 1.7.0 : (13/12/06) ! renamed SPLASH instead of SUPERSPHPLOT; much faster data read ! for gadget and sphNG reads (only required columns read); ! physical units can be saved to file; new menu formats; various ! other bug fixes. ! 1.6.2 (24/10/06) ! : fast particle plotting and streamline plotting implemented; ! more bug fixes with interactive mode on multiplots; various other bug fixes. ! 1.6.1 (24/8/06) ! : bug fixes to 1.6.0, further improvements to interactive mode on multiplots. ! 1.6.0 (10/8/06) ! : Interactive mode on multiple plots per page; highly optimised interpolation ! + parallel version; new Makefile; various bug fixes ! 1.5.4 (06/7/06) ! : Handles multiple SPH/non-SPH particle types; axes redrawn after rendering; ! minor bug fixes ! 1.5.3 (27/6/06) ! : minor bug fixes/improvements to multiple plots per page, colour bar labelling ! tiled plots, legend. Accelerated rendering option for projections. ! 1.5.2 (11/5/06) ! : S) option for saving limits and defaults; MUCH faster interactive ! replotting (no unnecessary re-rendering), a few other minor things ! 1.5.1 (26/4/06) ! : docs updated for v1.5, other minor changes ! 1.5.0 (17/3/06) ! : 3D perspective added, 3D opacity rendering, improved rotation, ! colour schemes, adjustable vector arrows (+legend), improved timestepping ! behaviour, speed enhancements, physical unit rescaling ! 1.0.5 (28/9/05) ! : error calculation for exact solutions, legend for plot markers, ! exact_densityprofiles added, more colour schemes, ! unit rescaling improved, other minor changes + bug fixes ! 1.0.4 (17/8/05) ! : better colour schemes; interactive colour scheme changing; ! various minor changes and bug fixes ! 1.0.3 (5/7/05) ! : rescale data option; better page setup; improved zooming; ! interactive particle tracking + various minor changes and bug fixes ! 1.0.2 : much improved ascii data read; better line plotting; zoom on ! powerspectrum plots + various bug fixes ! 1.0.1 : bug fixes relating to colour bars on multiplots ! 1.0 : first "official" release: version given to many people at IPAM ! meeting and put on web. ! ! ------------------------------------------------------------------------- ! ! Modules/subroutines as follows (in alphabetical order): ! ! allocate : allocates memory for main arrays ! calc_quantities : calculates additional quantities from particle data ! colours : colour schemes for rendering ! colourparts : colours particles ! defaults : writes/reads default options to/from file ! exact : module handling exact solution settings ! exact_fromfile : reads an exact solution tabulated in a file ! exact_mhdshock : some tabulated solutions for mhd shocks ! exact_polytrope : exact solution for a polytrope ! exact_rhoh : exact relation between density and smoothing length ! exact_sedov : exact solution for sedov blast wave ! exact_shock : exact solution for hydrodynamic shocks ! exact_wave : exact solution for a propagating sine wave ! exact_toystar1D : exact solution for the 1D toy star problem ! exact_toystar2D : exact solution for the 2D toy star problem ! fieldlines : module handling streamline plotting ! get_data : wrapper for main data read ! geometry : module handling different coordinate systems ! globaldata : various modules containing "global" variables ! interactive : drives interactive mode ! interpolate1D : interpolation of 1D SPH data to grid using kernel ! interpolate2D : interpolation of 2D SPH data to grid ! interpolate3D_xsec : 3D cross section interpolations ! interpolate3D_projection : 3D interpolation integrated through domain ! legends : plots (time) legend on plot ! limits : sets initial plot limits and writes to/reads from limits file ! menu : main menu ! options_data : sets options relating to current data ! options_limits : sets options relating to plot limits ! options_page : sets options relating to page setup ! options_particleplots : sets options relating to particle plots ! options_powerspec : sets options for power spectrum plotting ! options_render : sets options for render plots ! options_vector : sets options for vector plots ! options_xsecrotate : sets options for cross sections and rotation ! particleplot : subroutines for particle plotting ! plotstep : main subroutines which drive plotting of a single timestep ! powerspectrums : calculates power spectrum of 1D data (2 methods) ! read_data_dansph : reads data from my format of data files ! read_data_mbate : reads data from matthew bate's format of data files ! render : takes array of pixels and plots render map/contours etc ! rotate : subroutines controlling rotation of particles ! setpage : sets up the PGPLOT page (replaces call to PGENV/PGLAB) ! splash : main program, drives menu loop ! timestepping : controls stepping through timesteps ! titles : reads a list of titles to be used to label each timestep ! transform : applies various transformations to data (log10, 1/x, etc) ! ! File format is specified in the subroutine read_data ! ! See the svn logs for a full ChangeLog ! ! Plots can be of two types: co-ordinate plots or not ! ! 1) Co-ordinate plots have co-ordinates as x and y axis ! these plots can be rendered with any scalar or vector array. ! ! The rendering routines interpolate from the particles to either ! a 2D or 3D grid. In 3D you can either render to a 3D grid and take ! cross sections, or render to a 2D grid using a table of the integrated ! SPH kernel. This 2D rendering results in a map of the quantity ! integrated through the third co-ordinate. ! Rendering to a full 3D grid can be quite slow - it is used only ! if many cross sections are taken all at once from the same data. ! ! 2) other plots have a variety of options, with lines joining the particles ! and various exact solutions. Plot limits can be fixed or adaptive. ! ! multiplot enables you to set up multiple plots per page, mixing from any type. ! !---------------------------------------------------------------------------------- use filenames, only:rootname,nfiles,maxfile,defaultsfile,limitsfile, & fileprefix,set_filenames use getdata, only:get_data use geomutils, only:set_coordlabels use defaults, only:defaults_set_initial,defaults_set,defaults_read,defaults_set_360 use limits, only:read_limits use kernels, only:ikernel,select_kernel_by_name,select_kernel use mainmenu, only:menu,allowrendering,set_extracols use mem_allocation, only:deallocate_all use projections3D, only:setup_integratedkernel use settings_data, only:buffer_data,lowmemorymode,debugmode,ndim,ncolumns,ncalc,nextra,numplot,ndataplots use system_commands, only:get_number_arguments,get_argument,get_environment use system_utils, only:lenvironment use asciiutils, only:read_asciifile,basename use write_pixmap, only:isoutputformat,iwritepixmap,pixmapformat,isinputformat,ireadpixmap,readpixformat use convert, only:convert_all use write_sphdata, only:issphformat use readwrite_griddata, only:isgridformat,print_gridformats use analysis, only:isanalysis use timestepping, only:timestep_loop use settings_page, only:interactive,device,nomenu implicit none integer :: i,ierr,nargs,ipickx,ipicky,irender,icontour,ivecplot logical :: ihavereadfilenames,evsplash,doconvert,useall,iexist,use_360 character(len=120) :: string character(len=12) :: convertformat character(len=*), parameter :: version = 'v2.8.0 [6th April 2018]' ! ! initialise some basic code variables ! call defaults_set_initial ! ! default names for defaults file and limits file ! fileprefix = 'splash' call set_filenames(trim(fileprefix)) evsplash = .false. lowmemorymode = lenvironment('SPLASH_LOW_MEM') .or. lenvironment('SPLASH_LOWMEM') debugmode = lenvironment('SPLASH_DEBUG') ! ! read all arguments off command line ! call get_number_arguments(nargs) ! ! extract command line arguments and filenames ! i = 0 nfiles = 0 iwritepixmap = .false. ireadpixmap = .false. doconvert = .false. useall = .false. nomenu = .false. ipickx = 0 ipicky = 0 irender = 0 icontour = 0 ivecplot = 0 use_360 = .false. do while (i < nargs) i = i + 1 call get_argument(i,string) if (string(1:1).eq.'-') then select case(trim(string(2:))) case('x') i = i + 1 call get_argument(i,string) read(string,*,iostat=ierr) ipickx if (ierr /= 0 .or. ipickx <= 0) call print_usage(quit=.true.) nomenu = .true. case('y') i = i + 1 call get_argument(i,string) read(string,*,iostat=ierr) ipicky if (ierr /= 0 .or. ipicky <= 0) call print_usage(quit=.true.) nomenu = .true. case('render','r','ren') i = i + 1 call get_argument(i,string) read(string,*,iostat=ierr) irender if (ierr /= 0 .or. irender < 0) call print_usage(quit=.true.) nomenu = .true. case('contour','c','cont','con') i = i + 1 call get_argument(i,string) read(string,*,iostat=ierr) icontour if (ierr /= 0 .or. icontour < 0) call print_usage(quit=.true.) case('vec','vecplot') i = i + 1 call get_argument(i,string) read(string,*,iostat=ierr) ivecplot if (ierr /= 0 .or. ivecplot < 0) call print_usage(quit=.true.) nomenu = .true. case('dev','device') i = i + 1 call get_argument(i,device) case('l') i = i + 1 call get_argument(i,limitsfile) case('d','f') i = i + 1 call get_argument(i,defaultsfile) case('p') i = i + 1 call get_argument(i,string) if (len_trim(string).gt.0) then fileprefix = trim(string) call set_filenames(trim(fileprefix)) endif case('o','writepix','wpix') i = i + 1 call get_argument(i,string) if (isoutputformat(string)) then iwritepixmap = .true. pixmapformat = trim(string) else stop endif case('readpix','rpix') i = i + 1 call get_argument(i,string) if (isinputformat(string)) then ireadpixmap = .true. readpixformat = trim(string) else stop endif case('e','ev') evsplash = .true. fileprefix = 'evsplash' call set_filenames(trim(fileprefix)) case('360','4pi','fourpi') use_360 = .true. ipickx = 2 ipicky = 3 nomenu = .true. case('lowmem','lm') lowmemorymode = .true. case('nolowmem','nlm') lowmemorymode = .false. case('-help') call print_usage print "(/,a)",' Basic splash usage is explained in the userguide,' print "(a,/)",' located in the directory splash/docs/splash.pdf' stop case default call print_usage if (string(2:2).ne.'v') print "(a)",'unknown command line argument '''//trim(string)//'''' stop end select elseif (trim(string).eq.'to' .or. trim(string).eq.'allto') then ! !--for converting SPH formats ! if (trim(string).eq.'allto') useall = .true. i = i + 1 call get_argument(i,string) if (isgridformat(string)) then doconvert = .true. convertformat = trim(string) elseif (issphformat(string)) then doconvert = .true. convertformat = trim(string) else call print_gridformats() stop endif elseif (trim(string).eq.'calc') then ! !--for performing analysis on a sequence of dump files ! i = i + 1 call get_argument(i,string) if (isanalysis(string)) then doconvert = .true. convertformat = trim(string) else stop endif elseif (len_trim(string).gt.0) then nfiles = nfiles + 1 if (nfiles.le.maxfile) then rootname(nfiles) = trim(string) endif endif enddo ! ! print header ! call print_header ! ! set default options (used if defaults file does not exist) ! call defaults_set(evsplash) if (use_360) call defaults_set_360() ! ! read default options from file if it exists ! call defaults_read(defaultsfile) ! ! look for a system-wide defaults file if the environment ! variable SPLASH_DEFAULTS is set, no local file is present ! and no alternative prefix has been set. ! inquire(file=defaultsfile,exist=iexist) if (.not.iexist .and. trim(fileprefix).eq.'splash') then call get_environment('SPLASH_DEFAULTS',string) if (len_trim(string).ne.0) then i = index(string,'.defaults') if (i.gt.0) then defaultsfile = trim(string) else defaultsfile = trim(string)//'.defaults' endif print "(a)",' Using SPLASH_DEFAULTS='//trim(defaultsfile) call defaults_read(defaultsfile) call set_filenames(trim(fileprefix)) endif endif ! ! check that we have got filenames ! if (nfiles.gt.0) then if (nfiles.gt.maxfile) then print*,' WARNING: number of files >= array size: setting nfiles = ',maxfile nfiles = maxfile endif endif if (nfiles.ge.1 .and. rootname(1)(1:1).ne.' ') then ihavereadfilenames = .true. if (nfiles.gt.1) print*,nfiles,' filenames read from command line' else ihavereadfilenames = .false. !print "(a)",' no filenames read from command line' call read_asciifile(trim(fileprefix)//'.filenames',nfiles,rootname) !print*,nfiles,' filenames read from '//trim(fileprefix)//'.filenames file' if (nfiles.gt.0) then ihavereadfilenames = .true. else call get_argument(0,string) print "(/,a/,/,5x,a)",' Usage: ',trim(basename(string))//' snap_0* (or use '& //trim(fileprefix)//'.filenames to list files)' print "(5x,a,/)",trim(basename(string))//' --help (for all command line options)' stop endif endif if (lowmemorymode) print "(a)",' << running in low memory mode >>' if (ikernel.eq.0) then !--if no kernel has been set call get_environment('SPLASH_KERNEL',string) if (len_trim(string).gt.0) then call select_kernel_by_name(string) else call select_kernel(0) endif else call select_kernel(ikernel) endif if (doconvert) then ! ! batch convert all dump files into the output format ! call convert_all(convertformat,ihavereadfilenames,useall) else ! ! read data from file ! if (buffer_data) then call get_data(-1,ihavereadfilenames) else call get_data(1,ihavereadfilenames,firsttime=.true.) endif ! ! setup kernel table for fast column density plots in 3D ! call setup_integratedkernel ! ! read plot limits from file (overrides get_data limits settings) ! call read_limits(trim(limitsfile),ierr) if (nomenu) then ! ! initialise the things we would need if we called menu directly ! call set_extracols(ncolumns,ncalc,nextra,numplot,ndataplots) call set_coordlabels(numplot) interactive = .false. ! ! check command line plot invocation ! if (ipicky.gt.0 .and. ipicky.le.numplot+1) then if (ipicky.le.numplot .and. (ipickx.eq.0 .or. ipickx.gt.numplot)) then print "(a)",' ERROR: x plot not set or out of bounds (use -x col)' stop endif if (irender.gt.0) then if (.not.allowrendering(ipicky,ipickx)) then print "(a)",' ERROR: cannot render with x, y choice (must be coords)' stop endif if (icontour.gt.numplot .or. icontour.lt.0) then print "(a)",' ERROR: contour plot choice out of bounds' stop endif elseif (icontour.gt.0) then print "(a)",' ERROR: -cont also requires -render setting' stop elseif (use_360) then print "(a)",' ERROR -360 also requires -render setting (e.g. -r 6)' stop endif else if (irender.gt.0 .and. ndim.ge.2) then ipicky = 2 ipickx = 1 if (.not.allowrendering(ipicky,ipickx)) then print "(a)",' ERROR: cannot render' stop endif if (icontour.gt.numplot .or. icontour.lt.0) then print "(a)",' ERROR: contour plot choice out of bounds' stop endif else print "(a)",' ERROR: y plot not set or out of bounds (use -y col)' stop endif endif call timestep_loop(ipicky,ipickx,irender,icontour,ivecplot) ! ! if we invoked an interactive device, enter the menu as usual, otherwise finish ! if (interactive) call menu else ! ! enter main menu ! call menu endif endif ! ! deallocate all memory (not strictly necessary) ! call deallocate_all contains !------------------------------------------------------ ! this subroutine prints the splash screen on startup !------------------------------------------------------ subroutine print_header implicit none print 10 10 format( & " _ _ _ _ _ ",/, & " _(_) ___ _ __ | | __ _ ___| |__ (_) _ (_)",/, & " _ (_) _ / __| '_ \| |/ _` / __| '_ \ _ (_) ",/, & " (_) _ (_) \__ \ |_) | | (_| \__ \ | | | _ (_) _ ",/, & " (_) _ |___/ .__/|_|\__,_|___/_| |_| (_) _ (_) ",/, & " (_) (_)|_| (_) (_) (_)(_) (_)(_) (_)(_) ") print 20 20 format(/, & ' ( B | y ) ( D | a | n | i | e | l ) ( P | r | i | c | e )',/) print "(a)",' ( '//trim(version)//' Copyright (C) 2005-2017 )' print 30 30 format(/, & ' * SPLASH comes with ABSOLUTELY NO WARRANTY.',/, & ' This is free software; and you are welcome to redistribute it ',/, & ' under certain conditions (see LICENCE file for details). *',/,/, & ' Updates/userguide: http://users.monash.edu.au/~dprice/splash ',/, & ' Email: daniel.price@monash.edu or splash-users@googlegroups.com',/, & ' Please cite Price (2007), PASA, 24, 159-173 (arXiv:0709.0832) if you ',/, & ' use SPLASH in print and don''t forget to send pics for the gallery.',/) end subroutine print_header subroutine print_usage(quit) use filenames, only:tagline implicit none logical, intent(in), optional :: quit logical :: ltemp print "(a)",trim(tagline) print "(a,/)",trim(version) print "(a,/)",'Usage: splash file1 file2 file3...' print "(a,/,a,/)",'Usage with flags: splash [-p fileprefix] [-d defaultsfile] [-l limitsfile] [-ev] ', & '[-lowmem] [-o format] [-x col] [-y col] [-render col] [-cont col] file1 file2 ...' print "(a,/)",'Command line options:' print "(a)",' -p fileprefix : change prefix to ALL settings files read/written by splash ' print "(a)",' -d defaultsfile : change name of defaults file read/written by splash' print "(a)",' -l limitsfile : change name of limits file read/written by splash' print "(a)",' -e, -ev : use default options best suited for line plotting (.ev files)' print "(a)",' -360 : set default options suited to 360 video' print "(a)",' -lm, -lowmem : use low memory mode [applies only to sphNG data read at present]' print "(a)",' -o pixformat : dump pixel map in specified format (use just -o for list of formats)' print "(/,a,/)",'Command line plotting mode:' print "(a)",' -x column : specify x plot on command line (ie. do not prompt for x)' print "(a)",' -y column : specify y plot on command line (ie. do not prompt for y)' print "(a)",' -r[ender] column : specify rendered quantity on command line (ie. no render prompt)' print "(a)",' (will take columns 1 and 2 as x and y if -x and/or -y not specified)' print "(a)",' -vec[tor] column : specify vector plot quantity on command line (ie. no vector prompt)' print "(a)",' -c[ontour] column : specify contoured quantity on command line (ie. no contour prompt)' print "(a)",' -dev device : specify plotting device on command line (ie. do not prompt)' print "(a)" ltemp = issphformat('none') call print_gridformats() print "(a)" ltemp = isanalysis('none') if (present(quit)) then if (quit) stop endif end subroutine print_usage end program splash splash/src/read_data_cactus_hdf5_utils.c000644 000766 000000 00000033763 13261626263 021327 0ustar00dpricewheel000000 000000 /* * This subroutine performs the calls to the HDF5 library for the * CACTUS data read * * Easier to do it this way and link with c than to try to link against * the Fortran interface (in the latter case the modules must * have been compiled with the *exact* compiler used to compile splash * which is a real pain). * */ #include #include #include #include #include #define MAX_DATASETS 100000 static int debug = 0; static hid_t file_id; static const herr_t HDF5_error = -1; static int have_indexed = 0; #define LEN_NAME 64 typedef struct { char name[LEN_NAME]; } dataset_t; static dataset_t Dataset[MAX_DATASETS]; static int iter[MAX_DATASETS]; static int iorder[MAX_DATASETS]; int read_cactus_dataset(hid_t file_id,char *name,int *ncol,int *ncells,int *ndim,int *n,double *time,double *deltax,int inheader); int read_cactus_iteration(hid_t file_id,int iter,int *next,int *nsteps,int *ncells,int *ndim,int *ncol,double *time,double *deltax,int inheader,int ignoretl); int read_cactus_grid(hid_t dataset_id,hid_t dataspace_id,int ndim,int mycol,int *n,int nx,int ny,int nz,int nghost[3],double orig[3],double delta[3]); void get_ndim_ncells(hid_t dataspace_id, int *ndim, int *nx,int *ny,int *nz); void set_blocklabel(int *icol, char *name, int *lenname); void sort_cactus_data(int *n, int iarr[*n], int iorder[*n]); void read_cactus_hdf5_data_fromc(int *icol,int *ntot,int *np,double temparr[*np]); void read_cactus_itype_fromc(int *ntot,int *np,int itype[*np]); void open_cactus_hdf5_file(char *filename, int istep, int *ncells, int *ncol, int *nsteps, int *ndim, int *ndimV, double *time, int ignoretl, int *ierr) { *ierr = 0; *ndim = 0; *ndimV = 0; *ncol = 5; /* x,y,z,h,m */ have_indexed = 0; if (debug) printf("DEBUG: opening %s \n",filename); file_id = H5Fopen(filename,H5F_ACC_RDONLY,H5P_DEFAULT); if (file_id == HDF5_error) { printf("ERROR opening %s \n",filename); *ierr = 1; return; } /* * Open the first iteration and read the number of dimensions, * number of cells and number of variables in file */ int next; double dx; *ierr = read_cactus_iteration(file_id,istep,&next,nsteps,ncells,ndim,ncol,time,&dx,1,ignoretl); *ierr = 0; *ndimV = *ndim; return; } void close_cactus_hdf5_file(int *ierr) { if (debug) printf("DEBUG: closing file \n"); herr_t status = H5Fclose( file_id ); if (status == HDF5_error) { printf("ERROR closing file \n"); *ierr = 1; } return; } void read_cactus_hdf5_data(char *filename,int istep,int *npart,double *time,double *dx,int ignoretl,int *ierr) { int next,ncol,ndim,nstepsread,nsteps; if (file_id == HDF5_error) { printf("ERROR with file_id %s \n",filename); *ierr = 1; return; } ncol = 5; nstepsread = read_cactus_iteration(file_id,istep,&next,&nsteps,npart,&ndim,&ncol,time,dx,0,ignoretl); if (nstepsread <= 0) { *ierr = -1; } } /* * read one iteration from the file, corresponding to all datasets with same iteration numbers */ int read_cactus_iteration(hid_t file_id,int istep,int *next,int *nsteps,int *ncells,int *ndim,int *ncol,double *time,double *deltax,int inheader,int ignore_time_levels) { hsize_t ndatasets[1]; /* get number of datasets in file */ H5Gget_num_objs(file_id, ndatasets); int it,tl,level,cnum; /* loop over all datasets looking for dataset matching the desired iteration number set function value to true (1) if it is present */ int i,nsub,ierr; int mylen = LEN_NAME; char name[LEN_NAME]; char thorn[LEN_NAME],thornprev[LEN_NAME]; int nstepsgot = 0; *ndim = 0; *ncells = 0; int n_datasets = (int)ndatasets[0]; if (n_datasets > MAX_DATASETS) { if (inheader) printf("ERROR: EXCEEDED MAX NUMBER OF DATASETS: INCREASE MAX_DATASETS TO READ ALL\n"); n_datasets = MAX_DATASETS; } if (inheader) { have_indexed = 0; } if (!have_indexed) { printf("Indexing %i datasets in file\n",n_datasets); } i = 0; cnum = 0; *next = 0; *nsteps = 0; strcpy(thornprev,""); int n = 0; int mystep = 0; int iterprev = -1; int tlprev = 0; while (i < n_datasets) { /* get dataset name, either by reading from file or from memory buffer */ if (!have_indexed) { H5Gget_objname_by_idx(file_id, i, name, LEN_NAME); strcpy(Dataset[i].name,name); if (debug) printf("%s\n",name); } else { /* have indexed file */ strcpy(name,Dataset[iorder[i]-1].name); } /* extract iteration number and thorn name */ it = -1; if (sscanf(name, "%s it=%i tl=%i rl=%i c=%i", thorn, &it, &tl, &level, &cnum)>=4) { /* 4 because c= is optional */ if (debug) printf("%s it=%i tl=%i rl=%i cnum=%i \n",thorn,it,tl,level,cnum); if (!ignore_time_levels || tl ==0) { /* count how many unique datasets (columns) there are */ if (strcmp(thorn,thornprev) != 0 || tl != tlprev) { *ncol = *ncol + 1; if (inheader) printf("-> %s tl=%i\n",thorn,tl); /* send dataset name back to phantom */ if (inheader) set_blocklabel(ncol,thorn,&mylen); nsub = 0; n = 0; if (debug) printf(" GOT ncells = %i\n",*ncells); *ncells = 0; } strncpy(thornprev,thorn,LEN_NAME); tlprev = tl; } else { it = -1; } } else { it = -1; n = 0; if (debug) printf("UNKNOWN dataset %s \n",name); } /* store iteration numbers so we can sort datasets by iteration number */ if (inheader && !have_indexed) iter[i] = it; if (it >= 0) { if (it != iterprev) { mystep++; } if (mystep == istep) { nsub++; ierr = read_cactus_dataset(file_id,name,ncol,ncells,ndim,&n,time,deltax,inheader); } else if (mystep > istep && have_indexed) { break;; } iterprev = it; } else { n = 0; } i++; } have_indexed = 1; if (nsub > 0) { nstepsgot = 1; *nsteps = mystep; /*n_datasets/nsub;*/ } if (inheader) { if (mystep > 1) { sort_cactus_data(&n_datasets,iter,iorder); } else { /* do not sort if only one iteration in file */ for (i=0;i 0) { printf("bbox: x %i %i y %i %i z %i %i\n",bbox[0],bbox[1],bbox[2],bbox[3],bbox[4],bbox[5]); printf("iorigin: %i %i %i\n",iorigin[0],iorigin[1],iorigin[2]); printf("origin: %e %e %e\n",origin[0],origin[1],origin[2]); printf("delta: %e %e %e\n",delta[0],delta[1],delta[2]); printf("Time=%e Delta=%e %e %e nghost=%i %i %i \n",*time,delta[0],delta[1],delta[2], nghost[0],nghost[1],nghost[2]); } if (inheader==0) { /* read dataset */ read_cactus_grid(dataset_id,dataspace_id,*ndim,*ncol,n,nx,ny,nz,nghost,origin,delta); } /* close dataspace and dataset */ status = H5Sclose(dataspace_id); if (status == HDF5_error) { printf("ERROR closing dataspace \n"); } status = H5Dclose(dataset_id); if (status == HDF5_error) { printf("ERROR closing data set \n"); } return (int) status; } /* * utility function to get dimensionality of a dataset */ void get_ndim_ncells(hid_t dataspace_id, int *ndim, int *nx,int *ny,int *nz) { *nx = 1; *ny = 1; *nz = 1; *ndim = H5Sget_simple_extent_ndims(dataspace_id); hsize_t dims[*ndim], maxdims[*ndim]; H5Sget_simple_extent_dims(dataspace_id,dims,maxdims); /* get number of cells */ if (*ndim > 0) *nx = dims[0]; if (*ndim > 1) *ny = dims[1]; if (*ndim > 2) *nz = dims[2]; } int read_cactus_grid(hid_t dataset_id,hid_t dataspace_id,int ndim,int mycol,int *n,int nx,int ny,int nz, int nghost[3],double orig[3],double delta[3]) { hid_t memspace_id; herr_t status; int ierr = 0; int i,j,k; int ncells = nx*ny*nz; /* make a temporary array to put each column as we read it */ hsize_t nsize[3]; nsize[0] = nx; nsize[1] = ny; nsize[2] = nz; memspace_id = H5Screate_simple(ndim,nsize,NULL); double *dat = malloc(ncells * sizeof(double)); status = H5Dread(dataset_id,H5T_NATIVE_DOUBLE,memspace_id,dataspace_id,H5P_DEFAULT,dat); if (status == HDF5_error) { printf("ERROR reading dataset \n"); ierr = 4; } /* map to one dimensional arrays and determine x,y,z for each cell */ double xi,yi,zi; double dx = delta[0]; double dy = delta[1]; double dz = delta[2]; if (debug) { printf("PATCH origin = %f %f %f\n",orig[0],orig[1],orig[2]); printf(" nx=%i x=%f->%f\n",nx,orig[0],(orig[0]+(nx-1)*dx)); printf(" ny=%i y=%f->%f\n",ny,orig[1],(orig[1]+(ny-1)*dy)); printf(" nz=%i z=%f->%f\n",nz,orig[2],(orig[2]+(nz-1)*dz)); } int icol; *n = *n + ncells; if (mycol==6) { double *xx = malloc(ncells * sizeof(double)); double *yy = malloc(ncells * sizeof(double)); double *zz = malloc(ncells * sizeof(double)); int *itype = malloc(ncells * sizeof(int)); if (debug) printf("constructing grid dx=%f, dy=%f, dz=%f\n",dx,dy,dz); /* here we have to reconstruct the x, y and z positions of each cell. The following looks hacked but works. We tested it. */ int ip = 0; for (k=0;k nx-nghost[2]-1)) { isghostz = 1; } else { isghostz = 0; } for (j=0;j ny-nghost[1]-1)) { isghosty = 1; } else { isghosty = 0; } for (i=0;i nz-nghost[0]-1)) { isghostx = 1; } else { isghostx = 0; } if (isghostx || isghosty || isghostz) { itype[ip] = 2; } else { itype[ip] = 1; } ip++; } } } /* send additional data through to Fortran */ icol = 1; read_cactus_hdf5_data_fromc(&icol,n,&ncells,xx); icol = 2; read_cactus_hdf5_data_fromc(&icol,n,&ncells,yy); icol = 3; read_cactus_hdf5_data_fromc(&icol,n,&ncells,zz); /* zone type (regular or ghost) */ read_cactus_itype_fromc(n,&ncells,itype); free(xx); free(yy); free(zz); free(itype); } icol = mycol; read_cactus_hdf5_data_fromc(&icol,n,&ncells,dat); /* deallocate memory */ free(dat); return ierr; } splash/src/pdfs.f90000644 000766 000000 00000021037 13261626263 015032 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !---------------------------------------------------------------- ! ! module for probability density function calculation ! and/or plotting on the particles ! !---------------------------------------------------------------- module pdfs implicit none public :: pdf_calc,pdf_write,mean_variance contains !----------------------------------------------------------------- ! ! subroutine bins particles into x, works out number in each bin, ! calculates normalisation for PDF. ! !----------------------------------------------------------------- subroutine pdf_calc(npart,xpart,xminplot,xmaxplot,nbins,xbin,pdf,pdfmin,pdfmax,& usefixedbins,volweighted,ierr,icolours,rhopart,pmass) !use transforms, only:transform,transform_inverse,transform_limits,convert_to_ln_fac implicit none integer, intent(in) :: npart,nbins real, dimension(:), intent(in) :: xpart real, intent(in) :: xminplot,xmaxplot real, intent(out), dimension(nbins) :: xbin,pdf real, intent(out) :: pdfmin,pdfmax ! integer, intent(in) :: itransx logical, intent(in) :: usefixedbins logical, intent(out) :: volweighted integer, intent(out) :: ierr integer, intent(in), dimension(:), optional :: icolours real, intent(in), dimension(:), optional :: rhopart,pmass integer :: ibin,i real :: dx,totprob,fi!,xbinprev !xbini,dxprev real :: xmin,xmax,xminpart,xmaxpart,weighti,totvol logical :: use_part ierr = 0 volweighted = .false. if (present(rhopart) .and. present(pmass)) then print "(a,i3,a)",' calculating (volume weighted) PDF using ',nbins,' bins' volweighted = .true. else print "(a,i3,a)",' calculating (mass weighted) PDF using ',nbins,' bins' endif ! !--set bins in PDF: must always use all the particles ! note that xpart will already have been transformed ! so these are min and max in transformed space ! xminpart = minval(xpart(1:npart)) xmaxpart = maxval(xpart(1:npart)) if (usefixedbins) then xmin = xminplot xmax = xmaxplot print "(a,1pe10.3,a,1pe10.3)",' PDF bins are fixed between the current x limits, min = ',xminplot,' max = ',xmaxplot if (xminpart.lt.xmin) print "(a)",' WARNING: particles fall outside of (fixed) bin range, will pile up on first bin' if (xmaxpart.gt.xmax) print "(a)",' WARNING: particles fall outside of (fixed) bin range, will pile up on last bin' dx = (xmax - xmin)/real(nbins) print "(a,1pe10.3)",' bin width = ',dx do ibin=1,nbins xbin(ibin) = xmin + (ibin-1)*dx enddo else xmin = xminpart xmax = xmaxpart dx = (xmax - xmin)/real(nbins-1) print "(a,1pe10.3)",' bin width = ',dx do ibin=1,nbins xbin(ibin) = xmin + (ibin-0.5)*dx enddo endif ! !--now calculate probability of finding a particle at each x ! pdf(:) = 0. totvol = 0. do i=1,npart if (present(icolours)) then use_part = (icolours(i).ge.0) else use_part = .true. endif !--do not use hidden particles if (use_part) then ibin = int((xpart(i) - xmin)/dx) + 1 if (ibin.lt.1) ibin = 1 if (ibin.gt.nbins) ibin = nbins if (volweighted) then if (rhopart(i).gt.0.) then weighti = pmass(i)/rhopart(i) else weighti = 0. endif else weighti = 1. endif totvol = totvol + weighti !!--take the PDF of ln(x) if quantity is logged !if (itransx.gt.0) then ! weighti = weighti*convert_to_ln_fac(itransx) !endif pdf(ibin) = pdf(ibin) + weighti endif enddo print*,' sum of weights = ',totvol ! !--get total area under pdf by trapezoidal rule ! totprob = 0. do ibin=1,nbins fi = pdf(ibin) totprob = totprob + dx*fi !!0.5*dx*(fi + fprev) enddo ! !--normalise pdf so total area is unity ! print*,'normalisation factor = ',totprob,totvol*dx ! =npart*dx for mass-weighted, totvol*dx for volume weighted !totprob = totvol*dx !totprob = dx if (totprob.le.0.) then ierr = 1 print "(a)",' error in normalisation factor: returning non-normalised PDF' else pdf(1:nbins) = pdf(1:nbins)/totprob !call pdf_write(nbins,xbin,pdf,labelx,itransx,volweighted) ! !--return min and max for adaptive plot limit setting ! (exclude zero as min) ! pdfmin = minval(pdf(1:nbins),mask=(pdf(1:nbins).gt.0.)) pdfmax = maxval(pdf(1:nbins)) endif end subroutine pdf_calc !----------------------------------------------------------------- ! interface which controls plotting of PDF ! (so can easily change properties of PDF plotting, ! e.g. histogram vs. line) !----------------------------------------------------------------- !subroutine pdf_plot(nbins,xbin,pb) ! use plotutils, only:plotline !,plotbins ! implicit none ! integer, intent(in) :: nbins ! real, dimension(:), intent(in) :: xbin,pb ! !--plot as line segment, with blanking at zero ! ! call plotline(nbins,xbin,pb,blank=0.) ! !--plot as histogram, with blanking of zero ! ! call plotbins(nbins,xbin,pb,blank=0.) !end subroutine pdf_plot !----------------------------------------------------------------- ! routine to write pdf to file !----------------------------------------------------------------- subroutine pdf_write(nbins,xbin,pb,labelx,volweighted,rootname,tagline) use asciiutils, only:safename implicit none character(len=*), intent(in) :: labelx,rootname,tagline integer, intent(in) :: nbins !,itransx real, intent(in), dimension(nbins) :: xbin,pb logical, intent(in) :: volweighted integer :: i,ierr integer, parameter :: iunit = 86 logical :: warned print "(a)",' writing to '//trim(rootname)//'_pdf_'//trim(safename(labelx))//'.dat' open(unit=iunit,file=trim(rootname)//'_pdf_'//trim(safename(labelx))//'.dat', & form='formatted',status='replace',iostat=ierr) if (ierr /= 0) then print "(a)",'ERROR: could not open file: no output' return endif if (volweighted) then write(iunit,"(a)",iostat=ierr) '# volume weighted PDF, calculated using '//trim(tagline) else write(iunit,"(a)",iostat=ierr) '# density weighted PDF, calculated using '//trim(tagline) endif if (ierr /= 0) print "(a)",' ERROR writing header line' write(iunit,"(a,i5,a)",iostat=ierr) '# ',nbins,' bins evenly spaced in '//trim(labelx) warned = .false. !--dump bins to file do i=1,nbins write(iunit,*,iostat=ierr) xbin(i),pb(i) if (ierr /= 0 .and. .not.warned) then print "(a)",' ERRORS during write' warned = .true. endif enddo close(iunit) return end subroutine pdf_write !------------------------------------------------- ! Subroutine to calculate the mean and variance ! of a set of data points ! Mean is trivial but variance uses a special ! formula to reduce round-off error ! see Press et al Numerical Recipes, section 14.2 ! this is similar to their subroutine avevar !------------------------------------------------- subroutine mean_variance(x,npts,xmean,xvariance) implicit none integer, intent(in) :: npts real, intent(in), dimension(npts) :: x real, intent(out) :: xmean, xvariance real :: roundoff, delta integer :: i ! !--calculate average ! xmean = 0. do i=1,npts xmean = xmean + x(i) enddo xmean = xmean/real(npts) ! !--calculate variance using the corrected two-pass formula ! ! var = 1/(n-1)*( sum (x-\bar{x}) - 1/n * (sum(x-\bar{x}) )^2 ) ! ! where the last term corrects for the roundoff error ! in the first term ! xvariance = 0. roundoff = 0. do i=1,npts delta = x(i) - xmean roundoff = roundoff + delta xvariance = xvariance + delta*delta enddo xvariance = (xvariance - roundoff**2/npts)/real(npts-1) return end subroutine mean_variance end module pdfs splash/src/exact_toystar2D.f90000644 000766 000000 00000034035 13261626263 017157 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------ ! Exact solutions for toystar in two dimensions ! ! For details see Monaghan and Price (2005), in prep. ! !------------------------------------------------------------ module toystar2D implicit none public :: exact_toystar2D public :: etar, detadr ! public because they are used in setup contains !------------------------------------------------------------ ! calculate exact solution for toystar in two dimensions ! ! non-linear solution solves ODEs, assumes linear velocity ! ! the solutions are all plots against radius ! ! iplot = 0 gives x vs y ! ! iplot = 1->5 gives rho, pr, u, vx, vy vs r !------------------------------------------------------------ subroutine exact_toystar2D(iplot,time,gamma,polyk,totmass, & ampl,denscentre,C0,jorder,morder, & V11,V22,V12,V21,xplot,yplot,ierr) implicit none integer, intent(in) :: iplot,jorder,morder integer, intent(out) :: ierr real, intent(in) :: time,gamma,polyk,totmass real, intent(in) :: C0, ampl, denscentre ! parameters for toy star real, intent(in) :: V11,V22,V12,V21 real, dimension(:), intent(inout) :: xplot real, dimension(:), intent(out) :: yplot real, parameter :: pi = 3.141592653589 integer :: i,npts,nsteps integer :: jmode,smode real :: H,C,D,B,omega,omegasq real :: radstar,dx,nu2 !!,scalefac real :: rhoplot,deltarho,vplot,deltav real :: gamm1,gam1,constK,sigma,period real, dimension(8) :: params, paramsp,dparams real :: fac,dt,t,phi,dphi,cosphi,sinphi,denom real :: massbefore,massafter logical linear ierr = 1 npts = size(xplot) linear = (jorder.ge.0 .and. morder.ge.0) gamm1 = gamma - 1. if (gamm1.lt.1.e-3) then print*,'Error: no toy star solution for isothermal yet' ierr = 1 return endif gam1 = 1./gamm1 if (polyK.le.0.) then print*,'Error: polytropic K <= 0 on input: using 0.25 by default' constK = 0.25 else constK = polyK !!0.25 ! this is K from P = K*rho**gamma endif omega = 1.0 ! this is omega from the main code (ie. from potential) omegasq = omega**2 B = 0. D = 0. if (linear) then !--------------------------------------------------------------------------- ! linear solution print*,' Plotting 2D toy star: linear solution r mode = ',jorder,' phi mode = ',morder jmode = jorder ! radial mode smode = morder ! non-axisymmetric modes (theta) ! sigma is the frequency of oscillation nu2 = (jmode + smode)*(jmode+smode + 2./gamm1) - smode**2 if (nu2.le.0.) then print*,'Error: nu^2 < 0 in linear toy star ',nu2 print*,' radial mode = ',jmode,' theta mode = ',smode ierr = 2 return else sigma = sqrt(0.5*omegasq*gamm1*nu2) endif print*,' Amplitude = ',ampl,' period = ',2*pi/sigma,' H,C = ',denscentre,C0 !!scalefac = polyk*gamma/(sigma*gamm1) radstar = sqrt((2.*polyk*gamma*denscentre**gamm1)/gamm1) xplot(1) = 0. dx = (radstar-xplot(1))/float(npts-1) do i=1,npts xplot(i) = xplot(1)+dx*(i-1) ! print*,i,' x,y = ',xplot(i),yplot(i) rhoplot = (denscentre - C0*xplot(i)**2) if (rhoplot.le.0.) rhoplot = 0. deltarho = etar(jmode,smode,xplot(i)/radstar,gamma) ! functional form of rho(r) !!print*,'deltarho = ',rhoplot,deltarho,xplot(i) rhoplot = (rhoplot + deltarho*ampl*SIN(sigma*time))**gam1 deltav = ampl*detadr(jmode,smode,xplot(i)/radstar,gamma) vplot = deltav*COS(sigma*time) select case(iplot) case(1) ! plot solution for density yplot(i) = rhoplot case(2) ! plot solution for pressure yplot(i) = constK*rhoplot**gamma case(3) ! plot solution for utherm yplot(i) = constK*(rhoplot**gamm1)/gamm1 case(4) ! plot solution for v_r yplot(i) = vplot case(5) yplot(i) = vplot**2 end select enddo !--------------------------------------------------------------------------- ! non-linear solution ! else if (jorder.lt.0 .and. smode.lt.0) then smode = 2 jmode = 0 else smode = 0 jmode = 2 endif print*,'Plotting 2D toy star: non-linear' ! solve for H, C and A given initial conditions on v, rho and the time. ! H = denscentre ! !--this is the static solution, determined from the total mass, polyk, gamma and omega ! radstar = sqrt(gamma*totmass/(pi*gamm1)) H = omegasq*gamm1*radstar**2/(2.*polyk*gamma) C = 0.5*gamm1*omegasq/(gamma*polyk) print*,' r_star = ',radstar,' rho = (',H,'-',C,'^2)**',gamm1 D = C B = 0. ! !--now solve the ODEs for V11,V22,V12,V21,H,C,D & B ! (we use a simple modified euler method) ! params(1)= V11 params(2)= V22 params(3)= V12 params(4)= V21 params(5)= H params(6)= C params(7)= D params(8)= B fac= 2.*gamma*polyk*gam1 massbefore = pi*gamm1/gamma*H**(gamma*gam1)/(sqrt(C*D - B**2)) ! !--get frequency to determine timestep ! nu2 = (jmode + smode)*(jmode+smode + 2./gamm1) - smode**2 if (nu2.le.0.) then print*,'Error: nu^2 < 0 in exact toy star ',nu2 print*,' radial mode = ',jmode,' theta mode = ',smode ierr = 2 return else sigma = sqrt(0.5*omegasq*gamm1*nu2) endif ! !--solve ODE's ! period = 2.*pi/sigma dt = 0.001*period nsteps = int(time/dt) dt = time/real(nsteps) t = 0. do i=1,nsteps t = t + dt call param_derivs(params,dparams,fac,gamm1,omegasq) paramsp(:)= params(:) + 0.5*dt*dparams(:) call param_derivs(paramsp,dparams,fac,gamm1,omegasq) params(:)= params(:) + dt*dparams(:) enddo ! !--have now got solution at current time ! H= params(5) C= params(6) D= params(7) B= params(8) print*,' solved ODEs to time = ',t,' in ',nsteps,' nsteps ' massafter = pi*gamm1/gamma*H**(gamma*gam1)/(sqrt(C*D - B**2)) print*,' conserved mass before = ',massbefore,' after =',massafter if (C.le.0.) then radstar = 0.5 stop '*** C = 0 = illegal' !!elseif (A.le.1.e-5) then else radstar = sqrt(H/C) endif xplot(1) = 0. dx = (radstar-xplot(1))/float(npts-1) do i=1,npts xplot(i) = xplot(1)+dx*(i-1) ! print*,i,' x,y = ',xplot(i),yplot(i) rhoplot = (H - C*xplot(i)**2) if (rhoplot.le.0.) rhoplot = 0. rhoplot = rhoplot**gam1 select case(iplot) case(1) ! plot solution for density yplot(i) = rhoplot case(2) ! plot solution for pressure yplot(i) = constK*rhoplot**gamma case(3) ! plot solution for utherm yplot(i) = constK*(rhoplot**gamm1)/gamm1 case(4) ! plot solution for v_r yplot(i) = ampl*xplot(i) case(5) yplot(i) = (ampl*xplot(i))**2 end select enddo ! !------------------------------------------------------------------------ ! endif if (iplot.gt.0 .and. iplot.le.5) then ierr = 0 elseif (iplot.eq.0) then print*,' plotting non-axisymmetric boundary' ! !--for x-y plots we plot the rho=0 curve (ie. toy star boundary) ! dphi = 2.*pi/real(npts-1) phi = 0. do i=1,npts phi = (i-1)*dphi cosphi = cos(phi) sinphi = sin(phi) denom = C*cosphi**2 + 2.*B*cosphi*sinphi + D*sinphi**2 radstar = SQRT(H/denom) xplot(i) = radstar*cosphi yplot(i) = radstar*sinphi enddo ierr = 0 ! call pgsfs(2) ! call pgcirc(0.0,0.0,radstar) ! ierr = 3 endif return end subroutine exact_toystar2D ! !--function that evaluates the polynomial for rho(r/re) for a given radial mode ! (from the power series solution to the 2nd order ODE) ! ! rad = r/r_star ! j = radial (axisymmetric) mode ! m = theta mode ! ! solution is for delta(rho**(gamma-1)) ! ie. rho**(gamma-1) = rho_0**(gamma-1) + etar ! ! and takes the form ! ! etar = rad**m sum_k a_k rad**k ! real function etar(j,m,rad,gamma) implicit none integer, intent(in) :: j,m ! j is the radial mode, m is the theta mode integer :: k,kprev real, intent(in) :: rad,gamma real :: denom,ak,akprev,gamm1,freqsq ! !--this solution is for arbitrary gamma ! gamm1 = gamma - 1. if (gamm1.lt.1.e-3) then print*,'error gamma -1 <= 0' etar = 0. return endif ! !--the solution is of the form ! drhor = a_0 + a_2 (r/re)**2 + a_4 (r/re)**4 + ... ! where for j = k, coefficients >= a_k+2 are zero ! freqsq = (j+m)*(j+m + 2./gamm1) - m**2 akprev = 1.0 ! this is a_0 which is the amplitude etar = akprev !!print*,'mode = ',j,m,' nu^2 = ',freqsq,' a_0 = ',akprev ! !--the co-efficients for the terms above a_0 are calculated using ! the recurrence relation between the a_k's ! do k = 2,j,2 kprev = k-2 denom = real((kprev + 2 + m)**2 - m**2) ak = akprev*(kprev**2 + 2.*kprev*m + 2.*(kprev+m)/gamm1 - freqsq)/denom !!print*,'coeff ',k,' = ',ak,k**2,2.*k/gamm1 etar = etar + ak*rad**k akprev = ak enddo etar = etar * rad**m end function etar ! !--function that evaluates the polynomial for v(r/re) for a given radial mode ! (from the power series solution to the 2nd order ODE) ! real function detadr(j,m,rad,gamma) implicit none integer, intent(in) :: j, m ! j is the radial mode, m is the theta mode integer :: k,kprev real, intent(in) :: rad,gamma real :: denom,term1,term2 real :: ak,akprev,gamm1,freqsq ! !--this solution is for arbitrary gamma ! gamm1 = gamma - 1. if (gamm1.lt.1.e-3) then print*,'error gamma -1 <= 0' detadr = 0. return endif ! !--the solution is of the form ! drhor = a_0 + a_2 (r/re)**2 + a_4 (r/re)**4 + ... ! where for j = k, coefficients >= a_k+2 are zero ! freqsq = (j+m)*(j+m + 2./gamm1) - m**2 detadr = 0. akprev = 1.0 ! this is a_0 which is the amplitude term1 = akprev term2 = 0. ! print*,'mode = ',j,m,' nu^2 = ',freqsq,' a_0 = ',akprev ! !--the co-efficients for the terms above a_0 are calculated using ! the recurrence relation between the a_k's ! do k = 2,j,2 kprev = k-2 denom = real((kprev + 2 + m)**2 - m**2) ak = akprev*(kprev**2 + 2.*kprev*m + 2.*(kprev+m)/gamm1 - freqsq)/denom !!print*,'coeff ',k,' = ',ak,k*ak,rad,(k-1) term1 = term1 + ak*rad**k term2 = term2 + k*ak*rad**(k-1) akprev = ak enddo if (m.eq.0) then detadr = term2 else detadr = m*rad**(m-1)*term1 + rad**m*term2 endif end function detadr subroutine param_derivs(func,dfunc,fac,gamm1,omegasq) implicit none real, intent(in), dimension(8) :: func real, intent(out), dimension(8) :: dfunc real, intent(in) :: fac, gamm1,omegasq real :: term, gamma term = func(1) + func(2) gamma = gamm1 + 1. dfunc(1)= fac*func(6) - func(1)*func(1) - func(3)*func(4) - omegasq dfunc(2)= fac*func(7) - func(2)*func(2) - func(3)*func(4) - omegasq dfunc(3)= fac*func(8) - func(3)*term dfunc(4)= fac*func(8) - func(4)*term dfunc(5)= -gamm1*term*func(5) dfunc(6)= -2.*func(6)*func(1) - gamm1*func(6)*term - 2.*func(8)*func(4) dfunc(7)= -2.*func(7)*func(2) - gamm1*func(7)*term - 2.*func(8)*func(3) dfunc(8)= -func(6)*func(3) - func(7)*func(4) - gamma*func(8)*term return end subroutine param_derivs !---------------------------------------------------------------------- ! ! this subroutine plots the alpha-beta relation in the 2D Toy star solution ! !---------------------------------------------------------------------- subroutine exact_toystar_ACplane2D(astart,bstart,sigmain,gamma) use plotlib, only:plot_swin,plot_box,plot_label,plot_line implicit none real, intent(in) :: astart,bstart,sigmain,gamma integer, parameter :: npts = 2000 integer :: i real :: gamm1,gam1,sigma real :: polyk,Omega2,constk real :: xstart,xend,ymin,ymax,xi,term,extra real, dimension(npts) :: xplot, yplot print*,' plotting alpha-beta plane...' gamm1 = gamma - 1. gam1 = 1./gamm1 polyk = 0.25 Omega2 = 1.0 sigma = 1.0 print*,' alpha = ',astart,' beta = ',bstart, 'sigma = ',sigma,sigmain ! !--find integration constant from starting values of alpha and beta ! constk = (astart**2 + bstart**2 + Omega2 & + 2.*polyk*gamma*(sigma*bstart**gamma)*gam1**2)/bstart print*,' integration constant = ',constk ! !--find limits of plot (ie. where alpha = 0) ! xstart = 0.25 xend = 2.0 ! print*,'plotting k = ',k,' cstart = ',cstart,' astart = ',astart ! print*,'min c = ',xstart,' max c = ',xend xstart = xstart + 0.000001 xend = xend - 0.000001 extra = 0.1*(xend-xstart) !!xcentre = 0.5*(xstart + xend) ymax = 2.0 ymin = -2.0 call plot_swin(xstart-extra,xend+extra,ymin,ymax) call plot_box('bcnst',0.0,0,'1bvcnst',0.0,0) call plot_label ('beta','alpha',' ') do i=1,npts xi = xstart + (i-1)*npts xplot(i) = xi term = -(xi**2 + Omega2 + 2.*polyk*gamma*(sigma*xi**gamma)*gam1**2 + constk*xi) if (term.le.0) then yplot(i) = 0. else yplot(i) = sqrt(term) endif enddo call plot_line(npts,xplot,yplot) return end subroutine exact_toystar_ACplane2D end module toystar2D splash/src/menu.f90000644 000766 000000 00000077174 13261626263 015057 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !-------------------- ! SPLASH MAIN MENU !-------------------- module mainmenu implicit none public :: menu,allowrendering,set_extracols private contains subroutine menu use filenames, only:defaultsfile,limitsfile,unitsfile,fileprefix,set_filenames use labels, only:label,labelvec,iamvec,isurfdens,itoomre,ipdf,icolpixmap,is_coord,ix use limits, only:write_limits,lim2,lim,reset_lim2,lim2set use options_data, only:submenu_data use settings_data, only:ndim,numplot,ndataplots,nextra,ncalc,ivegotdata, & buffer_data,ncolumns,icoords,icoordsnew,iRescale use settings_limits, only:submenu_limits,iadapt use settings_part, only:submenu_particleplots use settings_page, only:submenu_page,submenu_legend,interactive,nacross,ndown use settings_render, only:submenu_render,iplotcont_nomulti,icolours,double_rendering use settings_vecplot, only:submenu_vecplot,iplotpartvec use settings_xsecrot, only:submenu_xsecrotate,xsec_nomulti use settings_units, only:write_unitsfile use multiplot use prompting, only:prompt,print_logical use transforms, only:transform_label use defaults, only:defaults_write use getdata, only:get_data use geomutils, only:set_coordlabels use timestepping implicit none integer :: i,icol,ihalf,iadjust,indexi,ierr integer :: ipicky,ipickx,irender,ivecplot,icontourplot integer :: iamvecprev, ivecplottemp,ichoose integer :: maxdigits character(len=5) :: ioption character(len=100) :: vecprompt,string character(len=20) :: rprompt character(len=2) :: fmtstrlen character(len=50) :: fmtstr1,fmtstr2,fmtstr3 character(len=*), parameter :: sep="(55('-'))" logical :: iAllowRendering irender = 0 icontourplot = 0 ivecplot = 0 if (ndim > 1 .and. ix(1) > 0) then ipickx = ix(1) else ipickx = 1 endif ipicky = 1 menuloop: do !--------------------------------------------------------------------------- ! preliminaries !--------------------------------------------------------------------------- ! !--make sure the number of columns is set appropriately ! (nextra can change depending on what options are set) ! ! !--numplot is the total number of data columns (read + calculated) ! not including the particle co-ordinates ! nextra are extra graphs to plot (e.g. convergence plots, power spectrum) ! ! note that numplot and ndataplots should *only* be set here ! this means that even if ncolumns changes during data reads while plotting ! we don't start plotting new quantities ! call set_extracols(ncolumns,ncalc,nextra,numplot,ndataplots) ! !--set the coordinate and vector labels ! if working in a different coordinate system ! call set_coordlabels(numplot) !--set contents of the vector plotting prompt vecprompt(1:6) = '0=none' indexi = 7 iamvecprev = 0 do icol=1,numplot if (iamvec(icol).ne.0 .and. iamvec(icol).ne.iamvecprev) then iamvecprev = iamvec(icol) if (iamvec(icol).ge.10) then write(vecprompt(indexi:),"(',',1x,i2,'=',a)") & iamvec(icol),trim(labelvec(icol)) else write(vecprompt(indexi:),"(',',1x,i1,'=',a)") & iamvec(icol),trim(labelvec(icol)) endif indexi = len_trim(vecprompt) + 1 endif enddo ichoose = 0 !--------------------------------------------------------------------------- ! print menu !--------------------------------------------------------------------------- if (numplot.gt.0) then ! !--data columns ! call print_header() print sep ihalf = numplot/2 ! print in two columns iadjust = mod(numplot,2) maxdigits = floor(log10(real(maxplot)))+1 write(fmtstrlen,"(A1,I1)") "i",maxdigits fmtstr1 = "(1x,"//trim(adjustl(fmtstrlen))//",')',1x,a20,1x," & //trim(adjustl(fmtstrlen))//",')',1x,a20)" print fmtstr1, & (i,transform_label(label(i),itrans(i)), & ihalf + i + iadjust, transform_label(label(ihalf + i + iadjust), & itrans(ihalf+i+iadjust)),i=1,ihalf) if (iadjust.ne.0) then fmtstr2 = "(1x,"//trim(adjustl(fmtstrlen))//",')',1x,a20)" print fmtstr2, & ihalf + iadjust,transform_label(label(ihalf + iadjust), & itrans(ihalf+iadjust)) endif ! !--multiplot ! print sep fmtstr3 = "(1x,"//trim(adjustl(fmtstrlen))//",')',1x,a,'[ '," & //trim(adjustl(fmtstrlen))//",' ]',5x,a2,') ',a)" print fmtstr3, & numplot+1,'multiplot ',nyplotmulti,'m','set multiplot ' else ! !--if no data ! maxdigits = 1 print "(/a)",' No data: You may choose from the options below ' endif ! !--options ! print sep if (ndim.le.1) then print "(a)",' d(ata) p(age) o(pts) l(imits) le(g)end s,S(ave) q(uit)' else print "(a)",' d(ata) p(age) o(pts) l(imits) le(g)end h(elp)' print "(a)",' r(ender) v(ector) x(sec/rotate) s,S(ave) q(uit)' endif print sep ! !--prompt user for selection ! write(*,"(a)",ADVANCE='NO') 'Please enter your selection now (y axis or option):' read(*,"(a)",iostat=ierr) string if (ierr < 0) stop 'reached end of input' ! end of input (e.g. in script) if (ierr > 0) stop !'error reading input' ioption = string(1:maxdigits) !------------------------------------------------------------ ! if input is an integer and within range, plot data !------------------------------------------------------------ read(ioption,*,iostat=ierr) ipicky if (ierr /= 0) ipicky = -1 !--try to read more integers from the string ! if present, use these to set up an "instant multiplot" if (ipicky > 0 .and. ipicky < numplot+1 .and. len_trim(string) > 2) then call set_instant_multiplot(string,ipicky,ipickx,numplot,nyplotmulti,& multiplotx,multiploty,nacross,ndown) endif if (ipicky > 0 .and. ipicky <= numplot+1) then if (.not.ivegotdata) then ! !--do not allow plotting if no data - instead try to read data ! print*,' no data ' if (buffer_data) then call get_data(-1,.false.) else call get_data(1,.false.,firsttime=.true.) endif else ! !--if needed prompt for x axis selection ! if (ipicky <= (numplot-nextra)) then if (ipickx==0) then if (ndim > 1 .and. ix(1) > 0) then ipickx = ix(1) else ipickx = 1 ! do not allow zero as default endif endif if (ipickx==ipicky) then ! do not allow x same as y by default if (ipickx > 1) then ipickx = ipicky-1 else ipickx = ipicky+1 endif endif call prompt(' (x axis) ',ipickx) !--go back to y prompt if out of range if (ipickx.gt.numplot .or. ipickx.le.0) cycle menuloop ! !--work out whether rendering is allowed ! iAllowRendering = allowrendering(ipickx,ipicky,xsec_nomulti) ! !--prompt for render and vector plots ! -> only allow if in "natural" coord system, otherwise h's would be wrong) ! (a future feature might be to interpolate in icoord then translate the pixels ! to icoordsnew, or alternatively plot non-cartesian pixel shapes) ! -> also do not allow if transformations are applied ! if (is_coord(ipicky,ndim) .and. is_coord(ipickx,ndim)) then if (iAllowRendering) then call prompt('(render) (0=none)',irender,0,(numplot-nextra)) if (irender > 0 .and. iplotcont_nomulti .and. icolours /= 0) then if (double_rendering) then rprompt = '2nd render' else rprompt = 'contours' endif call prompt('('//trim(rprompt)//') (0=none)',icontourplot,0,(numplot-nextra)) if (icontourplot==irender) then if (iadapt) then print "(a)",' limits for '//trim(rprompt)//' are adaptive' else if (.not.lim2set(icontourplot)) lim2(icontourplot,:) = lim(icontourplot,:) call prompt(' enter min for '//trim(rprompt)//':',lim2(icontourplot,1)) call prompt(' enter max for '//trim(rprompt)//':',lim2(icontourplot,2)) if (all(abs(lim2(icontourplot,:)-lim(icontourplot,:)) < tiny(lim))) then call reset_lim2(icontourplot) endif endif endif endif else irender = 0 endif if (any(iamvec(1:numplot).ne.0) .and. (icoordsnew.eq.icoords)) then ivecplottemp = -1 ierr = 1 do while(ierr /= 0 .and. ivecplottemp /= 0) ivecplottemp = ivecplot ierr = 0 call prompt('(vector plot) ('//trim(vecprompt)//')',ivecplottemp,0,maxval(iamvec)) if (.not.any(iamvec(1:numplot).eq.ivecplottemp)) then print "(a)",'Error, value not in list' ierr = 1 endif enddo ivecplot = ivecplottemp else ivecplot = 0 endif if (ivecplot.gt.0 .and. irender.eq.0) then call prompt('plot particles?',iplotpartvec) endif else if (iAllowRendering) then call prompt('(render) (0=none)',irender,0,(numplot-nextra)) else irender = 0 endif ivecplot = 0 endif elseif (ipicky > 0 .and. ipicky==itoomre .or. ipicky==isurfdens) then if (ipicky==isurfdens) print "(a)",' setting x axis to r for surface density plot' if (ipicky==itoomre) print "(a)",' setting x axis to r for Toomre Q plot' ipickx = 1 irender = 0 ivecplot = 0 elseif (ipicky > 0 .and. ipicky==ipdf) then call prompt(' enter x axis for PDF calculation ',ipickx,1,ndataplots) irender = 0 ivecplot = 0 elseif (ipicky > 0 .and. ipicky==icolpixmap) then call prompt(' enter corresponding SPH column for particle data ',irender,0,ndataplots) ipickx = 0 ivecplot = 0 elseif (ipicky==numplot+1) then ! !--for multiplots, check that options are valid. If not, re-prompt for multiplot ! settings ! ipickx = 0 irender = 0 ivecplot = 0 if (any(multiploty(1:nyplotmulti) <= 0) .or. & any(multiploty(1:nyplotmulti) > numplot) .or. & any(multiplotx(1:nyplotmulti) <= 0) .or. & any(multiplotx(1:nyplotmulti) > numplot)) then print "(/,a,/)",'ERROR: multiplot settings out of range, please re-enter these' call options_multiplot endif endif ! !--call main plotting routine ! call timestep_loop(ipicky,ipickx,irender,icontourplot,ivecplot) endif !------------------------------------------------------------------------ ! if input is an integer > numplot+1, quit !------------------------------------------------------------------------ elseif (ipicky > numplot+1) then return else !------------------------------------------------------------------------ ! if input is a string, use menu options !------------------------------------------------------------------------ !-- Menu shortcuts; so you can type e.g. o2 and get the o)ptions menu, item 2 read(ioption(2:2),*,iostat=ierr) ichoose if (ierr /= 0) ichoose = 0 select case(ioption(1:1)) !------------------------------------------------------------------------ !+ Sets up plotting of (m)ultiple quantities per timestep case('m','M') call options_multiplot !------------------------------------------------------------------------ !+ This submenu sets options relating to the (d)ata read case('d','D') call submenu_data(ichoose) !------------------------------------------------------------------------ !+ This option turns (i)nteractive mode on/off case('i','I') interactive = .not.interactive print "(a)",' Interactive mode is '//print_logical(interactive) !------------------------------------------------------------------------ !+ This submenu sets (p)age setup options case('p','P') call submenu_page(ichoose) !------------------------------------------------------------------------ !+ This submenu sets particle plot (o)ptions case('o','O') call submenu_particleplots(ichoose) !------------------------------------------------------------------------ !+ This submenu sets le(g)end and title options case('g','G') call submenu_legend(ichoose) !------------------------------------------------------------------------ !+ This submenu sets (r)endering options case('r','R') if (ndim.le.1) print "(a)",'WARNING: these options have no effect in < 2D' call submenu_render(ichoose) !------------------------------------------------------------------------ !+ This submenu sets (v)ector plotting options case('v','V') if (ndim.le.1) print "(a)",'WARNING: these options have no effect in < 2D' call submenu_vecplot(ichoose) !------------------------------------------------------------------------ !+ This submenu sets cross section and rotation options case('x','X') if (ndim.le.1) print "(a)",'WARNING: these options have no effect in < 2D' call submenu_xsecrotate(ichoose) !------------------------------------------------------------------------ !+ This submenu sets options relating to the plot limits case('l','L') call submenu_limits(ichoose) !------------------------------------------------------------------------ !+ The (s)ave option saves the default options to a !+ file called `splash.defaults'' in the current directory which !+ is read automatically upon the next invocation of splash. !+ This file uses namelist formatting and may be edited !+ manually prior to startup if so desired. This is quite !+ useful for setting multiplots with many plots per page !+ The (S)ave option writes both the defaults file and !+ also saves the current plot limits to a file called !+ 'splash.limits' which is also read automatically !+ at startup. case('s') if (ioption(2:2).eq.'a') then call prompt('enter prefix for defaults file: ',fileprefix,noblank=.true.) if (index(fileprefix,'.defaults').eq.0) then defaultsfile = trim(fileprefix)//'.defaults' else defaultsfile = trim(fileprefix) endif endif call defaults_write(defaultsfile) case('S') if (ioption(2:2).eq.'a' .or. ioption(2:2).eq.'A') then call prompt('enter prefix for filenames: ',fileprefix,noblank=.true.) call set_filenames(trim(fileprefix)) endif call defaults_write(defaultsfile) call write_limits(limitsfile) if (iRescale) call write_unitsfile(unitsfile,ncolumns) !------------------------------------------------------------------------ !+ Slightly obsolete: prints whatever help may be helpful case('h','H') print "(10(/a))",' Hint: menu items can be shortcut by typing, e.g. o2 for ',& ' the o)ptions menu, item 2.',' ', & ' for detailed help, consult the user guide',' ',& ' (splash/docs/splash.pdf ',& ' or http://users.monash.edu.au/~dprice/splash/userguide/)', & ' ',' and/or the online FAQ. If you''re really stuck, email me! ' read* !------------------------------------------------------------------------ !+ (q)uit, unsurprisingly, quits. Typing a number greater !+ than the number of data columns also exits the program !+ (e.g. I often simply type 99 to exit). case('q','Q') return !------------------------------------------------------------------------ case DEFAULT print "(a)",'unknown option '//trim(ioption) end select endif enddo menuloop return contains !---------------------------------------------------- ! multiplot setup !---------------------------------------------------- subroutine options_multiplot use settings_page, only:nacross, ndown use settings_render, only:iplotcont_nomulti use limits, only:lim,lim2,lim2set,reset_lim2 use labels, only:is_coord,labeltype use params, only:maxparttypes use settings_data, only:ntypes implicit none integer :: ifac,ierr,itype,nvalues logical :: isamex, isamey, icoordplot, anycoordplot, imultisamepanel integer, dimension(maxparttypes) :: itypelist call prompt('Enter number of plots per timestep:',nyplotmulti,1,numplot) isamey = all(multiploty(1:nyplotmulti).eq.multiploty(1)) if (ndim.ge.2) call prompt('Same y axis for all?',isamey) if (isamey) then call prompt('Enter y axis for all plots',multiploty(1),1,numplot) multiploty(2:nyplotmulti) = multiploty(1) endif isamex = all(multiplotx(1:nyplotmulti).eq.multiplotx(1)) call prompt('Same x axis for all?',isamex) if (isamex) then call prompt('Enter x axis for all plots',multiplotx(1),1,numplot) multiplotx(2:nyplotmulti) = multiplotx(1) endif anycoordplot = .false. do i=1,nyplotmulti print*,'-------------- Plot number ',i,' --------------' if (.not.isamey) then call prompt(' y axis ',multiploty(i),1,numplot) endif if (multiploty(i).le.ndataplots .and. .not.isamex) then call prompt(' x axis ',multiplotx(i),1,ndataplots) else if (multiploty(i).eq.isurfdens) then print "(a)",' setting x axis to r for surface density plot' multiplotx(i) = 1 elseif (multiploty(i).eq.itoomre) then print "(a)",' setting x axis to r for Toomre Q plot' multiplotx(i) = 1 elseif (multiploty(i).eq.ipdf) then call prompt(' enter x axis for PDF calculation ',multiplotx(i),1,ndataplots) elseif (multiploty(i).eq.icolpixmap) then call prompt(' enter corresponding SPH column for particle data ',irendermulti(i),0,ndataplots) multiplotx(i) = 1 elseif(.not.isamex) then multiplotx(i) = multiploty(i) endif endif ! !--work out whether rendering is allowed ! iAllowRendering = allowrendering(multiplotx(i),multiploty(i)) icoordplot = (is_coord(multiplotx(i),ndim) .and. is_coord(multiploty(i),ndim)) if (icoordplot) anycoordplot = icoordplot if (icoordplot) then if (iAllowRendering) then call prompt('(render) (0=none)',irendermulti(i),0,numplot-nextra) if (irendermulti(i).gt.0 .and. iplotcont_nomulti .and. icolours.ne.0) then if (double_rendering) then rprompt = '2nd render' else rprompt = 'contours' endif call prompt('('//trim(rprompt)//') (0=none)',icontourmulti(i),0,numplot-nextra) if (icontourmulti(i).eq.irendermulti(i)) then if (iadapt) then print "(a)",' limits for '//trim(rprompt)//' are adaptive ' else if (.not.lim2set(icontourmulti(i))) lim2(icontourmulti(i),:) = lim(icontourmulti(i),:) call prompt(' enter min for '//trim(rprompt)//':',lim2(icontourmulti(i),1)) call prompt(' enter max for '//trim(rprompt)//':',lim2(icontourmulti(i),2)) if (all(abs(lim2(icontourmulti(i),:)-lim(icontourmulti(i),:)) < tiny(lim))) then call reset_lim2(icontourmulti(i)) endif endif endif else icontourmulti(i) = 0 endif !iplotcontmulti(i) = iplotcont_nomulti endif if (any(iamvec(1:numplot).gt.0)) then ivecplottemp = -1 ierr = 1 do while(ierr.ne.0 .and. ivecplottemp.ne.0) ivecplottemp = ivecplotmulti(i) ierr = 0 call prompt('(vector plot) ('//trim(vecprompt)//')',ivecplottemp,0,maxval(iamvec)) if (.not.any(iamvec(1:numplot).eq.ivecplottemp)) then print "(a)",'Error, value not in list' ierr = 1 endif enddo ivecplotmulti(i) = ivecplottemp else ivecplotmulti(i) = 0 endif if (ivecplotmulti(i).gt.0 .and. irendermulti(i).eq.0) then call prompt('plot particles?',iplotpartvec) endif else ! !--set irender, icontour and ivecplot to zero if no rendering allowed ! if (multiploty(i).ne.icolpixmap) irendermulti(i) = 0 icontourmulti(i) = 0 ivecplotmulti(i) = 0 endif if (icoordplot .and. ndim.ge.2) then call prompt(' is this a cross section (no=projection)? ',x_secmulti(i)) if (x_secmulti(i)) then call prompt('enter co-ordinate location of cross section slice',xsecposmulti(i)) endif endif ! !--prompt for selection of different particle types ! if more than one SPH particle type is present ! itypelist = 0 if (ntypes.ge.2) then call prompt('use all active particle types?',iusealltypesmulti(i)) if (iusealltypesmulti(i)) then nvalues = 0 itypelist(:) = 0 else ! !--prepare list of types based on current iplotpartoftypemulti ! nvalues = 0 do itype=1,ntypes if (iplotpartoftypemulti(itype,i)) then nvalues = nvalues + 1 itypelist(nvalues) = itype endif enddo if (nvalues.eq.0) then print*,'warning: internal error in type list' itypelist(:) = 0 nvalues = 1 endif ! !--prompt for list of types to use ! do itype=1,ntypes print "(i2,':',1x,a)",itype,'use '//trim(labeltype(itype))//' particles' enddo call prompt('Enter type or list of types to use',itypelist,nvalues,1,ntypes) ! !--set which particle types to plot ! iplotpartoftypemulti(:,i) = .false. iplotpartoftypemulti(itypelist(1:nvalues),i) = .true. endif else ! !--if ntypes < 2 always use the (only) particle type ! iusealltypesmulti(i) = .true. endif enddo if (isamex .and. .not.anycoordplot) then imultisamepanel = .false. !call prompt('plot all plots in same panel? (default is different panels)',imultisamepanel) else imultisamepanel = .false. endif if (nyplotmulti.eq.1 .or. imultisamepanel) then nacross = 1 ndown = 1 print*,'setting nacross,ndown = ',nacross,ndown elseif (mod(nacross*ndown,nyplotmulti).ne.0) then !--guess nacross,ndown based on largest factor ifac = nyplotmulti/2 do while (mod(nyplotmulti,ifac).ne.0 .and. ifac.gt.1) ifac = ifac - 1 end do if (ifac.le.1) then nacross = nyplotmulti/2 else nacross = ifac endif if (nacross.le.0) nacross = 1 ndown = nyplotmulti/nacross print*,'setting nacross,ndown = ',nacross,ndown else print*,'nacross = ',nacross,' ndown = ',ndown endif return end subroutine options_multiplot end subroutine menu !---------------------------------------------- ! utility function which determines whether ! or not rendering is allowed or not !---------------------------------------------- logical function allowrendering(iplotx,iploty,xsec) use labels, only:ih,irho,get_z_coord use multiplot, only:itrans use settings_data, only:ndim,ndataplots,icoords,icoordsnew use settings_render, only:icolour_particles use geometry, only:coord_is_length implicit none integer, intent(in) :: iplotx,iploty logical, intent(in), optional :: xsec integer :: itransx,itransy,iz logical :: is_xsec,islengthz if (present(xsec)) then is_xsec = xsec else is_xsec = .true. endif itransx = 0 itransy = 0 if (iplotx.gt.0) itransx = itrans(iplotx) if (iploty.gt.0) itransy = itrans(iploty) iz = get_z_coord(ndim,iplotx,iploty) islengthz = coord_is_length(iz,icoordsnew) ! !--work out whether rendering is allowed based on presence of rho, h & m in data read ! also must be in base coordinate system and no transformations applied ! if ((ih.gt.0 .and. ih.le.ndataplots) & .and.(irho.gt.0 .and. irho.le.ndataplots) & .and.(icoords.eq.icoordsnew .or. (.not.is_xsec .or. (is_xsec .and. islengthz))) & .and.(itransx.eq.0 .and. itransy.eq.0)) then allowrendering = .true. else allowrendering = .false. if (icolour_particles) allowrendering = .true. endif end function allowrendering !---------------------------------------------- ! utility function which sets up the "extra" ! plot columns and returns the total number ! of allowed columns for plotting !---------------------------------------------- subroutine set_extracols(ncolumns,ncalc,nextra,numplot,ndataplots) use params, only:maxplot use labels, only:ipowerspec,iacplane,isurfdens,itoomre,iutherm,ipdf,label,icolpixmap use settings_data, only:ndim,icoordsnew,ivegotdata,debugmode use settings_part, only:iexact use system_utils, only:lenvironment use write_pixmap, only:ireadpixmap implicit none integer, intent(in) :: ncolumns integer, intent(inout) :: ncalc integer, intent(out) :: nextra,numplot,ndataplots ! !-add extra columns (but not if nothing read from file) ! if (ncolumns.gt.0) then nextra = 0 ipowerspec = 0 iacplane = 0 isurfdens = 0 itoomre = 0 if (ndim.eq.3 .and. icoordsnew.eq.2 .or. icoordsnew.eq.3) then nextra = nextra + 1 isurfdens = ncolumns + ncalc + nextra label(isurfdens) = 'Surface density' if (iutherm.gt.0 .and. iutherm.le.ncolumns) then nextra = nextra + 1 itoomre = ncolumns + ncalc + nextra label(itoomre) = 'Toomre Q parameter' endif endif if (ndim.eq.3 .and. lenvironment('SPLASH_TURB')) then !--Probability Density Function nextra = nextra + 1 ipdf = ncolumns + ncalc + nextra label(ipdf) = 'PDF' endif if (ndim.le.1 .and. lenvironment('SPLASH_TURB')) then !! .or. ndim.eq.3) then ! if 1D or no coord data (then prompts for which x) nextra = nextra + 1 ! one extra plot = power spectrum ipowerspec = ncolumns + ncalc + nextra label(ipowerspec) = '1D power spectrum' else ipowerspec = 0 endif if (iexact.eq.6) then ! toy star plot a-c plane nextra = nextra + 1 iacplane = ncolumns + ncalc + nextra label(iacplane) = 'a-c plane' else iacplane = 0 endif !nextra = nextra + 1 !label(ncolumns+ncalc+nextra) = 'gwaves' if (ndim.ge.2) then if (ireadpixmap) then nextra = nextra + 1 icolpixmap = ncolumns + ncalc + nextra label(icolpixmap) = '2D pixel map' endif endif endif ! !--now that we know nextra, set the total number of allowed plots (numplot). ! if (ivegotdata) then numplot = ncolumns + ncalc + nextra if (numplot.gt.maxplot) then print "(a,i3,a)",' ERROR: total number of columns = ',numplot,' is greater ' print "(a,i3,a)",' than the current allowed maximum (',maxplot,').' print "(a)",' This is set by the parameter "maxplot" in the params module' print "(a)",' in the file globaldata.f90 -- edit this and recompile splash' print "(a)",' (or email me if this happens to increase the default limit)' stop endif ndataplots = ncolumns + ncalc else numplot = 0 ndataplots = 0 ncalc = 0 endif if (debugmode) print*,'DEBUG: numplot = ',numplot, ' ncalc = ',ncalc,' ndataplots = ',ndataplots return end subroutine set_extracols !---------------------------------------- ! instant multiplot setup from main menu !---------------------------------------- subroutine set_instant_multiplot(string,ipicky,ipickx,numplot,nmulti,multiplotx,multiploty,nx,ny) use params, only:maxplot use prompting, only:prompt character(len=*), intent(in) :: string integer, intent(in) :: numplot integer, intent(inout) :: ipicky,ipickx integer, intent(inout) :: nmulti,nx,ny integer, intent(inout) :: multiplotx(:),multiploty(:) integer :: ipickarr(maxplot),ierr,i ipickarr = 0 read(string,*,iostat=ierr) ipickarr i = 1 do while (i < size(ipickarr) .and. ipickarr(i) /= 0 .and. ipickarr(i) <= numplot) i = i + 1 enddo if (i > 2) then nmulti = i-1 !--make sure nmulti matches the number of panels on the page if (nmulti /= nx*ny) then nx = 1 ny = nmulti endif multiploty(1:nmulti) = ipickarr(1:nmulti) ipicky = numplot + 1 if (ipickx==0) ipickx = 1 ! do not allow zero as default call prompt(' (x axis) ',ipickx) multiplotx(1:nmulti) = ipickx endif end subroutine set_instant_multiplot !-------------------- ! print menu header !-------------------- subroutine print_header integer :: v(8),i integer, parameter :: m(48) = (/32,68,111,110,39,116,32,102,111,114,103,101,116,32,116,111,& 32,115,101,110,100,32,68,97,110,105,101,108,32,97,32,98,& 105,114,116,104,100,97,121,32,109,101,115,115,97,103,101,33/) integer, parameter :: c(49) = (/32,120,111,120,111,120,111,120,111,32,77,101,114,114,121,32,& 67,104,114,105,115,116,109,97,115,32,102,114,111,109,32,115,& 112,108,97,115,104,33,32,120,111,120,111,120,111,120,111,120,111/) integer, parameter :: d(49) = (/32,79,111,79,111,79,111,79,32,83,80,76,65,83,72,32,119, & 105,115,104,101,115,32,121,111,117,32,97,32,118,101,114,121,32,104,& 97,112,112,121,32,33,32,79,111,79,111,79,111,79/) call date_and_time(values=v) if (v(2)==m(1)/4 .and. v(3)==v(2)-2) then print "(/,48(a))",(achar(m(i)),i=1,48) elseif (v(2)==(m(1)-20) .and. v(3)>20) then print "(/,49(a))",(achar(c(i)),i=1,49) elseif (v(2)==nint(0.6) .and. v(3)==d(2)/79) then print "(/,40(a),i4,9(a))",(achar(d(i)),i=1,40),v(1),(achar(d(i)),i=41,49) else print "(/a)",' You may choose from a delectable sample of plots' endif end subroutine print_header end module mainmenu splash/src/read_data_mbate.f90000644 000766 000000 00000030244 13261626263 017152 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM MATTHEW BATE'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use settings_units, only:units use labels, only:unitslabel use mem_allocation implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer, parameter :: maxptmass = 1000 real, parameter :: pi=3.141592653589 integer :: i,j,ifile,ierr,ipart integer :: npart_max,nstep_max,ncolstep,npart,nptmassi,nunknown logical :: iexist,doubleprec character(len=len(rootname)+10) :: dumpfile integer :: nprint, n1, n2, nptmass, nstepsalloc integer, dimension(:), allocatable :: isteps, iphase integer, dimension(maxptmass) :: listpm !--use these lines if dump is double precision real(doub_prec), dimension(:,:), allocatable :: dattemp real(doub_prec) :: udisti,umassi,utimei real(doub_prec) :: timei, gammai real(doub_prec) :: rhozero, RK2 real(doub_prec) :: escap,tkin,tgrav,tterm real(doub_prec) :: dtmax !--use these lines for single precision real, dimension(:,:), allocatable :: dattemps real :: timesi, gammasi real :: rhozeros, RK2s real :: escaps,tkins,tgravs,tterms real :: dtmaxs,tcomp nstepsread = 0 nstep_max = 0 npart_max = maxpart ifile = 1 dumpfile = trim(rootname) ! !--check if data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 ncolstep = 11 ! number of columns in file ncolumns = ncolstep ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 print "(1x,a)",'reading Matthew Bate''s/Willy Benz''s old SPH code format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' else ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,end=55,iostat=ierr) udisti,umassi,utimei,nprint,n1,n2,timei,gammai,rhozero,RK2 print*,'nprint = ',nprint doubleprec = .true. !--try single precision if non-sensible values for time, gamma etc. if (ierr.ne.0 .or. timei.lt.0. .or. timei.gt.1e30 & .or. gammai.lt.1. .or. gammai.gt.10. & .or. rhozero.lt.0. .or. RK2.lt.0. .or. RK2.gt.1.e10) then doubleprec = .false. endif nstepsalloc = 1 do while (ierr == 0) npart_max = max(npart_max,nprint) nstepsalloc = nstepsalloc + 1 read(15,iostat=ierr) udisti,umassi,utimei,nprint enddo ierr = 0 if (.not.allocated(dat) .or. nprint.gt.npart_max) then npart_max = max(npart_max,INT(1.1*nprint)) call alloc(npart_max,nstepsalloc,ncolstep+ncalc) endif rewind(15) endif if (ierr /= 0) then print "(a)",'*** ERROR READING TIMESTEP HEADER ***' else ! !--loop over the timesteps in this file ! over_steps_in_file: do !npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then !if (nstepsread.gt.2) then ! nstepsalloc = j + 2*nstepsread !else ! nstepsalloc = j !endif call alloc(maxpart,nstepsalloc,maxcol) endif ! !--allocate integer arrays required for data read ! if (allocated(isteps)) deallocate(isteps) allocate(isteps(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' if (allocated(iphase)) deallocate(iphase) allocate(iphase(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' ! !--now read the timestep data in the dumpfile ! write(*,"(a,i5,a)",advance="no") '| step ',j,': ' if (doubleprec) then print "(a)",'double precision dump' ! !--allocate a temporary array for (double precision) variables ! if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(npart_max,ncolstep),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' read(15,end=55,iostat=ierr) udisti, umassi, utimei, & nprint, n1, n2, timei, gammai, rhozero, RK2, & (dattemp(i,7), i=1, nprint),escap, tkin, tgrav, tterm, & (dattemp(i,1), i=1, nprint), (dattemp(i,2), i=1, nprint), & (dattemp(i,3), i=1, nprint), (dattemp(i,4), i=1, nprint), & (dattemp(i,5), i=1, nprint), (dattemp(i,6), i=1, nprint), & (dattemp(i,8), i=1, nprint), (dattemp(i,9), i=1, nprint), & (dattemp(i,10), i=1, nprint), (dattemp(i,11),i=1,nprint), & dtmax, (isteps(i), i=1,nprint), (iphase(i),i=1,nprint), & nptmass, (listpm(i), i=1,nptmass) else ! !--allocate a temporary array for (double precision) variables ! if (allocated(dattemps)) deallocate(dattemps) allocate(dattemps(npart_max,ncolstep),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' print "(a)",'single precision dump' read(15,end=55,iostat=ierr) udisti, umassi, utimei, & nprint, n1, n2, timesi, gammasi, rhozeros, RK2s, & (dattemps(i,7), i=1, nprint),escaps, tkins, tgravs, tterms, & (dattemps(i,1), i=1, nprint), (dattemps(i,2), i=1, nprint), & (dattemps(i,3), i=1, nprint), (dattemps(i,4), i=1, nprint), & (dattemps(i,5), i=1, nprint), (dattemps(i,6), i=1, nprint), & (dattemps(i,8), i=1, nprint), (dattemps(i,9), i=1, nprint), & (dattemps(i,10), i=1, nprint), (dattemps(i,11),i=1,nprint), & dtmaxs, (isteps(i), i=1,nprint), (iphase(i),i=1,nprint), & nptmass, (listpm(i), i=1,nptmass) endif ! !--set transformation factors between code units/real units ! units(1:3) = udisti unitslabel(1:3) = ' [cm]' units(4:6) = udisti/utimei unitslabel(4:6) = ' [cm/s]' units(7) = udisti unitslabel(7) = ' [cm]' units(8) = (udisti/utimei)**2 unitslabel(8) = ' [erg/g]' units(9) = umassi unitslabel(9) = ' [g]' units(10) = umassi/udisti**3 unitslabel(10) = ' [g/cm\u3\d]' ! !--convert to single precision and separate pt masses from normal particles ! ipart = 0 do i=1,nprint if (iphase(i).eq.0) then ipart = ipart + 1 if (doubleprec) then dat(ipart,1:ncolstep,j) = real(dattemp(i,1:ncolstep)) else dat(ipart,1:ncolstep,j) = dattemps(i,1:ncolstep) endif endif enddo npart = ipart ! !--place point masses after normal particles ! nptmassi = 0 do i=1,nprint if (iphase(i).ge.1) then ipart = ipart + 1 nptmassi = nptmassi + 1 if (doubleprec) then dat(ipart,1:ncolstep,j) = real(dattemp(i,1:ncolstep)) else dat(ipart,1:ncolstep,j) = dattemps(i,1:ncolstep) endif endif enddo if (nptmass.gt.0) print*,' Number of point masses = ',nptmass if (nptmassi.ne.nptmass) print *,'WARNING: nptmass from iphase =',nptmassi,'not equal to nptmass' ! !--put any others as unknown ! nunknown = 0 do i=1,nprint if (iphase(i).lt.0) then ipart = ipart + 1 nunknown = nunknown + 1 if (doubleprec) then dat(ipart,1:ncolstep,j) = real(dattemp(i,1:ncolstep)) else dat(ipart,1:ncolstep,j) = dattemps(i,1:ncolstep) endif endif enddo if (nunknown.gt.0) print *,nunknown,' particles of unknown type (probably dead)' if (allocated(dattemp)) deallocate(dattemp) if (allocated(dattemps)) deallocate(dattemps) if (allocated(isteps)) deallocate(isteps) if (allocated(iphase)) deallocate(iphase) npartoftype(1,j) = npart npartoftype(2,j) = nptmassi npartoftype(3,j) = nunknown if (doubleprec) then gamma(j) = real(gammai) time(j) = real(timei) else gamma(j) = gammasi time(j) = timesi endif print*,' time = ',time(j),' gamma = ',gamma(j) if (ierr /= 0) then print "(a)",'*** INCOMPLETE DATA ***' nstepsread = nstepsread + 1 exit over_steps_in_file else nstepsread = nstepsread + 1 endif j = j + 1 enddo over_steps_in_file endif 55 continue ! !--reached end of file ! close(15) if (j-1 .gt. 0) then print*,'>> end of dump file: nsteps =',j-1,'ntot = ', & sum(npartoftype(:,j-1)),'nghost=',npartoftype(2,j-1) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ih = 7 ! smoothing length iutherm = 8 ! thermal energy ipmass = 9 ! particle mass irho = 10 ! location of rho in data array if (ncolumns.gt.10) then label(11) = 'dgrav' endif label(ix(1:ndim)) = labelcoord(1:ndim,1) do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo label(irho) = 'density' label(iutherm) = 'u' label(ih) = 'h' label(ipmass) = 'particle mass' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 3 !!maxparttypes labeltype(1) = 'gas' labeltype(2) = 'sink' labeltype(3) = 'unknown' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. UseTypeInRenderings(3) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/H5Part/000755 000766 000000 00000000000 13261626263 014656 5ustar00dpricewheel000000 000000 splash/src/plotutils.f90000644 000766 000000 00000010072 13261626263 016132 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !--------------------------------------------------------------------------- ! module containing application programming interfaces for basic ! plotting functions. The idea is to add more to this module to ! eventually use it to be able to change backends more easily. !--------------------------------------------------------------------------- module plotutils use plotlib, only:plot_line,plot_bins implicit none public :: plotline,plotbins,formatreal private contains ! ! line plotting, with blanking ! subroutine plotline(npts,xline,yline,blank) implicit none integer, intent(in) :: npts real, intent(in), dimension(:) :: xline,yline real, intent(in), optional :: blank integer :: i,nseg,istart if (present(blank)) then nseg = 0 istart = 1 !--plot line in segments, leaving blank segments where y=blank do i=1,npts if (abs(yline(i)-blank).lt.tiny(yline) .or. i.eq.npts) then if (nseg.gt.0) call plot_line(nseg,xline(istart:istart+nseg),yline(istart:istart+nseg)) istart = i+1 nseg = 0 else nseg = min(nseg + 1,npts-1) endif enddo else call plot_line(npts,xline,yline) endif return end subroutine plotline ! ! binned histogram plotting, with blanking ! subroutine plotbins(nbins,xbins,ybins,blank) implicit none integer, intent(in) :: nbins real, intent(in), dimension(:) :: xbins,ybins real, intent(in), optional :: blank integer :: i,nseg,istart if (present(blank)) then nseg = 0 istart = 1 !--plot line in segments, leaving blank segments where y=blank do i=1,nbins if (abs(ybins(i)-blank).lt.tiny(ybins) .or. i.eq.nbins) then if (nseg.gt.0) call plot_bins(nseg,xbins(istart:istart+nseg),ybins(istart:istart+nseg),.true.) istart = i+1 nseg = 0 else nseg = min(nseg + 1,nbins-1) endif enddo else call plot_bins(nbins,xbins,ybins,.true.) endif return end subroutine plotbins ! ! formatting of real variables into strings (like PGNUMB) ! subroutine formatreal(val,string,ierror) implicit none real, intent(in) :: val character(len=*), intent(out) :: string integer, intent(out), optional :: ierror integer :: ierr,i,idot logical :: nonzero if (abs(val).ge.1.d99) then write(string,"(1pe10.3)",iostat=ierr) val elseif (abs(val).lt.1.e-3 .or. abs(val).ge.1.e4) then write(string,"(1pe9.2)",iostat=ierr) val elseif (abs(val).lt.0.1) then write(string,"(f8.3)",iostat=ierr) val elseif (abs(val).ge.100.) then write(string,"(f8.0)",iostat=ierr) val else write(string,"(f8.2)",iostat=ierr) val endif string = adjustl(trim(string)) if (present(ierror)) ierror = ierr ! !--strip trailing zeros after the decimal place ! (and the decimal place if it is the last character) ! idot = index(string,'.') if (idot.gt.0) then nonzero = .false. do i = len_trim(string),idot,-1 if (.not.nonzero .and. string(i:i).eq.'0') then string(i:i) = ' ' elseif (.not.nonzero .and. string(i:i).eq.'.') then string(i:i) = ' ' nonzero = .true. else nonzero = .true. endif enddo endif string = trim(string) return end subroutine formatreal end module plotutils splash/src/read_data_gadget_hdf5.f90000644 000766 000000 00000102117 13261626263 020222 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR HDF5 OUTPUT FROM THE GADGET CODE ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! GSPLASH_USE_Z if 'YES' uses redshift in the legend instead of time ! GSPLASH_USE_IDS if 'YES' resorts particles according to their ParticleIDs ! GSPLASH_DARKMATTER_HSOFT if given a value > 0.0 will assign a ! smoothing length to dark matter particles which can then be ! used in the rendering ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Partial data read implemented Nov 2006 means that columns with ! the 'required' flag set to false are not read (read is therefore much faster) !------------------------------------------------------------------------- ! ! The module below contains interface routines to c functions ! that perform the actual calls to the HDF5 libs ! !------------------------------------------------------------------------- module gadgethdf5read use params, only:maxplot,doub_prec use labels, only:lenlabel use, intrinsic :: iso_c_binding, only:c_int,c_double,c_char implicit none real :: hsoft character(len=lenlabel), dimension(maxplot) :: blocklabelgas integer, dimension(maxplot) :: blocksize logical :: havewarned = .false. integer, parameter :: maxtypes = 6 logical :: arepo = .false. interface subroutine read_gadget_hdf5_header(filename,maxtypes,npartoftypei,massoftypei,& timeh,zh,iFlagSfr,iFlagFeedback,Nall,iFlagCool, & igotids,ndim,ndimV,nfiles,ncol,ierr) bind(c) import implicit none character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(in), value :: maxtypes integer(kind=c_int), intent(out) :: iFlagSfr,iFlagFeedback,iFlagCool,igotids integer(kind=c_int), dimension(6), intent(out) :: npartoftypei,Nall real(kind=c_double), dimension(6), intent(out) :: massoftypei real(kind=c_double), intent(out) :: timeh,zh integer(kind=c_int), intent(out) :: ndim,ndimV,nfiles,ncol,ierr end subroutine read_gadget_hdf5_header subroutine read_gadget_hdf5_data(filename,maxtypes,npartoftypei,massoftypei,& ncol,isrequired,i0,ierr) bind(c) import implicit none character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(in), value :: maxtypes integer(kind=c_int), dimension(6), intent(in) :: npartoftypei real(kind=c_double), dimension(6), intent(in) :: massoftypei integer(kind=c_int), intent(in), value :: ncol integer(kind=c_int), intent(out) :: ierr integer(kind=c_int), dimension(ncol), intent(in) :: isrequired integer(kind=c_int), dimension(maxtypes), intent(in) :: i0 end subroutine read_gadget_hdf5_data end interface contains !--------------------------------------------------------------------------- ! ! function to safely convert a string from c format (ie. with a terminating ! ascii null character) back to a normal Fortran string ! !--------------------------------------------------------------------------- function fstring(array) implicit none character(kind=c_char), dimension(:), intent(in) :: array character(len=size(array)-1) :: fstring integer :: i fstring = '' do i=1,size(array) if (array(i).eq.achar(0)) exit fstring(i:i) = array(i) enddo end function fstring !--------------------------------------------------------------------------- ! ! function to reformat the HDF5 label into the splash column label ! by inserting a space whereever a capital letter occurs ! !--------------------------------------------------------------------------- function reformatlabel(label) implicit none character(len=*), intent(in) :: label character(len=2*len(label)) :: reformatlabel integer :: is,ia,ib,ip reformatlabel = label ip = 1 do is = 2, len_trim(label) ip = ip + 1 ia = iachar(reformatlabel(ip:ip)) ib = iachar(reformatlabel(ip-1:ip-1)) if ((ia >= iachar('A').and.ia <= iachar('Z')) .and. .not. & (ib >= iachar('A').and.ib <= iachar('Z'))) then reformatlabel = reformatlabel(1:ip-1)//' '//reformatlabel(ip:) ip = ip + 1 endif enddo end function reformatlabel end module gadgethdf5read !------------------------------------------------------------------------- ! ! The routine that reads the data into splash's internal arrays ! !------------------------------------------------------------------------- subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol,maxstep use params, only:doub_prec,maxparttypes,maxplot use settings_data, only:ndim,ndimV,ncolumns,ncalc,iformat,required,ipartialread, & ntypes,debugmode,iverbose use settings_page, only:legendtext use mem_allocation, only:alloc use labels, only:ih,irho,ipmass,labeltype use system_utils, only:renvironment,lenvironment,ienvironment,envlist use asciiutils, only:cstring use gadgethdf5read, only:hsoft,blocklabelgas,havewarned,read_gadget_hdf5_header, & read_gadget_hdf5_data,maxtypes,arepo implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile,densfile,hfile character(len=20) :: string integer, dimension(maxparttypes) :: npartoftypei,Nall integer :: i,j,itype,ierr,ierrh,ierrrho,nhset,ifile integer :: index1,index2 integer :: ncolstep,npart_max,nstep_max,ntoti,ntotall,idot integer :: iFlagSfr,iFlagFeedback,iFlagCool,igotids,nfiles,nhfac integer, dimension(6) :: i0 integer, parameter :: iunit = 11, iunitd = 102, iunith = 103 logical :: iexist,reallocate,usez,debug,goterrors real(doub_prec) :: timetemp,ztemp real(doub_prec), dimension(6) :: massoftypei real :: hfact,hfactmean,pmassi real, parameter :: pi = 3.1415926536 integer, dimension(maxplot) :: isrequired nstepsread = 0 goterrors = .false. if (maxparttypes.lt.6) then print*,' *** ERROR: not enough particle types for GADGET data read ***' print*,' *** you need to edit splash parameters and recompile ***' stop endif if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! print "(1x,a)",'reading GADGET HDF5 format' inquire(file=datfile,exist=iexist) if (.not.iexist) then ! !--look for a file with .0 on the end for multiple-file reads ! datfile=trim(rootname)//'.0.hdf5' inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(rootname)//': file not found ***' return endif endif ! !--set parameters which do not vary between timesteps ! ndim = 0 ndimV = 0 ! idumpformat = ienvironment('GSPLASH_FORMAT') ! checkids = lenvironment('GSPLASH_CHECKIDS') usez = lenvironment('GSPLASH_USE_Z') debug = lenvironment('GSPLASH_DEBUG') .or. debugmode ! !--read data from snapshots ! i = istepstart ! !--i0 is the offset used to read the data into the arrays ! (non-zero for read from multiple files) ! The offset is different for each particle type, somewhat ! complicating the data read -- we shuffle the particles from ! multiple files so that they are in type order. ! i0(:) = 0 ! !--loop over the number of files ! ifile = 0 ntotall = 0 over_files: do while(iexist) write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ifile = ifile + 1 ! !--open file and read header information ! npartoftypei(:) = 0. Nall(:) = 0. massoftypei(:) = 0. if (debug) print*,'DEBUG: reading header...' call read_gadget_hdf5_header(cstring(datfile),maxtypes, & npartoftypei,massoftypei,timetemp,ztemp,iFlagSfr,iFlagFeedback,Nall,& iFlagCool,igotids,ndim,ndimV,nfiles,ncolstep,ierr) if (ierr /= 0) then print "(a)", '*** ERROR READING HEADER ***' return endif ! read(iunit,iostat=ierr) npartoftypei(1:6),massoftypei,timetemp,ztemp, & ! iFlagSfr,iFlagFeedback,Nall(1:6),iFlagCool,nfiles ntoti = int(sum(npartoftypei(1:6))) ! int here is unnecessary, but avoids compiler warnings if (nfiles.gt.1) then ntotall = int(sum(Nall(1:6))) else ntotall = ntoti endif ! !--if we are reading from multiple files, ! check that the sequence starts from the correct file ! if (nfiles.gt.1) then idot = index(datfile,'.hdf5') idot = index(datfile(1:idot-1),'.',back=.true.) if (ifile.eq.1 .and. datfile(idot:idot+1).ne.'.0') then if (nfiles.lt.100) then string = "(/,a,i2,a,/,a,/)" else string = "(/,a,i7,a,/,a,/)" endif print string,' ERROR: read is from multiple files (nfiles = ',nfiles,')',& ' but this is not the first file (does not end in .0.hdf5): skipping...' close(iunit) return endif endif if (ifile.eq.1) then ncolumns = ncolstep ! !--call set labels to get ih, ipmass, irho for use in the read routine ! hsoft = 0. ! to avoid unset variable call set_labels endif if (ifile.eq.1) then print*,'time : ',timetemp if (usez) then print "(1x,a,f8.2,a)",'z (redshift) : ',ztemp,' (using in legend from GSPLASH_USE_Z setting)' else print "(1x,a,f8.2,a)",'z (redshift) : ',ztemp,' (set GSPLASH_USE_Z=yes to use in legend)' endif endif print "(a,6(1x,i10))",' Npart (by type) : ',npartoftypei(1:6) if (any(massoftypei.gt.0.)) print "(a,6(1x,es10.3))",' Mass (by type) : ',massoftypei print "(a,6(1x,i10))",' N_gas : ',npartoftypei(1) print "(a,1x,i10)",' N_total : ',ntoti if (ifile.eq.1) print "(a,1x,i10)",' N data columns : ',ncolstep if (nfiles.gt.1 .and. ifile.eq.1) then print "(a,6(1x,i10))",' Nall : ',Nall(1:6) endif if (nfiles.gt.1) then if (ifile.eq.1) print "(a,i4,a)",' reading from ',nfiles,' files' elseif (nfiles.le.0) then print*,'*** ERROR: nfiles = ',nfiles,' in file header: aborting' return endif if (ifile.eq.1) then !--Softening lengths for Dark Matter Particles... hsoft = renvironment('GSPLASH_DARKMATTER_HSOFT') ! !--try to read dark matter and star particle smoothing lengths and/or density from a separate ! one column ascii file. If only density, use this to compute smoothing lengths. ! densfile = trim(rootname)//'.dens' hfile = trim(rootname)//'.hsml' hfact = 1.2 ! related to the analytic neighbour number (hfact=1.2 gives 58 neighbours in 3D) open(unit=iunitd,file=densfile,iostat=ierrrho,status='old',form='formatted') open(unit=iunith,file=hfile,iostat=ierrh,status='old',form='formatted') if (ih.eq.0 .and. (hsoft.gt.tiny(hsoft) .or. ierrrho.eq.0 .or. ierrh.eq.0)) then ncolumns = ncolumns + 1 blocklabelgas(ncolumns) = 'SmoothingLength' ih = ncolumns call set_labels endif if (irho.eq.0 .and. (hsoft.gt.tiny(hsoft) .or. ierrrho.eq.0 .or. ierrh.eq.0)) then ncolumns = ncolumns + 1 blocklabelgas(ncolumns) = 'Density' irho = ncolumns call set_labels endif ! !--if successfully read header, increment the nstepsread counter ! nstepsread = nstepsread + 1 endif ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntotall) else ! if first time, save on memory npart_max = int(ntotall) endif endif if (i.ge.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then if (igotids.eq.1) then call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol),mixedtypes=.true.) else call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol)) endif endif masstype(1:6,i) = massoftypei(1:6) ! !--copy npartoftypei into allocated header arrays ! and set the offset position of particle types in the main data arrays ! if (nfiles.eq.1 .or. ifile.eq.1) then i0(1) = 0 do itype=2,ntypes if (nfiles.eq.1) then i0(itype) = sum(npartoftypei(1:itype-1)) ! this is avoid depending on Nall at all for single file read else i0(itype) = sum(Nall(1:itype-1)) endif enddo npartoftype(:,i) = npartoftypei else i0(1) = npartoftype(1,i) do itype=2,ntypes i0(itype) = sum(Nall(1:itype-1)) + npartoftype(itype,i) enddo npartoftype(:,i) = npartoftype(:,i) + npartoftypei endif if (debugmode) print*,'DEBUG: starting position for each type in data array: ',i0(:) ! !--set time to be used in the legend ! if (ifile.eq.1) then if (usez) then !--use this line for redshift legendtext = 'z=' time(i) = real(ztemp) else !--use this line for code time time(i) = real(timetemp) endif else if (usez) then if (abs(real(ztemp)-time(i)).gt.tiny(0.)) print*,'ERROR: redshift different between files in multiple-file read' else if (abs(real(timetemp)-time(i)).gt.tiny(0.)) print*,'ERROR: time different between files in multiple-file read ' endif if (sum(Nall).ne.ntotall) then print*,' ERROR: Nall differs between files' goterrors = .true. endif endif ! !--read particle data ! got_particles: if (ntoti.gt.0) then isrequired(:) = 0 where (required(1:ncolumns)) isrequired(1:ncolumns) = 1 call read_gadget_hdf5_data(cstring(datfile),maxtypes,npartoftypei,massoftypei,ncolumns,isrequired,i0,ierr) endif got_particles ! !--now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. ! !--set flag to indicate that only part of this file has been read ! if (.not.all(required(1:ncolstep))) ipartialread = .true. ! !--for read from multiple files, work out the next file in the sequence ! iexist = .false. if (nfiles.gt.1 .and. ifile.lt.nfiles) then !--see if the next file exists idot = index(datfile,'.hdf5') idot = index(datfile(1:idot-1),'.',back=.true.) if (idot.le.0) then print "(a)",' ERROR: read from multiple files but could not determine next file in sequence' goterrors = .true. else write(string,*) ifile if (ifile.lt.10) then write(datfile,"(a,i1)") trim(datfile(1:idot))//trim(adjustl(string))//'.hdf5' elseif (ifile.lt.100) then write(datfile,"(a,i2)") trim(datfile(1:idot))//trim(adjustl(string))//'.hdf5' else write(datfile,"(a,i3)") trim(datfile(1:idot))//trim(adjustl(string))//'.hdf5' endif iexist = .false. inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' ERROR: read from multiple files '// & 'but could not find '//trim(datfile)//': next in sequence' goterrors = .true. endif endif endif enddo over_files if (arepo) then dat(1:npartoftype(1,i),ih,i) = dat(1:npartoftype(1,i),ih,i)**(1./3.) print "(a)",' this is an AREPO snapshot, using Volume**(1./3.) as smoothing length' elseif (required(ih) .and. size(dat(1,:,:)).ge.ih .and. npartoftype(1,i).gt.0) then ! !--for some reason the smoothing length output by GADGET is ! twice the usual SPH smoothing length ! (do this after we have read data from all of the files) ! print "(a)",' converting GADGET smoothing length on gas particles to usual SPH definition (x 0.5)' dat(1:npartoftype(1,i),ih,i) = 0.5*dat(1:npartoftype(1,i),ih,i) endif if (nfiles.gt.1. .and. any(npartoftype(:,i).ne.Nall(:))) then print*,'ERROR: sum of Npart across multiple files .ne. Nall in data read ' print*,'Npart = ',npartoftype(:,i) print*,'Nall = ',Nall(:) goterrors = .true. endif ! !--look for dark matter smoothing length/density files ! if (ierrh.eq.0 .or. ierrrho.eq.0) then if (ierrh.eq.0) then print "(a)",' READING DARK MATTER SMOOTHING LENGTHS from '//trim(hfile) ierr = 0 index1 = npartoftype(1,i)+1 index2 = npartoftype(1,i)+sum(npartoftype(2:,i)) read(iunith,*,iostat=ierr) (dat(j,ih,i),j=index1,index2) close(unit=iunith) if (ierr.lt.0) then nhset = 0 do j=index1,index2 if (dat(j,ih,i).gt.0.) nhset = nhset + 1 enddo print "(a,i10,a,/)",' *** END-OF-FILE: GOT ',nhset,' SMOOTHING LENGTHS ***' elseif (ierr.gt.0) then print "(a)", ' *** ERROR reading smoothing lengths from file' goterrors = .true. else print "(a,i10,a)",' SMOOTHING LENGTHS READ OK for ',index2-index1+1,' dark matter / star particles ' endif hsoft = 1.0 ! just so dark matter rendering is allowed in set_labels routine endif if (ierrrho.eq.0) then print "(a)",' READING DARK MATTER DENSITIES FROM '//trim(densfile) ierr = 0 index1 = npartoftype(1,i)+1 index2 = npartoftype(1,i)+sum(npartoftype(2:,i)) read(iunitd,*,iostat=ierr) (dat(j,irho,i),j=index1,index2) close(iunitd) if (ierr.lt.0) then nhset = 0 do j=index1,index2 if (dat(j,irho,i).gt.0.) nhset = nhset + 1 enddo print "(a,i10,a,/)",' *** END-OF-FILE: GOT ',nhset,' DENSITIES ***' elseif (ierr.gt.0) then print "(a)", ' *** ERROR reading dark matter densities from file' goterrors = .true. else print "(a,i10,a)",' DENSITY READ OK for ',index2-index1+1,' dark matter / star particles ' endif if (ierrh.ne.0 .and. ipmass.gt.0) then where(dat(:,irho,i) > tiny(dat)) dat(:,ih,i) = hfact*(dat(:,ipmass,i)/dat(:,irho,i))**(1./3.) elsewhere dat(:,ih,i) = 0. end where print "(a,i10,a,f5.2,a)", & ' SMOOTHING LENGTHS SET for ',j-1-index1,' DM/star particles using h = ',hfact,'*(m/rho)**(1/3)' endif hsoft = 1.0 ! just so dark matter rendering is allowed in set_labels routine endif else ! !--if a value for the dark matter smoothing length is set ! via the environment variable GSPLASH_DARKMATTER_HSOFT, ! give dark matter particles this smoothing length ! and a density of 1 (so column density plots work) ! if (hsoft.gt.tiny(hsoft)) then if (required(ih)) then print "(a,1pe10.3,a)",' ASSIGNING SMOOTHING LENGTH of h = ',hsoft, & ' to dark matter particles' !print*,'ih = ',ih,' npartoftype = ',npartoftype(1:2,i), shape(dat) if (ih.gt.0) then dat(npartoftype(1,i)+1:npartoftype(1,i)+npartoftype(2,i),ih,i) = hsoft else print*,' ERROR: smoothing length not found in data arrays' goterrors = .true. endif endif if (required(irho)) then if (irho.gt.0) then dat(npartoftype(1,i)+1:npartoftype(1,i)+npartoftype(2,i),irho,i) = 1.0 else print*,' ERROR: place for density not found in data arrays' goterrors = .true. endif endif else if (npartoftype(1,i).le.0 .and. sum(npartoftype(:,i)).gt.0) then print "(66('*'),4(/,a),/)",'* NOTE!! For GADGET data using dark matter only, column density ',& '* plots can be produced by setting the GSPLASH_DARKMATTER_HSOFT ',& '* environment variable to give the dark matter smoothing length', & '* (for a fixed smoothing length)' hsoft = (maxval(dat(:,1,i)) - minval(dat(:,1,i)))/sum(npartoftype(2:,i))**(1./3.) print*,' suggested value for GSPLASH_DARKMATTER_HSOFT = ',hsoft hsoft = 0. print "(7(/,a),/)",'* Alternatively, and for best results, calculate a number density', & '* on dark matter particles, set individual smoothing lengths from', & '* this using h = hfact*(n)**(-1/3), with hfact=1.2 and either ', & '* dump the results back into the HSML array in the original dump ', & '* file (if using the block-labelled format), or create an ascii ',& '* file called '//trim(hfile)//' containing the smoothing length ',& '* values for the dark matter particles.' print "(2(/,a),/,66('*'),/)", '* Also make sure normalised interpolations are OFF when plotting ',& '* dark matter density ' endif endif endif ! !--pause with fatal errors ! if (goterrors .and. .not.lenvironment('GSPLASH_IGNORE_ERRORS')) then print "(/,a)",'*** ERRORS detected during data read: data will be corrupted' print "(a,/)",' Please REPORT this and/or fix your file ***' print "(a)",' (set GSPLASH_IGNORE_ERRORS=yes to skip this message)' if (iverbose.ge.1) then print "(a)",' > Press any key to bravely proceed anyway <' read* endif endif ! !--give a friendly warning about using too few or too many neighbours ! (only works with equal mass particles because otherwise we need the number density estimate) ! if (ih.gt.0 .and. required(ih) .and. ipmass.gt.0 .and. required(ipmass) & .and. abs(massoftypei(1)).lt.tiny(0.) .and. ndim.eq.3 .and. .not.havewarned) then nhfac = 100 if (npartoftype(1,i).gt.nhfac) then hfactmean = 0. do j=1,nhfac pmassi = dat(j,ipmass,i) if (pmassi.gt.0.) then pmassi = 1./pmassi else pmassi = 0. endif hfact = dat(j,ih,i)*(dat(j,irho,i)*pmassi)**(1./ndim) hfactmean = hfactmean + hfact enddo hfact = hfactmean/real(nhfac) havewarned = .true. if (hfact.lt.1.125 .or. hfact.gt.1.45) then print "(/,a)",'** FRIENDLY NEIGHBOUR WARNING! **' print "(3x,a,f5.1,a,/,3x,a,f4.2,a,i1,a)", & 'It looks like you are using around ',4./3.*pi*(2.*hfact)**3,' neighbours,', & 'corresponding to h = ',hfact,'*(m/rho)^(1/',ndim,') in 3D:' if (hfact.lt.1.15) then print "(4(/,3x,a))",'This is a quite a low number of neighbours for the cubic spline and ', & 'may result in increased noise and inaccurate wave propagation speeds', & '(a cubic lattice is also an unstable initial configuration for the ',& ' particles in this regime -- see Morris 1996, Borve et al. 2004).' elseif (hfact.gt.1.45) then print "(4(/,3x,a))",'Using h >~ 1.5*(m/rho)^(1/3) with the cubic spline results in the', & 'particle pairing instability due to the first neighbour being placed under', & 'the hump in the kernel gradient. Whilst not fatal, it results in a', & 'loss of resolution so is a bit of a waste of cpu time.' print "(4(/,3x,a))",'If you are attempting to perform a "resolution study" by increasing the', & 'neighbour number, this is a *bad idea*, as you are also increasing h.', & '(a better way is to increase the smoothness of the integrals without changing h', & ' by adopting a smoother kernel such as the M6 Quintic that goes to 3h).' endif print "(/,3x,a,/,3x,a,/)", & 'A good default is h = 1.2 (m/rho)^1/ndim ', & 'corresponding to around 58 neighbours in 3D.' else print "(/,1x,a,f5.1,a,/,1x,a,f4.2,a,i1,a,/)", & 'Simulations employ ',4./3.*pi*(2.*hfact)**3,' neighbours,', & 'corresponding to h = ',hfact,'*(m/rho)^(1/',ndim,') in 3D' endif endif else !print*,'not true' endif ! !--cover the special case where no particles have been read ! if (ntotall.le.0) then npartoftype(1,i) = 1 dat(:,:,i) = 0. endif if (nstepsread.gt.0) then print "(a,i10,a)",' >> read ',sum(npartoftype(:,istepstart+nstepsread-1)),' particles' endif return end subroutine read_data subroutine read_gadgethdf5_data_fromc(icol,npartoftypei,temparr,id,itype,i0) bind(c) use, intrinsic :: iso_c_binding, only:c_int,c_double use particle_data, only:dat,iamtype use settings_data, only:debugmode use labels, only:label,ih use system_utils, only:lenvironment implicit none integer(kind=c_int), intent(in) :: icol,npartoftypei,itype,i0 real(kind=c_double), dimension(npartoftypei), intent(in) :: temparr integer(kind=c_int), dimension(npartoftypei), intent(in) :: id integer(kind=c_int) :: i,icolput integer :: nmax,nerr,idi logical :: useids icolput = icol if (debugmode) print "(a,i2,a,i2,a,i8)",'DEBUG: reading column ',icol,' type ',itype,' -> '//trim(label(icolput))//', offset ',i0 if (icolput.gt.size(dat(1,:,1)) .or. icolput.eq.0) then print "(a,i2,a)",' ERROR: column = ',icolput,' out of range in receive_data_fromc' return endif nmax = size(dat(:,1,1)) useids = lenvironment('GSPLASH_USE_IDS') .or. lenvironment('GSPLASH_CHECKIDS') if (all(id.le.0) .or. size(iamtype(:,1)).le.1) useids = .false. if (debugmode) print*,'DEBUG: using particle IDs = ',useids,' max = ',nmax if (useids) then nerr = 0 !print*,' id range is ',minval(id),' to ',maxval(id),' type ',itype+1,' column = ',trim(label(icolput)) do i=1,npartoftypei if (id(i).lt.1 .or. id(i).gt.nmax) then idi = id(i) ! !--correct for particle IDs > 1e9 (used to represent recycled particles?) ! if (idi.gt.1000000000) then idi = idi - 1000000000 if (idi.le.nmax .or. idi.le.0) then dat(idi,icolput,1) = real(temparr(i)) iamtype(idi,1) = itype + 1 else nerr = nerr + 1 if (debugmode .and. nerr.le.10) print*,i,'fixed id = ',idi endif else nerr = nerr + 1 if (debugmode .and. nerr.le.10) print*,i,' id = ',idi,idi-1000000000 endif else dat(id(i),icolput,1) = real(temparr(i)) iamtype(id(i),1) = itype + 1 endif enddo if (nerr.gt.0) print*,'ERROR: got particle ids outside array dimensions ',nerr,' times' else if (i0.lt.0) then print*,'ERROR: i0 = ',i0,' but should be positive: SOMETHING IS VERY WRONG...' return elseif (i0+npartoftypei.gt.nmax) then print "(a,i8,a)",' ERROR: offset = ',i0,': read will exceed array dimensions in receive_data_fromc' nmax = nmax - i0 else nmax = npartoftypei endif do i=1,nmax dat(i0+i,icolput,1) = real(temparr(i)) enddo if (size(iamtype(:,1)).gt.1) then do i=1,nmax iamtype(i0+i,1) = itype + 1 enddo endif endif return end subroutine read_gadgethdf5_data_fromc !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass, & ih,irho,ipr,iutherm,iBfirst,idivB,iax use params use settings_data, only:ndim,ndimV,ncolumns,ntypes,UseTypeInRenderings,iformat use geometry, only:labelcoord use system_utils, only:envlist,ienvironment use gadgethdf5read, only:hsoft,blocklabelgas,blocksize,reformatlabel,arepo use asciiutils, only:lcase implicit none integer :: i,j,icol,irank if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif icol = 1 ix = 0 do i=1,size(blocklabelgas) irank = blocksize(i) if (irank.gt.0 .and. (len_trim(blocklabelgas(i)).gt.0)) then select case(blocklabelgas(i)) case('Coordinates') ix(1) = icol ix(2) = icol + 1 if (irank.ge.3) ix(3) = icol + 2 case('Velocities','Velocity') ivx = icol case('SmoothingLength') ih = icol case('Volume') ih = icol arepo = .true. case('Masses','Mass') ipmass = icol case('InternalEnergy') iutherm = icol case('Density') irho = icol case('MagneticField') iBfirst = icol case default label(icol:icol+irank-1) = reformatlabel(blocklabelgas(i)) end select if (irank.eq.ndimV) then iamvec(icol:icol+ndimV-1) = icol labelvec(icol:icol+ndimV-1) = label(icol) do j=1,ndimV label(icol+j-1) = trim(labelvec(icol))//'\d'//labelcoord(j,1) enddo endif icol = icol + irank endif enddo ! !--set labels of the quantities read in ! if (ix(1).gt.0) label(ix(1:ndim)) = labelcoord(1:ndim,1) if (irho.gt.0) label(irho) = 'density' if (iutherm.gt.0) label(iutherm) = 'u' if (ipmass.gt.0) label(ipmass) = 'particle mass' if (ih.gt.0) label(ih) = 'h' ! !--set labels for vector quantities ! if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo endif if (iax.gt.0) then iamvec(iax:iax+ndimV-1) = iax labelvec(iax:iax+ndimV-1) = 'a' do i=1,ndimV label(iax+i-1) = trim(labelvec(iax))//'\d'//labelcoord(i,1) enddo endif if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'\d'//labelcoord(i,1) enddo endif !--set labels for each particle type ! ntypes = 6 labeltype(1) = 'gas' labeltype(2) = 'dark matter' labeltype(3) = 'boundary 1' labeltype(4) = 'boundary 2' labeltype(5) = 'star' labeltype(6) = 'sink / black hole' UseTypeInRenderings(1) = .true. ! !--dark matter particles are of non-SPH type (ie. cannot be used in renderings) ! unless they have had a smoothing length defined ! if (hsoft.gt.tiny(hsoft)) then UseTypeInRenderings(2) = .true. else UseTypeInRenderings(2) = .false. endif UseTypeInRenderings(3:6) = .false. !----------------------------------------------------------- return end subroutine set_labels subroutine set_blocklabel(icol,irank,name) bind(c) use, intrinsic :: iso_c_binding, only:c_int, c_char use gadgethdf5read, only:blocklabelgas,blocksize,fstring implicit none integer(kind=c_int), intent(in) :: icol,irank character(kind=c_char), dimension(256), intent(in) :: name blocklabelgas(icol+1) = fstring(name) blocksize(icol+1) = irank !print*,icol+1,' name = ',trim(blocklabelgas(icol+1)),' x ',irank end subroutine set_blocklabel splash/src/system_unix.f90000644 000766 000000 00000003336 13261626263 016467 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- ! ! this module contains wrappers for all of the ! system and compiler dependent routines ! ! these are called from the main program by their generic names, ! and in here the actual call to the system is performed ! ! THIS ONE IS FOR MANY UNIX COMPILERS ! module system_commands implicit none contains subroutine get_number_arguments(nargs) integer, intent(out) :: nargs integer :: iargc nargs = iargc() end subroutine get_number_arguments subroutine get_argument(iarg,argstring) integer, intent(in) :: iarg character(len=*), intent(out) :: argstring call getarg(iarg,argstring) end subroutine get_argument subroutine get_environment(variable,value) character(len=*), intent(in) :: variable character(len=*), intent(out) :: value call getenv(variable,value) end subroutine get_environment end module system_commands splash/src/read_data_jules.f90000644 000766 000000 00000017626 13261626263 017215 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR JOE'S 2D SPH CODE ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! iam(maxpart,maxstep): integer identification of particle type ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns use mem_allocation implicit none integer, intent(IN) :: indexstart,ipos integer, intent(OUT) :: nstepsread character(LEN=*), intent(IN) :: rootname integer :: i,j,ifile,ierr,npart,nweird,nbnd,nother,n integer :: istep,nprint,npart_max,nstep_max,icol,ncolstep integer :: iambodi,iamskini,imovei logical :: iexist character(LEN=LEN(rootname)+4) :: dumpfile real :: timei,dti,hi,pmass,totmass,rhozero nstepsread = 0 nstep_max = 0 npart_max = maxpart ifile = 1 dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: ',trim(dumpfile),' file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 2 ndimV = 2 ncolstep = 17 ncolumns = 18 ! number of columns in file ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,2) j = indexstart nstepsread = 0 write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='formatted') if (ierr /= 0) then print*,'*** ERROR OPENING ',trim(dumpfile),' ***' else ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,*,end=55,iostat=ierr) timei,istep,n,nprint,hi,dti print*,'time = ',timei,' step = ',istep,' n = ',n,' ng = ',nprint print*,'first time = ',hi,' npart = ',nprint if (.not.allocated(dat) .or. (nprint.gt.npart_max)) then npart_max = max(npart_max,INT(1.1*(nprint))) call alloc(npart_max,nstep_max,ncolumns,mixedtypes=.true.) endif rewind(15) endif if (ierr /= 0) then print*,'*** ERROR READING TIMESTEP HEADER ***' else !oversteps: do ! !--loop over the timesteps in this file ! npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+1,maxcol,mixedtypes=.true.) endif ! !--now read the timestep data in the dumpfile ! read(15,*,end=55,iostat=ierr) timei,istep,n,nprint,hi,dti rhozero = 1000. totmass = 3.*4.8*rhozero pmass = totmass/real(nprint) print*,' assuming total mass = ',totmass,' (rhozero = ',rhozero,')' print*,' gives particle mass = ',pmass nbnd = 0 npart = 0 nother = 0 nweird = 0 do i=1,nprint read(15,*,end=55,iostat=ierr) (dat(i,icol,j),icol = 1,7) read(15,*,end=55,iostat=ierr) (dat(i,icol,j),icol = 8,13) read(15,*,end=55,iostat=ierr) iamtype(i,j),iambodi,iamskini,imovei read(15,*,end=55,iostat=ierr) (dat(i,icol,j),icol = 14,17) select case(iamtype(i,j)) case(0) nbnd = nbnd + 1 iamtype(i,j) = 2 case(1) npart = npart + 1 iamtype(i,j) = 1 case(2) nother = nother + 1 iamtype(i,j) = 3 case default print*,'iamtype = ',iamtype(i,j) nweird = nweird + 1 iamtype(i,j) = 4 end select !print*,i,(dat(i,icol,j),icol = 1,ncolstep),iamtype(i,j) !--make a fake column for mass dat(i,18,j) = pmass enddo time(j) = timei if (ierr /= 0) then print*,'got to ',i,' step ',j print "(a)",'|*** ERROR READING TIMESTEP ***' return else nstepsread = nstepsread + 1 endif npartoftype(:,j) = 0 npartoftype(1,j) = npart npartoftype(2,j) = nbnd npartoftype(3,j) = nother npartoftype(4,j) = nweird if (nweird.gt.0) print*,' WARNING: ',nweird,' particles of unknown type' print*,j,' time = ',time(j) gamma(j) = 1.666666666667 j = j + 1 !enddo oversteps endif 55 continue ! !--reached end of file ! close(15) print*,'nstepsread = ',nstepsread print*,'>> end of dump file: nsteps =',j-1,'nfluid = ',npartoftype(1,j-1),'nbound=',npartoftype(2,j-1) return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo label(ix(1:ndim)) = labelcoord(1:ndim,1) ivx = ndim+1 iamvec(5:6) = 5 labelvec(5:6) = 'f' irho = 7 label(7) = 'density' ipr = 8 label(8) = 'pressure' label(9) = 'vorticity' label(10) = 'pvisc' label(11) = 'div v' label(12) = 'hp' ih = 12 ! smoothing length label(13) = 'concentration' label(14) = 'diffc' label(15) = 'pmix' iamvec(16:17) = 16 labelvec(16:17) = 'vhat' ipmass = 18 ! particle mass label(ipmass) = 'particle mass' iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 4 !!maxparttypes labeltype(1) = 'fluid' labeltype(2) = 'boundary' labeltype(3) = 'other' labeltype(4) = 'unknown' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. UseTypeInRenderings(3) = .true. UseTypeInRenderings(4) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_gadget_hdf5_utils.c000644 000766 000000 00000045061 13261626263 021272 0ustar00dpricewheel000000 000000 /* * This subroutine performs the calls to the HDF5 library for the * GADGET data read * * Easier to do it this way and link with c than to try to link against * the Fortran interface (in the latter case the modules must * have been compiled with the *exact* compiler used to compile splash * which is a real pain). * */ #include #include #include #include static int debug = 0; int checkfordataset(hid_t file_id, char *datasetname); int read_gadgethdf5_dataset(hid_t group_id, char *datasetname, int itype, int maxtypes, int npartoftype[maxtypes], int i0[maxtypes], int ncol, int isrequired[ncol], int *id, int *j); int get_rank(hid_t dataspace_id); int get_rank_by_name(hid_t group_id, char *name); void set_blocklabel(int *icol, int *irank, char *name); void read_gadgethdf5_data_fromc(int *icol, int *npartoftypei, double temparr[*npartoftypei], int id[*npartoftypei], int *itype, int *i0); void get_vel_info(hid_t group_id, char *name, int *ndimV); void get_mass_info(hid_t group_id, char *name, int *rank); void read_gadget_hdf5_header(char *filename, int maxtypes, int *npartoftype[maxtypes], double *massoftype[maxtypes], double *time, double *redshift, int *iFlagSfr, int *iFlagFeedback, int *Nall[maxtypes], int *iFlagCool, int *igotids, int *ndim, int *ndimV, int *nfiles, int *ncol, int *ierr) { hid_t file_id; hid_t group_id, dataset_id; hid_t attrib_id, dataspace_id; herr_t status; herr_t HDF5_error = -1; *ierr = 0; *igotids = 0; if (debug) printf("DEBUG: opening %s \n",filename); file_id = H5Fopen(filename,H5F_ACC_RDONLY,H5P_DEFAULT); if (file_id == HDF5_error) { printf("ERROR opening %s \n",filename); *ierr = 1; return; } /* * Open the "Header" dataset and read the header information * */ if (!checkfordataset(file_id,"Header")) { printf(" ERROR: \"Header\" dataset not found in GADGET HDF5 file\n"); *ierr = 2; return; } #if H5_VERSION_GE(1,8,0) group_id = H5Gopen2(file_id,"Header",H5P_DEFAULT); #else group_id = H5Gopen(file_id,"Header"); #endif if (group_id == HDF5_error) { printf("ERROR opening Header data set \n"); *ierr = 2; return; } int nattrib; int i; char name[256],maindataset[256]; char namevels[256],namemass[256]; nattrib = H5Aget_num_attrs(group_id); /* * Read through all of the attributes in the header, so we * can still spit out the values even if they are not used by SPLASH */ double BoxSize,HubbleParam,Omega0,OmegaLambda; int iFlagStellarAge,iFlagMetals; for(i=0; i < nattrib; i++) { attrib_id = H5Aopen_idx(group_id,i); ssize_t attr_status; attr_status = H5Aget_name(attrib_id, 256, name); hid_t type_id; type_id = H5Aget_type(attrib_id); /*type_class = H5Tget_native_type(type_id,H5T_DIR_ASCEND);*/ if (strcmp(name,"Time")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,time); } else if (strcmp(name,"MassTable")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,massoftype); /*printf(" Masses = %i %f \n",maxtypes,massoftype[1]);*/ } else if (strcmp(name,"NumPart_ThisFile")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,npartoftype); } else if (strcmp(name,"NumPart_Total")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,Nall); } else if (strcmp(name,"Redshift")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,redshift); } else if (strcmp(name,"NumFilesPerSnapshot")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,nfiles); } else if (strcmp(name,"Flag_Sfr")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,iFlagSfr); } else if (strcmp(name,"Flag_Cooling")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,iFlagCool); } else if (strcmp(name,"Flag_Feedback")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,iFlagFeedback); } else if (strcmp(name,"BoxSize")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,&BoxSize); } else if (strcmp(name,"HubbleParam")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,&HubbleParam); } else if (strcmp(name,"Omega0")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,&Omega0); } else if (strcmp(name,"OmegaLambda")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,&OmegaLambda); } else if (strcmp(name,"Flag_StellarAge")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,&iFlagStellarAge); } else if (strcmp(name,"Flag_Metals")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,&iFlagMetals); } else if (strcmp(name,"Time_GYR")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,time); } else { if (debug) printf("DEBUG: unknown attribute %s \n",name); } if (status==HDF5_error) { printf(" ERROR reading attribute %s \n",name); } status = H5Aclose(attrib_id); } status = H5Gclose(group_id); if (status == HDF5_error) { printf("ERROR closing Header data set \n"); *ierr = 3; return; } i = -1; int got = 0; while (!got && i < 5) { i++; sprintf(maindataset,"PartType%i",i); got = checkfordataset(file_id,maindataset); if (!got) { if (i==0) { printf(" WARNING: no gas particles found in GADGET HDF5 file\n"); } else { printf(" WARNING: \"%s\" dataset not found in GADGET HDF5 file\n",maindataset); } } } if (!got) { printf(" ERROR: No PartType dataset found in GADGET HDF5 file\n"); *ierr = 2; return; } if (debug) printf("DEBUG: main dataset= %s \n",maindataset); /* * Now we need to get the number of data columns in the file * (from the number of datasets in the "PartType0" group) */ #if H5_VERSION_GE(1,8,0) group_id = H5Gopen2(file_id,maindataset,H5P_DEFAULT); #else group_id = H5Gopen(file_id,maindataset); #endif if (group_id == HDF5_error) { printf("ERROR opening %s data set \n",maindataset); *ierr = 2; return; } hsize_t ndatasets; status = H5Gget_num_objs(group_id, &ndatasets); if (debug) printf("DEBUG: number of datasets = %i \n",(int)ndatasets); *ncol = 0; *ndim = 0; *ndimV = 0; int rank = 0; int j = 0; rank = get_rank_by_name(group_id,"ParticleIDs"); if (rank == 1) *igotids = 1; if (debug) printf("DEBUG: got IDs = %i\n",*igotids); strcpy(name,"Coordinates"); *ndim = get_rank_by_name(group_id,name); set_blocklabel(&j,ndim,name); *ncol = *ncol + *ndim; if (*ndim > 0) { j++; } else { printf("ERROR: %s dataset not found\n",name); *ierr = 3; return; } get_vel_info(group_id,namevels,ndimV); set_blocklabel(&j,ndimV,"Velocities"); *ncol = *ncol + *ndimV; if (*ndimV > 0) { j++; } else { printf("ERROR: Velocities not found in file\n"); *ierr = 3; return; } get_mass_info(group_id,namemass,&rank); if (rank == 0) { printf(" WARNING: Particle mass array not found in file\n"); } else { set_blocklabel(&j,&rank,"Masses"); *ncol = *ncol + rank; if (rank > 0) j++; } if (*ndim == 0 || *ndimV == 0) { printf("ERROR: got ndim = %i, ndimV = %i\n",*ndim,*ndimV); *ierr = 3; return; } int itype; for(i=0; i < (int)ndatasets; i++) { status = H5Gget_objname_by_idx(group_id, i, name, 256); itype = H5Gget_objtype_by_idx(group_id, i); /*if (debug) printf("DEBUG: checking %s\n",name);*/ /* Should not try to open it if object is not a dataset */ if (itype == H5G_DATASET) { #if H5_VERSION_GE(1,8,0) dataset_id = H5Dopen2(group_id,name,H5P_DEFAULT); #else dataset_id = H5Dopen(group_id,name); #endif dataspace_id = H5Dget_space(dataset_id); rank = get_rank(dataspace_id); if (strcmp(name,"ParticleIDs")&& strcmp(name,"Coordinates")&& strcmp(name,namevels)&& strcmp(name,namemass)) { if (debug) printf("DEBUG: storing %s x %i \n",name,rank); /* Send the dataset names back to Fortran * one by one, so they can be filled into * the array as appropriate */ set_blocklabel(&j,&rank,name); *ncol = *ncol + rank; if (rank > 0) j++; } else { if (debug) printf("DEBUG: ignoring %s \n",name); } status = H5Dclose(dataset_id); } else { if (debug) printf("DEBUG: skipping %s as it is not a dataset\n",name); } } status = H5Gclose(group_id); status = H5Fclose( file_id ); if (status == HDF5_error) { printf("ERROR closing file \n"); *ierr = 7; } if (debug) printf("DEBUG: finished header read \n"); } void read_gadget_hdf5_data(char *filename, int maxtypes, int npartoftype[maxtypes], double massoftype[maxtypes], int ncol, int isrequired[ncol], int i0[maxtypes], int *ierr) { hid_t file_id; hid_t group_id; herr_t status; herr_t HDF5_error = -1; char groupname[12]; char datasetname[256],namevels[256],namemass[256]; int i,ndimV,rank; int *id; if (debug) printf("DEBUG: re-opening %s \n",filename); file_id = H5Fopen(filename,H5F_ACC_RDONLY,H5P_DEFAULT); if (file_id == HDF5_error) { printf("ERROR re-opening %s \n",filename); *ierr = 1; return; } /* read dataset for each particle type present in dump file */ int itype,iobjtype; for (itype=0;itype 0) { /* If npartoftype[N] > 0 in header, look for dataset of the form PartTypeN */ sprintf(groupname,"PartType%i",itype); if (debug) printf("DEBUG: opening group %s\n",groupname); #if H5_VERSION_GE(1,8,0) group_id = H5Gopen2(file_id,groupname,H5P_DEFAULT); #else group_id = H5Gopen(file_id,groupname); #endif if (group_id == HDF5_error) { printf("ERROR opening %s group \n",groupname); *ierr = 2; } else { hsize_t ndatasets; status = H5Gget_num_objs(group_id, &ndatasets); if (debug) printf("DEBUG: number of datasets = %i \n",(int)ndatasets); /* get names of velocity and particle mass datasets */ get_vel_info(group_id,namevels,&ndimV); get_mass_info(group_id,namemass,&rank); /* read particle ID */ int k = 0; id = malloc(npartoftype[itype]*sizeof(int)); *ierr = read_gadgethdf5_dataset(group_id,"ParticleIDs",itype,maxtypes,npartoftype,i0,ncol,isrequired,id,&k); /* set all IDs to zero if not read */ if (*ierr != 0) printf("DEBUG: error from ID read = %i, rank = %i \n",*ierr,k); if (*ierr != 0 || k != 1) { for(k=0;k1) { rank = dims[1]; } else { rank = 1; } return rank; } /* * utility function to get dimensionality of a dataset */ int get_rank_by_name(hid_t group_id, char *name) { if (!checkfordataset(group_id,name)) { return 0; } herr_t HDF5_error = -1; #if H5_VERSION_GE(1,8,0) hid_t dataset_id = H5Dopen2(group_id,name,H5P_DEFAULT); #else hid_t dataset_id = H5Dopen(group_id,name); #endif if (dataset_id == HDF5_error) { printf("ERROR opening %s data set \n",name); return 0; } hid_t dataspace_id = H5Dget_space(dataset_id); int rank = get_rank(dataspace_id); H5Dclose(dataset_id); return rank; } /* * utility function to find velocity dataset and ndimV */ void get_vel_info(hid_t group_id, char *name, int *ndimV) { strcpy(name,"Velocities"); *ndimV = get_rank_by_name(group_id,name); /* If "Velocities" not found, try "Velocity" */ if (*ndimV <= 0) { strcpy(name,"Velocity"); *ndimV = get_rank_by_name(group_id,name); } return; } /* * utility function to find particle mass dataset and rank */ void get_mass_info(hid_t group_id, char *name, int *rank) { strcpy(name,"Masses"); *rank = get_rank_by_name(group_id,name); /* If "Masses" not found, try "Mass" */ if (*rank <= 0) { strcpy(name,"Mass"); *rank = get_rank_by_name(group_id,name); } return; } splash/src/limits.f90000644 000766 000000 00000032255 13261626263 015403 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------- ! ! subroutines to do with setting of plot limits from data ! and using only a subset of the particles according to a ! range in parameters ! !---------------------------------------------------------- module limits use params implicit none real, dimension(maxplot,2) :: lim,range,lim2 private :: warn_minmax public contains !---------------------------------------------------------- ! set plot limits for all columns ! NB: does not differentiate between particle types at the moment !---------------------------------------------------------- subroutine set_limits(ifromstep,itostep,ifromcol,itocol) use labels, only:label,ix use geometry, only:coord_transform_limits use particle_data, only:npartoftype,dat,maxcol use settings_data, only:ndim,icoords,icoordsnew integer, intent(in) :: ifromstep,itostep,ifromcol,itocol integer :: i,j,k,ntoti,itocoli print 100,ifromstep,itostep,ifromcol,itocol 100 format(/' setting plot limits: steps ',i5,'->',i5,' cols ',i2,'->',i3) if (ifromcol.gt.maxcol .or. maxcol.eq.0) then print "(a)",' *** error: set_limits: column > array size ***' return endif if (ifromcol.gt.itocol) then print "(a)",' *** error in call to set_limits: begin column > end column' return endif itocoli = itocol if (itocol.gt.maxcol) then print "(a,i3,a)",' *** warning: set_limits: only setting limits up to column ',maxcol,' ***' itocoli = maxcol endif !!--find limits of particle properties lim(ifromcol:itocol,1) = huge(lim) lim(ifromcol:itocol,2) = -huge(lim) do i=ifromstep,itostep ntoti = sum(npartoftype(:,i)) do j=ifromcol,itocoli do k=1,ntoti lim(j,1) = min(lim(j,1),dat(k,j,i)) lim(j,2) = max(lim(j,2),dat(k,j,i)) enddo enddo enddo ! !--warn if limits are the same ! do j=ifromcol,itocol call warn_minmax(label(j),lim(j,1),lim(j,2)) enddo !print "(a/)",' plot limits set' lim2(ifromcol:itocol,:) = 0. ! !--transform coord limits into new coordinate system if coord transform is applied ! if (icoordsnew.ne.icoords .and. ndim.gt.0) then if (ifromcol.le.ix(ndim)) then ! separate if is to avoid referencing ix(0) if ndim=0 call coord_transform_limits(lim(ix(1):ix(ndim),1),lim(ix(1):ix(ndim),2), & icoords,icoordsnew,ndim) endif endif return end subroutine set_limits !---------------------------------------------------------- ! save plot limits for all columns to a file !---------------------------------------------------------- subroutine write_limits(limitsfile) use settings_data, only:numplot,ndataplots character(len=*), intent(in) :: limitsfile integer :: i print*,'saving plot limits to file ',trim(limitsfile) open(unit=55,file=limitsfile,status='replace',form='formatted',ERR=998) do i=1,numplot if (rangeset(i) .and. i.lt.ndataplots .and. lim2set(i)) then write(55,"(6(1x,1pe14.6))",err=999) lim(i,1),lim(i,2),range(i,1),range(i,2),lim2(i,1),lim2(i,2) elseif (lim2set(i) .and. i.lt.ndataplots) then write(55,"(6(1x,1pe14.6))",err=999) lim(i,1),lim(i,2),0.,0.,lim2(i,1),lim2(i,2) elseif (rangeset(i) .and. i.lt.ndataplots) then write(55,"(4(1x,1pe14.6))",err=999) lim(i,1),lim(i,2),range(i,1),range(i,2) else write(55,"(2(1x,1pe14.6))",err=999) lim(i,1),lim(i,2) endif enddo close(unit=55) return 998 continue print*,'*** error opening limits file: limits not saved' return 999 continue print*,'*** error saving limits' close(unit=55) return end subroutine write_limits !---------------------------------------------------------- ! read plot limits for all columns from a file !---------------------------------------------------------- subroutine read_limits(limitsfile,ierr) use labels, only:label use settings_data, only:numplot,ncolumns,ncalc use asciiutils, only:ncolumnsline character(len=*), intent(in) :: limitsfile integer, intent(out) :: ierr integer :: i,ncolsline character(len=120) :: line logical :: iexist ierr = 0 inquire(file=limitsfile,exist=iexist) if (.not.iexist) then print "(1x,a)",trim(limitsfile)//' not found' ierr = 1 return endif open(unit=54,file=limitsfile,status='old',form='formatted',err=997) print "(a)",' read '//trim(limitsfile) do i=1,numplot read(54,"(a)",err=998,end=999) line ncolsline = ncolumnsline(line) if (ncolsline.lt.2) then goto 998 elseif (ncolsline.ge.6) then read(line,*,err=998,end=999) lim(i,1),lim(i,2),range(i,1),range(i,2),lim2(i,1),lim2(i,2) elseif (ncolsline.ge.4) then read(line,*,err=998,end=999) lim(i,1),lim(i,2),range(i,1),range(i,2) else read(line,*,err=998,end=999) lim(i,1),lim(i,2) endif call assert_sensible_limits(lim(i,1),lim(i,2)) ! !--warn if limits are the same ! call warn_minmax(label(i),lim(i,1),lim(i,2)) enddo close(unit=54) return 997 continue print*,trim(limitsfile),' not found' ierr = 1 return 998 continue call print_rangeinfo() call print_lim2info() print*,'*** error reading limits from file' ierr = 2 close(unit=54) return 999 continue !--only give error if we really do not have enough columns ! (on first call nextra is not set) if (i.le.ncolumns+ncalc) then print "(a,i3)",' end of file in '//trim(limitsfile)//': limits read to column ',i ierr = -1 endif !--print info about range restrictions read from file call print_rangeinfo() call print_lim2info() close(unit=54) return end subroutine read_limits !---------------------------------------------------------- ! get a subset of the particles by enforcing range restrictions !---------------------------------------------------------- subroutine get_particle_subset(icolours,datstep,ncolumns) use labels, only:label integer, intent(inout) :: icolours(:) real, intent(in) :: datstep(:,:) integer, intent(in) :: ncolumns integer :: icol if (anyrangeset()) then !--reset colours of all particles (to not hidden) if using range restriction where (icolours(:).eq.-1000) icolours(:) = 0 elsewhere icolours(:) = abs(icolours(:)) endwhere do icol=1,ncolumns if (rangeset(icol)) then print "(a,1pe10.3,a,1pe10.3,a)",' | using only particles in range ', & range(icol,1),' < '//trim(label(icol))//' < ',range(icol,2),' |' ! !--loop over the particles and colour those outside the range ! NB: background colour (0) is set to -1000 ! where (datstep(:,icol).lt.range(icol,1) .or. & datstep(:,icol).gt.range(icol,2)) where (icolours.eq.0) icolours = -1000 elsewhere icolours = -abs(icolours) end where end where endif enddo endif return end subroutine get_particle_subset !---------------------------------------------------------- ! reset all range restrictions to zero !---------------------------------------------------------- subroutine reset_all_ranges() use particle_data, only:icolourme print "(a)",' removing all range restrictions ' where (icolourme(:).eq.-1000) icolourme(:) = 0 elsewhere icolourme(:) = abs(icolourme(:)) endwhere range(:,:) = 0. return end subroutine reset_all_ranges !---------------------------------------------------------- ! function which returns whether or not a range ! has been set for a given column !---------------------------------------------------------- logical function rangeset(icol) integer, intent(in) :: icol rangeset = .false. if (abs(range(icol,2)-range(icol,1)).gt.tiny(range)) rangeset = .true. return end function rangeset !---------------------------------------------------------- ! function which returns whether or not lim2 ! has been set for a given column !---------------------------------------------------------- logical function lim2set(icol) integer, intent(in) :: icol lim2set = .false. if (abs(lim2(icol,2)).gt.tiny(lim2) .or. abs(lim2(icol,1)).gt.tiny(lim2)) lim2set = .true. return end function lim2set !---------------------------------------------------------- ! reset all range restrictions to zero !---------------------------------------------------------- subroutine reset_lim2(icol) integer, intent(in) :: icol print "(a)",' contour limits same as render limits' if (icol.gt.0 .and. icol.le.maxplot) lim2(icol,:) = 0 return end subroutine reset_lim2 !---------------------------------------------------------- ! function which returns whether or not a range ! has been set for any column !---------------------------------------------------------- logical function anyrangeset() use settings_data, only:ndataplots integer :: i anyrangeset = .false. do i=1,ndataplots if (rangeset(i)) anyrangeset = .true. enddo return end function anyrangeset !---------------------------------------------------------- ! prints info about current range restriction settings !---------------------------------------------------------- subroutine print_rangeinfo() use settings_data, only:ndataplots use labels, only:label integer :: i if (anyrangeset()) then print "(/,a,/)",'>> current range restrictions set: ' do i=1,ndataplots if (rangeset(i)) then print "(a,1pe10.3,a,1pe10.3,a)", & ' ( ',range(i,1),' < '//trim(label(i))//' < ',range(i,2),' )' endif enddo print "(/,2(a,/))",'>> only particles within this range will be plotted ', & ' and/or used in interpolation routines' !else !print "(/,a,/)",'>> no current parameter range restrictions set ' endif end subroutine print_rangeinfo !---------------------------------------------------------- ! prints info about current range restriction settings !---------------------------------------------------------- subroutine print_lim2info() use settings_data, only:ndataplots use labels, only:label integer :: i do i=1,ndataplots if (lim2set(i)) then print "(a,1pe10.3,a,1pe10.3,a)", & ' ( contours use ',lim2(i,1),' < '//trim(label(i))//' < ',lim2(i,2),' )' endif enddo end subroutine print_lim2info !---------------------------------------------------------- ! prints warning if min=max in limits setting !---------------------------------------------------------- subroutine warn_minmax(labelx,xmin,xmax) character(len=*), intent(in) :: labelx real, intent(in) :: xmin,xmax if (abs(xmin-xmax).lt.tiny(xmax)) then print "(a,a20,a,1pe9.2)",' warning: ',labelx,' min = max = ',xmin endif return end subroutine warn_minmax !---------------------------------------------------------- ! Makes sure that variable is within a given range ! If no range specified, ensures that it is within ! the allowed range for the variable type, ! i.e. -0.5*huge(x)->0.5*huge(x) !---------------------------------------------------------- subroutine assert_range(x,min,max) real, intent(inout) :: x real, intent(in), optional :: min,max real :: xmin,xmax xmin = -0.5*huge(xmin) ! for limits need xmax - xmin to xmax = 0.5*huge(xmax) ! be less than huge(x) if (present(min)) xmin = min if (present(max)) xmax = max if (x < xmin) x = xmin if (x > xmax) x = xmax if (x /= x) x = 0. return end subroutine assert_range !---------------------------------------------------------- ! Interface to the above, but checks two numbers at once ! and checks that max > min !---------------------------------------------------------- subroutine assert_sensible_limits(xmin,xmax) real, intent(inout) :: xmin,xmax real :: xtmp call assert_range(xmin) call assert_range(xmax) if (xmax < xmin) then xtmp = xmin xmin = xmax xmax = xtmp endif return end subroutine assert_sensible_limits !---------------------------------------------------------- ! Interface to the above, but checks two numbers at once ! and checks that max > min !---------------------------------------------------------- subroutine fix_equal_limits(xmin,xmax) real, intent(inout) :: xmin,xmax real :: xtmp call assert_sensible_limits(xmin,xmax) if (abs(xmax - xmin) < tiny(xmin)) then xmax = xmax + 1.0 if (xmin.gt.0.) then xmin = max(xmin - 1.0,0.) else xmin = xmin - 1.0 endif endif return end subroutine fix_equal_limits end module limits splash/src/fparser.f90000644 000766 000000 00000143710 13261626263 015543 0ustar00dpricewheel000000 000000 ! ! Copyright (c) 2000-2008, Roland Schmehl. All rights reserved. ! ! This software is distributable under the BSD license. See the terms of the ! BSD license in the documentation provided with this software. ! MODULE fparser !------- -------- --------- --------- --------- --------- --------- --------- ------- ! Fortran 90 function parser v1.1 !------- -------- --------- --------- --------- --------- --------- --------- ------- ! ! This function parser module is intended for applications where a set of mathematical ! fortran-style expressions is specified at runtime and is then evaluated for a large ! number of variable values. This is done by compiling the set of function strings ! into byte code, which is interpreted efficiently for the various variable values. ! ! The source code is available from http://fparser.sourceforge.net ! ! Please send comments, corrections or questions to the author: ! Roland Schmehl ! !------- -------- --------- --------- --------- --------- --------- --------- ------- ! The function parser concept is based on a C++ class library written by Juha ! Nieminen available from http://warp.povusers.org/FunctionParser/ !------- -------- --------- --------- --------- --------- --------- --------- ------- ! ! Modifications by D, Price for integration in SPLASH: ! 7th Aug 2009: added checkf interface routine to check syntax without compiling code ! added endf routine to stop memory leaks, also called from initf if needed ! bug fix with error message for sqrt(-ve) ! ! 9th Aug 2009: added Mathematical constant recognition (pi) ! ! 27th Jan 2010: check for -ve numbers to fractional powers and zero to negative power added ! ! 19th Oct 2016: added Fortran 2008 intrinsic functions ! added optional iErrType argument for error message printing IMPLICIT NONE !--modification here by D.Price: define type parameters here rather than in a separate module integer, parameter, public :: rn = KIND(0.0d0) ! Precision of real numbers integer, parameter, private :: is = SELECTED_INT_KIND(1) ! Data type of bytecode !--end modification !------- -------- --------- --------- --------- --------- --------- --------- ------- PUBLIC :: initf, & ! Initialize function parser for n functions parsef, & ! Parse single function string evalf, & ! Evaluate single function checkf, & ! Check syntax in a function string endf, & ! Clean up memory once finished EvalErrMsg ! Error message (Use only when EvalErrType>0) INTEGER, PUBLIC :: EvalErrType ! =0: no error occured, >0: evaluation error !--modification by D. Price: add parseErr parameter (used in checkf) INTEGER, PRIVATE :: ParseErrType ! =0: no error occured, >0: parse error !--modification by D. Price: add verboseness internal variable (used in checkf) LOGICAL, PRIVATE :: PrintErrors = .true. ! =0: no error occured, >0: parse error !------- -------- --------- --------- --------- --------- --------- --------- ------- PRIVATE SAVE INTEGER(is), PARAMETER :: cImmed = 1, & cNeg = 2, & cAdd = 3, & cSub = 4, & cMul = 5, & cDiv = 6, & cPow = 7, & cAbs = 8, & cExp = 9, & cLog10 = 10, & cLog = 11, & cSqrt = 12, & cSinh = 13, & cCosh = 14, & cTanh = 15, & cSin = 16, & cCos = 17, & cTan = 18, & cAsin = 19, & cAcos = 20, & cAtan = 21, & cBesj0 = 22, & cBesj1 = 23, & cBesy0 = 24, & cBesy1 = 25, & cerfcs = 26, & cerfc = 27, & cerf = 28, & cgamma = 29, & VarBegin = 30 CHARACTER (LEN=1), DIMENSION(cAdd:cPow), PARAMETER :: Ops = (/ '+', & '-', & '*', & '/', & '^' /) CHARACTER (LEN=5), DIMENSION(cAbs:cgamma), PARAMETER :: Funcs = (/'abs ', & 'exp ', & 'log10', & 'log ', & 'sqrt ', & 'sinh ', & 'cosh ', & 'tanh ', & 'sin ', & 'cos ', & 'tan ', & 'asin ', & 'acos ', & 'atan ', & 'besj0', & 'besj1', & 'besy0', & 'besy1', & 'erfcs', & 'erfc ', & 'erf ', & 'gamf ' /) TYPE tComp INTEGER(is), DIMENSION(:), POINTER :: ByteCode INTEGER :: ByteCodeSize REAL(rn), DIMENSION(:), POINTER :: Immed INTEGER :: ImmedSize REAL(rn), DIMENSION(:), POINTER :: Stack INTEGER :: StackSize, & StackPtr END TYPE tComp TYPE (tComp), DIMENSION(:), POINTER :: Comp ! Bytecode INTEGER, DIMENSION(:), ALLOCATABLE :: ipos ! Associates function strings ! CONTAINS ! SUBROUTINE initf (n) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Initialize function parser for n functions !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: n ! Number of functions INTEGER :: i !----- -------- --------- --------- --------- --------- --------- --------- ------- IF (ASSOCIATED(Comp)) THEN print "(a)",' fparser warning: initf called repeatedly without prior call to endf' CALL endf ENDIF ALLOCATE (Comp(n)) DO i=1,n NULLIFY (Comp(i)%ByteCode,Comp(i)%Immed,Comp(i)%Stack) END DO END SUBROUTINE initf ! SUBROUTINE endf() !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Clean up memory at the end of the function parsing/evaluation calls (D. Price) !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER :: i !----- -------- --------- --------- --------- --------- --------- --------- ------- IF (ASSOCIATED(Comp)) THEN DO i=1,size(Comp) IF (ASSOCIATED(Comp(i)%ByteCode)) DEALLOCATE ( Comp(i)%ByteCode, & Comp(i)%Immed, & Comp(i)%Stack ) ENDDO DEALLOCATE(Comp) ENDIF END SUBROUTINE endf ! SUBROUTINE parsef (i, FuncStr, Var, err, Verbose) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Parse ith function string FuncStr and compile it into bytecode !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: i ! Function identifier CHARACTER (LEN=*), INTENT(in) :: FuncStr ! Function string CHARACTER (LEN=*), DIMENSION(:), INTENT(in) :: Var ! Array with variable names CHARACTER (LEN=LEN(FuncStr)) :: Func ! Function string, local use INTEGER, INTENT(OUT), OPTIONAL :: err LOGICAL, INTENT(IN), OPTIONAL :: Verbose ! Turn error messages on/off !----- -------- --------- --------- --------- --------- --------- --------- ------- IF (i < 1 .OR. i > SIZE(Comp)) THEN WRITE(*,*) '*** Parser error: Function number ',i,' out of range' IF (present(err)) err = 1 RETURN END IF EvalErrType = 0 ! D. Price : to prevent accidental misuse ParseErrType = 0 PrintErrors = .true. IF (present(Verbose)) PrintErrors = Verbose ALLOCATE (ipos(LEN(Func))) ! Char. positions in orig. string Func = FuncStr ! Local copy of function string CALL Replace ('**','^ ',Func) ! Exponent into 1-Char. format CALL RemoveSpaces (Func) ! Condense function string !CALL GetConstants (Func) CALL CheckSyntax (Func,FuncStr,Var) DEALLOCATE (ipos) IF (present(err)) err = ParseErrType PrintErrors = .true. ! reset this to true !--D. Price: return after ParseErr here instead of stop inside CheckSyntax IF (ParseErrType /= 0) RETURN CALL Compile (i,Func,Var) ! Compile into bytecode END SUBROUTINE parsef ! INTEGER FUNCTION checkf(FuncStr, Var, Verbose) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Check syntax in a function string (added by D. Price) but do not compile it ! Returns an error code NOT related to ErrMsg ! Optional variable "verbose" determines whether or not error messages are printed !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(IN) :: FuncStr ! Function string CHARACTER (LEN=*), DIMENSION(:), INTENT(IN) :: Var ! Array with variable names LOGICAL, INTENT(IN), OPTIONAL :: Verbose ! Turn error messages on/off CHARACTER (LEN=LEN(FuncStr)) :: Func ! Function string, local use !----- -------- --------- --------- --------- --------- --------- --------- ------- EvalErrType = 0 ! D. Price : to prevent accidental misuse ParseErrType = 0 PrintErrors = .true. IF (present(Verbose)) PrintErrors = Verbose ALLOCATE (ipos(LEN(Func))) ! Char. positions in orig. string Func = FuncStr ! Local copy of function string CALL Replace ('**','^ ',Func) ! Exponent into 1-Char. format CALL RemoveSpaces (Func) ! Condense function string !CALL GetConstants (Func) CALL CheckSyntax (Func,FuncStr,Var) DEALLOCATE (ipos) PrintErrors = .true. ! reset this to true checkf = ParseErrType END FUNCTION checkf ! FUNCTION evalf (i, Val) RESULT (res) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Evaluate bytecode of ith function for the values passed in array Val(:) !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: i ! Function identifier REAL(rn), DIMENSION(:), INTENT(in) :: Val ! Variable values REAL(rn) :: res ! Result INTEGER :: IP, & ! Instruction pointer DP, & ! Data pointer SP ! Stack pointer REAL(rn), PARAMETER :: zero = 0._rn !----- -------- --------- --------- --------- --------- --------- --------- ------- DP = 1 SP = 0 DO IP=1,Comp(i)%ByteCodeSize SELECT CASE (Comp(i)%ByteCode(IP)) CASE (cImmed); SP=SP+1; Comp(i)%Stack(SP)=Comp(i)%Immed(DP); DP=DP+1 CASE (cNeg); Comp(i)%Stack(SP)=-Comp(i)%Stack(SP) CASE (cAdd); Comp(i)%Stack(SP-1)=Comp(i)%Stack(SP-1)+Comp(i)%Stack(SP); SP=SP-1 CASE (cSub); Comp(i)%Stack(SP-1)=Comp(i)%Stack(SP-1)-Comp(i)%Stack(SP); SP=SP-1 CASE (cMul); Comp(i)%Stack(SP-1)=Comp(i)%Stack(SP-1)*Comp(i)%Stack(SP); SP=SP-1 CASE (cDiv); IF (Comp(i)%Stack(SP)==0._rn) THEN; EvalErrType=1; res=zero; RETURN; ENDIF Comp(i)%Stack(SP-1)=Comp(i)%Stack(SP-1)/Comp(i)%Stack(SP); SP=SP-1 ! D. Price: check for zero to negative powers and negative numbers to fractional powers CASE (cPow); IF (Comp(i)%Stack(SP-1)==0._rn .and.Comp(i)%Stack(SP)<0._rn) & THEN; EvalErrType=1; res=zero; RETURN; ENDIF IF (Comp(i)%Stack(SP-1)<=0._rn .and.(Comp(i)%Stack(SP).ne.nint(Comp(i)%Stack(SP)))) & THEN; EvalErrType=5; res=zero; RETURN; ENDIF Comp(i)%Stack(SP-1)=Comp(i)%Stack(SP-1)**Comp(i)%Stack(SP); SP=SP-1 CASE (cAbs); Comp(i)%Stack(SP)=ABS(Comp(i)%Stack(SP)) CASE (cExp); Comp(i)%Stack(SP)=EXP(Comp(i)%Stack(SP)) CASE (cLog10); IF (Comp(i)%Stack(SP)<=0._rn) THEN; EvalErrType=3; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=LOG10(Comp(i)%Stack(SP)) CASE (cLog); IF (Comp(i)%Stack(SP)<=0._rn) THEN; EvalErrType=3; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=LOG(Comp(i)%Stack(SP)) CASE (cSqrt); IF (Comp(i)%Stack(SP)<0._rn) THEN; EvalErrType=2; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=SQRT(Comp(i)%Stack(SP)) CASE (cSinh); Comp(i)%Stack(SP)=SINH(Comp(i)%Stack(SP)) CASE (cCosh); Comp(i)%Stack(SP)=COSH(Comp(i)%Stack(SP)) CASE (cTanh); Comp(i)%Stack(SP)=TANH(Comp(i)%Stack(SP)) CASE (cSin); Comp(i)%Stack(SP)=SIN(Comp(i)%Stack(SP)) CASE (cCos); Comp(i)%Stack(SP)=COS(Comp(i)%Stack(SP)) CASE (cTan); Comp(i)%Stack(SP)=TAN(Comp(i)%Stack(SP)) CASE (cAsin); IF ((Comp(i)%Stack(SP)<-1._rn).OR.(Comp(i)%Stack(SP)>1._rn)) THEN EvalErrType=4; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=ASIN(Comp(i)%Stack(SP)) CASE (cAcos); IF ((Comp(i)%Stack(SP)<-1._rn).OR.(Comp(i)%Stack(SP)>1._rn)) THEN EvalErrType=4; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=ACOS(Comp(i)%Stack(SP)) CASE (cAtan); Comp(i)%Stack(SP)=ATAN(Comp(i)%Stack(SP)) CASE (cBesj0); Comp(i)%Stack(SP)=bessel_j0(Comp(i)%Stack(SP)) CASE (cBesj1); Comp(i)%Stack(SP)=bessel_j1(Comp(i)%Stack(SP)) CASE (cBesy0); IF (Comp(i)%Stack(SP)<=0._rn) THEN; EvalErrType=6; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=bessel_y0(Comp(i)%Stack(SP)) CASE (cBesy1); IF (Comp(i)%Stack(SP)<=0._rn) THEN; EvalErrType=7; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=bessel_y1(Comp(i)%Stack(SP)) CASE (cerf); Comp(i)%Stack(SP)=erf(Comp(i)%Stack(SP)) CASE (cerfc); Comp(i)%Stack(SP)=erfc(Comp(i)%Stack(SP)) CASE (cerfcs); Comp(i)%Stack(SP)=erfc_scaled(Comp(i)%Stack(SP)) CASE (cgamma); IF (Comp(i)%Stack(SP)==-abs(nint(Comp(i)%Stack(SP)))) THEN; EvalErrType=8; res=zero; RETURN; ENDIF Comp(i)%Stack(SP)=gamma(Comp(i)%Stack(SP)) CASE DEFAULT; SP=SP+1; Comp(i)%Stack(SP)=Val(Comp(i)%ByteCode(IP)-VarBegin+1) END SELECT END DO EvalErrType = 0 res = Comp(i)%Stack(1) END FUNCTION evalf ! SUBROUTINE CheckSyntax (Func,FuncStr,Var) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Check syntax of function string, returns 0 if syntax is ok !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: Func ! Function string without spaces CHARACTER (LEN=*), INTENT(in) :: FuncStr ! Original function string CHARACTER (LEN=*), DIMENSION(:), INTENT(in) :: Var ! Array with variable names INTEGER(is) :: n CHARACTER (LEN=1) :: c REAL(rn) :: r LOGICAL :: err INTEGER :: ParCnt, & ! Parenthesis counter j,ib,in,lFunc,inold,ibold !----- -------- --------- --------- --------- --------- --------- --------- ------- j = 1 ParCnt = 0 lFunc = LEN_TRIM(Func) step: DO IF (j > lFunc) THEN CALL ParseErrMsg (j, FuncStr) EXIT ENDIF c = Func(j:j) !-- -------- --------- --------- --------- --------- --------- --------- ------- ! Check for valid operand (must appear) !-- -------- --------- --------- --------- --------- --------- --------- ------- IF (c == '-' .OR. c == '+') THEN ! Check for leading - or + j = j+1 IF (j > lFunc) THEN CALL ParseErrMsg (j, FuncStr, 'Missing operand') EXIT ENDIF c = Func(j:j) IF (ANY(c == Ops)) THEN CALL ParseErrMsg (j, FuncStr, 'Multiple operators') EXIT ENDIF END IF n = MathFunctionIndex (Func(j:)) IF (n > 0) THEN ! Check for math function j = j+LEN_TRIM(Funcs(n)) IF (j > lFunc) THEN CALL ParseErrMsg (j, FuncStr, 'Missing function argument') EXIT ENDIF c = Func(j:j) IF (c /= '(') THEN CALL ParseErrMsg (j, FuncStr, 'Missing opening parenthesis') EXIT ENDIF END IF IF (c == '(') THEN ! Check for opening parenthesis ParCnt = ParCnt+1 j = j+1 CYCLE step END IF IF (SCAN(c,'0123456789.') > 0) THEN ! Check for number r = RealNum (Func(j:),ib,in,err) IF (err) THEN CALL ParseErrMsg (j, FuncStr, 'Invalid number format: '//Func(j+ib-1:j+in-2)) EXIT ENDIF j = j+in-1 IF (j > lFunc) EXIT c = Func(j:j) ELSE ! Check for variable n = VariableIndex (Func(j:),Var,ib,in) IF (n == 0) THEN ! DP: If not a variable, check for constants ibold = ib inold = in r = MathConst (Func(j:),ib,in,err) IF (err) THEN ! Return error if constants not found CALL ParseErrMsg (j, FuncStr, 'Invalid element: '//Func(j+ib-1:j+in-2)) ib = ibold in = inold EXIT ENDIF ENDIF j = j+in-1 IF (j > lFunc) EXIT c = Func(j:j) END IF DO WHILE (c == ')') ! Check for closing parenthesis ParCnt = ParCnt-1 IF (ParCnt < 0) CALL ParseErrMsg (j, FuncStr, 'Mismatched parenthesis') IF (Func(j-1:j-1) == '(') CALL ParseErrMsg (j-1, FuncStr, 'Empty parentheses') j = j+1 IF (j > lFunc) EXIT c = Func(j:j) END DO !-- -------- --------- --------- --------- --------- --------- --------- ------- ! Now, we have a legal operand: A legal operator or end of string must follow !-- -------- --------- --------- --------- --------- --------- --------- ------- IF (j > lFunc) EXIT IF (ANY(c == Ops)) THEN ! Check for multiple operators IF (j+1 > lFunc) CALL ParseErrMsg (j, FuncStr) IF (ANY(Func(j+1:j+1) == Ops)) CALL ParseErrMsg (j+1, FuncStr, 'Multiple operators') ELSE ! Check for next operand CALL ParseErrMsg (j, FuncStr, 'Missing operator') END IF !-- -------- --------- --------- --------- --------- --------- --------- ------- ! Now, we have an operand and an operator: the next loop will check for another ! operand (must appear) !-- -------- --------- --------- --------- --------- --------- --------- ------- j = j+1 END DO step IF (ParCnt > 0) CALL ParseErrMsg (j, FuncStr, 'Missing )') END SUBROUTINE CheckSyntax ! FUNCTION EvalErrMsg ( ierrType ) RESULT (msg) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Return error message !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), DIMENSION(8), PARAMETER :: m = (/ 'Division by zero ', & 'Argument of SQRT negative ', & 'Argument of LOG <= 0 ', & 'Argument of ASIN or ACOS illegal ', & '-ve number to fractional power ', & 'Argument of Bessel_y0 <= 0 ', & 'Argument of Bessel_y1 <= 0 ', & 'Argument of Gamma function illegal '/) CHARACTER (LEN=LEN(m)) :: msg INTEGER, INTENT(in), OPTIONAL :: ierrType !----- -------- --------- --------- --------- --------- --------- --------- ------- IF (present(ierrType)) THEN IF (iErrType < 1 .OR. iErrType > SIZE(m)) THEN msg = '' ELSE msg = m(iErrType) ENDIF ELSE IF (EvalErrType < 1 .OR. EvalErrType > SIZE(m)) THEN msg = '' ELSE msg = m(EvalErrType) ENDIF ENDIF END FUNCTION EvalErrMsg ! SUBROUTINE ParseErrMsg (j, FuncStr, Msg) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Print error message (modification by D.Price: do not terminate program, ! also added option to not print error message) !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: j CHARACTER (LEN=*), INTENT(in) :: FuncStr ! Original function string CHARACTER (LEN=*), OPTIONAL, INTENT(in) :: Msg INTEGER :: k !----- -------- --------- --------- --------- --------- --------- --------- ------- IF (PrintErrors) THEN IF (PRESENT(Msg)) THEN WRITE(*,*) '*** Error in syntax of function string: '//Msg ELSE WRITE(*,*) '*** Error in syntax of function string:' ENDIF WRITE(*,*) WRITE(*,'(A)') ' '//FuncStr IF (ALLOCATED(ipos)) THEN ! Avoid out-of-bounds-errors IF (SIZE(ipos).ge.j) THEN DO k=1,ipos(j) WRITE(*,'(A)',ADVANCE='NO') ' ' ! Advance to the jth position END DO WRITE(*,'(A)') '?' ENDIF ENDIF ENDIF ParseErrType = 1 END SUBROUTINE ParseErrMsg ! FUNCTION OperatorIndex (c) RESULT (n) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Return operator index !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=1), INTENT(in) :: c INTEGER(is) :: n,j !----- -------- --------- --------- --------- --------- --------- --------- ------- n = 0 DO j=cAdd,cPow IF (c == Ops(j)) THEN n = j EXIT END IF END DO END FUNCTION OperatorIndex ! FUNCTION MathFunctionIndex (str) RESULT (n) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Return index of math function beginnig at 1st position of string str !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: str INTEGER(is) :: n,j INTEGER :: k CHARACTER (LEN=LEN(Funcs)) :: fun !----- -------- --------- --------- --------- --------- --------- --------- ------- n = 0 DO j=cAbs,cgamma ! Check all math functions k = MIN(LEN_TRIM(Funcs(j)), LEN(str)) CALL LowCase (str(1:k), fun) IF (fun == Funcs(j)) THEN ! Compare lower case letters n = j ! Found a matching function EXIT END IF END DO END FUNCTION MathFunctionIndex ! FUNCTION VariableIndex (str, Var, ibegin, inext) RESULT (n) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Return index of variable at begin of string str (returns 0 if no variable found) !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: str ! String CHARACTER (LEN=*), DIMENSION(:), INTENT(in) :: Var ! Array with variable names INTEGER(is) :: n,j ! Index of variable INTEGER, OPTIONAL, INTENT(out) :: ibegin, & ! Start position of variable name inext ! Position of character after name INTEGER :: ib,in,lstr !----- -------- --------- --------- --------- --------- --------- --------- ------- n = 0 lstr = LEN_TRIM(str) IF (lstr > 0) THEN DO ib=1,lstr ! Search for first character in str IF (str(ib:ib) /= ' ') EXIT ! When lstr>0 at least 1 char in str END DO DO in=ib,lstr ! Search for name terminators IF (SCAN(str(in:in),'+-*/^) ') > 0) EXIT END DO DO j=1,SIZE(Var,kind=is) IF (str(ib:in-1) == Var(j)) THEN n = j ! Variable name found EXIT END IF END DO !--else below added by D. Price - should never be required though ELSE ! blank string ib = 1 ! to avoid compiler warnings in = 2 ! and any possible seg fault END IF IF (PRESENT(ibegin)) ibegin = ib IF (PRESENT(inext)) inext = in END FUNCTION VariableIndex ! SUBROUTINE RemoveSpaces (str) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Remove Spaces from string, remember positions of characters in old string !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(inout) :: str INTEGER :: k,lstr !----- -------- --------- --------- --------- --------- --------- --------- ------- lstr = LEN_TRIM(str) ipos = 0 do k=1,lstr ipos(k) = k enddo k = 1 DO WHILE (str(k:lstr) /= ' ') IF (str(k:k) == ' ') THEN str(k:lstr) = str(k+1:lstr)//' ' ! Move 1 character to left ipos(k:lstr) = (/ ipos(k+1:lstr), 0 /) ! Move 1 element to left k = k-1 END IF k = k+1 END DO END SUBROUTINE RemoveSpaces ! SUBROUTINE Replace (ca,cb,str) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Replace ALL appearances of character set ca in string str by character set cb !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: ca CHARACTER (LEN=LEN(ca)), INTENT(in) :: cb ! LEN(ca) must be LEN(cb) CHARACTER (LEN=*), INTENT(inout) :: str INTEGER :: j,lca !----- -------- --------- --------- --------- --------- --------- --------- ------- lca = LEN(ca) DO j=1,LEN_TRIM(str)-lca+1 IF (str(j:j+lca-1) == ca) str(j:j+lca-1) = cb END DO END SUBROUTINE Replace ! SUBROUTINE Compile (i, F, Var) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Compile i-th function string F into bytecode !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: i ! Function identifier CHARACTER (LEN=*), INTENT(in) :: F ! Function string CHARACTER (LEN=*), DIMENSION(:), INTENT(in) :: Var ! Array with variable names INTEGER :: istat !----- -------- --------- --------- --------- --------- --------- --------- ------- IF (ASSOCIATED(Comp(i)%ByteCode)) DEALLOCATE ( Comp(i)%ByteCode, & Comp(i)%Immed, & Comp(i)%Stack ) Comp(i)%ByteCodeSize = 0 Comp(i)%ImmedSize = 0 Comp(i)%StackSize = 0 Comp(i)%StackPtr = 0 CALL CompileSubstr (i,F,1,LEN_TRIM(F),Var) ! Compile string to determine size ALLOCATE ( Comp(i)%ByteCode(Comp(i)%ByteCodeSize), & Comp(i)%Immed(Comp(i)%ImmedSize), & Comp(i)%Stack(Comp(i)%StackSize), & STAT = istat ) IF (istat /= 0) THEN WRITE(*,*) '*** Parser error: Memory allocation for byte code failed' STOP ELSE Comp(i)%ByteCodeSize = 0 Comp(i)%ImmedSize = 0 Comp(i)%StackSize = 0 Comp(i)%StackPtr = 0 CALL CompileSubstr (i,F,1,LEN_TRIM(F),Var) ! Compile string into bytecode END IF ! END SUBROUTINE Compile ! SUBROUTINE AddCompiledByte (i, b) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Add compiled byte to bytecode !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: i ! Function identifier INTEGER(is), INTENT(in) :: b ! Value of byte to be added !----- -------- --------- --------- --------- --------- --------- --------- ------- Comp(i)%ByteCodeSize = Comp(i)%ByteCodeSize + 1 IF (ASSOCIATED(Comp(i)%ByteCode)) Comp(i)%ByteCode(Comp(i)%ByteCodeSize) = b END SUBROUTINE AddCompiledByte ! FUNCTION MathItemIndex (i, F, Var) RESULT (n) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Return math item index, if item is real number, enter it into Comp-structure !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: i ! Function identifier CHARACTER (LEN=*), INTENT(in) :: F ! Function substring CHARACTER (LEN=*), DIMENSION(:), INTENT(in) :: Var ! Array with variable names INTEGER(is) :: n ! Byte value of math item !----- -------- --------- --------- --------- --------- --------- --------- ------- n = 0 IF (SCAN(F(1:1),'0123456789.') > 0) THEN ! Check for begin of a number Comp(i)%ImmedSize = Comp(i)%ImmedSize + 1 IF (ASSOCIATED(Comp(i)%Immed)) Comp(i)%Immed(Comp(i)%ImmedSize) = RealNum (F) n = cImmed ELSE ! Check for a variable n = VariableIndex (F, Var) IF (n > 0) THEN n = VarBegin+n-1_is ELSE ! Check for Mathematical constants n = MathConstIndex(i, F) ENDIF END IF END FUNCTION MathItemIndex ! FUNCTION MathConstIndex (i, F, ibegin, inext) RESULT (n) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Routine added by D. Price ! Substitute values for Mathematical Constants (e.g. pi) !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: i ! Function identifier CHARACTER (LEN=*), INTENT(in) :: F ! Function substring INTEGER, OPTIONAL, INTENT(out) :: ibegin, & ! Start position of real number inext ! 1st character after real number INTEGER(is) :: n ! Byte value of math item REAL(rn) :: res LOGICAL :: err INTEGER :: ib,in !----- -------- --------- --------- --------- --------- --------- --------- ------- n = 0 res = MathConst(F,ib,in,err) IF (.not.err) THEN Comp(i)%ImmedSize = Comp(i)%ImmedSize + 1 IF (ASSOCIATED(Comp(i)%Immed)) Comp(i)%Immed(Comp(i)%ImmedSize) = res n = cImmed ELSE ib = 1 in = 1 END IF IF (PRESENT(ibegin)) ibegin = ib IF (PRESENT(inext)) inext = in END FUNCTION MathConstIndex ! FUNCTION CompletelyEnclosed (F, b, e) RESULT (res) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Check if function substring F(b:e) is completely enclosed by a pair of parenthesis !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: F ! Function substring INTEGER, INTENT(in) :: b,e ! First and last pos. of substring LOGICAL :: res INTEGER :: j,k !----- -------- --------- --------- --------- --------- --------- --------- ------- res=.false. IF (F(b:b) == '(' .AND. F(e:e) == ')') THEN k = 0 DO j=b+1,e-1 IF (F(j:j) == '(') THEN k = k+1 ELSEIF (F(j:j) == ')') THEN k = k-1 END IF IF (k < 0) EXIT END DO IF (k == 0) res=.true. ! All opened parenthesis closed END IF END FUNCTION CompletelyEnclosed ! RECURSIVE SUBROUTINE CompileSubstr (i, F, b, e, Var) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Compile i-th function string F into bytecode !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: i ! Function identifier CHARACTER (LEN=*), INTENT(in) :: F ! Function substring INTEGER, INTENT(in) :: b,e ! Begin and end position substring CHARACTER (LEN=*), DIMENSION(:), INTENT(in) :: Var ! Array with variable names INTEGER(is) :: n INTEGER :: b2,j,k,io CHARACTER (LEN=*), PARAMETER :: calpha = 'abcdefghijklmnopqrstuvwxyz'// & 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Check for special cases of substring !----- -------- --------- --------- --------- --------- --------- --------- ------- IF (F(b:b) == '+') THEN ! Case 1: F(b:e) = '+...' ! WRITE(*,*)'1. F(b:e) = "+..."' CALL CompileSubstr (i, F, b+1, e, Var) RETURN ELSEIF (CompletelyEnclosed (F, b, e)) THEN ! Case 2: F(b:e) = '(...)' ! WRITE(*,*)'2. F(b:e) = "(...)"' CALL CompileSubstr (i, F, b+1, e-1, Var) RETURN ELSEIF (SCAN(F(b:b),calpha) > 0) THEN n = MathFunctionIndex (F(b:e)) IF (n > 0) THEN b2 = b+INDEX(F(b:e),'(')-1 IF (CompletelyEnclosed(F, b2, e)) THEN ! Case 3: F(b:e) = 'fcn(...)' ! WRITE(*,*)'3. F(b:e) = "fcn(...)"' CALL CompileSubstr(i, F, b2+1, e-1, Var) CALL AddCompiledByte (i, n) RETURN END IF END IF ELSEIF (F(b:b) == '-') THEN IF (CompletelyEnclosed (F, b+1, e)) THEN ! Case 4: F(b:e) = '-(...)' ! WRITE(*,*)'4. F(b:e) = "-(...)"' CALL CompileSubstr (i, F, b+2, e-1, Var) CALL AddCompiledByte (i, cNeg) RETURN ELSEIF (SCAN(F(b+1:b+1),calpha) > 0) THEN n = MathFunctionIndex (F(b+1:e)) IF (n > 0) THEN b2 = b+INDEX(F(b+1:e),'(') IF (CompletelyEnclosed(F, b2, e)) THEN ! Case 5: F(b:e) = '-fcn(...)' ! WRITE(*,*)'5. F(b:e) = "-fcn(...)"' CALL CompileSubstr(i, F, b2+1, e-1, Var) CALL AddCompiledByte (i, n) CALL AddCompiledByte (i, cNeg) RETURN END IF END IF ENDIF END IF !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Check for operator in substring: check only base level (k=0), exclude expr. in () !----- -------- --------- --------- --------- --------- --------- --------- ------- DO io=cAdd,cPow ! Increasing priority +-*/^ k = 0 DO j=e,b,-1 IF (F(j:j) == ')') THEN k = k+1 ELSEIF (F(j:j) == '(') THEN k = k-1 END IF IF (k == 0 .AND. F(j:j) == Ops(io) .AND. IsBinaryOp (j, F)) THEN IF (ANY(F(j:j) == Ops(cMul:cPow)) .AND. F(b:b) == '-') THEN ! Case 6: F(b:e) = '-...Op...' with Op > - ! WRITE(*,*)'6. F(b:e) = "-...Op..." with Op > -' CALL CompileSubstr (i, F, b+1, e, Var) CALL AddCompiledByte (i, cNeg) RETURN ELSE ! Case 7: F(b:e) = '...BinOp...' ! WRITE(*,*)'7. Binary operator',F(j:j) CALL CompileSubstr (i, F, b, j-1, Var) CALL CompileSubstr (i, F, j+1, e, Var) CALL AddCompiledByte (i, OperatorIndex(Ops(io))) Comp(i)%StackPtr = Comp(i)%StackPtr - 1 RETURN END IF END IF END DO END DO !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Check for remaining items, i.e. variables or explicit numbers !----- -------- --------- --------- --------- --------- --------- --------- ------- b2 = b IF (F(b:b) == '-') b2 = b2+1 n = MathItemIndex(i, F(b2:e), Var) ! WRITE(*,*)'8. AddCompiledByte ',n CALL AddCompiledByte (i, n) Comp(i)%StackPtr = Comp(i)%StackPtr + 1 IF (Comp(i)%StackPtr > Comp(i)%StackSize) Comp(i)%StackSize = Comp(i)%StackSize + 1 IF (b2 > b) CALL AddCompiledByte (i, cNeg) END SUBROUTINE CompileSubstr ! FUNCTION IsBinaryOp (j, F) RESULT (res) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Check if operator F(j:j) in string F is binary operator ! Special cases already covered elsewhere: (that is corrected in v1.1) ! - operator character F(j:j) is first character of string (j=1) !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE INTEGER, INTENT(in) :: j ! Position of Operator CHARACTER (LEN=*), INTENT(in) :: F ! String LOGICAL :: res ! Result INTEGER :: k LOGICAL :: Dflag,Pflag !----- -------- --------- --------- --------- --------- --------- --------- ------- res=.true. IF (F(j:j) == '+' .OR. F(j:j) == '-') THEN ! Plus or minus sign: IF (j == 1) THEN ! - leading unary operator ? res = .false. ELSEIF (SCAN(F(j-1:j-1),'+-*/^(') > 0) THEN ! - other unary operator ? res = .false. ELSEIF (SCAN(F(j+1:j+1),'0123456789') > 0 .AND. & ! - in exponent of real number ? SCAN(F(j-1:j-1),'eEdD') > 0) THEN Dflag=.false.; Pflag=.false. k = j-1 DO WHILE (k > 1) ! step to the left in mantissa k = k-1 IF (SCAN(F(k:k),'0123456789') > 0) THEN Dflag=.true. ELSEIF (F(k:k) == '.') THEN IF (Pflag) THEN EXIT ! * EXIT: 2nd appearance of '.' ELSE Pflag=.true. ! * mark 1st appearance of '.' ENDIF ELSE EXIT ! * all other characters END IF END DO IF (Dflag .AND. (k == 1 .OR. SCAN(F(k:k),'+-*/^(') > 0)) res = .false. END IF END IF END FUNCTION IsBinaryOp ! FUNCTION RealNum (str, ibegin, inext, error) RESULT (res) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Get real number from string - Format: [blanks][+|-][nnn][.nnn][e|E|d|D[+|-]nnn] !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: str ! String REAL(rn) :: res ! Real number INTEGER, OPTIONAL, INTENT(out) :: ibegin, & ! Start position of real number inext ! 1st character after real number LOGICAL, OPTIONAL, INTENT(out) :: error ! Error flag INTEGER :: ib,in,istat LOGICAL :: Bflag, & ! .T. at begin of number in str InMan, & ! .T. in mantissa of number Pflag, & ! .T. after 1st '.' encountered Eflag, & ! .T. at exponent identifier 'eEdD' InExp, & ! .T. in exponent of number DInMan, & ! .T. if at least 1 digit in mant. DInExp, & ! .T. if at least 1 digit in exp. err ! Local error flag !----- -------- --------- --------- --------- --------- --------- --------- ------- Bflag=.true.; InMan=.false.; Pflag=.false.; Eflag=.false.; InExp=.false. DInMan=.false.; DInExp=.false. ib = 1 in = 1 DO WHILE (in <= LEN_TRIM(str)) SELECT CASE (str(in:in)) CASE (' ') ! Only leading blanks permitted ib = ib+1 IF (InMan .OR. Eflag .OR. InExp) EXIT CASE ('+','-') ! Permitted only IF (Bflag) THEN InMan=.true.; Bflag=.false. ! - at beginning of mantissa ELSEIF (Eflag) THEN InExp=.true.; Eflag=.false. ! - at beginning of exponent ELSE EXIT ! - otherwise STOP ENDIF CASE ('0':'9') ! Mark IF (Bflag) THEN InMan=.true.; Bflag=.false. ! - beginning of mantissa ELSEIF (Eflag) THEN InExp=.true.; Eflag=.false. ! - beginning of exponent ENDIF IF (InMan) DInMan=.true. ! Mantissa contains digit IF (InExp) DInExp=.true. ! Exponent contains digit CASE ('.') IF (Bflag) THEN Pflag=.true. ! - mark 1st appearance of '.' InMan=.true.; Bflag=.false. ! mark beginning of mantissa ELSEIF (InMan .AND..NOT.Pflag) THEN Pflag=.true. ! - mark 1st appearance of '.' ELSE EXIT ! - otherwise STOP END IF CASE ('e','E','d','D') ! Permitted only IF (InMan) THEN Eflag=.true.; InMan=.false. ! - following mantissa ELSE EXIT ! - otherwise STOP ENDIF CASE DEFAULT EXIT ! STOP at all other characters END SELECT in = in+1 END DO err = (ib > in-1) .OR. (.NOT.DInMan) .OR. ((Eflag.OR.InExp).AND..NOT.DInExp) IF (err) THEN res = 0.0_rn ELSE READ(str(ib:in-1),*,IOSTAT=istat) res err = istat /= 0 END IF IF (PRESENT(ibegin)) ibegin = ib IF (PRESENT(inext)) inext = in IF (PRESENT(error)) error = err END FUNCTION RealNum ! FUNCTION MathConst (str, ibegin, inext, error) RESULT (res) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Return values of Mathematical constants in string !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: str ! String REAL(rn) :: res ! Real number INTEGER, OPTIONAL, INTENT(out) :: ibegin, & ! Start position of real number inext ! 1st character after real number LOGICAL, OPTIONAL, INTENT(out) :: error ! Error flag INTEGER :: ib,in LOGICAL :: err ! Local error flag !----- -------- --------- --------- --------- --------- --------- --------- ------- ib = 1 in = 1 err = .false. IF (len(str) < 2) THEN res = 0.0_rn err = .true. ELSE IF (str(1:2)=='pi') THEN res = 3.14159265358979323846_rn in = 3 ELSE res = 0.0_rn err = .true. ENDIF ENDIF IF (PRESENT(ibegin)) ibegin = ib IF (PRESENT(inext)) inext = in IF (PRESENT(error)) error = err END FUNCTION MathConst ! SUBROUTINE LowCase (str1, str2) !----- -------- --------- --------- --------- --------- --------- --------- ------- ! Transform upper case letters in str1 into lower case letters, result is str2 !----- -------- --------- --------- --------- --------- --------- --------- ------- IMPLICIT NONE CHARACTER (LEN=*), INTENT(in) :: str1 CHARACTER (LEN=*), INTENT(out) :: str2 INTEGER :: j,k CHARACTER (LEN=*), PARAMETER :: lc = 'abcdefghijklmnopqrstuvwxyz' CHARACTER (LEN=*), PARAMETER :: uc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' !----- -------- --------- --------- --------- --------- --------- --------- ------- str2 = str1 DO j=1,LEN_TRIM(str1) k = INDEX(uc,str1(j:j)) IF (k > 0) str2(j:j) = lc(k:k) END DO END SUBROUTINE LowCase ! END MODULE fparser splash/src/read_data_falcON_hdf5_utils.cc000644 000766 000000 00000061232 13261626263 021342 0ustar00dpricewheel000000 000000 // -*- C++ -*- //////////////////////////////////////////////////////////////////////////////// /// /// \file read_falcON_utils.cc /// /// \brief towards reading falcON HDF5 snapshots into splash /// \date 12-Aug-2015 // // Copyright (C) 2015 Walter Dehnen. // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // PART 1: interfaces with C-linkage // this part may be used like a header file // //////////////////////////////////////////////////////////////////////////////// extern "C" { /// maximum number of particles types expected /// (actually falcON currently only supports 3 particle types) static constexpr int max_num_types = 6; // // 1.1 routines to be provided by SPLASH and called from PART 2 below // /// /// transfer data to splash /// /// \param[in] icol column of data /// \param[in] ndat number of data == number of particles of type /// \param[in] data array of ndat data /// \param[in] type index of particle type /// /// \note called by read_falcON_snapshot() void read_falcON_data_into_splash(const int*icol, const int*ndat, const double*data, const int*type); /// /// tell splash about the label of a data column /// /// \param[in] icol column of data /// \param[in] name label for this column /// /// \note called by read_falcON_snapshot() void set_splash_block_label(const int*icol, const char*name); /// /// tell splash about the label of a particle type /// /// \param[in] ityp column of data /// \param[in] name label for this column /// /// \note called by read_falcON_snapshot() void set_splash_particle_label(const int*ityp, const char*name); // // 1.2 routines provided here, but to be called from SPLASH // /// /// set debugging level /// /// \param[in] debug debugging level /// /// \note currently only debug=0,1,2,3 are distinguished. /// 0 means no output in case of an error, /// 1 means diagnostic output in case of error, /// 2 also prints errors from the HDF5 library /// 3 may print extra information (used in debugging this file) void set_falcON_debugging_level(const int*debug); /// /// opens a falcON HDF5 snapshot file /// /// \param[in] filename name of data file /// \param[out] ierr non-zero if file couldn't be opened /// /// \note precondition: none /// \note postcondition: open_falcON_snapshot() can be called /// \note We close any previously opened file (only one snapshot file can be /// open at any time with this implementation), but warn if the /// filename matches with the currently open file, if any. /// \note Use num_falcON_snapshots() for the number of snapshots in the file void open_falcON_file(const char*filename, int*ierr); /// /// queries whether a file is open /// int falcON_file_is_open(); /// /// closes the currently open (if any) falcON HDF5 snapshot file. /// /// \note precondition: none /// \note postcondition: falcON_file_is_open() will return false void close_falcON_file(); /// /// queries if there is another snapshot present the currently open file /// /// \note precondition: falcON_file_is_open() int have_falcON_snapshot(int*ierr); /// /// query the number of snapshots in the currently open file /// /// \note precondition: falcON_file_is_open() /// \note When using have_falcON_snapshot(), it may not be necessary to call /// this function, see main() in PART 3 below for an example. /// \note This function is trivial for falcON HDF5-based snapshot files, /// but non-trivial for NEMO snapshot files, which we may support in /// the future without changing this interface. int num_falcON_snapshots(int*ierr); /// /// opens the next falcON snapshot in the currently open file and /// reads its header /// /// \param[out] ntype number of particle types /// \param[out] npart number of particles per type /// \param[out] ncol number of splash columns /// \param[out] dimX number of spatial dimensions /// \param[out] dimV number of velocity dimensions /// \param[out] time simulation time of snapshot /// \param[out] hper if hper[d]!=0, dimension d is periodic with |x|= 5 // // The HDF5 library may have been compiled with another (older) GNU C++ ABI // than the one used in the rest of the falcON.2 code. Here, we must use the // same C++ ABI as used with the HDF5 C++ library. This is controlled by the // following macro, see also // // https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html // # define _GLIBCXX_USE_CXX11_ABI 0 #endif #if __cplusplus < 201103L # error requiring C++11 #endif #include #include #include #include #include #include #include #include /// /// 2.1 auxiliary functionality /// (symbols from the anonymous namespace have internal linkage /// namespace { /// simple struct with data for each set of particles struct Particles { std::unique_ptr group; ///< HDF5 group std::string name; ///< name: 'sink', 'gas', 'std' std::size_t number; ///< number of particles // (I couldn't figure out to avoid this via aggregate initialisation) Particles(H5::Group*g, std::string const&t, std::size_t n) : group(g), name(t), number(n) {} }; // using name_count_map = std::map; using name_and_count = name_count_map::value_type; // static data int Debug = 0; ///< debug level (0 or not 0) int IndexSnap = 0; ///< index of next snapshot int NumSnap = 0; ///< number of snapshots in file int SplashCol = 0; ///< column for splash data int NumCol = 0; ///< number of splash columns name_count_map Fields; ///< field:dims map of all fields std::vector Part; ///< data for particle sets std::vector Buffer; ///< data buffer for input std::unique_ptr File; ///< file std::string FileName; ///< name of currently open file const hsize_t k[1] = {3}; const H5::ArrayType VectorType ///< HDF5 data type for double[3] {H5::PredType::NATIVE_DOUBLE,1,k}; // closes a splash column inline void close_column(const char*name) { if(SplashCol >= NumCol) throw "number of columns exceeds expected " + std::to_string(NumCol); set_splash_block_label(&SplashCol,name); SplashCol++; } // closes a splash column inline void close_column(std::string const&name) { close_column(name.c_str()); } // closes currently open snapshot inline void close_snapshot() { SplashCol = 0; NumCol = 0; Part.clear(); Fields.clear(); } // select field for this particle type? // NOTE Without selecting, we would potentially read meaningless data. This // will not be necessary in future versions of falcON. inline bool select(std::string const&field, std::string const&ptype) { return (field=="spin" || field=="eabs" || field=="maxA")? (ptype=="sink") : (field=="snum" || field=="uin" || field=="entr" || field=="dlKt" || field=="dlKe" || field=="srho" || field=="alfa" || field=="divv" || field=="dlht" || field=="vsig" || field=="fact" || field=="csnd" || field=="pres" || field=="vort" || field=="dtdv" || field=="qmin" || field=="delE" || field=="coll")? (ptype=="gas") : (field=="krnH" || field=="maxR") ? (ptype=="sink" || ptype=="gas") : true; } // try to read a component of a falcON field into a splash column void read_column(std::string const&field, const hsize_t comp, const hsize_t dims) { bool read = false; // loop particle types for(int type=0; type!=int(Part.size()); ++type) { const auto&part = Part[std::size_t(type)]; if(!select(field,part.name)) continue; H5::DataSet data; try { data = part.group->openDataSet(field); } catch(...) { continue; // field not present: continue with next particle type } // obtain size of data set auto space = data.getSpace(); hsize_t count[2]; auto rank = space.getSimpleExtentDims(count); assert(part.number==count[0]); const int ndat = int(part.number); // read component Buffer.resize(part.number); switch(dims) { case 1: // scalar field assert(rank==1); data.read(Buffer.data(),H5::PredType::NATIVE_DOUBLE,space,space); // reduce smoothing length by factor 2 if(field=="krnH" && part.name=="gas") for(auto&x:Buffer) x*=0.5; if(Debug>2) std::clog<<"reading "<2) std::clog<<"reading "<(std::toupper(field[0])); name+= comp==0? "xx": comp==1? "xy": comp==2? "xz": comp==3? "yy": comp==4? "yz": "zz"; } else name=field; close_column(name); } } // read a field inline void read_field(name_and_count const&field) { for(hsize_t comp=0; comp!=field.second; ++comp) read_column(field.first, comp, field.second); } // try to read a falcON field inline void read_field(std::string const&field) { const auto field_iter = Fields.find(field); if(field_iter==Fields.end()) std::clog<<"WARNING: falcON field '"<openAttribute("falcON"); } catch(...) { if(Debug) std::clog<<"open_falcON_file(): file '"<openAttribute("num_snapshots"); attr.read(H5::PredType::NATIVE_UINT32,&NumSnap); } catch(H5::Exception const&exc) { // should never happen if(Debug) std::clog<<"open_falcON_file('"<= NumSnap) { if(Debug) std::clog<<"open_falcON_snapshot(): no more than " <openGroup(name); // read time and hper auto attr = snap.openAttribute("time"); attr.read(H5::PredType::NATIVE_DOUBLE,time); attr = snap.openAttribute("hper"); attr.read(VectorType,hper); // read npart[] and open particle sets std::array types = {{"sink","gas","std"}}; for(const auto&type:types) { name = "N"; name+= type; unsigned number; try { attr = snap.openAttribute(name); attr.read(H5::PredType::NATIVE_UINT32,&number); } catch(...) { // should never go here number = 0; } if(number) { // open HDF5 group for particles const auto part = snap.openGroup(type); Part.emplace_back(new H5::Group(part),type,number); npart[(*ntyp)++] = int(number); // collect (field:dims) pairs and count columns for(hsize_t fld=0; fld!=part.getNumObjs(); ++fld) { const auto field = part.getObjnameByIdx(fld); const auto have_field = Fields.count(field); if(have_field) { // field 'krnH' generates an extra column for each particle type if(field=="krnH") (*ncol)++; } else { hsize_t count[2]; const auto dims = 1==part.openDataSet(field).getSpace(). getSimpleExtentDims(count)? 1:count[1]; Fields[field] = dims; (*ncol)+= int(dims); } if(Debug>2) std::clog<<"counting columns: type="<" <<(*ncol)< PreferredOrder = {"pos", "key", "vel", "acc", "mass", "pot", "pex", "rung", "krnH", "srho", "uin", "entr", "divv", "dlKt", "dlKe", "alfa"}; try { auto FieldsToRead=Fields; // read some fields in preferred order for(const auto&field:PreferredOrder) if(FieldsToRead.erase(field)) read_field(field); // read remaining fields for(const auto&field:FieldsToRead) read_field(field); } catch(H5::Exception const&exc) { // catch any HDF5 error if(Debug) std::clog<<"read_falcON_snapshot(): HDF5 error: \"" < #include #include // // 3.1 implement interface 1.1 using C++ // namespace { struct data_per_particle_type { std::string name; std::size_t number; std::vector< std::vector > columns; }; std::vector particle_data; std::vector column_labels; } // void set_splash_block_label(const int*icol, const char*name) { column_labels.at(std::size_t(*icol)) = name; } // void set_splash_particle_label(const int*type, const char*name) { particle_data.at(std::size_t(*type)).name = name; } // void read_falcON_data_into_splash(const int*icol, const int*ndat, const double*from, const int*type) { auto&part = particle_data.at(std::size_t(*type)); assert(*ndat > 0); assert(part.number == std::size_t(*ndat)); part.columns.at(std::size_t(*icol)).resize(std::size_t(*ndat)); std::copy(from,from+*ndat,part.columns[std::size_t(*icol)].begin()); } // // 3.2 an executable that dumps a falcON file // int main(int argc, const char**argv) { // obtain program parameters if(argc<2) { std::clog<<"usage: '"<= 0.0) & .or. (abs(2.0*f) > abs(dxold*df))) then dxold = dx dx = 0.5*(xh-xl) rts = xl+dx if (abs(xl-rts) < tiny(rts)) then rtsafe = rts return endif else dxold = dx dx = f/df temp = rts rts = rts - dx if (abs(temp-rts) < tiny(rts)) then rtsafe = rts return endif endif if (abs(dx) < xacc) then rtsafe = rts return endif call func(q, L, rts, f, df, xll) if (f < 0.0) then xl = rts else xh = rts endif enddo rtsafe = 0. return end function rtsafe real function left_limit(q, L) real, intent(in) :: q,L left_limit = rtsafe(rlimit,q,L,-0.5*L,-L,0., roche_accuracy); end function left_limit real function right_limit(q, L) real, intent(in) :: q,L right_limit = rtsafe(rlimit,q,L,1.5-0.5*L,2.0-L,0., roche_accuracy); end function right_limit ! ! return roche radius as fraction of the semi-major axis. ! So to obtain the true roche_radius call: ! real Rl = semi_major_axis * roche_radius(m1, m2); ! Eggleton PP., ApJ, 1983, 268, 368. ! real function roche_radius(mthis, mother) real, intent(in) :: mthis,mother real :: mr,q1_3,q2_3 mr = mthis/mother q1_3 = mr**(1./3.) q2_3 = q1_3**2 roche_radius = 0.49*q2_3/(0.6*q2_3 + log(1 + q1_3)) end function roche_radius real function first_Lagrangian_point(qinv) real, intent(in) :: qinv real :: fL, dfL, dL, L, q11 q11 = 1./(1.+qinv) L = 0.5 + 0.2222222*log10(qinv) dL = 1.e7 do while (abs(dL)>1.e-6) fL = qinv/L**2- 1./(1.-L)**2 - (1.+qinv)*L + 1. dfL=-2*qinv/L**3 - 2./(1.-L)**3 - (1.+qinv) dL = -fL/(dfL*L) L = L*(1.+dL) enddo first_Lagrangian_point = L end function first_Lagrangian_point subroutine rline(q, L, y, f, df, xl) real, intent(in) :: q, L, y, xl real, intent(out) :: f, df real :: xsq,onexsq,qi,q11,cnst,cnst2,r1,r2 xsq=xl*xl onexsq=(1.-xl)**2 qi=q q11=1./(1.+qi) cnst =qi/L+1./(1.-L) + 0.5*(1.+qi)*(L-q11)**2 cnst2=0.5*(1.+qi)*(xl-q11)**2 - cnst r1=sqrt(xsq+y) r2=sqrt(onexsq+y) f=qi/r1+1./r2+cnst2 df =-0.5*qi/r1**3 - 0.5/r2**3 end subroutine rline subroutine compute_lobes(q, L, npts, xplot, yplot) real, intent(in) :: q, L integer, intent(in) :: npts real, intent(out), dimension(2*npts+1) :: xplot,yplot real :: qi,q11,cnst,lrl,rrl,y1,y2,ysq,dxl,dxr integer :: i qi = 1/q q11 = 1./(1.+qi) cnst = qi/L+1./(1.-L) + 0.5*(1.+qi)*(L-q11)**2 lrl = left_limit(q, L) xplot(1) = lrl yplot(1) = 0. xplot(npts+1) = L yplot(npts+1) = 0. rrl = right_limit(q, L) xplot(2*npts) = rrl yplot(2*npts) = 0. y1 = 0. y2 = L*L !--left lobe dxl = (xplot(npts+1)-xplot(1))/real(npts) do i=1,npts xplot(i+1) = xplot(1) + i*dxl ysq = rtsafe(rline,qi,L,y1,y2,xplot(i+1),roche_accuracy) yplot(i+1) = sqrt(ysq) enddo !--right lobe dxr = (xplot(2*npts)-xplot(npts+1))/real(npts) do i=1,npts xplot(npts+i+1) = xplot(npts+1) + i*dxr ysq = rtsafe(rline,qi,L,y1,y2,xplot(npts+i+1),roche_accuracy) yplot(npts+i+1) = sqrt(ysq) enddo end subroutine compute_lobes end module rochelobe splash/src/system_f2003.f90000644 000766 000000 00000003375 13261626263 016241 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- ! ! this module contains wrappers for all of the ! system and compiler dependent routines ! ! these are called from the main program by their generic names, ! and in here the actual call to the system is performed ! ! THIS ONE IS FOR FORTRAN 2003 COMPILERS ! module system_commands implicit none contains subroutine get_number_arguments(nargs) integer, intent(out) :: nargs nargs = COMMAND_ARGUMENT_COUNT() end subroutine get_number_arguments subroutine get_argument(iarg,argstring) integer, intent(in) :: iarg character(len=*), intent(out) :: argstring call GET_COMMAND_ARGUMENT(iarg,argstring) end subroutine get_argument subroutine get_environment(variable,value) character(len=*), intent(in) :: variable character(len=*), intent(out) :: value call GET_ENVIRONMENT_VARIABLE(variable,value) end subroutine get_environment end module system_commands splash/src/read_data_dansph_old.f90000644 000766 000000 00000037425 13261626263 020205 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! the data is stored in the global array dat ! ! THIS VERSION FOR DAN'S SPMHD CODE (BINARY DUMPS) ! PRE NOV 2005 FORMAT ! -> Now automatically handles single/double precision ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use exact, only:hfact use particle_data use params use labels use filenames, only:nfiles use settings_data, only:ndim,ndimV,ncolumns,ncalc,icoords,iformat, & buffer_data use mem_allocation use geometry, only:labelcoordsys implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+4) :: datfile integer :: i,j,icol,ierr,iunit integer :: ncol_max,ndim_max,npart_max,ndimV_max,nstep_max integer :: npartin,ntotin,ncolstep,nparti,ntoti integer, dimension(3) :: ibound logical :: reallocate, singleprecision real :: timein, gammain, hfactin real, dimension(3) :: xmin, xmax real(doub_prec) :: timeind,gammaind,hfactind real(doub_prec), dimension(3) :: xmind, xmaxd iunit = 11 ! file unit number ndim_max = 1 ndimV_max = 1 nstepsread = 0 if (rootname(1:1).ne.' ') then datfile = trim(rootname) !print*,'rootname = ',rootname else print*,' **** no data read **** ' return endif print "(1x,a)",'reading old ndspmhd format' write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! open(unit=iunit,iostat=ierr,file=datfile,status='old',form='unformatted') if (ierr /= 0) then print*,'*** Error opening '//trim(datfile)//' ***' return endif ! !--read first header line ! singleprecision = .false. read(iunit,iostat=ierr,end=80) timeind,npartin,ntotin,gammaind, & hfactind,ndim_max,ndimV_max,ncol_max,icoords !!print*,'time = ',timeind,' hfact = ',hfactind,' ndim=',ndim_max,'ncol=',ncol_max !!print*,'npart = ',npartin,ntotin if (ierr /= 0 .or. ndim_max.le.0 .or. ndim_max.gt.3 & .or. ndimV_max.le.0 .or. ndimV_max.gt.3 & .or. ncol_max.le.0 .or. ncol_max.gt.100 & .or. npartin.le.0 .or. npartin.gt.1e7 .or. ntotin.le.0 .or. ntotin.gt.1e7 & .or. icoords.le.0 .or. icoords.gt.10) then ! !--try single precision ! rewind(iunit) read(iunit,iostat=ierr,end=80) timein,npartin,ntotin,gammain, & hfactin,ndim_max,ndimV_max,ncol_max,icoords singleprecision = .true. if (ierr /= 0) then print "(a)",'*** Error reading first header ***' print*,' time = ',timein,' hfact = ',hfactin,' ndim=',ndim_max,'ncol=',ncol_max close(iunit) return endif endif ! !--allocate memory for data arrays ! if (buffer_data) then nstep_max = max(nfiles,maxstep,indexstart) else nstep_max = max(1,maxstep,indexstart) endif npart_max = max(int(1.5*ntotin),maxpart) if (.not.allocated(dat) .or. ntotin.gt.maxpart & .or. nstep_max.gt.maxstep .or. ncol_max.gt.maxcol) then call alloc(npart_max,nstep_max,ncol_max+ncalc) endif ! !--rewind file ! rewind(iunit) i = indexstart nstepsread = 0 overstepsinfile: do while (i <= maxstep) !!print*,' reading step ',i reallocate = .false. npart_max = maxpart nstep_max = maxstep ! !--read header line for this timestep ! if (singleprecision) then print "(a)",'single precision dump' read(iunit,iostat=ierr,end=67) timein,nparti,ntoti,gammain, & hfactin,ndim,ndimV,ncolstep,icoords,iformat,ibound(1:ndim), & xmin(1:ndim),xmax(1:ndim) else print "(a)",'double precision dump' read(iunit,iostat=ierr,end=67) timeind,nparti,ntoti,gammaind, & hfactind,ndim,ndimV,ncolstep,icoords,iformat,ibound(1:ndim), & xmind(1:ndim),xmaxd(1:ndim) timein = real(timeind) gammain = real(gammaind) hfactin = real(hfactind) xmin = real(xmind) xmax = real(xmaxd) endif if (ierr /= 0) then print*,'*** error reading timestep header ***' close(iunit) return else ! count this as a successfully read timestep, even if data is partial nstepsread = nstepsread + 1 endif time(i) = timein gamma(i) = gammain hfact = hfactin npartoftype(1,i) = nparti npartoftype(2,i) = ntoti - nparti print "(/a14,':',f8.4,a8,':',i8,a8,':',i8)",' time',time(i),'npart',nparti,'ntotal',ntoti print "(a14,':',i8,a8,':',f8.4,a8,':',f8.4)",' ncolumns',ncolstep,'gamma',gamma(i),'hfact',hfact print "(a14,':',i8,a8,':',i8)",'ndim',ndim,'ndimV',ndimV if (icoords.gt.1) print "(a14,':',2x,a)",' geometry',labelcoordsys(icoords) if (any(ibound(1:ndim).ne.0)) then print "(a14,':',a15,' =',3(f8.4))",'boundaries','xmin',xmin(1:ndim) print "(15x,a15,' =',3(f8.4))",'xmax',xmax(1:ndim) endif ! !--check for errors in timestep header ! if (ndim.gt.3 .or. ndimV.gt.3) then print*,'*** error in header: ndim or ndimV in file> 3' nstepsread = nstepsread - 1 ndim = ndim_max ndimV = ndimV_max close(iunit) return endif if (ndim.gt.ndim_max) ndim_max = ndim if (ndimV.gt.ndimV_max) ndimV_max = ndimV if (ncolstep.ne.ncol_max) then print*,'*** Warning number of columns not equal for timesteps' ncolumns = ncolstep print*,'ncolumns = ',ncolumns,ncol_max if (ncolumns.gt.ncol_max) ncol_max = ncolumns endif if (ncolstep.gt.maxcol) then reallocate = .true. ncolumns = ncolstep ncol_max = ncolumns else ncolumns = ncolstep endif if (ntoti.gt.maxpart) then !print*, 'ntot greater than array limits!!' reallocate = .true. npart_max = int(1.5*ntoti) endif if (i.gt.maxstep) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate) then call alloc(npart_max,nstep_max,ncol_max+ncalc) endif if (ntoti.gt.0) then ! !--read position vector ! icol = 1 call readvec(dat(1:ntoti,1:ndim,i),ntoti,ndim,singleprecision,ierr) if (ierr /= 0) then print "(a)",'*** error reading particle positions ***' exit overstepsinfile endif icol = icol + ndim ! !--read velocity vector ! call readvec(dat(1:ntoti,icol:icol+ndimV-1,i),ntoti,ndimV,singleprecision,ierr) if (ierr /= 0) then print "(a)",'*** error reading velocities ***' exit overstepsinfile endif icol = icol + ndimV ! !--read scalar variables ! do j=1,4 call readcol(dat(1:ntoti,icol,i),ntoti,singleprecision,ierr) if (ierr /= 0) print "(a)",'*** error reading column data ***' icol = icol + 1 enddo ! !--non-MHD output ! if (iformat.ne.2) then ! !--read alpha, alphau ! call readvec(dat(1:ntoti,icol:icol+1,i),ntoti,2,singleprecision,ierr) if (ierr /= 0) then print "(a)",'*** error reading alphas ***' exit overstepsinfile endif icol = icol + 2 ! !--pr, div v, gradh ! do j=icol,ncolstep call readcol(dat(1:ntoti,j,i),ntoti,singleprecision,ierr) if (ierr /= 0) print "(a)",'*** error reading column data ***' enddo else ! !--MHD output ! ! !--read alpha, alphau, alphaB ! call readvec(dat(1:ntoti,icol:icol+2,i),ntoti,3,singleprecision,ierr) if (ierr /= 0) then print "(a)",'*** error reading alphas ***' exit overstepsinfile endif icol = icol + 3 ! !--Bfield ! call readvec(dat(1:ntoti,icol:icol+ndimV-1,i),ntoti,ndimV,singleprecision,ierr) if (ierr /= 0) then print "(a)",'*** error reading B ***' exit overstepsinfile endif icol = icol + ndimV ! !--psi, pr, div v, div B ! do j = 1,4 call readcol(dat(1:ntoti,icol,i),ntoti,singleprecision,ierr) if (ierr /= 0) print "(a)",'*** error reading column data ***' icol = icol + 1 enddo ! !--curl B ! call readvec(dat(1:ntoti,icol:icol+ndimV-1,i),ntoti,ndimV,singleprecision,ierr) if (ierr /= 0) then print "(a)",'*** error reading curl B ***' exit overstepsinfile endif icol = icol + ndimV endif !!print*,'columns read = ',icol,' should be = ',ncolumns else npartoftype(1,i) = 1 npartoftype(2,i) = 0 dat(:,:,i) = 0. endif i = i + 1 enddo overstepsinfile 67 continue !!!print "(a)",' > end of file <' ! !--close data file and return ! close(unit=11) ncolumns = ncol_max ndim = ndim_max ndimV = ndimV_max print*,'> Read steps ',indexstart,'->',indexstart + nstepsread - 1, & ' last step ntot = ',sum(npartoftype(:,indexstart+nstepsread-1)) return ! !--errors ! 80 continue print*,' *** data file empty, no steps read ***' return contains subroutine readvec(datin,ntotal,ndims,singleprec,ierr) implicit none integer, intent(in) :: ndims,ntotal integer, intent(out) :: ierr real, intent(out), dimension(ntotal,ndims) :: datin logical, intent(in) :: singleprec integer :: ipos real, dimension(ndims,ntotal) :: datvec real(doub_prec), dimension(ndims,ntotal) :: datvecd ! !--read a vector quantity and restructure into columns ! if (singleprec) then read (iunit,iostat=ierr) datvec(1:ndims,1:ntotal) do ipos = 1,ndims datin(1:ntotal,ipos) = datvec(ipos,1:ntotal) enddo else read (iunit,iostat=ierr) datvecd(1:ndims,1:ntotal) do ipos = 1,ndims datin(1:ntotal,ipos) = real(datvecd(ipos,1:ntotal)) enddo endif end subroutine readvec ! !--read scalar quantity and convert to single precision ! subroutine readcol(datin,ntotal,singleprec,ierr) implicit none integer, intent(in) :: ntotal integer, intent(out) :: ierr real, intent(out), dimension(ntotal) :: datin logical, intent(in) :: singleprec real(doub_prec), dimension(ntotal) :: dattempd ! !--read several scalar quantities ! if (singleprec) then read (iunit,iostat=ierr) datin(1:ntotal) else read (iunit,iostat=ierr) dattempd(1:ntotal) datin(1:ntotal) = real(dattempd(1:ntotal)) endif end subroutine readcol end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data, only:ndim,ndimV,ncolumns,iformat,ntypes, & UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = ndim + 1 ih = ndim + ndimV + 1 ! smoothing length irho = ndim + ndimV + 2 ! location of rho in data array iutherm = ndim + ndimV + 3 ! thermal energy ipmass = ndim + ndimV + 4 ! particle mass label(ix(1:ndim)) = labelcoord(1:ndim,1) ! !--label vector quantities (e.g. velocity) appropriately ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx+i-1))//'\d'//labelcoord(i,1) enddo label(irho) = '\gr' label(iutherm) = 'u' label(ih) = 'h ' label(ipmass) = 'particle mass' label(ndim + ndimV+5) = '\ga' label(ndim + ndimV+6) = '\ga\du' if (iformat.eq.2) then ! !--mag field (vector) ! label(ndim + ndimV+7) = '\ga\dB' iBfirst = ndim + ndimV+7+1 ! location of Bx iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'\d'//labelcoord(i,1) !' (x10\u-3\d)' !//'/rho' enddo ! !--more scalars ! label(ndim+2*ndimV+8) = 'psi' ipr = ndim + 2*ndimV + 9 ! pressure label(ipr) = 'P' label(ndim+2*ndimV+10) = 'div v' idivB = ndim+2*ndimV+11 label(idivB) = 'div B' ! !--current density (vector) ! iJfirst = ndim+2*ndimV+11+1 iamvec(iJfirst:iJfirst+ndimV-1) = iJfirst labelvec(iJfirst:iJfirst+ndimV-1) = 'J' do i=1,ndimV label(iJfirst+i-1) = trim(labelvec(iJfirst))//labelcoord(i,1) enddo else ipr = ndim + ndimV + 7 ! pressure label(ipr) = 'P' label(ndim+ndimV+8) = 'grad h' label(ndim+ndimV+9) = 'grad soft' label(ndim+ndimV+10) = 'phi' label(ndim+ndimV+11) = 'f_grav' ! label(ndim+ndimV+8) = 'div v' ! label(ndim+ndimV+9) = 'grad h' if (iformat.eq.3) then !!!irho = ndim+ndimV+9 label(ndim+ndimV+9) = 'rho*' label(ndim+ndimV+10) = 'sqrt g' iamvec(ndim+ndimV+11:ndim+ndimV+10+ndimV) = ndim+ndimV+11 labelvec(ndim+ndimV+11:ndim+ndimV+10+ndimV) = 'pmom' do i=1,ndimV label(ndim+ndimV+10+i) = labelvec(ndim+ndimV+11)//labelcoord(i,1) enddo endif iBfirst = 0 endif if (ncolumns.gt.ndim+3*ndimV+11) then label(ndim+3*ndimV+12) = 'f_visc_x' label(ndim+3*ndimV+13) = 'f_visc_y' label(ndim+3*ndimV+14) = 'f_x' label(ndim+3*ndimV+15) = 'f_y' endif ! !--these are here for backwards compatibility -- could be removed ! if (ncolumns.gt.ndim+3*ndimV+7) then ! label(ndim + 3*ndimV+8) = 'v_parallel' ! label(ndim + 3*ndimV+9) = 'v_perp' ! label(ndim + 3*ndimV+10) = 'B_parallel' ! label(ndim + 3*ndimV+11) = 'B_perp' ! endif ! !--set labels for each type of particles ! ntypes = 2 labeltype(1) = 'gas' labeltype(2) = 'ghost' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_seren.f90000644 000766 000000 00000156515 13261626263 017210 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR OUTPUT FROM THE SEREN CODE ! HANDLES BOTH ASCII AND BINARY FILES ! ! THE FOLLOWING ENVIRONMENT VARIABLES AFFECT THIS FORMAT: ! ! DSPLASH_EXTRACOLS : set to number of extra columns to read (after itype) ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Partial data read implemented means that columns with ! the 'required' flag set to false are not read (read is therefore much faster) !------------------------------------------------------------------------- module seren_data_store implicit none integer :: seren_maxparttypes ! Number of types we are using character(len=20) :: format_id ! File format (for verification) integer :: nunits ! Number of units integer :: ndata ! Number of data entries integer :: ptot, stot ! Number of particles/sinks integer :: pboundary, picm, pgas ! Number of each type of particle integer :: pcdm, pdust, pion ! Number of each type of particle integer :: PR, NDIMtemp, VDIMtemp, BDIMtemp ! Important parameters integer :: dmdt_range ! DMDT_RANGE character(len=20) :: data_id(1:500) ! Char ids of arrays written character(len=20) :: unit_data(1:500) ! Unit data real :: unit_coeff(1:500) ! Unit multiplier integer :: typedata(1:5,1:500) ! type data header array integer :: itemp, iporig ! Since SPLASH does not have one integer :: iunknown(1:1000) ! For unknown data types character(len=20) :: r_unit ! length unit character(len=20) :: m_unit ! mass unit character(len=20) :: rho_unit ! density unit character(len=20) :: h_unit ! smoothing length unit integer, parameter :: DP = selected_real_kind(p=15) ! double precision integer, parameter :: SP = selected_real_kind(p=6) ! single precision integer, parameter :: ILP = selected_int_kind(r=15) ! Integer long precision ! Length units in metres real(kind=DP),parameter :: r_pc = 3.08568E16_DP ! parsec real(kind=DP),parameter :: r_au = 1.49597870E11_DP ! astronomical unit real(kind=DP),parameter :: r_sun = 6.96E8_DP ! solar radius real(kind=DP),parameter :: r_earth = 6.371E6_DP ! Earth radius ! Mass units in kilograms real(kind=DP),parameter :: m_sun = 1.98892E30_DP ! solar mass real(kind=DP),parameter :: m_jup = 1.8986E27_DP ! Jupiter mass real(kind=DP),parameter :: m_earth = 5.9736E24_DP ! Earth mass ! Time units in seconds real(kind=DP),parameter :: myr = 3.1556952E13_DP ! megayear real(kind=DP),parameter :: yr = 3.1556952E7_DP ! year real(kind=DP),parameter :: day = 8.64E4_DP ! day end module seren_data_store subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,iamtype,npartoftype,time,gamma,maxpart,maxcol,maxstep use params use settings_data, only:ndim,ndimV,ncolumns,ncalc,ipartialread,ntypes use settings_units, only:unitzintegration, unit_interp use mem_allocation, only:alloc use labels, only:labeltype,labelzintegration use system_utils, only:ienvironment use seren_data_store implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile integer, parameter :: iunit = 16 integer :: i,step,ierr,iambinaryfile,itype integer :: npart_max,nstep_max logical :: iexist,reallocate,doubleprec character(len=50) :: string integer :: idata(1:50) integer (kind=ILP) :: ilpdata(1:50) real :: rdata(1:50) real(doub_prec) :: rdata_dp(1:50) real(doub_prec) :: dpdata(1:50) real :: timetemp,gammatemp unit_coeff = 1. ! not yet used m_unit = "" rho_unit = "" h_unit = "" !iRescale = .TRUE. ipartialread = .false. ! we always read full data file seren_maxparttypes = min(maxparttypes,7) nstepsread = 0 if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(datfile)//': file not found ***' return endif ! !--read data from snapshots ! step = istepstart write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! ! !--determine whether file is binary or ascii, open it and read the header ! ! Try binary first, then ascii open(unit=iunit,file=datfile,status='old',form='unformatted',iostat=ierr) if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(datfile)//' ***' return endif ! !--read the file header ! try binary format first, and if unsuccessful try ascii ! read (unit=iunit,iostat=ierr) format_id if (ierr /= 0 .OR. trim(adjustl(format_id)) /= "SERENBINARYDUMPV2") then ! Ascii format iambinaryfile = 0 close (unit=iunit) open(unit=iunit,file=datfile,status='old',form='formatted',iostat=ierr) if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(datfile)//' AS ASCII ***' return endif rewind(unit=iunit) read (unit=iunit,fmt=*,iostat=ierr) format_id if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(datfile)//' - UNKNOWN FILE FORMAT '//format_id//' ***' return endif if (trim(adjustl(format_id)) /= "SERENASCIIDUMPV2") then print "(a)",'*** ERROR OPENING '//trim(datfile)//' AS ASCII - WRONG FILE FORMAT ***' return end if else iambinaryfile = 1 end if if (iambinaryfile==1) then print "(a)",' reading binary seren v2 format ' read (iunit) PR read (iunit) NDIMtemp read (iunit) VDIMtemp read (iunit) BDIMtemp else print "(a)",' reading ascii seren v2 format ' read (iunit,*) PR read (iunit,*) NDIMtemp read (iunit,*) VDIMtemp read (iunit,*) BDIMtemp end if if (iambinaryfile==0) then ! Don't care about precision doubleprec = .FALSE. else if (PR == 8 .OR. PR == 2) then ! Double precision file print "(a)",' Double precision file' doubleprec = .TRUE. else if (PR == 4 .OR. PR == 1) then ! Single precision file print "(a)",' Single precision file' doubleprec = .FALSE. else print "(a)",'*** WARNING OPENING '//trim(datfile)//' - ASSUMING SINGLE PRECISION ***' doubleprec = .FALSE. end if typedata = 0 if (iambinaryfile.eq.1) then call read_serenheader_binary(iunit) else if (iambinaryfile.eq.0) then call read_serenheader_ascii(iunit) end if ! !--get values of quantities from the header ! ndim = NDIMtemp ndimv = VDIMtemp ptot = idata(1) stot = idata(2) pboundary = idata(3) picm = idata(4) pgas = idata(5) pcdm = idata(6) pdust = idata(7) pion = idata(8) dmdt_range = idata(30) !--check for errors in integer header (either from corrupt file or wrong endian) if (ptot+stot.le.0 .or. ptot+stot.gt.1.e10) then if (iambinaryfile.eq.1) then print "(a)",' ERROR reading binary file header: wrong endian? ' else print "(a)",' ERROR reading ascii file header ' endif close(unit=iunit) return endif timetemp = real(dpdata(1)) gammatemp = 1. ! Not saved in file print*,'time : ',timetemp print*,'gamma : ',gammatemp print*,'n_total : ',ptot call set_labels ! !--if successfully read header, increment the nstepsread counter ! nstepsread = nstepsread + 1 ! !-- now work out dimensionless weight unit and z integration unit ! call find_weights(unit_interp,unitzintegration,labelzintegration) ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if ((ptot+stot).gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*(ptot+stot)) else ! if first time, save on memory npart_max = int(ptot+stot) endif endif if (step.ge.maxstep .and. step.ne.1) then nstep_max = step + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol),mixedtypes=.true.) endif ! !--copy header into header arrays ! npartoftype(:,step) = 0 ! npartoftype(1,step) = ptot time(step) = timetemp gamma(step) = gammatemp ! !--read particle data ! if (ptot.gt.0) then ! if (iambinaryfile.eq.1) then ! call read_dragonbody_binary(iunit,ierr) ! else ! call read_dragonbody_ascii(iunit,ierr) ! endif call read_serenbody(iunit,ierr) else ptot = 0 ! npartoftype(1,step) = 0 ! npartoftype(:,step) = 0 dat(:,:,step) = 0. endif ! if (allocated(iamtype)) then ! !--relabel particle types call set_types(iamtype(:,step),ptot+stot,npartoftype(:,step)) ! endif if (any(npartoftype(2:,step).ne.0)) then do itype=1,ntypes if (npartoftype(itype,step).gt.0) then string = ' ' write(string,"(a)") 'n_'//trim(labeltype(itype)) write(string(18:len(string)),"(a)") ':' print*,trim(string),' ',npartoftype(itype,step) endif enddo endif ! ! ! !--set flag to indicate that only part of this file has been read ! ! ! if (.not.all(required(1:ncolumns))) ipartialread = .true. ! !--close data file and return ! close(unit=iunit) return contains !---------------------------------------------------- ! binary header read !---------------------------------------------------- subroutine read_serenheader_binary(iunitb) implicit none integer, intent(in) :: iunitb read (iunitb,end=55) idata read (iunitb,end=55) ilpdata if (doubleprec) then read (iunitb,end=55) rdata_dp else read (iunitb,end=55) rdata endif read (iunitb,end=55) dpdata ndata = idata(21) nunits = idata(20) if (nunits > 0) read (iunitb) unit_data(1:nunits) if (ndata > 0) read (iunitb) data_id(1:ndata) if (ndata > 0) read (iunitb) typedata(1:5,1:ndata) return 55 continue print "(a)",' ERROR: end of file in binary header read' stop !return end subroutine read_serenheader_binary !---------------------------------------------------- ! ascii header read !---------------------------------------------------- subroutine read_serenheader_ascii(iunita) implicit none integer, intent(in) :: iunita do i=1,size(idata) read (iunita,*,end=55) idata(i) end do do i=1,size(ilpdata) read (iunita,*,end=55) ilpdata(i) end do do i=1,size(rdata) read (iunita,*,end=55) rdata(i) end do do i=1,size(dpdata) read (iunita,*,end=55) dpdata(i) end do ndata = idata(21) nunits = idata(20) do i=1,nunits read (iunita,'(A)') unit_data(i) end do do i=1,ndata read (iunita,'(A)') data_id(i) end do do i=1,ndata read (iunita,*) typedata(1:5,i) end do return 55 continue print "(a)",' ERROR: end of file in ascii header read' stop !return end subroutine read_serenheader_ascii !---------------------------------------------------- ! body read !---------------------------------------------------- subroutine read_serenbody(iunit,ierr_out) use seren_data_store use labels, only:ix,ivx,ipmass,ih,irho,iBfirst,iutherm implicit none integer, intent(in) :: iunit integer, intent(out) :: ierr_out integer :: ierr, ierr1 integer :: j, k integer, dimension(:), allocatable :: dummy_int integer, dimension(:,:), allocatable :: dummy_int_2D real(kind=SP), dimension(:,:), allocatable :: dummy real(kind=DP), dimension(:,:), allocatable :: dummy_dp real(kind=SP), dimension(:), allocatable :: dummy_scalar real(kind=DP), dimension(:), allocatable :: dummy_dp_scalar integer(kind=ILP), dimension(:), allocatable :: dummy_ilp_int logical, dimension(:), allocatable :: dummy_logical integer(kind=ILP), dimension(:,:), allocatable :: dummy_ilp_int_2D logical, dimension(:,:), allocatable :: dummy_logical_2D logical :: ldummy(1:2) integer :: idummy2(1:2) real(kind=SP), allocatable :: raux(:) real(kind=DP), allocatable :: raux_dp(:) real, allocatable :: sink_dat_array(:,:,:) integer :: s integer :: unknown integer :: pfirst integer :: plast integer :: width integer :: typecode integer :: unit_id integer :: sink_data_length character(len=30) :: sink_format_string character(len=50) :: format_string integer :: nl,ni,nli,npr,ndp,nchar logical, allocatable :: l_data_st(:) integer, allocatable :: i_data_st(:) integer(kind=ILP), allocatable :: ilp_data_st(:) real(kind=SP), allocatable :: sp_data_st(:) real(kind=DP), allocatable :: dp_data_st(:) real(kind=DP), allocatable :: dp_data_st2(:) unknown = 0 if (stot>0) then sink_data_length = 11+NDIMtemp+VDIMtemp+2*dmdt_range allocate(raux(1:sink_data_length)) if (doubleprec) allocate(raux_dp(1:sink_data_length)) allocate(sink_dat_array(1:stot,1:ncolumns,1)) sink_dat_array = 0. write (sink_format_string,'(A,I0,A)') "(", sink_data_length, "E18.10)" end if ierr_out = 0 allocate(dummy_int(1:ptot),stat=ierr) if (ierr /= 0) then print *,' ERROR allocating memory' goto 56 endif if (doubleprec) allocate(dummy_dp_scalar(1:ptot),stat=ierr) if (ierr /= 0) then print *,' ERROR allocating memory' goto 56 endif allocate(dummy_scalar(1:ptot),stat=ierr) if (ierr /= 0) then print *,' ERROR allocating memory' goto 56 endif write (6,'(A,A)',ADVANCE="NO") "Loading: " do i=1,ndata if (i==1) then write (6,'(A)',ADVANCE="NO") trim(data_id(i)) else write (6,'(A,A)',ADVANCE="NO") ", ", trim(data_id(i)) end if select case (trim(data_id(i))) case ("porig") ! Original particle number ! Read through porig numbers pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) dummy_int(pfirst:plast) else do k=pfirst,plast read(iunit,fmt=*,end=55,iostat=ierr1) dummy_int(k) if (ierr1 /= 0) ierr = ierr1 end do end if if (ierr /= 0) then print*,' WARNING: errors reading through porig ' ierr_out = -1 end if do k=pfirst,plast dat(k,iporig,step) = real(dummy_int(k)) enddo if (ierr /= 0) then print*,' WARNING: errors reading unknown data type ', trim(data_id(i)) ierr_out = -1 end if case ("r") ! Position if (doubleprec) allocate(dummy_dp(1:NDIMtemp,1:ptot),stat=ierr) allocate(dummy(1:NDIMtemp,1:ptot),stat=ierr) pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp(1:NDIMtemp,pfirst:plast) dummy(1:NDIMtemp,pfirst:plast) = real(dummy_dp(1:NDIMtemp,pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy(1:NDIMtemp,pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy(1:NDIMtemp,k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,ix(1):ix(1)+NDIMtemp-1,step) = dummy(1:NDIMtemp,k) enddo if (ierr /= 0) then print*,' WARNING: errors reading positions ' ierr_out = -1 end if deallocate(dummy) if (doubleprec) deallocate(dummy_dp) case ("h") ! Smoothing lengths pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp_scalar(pfirst:plast) dummy_scalar(pfirst:plast) = real(dummy_dp_scalar(pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy_scalar(pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_scalar(k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,ih,step) = dummy_scalar(k) enddo if (ierr /= 0) then print*,' WARNING: errors reading smoothing lengths' ierr_out = -1 end if case ("m") ! Mass pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp_scalar(pfirst:plast) dummy_scalar(pfirst:plast) = real(dummy_dp_scalar(pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy_scalar(pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_scalar(k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,ipmass,step) = dummy_scalar(k) enddo if (ierr /= 0) then print*,' WARNING: errors reading masses' ierr_out = -1 end if case ("v") ! Velocities if (doubleprec) allocate(dummy_dp(1:VDIMtemp,1:ptot),stat=ierr) allocate(dummy(1:VDIMtemp,1:ptot),stat=ierr) pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp(1:VDIMtemp,pfirst:plast) dummy(1:VDIMtemp,pfirst:plast) = real(dummy_dp(1:VDIMtemp,pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy(1:VDIMtemp,pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy(1:VDIMtemp,k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,ivx:ivx+VDIMtemp-1,step) = dummy(1:VDIMtemp,k) enddo if (ierr /= 0) then print*,' WARNING: errors reading velocities ' ierr_out = -1 end if deallocate(dummy) if (doubleprec) deallocate(dummy_dp) case ("rho") ! Densities pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp_scalar(pfirst:plast) dummy_scalar(pfirst:plast) = real(dummy_dp_scalar(pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy_scalar(pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_scalar(k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,irho,step) = dummy_scalar(k) enddo if (ierr /= 0) then print*,' WARNING: errors reading densities' ierr_out = -1 end if case ("temp") ! Temperatures pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp_scalar(pfirst:plast) dummy_scalar(pfirst:plast) = real(dummy_dp_scalar(pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy_scalar(pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_scalar(k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,itemp,step) = dummy_scalar(k) enddo if (ierr /= 0) then print*,' WARNING: errors reading temperatures' ierr_out = -1 end if case ("u") ! Internal energy pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp_scalar(pfirst:plast) dummy_scalar(pfirst:plast) = real(dummy_dp_scalar(pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy_scalar(pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_scalar(k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,iutherm,step) = dummy_scalar(k) enddo if (ierr /= 0) then print*,' WARNING: errors reading internal energy' ierr_out = -1 end if case ("B") ! Magnetic fields if (doubleprec) allocate(dummy_dp(1:BDIMtemp,1:ptot),stat=ierr) allocate(dummy(1:BDIMtemp,1:ptot),stat=ierr) pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp(1:BDIMtemp,pfirst:plast) dummy(1:BDIMtemp,pfirst:plast) = real(dummy_dp(1:BDIMtemp,pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy(1:BDIMtemp,pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy(1:BDIMtemp,k) if (ierr1 /= 0) ierr = ierr1 end do end if do k=pfirst,plast dat(k,iBfirst:iBfirst+BDIMtemp-1,step) = dummy(1:BDIMtemp,k) enddo if (ierr /= 0) then print*,' WARNING: errors reading magnetic fields' ierr_out = -1 end if deallocate(dummy) if (doubleprec) deallocate(dummy_dp) case ("sink_v1") ! Load sinks in sink data storage, will add them later pfirst = typedata(2,i); plast = typedata(3,i) if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) nl,ni,nli,npr,ndp,nchar else read(iunit,fmt=*,end=55,iostat=ierr) nl,ni,nli,npr,ndp,nchar end if do s=pfirst,plast if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) ldummy read(iunit,end=55,iostat=ierr) idummy2 if (doubleprec) then read(iunit,end=55,iostat=ierr) raux_dp raux = real(raux_dp) else read(iunit,end=55,iostat=ierr) raux end if else read(iunit,'(2L1)',end=55,iostat=ierr) ldummy read(iunit,fmt=*,end=55,iostat=ierr) idummy2 read(iunit,sink_format_string) raux(1:sink_data_length) end if if (ix(1)/=0) sink_dat_array(s,ix(1):ix(NDIMtemp),1) = raux(2:NDIMtemp+1) if (ivx/=0) sink_dat_array(s,ivx:ivx+VDIMtemp-1,1) = raux(NDIMtemp+2:NDIMtemp+VDIMtemp+1) if (ipmass/=0) sink_dat_array(s,ipmass,1) = raux(NDIMtemp+VDIMtemp+2) if (ih/=0) sink_dat_array(s,ih,1) = raux(NDIMtemp+VDIMtemp+3) if (itemp/=0) sink_dat_array(s,itemp,1) = raux(NDIMtemp+VDIMtemp+11) end do case default !print*,' WARNING: unknown data type ', trim(data_id(i)) ! Assume this is an unknown data type !ierr_out = -4 width = typedata(1,i) pfirst = typedata(2,i); plast = typedata(3,i) typecode = typedata(4,i); unit_id = typedata(5,i) unknown = unknown + 1 if (typecode == 7) then ! Special data structure we don't understand; read and skip if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) nl,ni,nli,npr,ndp,nchar else read(iunit,fmt=*,end=55,iostat=ierr) nl,ni,nli,npr,ndp,nchar end if if (nchar > 0) stop "Fail! character data :(" if (nl > 0) allocate(l_data_st(1:nl)) if (ni > 0) allocate(i_data_st(1:ni)) if (nli > 0) allocate(ilp_data_st(1:nli)) if (npr > 0) then allocate(sp_data_st(1:npr)) if (doubleprec) allocate(dp_data_st(1:npr)) end if if (ndp > 0) allocate(dp_data_st2(1:ndp)) if (iambinaryfile==1) then do j=pfirst, plast if (nl > 0) read(iunit,end=55,iostat=ierr) l_data_st if (ni > 0) read(iunit,end=55,iostat=ierr) i_data_st if (nli > 0) read(iunit,end=55,iostat=ierr) ilp_data_st if (npr > 0) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dp_data_st else read(iunit,end=55,iostat=ierr) sp_data_st end if end if if (ndp > 0) read(iunit,end=55,iostat=ierr) dp_data_st2 end do else do j=pfirst, plast if (nl > 0) then write (format_string,'(A,I0,A)') "(",nl,"L1)" read(iunit,end=55,iostat=ierr,fmt=format_string) l_data_st end if if (ni > 0) then read(iunit,end=55,iostat=ierr,fmt=*) i_data_st end if if (nli > 0) then read(iunit,end=55,iostat=ierr,fmt=*) ilp_data_st end if if (npr > 0) then if (doubleprec) then read(iunit,end=55,iostat=ierr,fmt=*) dp_data_st else read(iunit,end=55,iostat=ierr,fmt=*) sp_data_st end if end if if (ndp > 0) then read(iunit,end=55,iostat=ierr,fmt=*) dp_data_st2 end if end do end if if (ierr /= 0) then print*,' WARNING: errors reading unknown data structure ', trim(data_id(i)) ierr_out = -1 end if if (allocated(i_data_st)) deallocate(i_data_st) if (allocated(ilp_data_st)) deallocate(ilp_data_st) if (allocated(sp_data_st)) deallocate(sp_data_st) if (allocated(dp_data_st)) deallocate(dp_data_st) if (allocated(dp_data_st2)) deallocate(dp_data_st2) else if (typecode == 6) then ! Character data; read and skip stop "Fail! character data :(" ! I have realised this was a silly idea ! If we work out how this should work, I can put it in else if (typecode >= 1 .AND. typecode <= 5) then ! 1 = logical, 2 = integer, 3 = long integer, 4 = PR, 5 = DP ! Normal data set; either scalar or vector if (width == 1) then ! Scalar data if (typecode==1) then ! Logical data array allocate(dummy_logical(1:ptot)) dummy_logical = .FALSE. if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) dummy_logical(pfirst:plast) else do k=pfirst,plast read(iunit,'(L1)',end=55,iostat=ierr1) dummy_logical(k) if (ierr1 /= 0) ierr = ierr1 end do end if where (dummy_logical) dummy_scalar=1.d0 elsewhere dummy_scalar=0.d0 end where deallocate(dummy_logical) else if (typecode==2) then ! Integer data array dummy_int = 0 if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) dummy_int(pfirst:plast) else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_int(k) if (ierr1 /= 0) ierr = ierr1 end do end if dummy_scalar(pfirst:plast) = real(dummy_int(pfirst:plast)) else if (typecode==3) then ! Long integer data array allocate(dummy_ilp_int(1:ptot)) dummy_ilp_int = 0 if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) dummy_ilp_int(pfirst:plast) else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_ilp_int(k) if (ierr1 /= 0) ierr = ierr1 end do end if dummy_scalar(pfirst:plast) = real(dummy_ilp_int(pfirst:plast)) deallocate(dummy_ilp_int) else if (typecode==4) then ! PR data array if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp_scalar(pfirst:plast) dummy_scalar(pfirst:plast) = real(dummy_dp_scalar(pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy_scalar(pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_scalar(k) if (ierr1 /= 0) ierr = ierr1 end do end if else if (typecode==5) then ! DP data array if (iambinaryfile==1) then if (.NOT.doubleprec) allocate(dummy_dp_scalar(pfirst:plast)) read(iunit,end=55,iostat=ierr) dummy_dp_scalar(pfirst:plast) dummy_scalar(pfirst:plast) = real(dummy_dp_scalar(pfirst:plast)) if (.NOT.doubleprec) deallocate(dummy_dp_scalar) else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_scalar(k) if (ierr1 /= 0) ierr = ierr1 end do end if end if do k=pfirst,plast dat(k,iunknown(unknown),step) = dummy_scalar(k) enddo if (ierr /= 0) then print*,' WARNING: errors reading unknown data type ', trim(data_id(i)) ierr_out = -1 end if else ! Vector data allocate(dummy(1:width,1:ptot),stat=ierr) if (typecode == 1) then ! Logical data array allocate(dummy_logical_2D(1:width,1:ptot)) dummy_logical_2D = .FALSE. if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) dummy_logical_2D(1:width,pfirst:plast) else write (format_string,'(A,I0,A)') "(",width,"L1)" do k=pfirst,plast read(iunit,fmt=format_string,end=55,iostat=ierr1) dummy_logical_2D(1:width,k) if (ierr1 /= 0) ierr = ierr1 end do end if where (dummy_logical_2D) dummy=1.d0 elsewhere dummy=0.d0 end where deallocate(dummy_logical_2D) else if (typecode == 2) then ! Integer data array allocate(dummy_int_2D(1:width,1:ptot)) dummy_int_2D = 0 if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) dummy_int_2D(1:width,pfirst:plast) else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_int_2D(1:width,k) if (ierr1 /= 0) ierr = ierr1 end do end if dummy(1:width,pfirst:plast) = real(dummy_int_2D(1:width,pfirst:plast)) deallocate(dummy_int_2D) else if (typecode == 3) then ! Long integer data array allocate(dummy_ilp_int_2D(1:width,1:ptot)) dummy_ilp_int_2D = 0 if (iambinaryfile==1) then read(iunit,end=55,iostat=ierr) dummy_ilp_int_2D(1:width,pfirst:plast) else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy_ilp_int_2D(1:width,k) if (ierr1 /= 0) ierr = ierr1 end do end if dummy(1:width,pfirst:plast) = real(dummy_ilp_int_2D(1:width,pfirst:plast)) deallocate(dummy_ilp_int_2D) else if (typecode == 4) then ! PR data array if (doubleprec) allocate(dummy_dp(1:width,1:ptot),stat=ierr) if (iambinaryfile==1) then if (doubleprec) then read(iunit,end=55,iostat=ierr) dummy_dp(1:width,pfirst:plast) dummy(1:width,pfirst:plast) = real(dummy_dp(1:width,pfirst:plast)) else read(iunit,end=55,iostat=ierr) dummy(1:width,pfirst:plast) end if else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy(1:width,k) if (ierr1 /= 0) ierr = ierr1 end do end if if (doubleprec) deallocate(dummy_dp) else if (typecode == 5) then ! DP data array if (iambinaryfile==1) then allocate(dummy_dp(1:width,1:ptot),stat=ierr) read(iunit,end=55,iostat=ierr) dummy_dp(1:width,pfirst:plast) dummy(1:width,pfirst:plast) = real(dummy_dp(1:width,pfirst:plast)) deallocate(dummy_dp) else do k=pfirst,plast read(iunit,*,end=55,iostat=ierr1) dummy(1:width,k) if (ierr1 /= 0) ierr = ierr1 end do end if end if do k=pfirst,plast dat(k,iunknown(unknown):iunknown(unknown)+width-1,step) = dummy(1:width,k) enddo if (ierr /= 0) then print*,' WARNING: errors reading unknown data type ', trim(data_id(i)) ierr_out = -1 end if deallocate(dummy) end if end if end select end do write (6,*) if (stot>0) then ! Load sink stuff into end of dat array dat(ptot+1:ptot+stot,1:ncolumns,step) = sink_dat_array(1:stot,1:ncolumns,1) end if return 55 continue if (iambinaryfile==1) print "(a)",' ERROR: end of file in binary read' if (iambinaryfile==0) print "(a)",' ERROR: end of file in ascii read' ierr_out = -3 return 56 continue ierr_out = -2 return end subroutine !---------------------------------------------------- ! translate types into order (for old dragon read) !---------------------------------------------------- subroutine set_types(itypei,ntotal,noftype) implicit none integer(kind=int1), dimension(:), intent(inout) :: itypei integer, intent(in) :: ntotal integer, dimension(:), intent(out) :: noftype integer :: noftype_temp(1:7) noftype = 0 noftype_temp(1) = pgas noftype_temp(2) = pboundary noftype_temp(3) = stot noftype_temp(4) = picm noftype_temp(5) = pcdm noftype_temp(6) = pdust noftype_temp(7) = pion if (sum(noftype_temp(1:7)).ne.ntotal) then print "(a)",' INTERNAL ERROR setting number in each type in dragon read' endif do i=1,7 if (i > seren_maxparttypes .AND. noftype_temp(i) > 0) then print*,' *** ERROR: not enough particle types for SEREN data read ***' print*,' *** you need to edit splash parameters and recompile ***' stop end if end do noftype(1:seren_maxparttypes) = noftype_temp(1:seren_maxparttypes) if (pboundary>0) itypei(1:pboundary) = 2 if (picm>0) itypei(pboundary+1:pboundary+picm) = 4 if (pgas>0) itypei(pboundary+picm+1:pboundary+picm+pgas) = 1 if (pcdm>0) itypei(pboundary+picm+pgas+1:pboundary+picm+pgas+pcdm) = 5 if (pdust>0) itypei(pboundary+picm+pgas+pcdm+1:pboundary+picm+pgas+pcdm+pdust) = 6 if (pion>0) itypei(pboundary+picm+pgas+pcdm+pdust+1:ptot) = 7 if (stot>0) itypei(ptot+1:ptot+stot) = 3 return end subroutine set_types end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,unitslabel,& &ix,ivx,ipmass,ih,irho,iBfirst,iutherm,lenlabel,lenunitslabel use params use settings_data, only:ndim,ndimV,ncolumns,ntypes,UseTypeInRenderings use geometry, only:labelcoord use settings_units, only:units use seren_data_store implicit none integer :: i, j, width, unit_no integer :: nunknown ! Number of unknown data types character(len=lenunitslabel) :: unit_base, unit_string character(len=lenlabel) :: type_names(1:7) logical :: type_use_render(1:7) if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif ! Calculate number of columns to read iporig = 0 ncolumns = 0 nunknown = 0 do i=1,ndata unit_no = typedata(5,i) !write (6,*) "i = ", i, "; data_id(i) = ", data_id(i) unit_base = "" unit_string = "" if (unit_no < 0 .OR. unit_no > nunits) then print*,'*** ERROR: unit_no = ',unit_no,' in set_labels ***' else if (unit_no /= 0) then unit_base = trim(adjustl(unit_data(unit_no))) unit_string = unit_base call translate_unit_names(unit_string) unit_string = ' ['//trim(adjustl(unit_string))//']' end if !write (6,*) "unit_base, unit_string = ", unit_base, unit_string select case (trim(data_id(i))) case ("porig") ! Original particle number iporig = -1 ! We always want this last case ("r") ! Position do j=1,NDIMtemp ix(j) = ncolumns + j units(ix(j)) = unit_coeff(ix(j)) unitslabel(ix(j)) = unit_string end do ! unitzintegration = units(ix(1)) ! labelzintegration = ' ['//trim(adjustl(unit_string))//']' label(ix(1:ndim)) = labelcoord(1:ndim,1) ncolumns = ncolumns + NDIMtemp r_unit = trim(adjustl(unit_base)) case ("h") ! Smoothing lengths ih = ncolumns + 1 units(ih) = unit_coeff(ih) unitslabel(ih) = unit_string label(ih) = 'h' ncolumns = ncolumns + 1 h_unit = trim(adjustl(unit_base)) case ("m") ! Mass ipmass = ncolumns + 1 units(ipmass) = unit_coeff(ipmass) unitslabel(ipmass) = unit_string label(ipmass) = 'particle mass' ncolumns = ncolumns + 1 m_unit = trim(adjustl(unit_base)) case ("v") ! Velocities ivx = ncolumns + 1 iamvec(ivx:ivx+VDIMtemp-1) = ivx labelvec(ivx:ivx+VDIMtemp-1) = 'v' do j=1,VDIMtemp label(ivx+j-1) = 'v\d'//labelcoord(j,1) units(ivx+j-1) = unit_coeff(ivx+j-1) unitslabel(ivx+j-1) = unit_string enddo ncolumns = ncolumns + VDIMtemp case ("rho") ! Densities irho = ncolumns + 1 units(irho) = unit_coeff(irho) unitslabel(irho) = unit_string label(irho) = 'density' ncolumns = ncolumns + 1 rho_unit = trim(adjustl(unit_base)) case ("temp") ! Temperatures itemp = ncolumns + 1 ! NOT A PROPER SPLASH i_quantity units(itemp) = unit_coeff(itemp) unitslabel(itemp) = unit_string label(ncolumns + 1) = 'temperature' ncolumns = ncolumns + 1 case ("u") ! Internal energy iutherm = ncolumns + 1 units(iutherm) = unit_coeff(iutherm) unitslabel(iutherm) = unit_string label(ncolumns + 1) = 'internal energy' ncolumns = ncolumns + 1 case ("B") ! Magnetic fields iBfirst = ncolumns + 1 iamvec(iBfirst:iBfirst+BDIMtemp-1) = iBfirst labelvec(iBfirst:iBfirst+BDIMtemp-1) = 'B' do j=1,BDIMtemp label(iBfirst+j-1) = 'B\d'//labelcoord(j,1) units(iBfirst+j-1) = unit_coeff(iBfirst+j-1) unitslabel(iBfirst+j-1) = unit_string enddo ncolumns = ncolumns + BDIMtemp case ("sink_v0") ! Do nothing yet, sinks are stored separately case ("sink_v1") ! Do nothing yet, sinks are stored separately case default print*,' WARNING reading file: unknown data type ', trim(data_id(i)) if (typedata(4,i) == 7) cycle ! Special data module we don't understand; ignore if (typedata(4,i) == 6) cycle ! Not a lot we can do with character data here! width = typedata(1,i) nunknown = nunknown + 1 iunknown(nunknown) = ncolumns + 1 ! NOT A PROPER SPLASH i_quantity if (width == 1) then label(iunknown(nunknown)) = trim(data_id(i)) units(iunknown(nunknown)) = unit_coeff(iunknown(nunknown)) unitslabel(iunknown(nunknown)) = unit_string else if (width <= NDIMtemp) then do j=1,width label(iunknown(nunknown)+j-1) = trim(data_id(i))//'\d'//labelcoord(j,1) units(iunknown(nunknown)+j-1) = unit_coeff(iunknown(nunknown)) unitslabel(iunknown(nunknown)+j-1) = unit_string end do else do j=1,width write(label(iunknown(nunknown)+j-1),'(A,A,I0)') trim(data_id(i)),'\d',j units(iunknown(nunknown)+j-1) = unit_coeff(iunknown(nunknown)) unitslabel(iunknown(nunknown)+j-1) = unit_string end do end if ncolumns = ncolumns + width ! Add width of data to ncolumns end select end do if (iporig == -1) then ! If there is porig, add as last column iporig = ncolumns + 1 ! NOT A PROPER SPLASH i_quantity label(ncolumns + 1) = 'particle id' ncolumns = ncolumns + 1 end if !--set labels for each particle type ! ntypes = seren_maxparttypes type_names = (/'gas ','boundary','sink ','icm ','cdm ','dust ','ion '/) type_use_render = (/.TRUE.,.TRUE.,.FALSE.,.TRUE.,.TRUE.,.TRUE.,.TRUE./) labeltype(1:ntypes) = type_names(1:ntypes) UseTypeInRenderings(1:ntypes) = type_use_render(1:ntypes) !----------------------------------------------------------- return end subroutine set_labels subroutine find_weights(out_unit_interp,out_unitzintegration,out_labelzintegration) use labels, only:lenunitslabel use params use seren_data_store implicit none real(doub_prec), intent(out) :: out_unit_interp real, intent(out) :: out_unitzintegration character(len=lenunitslabel), intent(out) :: out_labelzintegration real(doub_prec) :: dm_unit, dh_unit, drho_unit, dr_unit logical :: do_dimweight, do_zintegration character(len=lenunitslabel) :: rho_length_label real(doub_prec) :: rho_length out_unit_interp = 1.0 out_unitzintegration = 1.0 out_labelzintegration = "" do_dimweight = .TRUE. do_zintegration = .TRUE. if (m_unit=="") then print*,'No masses or no mass units!' print*,'Cannot create dimensionless weight (unnormalised rendered plots may be incorrect)' do_dimweight = .FALSE. end if if (h_unit=="") then print*,'No smoothing lengths or no smoothing length units!' print*,'Cannot create dimensionless weight (unnormalised rendered plots may be incorrect)' do_dimweight = .FALSE. end if if (rho_unit=="") then print*,'No densities or no density units!' print*,'Cannot create dimensionless weight (unnormalised rendered plots may be incorrect)' do_dimweight = .FALSE. print*,'Cannot set unitzintegration (column density plots may be incorrect)' do_zintegration = .FALSE. end if if (r_unit=="") then print*,'No positions or no position units!' print*,'Cannot set unitzintegration (column density plots may be incorrect)' do_zintegration = .FALSE. end if ! Length unit in S.I. units (m) if (r_unit=="pc") then dr_unit = r_pc else if (r_unit=="au") then dr_unit = r_au else if (r_unit=="r_sun") then dr_unit = r_sun else if (r_unit=="r_earth") then dr_unit = r_earth else if (r_unit=="km") then dr_unit = 1000.0_DP else if (r_unit=="m") then dr_unit = 1.0_DP else if (r_unit=="cm") then dr_unit = 0.01_DP else print*,'Unknown position unit ', r_unit, '!' print*,'Cannot set unitzintegration (column density plots may be incorrect)' do_zintegration = .FALSE. dr_unit = 1.0_DP end if ! Length unit in S.I. units (m) if (h_unit=="pc") then dh_unit = r_pc else if (h_unit=="au") then dh_unit = r_au else if (h_unit=="r_sun") then dh_unit = r_sun else if (h_unit=="r_earth") then dh_unit = r_earth else if (h_unit=="km") then dh_unit = 1000.0_DP else if (h_unit=="m") then dh_unit = 1.0_DP else if (h_unit=="cm") then dh_unit = 0.01_DP else print*,'Unknown smoothing length unit ', h_unit, '!' print*,'Cannot create dimensionless weight (unnormalised rendered plots may be incorrect)' do_dimweight = .FALSE. dh_unit = 1.0_DP end if ! Mass units in S.I. units (kg) if (m_unit=="m_sun") then dm_unit = m_sun else if (m_unit=="m_jup") then dm_unit = m_jup else if (m_unit=="m_earth") then dm_unit = m_earth else if (m_unit=="kg") then dm_unit = 1._DP else if (m_unit=="g") then dm_unit = 1.0E-3_DP else print*,'Unknown mass unit ', m_unit, '!' print*,'Cannot create dimensionless weight (unnormalised rendered plots may be incorrect)' do_dimweight = .FALSE. dm_unit = 1._DP end if ! Density units in S.I. units (i.e. kg/m^3) if (rho_unit=="m_sun_pc3") then drho_unit = m_sun / (r_pc**3) rho_length = r_pc rho_length_label = "pc" else if (rho_unit=="m_sun_pc2") then drho_unit = m_sun / (r_pc**2) rho_length = r_pc rho_length_label = "pc" else if (rho_unit=="kg_m3") then drho_unit = 1.0_DP rho_length = 1.0_DP rho_length_label = "m" else if (rho_unit=="kg_m2") then drho_unit = 1.0_DP rho_length = 1.0_DP rho_length_label = "m" else if (rho_unit=="g_cm3") then drho_unit = 1.0E3_DP rho_length = 0.01_DP rho_length_label = "cm" else if (rho_unit=="g_cm2") then drho_unit = 10.0_DP rho_length = 0.01_DP rho_length_label = "cm" else print*,'Unknown density unit ', rho_unit, '!' print*,'Cannot create dimensionless weight (unnormalised rendered plots may be incorrect)' do_dimweight = .FALSE. print*,'Cannot set unitzintegration (column density plots may be incorrect)' do_zintegration = .FALSE. rho_length = 1.0_DP end if if (do_dimweight) then out_unit_interp = dm_unit/(drho_unit*dh_unit**NDIMtemp) end if if (do_zintegration) then out_unitzintegration = dr_unit / rho_length out_labelzintegration = rho_length_label end if return end subroutine find_weights subroutine translate_unit_names(unit_name) implicit none character(len=*), intent(inout) :: unit_name select case (trim(unit_name)) case ("r_sun") unit_name = "r\dSun\u" case ("au") unit_name = "AU" case ("r_earth") unit_name = "r\dEarth\u" case ("m_sun") unit_name = "M\d\(2281)\u" case ("m_jup") unit_name = "M\dJupiter\u" case ("m_earth") unit_name = "M\dEarth\u" case ("myr") unit_name = "Myrs" case ("km_s") unit_name = "km s\u-1\d" case ("au_yr") unit_name = "AU / yr" case ("m_s") unit_name = "m s\u-1\d" case ("cm_s") unit_name = "cm s\u-1\d" case ("km_s2") unit_name = "km \u-2\d" case ("au_yr2") unit_name = "AU yr\u-2\d" case ("m_s2") unit_name = "m s\u-2\d" case ("cm_s2") unit_name = "cm s\u-2\d" case ("m_sun_pc3") unit_name = "M\d\(2281)\u pc\u-3\d" case ("kg_m_3") unit_name = "kg m\u-3\d" case ("g_cm_3") unit_name = "g cm\u-3\d" case ("m_sun_pc2") unit_name = "M\d\(2281)\u pc\u-2\d" case ("kg_m_2") unit_name = "kg m\u-2\d" case ("g_cm_2") unit_name = "g cm\u-2\d" case ("g_cms2") unit_name = "g cm\u-2\d" case ("10^40erg") unit_name = "\x 10\u40\d ergs" case ("m_sunkm_s") unit_name = "M\d\(2281)\u km s\u-1\d" case ("m_sunau_yr") unit_name = "M\d\(2281)\u AU yr\u-1\d" case ("kgm_s") unit_name = "kg m s\u-1\d" case ("gcm_s") unit_name = "g cm s\u-1\d" case ("m_sunkm2_s") unit_name = "M\d\(2281)\u km\u2\d s\u-1\d" case ("m_sunau2_yr") unit_name = "M\d\(2281)\u AU\u2\d yr\u-1\d" case ("kgm2_s") unit_name = "kg m\u2\d s\u-1\d" case ("gcm2_s") unit_name = "g cm\u2\d s\u-1\d" case ("rad_s") unit_name = "radians s\u-1\d" case ("m_sun_myr") unit_name = "M\d\(2281)\u Myr\u-1\d" case ("m_sun_yr") unit_name = "M\d\(2281)\u yr\u-1\d" case ("kg_s") unit_name = "kg s\u-1\d" case ("g_s") unit_name = "g s\u-1\d" case ("L_sun") unit_name = "Ld\(2281)\u" case ("J_s") unit_name = "J s\u-1\d" case ("ergs_s") unit_name = "ergs s\u-1\d" case ("m2_kg") unit_name = "m\u2\d kg\u-1\d" case ("cm2_g") unit_name = "cm\u2\d g\u-1\d" case ("tesla") unit_name = "Tesla" case ("gauss") unit_name = "Gauss" case ("C_s_m2") unit_name = "C s\u-1\d m\u-2\d" case ("J_kg") unit_name = "J kg\u-1\d" case ("erg_g") unit_name = "ergs g\u-1\d" case default unit_name = unit_name ! this could be improved end select return end subroutine translate_unit_names splash/src/titles.f90000644 000766 000000 00000005372 13261626263 015406 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- module titles implicit none integer, parameter, private :: maxtitles = 50 integer, parameter, private :: maxsteplegend = 100 integer, parameter, public :: lensteplegend = 60 integer, parameter, public :: lenpagetitles = 60 character(len=lenpagetitles), dimension(maxtitles), public :: pagetitles character(len=lensteplegend), dimension(maxsteplegend), public :: steplegend public :: read_titles, read_steplegend private contains ! !--reads a list of titles (one per line), to be used to label each plot on page ! subroutine read_titles(ntitles) use asciiutils, only:read_asciifile use filenames, only:fileprefix implicit none integer, intent(out) :: ntitles character(len=50) :: titlefile logical :: iexist titlefile = trim(fileprefix)//'.titles' !--also allow obsolete title filename inquire(file=titlefile,exist=iexist) if (.not.iexist) then inquire(file='titlelist',exist=iexist) if (iexist) titlefile='titlelist' endif ntitles = 0 call read_asciifile(titlefile,ntitles,pagetitles) if (ntitles.gt.0) print "(a)",'read plot titles from file '//trim(titlefile) return end subroutine read_titles ! !--reads a list of labels (one per line) to be used in the timestep legend ! (ie. for multiple timesteps on same page) ! subroutine read_steplegend(nsteptitles) use asciiutils, only:read_asciifile use filenames, only:fileprefix implicit none integer, intent(out) :: nsteptitles character(len=50) :: legendfile logical :: iexist legendfile = trim(fileprefix)//'.legend' !--also allow obsolete legend filename inquire(file=legendfile,exist=iexist) if (.not.iexist) then inquire(file='legend',exist=iexist) if (iexist) legendfile='legend' endif nsteptitles = 0 call read_asciifile(legendfile,nsteptitles,steplegend) if (nsteptitles.gt.0) print "(a)"//'read legend text from file '''//trim(legendfile)//'''' return end subroutine read_steplegend end module titles splash/src/sort.f90000644 000766 000000 00000013410 13261626263 015061 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module sort implicit none public :: indexx, indexxi private contains subroutine indexx(n, arr, indx) !************************************************************ ! * ! This is INDEXX using the quicksort algorithm. * ! * !************************************************************ implicit none integer, parameter :: m=7, nstack=500 integer, intent(in) :: n real, dimension(n), intent(in) :: arr integer, dimension(n), intent(out) :: indx integer :: i,j,k,l,ir,jstack,indxt,itemp integer, dimension(nstack) :: istack real :: a do j = 1, n indx(j) = j enddo jstack = 0 l = 1 ir = n 1 if (ir - l.lt.m) then do j = l + 1, ir indxt = indx(j) a = arr(indxt) do i = j - 1, 1, -1 if (arr(indx(i)).le.a) goto 2 indx(i + 1) = indx(i) end do i = 0 2 indx(i + 1) = indxt end do if (jstack.eq.0) return ir = istack(jstack) l = istack(jstack - 1) jstack = jstack - 2 else k = (l + ir)/2 itemp = indx(k) indx(k) = indx(l + 1) indx(l + 1) = itemp if (arr(indx(l + 1)).gt.arr(indx(ir))) then itemp = indx(l + 1) indx(l + 1) = indx(ir) indx(ir) = itemp endif if (arr(indx(l)).gt.arr(indx(ir))) then itemp = indx(l) indx(l) = indx(ir) indx(ir) = itemp endif if (arr(indx(l + 1)).gt.arr(indx(l))) then itemp = indx(l + 1) indx(l + 1) = indx(l) indx(l) = itemp endif i = l + 1 j = ir indxt = indx(l) a = arr(indxt) 3 continue i = i + 1 if (arr(indx(i)).lt.a) goto 3 4 continue j = j - 1 if (arr(indx(j)).gt.a) goto 4 if (j.lt.i) goto 5 itemp = indx(i) indx(i) = indx(j) indx(j) = itemp goto 3 5 indx(l) = indx(j) indx(j) = indxt jstack = jstack + 2 if (jstack.gt.nstack) then print*,'fatal error!!! stacksize exceeded in sort' print*,'(need to set parameter nstack higher in subroutine indexx ' print*,' this is in the file sort.f90)' stop endif if (ir - i + 1.ge.j - l) then istack(jstack) = ir istack(jstack - 1) = i ir = j - 1 else istack(jstack) = j - 1 istack(jstack - 1) = l l = i endif endif goto 1 end subroutine indexx !---------------------------------- ! integer version of above routine !---------------------------------- subroutine indexxi(n, arr, indx) !************************************************************ ! * ! This is INDEXX using the quicksort algorithm. * ! * !************************************************************ implicit none integer, parameter :: m=7, nstack=500 integer, intent(in) :: n integer, dimension(n), intent(in) :: arr integer, dimension(n), intent(out) :: indx integer :: i,j,k,l,ir,jstack,indxt,itemp integer, dimension(nstack) :: istack integer :: a do j = 1, n indx(j) = j enddo jstack = 0 l = 1 ir = n 1 if (ir - l.lt.m) then do j = l + 1, ir indxt = indx(j) a = arr(indxt) do i = j - 1, 1, -1 if (arr(indx(i)).le.a) goto 2 indx(i + 1) = indx(i) end do i = 0 2 indx(i + 1) = indxt end do if (jstack.eq.0) return ir = istack(jstack) l = istack(jstack - 1) jstack = jstack - 2 else k = (l + ir)/2 itemp = indx(k) indx(k) = indx(l + 1) indx(l + 1) = itemp if (arr(indx(l + 1)).gt.arr(indx(ir))) then itemp = indx(l + 1) indx(l + 1) = indx(ir) indx(ir) = itemp endif if (arr(indx(l)).gt.arr(indx(ir))) then itemp = indx(l) indx(l) = indx(ir) indx(ir) = itemp endif if (arr(indx(l + 1)).gt.arr(indx(l))) then itemp = indx(l + 1) indx(l + 1) = indx(l) indx(l) = itemp endif i = l + 1 j = ir indxt = indx(l) a = arr(indxt) 3 continue i = i + 1 if (arr(indx(i)).lt.a) goto 3 4 continue j = j - 1 if (arr(indx(j)).gt.a) goto 4 if (j.lt.i) goto 5 itemp = indx(i) indx(i) = indx(j) indx(j) = itemp goto 3 5 indx(l) = indx(j) indx(j) = indxt jstack = jstack + 2 if (jstack.gt.nstack) then print*,'fatal error!!! stacksize exceeded in sort' print*,'(need to set parameter nstack higher in subroutine indexx ' print*,' this is in the file sort.f90)' stop endif if (ir - i + 1.ge.j - l) then istack(jstack) = ir istack(jstack - 1) = i ir = j - 1 else istack(jstack) = j - 1 istack(jstack - 1) = l l = i endif endif goto 1 end subroutine indexxi end module sort splash/src/colours.f90000644 000766 000000 00000067254 13261626263 015577 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ! This module contains subroutines and variables for setting the ! colour schemes for rendered plots ! module colours implicit none integer, parameter :: ncolourmax = 256 integer, parameter :: ncolourschemes = 39 character(len=17), dimension(ncolourschemes), parameter :: schemename = & (/'greyscale ', & 'red ', & 'Bate red-yellow ', & 'heat ', & 'rainbow ', & 'prism ', & 'red-blue-yellow ', & 'blue-yellow-red ', & 'purple-blue-green', & 'gamma ', & 'gamma- no black ', & 'grn-red-blue-wht ', & 'blk-blu-cyan-yell', & 'rainbow II ', & 'rainbow II (casa)', & 'haze ', & 'huesatval2 ', & 'blue-red ', & 'blue-grn-red-yell', & 'Bate BRY saoimage', & 'ice (blue-white) ', & 'fire ', & 'blue-red ', & 'menacing ', & 'rt ', & 'Dolag I ', & 'Dolag II ', & 'Dolag III ', & 'Alice WBYR ', & 'light blue ', & 'light green ', & 'light red ', & 'CMRmap ', & 'blue-black-red ', & 'inferno ', & 'viridis ', & 'ocean ', & 'casa blue ', & 'green-brown '/) ! !--rgb colours of the colour table are stored in the array below ! this is used for colour blending (opacity rendering) ! integer :: ifirstcolour, ncolours real, dimension(3,ncolourmax) :: rgbtable contains ! ------------------------------------------------------------------------ ! defines colour schemes for rendering ! ** add your own here ** ! ------------------------------------------------------------------------ subroutine colour_set(icolourscheme) use plotlib, only:plot_qcol,plot_qcir,plot_scir,plot_ctab,plot_scr,plot_qcr use settings_data, only:debugmode implicit none integer, intent(in) :: icolourscheme integer :: i,icolmin,icolmax,ncolmax,nset,index real :: brightness,contrast real, dimension(64) :: lumarr,redarr,greenarr,bluearr ncolours = ncolourmax-1 nset = 0 ! !--set first colour index (warning: colours 1-16 have presets, so ! overwriting these means that line graphs that use colour will come ! out funny). Best to leave 0 and 1 alone as these are black and white. ! ifirstcolour = 2 ! !--inquire as to colour range available on current device ! adjust ncolours if necessary ! call plot_qcol(icolmin,icolmax) ! print*,' from device = ',icolmin,icolmax call plot_qcir(icolmin,icolmax) ! print*,' other = ',icolmin,icolmax if (ifirstcolour.lt.icolmin) ifirstcolour = icolmin ncolmax = icolmax - ifirstcolour if (ncolours.gt.ncolmax) then ncolours = ncolmax print*,'Warning: Device allows only ',ncolours+1,' colours' endif ! !--set this as the range of colour indices to use ! if (debugmode) print*,'DEBUG: querying colour index range' call plot_scir(ifirstcolour,ifirstcolour+ncolours) if (abs(icolourscheme).le.ncolourschemes) then brightness = 0.5 contrast = 1.0 !--invert colour table for negative values if (icolourscheme.lt.0) contrast = -1.0 select case(abs(icolourscheme)) case(1) !--greyscale nset = 2 lumarr(1:nset) = (/0.000,1.000/) redarr(1:nset) = (/0.000,1.000/) greenarr(1:nset)= (/0.000,1.000/) bluearr(1:nset) = (/0.000,1.000/) case(2) !--red temperature (IDL red-temperature) nset = 5 lumarr(1:nset) = (/0.000,0.475,0.694,0.745,1.000/) redarr(1:nset) = (/0.000,0.686,1.000,1.000,1.000/) greenarr(1:nset)= (/0.000,0.004,0.420,0.518,1.000/) bluearr(1:nset) = (/0.000,0.000,0.000,0.000,1.000/) case(3) !--Bate red-yellow-white nset = 4 lumarr(1:nset) = (/0.000,0.337,0.666,1.000/) redarr(1:nset) = (/0.000,1.000,1.000,1.000/) greenarr(1:nset)= (/0.000,0.000,1.000,1.000/) bluearr(1:nset) = (/0.000,0.000,0.000,1.000/) case(4) !--heat nset = 5 lumarr(1:nset) = (/0.,0.25,0.5,0.75,1.0/) redarr(1:nset) = (/0.0,0.0,0.0,1.0,1.0/) greenarr(1:nset)= (/0.0,1.0,1.0,1.0,0.0/) bluearr(1:nset) = (/1.0,1.0,0.0,0.0,0.0/) case(5) !--rainbow nset = 8 lumarr(1:nset) = (/0.0,0.125,0.225,0.25,0.425,0.625,0.8125,1.0/) redarr(1:nset) = (/0.0,0.341,0.100,0.00,0.000,0.000,1.0000,1.0/) greenarr(1:nset)= (/0.0,0.000,0.000,0.00,1.000,1.000,1.0000,0.0/) bluearr(1:nset) = (/0.0,0.569,1.000,1.00,1.000,0.000,0.0000,0.0/) case(6) !--prism (IDL prism) nset = 8 lumarr(1:nset) = (/0.000,0.251,0.263,0.494,0.502,0.749,0.753,1.000/) redarr(1:nset) = (/0.000,0.953,1.000,0.035,0.000,0.000,0.000,0.000/) greenarr(1:nset)= (/0.000,0.000,0.043,0.969,1.000,0.000,0.000,0.000/) bluearr(1:nset) = (/0.000,0.000,0.000,0.000,0.027,0.984,1.000,0.000/) case(7) !--red-blue-yellow (IDL 16: stern special) nset = 7 lumarr(1:nset) = (/0.000,0.055,0.247,0.251,0.502,0.737,1.000/) redarr(1:nset) = (/0.000,0.996,0.000,0.251,0.502,0.737,1.000/) greenarr(1:nset)= (/0.000,0.055,0.247,0.251,0.502,0.737,1.000/) bluearr(1:nset) = (/0.000,0.106,0.490,0.498,1.000,0.000,1.000/) case(8) !--blue-yellow-red (IDL 34: blue-red) nset = 10 lumarr(1:nset) = (/0.000,0.004,0.125,0.129,0.380,0.384,0.635,0.886,0.996,1.000/) redarr(1:nset) = (/0.000,0.000,0.000,0.000,0.000,0.000,1.000,1.000,0.514,0.514/) greenarr(1:nset)= (/0.000,0.000,0.000,0.000,1.000,1.000,1.000,0.000,0.000,0.000/) bluearr(1:nset) = (/0.514,0.514,1.000,1.000,1.000,1.000,0.000,0.000,0.000,0.000/) ! nset = 6 ! lumarr(1:nset) = (/0.0,0.2,0.4,0.6,0.8,1.0/) ! redarr(1:nset) = (/0.0,0.0,0.5,1.0,1.0,0.5/) ! bluearr(1:nset) = (/1.0,1.0,0.5,0.0,0.0,0.0/) ! greenarr(1:nset)= (/0.0,1.0,0.5,1.0,0.0,0.0/) case(9) !--purple-blue-green nset = 6 lumarr(1:nset) = (/0.0,0.1,0.2,0.5,0.8,1.0/) redarr(1:nset) = (/0.0,0.1,0.5,0.02,0.0,0.0/) bluearr(1:nset) = (/0.0,0.2,0.5,0.98,0.0,0.0/) greenarr(1:nset)= (/0.0,0.0,0.0,0.0,0.62,0.98/) case(10) !--gamma (IDL 6: stdgamma-ii) nset = 18 lumarr(1:nset) =(/0.,0.184,0.192,0.251,0.31,0.376,0.427,0.431,0.443,0.502,0.569,0.624,0.635,0.682,0.69,0.749,0.753,1./) redarr(1:nset) =(/0.,0.000,0.035,0.318,0.31,0.643,0.914,1.000,1.000,1.000,1.000,1.000,1.000,0.639,0.678,0.976,1.00,1./) greenarr(1:nset)=(/0.,0.000,0.000,0.000,0.00,0.000,0.000,0.000,0.000,0.318,0.639,0.639,0.639,0.639,0.639,1.000,1.00,1./) bluearr(1:nset) =(/0.,0.957,1.000,0.682,0.365,0.00,0.000,0.000,0.000,0.000,0.322,0.000,0.000,0.000,0.000,0.188,0.20,1./) case(11) !--gamma but without the fade to black nset = 18 lumarr(1:nset) =(/0.,0.184,0.192,0.251,0.31,0.376,0.427,0.431,0.443,0.502,0.569,0.624,0.635,0.682,0.69,0.749,0.753,1./) redarr(1:nset) =(/0.,0.000,0.035,0.318,0.31,0.643,0.914,1.000,1.000,1.000,1.000,1.000,1.000,0.639,0.678,0.976,1.00,1./) greenarr(1:nset)=(/0.,0.000,0.000,0.000,0.00,0.000,0.000,0.000,0.000,0.318,0.639,0.639,0.639,0.639,0.639,1.000,1.00,1./) bluearr(1:nset) =(/0.5,0.957,1.000,0.682,0.365,0.00,0.000,0.000,0.000,0.000,0.322,0.000,0.000,0.000,0.000,0.188,0.20,1./) case(12) !--IDL 3: grn-red-blu-wht nset = 13 lumarr(1:nset) = (/0.000,0.008,0.047,0.110,0.125,0.267,0.282,0.298,0.773,0.788,0.863,0.988,1.000/) redarr(1:nset) = (/0.000,0.000,0.000,0.000,0.094,0.941,0.988,0.988,0.643,0.639,0.580,0.988,1.000/) greenarr(1:nset)= (/0.000,0.282,0.424,0.988,0.941,0.094,0.000,0.000,0.000,0.000,0.000,0.988,1.000/) bluearr(1:nset) = (/0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.004,0.984,1.000,1.000,1.000,1.000/) case(13) !--black-blue-cyan-yellow nset = 4 lumarr(1:nset) = (/0.,0.333,0.666,1.0/) redarr(1:nset) = (/0.0,0.0,0.0,1.0/) greenarr(1:nset)= (/0.0,0.0,1.0,1.0/) bluearr(1:nset) = (/0.0,1.0,1.0,0.0/) case(14) !--rainbow II (as used in NS merger I) nset = 10 lumarr(1:nset) = (/0.000,0.153,0.157,0.310,0.314,0.467,0.471,0.624,0.627,1.000/) redarr(1:nset) = (/1.000,1.000,0.996,0.016,0.000,0.000,0.000,0.000,0.020,1.000/) greenarr(1:nset)= (/0.000,0.980,1.000,1.000,1.000,1.000,0.984,0.004,0.000,0.000/) bluearr(1:nset) = (/0.000,0.000,0.000,0.000,0.012,0.988,1.000,1.000,1.000,1.000/) case(15) ! rainbow scheme from CASA nset = 56 lumarr(1:nset) = (/0.000,0.008,0.016,0.035,0.051,0.062,0.070,0.082,0.094,0.105,& 0.113,0.137,0.152,0.160,0.172,0.184,0.191,0.203,0.215,0.227,& 0.234,0.258,0.277,0.285,0.309,0.328,0.336,0.406,0.430,0.457,& 0.480,0.520,0.570,0.602,0.609,0.621,0.629,0.637,0.648,0.656,& 0.668,0.680,0.754,0.762,0.770,0.777,0.793,0.801,0.812,0.820,& 0.844,0.852,0.867,0.875,0.941,1.000/) redarr(1:nset) = (/0.000,0.055,0.102,0.235,0.325,0.384,0.455,0.494,0.404,0.341,& 0.282,0.149,0.043,0.000,0.004,0.012,0.024,0.047,0.106,0.133,& 0.157,0.200,0.224,0.247,0.271,0.302,0.318,0.420,0.443,0.486,& 0.514,0.561,0.510,0.557,0.600,0.659,0.737,0.800,0.894,0.969,& 0.988,1.000,0.996,0.992,0.969,0.945,0.918,0.898,0.882,0.875,& 0.859,0.859,0.855,0.847,0.725,0.635/) greenarr(1:nset)= (/0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,& 0.000,0.000,0.000,0.000,0.016,0.039,0.078,0.125,0.184,0.231,& 0.275,0.353,0.400,0.435,0.478,0.533,0.565,0.745,0.788,0.863,& 0.910,0.992,0.875,0.804,0.776,0.776,0.812,0.839,0.886,0.925,& 0.961,1.000,0.988,0.969,0.886,0.788,0.659,0.549,0.455,0.349,& 0.180,0.114,0.031,0.000,0.000,0.000/) bluearr(1:nset) = (/0.000,0.208,0.239,0.333,0.404,0.451,0.502,0.545,0.620,0.667,& 0.722,0.843,0.922,0.961,0.925,0.875,0.816,0.769,0.702,0.655,& 0.616,0.529,0.467,0.435,0.388,0.337,0.310,0.188,0.188,0.204,& 0.216,0.235,0.208,0.184,0.180,0.184,0.188,0.196,0.208,0.216,& 0.227,0.235,0.231,0.227,0.208,0.184,0.153,0.129,0.106,0.067,& 0.016,0.004,0.000,0.000,0.000,0.000,0.208/) !--rainbow III ! nset = 13 ! lumarr(1:nset) = (/0.000,0.004,0.110,0.114,0.333,0.557,0.561,0.565,0.569,0.776,0.780,0.996,1.000/) ! redarr(1:nset) = (/0.486,0.486,0.012,0.000,0.000,0.004,0.020,0.051,0.055,0.992,1.000,1.000,1.000/) ! greenarr(1:nset)= (/0.000,0.000,0.000,0.008,0.996,1.000,1.000,1.000,1.000,1.000,0.988,0.020,0.020/) ! bluearr(1:nset) = (/1.000,1.000,1.000,1.000,1.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000/) case(16) !--haze (IDL 17: haze) ! nset = 11 ! lumarr(1:nset) = (/0.000,0.008,0.016,0.486,0.494,0.502,0.514,0.953,0.961,0.996,1.000/) ! redarr(1:nset) = (/0.655,1.000,0.976,0.047,0.031,0.016,0.027,0.898,0.914,0.984,0.984/) ! greenarr(1:nset)= (/0.439,0.835,0.824,0.161,0.149,0.137,0.122,0.706,0.718,0.765,0.765/) ! bluearr(1:nset) = (/1.000,0.996,0.980,0.510,0.502,0.494,0.482,0.043,0.035,0.000,0.000/) !--without the bottom colour nset = 11 lumarr(1:nset) = (/0.000,0.008,0.016,0.486,0.494,0.502,0.514,0.953,0.961,0.996,1.000/) redarr(1:nset) = (/1.000,1.000,0.976,0.047,0.031,0.016,0.027,0.898,0.914,0.984,0.984/) greenarr(1:nset)= (/0.835,0.835,0.824,0.161,0.149,0.137,0.122,0.706,0.718,0.765,0.765/) bluearr(1:nset) = (/1.000,0.996,0.980,0.510,0.502,0.494,0.482,0.043,0.035,0.000,0.000/) case(17) !--huesatval nset = 11 lumarr(1:nset) = (/0.000,0.506,0.514,0.675,0.682,0.843,0.851,0.961,0.969,0.996,1.000/) redarr(1:nset) = (/1.000,0.494,0.486,0.329,0.345,0.988,1.000,1.000,1.000,1.000,1.000/) greenarr(1:nset)= (/0.992,0.992,1.000,1.000,1.000,1.000,0.969,0.345,0.294,0.114,0.114/) bluearr(1:nset) = (/0.992,1.000,0.980,0.337,0.322,0.161,0.153,0.043,0.035,0.008,0.008/) case(18) !--blue-red2 (IDL 12: blue-red) nset = 10 lumarr(1:nset) = (/0.000,0.016,0.247,0.255,0.498,0.506,0.749,0.757,0.996,1.000/) redarr(1:nset) = (/0.000,0.000,0.000,0.000,0.000,0.016,1.000,1.000,1.000,1.000/) greenarr(1:nset)= (/0.000,0.016,1.000,0.984,0.000,0.000,0.000,0.000,0.000,0.000/) bluearr(1:nset) = (/0.000,0.016,1.000,1.000,1.000,1.000,1.000,0.984,0.000,0.000/) case(19) !--blue-green-red-yellow (IDL 5: blue-green-red-yellow) nset = 8 lumarr(1:nset) = (/0.000,0.125,0.188,0.314,0.439,0.502,0.565,1.000/) redarr(1:nset) = (/0.000,0.000,0.000,0.000,0.000,0.471,0.784,1.000/) greenarr(1:nset)= (/0.000,0.000,0.196,0.588,0.549,0.392,0.000,1.000/) bluearr(1:nset) = (/0.000,0.259,0.392,0.392,0.000,0.000,0.000,0.000/) case(20) !--Bate BRY saoimage nset = 5 lumarr(1:nset) = (/0.000,0.25,0.50,0.75,1.00/) redarr(1:nset) = (/0.000,0.00,1.00,1.00,1.00/) greenarr(1:nset)= (/0.000,0.00,0.00,1.00,1.00/) bluearr(1:nset) = (/0.000,1.00,0.00,0.00,1.00/) ! !--Bate BRY original ! nset = 5 ! lumarr(1:nset) = (/0.000,0.195,0.586,0.781,1.00/) ! redarr(1:nset) = (/0.000,0.000,1.000,1.000,1.00/) ! greenarr(1:nset)= (/0.000,0.000,0.000,1.000,1.00/) ! bluearr(1:nset) = (/0.000,1.000,0.000,0.000,1.00/) case(21) !--ice blue (IDL blue-white) nset = 5 lumarr(1:nset) = (/0.000,0.376,0.737,0.753,1.000/) redarr(1:nset) = (/0.000,0.000,0.000,0.000,1.000/) greenarr(1:nset)= (/0.000,0.000,0.580,0.604,1.000/) bluearr(1:nset) = (/0.000,0.510,1.000,1.000,1.000/) case(22) !--fire (from FLASH code) nset = 6 lumarr(1:nset) = (/0.000,0.016,0.078,0.643,0.800,1.000/) redarr(1:nset) = (/1.000,1.000,1.000,1.000,0.000,0.973/) greenarr(1:nset)= (/0.000,0.039,0.129,0.957,0.357,0.980/) bluearr(1:nset) = (/0.000,0.000,0.000,0.000,0.000,0.973/) case(23) !--blue-red (from FLASH code) nset = 7 lumarr(1:nset) = (/0.000,0.149,0.345,0.541,0.643,0.769,1.000/) redarr(1:nset) = (/0.000,0.000,0.157,0.792,0.894,0.988,0.996/) greenarr(1:nset)= (/0.063,0.271,0.584,0.800,0.427,0.220,0.004/) bluearr(1:nset) = (/0.173,0.722,0.153,0.153,0.082,0.165,0.000/) case(24) !--menacing (from FLASH code) nset = 22 lumarr(1:nset) = (/0.000,0.078,0.133,0.161,0.169,0.263,0.271,0.302, & 0.357,0.455,0.478,0.514,0.545,0.588,0.612,0.624, & 0.643,0.722,0.749,0.796,0.808,1.000/) redarr(1:nset) = (/0.388,1.000,0.757,0.953,0.949,0.859,0.906,1.000, & 1.000,0.196,0.098,0.004,0.000,0.000,0.000,0.000, & 0.000,0.051,0.278,0.561,0.612,0.988/) greenarr(1:nset)= (/0.000,0.000,0.118,0.318,0.306,0.749,0.792,1.000, & 1.000,0.490,0.420,0.220,0.125,0.906,0.718,0.569, & 0.000,0.000,0.004,0.008,0.000,0.973/) bluearr(1:nset) = (/0.000,0.000,0.024,0.051,0.059,0.173,0.157,0.000, & 0.000,0.086,0.063,0.004,0.000,0.941,0.827,0.725, & 0.322,0.302,0.518,0.635,0.600,0.988/) case(25) !--RT (from FLASH code) nset = 6 lumarr(1:nset) = (/0.000,0.455,0.580,0.765,0.773,1.000/) redarr(1:nset) = (/0.220,1.000,1.000,1.000,1.000,1.000/) greenarr(1:nset)= (/0.000,0.000,0.478,0.980,1.000,1.000/) bluearr(1:nset) = (/0.000,0.000,0.000,0.588,0.608,0.737/) case(26) ! !--these are Klaus Dolag colour schemes nset = 3 !--blue-green-red ("highlight") lumarr(1:nset) = (/0.0,0.5,1.0/) redarr(1:nset) = (/0.0,0.5,1.0/) greenarr(1:nset) = (/0.0,1.0,0.0/) bluearr(1:nset) = (/1.0,0.5,0.0/) case(27) nset = 3 !--red-greeny-blue lumarr(1:nset) = (/0.0,0.5,1.0/) redarr(1:nset) = (/1.0,0.66,0.0/) greenarr(1:nset) = (/0.0,0.66,0.0/) bluearr(1:nset) = (/0.0,0.66,1.0/) case(28) nset = 5 !--dolag other lumarr(1:nset) = (/0.0,0.33,0.5,0.66,1.0/) redarr(1:nset) = (/0.0,1.00,0.5,0.00,1.0/) greenarr(1:nset) = (/0.0,0.66,1.0,0.66,0.0/) bluearr(1:nset) = (/1.0,0.66,0.5,0.33,0.0/) case(29) !--Alice WBYR nset = 12 lumarr(1:nset) = (/0.0,0.002,0.00672,0.01344,0.40824,0.41496,0.42168,0.43176,0.80052,0.80724,0.84,1.0/) redarr(1:nset) = (/1.0,1.0,1.0,0.976,0.047,0.031,0.016,0.027,0.898,0.914,0.984,0.996/) greenarr(1:nset) = (/1.0,0.835,0.835,0.824,0.161,0.149,0.137,0.122,0.706,0.718,0.765,0.055/) bluearr(1:nset) = (/1.0,1.0,0.996,0.980,0.510,0.502,0.494,0.482,0.043,0.035,0.0,0.0/) case(30) nset = 6 !--light blue lumarr(1:nset) = (/0.0,0.125,0.25,0.5,0.75,1.0/) redarr(1:nset) = (/0.000,0.00,0.00,0.300,0.700,1.000/) greenarr(1:nset) = (/0.145,0.25,0.36,0.612,0.816,1.000/) bluearr(1:nset) = (/0.350,0.54,0.65,0.800,0.918,1.000/) case(31) nset = 6 !--light green lumarr(1:nset) = (/0.0,0.125,0.25,0.5,0.75,1.0/) redarr(1:nset) = (/0.00,0.0,0.075,0.37,0.73,1.000/) greenarr(1:nset) = (/0.20,0.353,0.471, 0.718,0.895,1.000/) bluearr(1:nset) = (/0.082,0.13,0.21, 0.384,0.700,1.000/) case(32) !--light red nset = 7 lumarr(1:nset) = (/0.00,0.13,0.25,0.4,0.50,0.75,1.00/) redarr(1:nset) = (/0.44,0.60,0.84,1.0,1.00,1.00,1.00/) greenarr(1:nset)= (/0.11,0.15,0.21,0.35,0.50,0.75,1.00/) bluearr(1:nset) = (/0.00,0.00,0.00,0.00,0.15,0.55,1.00/) case(33) nset = 9 !--from Carey Rappaport ! "A colormap for effective black and white rendering of color scale images" ! IEEE Antennas Propagat. Mag. vol. 44, no. 3, pp 94-96, Jun 2002. lumarr(1:nset) = (/0.0,0.125,0.25,0.375,0.5,0.625,0.75,0.875,1.0/) redarr(1:nset) = (/0.00,0.15,0.30,0.60,1.00,0.90,0.90,0.90,1.00/) greenarr(1:nset) = (/0.00,0.15,0.15,0.20,0.25,0.50,0.75,0.90,1.00/) bluearr(1:nset) = (/0.00,0.50,0.75,0.50,0.15,0.00,0.10,0.50,1.00/) case(34) !--heat but with black in the middle nset = 5 lumarr(1:nset) = (/0.,0.25,0.5,0.75,1.0/) redarr(1:nset) = (/0.0,0.0,0.0,1.0,0.95/) greenarr(1:nset)= (/0.0,1.0,0.0,1.0,0.0/) bluearr(1:nset) = (/0.95,1.0,0.0,0.0,0.0/) case(35) !--inferno nset = 32 lumarr(1:nset) = (/0.000000, & 0.032258,0.064516,0.096774,0.129032,0.161290,0.193548,0.225806,0.258065, & 0.290323,0.322581,0.354839,0.387097,0.419355,0.451613,0.483871,0.516129, & 0.548387,0.580645,0.612903,0.645161,0.677419,0.709677,0.741935,0.774194, & 0.806452,0.838710,0.870968,0.903226,0.935484,0.967742,1.000000/) redarr(1:nset) = (/0.001462, & 0.013995,0.042253,0.081962,0.135778,0.190367,0.244967,0.297178,0.354032, & 0.403894,0.453651,0.503493,0.559624,0.609330,0.658463,0.706500,0.758422, & 0.801871,0.841969,0.878001,0.912966,0.938675,0.959114,0.974176,0.984591, & 0.987926,0.985566,0.977497,0.962517,0.948683,0.951740,0.988362/) greenarr(1:nset) = (/0.000466, & 0.011225,0.028139,0.043328,0.046856,0.039309,0.037055,0.047470,0.066925, & 0.085580,0.103848,0.121575,0.141346,0.159474,0.178962,0.200728,0.229097, & 0.258674,0.292933,0.332060,0.381636,0.430091,0.482014,0.536780,0.601122, & 0.660250,0.720782,0.782258,0.851476,0.910473,0.960587,0.998364/) bluearr(1:nset) = (/0.013866, & 0.071862,0.141141,0.215289,0.299776,0.361447,0.400007,0.420491,0.430906, & 0.433179,0.430498,0.423356,0.410078,0.393589,0.372748,0.347777,0.315266, & 0.283099,0.248564,0.212268,0.169755,0.130438,0.089499,0.048392,0.023606, & 0.051750,0.112229,0.185923,0.285546,0.395289,0.524203,0.644924/) case(36) !--viridis nset = 32 lumarr(1:nset) = (/0.000000, & 0.032258,0.064516,0.096774,0.129032,0.161290,0.193548,0.225806,0.258065, & 0.290323,0.322581,0.354839,0.387097,0.419355,0.451613,0.483871,0.516129, & 0.548387,0.580645,0.612903,0.645161,0.677419,0.709677,0.741935,0.774194, & 0.806452,0.838710,0.870968,0.903226,0.935484,0.967742,1.000000/) redarr(1:nset) = (/0.267004, & 0.277018,0.282327,0.282884,0.278012,0.269308,0.257322,0.243113,0.225863, & 0.210503,0.195860,0.182256,0.168126,0.156270,0.144759,0.133743,0.123463, & 0.119423,0.124780,0.143303,0.180653,0.226397,0.281477,0.344074,0.421908, & 0.496615,0.575563,0.657642,0.751884,0.835270,0.916242,0.993248/) greenarr(1:nset) = (/0.004874, & 0.050344,0.094955,0.135920,0.180367,0.218818,0.256130,0.292092,0.330805, & 0.363727,0.395433,0.426184,0.459988,0.489624,0.519093,0.548535,0.581687, & 0.611141,0.640461,0.669459,0.701402,0.728888,0.755203,0.780029,0.805774, & 0.826376,0.844566,0.860219,0.874951,0.886029,0.896091,0.906157/) bluearr(1:nset) = (/0.329415, & 0.375715,0.417331,0.453427,0.486697,0.509577,0.526563,0.538516,0.547314, & 0.552206,0.555276,0.557120,0.558082,0.557936,0.556572,0.553541,0.547445, & 0.538982,0.527068,0.511215,0.488189,0.462789,0.432552,0.397381,0.351910, & 0.306377,0.256415,0.203082,0.143228,0.102646,0.100717,0.143936/) case(37) !--ocean (cut from pyplot colormap terrain from 0->0.25) nset = 32 lumarr(1:nset) = (/0.000000, & 0.032258,0.064516,0.096774,0.129032,0.161290,0.193548,0.225806,0.258065, & 0.290323,0.322581,0.354839,0.387097,0.419355,0.451613,0.483871,0.516129, & 0.548387,0.580645,0.612903,0.645161,0.677419,0.709677,0.741935,0.774194, & 0.806452,0.838710,0.870968,0.903226,0.935484,0.967742,1.000000/) redarr(1:nset) = (/0.2, & 0.18954248,0.17908497,0.16862745,0.15816993,0.14771242,0.1372549 ,0.12679739,0.11633987, & 0.10588235,0.09542484,0.08496732,0.0745098 ,0.06405229,0.05359477,0.04313725,0.02745098, & 0.01699346,0.00653595,0. ,0. ,0. ,0. ,0. ,0. , & 0. ,0. ,0. ,0. ,0. ,0. ,0.00392157/) greenarr(1:nset) = (/0.2, & 0.22091503,0.24183007,0.2627451 ,0.28366013,0.30457516,0.3254902 ,0.34640523,0.36732026, & 0.38823529,0.40915033,0.43006536,0.45098039,0.47189542,0.49281046,0.51372549,0.54509804, & 0.56601307,0.5869281 ,0.60588235,0.62156863,0.6372549 ,0.65294118,0.66862745,0.68431373, & 0.7 ,0.71568627,0.73137255,0.74705882,0.7627451 ,0.77843137,0.80078431 /) bluearr(1:nset) = (/0.6, & 0.62091503,0.64183007,0.6627451 ,0.68366013,0.70457516,0.7254902 ,0.74640523,0.76732026, & 0.78823529,0.80915033,0.83006536,0.85098039,0.87189542,0.89281046,0.91372549,0.94509804, & 0.96601307,0.9869281 ,0.98235294,0.93529412,0.88823529,0.84117647,0.79411765,0.74705882, & 0.7 ,0.65294118,0.60588235,0.55882353,0.51176471,0.46470588,0.40078431/) case(38) !--casa blue nset = 28 lumarr(1:nset) = (/0.000,0.012,0.020,0.047,0.055,0.082,0.098,0.109,0.152,0.168,& 0.180,0.223,0.273,0.305,0.375,0.395,0.402,0.426,0.496,0.504,& 0.574,0.598,0.605,0.676,0.781,0.848,0.992,1.000/) redarr(1:nset) = (/0.996,0.957,0.918,0.816,0.796,0.706,0.655,0.620,0.518,0.471,& 0.447,0.369,0.286,0.247,0.173,0.157,0.145,0.133,0.090,0.078,& 0.039,0.031,0.027,0.016,0.004,0.000,0.000,0.000/) greenarr(1:nset)= (/0.996,0.957,0.918,0.816,0.796,0.706,0.655,0.620,0.518,0.471,& 0.447,0.369,0.286,0.247,0.173,0.157,0.145,0.133,0.090,0.078,& 0.039,0.031,0.027,0.016,0.004,0.000,0.000,0.000/) bluearr(1:nset) = (/1.000,0.988,0.976,0.941,0.937,0.910,0.886,0.878,0.835,0.816,& 0.804,0.761,0.714,0.682,0.612,0.592,0.580,0.561,0.494,0.482,& 0.416,0.400,0.384,0.318,0.231,0.165,0.008,0.000/) case(39) !--green-grey-brown nset = 3 lumarr(1:nset) = (/0.0,0.5,1.0/) redarr(1:nset) = (/0.0,0.66,0.5/) greenarr(1:nset) = (/0.83,0.66,0.33/) bluearr(1:nset) = (/0.33,0.66,0.17/) end select if (debugmode) print*,'DEBUG: setting colour table' call plot_ctab(lumarr(1:nset),redarr(1:nset),greenarr(1:nset),bluearr(1:nset), & nset,contrast,brightness) endif ! !--if icolourscheme = ncolourschemes+1 set the PGPLOT colour indices ! from the contents of the rgbtable array ! if (abs(icolourscheme).eq.ncolourschemes+1) then call plot_scir(ifirstcolour,ifirstcolour+ncolourmax) do i=1,ncolourmax index = ifirstcolour + (i-1) call plot_scr(index,rgbtable(1,i),rgbtable(2,i),rgbtable(3,i)) enddo print "(1x,a)",'using colour scheme other' elseif (abs(icolourscheme).le.ncolourschemes) then ! !--also store the colour table as a list of r,g,b values ! if (debugmode) print*,'DEBUG: querying colour table' do i=1,ncolours+1 index = ifirstcolour + (i-1) call plot_qcr(index,rgbtable(1,i),rgbtable(2,i),rgbtable(3,i)) enddo if (icolourscheme.lt.0) then print "(1x,a)",'using colour scheme inverse '//trim(schemename(abs(icolourscheme))) else print "(1x,a)",'using colour scheme '//trim(schemename(icolourscheme)) endif else print "(1x,a)",'warning: unknown colour scheme - uses default greyscale' endif if (debugmode) print*,'DEBUG: finished colour_set' return end subroutine colour_set !------------------------------------------------ ! demonstration plot of all the colour schemes !------------------------------------------------ subroutine colour_demo implicit none ! integer :: i,j,nc ! !--npixx should be >= ncolours in setcolours.f ! ! integer, parameter :: npixx = ncolourmax ! integer, parameter :: npixy = npixx/10 ! real, dimension(npixx,npixy) :: sample ! real :: xmin,xmax,ymin,ymax,dx,dy,trans(6) ! character(len=10) :: string ! call pgbegin(0,'?',1,ncolourschemes) !! call pgpaper(6.0,8.0) !!!0.25/sqrt(2.)) ! xmin = 0.0 ! xmax = 1.0 ! ymin = 0.0 ! ymax = 0.1 ! dx = (xmax-xmin)/float(npixx) ! dy = (ymax-ymin)/float(npixy) ! trans(1) = xmin - 0.5*dx ! trans(2) = dx ! trans(3) = 0.0 ! trans(4) = ymin - 0.5*dy ! trans(5) = 0.0 ! trans(6) = dy ! do j=1,npixy ! do i=1,npixx ! sample(i,j) = (i-1)*dx ! enddo ! enddo !! call pgsch(2.0) !! call pgenv(xmin,xmax,ymin,ymax,0,-1) !! call pgsch(1.0) !! call pggray(sample,npixx,npixy,1,npixx,1,npixy, & !! minval(sample),maxval(sample),trans) !! call pgnumb(1,0,0,string,nc) !! call pgsch(7.0) !! call pgmtxt('t',0.5,0.5,0.5,string(1:nc)//': '//trim(schemename(1))) ! do i=1,ncolourschemes ! call pgsch(2.0) ! call pgenv(xmin,xmax,ymin,ymax,0,-1) ! call pgsch(7.0) ! call pgnumb(i,0,0,string,nc) ! call pgmtxt('t',0.5,0.5,0.5,string(1:nc)//': '//trim(schemename(i))) ! call colour_set(i) ! call pgimag(sample,npixx,npixy,1,npixx,1,npixy, & ! minval(sample),maxval(sample),trans) ! enddo ! call pgsch(1.0) ! call pgend end subroutine colour_demo end module colours splash/src/read_data_urban.f90000644 000766 000000 00000022412 13261626263 017167 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM ANDREA URBAN'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,maxpart,maxcol,maxstep,time,gamma use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation, only:alloc !use system_utils, only:lenvironment,ienvironment use asciiutils, only:get_ncolumns implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,ierr,lab,icol,ilen integer :: nprint,npart_max,nstep_max integer :: ncol,nerr,nheaderlines logical :: iexist,timeset,gammaset real :: dummyreal character(len=len(rootname)+5) :: dumpfile integer, parameter :: iunit = 15 nstepsread = 0 nstep_max = 0 npart_max = maxpart dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 !--number of columns to read from file ! this is determined automatically !ncol = 13 j = indexstart nstepsread = 0 print "(a)",' reading Andrea Urban ascii file format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the file and read the number of particles ! open(unit=iunit,file=dumpfile,status='old',form='formatted',iostat=ierr) if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return else call get_ncolumns(iunit,ncol,nheaderlines) ncol = max(ncol - 1,0) if (ncol.le.0) then print "(a,/)",' *** no data read from file ***' return endif ! !--allocate memory initially ! nprint = 10001 nstep_max = max(nstep_max,indexstart,1) if (.not.allocated(dat) .or. (nprint.gt.npart_max) .or. (ncol+ncalc).gt.maxcol) then npart_max = max(npart_max,nprint) call alloc(npart_max,nstep_max,ncol+ncalc) endif endif npart_max = max(npart_max,nprint) ncolumns = ncol ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+1,maxcol) endif ! !--read header lines, try to use it to set time ! timeset = .false. gammaset = .false. if (nheaderlines.gt.0) then print*,'skipping ',nheaderlines,' header lines' do i=1,nheaderlines read(iunit,*,iostat=ierr) dummyreal if (timeset .and. .not.gammaset .and. ierr.eq.0 & .and. dummyreal.gt.0.999999 .and. dummyreal.lt.2.000001) then print*,'setting gamma = ',dummyreal,' from header line ',i gamma(j) = dummyreal gammaset = .true. endif if (ierr.eq.0 .and. .not. timeset) then time(j) = dummyreal timeset = .true. print*,'setting time = ',dummyreal,' from header line ',i endif enddo endif ! !--now read the timestep data in the dumpfile ! dat(:,:,j) = 0. ! time(j) = -1.0 ! time not read ! !--now read the timestep data in the dumpfile ! i = 0 ierr = 0 nerr = 0 overparts: do while (ierr >= 0) i = i + 1 if (i.gt.npart_max) then ! reallocate memory if necessary npart_max = 10*npart_max call alloc(npart_max,nstep_max,ncol+ncalc) endif read(iunit,*,iostat=ierr) (dat(i,icol,j),icol = 1,10),lab,(dat(i,icol,j),icol=11,ncol) if (ierr > 0) then nerr = nerr + 1 if (nerr .le. 10) print "(a,i8,a)",' ERROR reading data from line ',i+nheaderlines,', skipping' i = i - 1 ! ignore lines with errors endif enddo overparts close(iunit) nprint = i - 1 nstepsread = nstepsread + 1 if (nerr > 10) then print "(a,i8,a)",' *** WARNING: errors whilst reading file on ',nerr,' lines: skipped these ***' endif if (ierr < 0) then print*,'end of file: npart = ',nprint endif npartoftype(:,j) = 0 npartoftype(1,j) = nprint ! !--now open the sink particle file and read it ! !--find the last underscore in the file name ilen = index(rootname,'_',back=.true.) if (ilen.le.0) ilen = len_trim(rootname) + 1 dumpfile = rootname(1:ilen-1)//'_S' inquire(file=trim(dumpfile),exist=iexist) if (iexist) then open(unit=iunit+1,file=trim(dumpfile),form='formatted',status='old',iostat=ierr) if (ierr.ne.0) then print "(a)",' ERROR: could not open sink particle file '//trim(dumpfile) else i = npartoftype(1,j) ierr = 0 nerr = 0 oversinks: do while (ierr >= 0) i = i + 1 if (i.gt.npart_max) then ! reallocate memory if necessary npart_max = npart_max + 1000 call alloc(npart_max,nstep_max,ncol+ncalc) endif read(iunit+1,*,iostat=ierr) (dat(i,icol,j),icol = 1,10) if (ierr > 0) then nerr = nerr + 1 if (nerr .le. 10) print "(a,i8,a)",' ERROR reading sink data from line ',i+nheaderlines,', skipping' i = i - 1 ! ignore lines with errors endif enddo oversinks endif npartoftype(2,j) = i - 1 - npartoftype(1,j) print "(a,i8,a)",' read ',npartoftype(2,j),' sink particles from '//trim(dumpfile) close(iunit+1) else print "(a)",' sink particle file ('//trim(dumpfile)//') not present' endif ! !--look for a _t file for the time (interim measure) ! dumpfile = rootname(1:ilen-1)//'_t' inquire(file=trim(dumpfile),exist=iexist) if (iexist) then open(unit=iunit+2,file=trim(dumpfile),form='formatted',status='old',iostat=ierr) if (ierr.ne.0) then print "(a)",' ERROR: could not open time file '//trim(dumpfile) else read(iunit+2,*,iostat=ierr) time(j) if (ierr.ne.0) then print "(a)",' ERROR reading time from file '//trim(dumpfile) else print*,' got time = ',time(j),' from file '//trim(dumpfile) endif endif close(iunit+2) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,labelvec,labeltype,iamvec,& ix,ivx,ih,irho,iutherm,ipmass use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord !use settings_units, only:units,unitslabel implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ipmass = 7 ih = 8 irho = 9 iutherm = 10 label(ix(1:ndim)) = labelcoord(1:ndim,1) label(ipmass) = 'particle mass' label(ih) = 'h' label(irho) = 'density' if (iutherm.gt.0) label(iutherm) = 'u' label(11) = 't\ddust\u' label(12) = 'N\dcol\u' label(13) = 'N\dloc\u' if (ivx.ne.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 2 labeltype(1) = 'gas' labeltype(2) = 'sink' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_VINE.f90000644 000766 000000 00000045020 13261626263 016621 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM THE VINE CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! VINE_MHD or VSPLASH_MHD if 'YES' or 'TRUE', reads MHD dump files ! VINE_HFAC or VSPLASH_HFAC if 'YES' or 'TRUE', multiplies the ! smoothing lengths read from the file by a factor of 2.8 for ! compatibility with older VINE dump files. ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- module vineread implicit none ! ! These are the indices of various values saved in ! the header part of the dump file. ! ! SHOULD BE IDENTICAL TO THOSE DEFINED IN THE VINE io.F FILE... ! integer,parameter::id_iheadlen= 1, id_npart = 2, id_npart_sph = 3 integer,parameter::id_nstep = 4, id_idump = 5, id_makechkpnt= 6 integer,parameter::id_idbg = 7, id_itme = 8, id_ibctype = 9 integer,parameter::id_indts =10, id_iusebin=11, id_ipres =12 integer,parameter::id_ipdv =13, id_ieos =14, id_icool =15 integer,parameter::id_iheat =16, id_ivisc =17, id_ivtime =18 integer,parameter::id_ibals =19, id_ishok =20, id_igrav =21 integer,parameter::id_isoft =22, id_imac =23, id_ihts =24 integer,parameter::id_iexpand =25, id_ncdumps=26, id_maxclumpsize=27 integer,parameter::id_intgrtr =28, id_ishift =29, id_ndim =30 integer,parameter::id_io_fmt =31, id_iunits =32, id_maxbunchsize=33 integer,parameter::id_npoim =34, id_ndt_ana=35, id_maxbuild =36 integer,parameter::id_fullextrap=37,id_revise=38, id_n_dtmax =39 integer,parameter::id_n_ana =40 integer,parameter::id_lastint1=41 !1 after last id for integers integer, parameter::id_t = 1,id_dtmax = 2,id_deltnew = 3 integer, parameter::id_gamma = 4,id_ekin = 5,id_egrav = 6 integer, parameter::id_etherm = 7,id_ascale = 8,id_cosbox = 9 integer, parameter::id_vlength =10,id_alfstar =11,id_betastar =12 integer, parameter::id_tol =13,id_cfl =14,id_dhmax =15 integer, parameter::id_treeacc =16,id_sepmax =17,id_hmin =18 integer, parameter::id_eps =19,id_dtinit =20,id_tstop =21 integer, parameter::id_dt_out =22,id_uconst =23,id_xmas =24 integer, parameter::id_ylen =25,id_umin =26,id_rcore =27 integer, parameter::id_h_0 =28,id_omegam =29,id_gmasslim =30 integer, parameter::id_hmax =31,id_uexpon =32,id_ftol =33 integer, parameter::id_clhfrac =34,id_xmin =35,id_xmax =36 integer, parameter::id_ymin =37,id_ymax =38,id_zmin =39 integer, parameter::id_zmax =40 integer, parameter::id_eosK =41,id_rhozero =42,id_ftolpm =43 integer, parameter::id_tolpm =44,id_tnextout=45,id_alfmax =46 integer, parameter::id_vtol =47,id_vtolpm =48,id_dtmin =49 integer, parameter::id_tlastout=50,id_dt_ana =51,id_bsepmax =52 integer, parameter::id_taucool =53 integer, parameter::id_lastreal1=54 !1 after last id for real numbers end module vineread subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:npartoftype,dat,time,gamma,maxcol,maxpart,maxstep use params, only:doub_prec use settings_data, only:ndim,ndimV,ncolumns,ncalc use labels, only:ivx, iBfirst,ih,ipmass use mem_allocation, only:alloc use system_utils, only:lenvironment use vineread, only:id_gamma,id_iheadlen,id_ndim,id_npart,id_npart_sph,& id_npoim,id_t implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: iheadlength integer :: i,j,ierr,nparti,ntoti,i1,icol integer :: npart_max,nstep_max,ncolstep,nptmass logical :: iexist,mhdread,useipindx character(len=len(rootname)+10) :: dumpfile integer, parameter :: maxheadlength = 1000 integer, dimension(maxheadlength) :: iheader integer, dimension(:), allocatable :: ipindx,itstepbin !--we are assuming dump is double precision real(doub_prec), dimension(maxheadlength) :: dheader real(doub_prec), dimension(:,:), allocatable :: dattemp, dattempvec real :: dum real :: hfactor nstepsread = 0 npart_max = maxpart !--this is the default header length iheadlength = maxheadlength dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 mhdread = .false. if (lenvironment('VSPLASH_MHD') .or. lenvironment('VINE_MHD')) then mhdread = .true. endif if (lenvironment('VSPLASH_HFAC') .or. lenvironment('VINE_HFAC')) then hfactor = 2.8 else hfactor = 1.0 endif nstep_max = max(indexstart,1) j = indexstart nstepsread = 0 nparti = 0 ncolstep = 0 write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) if (mhdread) then print "(a)",' reading VINE MHD format' else print "(a)",' reading default VINE format (set VINE_MHD=yes for MHD)' endif ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' else ! !--read timestep header (integers only) ! read(15,iostat=ierr) (iheader(i),i=1,iheadlength) ! !--get number of particles from header and allocate memory ! iheadlength = iheader(id_iheadlen) if (iheadlength.gt.maxheadlength) print "(a)",' ERROR: header length too big!' ntoti = iheader(id_npart ) nparti = iheader(id_npart_sph) nptmass = iheader(id_npoim ) ndim = iheader(id_ndim ) if (ntoti.lt.nparti) then print*,' *** WARNING: ntotal < npart_sph in header, setting n_total=n_sph' ntoti = nparti endif if (nptmass.lt.0) then print*,' *** WARNING: error in nptmass read from header, nptmass = ',nptmass,' setting to 0' nptmass = 0 endif if (nparti.le.0) then print*,' *** WARNING: error in npart read from header, npart = ',nparti ierr = 2 endif if (ndim.le.0 .or. ndim.gt.3) then print*,' *** WARNING: error in ndim read from header, ndim = ',ndim ierr = 1 endif ndimV = ndim if (ndim.ne.3) print "(a,i1)",' number of dimensions = ',ndim if (mhdread) then ncolstep = 2*ndim + 6 + ndim else ncolstep = 2*ndim + 6 endif ncolumns = ncolstep if ((.not.allocated(dat) .or. ntoti+nptmass.gt.npart_max) .and. ierr.eq.0) then if (.not.allocated(dat)) then npart_max = ntoti + nptmass else npart_max = max(npart_max,INT(1.1*(ntoti+nptmass))) endif call alloc(npart_max,nstep_max,ncolstep+ncalc) endif ! !--rewind file ! rewind(15) endif if (ierr /= 0) then print "(/,a)", ' *** ERROR READING TIMESTEP HEADER: wrong endian? ***' print "(/,a)", ' (see splash userguide for compiler-dependent' print "(a)", ' ways to change endianness on the command line)' print "(/,a)", ' (set environment variable VINE_MHD to yes or TRUE ' print "(a,/)", ' if you are trying to read MHD format)' else npart_max = max(npart_max,ntoti) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+2*nstepsread,maxcol) endif ! !--allocate a temporary array for double precision variables ! if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(npart_max,ncolstep),stat=ierr) dattemp = 0. if (ierr /= 0) print*,'not enough memory in read_data (dattemp)' ! !--allocate a temporary array for vectors ! if (allocated(dattempvec)) deallocate(dattempvec) if (mhdread) then allocate(dattempvec(3*ndim+1,npart_max),stat=ierr) else allocate(dattempvec(2*ndim+1,npart_max),stat=ierr) endif dattempvec = 0. if (ierr /= 0) print*,'not enough memory in read_data (dattempvec)' ! !--allocate a temporary array for particle index ! if (allocated(ipindx)) deallocate(ipindx) allocate(ipindx(npart_max),stat=ierr) !ipindx = 0 if (ierr /= 0) print*,'not enough memory in read_data (ipindx)' ! !--allocate a temporary array for itstepbin (MHD or point masses only) ! if (mhdread .or. nptmass.gt.0) then if (allocated(itstepbin)) deallocate(itstepbin) allocate(itstepbin(npart_max),stat=ierr) !itstepbin = 0 if (ierr /= 0) print*,'not enough memory in read_data (itstepbin)' endif ! !--now read the timestep data in the dumpfile ! write(*,"(a,i5,a)",advance="no") '| step ',j,': ' ivx = ndim + 2 ! location of vx in 'columns' ! starting point for non position and velocity columns icol = ndim + 1 + ndimV + 1 if (mhdread) then if (nptmass.gt.0) then print "(a)",' WARNING: MHD format but point masses are present' print "(a)",' and reading of point masses is not implemented' print "(a)",' *** Please email a copy of io.F so I can fix this *** ' endif iBfirst = icol+5 read(15,iostat=ierr) & (iheader(i),i=1,iheadlength), & (dheader(i),i=1,iheadlength), & (dattempvec(1:ndim+1,i),i=1,ntoti), & (dattempvec(ivx:ivx+ndimV-1,i),i=1,ntoti), & (dattemp(i,icol), i=1,ntoti), & (dattemp(i,icol+1), i=1,nparti), & (dattemp(i,icol+2), i=1,nparti), & (dattemp(i,icol+3), i=1,nparti), & (dattemp(i,icol+4), i=1,ntoti), & (ipindx(i), i=1,ntoti), & (itstepbin(i),i=1,ntoti), & (dattempvec(ivx+ndimV:ivx+2*ndimV-1,i),i=1,nparti) else if (nptmass.gt.0) then ! !--read point mass information at the end of the dump file ! read(15,iostat=ierr) & (iheader(i),i=1,iheadlength), & (dheader(i),i=1,iheadlength), & (dattempvec(1:ndim+1,i),i=1,ntoti), & (dattempvec(ivx:ivx+ndimV-1,i),i=1,ntoti), & (dattemp(i,icol), i=1,ntoti), & (dattemp(i,icol+1), i=1,nparti), & (dattemp(i,icol+2), i=1,nparti), & (dattemp(i,icol+3), i=1,nparti), & (dattemp(i,icol+4), i=1,ntoti), & (ipindx(i), i=1,ntoti), & (dum, i=1,nparti), & (itstepbin(i), i=1,ntoti), & (dattempvec(1:ndim+1,i),i=ntoti+1,ntoti+nptmass), & (dattempvec(ivx:ivx+ndimV-1,i),i=ntoti+1,ntoti+nptmass), & ((dum, i1=1,3),i=1,nptmass), & (dattemp(i,icol), i=ntoti+1,ntoti+nptmass) else ! !--no point masses, so shorter read ! read(15,iostat=ierr) & (iheader(i),i=1,iheadlength), & (dheader(i),i=1,iheadlength), & (dattempvec(1:ndim+1,i),i=1,ntoti), & (dattempvec(ivx:ivx+ndimV-1,i),i=1,ntoti), & (dattemp(i,icol), i=1,ntoti), & (dattemp(i,icol+1), i=1,nparti), & (dattemp(i,icol+2), i=1,nparti), & (dattemp(i,icol+3), i=1,nparti), & (dattemp(i,icol+4), i=1,ntoti), & (ipindx(i), i=1,ntoti) endif endif if (ierr < 0) then print "(a)",'*** END OF FILE IN READ DATA ***' elseif (ierr /= 0) then if (mhdread) then print "(a)",'*** ERROR READING DATA: MAYBE NOT AN MHD FILE?? ***' else print "(a)",'*** ERROR READING DATA ***' print "(/,a)", ' (set environment variable VINE_MHD to yes or TRUE ' print "(a,/)", ' if you are trying to read MHD format)' endif endif nstepsread = nstepsread + 1 ! !--spit out time ! time(j) = real(dheader(id_t )) gamma(j) = real(dheader(id_gamma)) print "(a,es10.3,3(a,i8))",'t = ',time(j),' n(SPH) = ',ntoti,' n(Nbody) = ',ntoti-nparti,' n(star) = ',nptmass ! !--check sanity of ipindx array: do not sort particles if values not sensible ! useipindx = .true. if (any(ipindx(1:ntoti).le.0 .or. ipindx(1:ntoti).gt.ntoti)) then print*,'WARNING: ipindx array has values < 0 or > ntot: particles not sorted' useipindx = .false. endif ! !--convert posm and velocity vectors to columns and double to single precision ! do i=1,2*ndim+1 if (useipindx) then dat(ipindx(1:ntoti),i,j) = real(dattempvec(i,1:ntoti)) else dat(1:ntoti,i,j) = real(dattempvec(i,1:ntoti)) endif enddo if (nptmass.gt.0) then do i=1,2*ndim+1 dat(ntoti+1:ntoti+nptmass,i,j) = real(dattempvec(i,ntoti+1:ntoti+nptmass)) enddo endif ! !--convert B vectors to columns and double to single precision ! if (mhdread) then i1 = iBfirst - 1 do i=ivx+ndimV,ivx+2*ndimV-1 i1 = i1 + 1 if (useipindx) then dat(ipindx(1:nparti),i1,j) = real(dattempvec(i,1:nparti)) else dat(1:nparti,i1,j) = real(dattempvec(i,1:nparti)) endif enddo endif ! !--now convert scalars ! if (useipindx) then dat(ipindx(1:ntoti),icol:ncolstep,j) = real(dattemp(1:ntoti,icol:ncolstep)) else dat(1:ntoti,icol:ncolstep,j) = real(dattemp(1:ntoti,icol:ncolstep)) endif if (nptmass.gt.0) then dat(ntoti+1:ntoti+nptmass,icol,j) = real(dattemp(ntoti+1:ntoti+nptmass,icol)) endif call set_labels if (ih.gt.0 .and. hfactor.gt.1.0) then dat(1:ntoti+nptmass,ih,j) = hfactor*dat(1:ntoti+nptmass,ih,j) endif if (nptmass.lt.10) then do i=1,nptmass if (ndim.eq.2) then print "('| point mass ',i1,': pos = (',es10.2,',',es10.2,'), mass = ',es10.2)", & i,dat(ntoti+i,1:ndim,j),dat(ntoti+i,ipmass,j) else print "('| point mass ',i1,': pos = (',2(es10.2,','),es10.2,'), mass = ',es10.2)", & i,dat(ntoti+i,1:ndim,j),dat(ntoti+i,ipmass,j) endif enddo endif ! !--set particle numbers ! npartoftype(1,j) = nparti npartoftype(2,j) = ntoti - nparti npartoftype(3,j) = nptmass ! !--clean up ! if (allocated(dattemp)) deallocate(dattemp) if (allocated(dattempvec)) deallocate(dattempvec) if (allocated(ipindx)) deallocate(ipindx) if (allocated(itstepbin)) deallocate(itstepbin) endif ! !--reached end of file ! close(15) if (nstepsread .gt. 0) then print*,'>> end of dump file: ntotal = ',sum(npartoftype(:,j)) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,ih,ipmass,ivx,iutherm,irho,ix,iBfirst, & labelvec,iamvec,labeltype use params use settings_data, only:ndim,ndimV,UseTypeInRenderings,ntypes use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ipmass = ndim+1 ! particle mass ivx = ndim+2 ih = ndim + 1 + ndimV + 1 ! smoothing length iutherm = ih+1 ! thermal energy irho = iutherm+1 ! location of rho in data array label(ix(1:ndim)) = labelcoord(1:ndim,1) label(irho) = 'density' label(iutherm) = 'u' label(ih) = 'h' label(ipmass) = 'particle mass' label(irho+1) = 'alpha' label(irho+2) = 'potential energy' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 3 labeltype(1) = 'gas' labeltype(2) = 'Nbody' labeltype(3) = 'point mass' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. UseTypeInRenderings(3) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/options_data.f90000644 000766 000000 00000021331 13261626263 016557 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing settings and options related to the data read ! includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module options_data implicit none public :: submenu_data,defaults_set_data private contains !--------------------------------------------- ! set default values for these options ! (most should be set upon call to read_data) !--------------------------------------------- subroutine defaults_set_data use settings_data use params, only:maxplot implicit none integer :: i numplot=maxplot ! reset if read from file ncalc = 0 ! number of columns to calculate(e.g. radius) nextra = 0 ! extra plots aside from particle data ncolumns=maxplot-ncalc ! number of columns in data file ndataplots = ncolumns ndim = 0 ! number of coordinate dimensions ndimV = ndim ! default velocity same dim as coords istartatstep = 1 ! timestep to start from iendatstep = 1000 ! timestep to finish on nfreq = 1 ! frequency of timesteps to read icoords = 1 ! coordinate system of simulation iformat = 0 ! file format buffer_data = .false. iUseStepList = .false. do i=1,size(isteplist) isteplist(i) = i enddo iCalcQuantities = .false. DataIsBuffered = .false. iRescale = .false. ivegotdata = .false. ntypes = 1 xorigin = 0. itracktype = 0 ! particle tracking limits (none) itrackoffset = 0 ipartialread = .false. ! strictly unnecessary as set in get_data iverbose = 1 return end subroutine defaults_set_data !---------------------------------------------------------------------- ! sets options relating to current data ! (read new data or change timesteps plotted) !---------------------------------------------------------------------- subroutine submenu_data(ichoose) use filenames, only:nsteps,nstepsinfile,ifileopen,unitsfile use prompting, only:prompt,print_logical use getdata, only:get_data,get_labels use settings_data, only:istartatstep,iendatstep,nfreq,iUseStepList, & isteplist,buffer_data,iCalcQuantities,iRescale, & DataIsBuffered,numplot,ncalc,ncolumns use calcquantities, only:calc_quantities,setup_calculated_quantities use limits, only:set_limits use labels, only:label,unitslabel,labelzintegration use settings_units, only:units,set_units,write_unitsfile,unitzintegration implicit none integer, intent(in) :: ichoose integer :: ians, i character(len=30) :: fmtstring logical :: ireadnow,UnitsHaveChanged,iRescaleprev,iwriteunitsfile ians = ichoose print "(a)",'----------------- data read options -------------------' if (ians.le.0 .or. ians.gt.8) then if (iUseStepList) then print 10, iendatstep,print_logical(iUseStepList),print_logical(buffer_data), & print_logical(iCalcQuantities),print_logical(iRescale) else print 10, (iendatstep-istartatstep+1)/nfreq,print_logical(iUseStepList), & print_logical(buffer_data),print_logical(iCalcQuantities), & print_logical(iRescale) endif 10 format( & ' 0) exit ',/, & ' 1) read new data /re-read data',/, & ' 2) change number of timesteps used ( ',i5, ' )',/, & ' 3) plot selected steps only ( ',a,' )',/, & ' 4) buffering of data on/off ( ',a,' )',/, & ' 5) turn calculate extra quantities on/off ( ',a,' )',/, & ' 6) edit list of calculated quantities ',/, & ' 7) use physical units ( ',a,' )',/,& ' 8) change physical unit settings ') call prompt('enter option',ians,0,8) endif ! !--options ! select case(ians) !------------------------------------------------------------------------ case(1) if (buffer_data) then call get_data(-1,.false.) else call get_data(1,.false.,firsttime=.true.) endif !------------------------------------------------------------------------ case(2) iUseStepList = .false. call prompt('Start at timestep ',istartatstep,1,nsteps) call prompt('End at timestep ',iendatstep,istartatstep,nsteps) call prompt(' Frequency of steps to read',nfreq,1,nsteps) print *,' Steps = ',(iendatstep-istartatstep+1)/nfreq !------------------------------------------------------------------------ case(3) iUseStepList = .true. istartatstep = 1 nfreq = 1 iendatstep = min(iendatstep,size(isteplist),nsteps) call prompt('Enter number of steps to plot ', & iendatstep,1,size(isteplist)) do i=1,iendatstep if (isteplist(i).le.0 .or. isteplist(i).gt.nsteps) isteplist(i) = i write(fmtstring,"(a,i2)") 'Enter step ',i call prompt(fmtstring,isteplist(i),1,nsteps) enddo !------------------------------------------------------------------------ case(4) buffer_data = .not.buffer_data print "(/a)",' Buffering of data = '//print_logical(buffer_data) if (buffer_data) then call prompt('Do you want to read all files into memory now?',ireadnow) if (ireadnow) then call get_data(-1,.true.) endif endif !------------------------------------------------------------------------ case(5,6) if (ians.eq.5) iCalcQuantities = .not.iCalcQuantities if (iCalcQuantities .or. ians.eq.6) then call setup_calculated_quantities(ncalc) if (ians.eq.6 .and. .not.iCalcQuantities) then if (ncalc.gt.0) iCalcQuantities = .true. endif if (iCalcQuantities) then if (DataIsBuffered) then call calc_quantities(1,nsteps) call set_limits(1,nsteps,ncolumns+1,ncolumns+ncalc) else if (ifileopen.gt.0) then call calc_quantities(1,nstepsinfile(ifileopen)) call set_limits(1,nstepsinfile(ifileopen),ncolumns+1,ncolumns+ncalc) endif endif endif else print "(/a)",' Calculation of extra quantities is '//print_logical(iCalcQuantities) endif !------------------------------------------------------------------------ case(7) print "(a)",'current settings for conversion to physical units are:' call get_labels ! reset labels for printing do i=1,ncolumns print "(a,a3,a,a3,es10.3)",trim(label(i))//trim(unitslabel(i)),' = ',trim(label(i)),' x ',units(i) enddo print "(a,a3,a,a3,es9.2)",'time'//trim(unitslabel(0)),' = ','time',' x ',units(0) print "(a,a3,a,a3,es9.2)",'dz '//trim(labelzintegration),' = ','dz',' x ',unitzintegration iRescaleprev = iRescale iRescale = .not.iRescale call prompt('Use physical units?',iRescale) if ((iRescale .and..not. iRescaleprev) .or. (iRescaleprev .and..not.iRescale)) then if (buffer_data) then call get_data(-1,.true.) else call get_data(1,.true.,firsttime=.true.) endif endif !------------------------------------------------------------------------ case(8) UnitsHaveChanged = .false. call set_units(ncolumns,numplot,UnitsHaveChanged) iwriteunitsfile = .true. call prompt(' save units to file? ',iwriteunitsfile) if (iwriteunitsfile) call write_unitsfile(trim(unitsfile),numplot) if (.not.iRescale .and. UnitsHaveChanged) call prompt('Apply physical units to data?',iRescale) ! !--re-read/rescale data if units have changed ! if (UnitsHaveChanged) then if (buffer_data) then call get_data(-1,.true.) else call get_data(1,.true.,firsttime=.true.) endif else call get_labels endif !------------------------------------------------------------------------ end select return end subroutine submenu_data end module options_data splash/src/read_data_mbate_mhd.f90000644 000766 000000 00000025765 13261626263 020016 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM MATTHEW BATE'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer, parameter :: maxptmass = 1000 real, parameter :: pi=3.141592653589 integer :: i,j,ifile,ierr integer :: npart_max,nstep_max,ncolstep logical :: iexist character(len=3) :: fileno character(len=len(rootname)+10) :: dumpfile integer :: nprint, nghosti, n1, n2, nptmass integer, dimension(:), allocatable :: isteps, iphase integer, dimension(maxptmass) :: listpm ! real(doub_prec), dimension(:,:), allocatable :: dattemp ! real(doub_prec), dimension(:), allocatable :: dummy ! real(doub_prec) :: udisti,umassi,utimei, umagfdi ! real(doub_prec) :: timei, gammai ! real(doub_prec) :: rhozero, RK2, tcomp ! real(doub_prec) :: escap,tkin,tgrav,tterm,tmag ! real(doub_prec) :: dtmax real, dimension(:,:), allocatable :: dattemp real, dimension(:), allocatable :: dummy real(doub_prec) :: udisti,umassi,utimei, umagfdi real :: timei, gammai real :: rhozero, RK2, tcomp real :: escap,tkin,tgrav,tterm,tmag real :: dtmax nstepsread = 0 nstep_max = 0 npart_max = maxpart ifile = 1 ! !--for rootnames without the '00', read all files starting at #1 ! if (len_trim(rootname).lt.7) then ifile = 1 if (len_trim(rootname).eq.4) then write(fileno,"(i1,i1,i1)") ifile/100,mod(ifile,100)/10,mod(ifile,10) dumpfile = rootname(1:4)//fileno elseif (len_trim(rootname).eq.5) then write(fileno,"(i1,i1)") ifile/10,mod(ifile,10) dumpfile = rootname(1:5)//trim(fileno) endif else dumpfile = trim(rootname) endif ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 ncolstep = 19 ncolumns = ncolstep !!max(19,maxcol) ! number of columns in file ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 print "(1x,a)",'reading Matthew Bate''s/Willy Benz''s old SPH code format (MHD)' do while (iexist) write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' else ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,end=55,iostat=ierr) udisti,umassi,utimei,umagfdi,nprint if (.not.allocated(dat) .or. nprint.gt.npart_max) then npart_max = max(npart_max,INT(1.1*nprint)) call alloc(npart_max,nstep_max,ncolstep+ncalc) endif rewind(15) endif if (ierr /= 0) then print "(a)",'*** ERROR READING TIMESTEP HEADER ***' else ! !--loop over the timesteps in this file ! over_steps_in_file: do npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+1,maxcol) endif ! !--allocate a temporary array for double precision variables ! if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(npart_max,ncolumns),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' ! !--allocate a dummy arrays for data I want to throw away ! if (allocated(dummy)) deallocate(dummy) allocate(dummy(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' if (allocated(isteps)) deallocate(isteps) allocate(isteps(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' if (allocated(iphase)) deallocate(iphase) allocate(iphase(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' ! !--now read the timestep data in the dumpfile ! write(*,"(a,i5,a)",advance="no") '| step ',j,': ' read(15,end=55,iostat=ierr) udisti, umassi, utimei, umagfdi, & nprint, nghosti, n1, n2, timei, gammai, rhozero, RK2, & (dattemp(i,7), i=1, nprint), (dattemp(i,8), i=1,nprint), & escap, tkin, tgrav, tterm, tmag, & (dattemp(i,1), i=1, nprint), (dattemp(i,2), i=1, nprint), & (dattemp(i,3), i=1, nprint), (dattemp(i,4), i=1, nprint), & (dattemp(i,5), i=1, nprint), (dattemp(i,6), i=1, nprint), & (dattemp(i,9), i=1, nprint), (dattemp(i,10), i=1, nprint), & (dattemp(i,11), i=1, nprint), (dattemp(i,12), i=1, nprint), & (dattemp(i,13), i=1, nprint), (dattemp(i,14), i=1, nprint), & (dattemp(i,15), i=1, nprint), (dattemp(i,16), i=1, nprint), & (dattemp(i,17), i=1, nprint), (dattemp(i,18), i=1, nprint), & (dattemp(i,19), i=1, nprint), (dummy(i),i=1,nprint), & dtmax, (isteps(i), i=1,nprint), (iphase(i),i=1,nprint), & nptmass, (listpm(i), i=1,nptmass) if (ierr /= 0) then print "(a)",'*** INCOMPLETE DATA (CHECK PRECISION) ***' exit over_steps_in_file else nstepsread = nstepsread + 1 endif ! !--convert to single precision ! print "(a,i8)",'ntotal = ',nprint print "(a)",'| converting to single precision... ' dat(1:nprint,1:ncolumns,j) = real(dattemp(1:nprint,1:ncolumns)) if (allocated(dattemp)) deallocate(dattemp) if (allocated(dummy)) deallocate(dummy) if (allocated(isteps)) deallocate(isteps) if (allocated(iphase)) deallocate(iphase) npartoftype(1,j) = nprint-nghosti npartoftype(2,j) = nghosti gamma(j) = real(gammai) tcomp = sqrt((3.*pi)/(32*rhozero)) time(j) = real(timei)/tcomp j = j + 1 enddo over_steps_in_file endif 55 continue ! !--reached end of file ! close(15) print*,'>> end of dump file: nsteps =',j-1 if (j-1.gt.0) then print*,'ntot = ',sum(npartoftype(:,j-1)),'nghost=',npartoftype(2,j-1) endif ! !--if just the rootname has been input, ! set next filename and see if it exists ! ifile = ifile + 1 if (len_trim(rootname).eq.4) then write(fileno,"(i1,i1,i1)") ifile/100,mod(ifile,100)/10,mod(ifile,10) dumpfile = rootname(1:4)//fileno inquire(file=dumpfile,exist=iexist) elseif (len_trim(rootname).eq.5) then write(fileno,"(i1,i1)") ifile/10,mod(ifile,10) dumpfile = rootname(1:5)//trim(fileno) inquire(file=dumpfile,exist=iexist) else iexist = .false. ! exit loop endif enddo return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 irho = 18 ! location of rho in data array iutherm = 16 ! thermal energy ih = 7 ! smoothing length ipmass = 17 ! particle mass label(ix(1:ndim)) = labelcoord(1:ndim,1) do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo label(irho) = '\gr' label(iutherm) = 'u' label(ih) = 'h ' label(ipmass) = 'particle mass' label(8) = 'alpha' label(19) = 'psi' label(ndim + ndimV+5) = '\ga' if (ncolumns.gt.11) then iBfirst = 9 ! location of Bx do i=1,ndimV label(iBfirst + i-1) = 'B\d'//labelcoord(i,1) !' (x10\u-3\d)' !//'/rho' enddo idivB = 12 label(idivB) = 'div B' do i=1,ndimV label(13 + i-1) = 'J'//labelcoord(i,1) enddo else iBfirst = 0 endif ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo !--mag field if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'\d'//labelcoord(i,1) enddo endif !--current density iamvec(13:13+ndimV-1) = 13 labelvec(13:13+ndimV-1) = 'J' do i=1,ndimV label(13+i-1) = trim(labelvec(13))//'\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 3 !!maxparttypes labeltype(1) = 'gas' labeltype(2) = 'ghost' labeltype(3) = 'sink' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_kitp.f90000644 000766 000000 00000013151 13261626263 017027 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT IN KITP FORMAT ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,time,npartoftype,gamma,maxpart,maxcol use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation, only:alloc implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,ierr integer :: np integer :: ncol,nread,nstep_max logical :: iexist character(len=len(rootname)) :: dumpfile real :: wp,timei nstepsread = 0 nstep_max = 0 dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 !--number of columns to read from file ncol = 7 ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 print "(1x,a)",'reading KITP SPH format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,file=dumpfile,status='old',form='unformatted',iostat=ierr) if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return else ! !--read the number of particles in the header and allocate memory ! read(15,iostat=ierr) np,wp timei = 0. print "(a,f10.2,a,i10,a,f10.4)",' time: ',timei,' npart: ',np,' wp: ',wp !--barf if stupid values read if (np.le.0 .or. np.gt.1e10) then print "(a)",' *** ERRORS IN TIMESTEP HEADER: WRONG ENDIAN? ***' close(15) return elseif (ierr /= 0) then print "(a)",'*** WARNING: ERRORS READING HEADER ***' close(15) return endif ncolumns = ncol if (.not.allocated(dat) .or. np.gt.maxpart) then call alloc(np,nstep_max,ncol+ncalc) endif ! !--now read the timestep data in the dumpfile ! dat(:,:,j) = 0. time(j) = 0. nread = 0 do i=1,ncol read(15,end=44,iostat=ierr) dat(1:np,i,j) if (ierr /= 0) print*,' error reading column ',i nread = nread + 1 enddo 44 continue if (nread.lt.ncol) then print "(a)",' WARNING: END OF FILE: read to column ',nread endif nstepsread = nstepsread + 1 npartoftype(1,j) = np gamma(j) = 1.666666666667 j = j + 1 endif close(15) if (allocated(npartoftype)) then print*,'>> end of dump file: nsteps =',j-1,'ntot = ',sum(npartoftype(:,j-1)) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,labelvec,labeltype,iamvec,& ix,ivx,ih,irho,iutherm,ipmass use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 irho = 7 label(ix(1:ndim)) = labelcoord(1:ndim,1) if (ivx.ne.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = labelvec(ivx)//'\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 1 labeltype(1) = 'gas' UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/options_xsecrotate.f90000644 000766 000000 00000071274 13261626263 020042 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing settings and options relating to cross sections, ! rotations and 3D plotting. ! Includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module settings_xsecrot implicit none !--public variables integer, public :: nframes,nseq integer, public :: nxsec,irotateaxes logical, public :: xsec_nomulti, irotate, flythru, use3Dperspective, use3Dopacityrendering logical, public :: writeppm, rendersinks real, public :: anglex, angley, anglez, zobserver, dzscreenfromobserver real, public :: taupartdepth,xsecwidth real, public :: xsecpos_nomulti,xseclineX1,xseclineX2,xseclineY1,xseclineY2 real, public, dimension(3) :: xorigin,xminrotaxes,xmaxrotaxes !--private variables related to animation sequences integer, parameter, private :: maxseq = 6 integer, dimension(maxseq), public :: iseqstart,iseqend,iseqtype integer, public :: icolchange real, public :: xminseqend,xmaxseqend,yminseqend,ymaxseqend real, public :: anglezend,angleyend,anglexend,zobserverend,taupartdepthend real, public :: xmincolend,xmaxcolend,xsecpos_nomulti_end logical, private :: ihavesetsequence character(len=*), dimension(maxseq), parameter, private :: labelseqtype = & (/'steady zoom on x and y axes ', & 'steady rotation ', & 'steady change of limits (e.g. for colour bar) ', & 'steady movement of 3D observer ', & 'sequence of cross section slices through a 3D box ', & 'steady change of opacity for 3D surface plots '/) !--namelists for writing to defaults file and .anim file public :: xsecrotopts namelist /xsecrotopts/ xsec_nomulti,xsecpos_nomulti,flythru, & xseclineX1,xseclineX2,xseclineY1,xseclineY2, & irotate,irotateaxes,anglex, angley, anglez, & xminrotaxes,xmaxrotaxes,use3Dperspective, & use3Dopacityrendering,zobserver,dzscreenfromobserver, & taupartdepth,writeppm,xsecwidth,rendersinks public :: animopts namelist /animopts/ nseq,nframes,iseqstart,iseqend,iseqtype, & xminseqend,xmaxseqend,yminseqend,ymaxseqend, & anglezend,angleyend,anglexend,zobserverend,taupartdepthend, & icolchange,xmincolend,xmaxcolend,xsecpos_nomulti_end !--public procedure names public :: defaults_set_xsecrotate,submenu_xsecrotate,getsequencepos,insidesequence public :: setsequenceend procedure(add_sequence), pointer :: addseq => null() procedure(delete_sequence), pointer :: delseq => null() procedure(check_sequences), pointer :: checkseq => null() private contains !--------------------------------------------- ! set default values for these options !--------------------------------------------- subroutine defaults_set_xsecrotate implicit none xsec_nomulti = .false. ! take cross section of data / particles xsecpos_nomulti = 0. ! position of cross section flythru = .false. ! take series of cross sections through data xseclineX1 = 0.0 xseclineX2 = 0.0 xseclineY1 = 0.0 xseclineY2 = 0.0 xsecwidth = 0.0 ! width of xsec slices - zero means suggest better value to user irotate = .false. irotateaxes = 0 anglex = 0. angley = 0. anglez = 0. xminrotaxes = 0. xmaxrotaxes = 0. use3Dperspective = .false. use3Dopacityrendering = .false. zobserver = 0. dzscreenfromobserver = 0. taupartdepth = 2. writeppm = .true. !--defaults for animation sequences nseq = 0 nframes = 0 iseqstart(:) = 0 iseqend(:) = 0 iseqtype(:) = 0 xminseqend = 0. xmaxseqend = 0. yminseqend = 0. ymaxseqend = 0. anglezend = 360. angleyend = 0. anglexend = 0. icolchange = 0 xmincolend = 0. xmaxcolend = 0. zobserverend = 0. taupartdepthend = 2000.0 xsecpos_nomulti_end = 0. ihavesetsequence = .false. rendersinks = .false. return end subroutine defaults_set_xsecrotate !---------------------------------------------------------------------- ! sets options relating to cross sectioning / rotation !---------------------------------------------------------------------- subroutine submenu_xsecrotate(ichoose) use filenames, only:nsteps,nstepsinfile,ifileopen use labels, only:label,ix,irad,get_sink_type use limits, only:lim use prompting, only:prompt,print_logical use promptlist, only:prompt_list use settings_data, only:ndim,xorigin,iCalcQuantities,DataIsBuffered,ntypes use calcquantities, only:calc_quantities use plotlib, only:plotlib_supports_alpha implicit none integer, intent(in) :: ichoose integer :: ians,i logical :: ichangedorigin character(len=1) :: labelx(3) character(len=4) :: text real, dimension(3) :: xorigintemp print "(a)",'---------- cross section / 3D plotting options --------' if (ndim.eq.1) print*,' WARNING: none of these options have any effect in 1D' ians = ichoose if (xsec_nomulti) then text = 'xsec' else text = 'proj' endif if (ians.le.0 .or. ians.gt.6) then print 10,text,print_logical(irotate),anglex,angley,anglez, & print_logical(use3Dperspective),print_logical(use3Dopacityrendering), & irotateaxes,nseq 10 format( & ' 0) exit ',/, & ' 1) switch between cross section/projection ( ',a4,' )',/, & ' 2) rotation on/off/settings (incl. origin pos) ( ',a,3(1x,f5.1),' )',/, & ' 3) 3D perspective on/off ( ',a,' )',/, & ' 4) 3D surface rendering on/off ( ',a,' )',/, & ' 5) set axes for rotated/3D plots ( ',i2,' )',/, & ' 6) set animation sequence (rotate,flythru etc.) ( ',i2,' )') call prompt('enter option',ians,0,6) endif ! !--options ! select case(ians) !------------------------------------------------------------------------ case(1) xsec_nomulti = .not.xsec_nomulti print *,' Cross section = ',xsec_nomulti !------------------------------------------------------------------------ case(2) call prompt('use rotation?',irotate) print "(a)",' rotation is '//trim(print_logical(irotate)) if (irotate) then print*,'note that rotations are done in the order z-y-x ' print*,'this means the y and x rotations are done about the *new* y and x axes' print*,'if in doubt, set the angles interactively in this order' call prompt('enter rotation angle about z axis (deg)',anglez,0.,360.) if (ndim.eq.3) then call prompt('enter rotation angle about y axis (deg)',angley,0.,360.) call prompt('enter rotation angle about x axis (deg)',anglex,0.,360.) endif endif !xorigin(1:ndim) = 0.5*(lim(1:ndim,1) + lim(1:ndim,2)) xorigintemp(1:ndim) = xorigin(1:ndim) ichangedorigin = .false. print "(a)",' Note that origin settings affect both rotation and radius calculations' labelx=(/'x','y','z'/) do i=1,ndim call prompt('enter location of origin '//labelx(i),xorigin(i)) if (abs(xorigin(i)-xorigintemp(i)).gt.tiny(0.)) then ichangedorigin = .true. endif enddo !--recalculate radius if origin settings have changed if (ichangedorigin .and. iCalcQuantities .and. irad.gt.0) then if (DataIsBuffered) then call calc_quantities(1,nsteps) else call calc_quantities(1,nstepsinfile(ifileopen)) endif endif !------------------------------------------------------------------------ case(3) call prompt(' Use 3D perspective? ',use3Dperspective) if (use3Dperspective) then if (.not.irotate) irotate = .true. if (abs(anglez).lt.tiny(anglez) .and. & abs(angley).lt.tiny(angley) .and. & abs(anglex).lt.tiny(anglex)) then anglez = 30. anglex = 60. print "(a)",' setting default rotation angles to 30,0,60' endif else ! turn off opacity rendering if 3D perspective has been turned off use3Dopacityrendering = .false. endif !------------------------------------------------------------------------ case(4) call prompt(' Use 3D opacity rendering? ',use3Dopacityrendering) if (use3Dopacityrendering .and..not.use3Dperspective) then print "(a)",' also turning on 3D perspective (which must be set for this to work)' use3Dperspective = .true. endif if (use3Dopacityrendering) then if (.not.plotlib_supports_alpha) then print "(/,a)",' Warning: 3D opacity rendering sends only an approximate version ' print "(a,/)",' to the PGPLOT device (not corrected for brightness) ' call prompt(' Do you want to write a ppm file in addition to PGPLOT output?',writeppm) else writeppm = .false. !call prompt(' Do you want to apply the brightness correction?',writeppm) endif endif if (use3Dopacityrendering .and. get_sink_type(ntypes) > 0) then call prompt('Include sinks in opacity rendering (no=plot on top)?',rendersinks) endif !------------------------------------------------------------------------ case(5) print*,'0 : do not plot rotated axes' print*,'1 : plot rotated axes' print*,'2 : plot rotated box' print*,'3 : plot gridded x-y plane' call prompt('enter type of axes to plot',irotateaxes,0,3) if (irotateaxes.gt.0) then !--if not previously set, use current plot limits if (all(abs(xminrotaxes).le.tiny(xminrotaxes))) then xminrotaxes(:) = lim(ix(:),1) xmaxrotaxes(:) = lim(ix(:),2) endif do i=1,ndim call prompt('enter '//trim(label(ix(i)))//'min:',xminrotaxes(i)) call prompt('enter '//trim(label(ix(i)))//'max:',xmaxrotaxes(i)) enddo endif !------------------------------------------------------------------------ case(6) call submenu_animseq() end select return end subroutine submenu_xsecrotate !---------------------------------------------------------------------- ! sets up animation sequences !---------------------------------------------------------------------- subroutine submenu_animseq() use promptlist, only:prompt_list use prompting, only:prompt addseq => add_sequence checkseq => check_sequences delseq => delete_sequence call prompt_list(nseq,maxseq,'sequence',checkseq,addseq,delseq) end subroutine submenu_animseq !----------------------------------- ! print the current list of shapes !----------------------------------- subroutine check_sequences(n) implicit none integer, intent(in) :: n integer :: iseq print "(/,a)", ' Current list of animation sequences:' if (n.gt.0) then do iseq=1,n print "(i2,') ',a)",iseq,labelseqtype(iseqtype(iseq)) enddo else print "(a)",' (none)' endif end subroutine check_sequences subroutine delete_sequence(iseq,n) implicit none integer, intent(in) :: iseq integer, intent(inout) :: n integer :: i if (iseq.gt.0 .and. n.gt.0 .and. iseq.le.maxseq) then if (iseqtype(iseq).gt.0 .and. iseqtype(iseq).le.maxseq) then print "(a,i1,': ',a)",' deleting sequence ',iseq,trim(labelseqtype(iseqtype(iseq))) endif iseqtype(iseq) = 0 do i=iseq+1,n iseqtype(i-1) = iseqtype(i) enddo n = n - 1 endif end subroutine delete_sequence subroutine add_sequence(istart,iend,n) use prompting, only:prompt use limits, only:lim use labels, only:ix,irho use settings_data, only:ndim,istartatstep,iendatstep,numplot use filenames, only:nsteps implicit none integer, intent(in) :: istart,iend integer, intent(inout) :: n integer :: i,j,ierr i = istart + 1 over_sequences: do while (i.le.iend .and. i.le.maxseq) if (i.gt.n) n = i if (n.gt.0) then !--set sensible default value for number of frames if (nframes.eq.0) then if (nsteps.gt.1) then nframes = 1 else nframes = 10 endif endif call prompt('Enter number of frames generated between dumps (applies to all sequences)',nframes,1,500) !call prompt('Use same sequence position for all plots on the page?',imultiframeseq) endif print "(a,i2,a)",'----------------- sequence ',i,' ----------------------' if (iseqstart(i).eq.0) iseqstart(i) = max(istartatstep,1) if (iseqend(i).eq.0) iseqend(i) = max(1,iendatstep,istartatstep) if (nsteps.gt.1) then call prompt('Enter starting dump for sequence ',iseqstart(i),1,nsteps) call prompt('Enter finishing dump for sequence ',iseqend(i),1,nsteps) endif ierr = 1 do while (ierr /= 0) print "(7(/,1x,i1,1x,':',1x,a))",0,'none (remove sequence) ', & (j,labelseqtype(j),j=1,maxseq) call prompt('Enter type of sequence ',iseqtype(i),0,maxseq) !--allow only one sequence of each type ierr = 0 if (i.gt.0) then do j=1,n if (i.ne.j .and. (iseqtype(j).eq.iseqtype(i)) .and. (iseqtype(i).gt.0)) ierr = 2 enddo if (ierr.eq.2) print "(/,a)",' Error: can only have one sequence of each type ' endif end do select case(iseqtype(i)) case(1) print "(a)",'Note: zoom sequence starts using current fixed x,y plot limits' if (abs(xminseqend).lt.tiny(xminseqend) .and. abs(xmaxseqend).lt.tiny(xmaxseqend)) then xminseqend = lim(1,1) xmaxseqend = lim(1,2) endif call prompt(' Enter finishing xmin ',xminseqend) call prompt(' Enter finishing xmax ',xmaxseqend) if (abs(yminseqend).lt.tiny(yminseqend) .and. abs(ymaxseqend).lt.tiny(ymaxseqend)) then yminseqend = lim(2,1) ymaxseqend = lim(2,2) endif call prompt(' Enter finishing ymin ',yminseqend) call prompt(' Enter finishing ymax ',ymaxseqend) case(2) if (ndim.lt.2) then print "(a)",' ERROR: cannot use this sequence in 1D' iseqtype(i) = 0 endif if (.not.irotate) then print "(a)",' Turning rotation on...' irotate = .true. endif print "(a)",'Note: rotation sequence starts using current rotation settings' call prompt(' Enter finishing rotation angle (z axis) ',anglezend) call prompt(' Enter finishing rotation angle (y axis) ',angleyend) call prompt(' Enter finishing rotation angle (x axis) ',anglexend) case(3) if (icolchange.le.0 .or. icolchange.gt.numplot) then if (irho.gt.0 .and. irho.le.numplot) then icolchange = irho else icolchange = 1 endif endif call prompt(' Enter column to change limits ',icolchange,1,numplot) print "(a)",'Note: limits start from current fixed plot limits for this column' if (abs(xmincolend).lt.tiny(xmincolend) .and. abs(xmaxcolend).lt.tiny(xmaxcolend)) then xmincolend = lim(icolchange,1) xmaxcolend = lim(icolchange,2) endif call prompt(' Enter finishing minimum value ',xmincolend) call prompt(' Enter finishing maximum value ',xmaxcolend) case(4) if (ndim.ne.3) then print "(a)",' ERROR: cannot use this sequence in < 3D' iseqtype(i) = 0 endif if (.not.use3Dperspective) then print "(a)",'Turning 3D perspective on...' use3Dperspective = .true. endif print "(a)",'Note: observer starts at current observer settings ' print "(a)",' (screen height does not change)' !--try to give sensible default values if (abs(zobserverend).lt.tiny(zobserverend)) then if (abs(zobserver).gt.tiny(zobserver)) then zobserverend = 5.*zobserver elseif (ix(3).gt.0 .and. ix(3).le.numplot) then zobserverend = 10.*lim(ix(3),2) endif endif call prompt(' Enter finishing 3D observer height ',zobserverend) case(5) if (ndim.ne.3) then print "(a)",' ERROR: cannot use this sequence in < 3D' iseqtype(i) = 0 endif if (.not.xsec_nomulti) then print "(a)",'Changing from projection to cross-section' xsec_nomulti = .true. if (use3Dperspective .and. .not.use3Dopacityrendering) then print "(a)",'Turning 3D perspecitve off' use3Dperspective = .false. endif endif print "(a)",'Note: slice position starts from value set at initial prompt' call prompt(' Enter finishing slice position ',xsecpos_nomulti_end) case(6) if (ndim.ne.3) then print "(a)",' ERROR: cannot use this sequence in < 3D' iseqtype(i) = 0 endif if (.not.use3Dperspective .or. .not.use3Dopacityrendering) then print "(a)",'Turning 3D opacity rendering and 3D perspective on...' use3Dopacityrendering = .true. use3Dperspective = .true. endif print "(3(a,/))",'Note: opacity sequence starts from current opacity value ', & ' and that logarithmic steps are used if finishing value is', & ' set to more than 1000 times the starting value (or vice-versa) ' call prompt('Enter finishing opacity in units of average smoothing length ',taupartdepthend) case default call delete_sequence(i,n) exit over_sequences end select i = i + 1 enddo over_sequences if (all(iseqtype(1:n).eq.0)) then n = 0 else ihavesetsequence = .true. endif return end subroutine add_sequence !---------------------------------------------------------------------- ! ! subroutine called from interactive mode which sets the current ! plot settings as the end point to an animation sequence ! !---------------------------------------------------------------------- subroutine setsequenceend(ipos,iplotx,iploty,irender,rotation, & anglexi,angleyi,anglezi,zobserveri,use3Dopacity,taupartdepthi, & x_sec,xsecposi,xmin,xmax,ymin,ymax,rendermin,rendermax) use limits, only:lim use multiplot, only:itrans use settings_data, only:ndim,numplot use transforms, only:transform_limits,transform_limits_inverse,transform_label implicit none integer, intent(in) :: ipos,iplotx,iploty,irender real, intent(in) :: anglexi,angleyi,anglezi,zobserveri,taupartdepthi,xsecposi real, intent(in) :: xmin,xmax,ymin,ymax,rendermin,rendermax logical, intent(in) :: rotation, use3Dopacity,x_sec integer :: i real :: xminfixed,xmaxfixed,yminfixed,ymaxfixed,renderminfixed,rendermaxfixed nseq = 0 iseqtype(:) = 0 ! !--compare transformed limits ! xminfixed = lim(iplotx,1) xmaxfixed = lim(iplotx,2) call transform_limits(xminfixed,xmaxfixed,itrans(iplotx)) yminfixed = lim(iploty,1) ymaxfixed = lim(iploty,2) call transform_limits(yminfixed,ymaxfixed,itrans(iploty)) if (irender.gt.0 .and. irender.le.numplot) then renderminfixed = lim(irender,1) rendermaxfixed = lim(irender,2) call transform_limits(renderminfixed,rendermaxfixed,itrans(irender)) endif !--set however many sequences are required to capture the change in parameters ! !--change of x-y limits if ( notequal(xmin,xminfixed) .or. notequal(xmax,xmaxfixed) & .or.notequal(ymin,yminfixed) .or. notequal(ymax,ymaxfixed)) then nseq = nseq + 1 iseqtype(nseq) = 1 xminseqend = xmin xmaxseqend = xmax yminseqend = ymin ymaxseqend = ymax print*,trim(transform_label('xmin,max',itrans(iplotx)))//' start = ',xminfixed,xmaxfixed, & ' end = ',xminseqend,xmaxseqend print*,trim(transform_label('ymin,max',itrans(iploty)))//' start = ',yminfixed,ymaxfixed, & ' end = ',yminseqend,ymaxseqend !--always store untransformed limits call transform_limits_inverse(xminseqend,xmaxseqend,itrans(iplotx)) call transform_limits_inverse(yminseqend,ymaxseqend,itrans(iploty)) endif !--change of rotation angles if (ndim.ge.2 .and. rotation .and. & (notequal(anglexi,anglex).or.notequal(angleyi,angley).or.notequal(anglezi,anglez))) then nseq = nseq + 1 iseqtype(nseq) = 2 anglexend = anglexi angleyend = angleyi anglezend = anglezi print*,'angle x start = ',anglex,' end = ',anglexend print*,'angle y start = ',angley,' end = ',angleyend print*,'angle z start = ',anglez,' end = ',anglezend endif !--change of render limits if (ndim.gt.1 .and. irender.gt.0 .and. irender.le.numplot) then if (notequal(rendermin,renderminfixed) .or. notequal(rendermax,rendermaxfixed)) then nseq = nseq + 1 iseqtype(nseq) = 3 icolchange = irender xmincolend = rendermin xmaxcolend = rendermax print*,trim(transform_label('rendermin,max',itrans(irender)))//' start = ',renderminfixed,rendermaxfixed, & ' end = ',xmincolend,xmaxcolend !--always store untransformed limits call transform_limits_inverse(xmincolend,xmaxcolend,itrans(irender)) endif endif !--change of observer position if (ndim.eq.3 .and. notequal(zobserveri,zobserver)) then nseq = nseq + 1 iseqtype(nseq) = 4 zobserverend = zobserveri endif !--change of cross section position if (ndim.eq.3 .and. x_sec .and. notequal(xsecpos_nomulti,xsecposi)) then nseq = nseq + 1 iseqtype(nseq) = 5 xsecpos_nomulti_end = xsecposi endif !--change of opacity if (use3Dopacity .and. notequal(taupartdepthi,taupartdepth)) then nseq = nseq + 1 iseqtype(nseq) = 6 taupartdepthend = taupartdepthi endif !--all sequences start from 1 and end at current dump position iseqstart(1:nseq) = 1 iseqend(1:nseq) = ipos if (nseq.gt.0) then print "(1x,a,i1,a)",'total of ',nseq,' sequences set:' do i=1,nseq print "(1x,i1,': ',a)",i,trim(labelseqtype(iseqtype(i))) enddo print "(a,i5)",' sequences start at dump 1 and end at dump ',ipos if (nframes.le.0) then if (ipos.eq.1) then nframes = 10 else nframes = 1 endif print "(a,i3)",' setting number of frames = ',nframes endif else print "(a)",' no sequences set (no change in parameters)' endif return end subroutine setsequenceend !---------------------------------------------------------------------- ! utility function for comparing real numbers !---------------------------------------------------------------------- logical function notequal(r1,r2) implicit none real, intent(in) :: r1,r2 if (abs(r1-r2).gt.epsilon(r1)) then notequal = .true. else notequal = .false. endif end function notequal !---------------------------------------------------------------------- ! query function determining whether or not a given timestep ! is inside an animation sequence or not ! (and thus whether or not to generate extra frames) !---------------------------------------------------------------------- logical function insidesequence(ipos) implicit none integer, intent(in) :: ipos integer :: i insidesequence = .false. do i=1,nseq if (iseqtype(i).gt.0 .and. iseqstart(i).le.ipos .and. iseqend(i).ge.ipos) then insidesequence = .true. endif enddo return end function insidesequence !---------------------------------------------------------------------- ! query function which returns the current plot parameters ! based on the position in each sequence ! (given the current frame & dump position) !---------------------------------------------------------------------- subroutine getsequencepos(ipos,iframe,iplotx,iploty,irender, & anglexi,angleyi,anglezi,zobserveri,dzscreen,taupartdepthi, & xsecposi,xmin,xmax,ymin,ymax,rendermin,rendermax,isetrenderlimits) use limits, only:lim use multiplot, only:itrans use transforms, only:transform_limits implicit none integer, intent(in) :: ipos,iframe,iplotx,iploty,irender real, intent(out) :: anglexi,angleyi,anglezi,zobserveri,dzscreen,taupartdepthi,xsecposi real, intent(out) :: xmin,xmax,ymin,ymax,rendermin,rendermax logical, intent(out) :: isetrenderlimits logical :: logtaudepth integer :: i,iposinseq,iposend real :: xfrac,xminstart,xmaxstart,xminend,xmaxend,yminstart,ymaxstart,yminend,ymaxend isetrenderlimits = .false. do i=1,nseq !--set starting values based on first position if (ipos.ge.iseqstart(i)) then iposinseq = (ipos-iseqstart(i))*nframes + iframe iposend = (iseqend(i)-iseqstart(i))*nframes + nframes xfrac = (iposinseq-1)/real(iposend-1) xfrac = min(xfrac,1.0) if (iposinseq.gt.iposend) then print "(1x,a)",'--> '//trim(labelseqtype(iseqtype(i)))//' finished : frac = 1.0' else print "(1x,a,i3,a,i3,a,f5.2)",'--> frame ', & iposinseq,' / ',iposend,' of '//trim(labelseqtype(iseqtype(i)))//': frac = ',xfrac endif select case(iseqtype(i)) case(1) xminstart = lim(iplotx,1) xmaxstart = lim(iplotx,2) yminstart = lim(iploty,1) ymaxstart = lim(iploty,2) call transform_limits(xminstart,xmaxstart,itrans(iplotx)) call transform_limits(yminstart,ymaxstart,itrans(iploty)) xminend = xminseqend xmaxend = xmaxseqend yminend = yminseqend ymaxend = ymaxseqend call transform_limits(xminend,xmaxend,itrans(iplotx)) call transform_limits(yminend,ymaxend,itrans(iploty)) !--steps are linear in the transformed space ! and limits returned are *already transformed* xmin = xminstart + xfrac*(xminend - xminstart) xmax = xmaxstart + xfrac*(xmaxend - xmaxstart) ymin = yminstart + xfrac*(yminend - yminstart) ymax = ymaxstart + xfrac*(ymaxend - ymaxstart) case(2) anglexi = anglex + xfrac*(anglexend - anglex) angleyi = angley + xfrac*(angleyend - angley) anglezi = anglez + xfrac*(anglezend - anglez) case(3) !--steps are linear in the transformed space ! and limits returned are *already transformed* if (iplotx.eq.icolchange) then xminstart = lim(iplotx,1) xmaxstart = lim(iplotx,2) call transform_limits(xminstart,xmaxstart,itrans(iplotx)) xminend = xmincolend xmaxend = xmaxcolend call transform_limits(xminend,xmaxend,itrans(iplotx)) xmin = xminstart + xfrac*(xminend - xminstart) xmax = xmaxstart + xfrac*(xmaxend - xmaxstart) elseif (iploty.eq.icolchange) then yminstart = lim(iploty,1) ymaxstart = lim(iploty,2) call transform_limits(yminstart,ymaxstart,itrans(iploty)) yminend = xmincolend ymaxend = xmaxcolend call transform_limits(yminend,ymaxend,itrans(iploty)) ymin = yminstart + xfrac*(yminend - yminstart) ymax = ymaxstart + xfrac*(ymaxend - ymaxstart) elseif (irender.eq.icolchange) then xminstart = lim(irender,1) xmaxstart = lim(irender,2) call transform_limits(xminstart,xmaxstart,itrans(irender)) xminend = xmincolend xmaxend = xmaxcolend call transform_limits(xminend,xmaxend,itrans(irender)) rendermin = xminstart + xfrac*(xminend - xminstart) rendermax = xmaxstart + xfrac*(xmaxend - xmaxstart) isetrenderlimits = .true. endif case(4) zobserveri = zobserver + xfrac*(zobserverend - zobserver) dzscreen = zobserveri case(5) xsecposi = xsecpos_nomulti + xfrac*(xsecpos_nomulti_end - xsecpos_nomulti) case(6) logtaudepth = (taupartdepthend .gt. 1.001e3*taupartdepth) & .or.(taupartdepthend .lt. 1.001e-3*taupartdepth) if (logtaudepth) then print "(a)",' (incrementing optical depth logarithmically)' taupartdepthi = taupartdepth*(taupartdepthend/taupartdepth)**xfrac else taupartdepthi = taupartdepth + xfrac*(taupartdepthend - taupartdepth) endif end select endif enddo return end subroutine getsequencepos end module settings_xsecrot splash/src/globaldata.f90000644 000766 000000 00000012502 13261626263 016165 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------------- ! ! modules containing global variables ! !---------------------------------------------------------------------------- ! !--global parameters ! module params implicit none integer, parameter :: doub_prec = selected_real_kind(P=10,R=30) integer, parameter :: sing_prec = selected_real_kind(P=5,R=15) integer, parameter :: int1 = selected_int_kind(1) integer, parameter :: int8 = selected_int_kind(10) integer, parameter :: maxplot=512 ! maximum number of plots (for multiplot arrays) integer, parameter :: maxparttypes = 12 ! max # of different particle types public end module params module physcon use params, only:doub_prec implicit none real(doub_prec), parameter :: solarrcgs = 6.955d10 real(doub_prec), parameter :: solarmcgs = 1.989d33 public end module physcon ! !--particle data ! module particle_data use params implicit none integer :: maxpart,maxstep,maxcol ! dimensions of dat array integer, allocatable, dimension(:) :: icolourme integer(kind=int1), allocatable, dimension(:,:) :: iamtype integer, allocatable, dimension(:,:) :: npartoftype real, allocatable, dimension(:,:) :: masstype real, allocatable, dimension(:) :: time, gamma real, allocatable, dimension(:,:,:) :: dat real, parameter :: time_not_read_val = -0.5*huge(0.) public contains logical function time_was_read(t) real, intent(in) :: t time_was_read = .true. if (t <= time_not_read_val) time_was_read = .false. end function time_was_read end module particle_data ! !--filenames ! module filenames implicit none integer, parameter :: maxfile = 10001 integer :: nfiles,nsteps,ifileopen,iposopen character(len=120), dimension(maxfile) :: rootname character(len=100) :: fileprefix character(len=120) :: defaultsfile,limitsfile,unitsfile,coloursfile integer, dimension(maxfile) :: nstepsinfile character(len=68) :: tagline = & 'SPLASH: A visualisation tool for SPH data (c)2004-2017 Daniel Price' public contains subroutine set_filenames(prefix) implicit none character(len=*), intent(in) :: prefix fileprefix = trim(adjustl(prefix)) if (fileprefix(len_trim(fileprefix):len_trim(fileprefix)).eq.'.') then fileprefix = fileprefix(1:len_trim(fileprefix)-1) endif defaultsfile = trim(adjustl(fileprefix))//'.defaults' limitsfile = trim(adjustl(fileprefix))//'.limits' unitsfile = trim(adjustl(fileprefix))//'.units' coloursfile = trim(adjustl(fileprefix))//'.colours' return end subroutine set_filenames end module filenames !------------------------------------ ! modules containing plot settings !------------------------------------ ! !--data ! module settings_data use params implicit none integer :: numplot,ncalc,ncolumns,nextra integer :: ndataplots integer :: ndim, ndimv integer :: ndusttypes integer :: idustfrac_plot = 0 integer :: ideltav_plot = 0 integer :: icoords,icoordsnew,iformat,ntypes,iexact integer :: istartatstep,iendatstep,nfreq integer :: itracktype,itrackoffset,iverbose integer, dimension(10) :: isteplist logical :: fakedust = .false. logical :: ivegotdata, DataIsBuffered, ipartialread logical :: buffer_data,iUseStepList,iCalcQuantities,iRescale logical, parameter :: buffer_steps_in_file = .false. !--required array is dimensioned 0:maxplot so that required(icol) = .true. ! does nothing bad if icol = 0 (much safer that way) logical :: lowmemorymode logical :: debugmode logical, dimension(0:maxplot) :: required logical, dimension(maxparttypes) :: UseTypeInRenderings real, dimension(3) :: xorigin namelist /dataopts/ buffer_data,iCalcQuantities,iRescale,xorigin, & itracktype,itrackoffset,idustfrac_plot,ideltav_plot public end module settings_data ! !--multiplot settings ! module multiplot use params implicit none integer :: nyplotmulti integer, dimension(maxplot) :: multiplotx,multiploty integer, dimension(maxplot) :: irendermulti,ivecplotmulti integer, dimension(maxplot) :: itrans,icontourmulti logical, dimension(maxplot) :: x_secmulti real, dimension(maxplot) :: xsecposmulti logical, dimension(maxplot) :: iusealltypesmulti logical, dimension(maxparttypes,maxplot) :: iplotpartoftypemulti ! !--sort these into a namelist for input/output ! namelist /multi/ nyplotmulti, & itrans,multiplotx,multiploty,irendermulti, & ivecplotmulti,icontourmulti,x_secmulti,xsecposmulti, & iusealltypesmulti,iplotpartoftypemulti public end module multiplot splash/src/analysis.f90000644 000766 000000 00000120615 13261626263 015723 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------- ! module implementing the ability to use SPLASH to produce ! evolution files from a sequence of SPH dump files ! (ie. in order to produce plots of certain quantities vs time) ! Command is "splash calc X" where X is analysis type. !------------------------------------------------------------------- module analysis public :: isanalysis,open_analysis,close_analysis,write_analysis private integer, private, parameter :: iunit = 89 integer, private, parameter :: maxlevels = 20 ! ! default settings for the density thresholds for massaboverho output ! integer, private :: nlevels,nfilesread real, dimension(maxlevels), private :: rholevels character(len=64), private :: fileout real, dimension(:,:), allocatable :: datmean,datvar contains !----------------------------------------------------------------- ! utility to check if the choice of analysis type is valid ! and if not, to print the available options !----------------------------------------------------------------- logical function isanalysis(string,noprint) implicit none character(len=*), intent(in) :: string logical, intent(in), optional :: noprint logical :: doprint isanalysis = .false. select case(trim(string)) case('energies','energy') isanalysis = .true. case('massaboverho') isanalysis = .true. case('max','maxvals') isanalysis = .true. case('min','minvals') isanalysis = .true. case('diff','diffvals') isanalysis = .true. case('delta','deltavals') isanalysis = .true. case('amp','ampvals') isanalysis = .true. case('mean','meanvals') isanalysis = .true. case('rms','rmsvals') isanalysis = .true. case('vrms','vrmsvals','vwrms','rmsvw') isanalysis = .true. case('rhovar','rhomach') isanalysis = .true. case('kh') isanalysis = .true. case('timeaverage','timeav') isanalysis = .true. case('ratio') isanalysis = .true. case('tracks') isanalysis = .true. end select if (present(noprint)) then doprint = .not.noprint else doprint = .true. endif if (.not.isanalysis .and. doprint) then print "(a)",' Analysis mode ("splash calc X dumpfiles") on a sequence of dump files: ' print "(a)",' splash calc energies : calculate KE,PE,total energy vs time' print "(a)",' output to file called ''energy.out''' print "(a)",' calc massaboverho : mass above a series of density thresholds vs time' print "(a)",' output to file called ''massaboverho.out''' ! print "(a)",' calc rhomach : density variance and RMS velocity dispersion vs. time' ! print "(a)",' output to file called ''rhomach.out''' print "(a)",' calc max : maximum of each column vs. time' print "(a)",' output to file called ''maxvals.out''' print "(a)",' calc min : minimum of each column vs. time' print "(a)",' output to file called ''minvals.out''' print "(a)",' calc diff : (max - min) of each column vs. time' print "(a)",' output to file called ''diffvals.out''' print "(a)",' calc amp : 0.5*(max - min) of each column vs. time' print "(a)",' output to file called ''ampvals.out''' print "(a)",' calc delta : 0.5*(max - min)/mean of each column vs. time' print "(a)",' output to file called ''deltavals.out''' print "(a)",' calc mean : mean of each column vs. time' print "(a)",' output to file called ''meanvals.out''' print "(a)",' calc rms : (mass weighted) root mean square of each column vs. time' print "(a)",' output to file called ''rmsvals.out''' print "(a)",' calc tracks : track particle data vs time for selected* particle,' print "(a)",' output to file called ''tracks.out''' ! print "(a)",' calc vrms : volume weighted root mean square of each column vs. time' ! print "(a)",' output to file called ''rmsvals-vw.out''' print "(a)",' ( * select "xy limits relative to particle" in l)imits menu", or' print "(a)",' press "t" in interactive mode, and save settings to splash.defaults )' print "(/,a)",' the above options all produce a small ascii file with one row per input file.' print "(a)",' the following option produces a file equivalent in size to one input file (in ascii format):' print "(/,a)",' calc timeaverage : time average of *all* entries for every particle' print "(a)",' output to file called ''time_average.out''' print "(/,a)",' calc ratio : ratio of *all* entries in each file compared to first' print "(a)",' output to file called ''ratio.out''' endif return end function isanalysis !---------------------------------------------------------------- ! open output file/ initialise quantities needed for analysis ! over all dump files !---------------------------------------------------------------- subroutine open_analysis(analysistype,required,ncolumns,ndim,ndimV) use labels, only:ix,ivx,iBfirst,iutherm,irho,ipmass,label use asciiutils, only:read_asciifile use filenames, only:rootname,nfiles,tagline use params, only:maxplot implicit none integer, intent(in) :: ncolumns,ndim,ndimV character(len=*), intent(in) :: analysistype logical, dimension(0:ncolumns), intent(out) :: required character(len=maxplot*18) :: headerline ! len=maxplot x 18 characters character(len=64) :: levelsfile character(len=maxplot*12) :: fmtstring logical :: iexist,standardheader integer :: ierr,i ! !--the 'required' array is used by the data reads (where implemented) ! to determine whether or not we actually need to read a given column ! from the file -- if not it can be skipped, leading to a faster ! data read. Here we want to specify which columns are required ! for the analysis in question. ! print "(/,5('-'),a,/)",'> CALCULATING '//trim(analysistype)//' vs time for all dump files' required(:)=.false. headerline = ' ' standardheader = .false. select case(trim(analysistype)) case('energy','energies') ! !--for energies need to read particle mass, velocity, utherm and if present, ! magnetic field and density. The obvious limitation here is that we ! cannot calculate the potential energy unless it is dumped and labelled ! (which is not currently implemented). ! required(ivx:ivx+ndimV-1) = .true. required(iBfirst:iBfirst+ndimV-1) = .true. required(iutherm) = .true. required(ipmass) = .true. if (iBfirst.gt.0) required(irho) = .true. required(ix(1:ndim)) = .true. ! !--set filename and header line ! fileout = 'energy.out' write(headerline,"('#',8(1x,'[',i2.2,1x,a11,']',2x))") & 1,'time',2,'ekin',3,'etherm',4,'emag',5,'epot',6,'etot',7,'totmom',8,'totang' case('massaboverho') ! !--only need to read mass and density from dump files ! required(ipmass) = .true. required(irho) = .true. ! !--need a user-configurable way of setting the density thresholds: ! implemented by setting them in a file which is read here ! (a new one is created if it doesn't exist) ! levelsfile = 'massaboverho.levels' inquire(file=levelsfile,exist=iexist) if (iexist) then call read_asciifile(trim(levelsfile),nlevels,rholevels) print "(a)",' read '//trim(levelsfile)//':' do i=1,nlevels print "(a,i2,a,es9.2)",' level ',i,': rho > ',rholevels(i) enddo print* else print "(a)",' SPLASH ANALYSIS: levels file '//trim(levelsfile)//' not found' print "(a)",' creating one with default levels for mass > rho' print "(a)",' edit this file to set the density levels' open(unit=iunit+1,file=levelsfile,status='new',form='formatted',iostat=ierr) if (ierr /= 0) then stop 'ERROR creating levels file' else nlevels = 10 rholevels(1:nlevels) = (/1.e-20,1.e-19,1.e-18,1.e-17,1.e-16, & 1.e-15,1.e-14,1.e-13,1.e-12,1.e-11/) write(iunit+1,*) rholevels(1:nlevels) close(iunit+1) endif stop endif ! !--set filename and header line ! fileout = 'massaboverho.out' write(headerline,"('#',1x,'[',i2.2,1x,a12,']',1x,20('[',i2.2,1x,a4,es8.1,a1,']',1x))") & 1,'time',(i+1,'M(r>',rholevels(i),')',i=1,nlevels) case('max','maxvals') ! !--read all columns from dump file ! required(:) = .true. ! !--set filename and header line ! fileout = 'maxvals.out' standardheader = .true. case('min','minvals') required(:) = .true. fileout = 'minvals.out' standardheader = .true. case('diff','diffvals') required(:) = .true. fileout = 'diffvals.out' standardheader = .true. case('amp','ampvals') required(:) = .true. fileout = 'ampvals.out' standardheader = .true. case('delta','deltavals','deltas') required(:) = .true. fileout = 'deltavals.out' standardheader = .true. case('mean','meanvals') required(:) = .true. fileout = 'meanvals.out' standardheader = .true. case('rms','rmsvals') required(:) = .true. fileout = 'rmsvals.out' standardheader = .true. case('vrms','vrmsvals','vwrms','rmsvw') required(:) = .true. fileout = 'rmsvals-vw.out' standardheader = .true. case('rhovar','rhomach') ! !--read density, velocity info ! required(ipmass) = .true. required(irho) = .true. required(ivx:ivx+ndimV-1) = .true. ! !--set filename and header line ! fileout = 'rhomach.out' write(fmtstring,"('(''#'',1x,',i3,'(''['',i2.2,1x,a12,'']'',2x))')",iostat=ierr) 17 write(headerline,fmtstring) 1,'time',2,'rhomean(vw)',3,'rhomean(mw)',4,'varrho(vw)',5,'varrho(mw)',& 6,'stddevrho(vw)',7,'stddevrho(mw)',8,'rms v (vw)',9,'rms v (mw)',10,'b (vw)',11,'b (mw)',& 12,'s mean(vw)',13,'s mean(mw)',14,'s var(vw)',15,'s var(mw)',16,'s stddev(vw)',17,'s stddev(mw)' case('kh') ! !--read all columns from dump file ! required(irho) = .true. required(ivx:ivx+ndimV-1) = .true. ! !--set filename and header line ! fileout = 'kh.out' standardheader = .true. write(fmtstring,"('(''#'',1x,',i3,'(''['',i2.2,1x,a12,'']'',2x))')",iostat=ierr) 2 write(headerline,fmtstring) 1,'time',2,'max(ekiny)' case('timeaverage','timeav') ! !--read all columns from dump file ! required(:) = .true. ! !--set filename and header line ! fileout = 'time_average.out' if (ncolumns.gt.0) then write(fmtstring,"('(''#'',1x,',i3,'(''['',i2.2,1x,a12,'']''))')",iostat=ierr) 2*ncolumns write(headerline,fmtstring,iostat=ierr) (i,label(i)(1:12),i=1,ncolumns),& (ncolumns+i,'err'//label(i)(1:9),i=1,ncolumns) endif case('ratio') ! !--read all columns from dump file ! required(:) = .true. ! !--set filename and header line ! fileout = 'ratio.out' if (ncolumns.gt.0 .and. ncolumns.ne.maxplot) then write(fmtstring,"('(''#'',1x,',i3,'(''['',i2.2,1x,a12,'']''))')",iostat=ierr) 2*ncolumns write(headerline,fmtstring,iostat=ierr) (i,label(i)(1:12),i=1,ncolumns),& (ncolumns+i,'err'//label(i)(1:9),i=1,ncolumns) endif case('tracks') ! !--read all of dump file ! required(:) = .true. ! !--set filename and header line ! fileout = 'tracks.out' standardheader = .true. end select if (standardheader) then ! !--standard header is time in column 1, with an entry for each column following ! (this is to avoid repeated code above) ! write(fmtstring,"('(''#'',1x,',i3,'(''['',i2.2,1x,a12,'']'',2x))')",iostat=ierr) ncolumns+1 write(headerline,fmtstring) 1,'time',(i+1,label(i)(1:12),i=1,ncolumns) endif ! !--do not replace the file if it already exists ! inquire(file=trim(fileout),exist=iexist) if (iexist) then print "(2(a,/))",' ERROR: analysis file '//trim(fileout)//' already exists', & ' delete, move or rename this file and try again' stop endif ! !--open the file for output ! open(unit=iunit,file=trim(fileout),status='new',form='formatted',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR opening file '//trim(fileout)//' for output' stop endif print "(a)",' WRITING '//trim(analysistype)//' vs time to file '//trim(fileout) ! !--write header if the headerline is set ! (no header is written if headerline is blank) ! if (len_trim(headerline).gt.0) then write(iunit,"(a)") '# '//trim(tagline) write(iunit,"(a)") '# '//trim(fileout)//' produced using "splash calc '//trim(analysistype)// & '" on dump files '//trim(rootname(1))//'->'//trim(rootname(nfiles)) write(iunit,"(a)") '# use asplash -e '//trim(fileout)//' to plot the contents of this file ' write(iunit,"(a)") '#' write(iunit,"(a)") trim(headerline) endif nfilesread = 0 return end subroutine open_analysis !---------------------------------------------------------------- ! this is the routine which actually calculates the quantities ! required from each dump file and spits out a line to the ! analysis file. Called once for each dump file. !---------------------------------------------------------------- subroutine write_analysis(time,dat,ntot,ntypes,npartoftype,massoftype,iamtype,ncolumns,ndim,ndimV,analysistype) use labels, only:ix,ivx,iBfirst,iutherm,irho,ipmass,labeltype,label use params, only:int1,doub_prec,maxplot use asciiutils, only:ucase use system_utils, only:renvironment use settings_part, only:iplotpartoftype use particle_data, only:time_was_read use settings_data, only:xorigin,icoords,icoordsnew,itracktype,itrackoffset use geomutils, only:change_coords use part_utils, only:get_tracked_particle implicit none integer, intent(in) :: ntot,ntypes,ncolumns,ndim,ndimV integer, intent(in), dimension(:) :: npartoftype real, intent(in), dimension(:) :: massoftype integer(kind=int1), intent(in), dimension(:) :: iamtype real, intent(in) :: time real, intent(in), dimension(:,:) :: dat character(len=*), intent(in) :: analysistype real(kind=doub_prec), dimension(maxlevels) :: massaboverho integer :: itype,i,j,ierr,ntot1,ncol1,nused,itrack real(kind=doub_prec) :: ekin,emag,etherm,epot,etot,totmom,pmassi,totang real(kind=doub_prec) :: totvol,voli,rhoi,rmsvmw,v2i real(kind=doub_prec) :: rhomeanmw,rhomeanvw,rhovarmw,rhovarvw,bval,bvalmw real(kind=doub_prec) :: smeanmw,smeanvw,svarmw,svarvw,si,ekiny,ekinymax real(kind=doub_prec) :: lmin(maxplot),lmax(maxplot),lmean(maxplot),rmsvali real(kind=doub_prec), dimension(3) :: xmom,angmom,angmomi,ri,vi real :: delta,dn,valmin,valmax,valmean,timei character(len=20) :: fmtstring logical :: change_coordsys real :: x0(3),v0(3) ! ! array with one value for each column ! real(kind=doub_prec) :: coltemp(maxplot), vals(maxplot), rmsval(maxplot) nfilesread = nfilesread + 1 if (time_was_read(time)) then timei = time print "(/,5('-'),a,', TIME=',es9.2,' FILE #',i5,/)",& '> CALCULATING '//trim(ucase(analysistype)),time,nfilesread else timei = 0. print "(/,5('-'),a,', FILE #',i5,' (TIME NOT READ)'/)",& '> CALCULATING '//trim(ucase(analysistype)),nfilesread endif change_coordsys = (icoordsnew.ne.icoords .and. ndim.gt.0 .and. all(ix(1:ndim).gt.0)) x0 = xorigin(:) ! note that it is not currently possible to do splash to ascii v0 = 0. ! with coords set relative to a tracked particle, so just use xorigin itrack = get_tracked_particle(itracktype,itrackoffset,npartoftype,iamtype) if (itrack==0) itrack = 1 select case(trim(analysistype)) case('energy','energies') ekin = 0. emag = 0. epot = 0. etherm = 0. etot = 0. xmom = 0. angmom = 0. nused = 0 do i=1,ntot itype = igettype(i) if (iplotpartoftype(itype)) then pmassi = particlemass(i,itype) !--kinetic energy if (ivx.gt.0 .and. ivx+ndimV-1.le.ncolumns) then vi(:) = 0. vi(1:ndimV) = dat(i,ivx:ivx+ndimV-1) ekin = ekin + pmassi*dot_product(vi,vi) !--linear momentum xmom = xmom + pmassi*vi !--angular momentum if (ndim.ge.1 .and. all(ix(1:ndim).gt.0)) then ri(:) = 0. ri(1:ndim) = dat(i,ix(1):ix(ndim)) call cross_product3D(ri,vi,angmomi) angmom(:) = angmom(:) + pmassi*angmomi(:) endif endif !--thermal energy if (iutherm.gt.0 .and. iutherm.le.ncolumns) then etherm = etherm + pmassi*dat(i,iutherm) endif !--magnetic energy if (iBfirst.gt.0 .and. iBfirst+ndimV-1.le.ncolumns) then emag = emag + pmassi*dot_product(dat(i,iBfirst:iBfirst+ndimV-1),& dat(i,iBfirst:iBfirst+ndimV-1))/dat(i,irho) endif nused = nused + 1 endif enddo ekin = 0.5*ekin emag = 0.5*emag etot = ekin + etherm + epot + emag totmom = sqrt(dot_product(xmom(1:ndimV),xmom(1:ndimV))) totang = sqrt(dot_product(angmom,angmom)) print "(7(/,1x,a6,' = ',es9.2))",'etot',etot,'ekin',ekin,'etherm',etherm,'epot',epot,'emag',emag,'totmom',totmom,'totang',totang ! !--write line to output file ! write(iunit,"(64(es18.10,1x))") timei,ekin,etherm,emag,epot,etot,totmom,totang if (nused.ne.ntot) print*,'energies calculated using ',nused,' of ',ntot,' particles' case('massaboverho') massaboverho(:) = 0. if (irho.gt.0 .and. irho.le.ncolumns) then ! !--warn if particle masses not found ! if (ipmass.le.0 .or. ipmass.gt.ncolumns .and. all(massoftype < tiny(massoftype))) then print "(a)",' WARNING in massaboverho analysis!'// & ' masses not read or are zero from dump file' endif ! !--calculate mass above each density threshold ! do i=1,ntot itype = igettype(i) pmassi = particlemass(i,itype) if (itype.eq.1) then ! !--gas particles contribute if they are above rho ! where(dat(i,irho).ge.rholevels(1:nlevels)) massaboverho(1:nlevels) = massaboverho(1:nlevels) + pmassi end where elseif (labeltype(itype).eq.'sink') then ! !--sink particles always contribute (ie. they are assumed to ! be above every density threshold) ! massaboverho(1:nlevels) = massaboverho(1:nlevels) + pmassi endif enddo ! !--write output to screen/terminal ! do i=1,nlevels print "(1x,'M(rho > ',es9.2,') = ',es9.2)",rholevels(i),massaboverho(i) enddo ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) nlevels+1 write(iunit,fmtstring) timei,massaboverho(1:nlevels) else print "(a)",' ERROR in massaboverho analysis!'// & ' either mass or density not found in dump file' return endif case('max','maxvals') ! !--calculate maximum for each column ! coltemp(:) = -huge(0.d0) !maxval(dat(1:ntot,i)) nused = 0 do j=1,ntot itype = igettype(j) if (iplotpartoftype(itype)) then vals(1:ncolumns) = real(dat(j,1:ncolumns),kind=doub_prec) if (change_coordsys) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) nused = nused + 1 do i=1,ncolumns coltemp(i) = max(coltemp(i),vals(i)) enddo endif enddo where (coltemp(:) < -0.5*huge(0.)) coltemp(:) = 0. ! !--write output to screen/terminal ! do i=1,ncolumns print "(1x,a20,'max = ',es18.10)",label(i),coltemp(i) enddo ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) ncolumns+1 write(iunit,fmtstring) timei,coltemp(1:ncolumns) if (nused.ne.ntot) print*,'max calculated using ',nused,' of ',ntot,' particles' case('min','minvals') ! !--calculate minimum for each column ! coltemp(:) = huge(0.d0) !minval(dat(1:ntot,i)) nused = 0 do j=1,ntot itype = igettype(j) if (iplotpartoftype(itype)) then vals(1:ncolumns) = real(dat(j,1:ncolumns),kind=doub_prec) if (change_coordsys) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) nused = nused + 1 do i=1,ncolumns coltemp(i) = min(coltemp(i),vals(i)) enddo endif enddo ! !--write output to screen/terminal ! do i=1,ncolumns print "(1x,a20,'min = ',es18.10)",label(i),coltemp(i) enddo ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) ncolumns+1 write(iunit,fmtstring) timei,coltemp(1:ncolumns) if (nused.ne.ntot) print*,'min calculated using ',nused,' of ',ntot,' particles' case('diff','diffvals','amp','ampvals','delta','deltavals','deltas','tracks') ! !--calculate difference between max and min for each column ! lmean(:) = 0. lmin(:) = huge(0.d0) lmax(:) = -huge(0.d0) nused = 0 do j=1,ntot itype = igettype(j) if (iplotpartoftype(itype) .or. j.eq.itrack) then vals(1:ncolumns) = real(dat(j,1:ncolumns),kind=doub_prec) if (change_coordsys) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) nused = nused + 1 do i=1,ncolumns lmin(i) = min(lmin(i), vals(i)) lmax(i) = max(lmax(i), vals(i)) lmean(i) = lmean(i) + vals(i) enddo if (j.eq.itrack) coltemp = vals endif enddo if (nused.gt.0) lmean(:) = lmean(:)/real(nused) select case(trim(analysistype)) case('amp','ampvals') do i=1,ncolumns coltemp(i) = 0.5*(lmax(i) - lmin(i)) print "(1x,a20,'0.5*(max - min) = ',es18.10)",label(i),coltemp(i) enddo case('delta','deltavals','deltas') do i=1,ncolumns valmean = real(lmean(i)) if (valmean > 0.) then coltemp(i) = 0.5*(lmax(i) - lmin(i))/valmean else coltemp(i) = 0.5*(lmax(i) - lmin(i)) endif print "(1x,a20,'0.5*(max - min)/mean = ',es18.10)",label(i),coltemp(i) enddo case('tracks') do i=1,ncolumns print "(1x,' particle ',i8,': ',a20,' = ',es18.10)",itrack,label(i),coltemp(i) enddo case default ! diff, diffvals do i=1,ncolumns coltemp(i) = lmax(i) - lmin(i) print "(1x,a20,'(max - min) = ',es18.10)",label(i),coltemp(i) enddo end select ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) ncolumns+1 write(iunit,fmtstring) timei,coltemp(1:ncolumns) if (nused.ne.ntot) then select case(trim(analysistype)) case('diff', 'diffvals') print*,'diff calculated using ',nused,' of ',ntot,' particles' case('amp','ampvals') print*,'amp calculated using ',nused,' of ',ntot,' particles' case('delta','deltavals','deltas') print*,'deltas calculated using ',nused,' of ',ntot,' particles' end select endif case('mean','meanvals') ! !--calculate mean for each column ! coltemp(:) = 0. nused = 0 do j=1,ntot itype = igettype(j) if (iplotpartoftype(itype)) then vals(1:ncolumns) = real(dat(j,1:ncolumns),kind=doub_prec) if (change_coordsys) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) nused = nused + 1 do i=1,ncolumns coltemp(i) = coltemp(i) + vals(i) enddo endif enddo if (nused.gt.0) then coltemp(:) = coltemp(:)/real(nused) else coltemp(:) = 0. endif ! !--write output to screen/terminal ! do i=1,ncolumns print "(1x,a20,'mean = ',es18.10)",label(i),coltemp(i) enddo ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) ncolumns+1 write(iunit,fmtstring) timei,coltemp(1:ncolumns) if (nused.ne.ntot) print*,'mean calculated using ',nused,' of ',ntot,' particles' case('rms','rmsvals') ! !--calculate RMS for each column ! coltemp(:) = 0. nused = 0 do j=1,ntot itype = igettype(j) if (iplotpartoftype(itype)) then vals(1:ncolumns) = real(dat(j,1:ncolumns),kind=doub_prec) if (change_coordsys) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) nused = nused + 1 do i=1,ncolumns coltemp(i) = coltemp(i) + vals(i)**2 enddo endif enddo if (nused.gt.0) then coltemp(:) = sqrt(coltemp(:)/real(nused)) else coltemp(:) = 0. endif ! !--write output to screen/terminal ! do i=1,ncolumns print "(1x,a20,'rms (mass weighted) = ',es18.10)",label(i),coltemp(i) enddo ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) ncolumns+1 write(iunit,fmtstring) timei,coltemp(1:ncolumns) if (nused.ne.ntot) print*,'rms calculated using ',nused,' of ',ntot,' particles' case('vrms','vrmsvals','vwrms','rmsvw') if (irho.le.0 .or. irho.gt.ncolumns) then print "(a)",' ERROR in volume weighted rms calculation!'// & ' density not present / not labelled in dump file, skipping...' return endif ! !--warn if particle masses not found ! if (ipmass.le.0 .or. ipmass.gt.ncolumns .and. all(massoftype < tiny(massoftype))) then print "(a)",' WARNING in volume weighted rms calculation!'// & ' masses not read or are zero from dump file' endif ! !--calculate volume-weighted RMS for each column ! rmsval(:) = 0. totvol = 0. do j=1,ntot itype = igettype(j) if (iplotpartoftype(itype)) then vals(1:ncolumns) = real(dat(j,1:ncolumns),kind=doub_prec) if (change_coordsys) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) pmassi = particlemass(j,itype) rhoi = dat(j,irho) if (rhoi.gt.0.) then voli = pmassi/rhoi else voli = 0. endif do i=1,ncolumns rmsval(i) = rmsval(i) + voli*vals(i)**2 enddo totvol = totvol + voli endif enddo coltemp(:) = real(sqrt(rmsval(:)/totvol)) print "(1x,a,es9.2)",'volume = ',totvol ! !--write output to screen/terminal ! do i=1,ncolumns print "(1x,a20,'rms (volume weighted) = ',es18.10)",label(i),coltemp(i) enddo ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) ncolumns+1 write(iunit,fmtstring) timei,coltemp(1:ncolumns) case('rhovar','rhomach') if (irho.le.0 .or. irho.gt.ncolumns) then print "(a)",' ERROR in density variance--rms velocity field calculation!'// & ' density not present / not labelled in dump file, skipping...' return endif ! !--warn if particle masses not found ! if (ipmass.le.0 .or. ipmass.gt.ncolumns .and. all(massoftype < tiny(massoftype))) then print "(a)",' WARNING in volume weighted rms calculation!'// & ' masses not read or are zero from dump file' endif if (ivx.le.0 .or. ivx.gt.ncolumns) then print "(a)",' WARNING in volume weighted rms calculation!'// & ' velocities not present / not labelled in dump file' endif ! !--calculate mean density and rms velocity values on first pass ! rmsvali = 0. rmsvmw = 0. rhomeanvw = 0. rhomeanmw = 0. totvol = 0. smeanvw = 0. smeanmw = 0. do i=1,ntot itype = igettype(i) pmassi = particlemass(i,itype) rhoi = dat(i,irho) if (rhoi.gt.0.) then voli = pmassi/rhoi else voli = 0. endif rhomeanmw = rhomeanmw + rhoi rhomeanvw = rhomeanvw + pmassi si = log(rhoi) smeanmw = smeanmw + si smeanvw = smeanvw + voli*si totvol = totvol + voli ! !--mean squared velocity ! if (ivx.gt.0 .and. ivx.le.ncolumns) then v2i = dot_product(dat(i,ivx:ivx+ndimV-1),dat(i,ivx:ivx+ndimV-1)) rmsvali = rmsvali + voli*v2i rmsvmw = rmsvmw + v2i endif enddo ! !--use the computed volume for velocity, otherwise won't be normalised correctly ! rmsvali = sqrt(rmsvali/totvol) rmsvmw = sqrt(rmsvmw/dble(ntot)) ! !--option to override volume from sum with environment variable ! voli = renvironment('SPLASH_CALC_VOLUME',errval=-1.0) if (voli.gt.0.) then print "(1x,a,es9.2)",& 'volume from sum(m/rho) = ',totvol totvol = voli print "(1x,a,es9.2,/)",& '**overridden with SPLASH_CALC_VOLUME = ',totvol else print "(1x,a,es9.2,/,1x,a,/)",& 'volume from sum(m/rho) = ',totvol,& '(override this using SPLASH_CALC_VOLUME environment variable)' endif rhomeanmw = rhomeanmw/real(ntot) rhomeanvw = rhomeanvw/totvol smeanmw = smeanmw/real(ntot) smeanvw = smeanvw/totvol ! !--calculate variance on second pass ! rhovarvw = 0. rhovarmw = 0. svarvw = 0. svarmw = 0. totvol = 0. do i=1,ntot itype = igettype(i) pmassi = particlemass(i,itype) rhoi = dat(i,irho) if (rhoi.gt.0.) then voli = pmassi/rhoi si = log(rhoi) else voli = 0. si = 0. endif totvol = totvol + voli rhovarmw = rhovarmw + (rhoi - rhomeanmw)**2 rhovarvw = rhovarvw + voli*(rhoi - rhomeanvw)**2 svarmw = svarmw + (si - smeanmw)**2 svarvw = svarvw + voli*(si - smeanvw)**2 enddo rhovarmw = rhovarmw/real(ntot) rhovarvw = rhovarvw/totvol svarmw = svarmw/real(ntot) svarvw = svarvw/totvol ! !--write output to screen/terminal ! print "(1x,'mean density (vol. weighted) = ',es11.4,' +/- ',es11.4)",rhomeanvw,sqrt(rhovarvw) print "(1x,'mean density (mass weighted) = ',es11.4,' +/- ',es11.4)",rhomeanmw,sqrt(rhovarmw) print "(1x,'density variance (vol. weighted) = ',es11.4)",rhovarvw print "(1x,'density variance (mass weighted) = ',es11.4)",rhovarmw print "(1x,'mean ln density (vol. weighted) = ',es11.4,' +/- ',es11.4)",smeanvw,sqrt(svarvw) print "(1x,' -0.5*var(ln density) = ',es11.4)",-0.5*svarvw print "(1x,'mean ln density (mass weighted) = ',es11.4,' +/- ',es11.4)",smeanmw,sqrt(svarmw) print "(1x,'ln density variance (vol. weighted) = ',es11.4)",svarvw print "(1x,'ln density variance (mass weighted) = ',es11.4)",svarmw print "(1x,'rms velocity (vol. weighted) = ',es11.4)",rmsvali print "(1x,'rms velocity (mass weighted) = ',es11.4)",rmsvmw if (rmsvali.gt.0.) then bval = sqrt(svarvw)/rmsvali else bval = 0. endif if (rmsvmw.gt.0.) then bvalmw = sqrt(svarmw)/rmsvmw else bvalmw = 0. endif print "(1x,'sqrt(sigma^2/v^2)(vol. weighted) = ',f11.3)",bval print "(1x,'sqrt(sigma^2/v^2)(mass weighted) = ',f11.3)",bvalmw ! !--write line to output file ! write(fmtstring,"('(',i3,'(es18.10,1x))')",iostat=ierr) 17 write(iunit,fmtstring) timei,rhomeanvw,rhomeanmw,rhovarvw,rhovarmw,sqrt(rhovarvw),sqrt(rhovarmw),& rmsvali,rmsvmw,bval,bvalmw,smeanvw,smeanmw,svarvw,svarmw,sqrt(svarvw),sqrt(svarmw) case('kh') if (irho.le.0 .or. irho.gt.ncolumns) then print "(a)",' ERROR in kh calculation!'// & ' density not present / not labelled in dump file, skipping...' return endif if (ivx.le.0 .or. ivx.gt.ncolumns) then print "(a)",' WARNING in kh calculation!'// & ' velocities not present / not labelled in dump file' endif ! !--calculate volume-weighted RMS for each column ! ekinymax = 0. do i=1,ntot ekiny = 0.5*dat(i,irho)*dat(i,ivx+1)**2 ekinymax = max(ekiny,ekinymax) enddo print "(1x,a,es9.2)",'ekiny(max) = ',ekinymax write(iunit,"(2(es18.10,1x))") timei,ekinymax case('timeaverage','timeav') if (.not.allocated(datmean)) then allocate(datmean(ntot,ncolumns),stat=ierr) if (ierr /= 0) stop 'error allocating memory for mean sum in calc' datmean = 0. endif if (.not.allocated(datvar)) then allocate(datvar(ntot,ncolumns),stat=ierr) if (ierr /= 0) stop 'error allocating memory for variance sum in calc' datvar = 0. endif ntot1 = size(datmean(:,1)) if (ntot.gt.ntot1) then print*,' WARNING: nrows = ',ntot,' > nrows from previous dumpfile =',ntot1 print*,' ignoring all rows/particles greater than ',ntot1 elseif (ntot.lt.ntot1) then print*,' WARNING: nrows = ',ntot,' < nrows from previous dumpfile =',ntot1 print*,' assuming zeros for rows/particles greater than ',ntot endif ncol1 = size(datmean(1,:)) if (ncolumns.gt.ncol1) then print*,' WARNING: ncolumns = ',ncolumns,' > ncolumns from previous dumpfile =',ncol1 print*,' ignoring all rows/particles greater than ',ncol1 elseif (ncolumns.lt.ncol1) then print*,' WARNING: ncolumns = ',ntot,' < ncolumns from previous dumpfile =',ncol1 print*,' assuming zeros for columns greater than ',ncolumns endif ntot1 = min(ntot1,ntot) ncol1 = min(ncol1,ncolumns) dn = 1./real(nfilesread) ! !--compute the mean and variance using Knuth/Welford's compensated ! summation algorithm to minimise round-off error ! (see http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance) ! do j=1,ncol1 do i=1,ntot1 delta = dat(i,j) - datmean(i,j) datmean(i,j) = datmean(i,j) + delta*dn datvar(i,j) = datvar(i,j) + delta*(dat(i,j) - datmean(i,j)) enddo enddo return !sum1(:,:) = sum1(:,:) + dat(1:ntot1,1:ncol1) !sum2(:,:) = sum2(:,:) + dat(1:ntot1,1:ncol1)**2 case('ratio') if (.not.allocated(datmean)) then allocate(datmean(size(dat(:,1)),size(dat(1,:))),stat=ierr) if (ierr /= 0) stop 'error allocating temporary memory in calc' datmean = 0. endif if (.not.allocated(datvar)) then allocate(datvar(size(dat(:,1)),size(dat(1,:))),stat=ierr) if (ierr /= 0) stop 'error allocating memory in calc' datvar = 0. endif ntot1 = size(datmean(:,1)) if (ntot.gt.ntot1) then print*,' WARNING: nrows = ',ntot,' > nrows from previous dumpfile =',ntot1 print*,' ignoring all rows/particles greater than ',ntot1 elseif (ntot.lt.ntot1) then print*,' WARNING: nrows = ',ntot,' < nrows from previous dumpfile =',ntot1 print*,' assuming zeros for rows/particles greater than ',ntot endif ncol1 = size(datmean(1,:)) if (size(dat(1,:)).gt.ncol1) then print*,' WARNING: ncolumns = ',ncolumns,' > ncolumns from previous dumpfile =',ncol1 print*,' ignoring all rows/particles greater than ',ncol1 elseif (ncolumns.lt.ncol1) then print*,' WARNING: ncolumns = ',ntot,' < ncolumns from previous dumpfile =',ncol1 print*,' assuming zeros for columns greater than ',ncolumns endif ntot1 = min(ntot1,ntot) ncol1 = min(ncol1,size(dat(1,:))) if (ntot1.le.0 .or. ncol1.le.0) then print "(a,i2,a,i2,a)",' ERROR: nrows = ',ntot1,' ncolumns = ',ncol1,' aborting...' return endif if (nfilesread.le.1) then !--store first dump datmean(1:ntot1,1:ncol1) = dat(1:ntot1,1:ncol1) else where (abs(datmean(1:ntot1,1:ncol1)) > epsilon(0.)) datvar(1:ntot1,1:ncol1) = dat(1:ntot1,1:ncol1)/datmean(1:ntot1,1:ncol1) ! ratio of current data to first step elsewhere datvar(1:ntot1,1:ncol1) = dat(1:ntot1,1:ncol1)/(datmean(1:ntot1,1:ncol1) + epsilon(0.)) end where valmin = datvar(1,1) valmax = datvar(1,1) valmean = 0. do j=1,ncol1 do i=1,ntot1 valmin = min(datvar(i,j),valmin) valmax = max(datvar(i,j),valmin) valmean = valmean + datvar(i,j) enddo enddo valmean = valmean/real(ntot1*ncol1) print "(/,a,es10.3)",' max ratio = ',valmax print "(a,es10.3)",' min ratio = ',valmin print "(a,es10.3,/)",' mean ratio = ',valmean print "(a)",'----> WRITING ratio.out ...' if (allocated(datmean) .and. allocated(datvar)) then write(iunit,"('# ',i4,1x,i4)") ntot1,ncol1 write(fmtstring,"(a,i6,a)",iostat=ierr) '(',ncol1,'(es14.6,1x,))' do i=1,ntot1 write(iunit,fmtstring) datvar(i,:) enddo endif endif return case default print "(a)",' ERROR: unknown analysis type in write_analysis routine' return end select print "(/,1x,'>>> ',a,' <<<')",'written to '//trim(fileout) return contains ! !--small internal utility to work out the particle type ! (which depends whether or not mixed types are stored) ! integer function igettype(i) implicit none integer :: np integer, intent(in) :: i if (size(iamtype).gt.1) then igettype = int(iamtype(i)) else np = 0 igettype = 0 do while (i.gt.np .and. igettype.le.ntypes) igettype = igettype + 1 np = np + npartoftype(igettype) enddo endif end function igettype ! !--small internal utility to get particle mass ! (depends on whether or not mass is stored for each particle ! or only for each type) ! real function particlemass(i,iparttype) implicit none integer, intent(in) :: i,iparttype if (ipmass.gt.0 .and. ipmass.le.ncolumns) then particlemass = dat(i,ipmass) else particlemass = massoftype(iparttype) endif end function particlemass end subroutine write_analysis subroutine cross_product3D(veca,vecb,vecc) use params, only:doub_prec implicit none real(kind=doub_prec), dimension(3), intent(in) :: veca,vecb real(kind=doub_prec), dimension(3), intent(out) :: vecc vecc(1) = veca(2)*vecb(3) - veca(3)*vecb(2) vecc(2) = veca(3)*vecb(1) - veca(1)*vecb(3) vecc(3) = veca(1)*vecb(2) - veca(2)*vecb(1) end subroutine cross_product3D !--------------------- ! close output file !--------------------- subroutine close_analysis(analysistype) implicit none character(len=*), intent(in) :: analysistype integer :: i select case(trim(analysistype)) case('timeaverage','timeav') print "(a)",'----> WRITING time_average.out ...' if (allocated(datmean) .and. allocated(datvar) .and. nfilesread.gt.0) then !--get standard deviation from variance (also normalise with 1/n) datvar(:,:) = sqrt(datvar(:,:))/sqrt(real(nfilesread)) do i=1,size(datmean(:,1)) write(iunit,"(1x,99(es15.7,2x))") datmean(i,:),datvar(i,:) enddo endif end select if (allocated(datmean)) deallocate(datmean) if (allocated(datvar)) deallocate(datvar) close(unit=iunit) return end subroutine close_analysis end module analysis splash/src/interpolate1D.f90000644 000766 000000 00000012210 13261626263 016602 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! ! Module containing all of the routines required for 1D interpolation ! !---------------------------------------------------------------------- module interpolations1D implicit none public :: interpolate1D contains !-------------------------------------------------------------------------- ! subroutine to interpolate from particle data to even 1D grid of pixels ! ! The data is smoothed using the SPH summation interpolant, ! that is, we compute the smoothed array according to ! ! datsmooth(pixel) = sum_j w_j W(r-r_j, h_j) ! ! where _j is the quantity at the neighbouring particle j and ! W is the smoothing kernel, for which we use the usual cubic spline. ! For an SPH interpolation the weight for each particle should be ! the dimensionless quantity ! ! w_j = m_j / (rho_j * h_j**ndim) ! ! Other weights can be used (e.g. constants), but in this case the ! normalisation option should also be set. ! ! Input: particle coordinates : x (npart) ! smoothing lengths : hh (npart) ! interpolation weights : weight (npart) ! scalar data to smooth : dat (npart) ! ! number of pixels in x : npixx ! pixel width : pixwidth ! option to normalise interpolation : normalise (.true. or .false.) ! ! Output: smoothed data : datsmooth (npixx) ! ! Written by Daniel Price 2003-2006 !-------------------------------------------------------------------------- subroutine interpolate1D(x,hh,weight,dat,itype,npart, & xmin,datsmooth,npixx,pixwidth,normalise) use kernels, only:cnormk1D,radkernel,wfunc implicit none integer, intent(in) :: npart,npixx real, intent(in), dimension(npart) :: x,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,pixwidth real, intent(out), dimension(npixx) :: datsmooth logical, intent(in) :: normalise real, dimension(npixx) :: datnorm integer :: i,ipix,ipixmin,ipixmax real :: hi,hi1,radkern,q2,wab,const real :: term,termnorm,dx,xpix datsmooth = 0. term = 0. if (normalise) then print*,'interpolating (normalised) from particles to 1D grid: npix,xmin,max=',npixx,xmin,xmin+npixx*pixwidth else print*,'interpolating (non-normalised) from particles to 1D grid: npix,xmin,max=',npixx,xmin,xmin+npixx*pixwidth endif if (pixwidth.le.0.) then print*,'interpolate1D: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate1D: warning: ignoring some or all particles with h < 0' endif const = cnormk1D ! normalisation constant ! !--loop over particles ! over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--skip particles with zero weights ! termnorm = const*weight(i) if (termnorm.le.0.) cycle over_parts ! !--skip particles with wrong h's ! hi = hh(i) if (hi.le.tiny(hi)) cycle over_parts ! !--set kernel related quantities ! hi1 = 1./hi radkern = radkernel*hi ! radius of the smoothing kernel term = termnorm*dat(i) ! !--for each particle work out which pixels it contributes to ! ipixmin = int((x(i) - radkern - xmin)/pixwidth) ipixmax = int((x(i) + radkern - xmin)/pixwidth) + 1 if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (ipixmax.gt.npixx) ipixmax = npixx ! to pixels in the image ! !--loop over pixels, adding the contribution from this particle ! do ipix = ipixmin,ipixmax xpix = xmin + (ipix-0.5)*pixwidth dx = xpix - x(i) q2 = dx*dx*hi1*hi1 ! !--SPH kernel - standard cubic spline ! wab = wfunc(q2) ! !--calculate data value at this pixel using the summation interpolant ! datsmooth(ipix) = datsmooth(ipix) + term*wab if (normalise) datnorm(ipix) = datnorm(ipix) + termnorm*wab enddo enddo over_parts ! !--normalise dat array ! if (normalise) then where (datnorm > 0.) datsmooth = datsmooth/datnorm end where endif return end subroutine interpolate1D end module interpolations1D splash/src/exact_bondi.f90000644 000766 000000 00000017132 13261626263 016356 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! Exact solution for Bondi flow ! ---------------------------------------------------------------------- module bondi implicit none public :: exact_bondi real, private, parameter :: pi = 3.1415926536 ! Constants from user input real, private :: rcrit, rhocrit ! for non-rel real, private :: den0, en0 ! for GR geodesic flow real, private :: adiabat ! for GR sonic-point flow (and rcrit as well) real, private :: C1,C2,Tc,n ! for GR sonic-point flow: intermediate constants not input by user real, private :: mass1 logical, private :: iswind private contains subroutine exact_bondi(iplot,time,gamma,const1,const2,m,relativistic,geodesic_flow,is_wind,xpts,ypts,ierr) integer, intent(in) :: iplot integer, intent(out) :: ierr real, intent(in) :: time,gamma,const1,const2,m logical, intent(in) :: relativistic, geodesic_flow,is_wind real, dimension(:), intent(in) :: xpts real, dimension(size(xpts)), intent(out) :: ypts integer :: i,npts real :: r,rhor,vr,ur npts = size(xpts) print "(4(a,g8.2))",' Plotting exact Bondi solution at t = ',time if (.not.relativistic) then rcrit = const1 rhocrit = const2 elseif (relativistic) then if (geodesic_flow) then den0 = const1 en0 = const2 elseif (.not.geodesic_flow) then rcrit = const1 adiabat = const2 iswind = is_wind endif endif ! !--determine which parameter to plot ! do i=1,npts r = xpts(i) vr = 0. ur = 0. rhor = 0. ! Note: Lambert functions solutions not great below 0.3 for some rcrit and rhocrit if (.not. relativistic .and. r>0.3) then call get_bondi_nonrel(rhor,vr,ur,r,m,gamma) elseif (relativistic .and. r>2.) then if (geodesic_flow) then call get_bondi_geodesic(rhor,vr,ur,r,m,gamma) elseif (.not. geodesic_flow) then call get_bondi_sonicpoint(rhor,vr,ur,r,m,gamma) endif endif select case(iplot) case(1) ypts(i) = vr if (.not.is_wind) ypts(i) = -vr case(2) ypts(i) = ur case(3) ypts(i) = rhor case default ypts(i) = 0. end select enddo ierr = 0 return end subroutine exact_bondi !------------------------------------------------------------------------ !--- Non-relativistic solution ------------------------------------------ !------------------------------------------------------------------------ subroutine get_bondi_nonrel(rho,v,u,r,mass,gamma) real, intent(out) :: rho,v,u real, intent(in) :: r,mass,gamma real :: cs2,vr,mdot cs2 = mass/(2.*rcrit) mdot = rhocrit*4.*pi*rcrit**2*sqrt(cs2) if (r>=rcrit) then vr = sqrt(-cs2*lambertw_0(-func(r,rcrit))) else vr = sqrt(-cs2*lambertw_neg1(-func(r,rcrit))) endif v = vr rho = mdot/(4.*pi*abs(v)*r**2) u = 0. end subroutine get_bondi_nonrel ! See Barry, Parlange & Li 2000, for the analytic approximations used for the Lambert W function. ! !--- Lambert W function for the principal branch (k=0) approaching from the negative real function lambertw_0(x) real, intent(in) :: x real, parameter :: exp1 = exp(1.) real :: eta, N1, N2 eta = 2. + 2.*exp1*x N2 = 6. + 3.*Sqrt(2.) - ((-5764. - 4108.*Sqrt(2.) + (2237. + 1457.*Sqrt(2.))*exp1)*eta)/& (-796. - 430.*Sqrt(2.) + (215. + 199.*Sqrt(2.))*exp1) N1 = (1. - 1./Sqrt(2.))*(Sqrt(2.) + N2) lambertw_0 = -1 + Sqrt(eta)/(1 + (Sqrt(eta)*N1)/(Sqrt(eta) + N2)) end function lambertw_0 !--- Lambert W function for the k=-1 branch real function lambertw_neg1(x) real, intent(in) :: x real, parameter :: M1 = 0.3361, M2 = -0.0042, M3 = -0.0201 real :: sigma sigma = -1. - Log(-x) lambertw_neg1 = -1. - sigma - (2.*(1. - 1./(1. + (M1*Sqrt(sigma)*(1. + exp(M3*Sqrt(sigma))*M2*sigma))/Sqrt(2.))))/M1 end function lambertw_neg1 !--- Function used in the non-rel solution of velocity [D(r)] (See: Cranmer 2004) real function func(r,rc) real, intent(in) :: r,rc func = (rc/r)**4 * exp(4.*(1.-rc/r) - 1.) end function func !------------------------------------------------------------------------ !--- GR geodesic flow solution ------------------------------------------ !------------------------------------------------------------------------ subroutine get_bondi_geodesic(rho,v,u,r,m,gamma) real, intent(out) :: rho,v,u real, intent(in) :: r,m,gamma real :: sqrtg,alpha,dfunc,efunc dfunc = den0/(r**2*sqrt(2.*m/r*(1.- 2.*m/r))) efunc = en0/((sqrt(2.*m/r)*r**2)**gamma * (1.- 2.*m/r)**((gamma + 1.)/4.)) sqrtg = 1. alpha = sqrt(1. - 2.*m/r) rho = sqrtg/alpha*dfunc v = sqrt(2.*m/r)*(1. - 2.*m/r) u = efunc/dfunc end subroutine get_bondi_geodesic !------------------------------------------------------------------------ !--- GR sonic point flow solution --------------------------------------- !------------------------------------------------------------------------ subroutine get_bondi_sonicpoint(rho,v,u,r,m,gamma) real, intent(out) :: rho,v,u real, intent(in) :: r,m,gamma real :: T,uvel,term,u0,dens,sqrtg real :: uc2,vc2 n = 1./(gamma-1.) mass1 = m uc2 = mass1/(2.*rcrit) vc2 = uc2/(1.-3.*uc2) Tc = vc2*n/(1.+n-vc2*n*(1.+n)) C1 = sqrt(uc2) * Tc**n * rcrit**2 C2 = (1. + (1.+n)*Tc)**2 * (1. - 2.*mass1/rcrit + C1**2/(rcrit**4*Tc**(2.*n))) ! Given an r, solve eq 76 for T numerically (Hawley, Smarr and Wilson 1976a) call Tsolve(T,r) uvel = C1/(r**2 * T**n) dens = adiabat*T**n u = T*n !get u0 at r term = 1./(1.-2.*mass1/r) u0 = sqrt(term*(1.+term*uvel**2)) v = uvel/u0 sqrtg = 1. rho = sqrtg*u0*dens end subroutine get_bondi_sonicpoint ! Newton Raphson subroutine Tsolve(T,r) real, intent(in) :: r real, intent(out) :: T real :: Tnew, diff logical :: converged integer :: its integer, parameter :: itsmax = 100 real, parameter :: tol = 1.e-5 ! These guess values may need to be adjusted for values of rcrit other than rcrit=8M if ((iswind .and. r>=rcrit) .or. (.not.iswind .and. r=rcrit)) then T = 100. endif converged = .false. its = 0 do while (.not.converged .and. its 3) then ierr = ierr_invalid_dimsin if (present(err)) err = ierr return elseif (ndimout < 1.or.ndimout > 3) then ierr = ierr_invalid_dimsout if (present(err)) err = ierr return elseif (ndimout > ndimin) then ierr = ierr_invalid_dims if (present(err)) err = ierr return elseif (abs(xin(1)) < 1e-8 .and. ndimout >= 2 .and. & (itypein==2 .or. itypein==3)) then ierr = ierr_r_is_zero if (present(err)) err = ierr xout(1:ndimout) = xin(1:ndimout) return endif ! !--now do transformation ! select case(itypein) ! !--input is cylindrical polars ! case(2) ! ! output is cartesian (default) ! if (itypeout /= 1) ierr = ierr_warning_assuming_cartesian if (ndimout==1) then xout(1) = xin(1) else ! r,phi,z -> x,y,z xout(1) = xin(1)*cos(xin(2)) xout(2) = xin(1)*sin(xin(2)) if (ndimout > 2) xout(3) = xin(3) endif ! !--input is spherical polars ! case(3) ! ! output is cartesian (default) ! if (itypeout /= 1) ierr = ierr_warning_assuming_cartesian select case(ndimout) case(1) ! r -> x xout(1) = xin(1) case(2) ! r,phi -> x,y xout(1) = xin(1)*cos(xin(2)) xout(2) = xin(1)*sin(xin(2)) case(3) ! r,phi,theta -> x,y,z sintheta = sin(xin(3)) xout(1) = xin(1)*cos(xin(2))*sintheta xout(2) = xin(1)*sin(xin(2))*sintheta xout(3) = xin(1)*cos(xin(3)) end select ! !--input is torus co-ordinates ! case(4) ! ! output is cartesian (default) ! if (itypeout /= 1) ierr = ierr_warning_assuming_cartesian if (ndimin /= 3) then xout(1:ndimout) = xin(1:ndimout) else rcyl = xin(1)*cos(xin(2)) + Rtorus xout(1) = rcyl*cos(xin(3)) if (ndimout >= 2) xout(2) = rcyl*sin(xin(3)) if (ndimout >= 3) xout(3) = xin(1)*sin(xin(2)) endif case(5) ! !--input is rotated cartesian coordinates ! ! ! output is cartesian ! if (itypeout /= 1) ierr = ierr_warning_assuming_cartesian xi = 0. xi(1:ndimin) = xin xouti(1) = xi(1)*cosa*cosb - xi(2)*sinb - xi(3)*sina*cosb xouti(2) = xi(1)*cosa*sinb + xi(2)*cosb - xi(3)*sina*sinb xouti(3) = xi(1)*sina + xi(3)*cosa xout(1:ndimout) = xouti ! !--input is cartesian co-ordinates ! case default select case(itypeout) case(2) ! !--output is cylindrical ! if (ndimin==1) then xout(1) = abs(xin(1)) ! cylindrical r else xout(1) = sqrt(dot_product(xin(1:2),xin(1:2))) if (ndimout >= 2) xout(2) = atan2(xin(2),xin(1)) ! phi if (ndimout==3) xout(3) = xin(3) ! z endif case(3) ! !--output is spherical ! xout(1) = sqrt(dot_product(xin,xin))! r if (ndimout >= 2) xout(2) = atan2(xin(2),xin(1)) ! phi if (ndimout >= 3) then ! theta = acos(z/r) xout(3) = acos(xin(3)/xout(1)) endif case(4) ! !--output is torus r,theta,phi co-ordinates ! if (ndimin /= 3) then ! not applicable if ndim < 3 xout(1:ndimout) = xin(1:ndimout) else rcyl = sqrt(xin(1)**2 + xin(2)**2) xout(1) = sqrt(xin(3)**2 + (rcyl - Rtorus)**2) if (ndimout >= 2) xout(2) = atan2(xin(3),rcyl-Rtorus) ! asin(xin(3)/xout(1)) if (ndimout >= 3) xout(3) = atan2(xin(2),xin(1)) endif case(5) ! !--output is rotated cartesian x1, x2, x3 ! xi = 0. xi(1:ndimin) = xin xouti(1) = cosa*(xi(1)*cosb + xi(2)*sinb) + xi(3)*sina xouti(2) = xi(2)*cosb - xi(1)*sinb xouti(3) = -sina*(xi(1)*cosb + xi(2)*sinb) + xi(3)*cosa xout(1:ndimout) = xouti(1:ndimout) case default ! ! just copy ! xout(1:ndimout) = xin(1:ndimout) end select end select if (present(err)) err = ierr return end subroutine coord_transform !----------------------------------------------------------------- ! Subroutine to transform vector components ! between different co-ordinate systems ! (e.g. from cartesian to cylindrical polar and vice versa) ! ! Arguments: ! xin(ndimin) : input co-ordinates, in ndimin dimensions ! vecin(ndimin) : components of vector in input co-ordinate basis ! itypein : input co-ordinate type ! ! vecout(ndimout) : components of vector in output co-ordinate basis ! itypeout : output co-ordinate type ! ! coords must be one of the following: ! 'cartesian' (default) ! 'cylindrical' ! 'spherical' ! ! Currently handles: ! ! cartesian -> cylindrical, spherical polar ! cylindrical -> cartesian ! spherical polar -> cartesian ! !----------------------------------------------------------------- pure subroutine vector_transform(xin,vecin,ndimin,itypein,vecout,ndimout,itypeout,err) integer, intent(in) :: ndimin,ndimout,itypein,itypeout real, intent(in) :: xin(ndimin),vecin(ndimin) real, intent(out) :: vecout(ndimout) integer, intent(out), optional :: err integer :: i,ierr real :: dxdx(3,3) real :: sinphi, cosphi real :: rr,rr1,rcyl,rcyl2,rcyl1 ierr = 0 ! !--check for errors in input ! if (ndimout > ndimin) then ierr = ierr_invalid_dims if (present(err)) err = ierr return elseif (itypein==itypeout) then vecout(1:ndimout) = vecin(1:ndimout) return elseif (ndimin < 1.or.ndimin > 3) then ierr = ierr_invalid_dimsin if (present(err)) err = ierr return elseif (ndimout < 1.or.ndimout > 3) then ierr = ierr_invalid_dimsout if (present(err)) err = ierr return elseif (abs(xin(1)) < 1e-8 .and. & (itypein==2 .or. itypein==3)) then ierr = ierr_r_is_zero if (present(err)) err = ierr vecout = 0. return endif ! !--set Jacobian matrix to zero ! dxdx = 0. ! !--calculate non-zero components of Jacobian matrix for the transformation ! select case(itypein) ! !--rotated cartesian ! case(5) call coord_transform(vecin,ndimin,itypein,vecout,ndimout,itypeout,err=ierr) if (present(err)) err = ierr return ! !--input is toroidal ! case(4) select case(itypeout) case default dxdx(1,1) = cos(xin(2))*cos(xin(3)) ! dx/dr dxdx(1,2) = -sin(xin(2))*cos(xin(3)) ! 1/r dx/dtheta dxdx(1,3) = sin(xin(3)) ! 1/rcyl dx/dphi dxdx(2,1) = cos(xin(2))*sin(xin(3)) ! dy/dr dxdx(2,2) = -sin(xin(2))*sin(xin(3)) ! 1/r dy/dtheta dxdx(2,3) = cos(xin(3)) ! 1/rcyl dy/dphi dxdx(3,1) = sin(xin(3)) ! dz/dr dxdx(3,2) = cos(xin(3)) ! 1/r dz/dtheta ! dxdx(3,3) = 0. ! dz/dphi end select ! !--input is spherical polars ! case(3) select case(itypeout) case default ! ! output is cartesian (default) ! dxdx(1,1) = cos(xin(2))*sin(xin(3)) ! dx/dr dxdx(1,2) = -sin(xin(2)) ! 1/rcyl dx/dphi dxdx(1,3) = cos(xin(2))*cos(xin(3)) ! 1/r dx/dtheta dxdx(2,1) = sin(xin(2))*sin(xin(3)) ! dy/dr dxdx(2,2) = cos(xin(2)) ! 1/rcyl dy/dphi dxdx(2,3) = sin(xin(2))*cos(xin(3)) ! 1/r dy/dtheta dxdx(3,1) = cos(xin(3)) ! dz/dr dxdx(3,3) = -sin(xin(3)) ! 1/r dz/dtheta end select ! !--input is cylindrical polars ! case(2) select case(itypeout) case default ! ! output is cartesian (default) ! sinphi = sin(xin(2)) cosphi = cos(xin(2)) dxdx(1,1) = cosphi ! dx/dr dxdx(1,2) = -sinphi ! 1/r*dx/dphi dxdx(2,1) = sinphi ! dy/dr dxdx(2,2) = cosphi ! 1/r*dy/dphi dxdx(3,3) = 1. ! dz/dz end select ! !--input is cartesian co-ordinates (default) ! case default select case(itypeout) case(5) call coord_transform(vecin,ndimin,itypein,vecout,ndimout,itypeout,err=ierr) return case(4) ! ! output is toroidal ! rcyl = sqrt(xin(1)**2 + xin(2)**2) if (rcyl > tiny(rcyl)) then rcyl1 = 1./rcyl else rcyl1 = 0. endif rr = sqrt((rcyl - Rtorus)**2 + xin(3)**2) if (rr > tiny(rr)) then rr1 = 1./rr else rr1 = 0. endif dxdx(1,1) = (rcyl - Rtorus)*xin(1)*rr1*rcyl1 ! dr/dx dxdx(1,2) = (rcyl - Rtorus)*xin(2)*rr1*rcyl1 ! dr/dy dxdx(1,3) = xin(3)*rr1 ! dr/dz dxdx(2,1) = -xin(3)*xin(1)*rr1*rcyl1 ! dtheta/dx dxdx(2,2) = -xin(3)*xin(2)*rr1*rcyl1 ! dtheta/dy dxdx(2,3) = (rcyl - Rtorus)*rr1 ! dtheta/dz dxdx(3,1) = -xin(2)*rcyl1 ! dphi/dx dxdx(3,2) = xin(1)*rcyl1 ! dphi/dy ! dxdx(3,3) = 0. ! dphi/dz case(3) ! ! output is spherical ! rr = sqrt(dot_product(xin,xin)) if (rr > tiny(rr)) then rr1 = 1./rr else rr1 = 0. endif dxdx(1,1) = xin(1)*rr1 ! dr/dx if (ndimin >= 2) dxdx(1,2) = xin(2)*rr1 ! dr/dy if (ndimin==3) dxdx(1,3) = xin(3)*rr1 ! dr/dz if (ndimin >= 2) then rcyl2 = dot_product(xin(1:2),xin(1:2)) rcyl = sqrt(rcyl2) if (rcyl > tiny(rcyl)) then rcyl1 = 1./rcyl else rcyl1 = 0. endif dxdx(2,1) = -xin(2)*rcyl1 ! rcyl dphi/dx dxdx(2,2) = xin(1)*rcyl1 ! rcyl dphi/dy dxdx(2,3) = 0. if (ndimin >= 3) then dxdx(3,1) = xin(1)*xin(3)*rr1*rcyl1 ! r dtheta/dx dxdx(3,2) = xin(2)*xin(3)*rr1*rcyl1 ! r dtheta/dy dxdx(3,3) = -rcyl2*rr1*rcyl1 ! r dtheta/dz endif endif case(2) ! !--output is cylindrical ! rr = sqrt(dot_product(xin(1:min(ndimin,2)),xin(1:min(ndimin,2)))) if (rr > tiny(rr)) then rr1 = 1./rr else rr1 = 0. endif dxdx(1,1) = xin(1)*rr1 ! dr/dx if (ndimin >= 2) dxdx(1,2) = xin(2)*rr1 ! dr/dy if (ndimout >= 2) then dxdx(2,1) = -xin(2)*rr1 ! r*dphi/dx dxdx(2,2) = xin(1)*rr1 ! r*dphi/dy if (ndimout==3) dxdx(3,3) = 1. ! dz/dz endif case default ierr = ierr_warning_assuming_cartesian vecout(1:ndimout) = vecin(1:ndimout) return end select end select ! !--now perform transformation using Jacobian matrix ! do i=1,ndimout vecout(i) = dot_product(dxdx(i,1:ndimin),vecin(1:ndimin)) enddo if (present(err)) err = ierr return end subroutine vector_transform !------------------------------------------------------------------ ! this subroutine attempts to switch plot limits / boundaries ! between various co-ordinate systems. !------------------------------------------------------------------ subroutine coord_transform_limits(xmin,xmax,itypein,itypeout,ndim) integer, intent(in) :: itypein,itypeout,ndim real, dimension(ndim), intent(inout) :: xmin,xmax real, dimension(ndim) :: xmaxtemp,xmintemp ! !--check for errors in input ! if (ndim < 1 .or. ndim > 3) then print*,'Error: limits coord transform: ndim invalid on input' return endif !print*,'modifying plot limits for new coordinate system' ! !--by default do nothing ! xmintemp(1:ndim) = xmin(1:ndim) xmaxtemp(1:ndim) = xmax(1:ndim) select case(itypein) case(5) ! !--rotated cartesian ! call coord_transform(xmin,ndim,itypein,xmintemp,ndim,itypeout) call coord_transform(xmax,ndim,itypein,xmaxtemp,ndim,itypeout) ! !--input is toroidal ! case(4) select case(itypeout) case default ! !--cartesian output ! xmintemp(1:min(ndim,2)) = -Rtorus - xmax(1) xmaxtemp(1:min(ndim,2)) = Rtorus + xmax(1) if (ndim==3) then xmintemp(3) = -xmax(1) xmaxtemp(3) = xmax(1) endif end select ! !--input is spherical ! case(3) select case(itypeout) case default ! !--cartesian output ! xmintemp(1:ndim) = -xmax(1) xmaxtemp(1:ndim) = xmax(1) end select ! !--input is cylindrical ! case(2) select case(itypeout) case default ! !--cartesian output ! xmintemp(1:max(ndim,2)) = -xmax(1) xmaxtemp(1:max(ndim,2)) = xmax(1) end select ! !--input is cartesian ! case default select case(itypeout) case(5) ! !--rotated cartesian ! call coord_transform(xmin,ndim,itypein,xmintemp,ndim,itypeout) call coord_transform(xmax,ndim,itypein,xmaxtemp,ndim,itypeout) case(4) ! !--output is toroidal ! xmintemp(1) = 0. xmaxtemp(1) = max(maxval(abs(xmax(1:min(ndim,2))))-Rtorus, & maxval(abs(xmin(1:min(ndim,2))))-Rtorus) if (ndim >= 2) then xmintemp(2) = -0.5*pi xmaxtemp(2) = 0.5*pi if (ndim >= 3) then xmintemp(3) = -pi xmaxtemp(3) = pi endif endif ! !--output is spherical ! case(3) !--rmin, rmax xmintemp(1) = 0. xmaxtemp(1) = max(maxval(abs(xmin(1:ndim))), & maxval(abs(xmax(1:ndim)))) if (ndim >= 2) then xmintemp(2) = -pi xmaxtemp(2) = pi if (ndim >= 3) then xmintemp(3) = 0. xmaxtemp(3) = pi endif endif ! !--output is cylindrical ! case(2) !--rmin, rmax xmintemp(1) = 0. if (ndim >= 2) then xmaxtemp(1) = sqrt(max((xmin(1)**2 + xmin(2)**2), & (xmax(1)**2 + xmax(2)**2))) xmintemp(2) = -pi xmaxtemp(2) = pi else xmaxtemp(1) = max(abs(xmin(1)),abs(xmax(1))) endif end select end select xmin(:) = min(xmintemp(:),xmaxtemp(:)) xmax(:) = max(xmintemp(:),xmaxtemp(:)) return end subroutine coord_transform_limits !------------------------------------------------------------ ! Routine to assist with interpolation in non-cartesian ! coordinate systems ! ! IN: ! xin - position in cartesian coords ! rad - radius of sphere around xin in cartesians ! OUT: ! xout - position in desired coord system ! xmin, xmax - bounding box of sphere in new coord system ! !------------------------------------------------------------ subroutine get_coord_limits(rad,xin,xout,xmin,xmax,itypein) real, intent(in) :: rad,xin(3) real, intent(out) :: xout(3),xmin(3),xmax(3) integer, intent(in) :: itypein real :: r,rcyl,dphi,dtheta select case(itypein) case(4) ! toroidal rcyl = sqrt(xin(1)**2 + xin(2)**2) r = sqrt(xin(3)**2 + (rcyl - Rtorus)**2) xout(1) = r xout(2) = atan2(xin(3),rcyl-Rtorus) ! asin(xin(3)/xout(1)) xout(3) = atan2(xin(2),xin(1)) xmin(1) = max(r-rad,0.) xmax(1) = r+rad if (r > 0. .and. xmin(1) > 0.) then dtheta = asin(rad/r) xmin(2) = max(xout(2) - dtheta,0.) xmax(2) = min(xout(2) + dtheta,pi) dphi = atan(rad/r) xmin(3) = xout(3)-dphi xmax(3) = xout(3)+dphi else xmin(2) = 0. xmax(2) = pi xmin(3) = -pi xmax(3) = pi endif case(3) ! spherical r = sqrt(dot_product(xin,xin)) xout(1) = r xout(2) = atan2(xin(2),xin(1)) ! phi xout(3) = acos(xin(3)/r) ! theta = acos(z/r) xmin(1) = max(r - rad,0.) xmax(1) = r + rad if (r > 0. .and. xmin(1) > 0.) then rcyl = sqrt(xin(1)**2 + xin(2)**2) if(rcyl > rad) then dphi = asin(rad/rcyl) xmin(2) = xout(2)-dphi xmax(2) = xout(2)+dphi else xmin(2) = -pi xmax(2) = pi endif dtheta = asin(rad/r) xmin(3) = xout(3)-dtheta xmax(3) = xout(3)+dtheta xmin(3) = max(xmin(3),0.) xmax(3) = min(xmax(3),pi) else xmin(2) = -pi xmax(2) = pi xmin(3) = 0. xmax(3) = pi endif case(2) ! cylindrical r = sqrt(xin(1)**2 + xin(2)**2) xout(1) = r xout(2) = atan2(xin(2),xin(1)) xout(3) = xin(3) xmin(1) = max(r-rad,0.) xmax(1) = r+rad if (r > 0. .and. xmin(1) > 0.) then dphi = atan(rad/r) xmin(2) = xout(2)-dphi xmax(2) = xout(2)+dphi else xmin(2) = -pi xmax(2) = pi endif xmin(3) = xout(3)-rad xmax(3) = xout(3)+rad case default xout = xin xmin = xin - rad xmax = xin + rad end select end subroutine get_coord_limits end module geometry splash/src/read_data_ndspmhd.f90000644 000766 000000 00000035410 13261626263 017517 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! the data is stored in the global array dat ! ! THIS VERSION FOR DAN'S SPMHD CODE (BINARY DUMPS) ! -> Now automatically handles single/double precision ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use exact, only:hfact use particle_data, only:npartoftype,time,gamma,dat,maxpart,maxstep,maxcol,iamtype use params use filenames, only:nfiles use settings_data, only:ndim,ndimV,ncolumns,ncalc,icoords,iformat, & buffer_data,iverbose,debugmode use mem_allocation, only:alloc use geometry, only:labelcoordsys use system_utils, only:lenvironment use labels, only:labeltype,print_types implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+4) :: datfile integer :: i,icol,ierr,iunit,ilen,j,ilast integer :: ncol_max,ndim_max,npart_max,ndimV_max,nstep_max integer :: npartin,ntotin,ncolstep,nparti,ntoti integer, dimension(3) :: ibound logical :: reallocate, singleprecision real :: timein, gammain, hfactin real, dimension(3) :: xmin, xmax real(doub_prec) :: timeind,gammaind,hfactind real(doub_prec), dimension(3) :: xmind, xmaxd real(doub_prec), dimension(:), allocatable :: dattempd integer, dimension(:), allocatable :: itype character(len=20) :: geomfile iunit = 11 ! file unit number ndim_max = 1 ndimV_max = 1 nstepsread = 0 if (rootname(1:1).ne.' ') then datfile = trim(rootname) !print*,'rootname = ',rootname else print*,' **** no data read **** ' return endif if (iverbose.ge.1) print "(1x,a)",'reading ndspmhd format' write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! open(unit=iunit,iostat=ierr,file=datfile,status='old',form='unformatted') if (ierr /= 0) then print*,' *** Error opening '//trim(datfile)//' ***' return endif ! !--read first header line ! singleprecision = .false. read(iunit,iostat=ierr,end=80) timeind,npartin,ntotin,gammaind, & hfactind,ndim_max,ndimV_max,ncol_max,iformat ! print*,'time = ',timeind,' hfact = ',hfactind,' ndim=',ndim_max,'ncol=',ncol_max ! print*,'npart = ',npartin,ntotin,geomfile if (ierr /= 0 .or. ndim_max.le.0 .or. ndim_max.gt.3 & .or. ndimV_max.le.0 .or. ndimV_max.gt.3 & .or. ncol_max.le.0 .or. ncol_max.gt.100 & .or. npartin.le.0 .or. npartin.gt.1e7 .or. ntotin.le.0 .or. ntotin.gt.1e7 & .or. iformat.lt.0 .or. iformat.gt.10) then ! !--try single precision ! rewind(iunit) read(iunit,iostat=ierr,end=80) timein,npartin,ntotin,gammain, & hfactin,ndim_max,ndimV_max,ncol_max,iformat singleprecision = .true. if (ierr /= 0 .or. ndim_max.le.0 .or. ndim_max.gt.3 & .or. ndimV_max.le.0 .or. ndimV_max.gt.3 & .or. ncol_max.le.0 .or. ncol_max.gt.100 & .or. npartin.le.0 .or. npartin.gt.1e7 .or. ntotin.le.0 .or. ntotin.gt.1e7 & .or. iformat.lt.0 .or. iformat.gt.10) then print "(a)",' *** Error reading first header ***' print*,' time = ',timein,' hfact = ',hfactin,' ndim=',ndim_max,'ncol=',ncol_max close(iunit) return endif endif ! !--allocate memory for data arrays ! if (buffer_data) then nstep_max = max(nfiles,maxstep,indexstart) else nstep_max = max(1,maxstep,indexstart) endif npart_max = max(int(1.5*ntotin),maxpart) if (.not.allocated(dat) .or. ntotin.gt.maxpart & .or. nstep_max.gt.maxstep .or. ncol_max.gt.maxcol) then call alloc(npart_max,nstep_max,ncol_max+ncalc,mixedtypes=.true.) endif ! !--rewind file ! rewind(iunit) i = indexstart nstepsread = 0 reallocate = .false. npart_max = maxpart nstep_max = maxstep geomfile = ' ' ! !--read header line for this timestep ! if (singleprecision) then if (debugmode) print "(a)",'DEBUG: single precision dump' read(iunit,iostat=ierr) timein,nparti,ntoti,gammain, & hfactin,ndim,ndimV,ncolstep,iformat,ibound(1:ndim), & xmin(1:ndim),xmax(1:ndim),ilen,geomfile(1:ilen) else if (debugmode) print "(a)",'DEBUG: double precision dump' read(iunit,iostat=ierr) timeind,nparti,ntoti,gammaind, & hfactind,ndim,ndimV,ncolstep,iformat,ibound(1:ndim), & xmind(1:ndim),xmaxd(1:ndim),ilen,geomfile(1:ilen) timein = real(timeind) gammain = real(gammaind) hfactin = real(hfactind) xmin = real(xmind) xmax = real(xmaxd) endif if (ierr /= 0) then print*,'*** error reading timestep header ***' close(iunit) return else ! count this as a successfully read timestep, even if data is partial nstepsread = nstepsread + 1 endif time(i) = timein gamma(i) = gammain hfact = hfactin npartoftype(1,i) = nparti npartoftype(3,i) = ntoti - nparti if (iverbose.ge.1) then print "(a14,':',es10.3,a6,':',i8,a8,':',i8)",' time',time(i),'npart',nparti,'ntotal',ntoti print "(a14,':',i8,a8,':',f8.4,a8,':',f8.4)",' ncolumns',ncolstep,'gamma',gamma(i),'hfact',hfact print "(a14,':',i8,a8,':',i8)",'ndim',ndim,'ndimV',ndimV else print "(1x,a,':',es10.3,a8,':',i8,a8,':',i8)",'time',time(i),'npart',nparti,'ntotal',ntoti endif select case(geomfile(1:6)) case('cylrpz') icoords = 2 case('sphrpt') icoords = 3 case default icoords = 1 end select if (icoords.ne.1) print "(a14,a)",' geometry: ',trim(geomfile)//' ('//trim(labelcoordsys(icoords))//')' if (iverbose.ge.1 .and. any(ibound(1:ndim).ne.0)) then print "(a14,':',a15,' =',3(f8.4))",'boundaries','xmin',xmin(1:ndim) print "(15x,a15,' =',3(f8.4))",'xmax',xmax(1:ndim) endif ! !--check for errors in timestep header ! if (ndim.gt.3 .or. ndimV.gt.3) then print*,'*** error in header: ndim or ndimV in file> 3' nstepsread = nstepsread - 1 ndim = ndim_max ndimV = ndimV_max close(iunit) return endif if (ndim.gt.ndim_max) ndim_max = ndim if (ndimV.gt.ndimV_max) ndimV_max = ndimV if (ncolstep.ne.ncol_max) then print*,'*** Warning number of columns not equal for timesteps' ncolumns = ncolstep if (iverbose.ge.1) print*,'ncolumns = ',ncolumns,ncol_max if (ncolumns.gt.ncol_max) ncol_max = ncolumns endif if (ncolstep.gt.maxcol) then reallocate = .true. ncolumns = ncolstep ncol_max = ncolumns else ncolumns = ncolstep endif if (ntoti.gt.maxpart) then !print*, 'ntot greater than array limits!!' reallocate = .true. npart_max = int(1.5*ntoti) endif if (i.gt.maxstep) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate) then call alloc(npart_max,nstep_max,ncol_max+ncalc,mixedtypes=.true.) endif if (ntoti.gt.0) then if (.not.singleprecision) allocate(dattempd(ntoti)) do icol=1,ncolstep if (singleprecision) then read (iunit,iostat=ierr,end=67) dat(1:ntoti,icol,i) else read (iunit,iostat=ierr,end=67) dattempd(1:ntoti) dat(1:ntoti,icol,i) = real(dattempd(1:ntoti)) endif if (ierr /= 0) print "(a,i2,a)",'*** error reading column ',icol,' ***' enddo if (allocated(dattempd)) deallocate(dattempd) allocate(itype(ntoti)) read(iunit,iostat=ierr) itype(1:ntoti) if (ierr.ne.0) then if (debugmode) print "(a)",'DEBUG: itype not found in dump file' iamtype(1:nparti,i) = 1 iamtype(nparti+1:ntoti,i) = 3 else ! !--assign SPLASH types from ndspmhd types ! npartoftype(:,i) = 0 do j=1,ntoti if (j > nparti) then if (itype(j)==12) then iamtype(j,i) = 4 npartoftype(4,i) = npartoftype(4,i) + 1 else iamtype(j,i) = 3 npartoftype(3,i) = npartoftype(3,i) + 1 endif elseif (itype(j).eq.2) then iamtype(j,i) = 2 npartoftype(2,i) = npartoftype(2,i) + 1 else iamtype(j,i) = 1 npartoftype(1,i) = npartoftype(1,i) + 1 endif enddo endif if (allocated(itype)) deallocate(itype) else npartoftype(:,i) = 0 npartoftype(1,i) = 1 dat(:,:,i) = 0. endif goto 68 67 continue print "(a)",' > end of file reached <' 68 continue ! !--close data file and return ! close(unit=11) ilast = i ! !--ONE FLUID DUST: FAKE IT AS IF IT IS TWO FLUIDS ! (copy the particles, then copy gas properties onto first lot, then dust properties onto second lot) ! ncolumns = ncol_max ndim = ndim_max ndimV = ndimV_max call set_labels !if (iformat.eq.5 .and. .not.lenvironment('NSPLASH_BARYCENTRIC')) then ! call fake_twofluids ! iformat = 1 !endif if (any(npartoftype(2:,ilast) > 0)) call print_types(npartoftype(:,ilast),labeltype) if (debugmode) print*,'DEBUG> Read steps ',indexstart,'->',indexstart + nstepsread - 1, & ' last step ntot = ',sum(npartoftype(:,indexstart+nstepsread-1)) return 80 continue print*,' *** data file empty : no timesteps ***' return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:ix,ivx,ih,irho,iutherm,ipmass,ipr,iBfirst, & idivB,iJfirst,iamvec,labelvec,label,labeltype, & irhorestframe,idustfrac,ideltav use params use settings_data, only:ndim,ndimV,iformat,ntypes, & UseTypeInRenderings,ncolumns use geometry, only:labelcoord implicit none integer :: i,icol if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = ndim + 1 ih = ndim + ndimV + 1 ! smoothing length irho = ndim + ndimV + 2 ! location of rho in data array iutherm = ndim + ndimV + 3 ! thermal energy ipmass = ndim + ndimV + 4 ! particle mass label(ix(1:ndim)) = labelcoord(1:ndim,1) ! !--label vector quantities (e.g. velocity) appropriately ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' label(irho) = '\gr' label(iutherm) = 'u' label(ih) = 'h ' label(ipmass) = 'particle mass' label(ndim + ndimV+5) = '\ga' label(ndim + ndimV+6) = '\ga\du' icol = ndim+ndimV + 7 if (iformat.eq.2 .or. iformat.eq.4) then ! !--mag field (vector) ! label(icol) = '\ga\dB' iBfirst = icol+1 ! location of Bx iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' icol = icol + ndimV ! !--more scalars ! icol = icol + 1 label(icol) = 'psi' icol = icol + 1 ipr = icol ! pressure label(ipr) = 'P' icol = icol + 1 label(icol) = 'div v' icol = icol + 1 idivB = icol label(idivB) = 'div B' ! !--current density (vector) ! iJfirst = icol + 1 iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = 'J' icol = icol + ndimV icol = icol + 1 label(icol) = 'grad h' iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = 'force' icol = icol + ndimV iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = 'A' icol = icol + ndimV else ipr = icol ! pressure label(ipr) = 'P' icol = icol + 1 label(icol) = 'div v' icol = icol + 1 label(icol) = 'grad h' iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = 'force' icol = icol + ndimV iBfirst = 0 if (ncolumns > 20) then iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = 'del^{2} v' icol = icol + ndimV iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = 'grad (div {\bf v})' icol = icol + ndimV endif endif if (iformat.eq.5) then icol = icol + 1 label(icol) = 'Dust fraction' idustfrac = icol iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = '\Deltav' ideltav = icol + 1 icol = icol + ndimV elseif (iformat.gt.2) then irhorestframe = irho icol = icol + 1 irho = icol label(icol) = 'rho*' !irho = icol icol = icol + 1 label(icol) = 'sqrt g' iamvec(icol+1:icol+ndimV) = icol + 1 labelvec(icol+1:icol+ndimV) = 'pmom' icol = icol + ndimV endif ! !--set labels for each type of particles ! ntypes = 4 labeltype(1) = 'gas' labeltype(2) = 'dust' labeltype(3) = 'ghost' labeltype(4) = 'ghost (dust)' UseTypeInRenderings(:) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/labels.f90000644 000766 000000 00000025662 13261626263 015350 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------- ! ! routines to do with storing and handling of plot labels ! !----------------------------------------------------------- module labels use params, only:maxplot, maxparttypes implicit none integer, parameter :: lenlabel = 80 integer, parameter :: lenunitslabel = 40 ! length of units label character(len=lenlabel), dimension(maxplot+2) :: label,labelvec character(len=20), dimension(maxparttypes) :: labeltype character(len=6), parameter :: labeldefault = 'column' character(len=lenunitslabel), dimension(0:maxplot), public :: unitslabel character(len=lenunitslabel), public :: labelzintegration integer, dimension(3) :: ix integer, dimension(maxplot) :: iamvec integer :: ivx,irho,iutherm,ipr,ih,irad,iBfirst,iBpol,iBtor,iax integer :: ipmass,ike,ispsound integer :: idivb,iJfirst,irhostar integer :: iacplane,ipowerspec integer :: icv,iradenergy integer :: isurfdens,itoomre integer :: ipdf,icolpixmap integer :: irhorestframe,idustfrac,ideltav integer :: idustfracsum,ideltavsum integer :: igrainsize,igraindens public contains !-------------------------------------------------------------- ! ! utility to reset default settings for column identification ! !-------------------------------------------------------------- subroutine reset_columnids implicit none ! !--array positions of specific quantities ! Identification is used in exact solution ! plotting and calculation of additional quantities ! ix(:) = 0 ivx = 0 ! vx irho = 0 ! density ipr = 0 ! pressure iutherm = 0 ! thermal energy ih = 0 ! smoothing length irad = 0 ! radius ipmass = 0 ! particle mass ipr = 0 ! pressure irad = 0 ! radius ipowerspec = 0 ! power spectrum iBfirst = 0 ! Bx iax = 0 ! ax (acceleration) iBpol = 0 ! B_polx iBtor = 0 ! B_torx igrainsize = 0 ! grainsize igraindens = 0 ! graindens iacplane = 0 ike = 0 idivB = 0 iJfirst = 0 icv = 0 iradenergy = 0 icolpixmap = 0 irhorestframe = 0 idustfrac = 0 idustfracsum = 0 ideltav = 0 ideltavsum = 0 return end subroutine reset_columnids !-------------------------------------------------------------- ! ! query function for whether column is a spatial coordinate ! !-------------------------------------------------------------- logical function is_coord(icol,ndim) implicit none integer, intent(in) :: icol,ndim integer :: i is_coord = .false. do i=1,ndim if (ix(i).eq.icol) is_coord = .true. enddo end function is_coord !-------------------------------------------------------------- ! ! query function for whether column is a spatial coordinate ! returns the location of the third dimension in the ! list of columns ! !-------------------------------------------------------------- integer function get_z_dir(ndim,iplotx,iploty) result(iplotz) implicit none integer, intent(in) :: ndim,iplotx,iploty integer :: i iplotz = 0 do i=1,ndim if ((iplotx.ne.iploty.and. & (ix(i).ne.iplotx).and.(ix(i).ne.iploty))) iplotz = ix(i) enddo end function get_z_dir !-------------------------------------------------------------- ! ! query function for which coordinate is z ! returns an integer between 1 and ndim ! !-------------------------------------------------------------- integer function get_z_coord(ndim,iplotx,iploty) result(iplotz) implicit none integer, intent(in) :: ndim,iplotx,iploty integer :: i i = get_z_dir(ndim,iplotx,iploty) iplotz = i - ix(1) + 1 if (iplotz < 0) iplotz = 1 if (iplotz > ndim) iplotz = ndim end function get_z_coord !----------------------------------------------------------------- ! ! utility to strip spaces, escape sequences and ! units labels from strings (this can be called for both ! function strings and variable labels) ! !----------------------------------------------------------------- elemental function shortstring(string,unitslab) use asciiutils, only:string_delete implicit none character(len=lenlabel), intent(in) :: string character(len=*), intent(in), optional :: unitslab character(len=lenlabel) :: shortstring integer :: ipos shortstring = string !--strip off the units label if (present(unitslab)) then if (len_trim(unitslab).gt.0) then !--remove units label (only do this once) ipos = index(trim(shortstring),trim(unitslab)) if (ipos.ne.0) then shortstring = shortstring(1:ipos-1)//& shortstring(ipos+len_trim(unitslab)+1:len_trim(shortstring)) endif endif endif !--remove spaces call string_delete(shortstring,' ') !--remove escape sequences (\d etc.) call string_delete(shortstring,'\d') call string_delete(shortstring,'\u') call string_delete(shortstring,'\g') call string_delete(shortstring,'\') call string_delete(shortstring,'_') end function shortstring !------------------------------------------------------------------ ! ! Same as shortstring, but also strips any arithmetic operators ! should be applied to variable names, but not function strings ! !----------------------------------------------------------------- elemental function shortlabel(string,unitslab) use asciiutils, only:string_delete implicit none character(len=lenlabel), intent(in) :: string character(len=*), intent(in), optional :: unitslab character(len=lenlabel) :: shortlabel if (present(unitslab)) then shortlabel = shortstring(string,unitslab) else shortlabel = shortstring(string) endif !--remove arithmetic operators from labels call string_delete(shortlabel,'**') call string_delete(shortlabel,'/') call string_delete(shortlabel,'*') call string_delete(shortlabel,'+') call string_delete(shortlabel,'-') call string_delete(shortlabel,'^') call string_delete(shortlabel,'sqrt(') call string_delete(shortlabel,'(') call string_delete(shortlabel,')') call string_delete(shortlabel,'{') call string_delete(shortlabel,'}') call string_delete(shortlabel,'[') call string_delete(shortlabel,']') call string_delete(shortlabel,'<') call string_delete(shortlabel,'>') call string_delete(shortlabel,'\(2268)') end function shortlabel !--------------------------------------------------------------- ! interface for adjusting the label for column-integrated plots !--------------------------------------------------------------- function integrate_label(labelin,iplot,izcol,normalise,iRescale,labelzint,& projlabelformat,iapplyprojformat) use asciiutils, only:string_replace implicit none character(len=*), intent(in) :: labelin,labelzint,projlabelformat integer, intent(in) :: iplot,izcol,iapplyprojformat logical, intent(in) :: normalise,iRescale character(len=len(label)+20) :: integrate_label if (len_trim(projlabelformat).ne.0 .and. (iapplyprojformat.eq.0 .or. iapplyprojformat.eq.iplot)) then integrate_label = projlabelformat call string_replace(integrate_label,'%l',trim(labelin)) if (iRescale) then call string_replace(integrate_label,'%z',trim(label(izcol)(1:index(label(izcol),unitslabel(izcol))-1))) call string_replace(integrate_label,'%uz',trim(unitslabel(izcol))) else call string_replace(integrate_label,'%z',trim(label(izcol))) endif else if (normalise) then integrate_label = '< '//trim(labelin)//' >' else if (iRescale) then integrate_label = '\(2268) '//trim(labelin)//' d'// & trim(label(izcol)(1:index(label(izcol),unitslabel(izcol))-1))//trim(labelzint) else integrate_label = '\(2268) '//trim(labelin)//' d'//trim(label(izcol)) endif if (iplot.eq.irho .and. (index(labelin,'density').ne.0 .or. index(labelin,'rho').ne.0)) then integrate_label = 'column density' integrate_label = trim(integrate_label)//get_unitlabel_coldens(iRescale,labelzint,unitslabel(irho)) endif endif endif end function integrate_label !----------------------------------------------------------------- ! ! utility to convert the units label to g/cm^2 where appropriate ! would be nice to have a more robust way of knowing what the units mean ! !----------------------------------------------------------------- function get_unitlabel_coldens(iRescale,labelzint,unitlabel) logical, intent(in) :: iRescale character(len=*), intent(in) :: labelzint,unitlabel character(len=lenunitslabel) :: get_unitlabel_coldens if (iRescale .and. index(labelzint,'cm').gt.0 & .and. trim(adjustl(unitlabel)).eq.'[g/cm\u3\d]') then get_unitlabel_coldens = ' [ g/cm\u2\d]' else get_unitlabel_coldens = ' ' endif end function get_unitlabel_coldens !----------------------------------------------------------------- ! ! utility to "guess" which particle type contains sink particles ! from the label ! !----------------------------------------------------------------- integer function get_sink_type(ntypes) implicit none integer, intent(in) :: ntypes integer :: i get_sink_type = 0 do i=1,ntypes if (get_sink_type.eq.0 .and. index(labeltype(i),'sink').ne.0) get_sink_type = i enddo end function get_sink_type !----------------------------------------------------------------- ! ! utility to neatly print number of particles by type ! !----------------------------------------------------------------- subroutine print_types(noftype,ltype) integer, dimension(:), intent(in) :: noftype character(len=*), dimension(:), intent(in) :: ltype integer :: itype,n,i character(len=1) :: sp i = 0 sp = ' ' do itype=1,size(noftype) n = noftype(itype) if (n > 0) then i = i + 1 if (i > 1) sp = ',' if (n < 10000) then write(*,"(a,i4)",advance='no') trim(sp)//' n('//trim(ltype(itype))//') = ',n elseif (n < 1000000) then write(*,"(a,i6)",advance='no') trim(sp)//' n('//trim(ltype(itype))//') = ',n elseif (n < 100000000) then write(*,"(a,i8)",advance='no') trim(sp)//' n('//trim(ltype(itype))//') = ',n else write(*,"(a,i10)",advance='no') trim(sp)//' n('//trim(ltype(itype))//') = ',n endif endif enddo write(*,*) end subroutine print_types end module labels splash/src/read_data_vanaverbeke.f90000644 000766 000000 00000020425 13261626263 020353 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT ! FROM SIGFRIED VANAVERBEKE'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! VSPLASH_SINGLEPREC if 'YES' or 'TRUE' then assumes data is single precision ! VSPLASH_NCOL to change the number of columns read from the file, ! e.g. setenv VSPLASH_NCOL=13 ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,time,npartoftype,gamma,maxpart use params use settings_data, only:ndim,ndimV,ncolumns use mem_allocation, only:alloc use system_utils, only:lenvironment,ienvironment implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,k,ierr integer :: nprint,ntotal,npart_max,nstep_max integer :: ncol,nread,nerr,ncoltemp,nsink,nsinkcol,nacc logical :: iexist,doubleprec integer, parameter :: iunit = 15 character(len=len(rootname)) :: dumpfile real :: timei,gammai real(doub_prec), dimension(maxplot) :: datdb real(doub_prec) :: timedb,gammadb nstepsread = 0 nstep_max = 0 npart_max = maxpart dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 !--number of columns to read from file ncol = 10 nsinkcol = 7 doubleprec = .true. !--can override these settings with environment variables if (lenvironment('VSPLASH_SINGLEPREC')) doubleprec = .false. ncoltemp = ienvironment('VSPLASH_NCOL') if (ncoltemp.gt.0) ncol = ncoltemp ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 print "(a)",' reading Vanaverbeke format...' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=iunit,file=dumpfile,status='old',form='unformatted',iostat=ierr) if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return else timei = 0. read(iunit,iostat=ierr) nprint,nacc,nsink print "(3(a,i10))",'npart:',nprint,' naccreted:',nacc,' nsinks:',nsink if (doubleprec) then read(iunit,iostat=ierr) timedb,gammadb timei = real(timedb) gammai = real(gammadb) else read(iunit,iostat=ierr) timei,gammai endif print "(2(a,1pe12.3))",'time:',timei,' gamma:',gammai !--barf if stupid values read if (nprint.lt.0 .or. nacc.lt.0 .or. nsink.lt.0 .or. nsink.gt.1e7) then print "(a)",' *** ERROR IN TIMESTEP HEADER: WRONG ENDIAN? (or old header format)?' close(iunit) return elseif (ierr /= 0) then print "(a)",'*** ERROR READING TIMESTEP HEADER: WRONG ENDIAN? ***' close(iunit) return endif if (timei.lt.0. .or. gammai.lt.1.0 .or. gammai.gt.2.0) then print*,'*** ERROR IN HEADER: strange time and/or gamma read: wrong precision?' endif ncolumns = ncol ntotal = nprint + nsink if (.not.allocated(dat) .or. ntotal.gt.npart_max) then npart_max = max(npart_max,ntotal) call alloc(npart_max,nstep_max,ncolumns) endif ! !--now read the timestep data in the dumpfile ! dat(:,:,j) = 0. time(j) = timei gamma(j) = gammai if (doubleprec) then nerr = 0 nread = 0 do i=1,nprint nread = nread + 1 read(iunit,end=44,iostat=ierr) (datdb(k),k=1,ncol) if (ierr /= 0) then nerr = nerr + 1 else dat(i,1:ncol,j) = real(datdb(1:ncol)) endif enddo do i=nprint+1,nprint+nsink nread = nread + 1 read(iunit,end=44,iostat=ierr) (datdb(k),k=1,nsinkcol) if (ierr /= 0) then nerr = nerr + 1 else dat(i,1:nsinkcol,j) = real(datdb(1:nsinkcol)) endif enddo else nerr = 0 nread = 0 do i=1,nprint nread = nread + 1 read(iunit,end=44,iostat=ierr) dat(i,1:ncol,j) if (ierr /= 0) nerr = nerr + 1 enddo do i=nprint+1,nprint+nsink nread = nread + 1 read(iunit,end=44,iostat=ierr) dat(i,1:nsinkcol,j) if (ierr /= 0) nerr = nerr + 1 enddo endif goto 45 44 continue print "(a,i10)",' WARNING: END-OF-FILE AT LINE ',nread 45 continue if (nerr.gt.0) print *,'*** WARNING: ERRORS DURING READ ON ',nerr,' LINES' nstepsread = nstepsread + 1 npartoftype(1,j) = nprint - nacc npartoftype(2,j) = nacc npartoftype(3,j) = nsink j = j + 1 endif close(iunit) if (allocated(npartoftype)) then print*,'>> end of dump file: nsteps =',j-1,'ntot = ',sum(npartoftype(:,j-1)) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,labelvec,labeltype,iamvec,& ix,ivx,ih,irho,iutherm,ipmass,ispsound use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord !use settings_units, only:units,unitslabel implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ipmass = 7 ih = ipmass + 1 iutherm = ih + 1 irho = iutherm + 1 ispsound = 0 label(ix(1:ndim)) = labelcoord(1:ndim,1) label(ih) = 'h' if (irho.gt.0) label(irho) = '\gr' if (ipmass.gt.0) label(ipmass) = 'particle mass' if (iutherm.gt.0) label(iutherm) = 'u' if (ispsound.gt.0) label(ispsound) = 'c_s' if (ivx.ne.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//trim(labelcoord(i,1)) enddo endif ! !--set labels for each particle type ! ntypes = 3 labeltype(1) = 'gas' labeltype(2) = 'accreted/dead' labeltype(3) = 'sink' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/pagecolours.f90000644 000766 000000 00000012450 13261626263 016420 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2010 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !---------------------------------------------------------- ! module handling page colour schemes, for generic ! plotting library !---------------------------------------------------------- module pagecolours implicit none integer, parameter :: maxpagecolours = 2 contains !---------------------------------------------------------- ! query function for the colour scheme !---------------------------------------------------------- function pagecolourscheme(ischeme,short) integer, intent(in) :: ischeme character(len=21) :: pagecolourscheme logical, intent(in), optional :: short logical :: use_short use_short = .false. if (present(short)) use_short = short select case(ischeme) case(2) pagecolourscheme = 'white-on-black' case(1) pagecolourscheme = 'black-on-white' case default if (use_short) then pagecolourscheme = 'default' else pagecolourscheme = 'plot library default' endif end select end function pagecolourscheme !---------------------------------------------------------- ! set the colour index 1 and 0 of the plotting library ! corresponding to the foreground and background colours ! (must be called after the plot library is initialised) !---------------------------------------------------------- subroutine set_pagecolours(ischeme) use plotlib, only:plot_scr integer, intent(in) :: ischeme select case(ischeme) case(2) !--white-on-black call plot_scr(0,0.,0.,0.) call plot_scr(1,1.,1.,1.) case(1) !--black-on-white call plot_scr(0,1.,1.,1.) call plot_scr(1,0.,0.,0.) end select end subroutine set_pagecolours !---------------------------------------------------------- ! query function for the name of the foreground colour !---------------------------------------------------------- function colour_fore(ischeme) integer, intent(in) :: ischeme character(len=5) :: colour_fore select case(ischeme) case(2) colour_fore = 'white' case(1) colour_fore = 'black' case default colour_fore = ' ' end select end function colour_fore !---------------------------------------------------------- ! query function for the name of the background colour !---------------------------------------------------------- function colour_back(ischeme) integer, intent(in) :: ischeme character(len=5) :: colour_back select case(ischeme) case(2) colour_back = 'black' case(1) colour_back = 'white' case default colour_back = ' ' end select end function colour_back !---------------------------------------------------------- ! write line colours to file !---------------------------------------------------------- subroutine write_coloursfile(filename,nc,linecolours) character(len=*), intent(in) :: filename integer, intent(in) :: nc real, intent(in) :: linecolours(3,nc) integer :: i,ierr integer, parameter :: lu = 37 open(unit=lu,file=trim(filename),status='replace',iostat=ierr) do i=1,nc write(lu,"(3(f8.3,1x))") linecolours(:,i) enddo close(lu) end subroutine write_coloursfile !---------------------------------------------------------- ! read line colours from file !---------------------------------------------------------- subroutine read_coloursfile(filename,maxc,linecolours,nc,ierr) character(len=*), intent(in) :: filename integer, intent(in) :: maxc real, intent(out) :: linecolours(3,maxc) integer, intent(out) :: nc,ierr integer :: i integer, parameter :: lu = 38 ierr = 0 nc = 0 open(unit=lu,file=trim(filename),status='old',iostat=ierr) if (ierr /= 0) return do i=1,maxc read(lu,*,iostat=ierr) linecolours(:,i) if (ierr==0) nc = nc + 1 enddo close(lu) end subroutine read_coloursfile !---------------------------------------------------------- ! read line colours from file !---------------------------------------------------------- subroutine set_linecolours(linepalette,filename,maxc,linecolours) use plotlib, only:plot_scr,plot_set_palette,plotlib_maxpalette character(len=*), intent(in) :: filename integer, intent(in) :: linepalette,maxc real, intent(out) :: linecolours(3,maxc) integer :: i,nc,ierr if (linepalette > 0 .and. linepalette <= plotlib_maxpalette) then call plot_set_palette(linepalette) elseif (linepalette < 0) then call read_coloursfile(filename,maxc,linecolours,nc,ierr) do i=1,nc call plot_scr(i+1,linecolours(1,i),linecolours(2,i),linecolours(3,i)) enddo endif end subroutine set_linecolours end module pagecolours splash/src/defaults.f90000644 000766 000000 00000023112 13261626263 015701 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------- ! ! Module containing subroutines relating to ! setting/saving default options ! !------------------------------------------------- module defaults implicit none contains !! !! initialise some variables !! this should only be called at code start !! subroutine defaults_set_initial use filenames, only:rootname use labels, only:label,labeltype,iamvec,labelvec,labeldefault,reset_columnids use limits, only:lim,range use particle_data, only:maxpart,maxstep,maxcol use settings_data, only:UseTypeInRenderings use settings_page, only:device implicit none integer :: i ! !--limits (could set them to anything but min & max must be different ! to enable them to be reset interactively if not set elsewhere) ! lim(:,1) = 0. lim(:,2) = 1. range(:,:) = 0. call reset_columnids() ! !--filenames ! rootname = ' ' ! !--data array sizes ! maxpart = 0 maxcol = 0 maxstep = 0 ! !--labels ! ! column labels do i=1,size(label) write(label(i),"(a,1x,i3)") trim(labeldefault),i enddo ! particle types labeltype(1) = 'gas' do i=2,size(labeltype) if (i > 9) then write(labeltype(i),"(a,1x,i2)") 'type',i else write(labeltype(i),"(a,1x,i1)") 'type',i endif enddo UseTypeInRenderings(:) = .false. UseTypeInRenderings(1) = .true. ! vector labels iamvec(:) = 0 labelvec = ' ' ! device from command line device = ' ' return end subroutine defaults_set_initial !! !! set initial default options !! these are used if no defaults file is found !! subroutine defaults_set(use_evdefaults) use exact, only:defaults_set_exact use multiplot use settings_limits, only:defaults_set_limits use options_data, only:defaults_set_data use settings_part, only:defaults_set_part,defaults_set_part_ev use settings_page, only:defaults_set_page,defaults_set_page_ev use settings_render, only:defaults_set_render use settings_vecplot, only:defaults_set_vecplot use settings_xsecrot, only:defaults_set_xsecrotate use settings_powerspec, only:defaults_set_powerspec use settings_units, only:defaults_set_units use titles, only:pagetitles,steplegend implicit none logical, intent(in) :: use_evdefaults integer :: i ! !--set defaults for submenu options ! call defaults_set_data call defaults_set_limits call defaults_set_page call defaults_set_part call defaults_set_render call defaults_set_xsecrotate call defaults_set_vecplot call defaults_set_exact call defaults_set_powerspec call defaults_set_units ! !--if using evsplash, override some default options ! if (use_evdefaults) then print "(a)",'setting evsplash defaults' call defaults_set_page_ev call defaults_set_part_ev endif itrans(:) = 0 ! !--multiplot ! nyplotmulti = 4 ! number of plots in multiplot multiploty(:) = 0 do i=1,4 multiploty(i) = 1+i ! first plot : y axis enddo multiplotx(:) = 1 ! first plot : x axis irendermulti(:) = 0 ! rendering ivecplotmulti(:) = 0 ! vector plot x_secmulti(:) = .false. ! take cross section? xsecposmulti(:) = 0.0 ! position of cross section icontourmulti(:) = 0 ! contour plot iplotpartoftypemulti(:,:) = .false. do i=1,size(iplotpartoftypemulti(:,1)) iplotpartoftypemulti(i,i) = .true. enddo iusealltypesmulti(:) = .true. ! !--titles ! pagetitles = ' ' steplegend = ' ' return end subroutine defaults_set ! ! set defaults for 360 video mode ! subroutine defaults_set_360() use settings_page, only:defaults_set_page_360 use settings_render, only:defaults_set_render_360 use settings_data, only:icoordsnew icoordsnew = 3 call defaults_set_render_360 call defaults_set_page_360() end subroutine defaults_set_360 ! ! writes default options to file (should match defaults_read) ! subroutine defaults_write(filename) use exact, only:exactopts,exactparams use filenames, only:rootname,nfiles use settings_data, only:dataopts use settings_part, only:plotopts use settings_page, only:pageopts use settings_render, only:renderopts use settings_vecplot, only:vectoropts use settings_xsecrot, only:xsecrotopts,animopts use settings_powerspec, only:powerspecopts use multiplot, only:multi use shapes, only:shapeopts use calcquantities, only:calcopts implicit none character(len=*), intent(in) :: filename integer :: i,ierr integer, parameter :: iunit = 1 open(unit=iunit,file=trim(adjustl(filename)),status='replace',form='formatted', & delim='apostrophe',iostat=ierr) ! without delim namelists may not be readable if (ierr /= 0) then print*,'ERROR: cannot write file '//trim(filename) close(unit=iunit) return endif write(iunit,NML=dataopts) write(iunit,NML=plotopts) write(iunit,NML=pageopts) write(iunit,NML=renderopts) write(iunit,NML=vectoropts) write(iunit,NML=xsecrotopts) write(iunit,NML=powerspecopts) write(iunit,NML=exactopts) write(iunit,NML=exactparams) write(iunit,NML=multi) write(iunit,NML=shapeopts) write(iunit,NML=calcopts) write(iunit,NML=animopts) do i=1,nfiles write(iunit,"(a)") trim(rootname(i)) enddo close(unit=iunit) print "(a)",'default options saved to file '//trim(filename) return end subroutine defaults_write !----------------------------------------------- ! reads default options from file ! uses namelist input to group the options ! these are specified in the modules !----------------------------------------------- subroutine defaults_read(filename) use filenames, only:rootname,maxfile use multiplot, only:multi use settings_data, only:dataopts,idustfrac_plot use settings_part, only:plotopts use settings_page, only:pageopts use settings_render, only:renderopts use settings_vecplot, only:vectoropts use settings_xsecrot, only:xsecrotopts,animopts use settings_powerspec, only:powerspecopts use exact, only:exactopts,exactparams use shapes, only:shapeopts use calcquantities, only:calcopts implicit none character(len=*), intent(in) :: filename logical :: iexist integer :: ierr,i integer, parameter :: iunit = 1 inquire (exist=iexist, file=filename) if (iexist) then open(unit=iunit,file=filename,status='old',form='formatted',delim='apostrophe',err=88) ierr = 0 read(iunit,NML=dataopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading data options from '//trim(filename) ierr = 0 read(iunit,NML=plotopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading plot options from '//trim(filename) ierr = 0 read(iunit,NML=pageopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading page options from '//trim(filename) ierr = 0 read(iunit,NML=renderopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading render options from '//trim(filename) ierr = 0 read(iunit,NML=vectoropts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading vector plot options from '//trim(filename) ierr = 0 read(iunit,NML=xsecrotopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading xsec/rotation options from '//trim(filename) ierr = 0 read(iunit,NML=powerspecopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading power spectrum options from '//trim(filename) ierr = 0 read(iunit,NML=exactopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading exact solution options from '//trim(filename) ierr = 0 read(iunit,NML=exactparams,iostat=ierr) if (ierr /= 0) print "(a)",'error reading exact solution parameters from '//trim(filename) ierr = 0 read(iunit,NML=multi,iostat=ierr) if (ierr /= 0) print "(a)",'error reading multiplot options from '//trim(filename) ierr = 0 read(iunit,NML=shapeopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading shape options from '//trim(filename) ierr = 0 read(iunit,NML=calcopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading calculated quantity settings from '//trim(filename) ierr = 0 read(iunit,NML=animopts,iostat=ierr) if (ierr /= 0) print "(a)",'error reading animation sequence settings from '//trim(filename) if (len_trim(rootname(1)).eq.0) then do i=1,maxfile read(iunit,*,end=66,iostat=ierr) rootname(i) enddo endif 66 continue close(unit=iunit) print*,'read '//trim(filename) return else print*,trim(filename)//' not found: using default settings' return endif 88 continue print "(a)",' *** error opening defaults file '//trim(filename)//': using default settings' return end subroutine defaults_read end module defaults splash/src/interpolate3D_opacity.f90000644 000766 000000 00000034470 13261626263 020350 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module interpolate3D_opacity use projections3D, only:wfromtable,coltable use kernels, only:radkernel2 use sort, only:indexx use interpolation, only:weight_sink implicit none contains !-------------------------------------------------------------------------- ! $Id: interpolate3D_opacity.f90,v 1.16 2007/11/20 17:05:35 dprice Exp $ ! ! subroutine to do a ray trace through the particle data ! ! we use the radiation transport equation along a ray, that is ! the change in intensity from one side of a particle to the other is ! given by: ! ! I_nu = I_nu(0) exp(-tau_i) + S_nu (1 - exp(-tau_i)) ! ! where tau_i is the integrated optical depth through the particle, ! and S_nu is the colour calculated from a colour table for the rendered data. ! We calculate an intensity in red, green and blue for colour plots. ! ! tau_i = kappa \int rho dz ! ! this is calculated using the SPH kernel for rho, so for each pixel ! the optical depth is incremented as the sum ! ! tau_i = kappa \sum_j m_j \int W dz ! ! where \int W dz is the SPH kernel integrated along one spatial dimension. ! This is interpolated from a pre-calculated table (see module projections3D for this). ! ! kappa is the monochromatic mass extinction coefficient ! (particle cross section per unit mass) and is a constant for all particles ! which must be given as input (although see below for calculations of a ! meaningful values for kappa in terms of "surface depth in units of smoothing lengths") ! ! Input: particle coordinates : x,y,z (npart) - note that z is only required for perspective ! particle masses : pmass (npmass) ! smoothing lengths : hh (npart) ! weight : m/(h^3 rho) (not used, but skips particles with w <= 0) ! scalar data to smooth : dat (npart) ! ! Particle masses can be sent in as either a single scalar (npmass = 1) ! or as an array of length npart (npmass=npart) ! ! Settings: zobs, dz1 : settings for 3D projection ! rkappa : particle cross section per unit mass ! ! Output: smoothed data : datsmooth (npixx,npixy) ! brightness array : brightness (npixx,npixy) ! !-------------------------------------------------------------------------- subroutine interp3D_proj_opacity(x,y,z,pmass,npmass,hh,weight,dat,zorig,itype,npart, & xmin,ymin,datsmooth,brightness,npixx,npixy,pixwidth,zobserver,dscreenfromobserver, & rkappa,zcut) implicit none real, parameter :: pi=3.1415926536 integer, intent(in) :: npart,npixx,npixy,npmass real, intent(in), dimension(npart) :: x,y,z,hh,weight,dat,zorig real, intent(in), dimension(npmass) :: pmass integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidth,zobserver,dscreenfromobserver, & zcut,rkappa real, dimension(npixx,npixy), intent(out) :: datsmooth, brightness integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax,nused,nsink integer :: iprintinterval, iprintnext,itmin integer, dimension(npart) :: iorder integer(kind=selected_int_kind(12)) :: ipart real :: hi,hi1,hi21,radkern,q2,wab,pmassav real :: term,dy,dy2,ypix,zfrac,hav,zcutoff real :: fopacity,tau,rkappatemp,termi,xi,yi real :: t_start,t_end,t_used,tsec logical :: iprintprogress,adjustzperspective,rendersink real, dimension(npixx) :: xpix,dx2i real :: xminpix,yminpix !#ifdef _OPENMP ! integer :: OMP_GET_NUM_THREADS !#else integer(kind=selected_int_kind(12)) :: iprogress !#endif datsmooth = 0. term = 0. brightness = 0. print "(1x,a)",'ray tracing from particles to pixels...' if (pixwidth.le.0.) then print "(a)",'interpolate3D_opacity: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate3D_opacity: warning: ignoring some or all particles with h < 0' endif !--check that npmass is sensible if (npmass.lt.1 .or. npmass.gt.npart) then print*,'interpolate3D_opacity: ERROR in input number of particle masses ' return endif !--these values for npmass are not sensible but the routine will still work if (npmass.ne.1 .and. npmass.ne.npart) then print*,'WARNING: interpolate3D_opacity: number of particle masses input =',npmass endif if (abs(dscreenfromobserver).gt.tiny(dscreenfromobserver)) then adjustzperspective = .true. zcutoff = zobserver else adjustzperspective = .false. zcutoff = huge(zobserver) endif ! !--kappa is the opacity in units of length^2/mass ! sent as an input parameter as it should be kept constant throughout the simulation ! ! However we compute a reasonable estimate below based on the current plot so that ! we can give the "actual" optical depth for the current frame in terms of number of ! smoothing lengths. This is purely for diagnostic purposes only. ! !--calculate average h hav = sum(hh(1:npart))/real(npart) !--average particle mass pmassav = sum(pmass(1:npmass))/real(npmass) rkappatemp = pi*hav*hav/(pmassav*coltable(0)) print*,'average h = ',hav,' average mass = ',pmassav print "(1x,a,f6.2,a)",'typical surface optical depth is ~',rkappatemp/rkappa,' smoothing lengths' ! !--print a progress report if it is going to take a long time ! (a "long time" is, however, somewhat system dependent) ! iprintprogress = (npart .ge. 100000) .or. (npixx*npixy .gt.100000) ! !--loop over particles ! iprintinterval = 25 if (npart.ge.1e6) iprintinterval = 10 iprintnext = iprintinterval ! !--get starting CPU time ! call cpu_time(t_start) ! !--first sort the particles in z so that we do the opacity in the correct order ! call indexx(npart,z,iorder) ! !--store x value for each pixel (for optimisation) ! xminpix = xmin - 0.5*pixwidth yminpix = ymin - 0.5*pixwidth do ipix=1,npixx xpix(ipix) = xminpix + ipix*pixwidth enddo nused = 0 nsink = 0 !!$OMP PARALLEL default(none) & !!$OMP SHARED(hh,z,x,y,zorig,pmass,dat,itype,datsmooth,npmass,npart) & !!$OMP SHARED(xmin,ymin,xminpix,yminpix,xpix,pixwidth) & !!$OMP SHARED(npixx,npixy,dscreenfromobserver,zobserver,adjustzperspective) & !!$OMP SHARED(zcut,zcutoff,iorder,rkappa,brightness) & !!$OMP PRIVATE(hi,zfrac,xi,yi,radkern) & !!$OMP PRIVATE(hi1,hi21,term,termi) & !!$OMP PRIVATE(ipixmin,ipixmax,jpixmin,jpixmax) & !!$OMP PRIVATE(dx2i,q2,ypix,dy,dy2,wab) & !!$OMP PRIVATE(ipart,i,ipix,jpix,tau,fopacity) & !!$OMP REDUCTION(+:nused) !!$OMP MASTER !#ifdef _OPENMP ! print "(1x,a,i3,a)",'Using ',OMP_GET_NUM_THREADS(),' cpus' !#endif !!$OMP END MASTER !!$OMP DO ORDERED SCHEDULE(dynamic) over_particles: do ipart=1,npart ! !--report on progress ! !#ifndef _OPENMP if (iprintprogress) then iprogress = 100*(ipart/npart) if (iprogress.ge.iprintnext) then write(*,"('(',i3,'% -',i12,' particles done)')") iprogress,ipart iprintnext = iprintnext + iprintinterval endif endif !#endif ! !--render in order from back to front ! i = iorder(ipart) ! !--skip particles with itype < 0 ! if (itype(i) < 0) cycle over_particles ! !--skip particles with weight < 0 ! but not if weight == weight_sink (=-1) ! rendersink = .false. if (abs(weight(i) - weight_sink) < tiny(0.)) then rendersink = .true. elseif (weight(i) <= 0.) then cycle over_particles endif ! !--allow slicing [take only particles with z(unrotated) < zcut] ! particle_within_zcut: if (zorig(i).lt.zcut .and. z(i).lt.zcutoff) then ! count particles within slice nused = nused + 1 ! !--adjust h according to 3D perspective ! need to be careful -- the kernel quantities ! change with z (e.g. radkern, r^2/h^2) ! but *not* the 1/h^2 in tau (because the change in 1/h^2 in tau ! would be cancelled by the corresponding change to h^2 in kappa) ! hi = hh(i) if (hi.le.0.) then cycle over_particles elseif (adjustzperspective) then zfrac = abs(dscreenfromobserver/(z(i)-zobserver)) hi = hi*zfrac endif !--these are the quantities used in the kernel r^2/h^2 radkern = 2.*hi hi1 = 1./hi hi21 = hi1*hi1 !--this is the term which multiplies tau if (npmass.eq.npart) then term = pmass(i)/(hh(i)*hh(i)) else term = pmass(1)/(hh(i)*hh(i)) endif ! !--determine colour contribution of current point ! (work out position in colour table) ! ! dati = dat(i) xi = x(i) yi = y(i) termi = dat(i) ! !--sink particles can have weight set to -1 ! indicating that we should include them in the rendering ! if (rendersink) then termi = pmass(i)/(4./3.*pi*hh(i)**3) ! define "density" of a sink nsink = nsink + 1 endif ! !--for each particle work out which pixels it contributes to ! ipixmin = int((xi - radkern - xmin)/pixwidth) jpixmin = int((yi - radkern - ymin)/pixwidth) ipixmax = int((xi + radkern - xmin)/pixwidth) + 1 jpixmax = int((yi + radkern - ymin)/pixwidth) + 1 if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (jpixmin.lt.1) jpixmin = 1 ! to pixels in the image if (ipixmax.gt.npixx) ipixmax = npixx ! (note that this optimises if (jpixmax.gt.npixy) jpixmax = npixy ! much better than using min/max) ! !--precalculate an array of dx2 for this particle (optimisation) ! do ipix=ipixmin,ipixmax dx2i(ipix) = ((xpix(ipix) - xi)**2)*hi21 enddo ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax ypix = yminpix + jpix*pixwidth dy = ypix - yi dy2 = dy*dy*hi21 do ipix = ipixmin,ipixmax q2 = dx2i(ipix) + dy2 ! !--SPH kernel - integral through cubic spline ! interpolate from a pre-calculated table ! if (q2.lt.radkernel2) then wab = wfromtable(q2) ! !--get incremental tau for this pixel from the integrated SPH kernel ! tau = rkappa*wab*term fopacity = 1. - exp(-tau) ! !--render, obscuring previously drawn pixels by relevant amount ! also calculate total brightness (`transparency') of each pixel ! datsmooth(ipix,jpix) = (1.-fopacity)*datsmooth(ipix,jpix) + fopacity*termi brightness(ipix,jpix) = brightness(ipix,jpix) + fopacity endif enddo enddo endif particle_within_zcut enddo over_particles !!$OMP END DO !!$OMP END PARALLEL ! !--get ending CPU time ! if (nsink > 99) then print*,'rendered ',nsink,' sink particles' elseif (nsink > 0) then print "(1x,a,i2,a)",'rendered ',nsink,' sink particles' endif call cpu_time(t_end) t_used = t_end - t_start if (t_used.gt.60.) then itmin = int(t_used/60.) tsec = t_used - (itmin*60.) print "(1x,a,i4,a,f5.2,1x,a)",'completed in',itmin,' min ',tsec,'s' else print "(1x,a,f5.2,1x,a)",'completed in ',t_used,'s' endif if (zcut.lt.huge(zcut)) print*,'slice contains ',nused,' of ',npart,' particles' return end subroutine interp3D_proj_opacity subroutine interp3D_proj_opacity_writeppm(datsmooth,brightness,npixx,npixy,datmin,datmax,istep) use colours, only:rgbtable,ncolours implicit none integer, intent(in) :: npixx,npixy real, intent(in), dimension(npixx,npixy) :: datsmooth,brightness real, intent(in) :: datmin,datmax integer, intent(in) :: istep character(len=120) :: filename ! real, dimension(3,npixx,npixy) :: rgb real, dimension(3) :: rgbi,drgb real :: dati,ddatrange,datfraci,ftable integer :: ipix,jpix,ir,ib,ig,ierr,maxcolour,indexi ! !--check for errors ! if (abs(datmax-datmin).gt.tiny(datmin)) then ddatrange = 1./abs(datmax-datmin) else print "(a)",'error: datmin=datmax : pointless writing ppm file' return endif ! !--write PPM-- ! write(filename,"(a,i5.5,a)") 'splash_',istep,'.ppm' open(unit=78,file=filename,status='replace',form='formatted',iostat=ierr) if (ierr /=0) then print*,'error opening ppm file' return endif print "(a)", 'writing to file '//trim(filename) ! !--PPM header ! maxcolour = 255 write(78,"(a)") 'P3' write(78,"(a)") '# splash.ppm created by splash (c) 2005-2007 Daniel Price' write(78,"(i4,1x,i4)") npixx, npixy write(78,"(i3)") maxcolour !--pixel information do jpix = npixy,1,-1 do ipix = 1,npixx dati = datsmooth(ipix,jpix) datfraci = (dati - datmin)*ddatrange datfraci = max(datfraci,0.) datfraci = min(datfraci,1.) !--define colour for current particle ftable = datfraci*ncolours indexi = int(ftable) + 1 indexi = min(indexi,ncolours) if (indexi.lt.ncolours) then !--do linear interpolation from colour table drgb(:) = rgbtable(:,indexi+1) - rgbtable(:,indexi) rgbi(:) = rgbtable(:,indexi) + (ftable - int(ftable))*drgb(:) else rgbi(:) = rgbtable(:,indexi) endif rgbi(:) = rgbi(:)*min(brightness(ipix,jpix),1.0) ir = max(min(int(rgbi(1)*maxcolour),maxcolour),0) ig = max(min(int(rgbi(2)*maxcolour),maxcolour),0) ib = max(min(int(rgbi(3)*maxcolour),maxcolour),0) write(78,"(i3,1x,i3,1x,i3,2x)") ir,ig,ib enddo enddo close(unit=78) return end subroutine interp3D_proj_opacity_writeppm end module interpolate3D_opacity splash/src/render.f90000644 000766 000000 00000022531 13261626263 015355 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------ ! Module containing "interface" routines between the calculated ! pixel arrays and the plot library routines which do the actual rendering !------------------------------------------------------------------------ module render use colourbar, only:plotcolourbar implicit none public :: render_pix, render_vec private contains !------------------------------------------------------------------------ ! this subroutine takes a 2D grid of data and renders it via the ! plotting library. Rendering is either: ! - contours (icolouropt = 0) ! - greyscale (icolouropt = 1) ! - colour (icolouropt>1) ! contouring plots nc contours between datmin and datmax. !------------------------------------------------------------------------ subroutine render_pix(datpix,datmin,datmax,label,npixx,npixy, & xmin,ymin,dx,dy,icolouropt,iplotcont,iColourBarStyle,ncontours,log, & ilabelcont,contmin,contmax,blank,transparent,alpha) use plotutils, only:formatreal use plotlib, only:plot_imag,plot_conb,plot_cons,plot_qch,plot_sch,& plot_qch,plot_sch,plot_conl,plot_gray,plot_imag_transparent,& plot_imag_alpha use contours_module, only:read_contours,contours_list,contourtitles implicit none integer, intent(in) :: npixx,npixy,ncontours,icolouropt real, intent(in) :: xmin,ymin,datmin,datmax,dx,dy real, dimension(npixx,npixy), intent(in) :: datpix logical, intent(in) :: iplotcont,log,ilabelcont integer, intent(in) :: iColourBarStyle character(len=*), intent(in) :: label real, intent(in), optional :: contmin,contmax,blank logical, intent(in), optional :: transparent real, dimension(npixx,npixy), intent(in), optional :: alpha integer :: i,ierr,nc real :: trans(6),levels(ncontours),dcont,charheight,cmin,cmax character(len=12) :: string logical :: iuse_transparent,ifixed_contours ! !--set up grid for rendering ! trans(1) = xmin - 0.5*dx ! this is for the pgimag call trans(2) = dx ! see help for pgimag/pggray/pgcont trans(3) = 0.0 trans(4) = ymin - 0.5*dy trans(5) = 0.0 trans(6) = dy iuse_transparent = .false. if (present(transparent)) iuse_transparent = transparent !print*,'rendering...',npixx,'x',npixy,'=',size(datpix),' pixels' if (abs(icolouropt).eq.1) then ! greyscale if (iColourBarStyle.gt.0) call plotcolourbar(iColourBarstyle,icolouropt,datmin,datmax,trim(label),log,0.) if (icolouropt.eq.1) then call plot_gray(datpix,npixx,npixy,1,npixx,1,npixy,datmin,datmax,trans) else !--allow inverse greyscale call plot_imag(datpix,npixx,npixy,1,npixx,1,npixy,datmin,datmax,trans) endif elseif (abs(icolouropt).gt.0) then ! colour ! !--plot colour bar ! if (iColourBarStyle.gt.0) call plotcolourbar(iColourBarstyle,icolouropt,datmin,datmax,trim(label),log,0.) ! !--plot pixel map ! if (iuse_transparent) then call plot_imag_transparent(datpix,npixx,npixy,1,npixx,1,npixy,datmin,datmax,trans) else if (present(alpha)) then call plot_imag_alpha(datpix,alpha,npixx,npixy,1,npixx,1,npixy,datmin,datmax,trans) else call plot_imag(datpix,npixx,npixy,1,npixx,1,npixy,datmin,datmax,trans) endif endif endif ! !--contours ! if (iplotcont) then nc = ncontours if (present(contmin)) then cmin = contmin else cmin = datmin endif if (present(contmax)) then cmax = contmax else cmax = datmax endif ! !--set contour levels: first attempt to read these ! from a file. If file does not exist or errors during read ! then we construct the default levels as usual. ! call read_contours(nc,ierr) if (ierr.eq.0 .and. nc.gt.0) then ifixed_contours = .true. else nc = ncontours ifixed_contours = .false. endif if (ifixed_contours) then do i=1,min(nc,ncontours) print*,"contour @ ", contours_list(i), ": ", trim(contourtitles(i)) levels(i) = contours_list(i) enddo dcont = 0. elseif (nc.le.0) then print*,'ERROR: cannot plot contours with ',nc,' levels' return elseif (nc.eq.1) then levels(1) = cmin dcont = 0. else dcont = (cmax-cmin)/real(nc-1) ! even contour levels do i=1,nc levels(i) = cmin + real(i-1)*dcont enddo endif ! !--plot contours (use pgcont if pgcons causes trouble) ! with blanking if blank is input ! if (present(blank)) then if (.not.ifixed_contours) then print 10,nc,' contours (with blanking)',levels(1),levels(nc),dcont print 20,levels(1:nc) endif !print*,' blanking = ',blank,'min,max = ',datmin,datmax call plot_conb(datpix,npixx,npixy,1,npixx,1,npixy,levels,nc,trans,blank) else if (.not.ifixed_contours) then print 10,nc,' contours',levels(1),levels(nc),dcont print 20,levels(1:nc) endif call plot_cons(datpix,npixx,npixy,1,npixx,1,npixy,levels,nc,trans) endif 10 format(1x,'plotting ',i4,a,' between ',es10.2,' and ',es10.2,', every ',es10.2,':') 20 format(10(6(1x,es9.2),/)) ! !--labelling of contour levels ! if (ilabelcont) then call plot_qch(charheight) ! query character height call plot_sch(0.75*charheight) ! shrink character height do i=1,nc if (ifixed_contours) then string=adjustl(contourtitles(i)) else call formatreal(levels(i),string) endif call plot_conl(datpix,npixx,npixy,1,npixx,1,npixy,levels(i),trans,trim(string),npixx/2,30) enddo call plot_sch(charheight) ! restore character height endif ! !--this line prints the label inside the contour plot ! (now obsolete-- this functionality can be achieved using plot titles) ! call pgmtxt('T',-2.0,0.05,0.0,trim(label)) endif return end subroutine render_pix !-------------------------------------------------------------------------- ! this subroutine takes a 2D grid of vector data (ie. x and y components) ! and plots an arrow map of it !-------------------------------------------------------------------------- subroutine render_vec(vecpixx,vecpixy,vecmax,npixx,npixy, & xmin,ymin,dx,dy,label,unitslabel,plotlegend) use legends, only:legend_vec use settings_vecplot, only:hposlegendvec,vposlegendvec,& iplotarrowheads,iallarrowssamelength use plotlib, only:plot_sah,plot_qch,plot_sch,plot_vect implicit none integer, intent(in) :: npixx,npixy real, intent(in) :: xmin,ymin,dx,dy real, intent(inout) :: vecmax real, dimension(npixx,npixy), intent(in) :: vecpixx,vecpixy real, dimension(npixx,npixy) :: dvmag character(len=*), intent(in) :: label,unitslabel logical, intent(in) :: plotlegend real :: trans(6),scale real :: charheight !set up grid for rendering trans(1) = xmin - 0.5*dx ! this is for the pgimag call trans(2) = dx ! see help for pgimag/pggray/pgcont trans(3) = 0.0 trans(4) = ymin - 0.5*dy trans(5) = 0.0 trans(6) = dy print*,'vector plot..',npixx,'x',npixy,'=',size(vecpixx),' pixels' !!print*,'max(x component) = ',maxval(vecpixx),'max(y component) = ',maxval(vecpixy) if (iplotarrowheads) then call plot_sah(2,45.0,0.7) ! arrow style else call plot_sah(2,0.0,1.0) endif call plot_qch(charheight) call plot_sch(0.3) ! size of arrow head if (iallarrowssamelength) then !!if (vecmax.le.0.0) vecmax = 1.0 ! adaptive limits scale=0.9*dx !!/vecmax print*,trim(label),' showing direction only: max = ',vecmax where (abs(vecpixx).gt.tiny(vecpixx) .and. abs(vecpixy).gt.tiny(vecpixy)) dvmag(:,:) = 1./sqrt(vecpixx**2 + vecpixy**2) elsewhere dvmag(:,:) = 0. end where call plot_vect(vecpixx(:,:)*dvmag(:,:),vecpixy(:,:)*dvmag(:,:),npixx,npixy, & 1,npixx,1,npixy,scale,0,trans,0.0) else if (vecmax.le.0.0) then ! adaptive limits scale = 0.0 vecmax = max(maxval(vecpixx(:,:)),maxval(vecpixy(:,:))) if (vecmax.gt.0.) scale = dx/vecmax else scale=dx/vecmax endif print*,trim(label),' max = ',vecmax call plot_vect(vecpixx(:,:),vecpixy(:,:),npixx,npixy, & 1,npixx,1,npixy,scale,0,trans,0.0) if (plotlegend) then call legend_vec(label,unitslabel,vecmax,dx,hposlegendvec,vposlegendvec,charheight) endif endif call plot_sch(charheight) return end subroutine render_vec end module render splash/src/write_data_gadget.f90000644 000766 000000 00000015040 13261626263 017531 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! Module implementing "splash to gadget" operation, writing ! a binary dump file suitable for input to the GADGET code !----------------------------------------------------------------- module write_data_gadget implicit none character(len=10), parameter, public :: formatname='gadget' public :: write_sphdata_gadget private contains subroutine write_sphdata_gadget(time,dat,iamtype,ntotal,ntypes,npartoftype, & masstype,ncolumns,filename) use labels, only:ih,ivx,ix,iutherm,irho,ipmass use settings_data, only:ndim use limits, only:lim use params, only:int1 implicit none integer, intent(in) :: ntotal,ntypes,ncolumns integer, intent(in), dimension(:) :: npartoftype real, intent(in) :: time real, intent(in), dimension(ntotal,ncolumns) :: dat integer(kind=int1), intent(in), dimension(:) :: iamtype real, intent(in), dimension(:) :: masstype character(len=*), intent(in) :: filename integer, parameter :: idump = 83 character(len=len(filename)+10) :: outfile integer, dimension(6) :: nall,ncrap,noftype real(kind=8), dimension(6) :: massoftype real(kind=8) :: boxsize,dtime real(kind=8), parameter :: dumz = 0.d0 real(kind=4), dimension(15) :: unused integer, parameter :: iflagsfr = 0, iflagfeedback = 0, iflagcool = 0 integer, parameter :: nfiles = 1 integer :: ierr,i,j,nmasses,ngas,itype integer, dimension(:), allocatable :: iorder ! !--define output file name ! outfile=trim(filename)//'.gadget' ! !--check if we have enough data to write a GADGET dump ! if (ndim.lt.3) then print "(a)",' ERROR: ndim < 3 but must be 3 for GADGET data -- cannot write PHANTOM dump, skipping...' return endif if (any(ix(:).le.0)) then print "(a)",' ERROR: position labels not set -- cannot write GADGET dump, skipping...' return endif if (ivx.le.0) then print "(a)",' ERROR: velocity not found in data -- cannot write GADGET dump, skipping...' return endif if (iutherm.le.0) then print "(a)",' ERROR: thermal energy not found in data -- cannot write GADGET dump, skipping...' return endif if (irho.le.0) then print "(a)",' ERROR: density not found in data -- cannot write GADGET dump, skipping...' return endif if (ih.le.0) then print "(a)",' ERROR: smoothing length not found in data -- cannot write GADGET dump, skipping...' return endif ! !--open dumpfile ! write(*,"(/,/,'--------> TIME = ',f10.4,"// & "': writing GADGET snapshot file ',a,' <--------',/)") time,trim(outfile) print "(a)", ' WARNING: conversion to GADGET format is LIMITED in scope...' print "(a)", ' Currently converts only basic hydro quantities (x,v,m,u,rho,h) ' print "(a)", ' and header quantities may be guessed/fudged ...your mileage may vary' print "(a,/)",' (if you use this functionality and want it improved, just let me know)' print "(a,i2)",' writing to unit ',idump open(unit=idump,file=outfile,status='replace',form='unformatted',iostat=ierr) if (ierr /= 0) then write(*,*) 'error: can''t create new dumpfile ',trim(outfile) return endif massoftype(:) = 0. nall(:) = 0 noftype(:) = 0 massoftype(1:ntypes) = masstype(1:ntypes) nall(1:ntypes) = npartoftype(1:ntypes) noftype(1:ntypes) = npartoftype(1:ntypes) ncrap(:) = 0 boxsize = abs(lim(ix(1),2) - lim(ix(1),1)) unused(:) = 0 dtime = time write(idump,err=100) noftype(1:6),massoftype(1:6),dtime,dumz, & iflagsfr,iflagfeedback,nall(1:6),iflagcool,nfiles,boxsize, & dumz,dumz,dumz,iflagsfr,iflagsfr,ncrap(1:6),iflagsfr,unused(:) ! !--work out how many particle masses to write ! nmasses = 0 do j=1,6 if (massoftype(j).le.0.) then nmasses = nmasses + noftype(j) endif enddo print*,'nmasses = ',nmasses ngas = npartoftype(1) print*,'ngas = ',ngas if (ntotal > ngas .and. (size(iamtype).gt.1)) then !--must print the particles ordered by type allocate(iorder(ntotal),stat=ierr) j = 0 do itype=1,min(ntypes,6) if (npartoftype(itype).gt.0) then do i=1,ntotal if (iamtype(i).eq.itype) then j = j + 1 iorder(j) = i endif enddo endif enddo if (j.lt.ntotal) then print*,' ERROR: too many particle types in conversion to gadget format' do i=j+1,ntotal iorder(i) = i enddo endif write(idump,err=100) ((dat(iorder(i),ix(j)),j=1,3),i=1,ntotal) write(idump,err=100) ((dat(iorder(i),j),j=ivx,ivx+2),i=1,ntotal) write(idump,err=100) (iorder(i),i=1,ntotal) ! particle id write(idump,err=100) (dat(iorder(i),ipmass), i=1,nmasses) write(idump,err=100) (dat(iorder(i),iutherm),i=1,ngas) write(idump,err=100) (dat(iorder(i),irho), i=1,ngas) write(idump,err=100) (2.*dat(iorder(i),ih), i=1,ngas) deallocate(iorder) else write(idump,err=100) ((dat(i,ix(j)),j=1,3),i=1,ntotal) write(idump,err=100) ((dat(i,j),j=ivx,ivx+2),i=1,ntotal) write(idump,err=100) (i,i=1,ntotal) ! particle id write(idump,err=100) (dat(i,ipmass), i=1,nmasses) write(idump,err=100) (dat(i,iutherm),i=1,ngas) write(idump,err=100) (dat(i,irho), i=1,ngas) write(idump,err=100) (2.*dat(i,ih), i=1,ngas) endif print*,'finished writing file -- OK' close(unit=idump) return 100 continue write(*,*) 'error whilst writing dumpfile '//trim(outfile) close(unit=idump) end subroutine write_sphdata_gadget end module write_data_gadget splash/src/read_data_jjm.f90000644 000766 000000 00000014340 13261626263 016641 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR JOE'S 2D SPH CODE ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! iam(maxpart,maxstep): integer identification of particle type ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns use mem_allocation implicit none integer, intent(IN) :: indexstart,ipos integer, intent(OUT) :: nstepsread character(LEN=*), intent(IN) :: rootname integer :: i,j,ifile,ierr integer :: istep,nprint,npart_max,nstep_max,icol logical :: iexist character(LEN=LEN(rootname)+4) :: dumpfile real :: timei,dti,hi nstepsread = 0 nstep_max = 0 npart_max = maxpart ifile = 1 dumpfile = trim(rootname) ! if (index(dumpfile,'.plt').eq.0) dumpfile = trim(rootname)//'.plt' ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: ',trim(dumpfile),' file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 2 ndimV = 2 ncolumns = 7 ! number of columns in file ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,2) j = indexstart nstepsread = 0 print "(1x,a)",'reading Joe Monaghan ascii format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='formatted') if (ierr /= 0) then print*,'*** ERROR OPENING ',trim(dumpfile),' ***' else ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,*,end=55,iostat=ierr) istep,nprint,timei,dti print*,'first time = ',timei,nprint if (.not.allocated(dat) .or. (nprint.gt.npart_max)) then npart_max = max(npart_max,INT(1.1*(nprint))) call alloc(npart_max,nstep_max,ncolumns) endif rewind(15) endif if (ierr /= 0) then print*,'*** ERROR READING TIMESTEP HEADER ***' else oversteps: do ! !--loop over the timesteps in this file ! npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,2*j,maxcol) endif ! !--now read the timestep data in the dumpfile ! read(15,*,end=55,iostat=ierr) istep,nprint,hi,time(j),dti do i=1,nprint read(15,*,end=55,iostat=ierr) (dat(i,icol,j),icol = 1,ncolumns) enddo masstype(1,j) = 1. if (ierr /= 0) then print "(a)",'|*** ERROR READING TIMESTEP ***' return else nstepsread = nstepsread + 1 endif npartoftype(:,j) = 0 npartoftype(1,j) = nprint print*,j,' time = ',time(j) gamma(j) = 1.666666666667 j = j + 1 enddo oversteps endif 55 continue ! !--reached end of file ! close(15) print*,'nstepsread = ',nstepsread print*,'>> end of dump file: nsteps =',j-1,'ntot = ',npartoftype(1,j-1),'nptmass=',npartoftype(2,j-1) return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo label(ix(1:ndim)) = labelcoord(1:ndim,1) ivx = ndim+1 ipr = ndim+ndimV+2 ih = ndim+ndimV+3 ! smoothing length label(ih) = 'h' irho = ndim+ndimV+1 ! location of rho in data array label(irho) = 'density' if (ipr.gt.0) label(ipr) = 'pressure' iutherm = 0 ! thermal energy ! label(iutherm) = 'u' ipmass = 0 ! particle mass ! label(ipmass) = 'particle mass' iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 1 !!maxparttypes labeltype(1) = 'gas' UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/interpolate_vec.f90000644 000766 000000 00000014234 13261626263 017262 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !-------------------------------------------------------------------------- ! ! Module with some auxiliary routines related to vector interpolation ! !-------------------------------------------------------------------------- module interpolate_vec implicit none public :: interpolate_vec_average public :: mask_vectors private contains !-------------------------------------------------------------------------- ! ! Hides vector arrows (sets them to zero) where there are no particles ! contained in the pixel (as opposed to merely contributing to the pixel) ! ! means you can avoid funny looking plots with arrows in otherwise ! empty regions ! ! Daniel Price 26/3/07 ! !-------------------------------------------------------------------------- subroutine mask_vectors(xplot,yplot,itype,npart,xmin,xmax,ymin,ymax, & vecpixx,vecpixy,npixvecx,npixvecy,minincell,blankval) implicit none integer, intent(in) :: npart,npixvecx,npixvecy,minincell integer, dimension(npart), intent(in) :: itype real, dimension(npart), intent(in) :: xplot,yplot real, intent(in) :: xmin,xmax,ymin,ymax,blankval real, dimension(npixvecx,npixvecy), intent(inout) :: vecpixx,vecpixy integer, dimension(npixvecx,npixvecy) :: nincell integer :: icellx,icelly,j real :: dxcell1,dycell1 character(len=16) :: chmin !--write nice, neat information line write(chmin,"(g10.0)") minincell print "(2x,a)",'(hiding arrows where there are < '//trim(adjustl(chmin))//' particles in pixel cell)' dxcell1 = (npixvecx - 1)/(xmax-xmin + tiny(xmin)) dycell1 = (npixvecy - 1)/(ymax-ymin + tiny(ymin)) ! !--count particles which fall into each pixel ("cell") ! nincell(:,:) = 0 do j=1,npart if (itype(j).ge.0) then ! exclude not-plotted particles icellx = int((xplot(j) - xmin)*dxcell1) + 1 icelly = int((yplot(j) - ymin)*dycell1) + 1 !--count number of particles in each cell if (icellx.gt.0 .and. icellx.le.npixvecx & .and. icelly.gt.0 .and. icelly.le.npixvecy) then nincell(icellx,icelly) = nincell(icellx,icelly) + 1 endif endif enddo ! !--set vector arrow lengths to zero in cells where there are no particles ! where (nincell.lt.minincell) vecpixx = blankval vecpixy = blankval end where return end subroutine mask_vectors !-------------------------------------------------------------------------- ! Interpolates vector quantity from particles to even grid of pixels ! ! This version just does a simple averaging by binning particles ! and taking the average of vx,vy in the cell to give a vector for ! that cell. This is because the interpolation of a vector quantity is ! usually to a *coarser* grid than the particles. ! ! Input: particle coordinates : x,y (npart) ! vector data to smooth : vecx (npart) ! vecy (npart) ! grid setup : xmin, ymin, dx ! ! Output: smoothed vector field : vecpixx (npixx,npixy) ! : vecpixy (npixx,npixy) ! ! Daniel Price, Institute of Astronomy, Cambridge, 20/8/04 !-------------------------------------------------------------------------- subroutine interpolate_vec_average(x,y,vecx,vecy,itype, & xmin,ymin,dx,dy,vecpixx,vecpixy,npart,npixx,npixy,z,zmin,zmax) implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,vecx,vecy integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,dx,dy real, intent(out), dimension(npixx,npixy) :: vecpixx, vecpixy real, intent(in), optional :: z(npart),zmin,zmax integer :: i,j,k,ix,iy integer, dimension(npixx,npixy) :: ihoc,numcell integer, dimension(npart) :: ll logical :: xsec !print "(a,i3,a,i3,a)",' averaging vector field onto ',npixx,'x',npixy,' pixels...' if (dx <= 0. .or. dy <= 0.) then print*,'interpolate_vec: error: pixel width <= 0' return endif ! !--interpolation is to a coarser grid, so just average ! bin particles into cells using a link list ! ihoc(:,:) = -1 ! head of chain numcell(:,:) = 0 xsec = present(z) .and. present(zmin) .and. present(zmax) over_parts: do i=1,npart if (xsec) then if (z(i) < zmin .or. z(i) > zmax) cycle over_parts endif if (itype(i).ge.0) then ix = int((x(i)-xmin)/dx)+1 iy = int((y(i)-ymin)/dy)+1 if ((ix.ge.1).and.(ix.le.npixx).and.(iy.ge.1).and.(iy.le.npixy)) then ll(i)=ihoc(ix,iy) ! set link list of this particle to old head of list ihoc(ix,iy) = i ! set head of chain to this particle endif endif enddo over_parts ! !--add up total vx,vy in each cell ! vecpixx(:,:) = 0. vecpixy(:,:) = 0. do j=1,npixy do i=1,npixx k = ihoc(i,j) do while (k.ne.-1) vecpixx(i,j) = vecpixx(i,j) + vecx(k) vecpixy(i,j) = vecpixy(i,j) + vecy(k) numcell(i,j) = numcell(i,j) + 1 k = ll(k) enddo enddo enddo ! !--divide by number of particles in that cell to get average vx,vy ! do j=1,npixy do i=1,npixx if (numcell(i,j).ne.0) then vecpixx(i,j) = vecpixx(i,j)/float(numcell(i,j)) vecpixy(i,j) = vecpixy(i,j)/float(numcell(i,j)) endif enddo enddo return end subroutine interpolate_vec_average end module interpolate_vec splash/src/read_data_amuse_hdf5.f90000644 000766 000000 00000031656 13261626263 020112 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR HDF5 OUTPUT FROM THE AMUSE FRAMEWORK ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Columns with the 'required' flag set to false are not read !------------------------------------------------------------------------- ! ! The module below contains interface routines to c functions ! that perform the actual calls to the HDF5 libs ! !------------------------------------------------------------------------- module amusehdf5read use params, only:maxplot,doub_prec use labels, only:lenlabel use, intrinsic :: iso_c_binding, only:c_int,c_double,c_char implicit none real :: hsoft character(len=lenlabel), dimension(maxplot) :: blocklabel logical :: havewarned = .false. integer, parameter :: maxtypes = 6 interface subroutine read_amuse_hdf5_header(filename,npart,ncol,ndim,ndimV,time,ierr) bind(c) import character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(out) :: npart,ncol,ndim,ndimV,ierr real(kind=c_double), intent(out) :: time end subroutine read_amuse_hdf5_header subroutine read_amuse_hdf5_data(filename,maxtypes,npartoftypei,& ncol,isrequired,ierr) bind(c) import implicit none character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(in), value :: maxtypes integer(kind=c_int), dimension(6), intent(in) :: npartoftypei integer(kind=c_int), intent(in), value :: ncol integer(kind=c_int), intent(out) :: ierr integer(kind=c_int), dimension(ncol), intent(in) :: isrequired end subroutine read_amuse_hdf5_data end interface end module amusehdf5read !------------------------------------------------------------------------- ! ! The routine that reads the data into splash's internal arrays ! !------------------------------------------------------------------------- subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol,maxstep use params, only:doub_prec,maxparttypes,maxplot use settings_data, only:ndim,ndimV,ncolumns,ncalc,iformat,required,ipartialread, & ntypes,debugmode,iverbose use settings_page, only:legendtext use mem_allocation, only:alloc use labels, only:ih,irho,ipmass,labeltype use system_utils, only:renvironment,lenvironment,ienvironment,envlist use asciiutils, only:cstring use amusehdf5read, only:hsoft,blocklabel,havewarned,read_amuse_hdf5_header, & read_amuse_hdf5_data,maxtypes implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile,densfile,hfile character(len=20) :: string integer :: i,j,itype,ierr integer :: index1,index2,nhfac integer :: ncolstep,npart_max,nstep_max,ntoti,ntotall,idot integer, parameter :: iunit = 11 logical :: iexist,reallocate,usez,debug,goterrors real(doub_prec) :: timetemp,ztemp real :: hfact,hfactmean,pmassi real, parameter :: pi = 3.1415926536 integer, dimension(maxplot) :: isrequired nstepsread = 0 goterrors = .false. if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! print "(1x,a)",'reading AMUSE HDF5 format' inquire(file=datfile,exist=iexist) if (.not.iexist) then ! !--append .hdf5 on the end if not already present ! datfile=trim(rootname)//'.hdf5' inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(rootname)//': file not found ***' return endif endif ! !--set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 ! !--read data from snapshots ! i = istepstart write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open file and read header information ! if (debug) print*,'DEBUG: reading header...' call read_amuse_hdf5_header(cstring(datfile),ntoti,ncolstep,ndim,ndimV,timetemp,ierr) if (ierr /= 0) then print "(a)", '*** ERROR READING HEADER ***' return endif ncolumns = ncolstep if (iverbose >= 1) print "(2(a,1x,i10))",' npart: ',ntoti,' ncolumns: ',ncolstep ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntotall) else ! if first time, save on memory npart_max = int(ntoti) endif endif if (i.ge.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol)) endif ! !--copy header data into allocated arrays ! npartoftype(1,i) = ntoti time(i) = real(timetemp) masstype(:,i) = 0. ! all masses read from file ! !--read particle data ! got_particles: if (ntoti > 0) then isrequired(:) = 0 where (required(1:ncolumns)) isrequired(1:ncolumns) = 1 call read_amuse_hdf5_data(cstring(datfile),ntypes,npartoftype(:,i),ncolumns,isrequired,ierr) nstepsread = 1 endif got_particles ! !--now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. ! !--set flag to indicate that only part of this file has been read ! if (.not.all(required(1:ncolstep))) ipartialread = .true. ! !--call set labels to identify location of smoothing length ! call set_labels ! !--for read from multiple files, work out the next file in the sequence ! ! !--for some reason the smoothing length output by GADGET is ! twice the usual SPH smoothing length ! (do this after we have read data from all of the files) ! if (required(ih) .and. size(dat(1,:,:)).ge.ih .and. npartoftype(1,i).gt.0 .and. ih.gt.0) then print "(a)",' converting GADGET smoothing length on gas particles to usual SPH definition (x 0.5)' dat(1:npartoftype(1,i),ih,i) = 0.5*dat(1:npartoftype(1,i),ih,i) endif ! !--give a friendly warning about using too few or too many neighbours ! (only works with equal mass particles because otherwise we need the number density estimate) ! if (ih.gt.0 .and. required(ih) .and. ipmass.gt.0 .and. required(ipmass) & .and. abs(masstype(1,i)).lt.tiny(0.) .and. ndim.eq.3 .and. .not.havewarned) then nhfac = 100 if (npartoftype(1,i).gt.nhfac) then hfactmean = 0. do j=1,nhfac pmassi = dat(j,ipmass,i) if (pmassi.gt.0.) then pmassi = 1./pmassi else pmassi = 0. endif hfact = dat(j,ih,i)*(dat(j,irho,i)*pmassi)**(1./ndim) hfactmean = hfactmean + hfact enddo hfact = hfactmean/real(nhfac) havewarned = .true. print "(/,1x,a,f5.1,a,/,1x,a,f4.2,a,i1,a,/)", & 'Simulations employ ',4./3.*pi*(2.*hfact)**3,' neighbours,', & 'corresponding to h = ',hfact,'*(m/rho)^(1/',ndim,') in 3D' endif endif ! !--cover the special case where no particles have been read ! if (ntoti.le.0) then npartoftype(1,i) = 1 dat(:,:,i) = 0. endif if (nstepsread.gt.0) then print "(a,i10,a)",' >> read ',sum(npartoftype(:,istepstart+nstepsread-1)),' particles' endif return end subroutine read_data subroutine read_amuse_hdf5_data_fromc(icol,npartoftypei,temparr,itype) bind(c) use, intrinsic :: iso_c_binding, only:c_int,c_double use particle_data, only:dat,iamtype use settings_data, only:debugmode use labels, only:label implicit none integer(kind=c_int), intent(in) :: icol,npartoftypei,itype real(kind=c_double), intent(in) :: temparr(npartoftypei) integer(kind=c_int) :: i,icolput integer :: nmax,nerr,idi logical :: useids icolput = icol if (debugmode) print "(a,i2,a,i2,a,i8)",'DEBUG: reading column ',icol,' type ',itype,' -> '//trim(label(icolput)) ! check column is within array limits if (icolput.gt.size(dat(1,:,1)) .or. icolput.eq.0) then print "(a,i2,a)",' ERROR: column = ',icolput,' out of range in receive_data_fromc' return endif ! ensure no array overflows nmax = min(npartoftypei,size(dat(:,1,1))) ! copy data into main splash array dat(1:nmax,icolput,1) = real(temparr(1:nmax)) ! set particle type if (size(iamtype(:,1)).gt.1) then do i=1,nmax iamtype(i,1) = itype + 1 enddo endif return end subroutine read_amuse_hdf5_data_fromc !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass, & ih,irho,ipr,iutherm,iBfirst,idivB,iax use params use settings_data, only:ndim,ndimV,ncolumns,ntypes,UseTypeInRenderings,iformat use geometry, only:labelcoord use system_utils, only:envlist,ienvironment use amusehdf5read, only:hsoft,blocklabel use asciiutils, only:lcase implicit none integer :: i,j,icol,irank if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif ix = 0 iutherm = 0 do icol=1,size(blocklabel) select case(trim(lcase(blocklabel(icol)))) case('x') ix(1) = icol case('y') ix(2) = icol case('z') ix(3) = icol case('vx') ivx = icol case('ax') iax = icol case('h_smooth') ih = icol case('mass') ipmass = icol case('density') irho = icol end select label(icol) = trim(blocklabel(icol)) enddo ! set labels of the quantities read in if (ix(1).gt.0) label(ix(1:ndim)) = labelcoord(1:ndim,1) !if (irho.gt.0) label(irho) = 'density' !if (iutherm.gt.0) label(iutherm) = 'u' !if (ipmass.gt.0) label(ipmass) = 'particle mass' !if (ih.gt.0) label(ih) = 'h' ! set labels for vector quantities if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'_'//labelcoord(i,1) enddo endif if (iax.gt.0) then iamvec(iax:iax+ndimV-1) = iax labelvec(iax:iax+ndimV-1) = 'a' do i=1,ndimV label(iax+i-1) = trim(labelvec(iax))//'_'//labelcoord(i,1) enddo endif ! set labels for each particle type labeltype(1) = 'gas' UseTypeInRenderings(:) = .false. UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels subroutine set_blocklabel(icol,name) bind(c) use, intrinsic :: iso_c_binding, only:c_int, c_char use amusehdf5read, only:blocklabel use asciiutils, only:fstring implicit none integer(kind=c_int), intent(in) :: icol character(kind=c_char), intent(in) :: name(256) blocklabel(icol) = trim(fstring(name)) !print*,icol,' name = ',trim(blocklabel(icol)) end subroutine set_blocklabel splash/src/read_data_sphNG.f90000644 000766 000000 00000263513 13261626263 017110 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM ! THE NEXT GENERATION SPH CODE (sphNG) ! ! (also my Phantom SPH code which uses a similar format) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! SSPLASH_RESET_CM if 'YES' then centre of mass is reset to origin ! SSPLASH_OMEGA if non-zero subtracts corotating velocities with omega as set ! SSPLASH_OMEGAT if non-zero subtracts corotating positions and velocities with omega as set ! SSPLASH_TIMEUNITS sets default time units, either 's','min','hrs','yrs' or 'tfreefall' ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Partial data read implemented Nov 2006 means that columns with ! the 'required' flag set to false are not read (read is therefore much faster) !------------------------------------------------------------------------- module sphNGread use params implicit none real(doub_prec) :: udist,umass,utime,umagfd real :: tfreefall integer :: istartmhd,istartrt,nmhd,idivvcol,icurlvxcol,icurlvycol,icurlvzcol integer :: nhydroreal4,istart_extra_real4 integer :: nhydroarrays,nmhdarrays,ndustarrays logical :: phantomdump,smalldump,mhddump,rtdump,usingvecp,igotmass,h2chem,rt_in_header,onefluid_dust logical :: usingeulr,cleaning logical :: batcode,tagged,debug integer, parameter :: maxarrsizes = 10 integer, parameter :: maxinblock = 128 ! max allowed in each block integer, parameter :: lentag = 16 character(len=lentag) :: tagarr(maxplot) integer, parameter :: itypemap_sink_phantom = 3 integer, parameter :: itypemap_dust_phantom = 2 integer, parameter :: itypemap_unknown_phantom = 8 !------------------------------------------ ! generic interface to utilities for tagged ! dump format !------------------------------------------ interface extract module procedure extract_int, extract_real4, extract_real8, & extract_intarr, extract_real4arr, extract_real8arr end interface extract contains !------------------------------------------------------------------- ! function mapping iphase setting in sphNG to splash particle types !------------------------------------------------------------------- elemental integer function itypemap_sphNG(iphase) integer*1, intent(in) :: iphase select case(int(iphase)) case(0) itypemap_sphNG = 1 case(1:9) itypemap_sphNG = 3 case(10:) itypemap_sphNG = 4 case default itypemap_sphNG = 5 end select end function itypemap_sphNG !--------------------------------------------------------------------- ! function mapping iphase setting in Phantom to splash particle types !--------------------------------------------------------------------- elemental integer function itypemap_phantom(iphase) integer*1, intent(in) :: iphase select case(int(iphase)) case(1:2) itypemap_phantom = iphase case(3:6) ! put sinks as type 3, everything else shifted by one itypemap_phantom = iphase + 1 case(-3) ! sink particles, either from external_binary or read from dump itypemap_phantom = itypemap_sink_phantom case default itypemap_phantom = itypemap_unknown_phantom end select end function itypemap_phantom !------------------------------------------ ! extraction of single integer variables !------------------------------------------ subroutine extract_int(tag,ival,intarr,tags,ntags,ierr) character(len=*), intent(in) :: tag integer, intent(out) :: ival integer, intent(in) :: ntags,intarr(:) character(len=lentag), intent(in) :: tags(:) integer, intent(out) :: ierr logical :: matched integer :: i ierr = 1 matched = .false. ival = 0 ! default if not found over_tags: do i=1,min(ntags,size(tags)) if (trim(tags(i))==trim(adjustl(tag))) then if (size(intarr) >= i) then ival = intarr(i) matched = .true. endif exit over_tags ! only match first occurrence endif enddo over_tags if (matched) ierr = 0 if (ierr /= 0) print "(a)",' ERROR: could not find '//trim(adjustl(tag))//' in header' end subroutine extract_int !------------------------------------------ ! extraction of single real*8 variables !------------------------------------------ subroutine extract_real8(tag,rval,r8arr,tags,ntags,ierr) character(len=*), intent(in) :: tag real*8, intent(out) :: rval real*8, intent(in) :: r8arr(:) character(len=lentag), intent(in) :: tags(:) integer, intent(in) :: ntags integer, intent(out) :: ierr logical :: matched integer :: i ierr = 1 matched = .false. rval = 0.d0 ! default if not found over_tags: do i=1,min(ntags,size(tags)) if (trim(tags(i))==trim(adjustl(tag))) then if (size(r8arr) >= i) then rval = r8arr(i) matched = .true. endif exit over_tags ! only match first occurrence endif enddo over_tags if (matched) ierr = 0 if (ierr /= 0) print "(a)",' ERROR: could not find '//trim(adjustl(tag))//' in header' end subroutine extract_real8 !------------------------------------------ ! extraction of single real*4 variables !------------------------------------------ subroutine extract_real4(tag,rval,r4arr,tags,ntags,ierr) character(len=*), intent(in) :: tag real*4, intent(out) :: rval real*4, intent(in) :: r4arr(:) character(len=lentag), intent(in) :: tags(:) integer, intent(in) :: ntags integer, intent(out) :: ierr logical :: matched integer :: i ierr = 1 matched = .false. rval = 0. ! default if not found over_tags: do i=1,min(ntags,size(tags)) if (trim(tags(i))==trim(adjustl(tag))) then if (size(r4arr) >= i) then rval = r4arr(i) matched = .true. endif exit over_tags ! only match first occurrence endif enddo over_tags if (matched) ierr = 0 if (ierr /= 0) print "(a)",' ERROR: could not find '//trim(adjustl(tag))//' in header' end subroutine extract_real4 !------------------------------------------ ! extraction of integer arrays !------------------------------------------ subroutine extract_intarr(tag,ival,intarr,tags,ntags,ierr) character(len=*), intent(in) :: tag integer, intent(out) :: ival(:) integer, intent(in) :: ntags,intarr(:) character(len=lentag), intent(in) :: tags(:) integer, intent(out) :: ierr integer :: i,nmatched ierr = 1 nmatched = 0 ival(:) = 0 ! default if not found over_tags: do i=1,min(ntags,size(tags)) if (trim(tags(i))==trim(adjustl(tag))) then if (size(intarr) >= i .and. size(ival) > nmatched) then nmatched = nmatched + 1 ival(nmatched) = intarr(i) endif endif enddo over_tags if (nmatched==size(ival)) ierr = 0 if (ierr /= 0) print "(a)",' ERROR: could not find '//trim(adjustl(tag))//' in header' end subroutine extract_intarr !------------------------------------------ ! extraction of real*8 arrays !------------------------------------------ subroutine extract_real8arr(tag,rval,r8arr,tags,ntags,ierr) character(len=*), intent(in) :: tag real*8, intent(out) :: rval(:) real*8, intent(in) :: r8arr(:) character(len=lentag), intent(in) :: tags(:) integer, intent(in) :: ntags integer, intent(out) :: ierr integer :: i,nmatched ierr = 1 nmatched = 0 rval = 0.d0 ! default if not found over_tags: do i=1,min(ntags,size(tags)) if (trim(tags(i))==trim(adjustl(tag))) then if (size(r8arr) >= i .and. size(rval) > nmatched) then nmatched = nmatched + 1 rval(nmatched) = r8arr(i) endif endif enddo over_tags if (nmatched==size(rval)) ierr = 0 if (ierr /= 0) print "(a)",' ERROR: could not find '//trim(adjustl(tag))//' in header' end subroutine extract_real8arr !------------------------------------------ ! extraction of real*4 arrays !------------------------------------------ subroutine extract_real4arr(tag,rval,r4arr,tags,ntags,ierr) character(len=*), intent(in) :: tag real*4, intent(out) :: rval(:) real*4, intent(in) :: r4arr(:) character(len=lentag), intent(in) :: tags(:) integer, intent(in) :: ntags integer, intent(out) :: ierr integer :: i,nmatched ierr = 1 nmatched = 0 rval = 0. ! default if not found over_tags: do i=1,min(ntags,size(tags)) if (trim(tags(i))==trim(adjustl(tag))) then if (size(r4arr) >= i .and. size(rval) > nmatched) then nmatched = nmatched + 1 rval(nmatched) = r4arr(i) endif endif enddo over_tags if (nmatched==size(rval)) ierr = 0 if (ierr /= 0) print "(a)",' ERROR: could not find '//trim(adjustl(tag))//' in header' end subroutine extract_real4arr !---------------------------------------------------------------------- ! Extract various options from the fileident string !---------------------------------------------------------------------- subroutine get_options_from_fileident(fileident,smalldump,tagged,phantomdump,& usingvecp,usingeulr,cleaning,h2chem,use_dustfrac,rt_in_header,batcode) character(len=*), intent(in) :: fileident logical, intent(out) :: smalldump,tagged,phantomdump,batcode logical, intent(out) :: usingvecp,usingeulr,cleaning,h2chem,use_dustfrac,rt_in_header smalldump = .false. phantomdump = .false. usingvecp = .false. usingeulr = .false. cleaning = .false. h2chem = .false. rt_in_header = .false. batcode = .false. tagged = .false. use_dustfrac = .false. if (fileident(1:1).eq.'S') then smalldump = .true. endif if (fileident(2:2).eq.'T') then tagged = .true. endif if (index(fileident,'Phantom').ne.0) then phantomdump = .true. else phantomdump = .false. endif if (index(fileident,'vecp').ne.0) then usingvecp = .true. endif if (index(fileident,'eulr').ne.0) then usingeulr = .true. endif if (index(fileident,'clean').ne.0) then cleaning = .true. endif if (index(fileident,'H2chem').ne.0) then h2chem = .true. endif if (index(fileident,'RT=on').ne.0) then rt_in_header = .true. endif if (index(fileident,'dust').ne.0) then use_dustfrac = .true. endif if (index(fileident,'This is a test').ne.0) then batcode = .true. endif end subroutine get_options_from_fileident !---------------------------------------------------------------------- ! Set position of header items manually for older (untagged) formats !---------------------------------------------------------------------- subroutine fake_header_tags(nreals,realarr,tagsreal) integer, intent(in) :: nreals real, intent(in) :: realarr(nreals) character(len=*), intent(out) :: tagsreal(:) integer, parameter :: ilocbinary = 24 integer :: ipos ! !--set the tags manually for older formats ! (but only the ones we care about) ! if (phantomdump) then tagsreal(1) = 'time' tagsreal(3) = 'gamma' tagsreal(4) = 'rhozero' tagsreal(6) = 'hfact' tagsreal(7) = 'tolh' tagsreal(15:19) = 'massoftype' if (nreals.ge.ilocbinary + 14) then if (nreals.ge.ilocbinary + 15) then ipos = ilocbinary else print*,'*** WARNING: obsolete header format for external binary information ***' ipos = ilocbinary + 1 endif if (debug) print*,'DEBUG: reading binary information from header ',ilocbinary if (any(realarr(ilocbinary:ilocbinary+14).ne.0.)) then tagsreal(ipos:ipos+2) = (/'x1','y1','z1'/) if (nreals.ge.ilocbinary+15) then tagsreal(ipos+3:ipos+9) = (/'m1','h1','x2','y2','z2','m2','h2'/) ipos = ipos + 10 else tagsreal(ipos+3:ipos+7) = (/'h1','x2','y2','z2','h2'/) ipos = ipos + 8 endif tagsreal(ipos:ipos+5) = (/'vx1','vy1','vz1','vx2','vy2','vz2'/) endif endif elseif (batcode) then tagsreal(1) = 'time' tagsreal(3) = 'gamma' tagsreal(4) = 'radL1' tagsreal(5) = 'PhiL1' tagsreal(15) = 'Er' else tagsreal(1) = 'gt' tagsreal(2) = 'dtmax' tagsreal(3) = 'gamma' tagsreal(4) = 'rhozero' tagsreal(5) = 'RK2' if (smalldump) then ! sphNG small dump if (nreals.eq.15) then tagsreal(15) = 'pmassinitial' else tagsreal(23) = 'pmassinitial' endif endif endif end subroutine fake_header_tags !---------------------------------------------------------------------- ! Routine to read the header of sphNG dump files and extract relevant ! information !---------------------------------------------------------------------- subroutine read_header(iunit,iverbose,debug,doubleprec,& npart,npartoftypei,n1,ntypes,nblocks,& narrsizes,realarr,tagsreal,nreals,ierr) use settings_data, only:ndusttypes integer, intent(in) :: iunit,iverbose logical, intent(in) :: debug,doubleprec integer, intent(out) :: npart,npartoftypei(:),n1,ntypes,nblocks,narrsizes,nreals,ierr real, intent(out) :: realarr(maxinblock) character(len=lentag), intent(out) :: tagsreal(maxinblock) character(len=lentag) :: tags(maxinblock) integer :: intarr(maxinblock) real(doub_prec) :: real8arr(maxinblock) real(sing_prec) :: real4arr(maxinblock) integer :: i,ierr1,ierr2,ierrs(4) integer :: nints,ninttypes,nreal4s,nreal8s,n2,nreassign,naccrete,nkill real(doub_prec), allocatable :: dattemp(:) real(sing_prec), allocatable :: dattempsingle(:) ! initialise empty tag array tags(:) = '' intarr(:) = 0 nblocks = 1 ! number of MPI blocks npartoftypei(:) = 0 read(iunit,iostat=ierr) nints if (ierr /=0) then print "(a)",'error reading nints' return else if (tagged) then if (nints > maxinblock) then print*,'WARNING: number of ints in header exceeds splash array limit, ignoring some' nints = maxinblock endif read(iunit,iostat=ierr1) tags(1:nints) read(iunit,iostat=ierr2) intarr(1:nints) if (ierr1 /= 0 .or. ierr2 /= 0) then print "(a)",'error reading integer header' ierr = 1 return endif if (debug) print*,'DEBUG: got tags = ',tags(1:nints) call extract('nblocks',nblocks,intarr,tags,nints,ierr) if (ierr /= 0) return call extract('nparttot',npart,intarr,tags,nints,ierr) if (ierr /= 0) return if (phantomdump) then call extract('ntypes',ntypes,intarr,tags,nints,ierr) if (ierr /= 0) return call extract('npartoftype',npartoftypei(1:ntypes),intarr,tags,nints,ierr) if (ierr /= 0) return endif if (phantomdump .and. nints < 7) ntypes = nints - 1 if (iverbose.ge.1) print *,'npart = ',npart,' MPI blocks = ',nblocks if (phantomdump) then n1 = npartoftypei(1) else call extract('n1',n1,intarr,tags,nints,ierr) endif else if (nints.lt.3) then if (.not.phantomdump) print "(a)",'WARNING: npart,n1,n2 NOT IN HEADER??' read(iunit,iostat=ierr) npart npartoftypei(1) = npart elseif (phantomdump) then if (nints.lt.7) then ntypes = nints - 1 read(iunit,iostat=ierr) npart,npartoftypei(1:ntypes) else ntypes = 5 read(iunit,iostat=ierr) npart,npartoftypei(1:5),nblocks endif if (debug) then print*,'DEBUG: ntypes = ',ntypes,' npartoftype = ',npartoftypei(:) endif n1 = npartoftypei(1) n2 = 0 elseif (nints.ge.7) then read(iunit,iostat=ierr) npart,n1,n2,nreassign,naccrete,nkill,nblocks else print "(a)",'warning: nblocks not read from file (assuming non-MPI dump)' read(iunit,iostat=ierr) npart,n1,n2 endif if (ierr /=0) then print "(a)",'error reading npart,n1,n2 and/or number of MPI blocks' return elseif (nblocks.gt.2000) then print *,'npart = ',npart,' MPI blocks = ',nblocks nblocks = 1 print*,' corrupt number of MPI blocks, assuming 1 ' else if (iverbose.ge.1) print *,'npart = ',npart,' MPI blocks = ',nblocks endif endif endif if (ntypes > maxparttypes) then print "(a,i2)",' WARNING: number of particle types exceeds array limits: ignoring types > ',maxparttypes ntypes = maxparttypes endif !--int*1, int*2, int*4, int*8 ierr1 = 0 ierr2 = 0 do i=1,4 read(iunit,iostat=ierr) ninttypes if (ninttypes > 0) then if (tagged) read(iunit,iostat=ierr1) read(iunit,iostat=ierr2) endif if (ierr /= 0 .or. ierr1 /= 0 .or. ierr2 /= 0) then print "(a)",'error skipping int types' return endif enddo !--default reals read(iunit,iostat=ierr) nreals if (ierr /=0) then print "(a)",'error reading default reals' return else if (nreals > maxinblock) then print*,'WARNING: number of reals in header exceeds splash array limit, ignoring some' nreals = maxinblock endif if (tagged) read(iunit,iostat=ierr) tagsreal(1:nreals) if (doubleprec) then read(iunit,iostat=ierr) real8arr(1:nreals) realarr(1:nreals) = real(real8arr(1:nreals)) else read(iunit,iostat=ierr) real4arr(1:nreals) realarr(1:nreals) = real(real4arr(1:nreals)) endif if (.not.tagged) call fake_header_tags(nreals,realarr,tagsreal) endif !--real*4, real*8 read(iunit,iostat=ierr) nreal4s if (nreal4s > 0) then if (tagged) read(iunit,iostat=ierr1) read(iunit,iostat=ierr2) endif if (ierr /= 0 .or. ierr1 /= 0 .or. ierr2 /= 0) then print "(a)",'error skipping real*4''s in header' return endif read(iunit,iostat=ierr) nreal8s if (ierr /= 0 .or. nreal8s < 0) then print "(a)",'error reading nreal8s' return endif ! print "(a,i3)",' ndoubles = ',nreal8s if (iverbose.ge.1) print "(4(a,i3),a)",' header contains ',nints,' ints, ',& nreals,' reals,',nreal4s,' real4s, ',nreal8s,' doubles' if (tagged) then if (nreal8s > maxinblock) then print*,'WARNING: number of real8''s in header exceeds splash array limit, ignoring some' nreal8s = maxinblock endif read(iunit,iostat=ierr) tags(1:nreal8s) read(iunit,iostat=ierr) real8arr(1:nreal8s) call extract('udist',udist,real8arr,tags,nreal8s,ierrs(1)) call extract('umass',umass,real8arr,tags,nreal8s,ierrs(2)) call extract('utime',utime,real8arr,tags,nreal8s,ierrs(3)) call extract('umagfd',umagfd,real8arr,tags,nreal8s,ierrs(4)) if (any(ierrs /= 0)) then print "(a)",' *** error reading units' endif ! extract the number of dust arrays are in the file if (onefluid_dust) ndusttypes = extract_ndusttypes(tags,tagsreal,intarr,nints) else if (nreal8s.ge.4) then read(iunit,iostat=ierr) udist,umass,utime,umagfd elseif (nreal8s.ge.3) then read(iunit,iostat=ierr) udist,umass,utime umagfd = 1.0 else print "(a)",'*** WARNING: units not found in file' udist = 1.0 umass = 1.0 utime = 1.0 umagfd = 1.0 endif endif if (ierr /= 0) then print "(a)",'*** error reading units' endif ! !--Total number of array blocks in the file ! read(iunit,iostat=ierr) narrsizes if (ierr /= 0) return if (debug) print*,' nblocks(total)=',narrsizes narrsizes = narrsizes/nblocks if (ierr /= 0) then print "(a)",'*** error reading number of array sizes ***' close(iunit) return elseif (narrsizes.gt.maxarrsizes) then narrsizes = maxarrsizes print "(a,i2)",'WARNING: too many array sizes: reading only ',narrsizes endif if (narrsizes.ge.4 .and. nreal8s.lt.4) then print "(a)",' WARNING: could not read magnetic units from dump file' endif if (debug) print*,' number of array sizes = ',narrsizes end subroutine read_header !---------------------------------------------------------------------- ! Read the header to each array block !---------------------------------------------------------------------- subroutine read_block_header(iunit,iblock,iarr,iverbose,debug,& isize,nint,nint1,nint2,nint4,nint8,nreal,nreal4,nreal8,& ntotblock,npart,ntotal,nptmasstot,ncolstep,ierr) integer, intent(in) :: iunit,iblock,iarr,iverbose logical, intent(in) :: debug integer*8, intent(out) :: isize(:) integer, intent(out) :: nint,nint1,nint2,nint4,nint8,nreal,nreal4,nreal8,ierr integer, intent(inout) :: ntotblock,npart,ntotal,nptmasstot,ncolstep read(iunit,iostat=ierr) isize(iarr),nint,nint1,nint2,nint4,nint8,nreal,nreal4,nreal8 if (iarr.eq.1) then ntotblock = isize(iarr) if (npart.le.0) npart = ntotblock ntotal = ntotal + ntotblock elseif (iarr.eq.2) then nptmasstot = nptmasstot + isize(iarr) endif if (debug) print*,'DEBUG: array size ',iarr,' size = ',isize(iarr) if (isize(iarr).gt.0 .and. iblock.eq.1) then if (iverbose.ge.1) print "(1x,a,i1,a,i12,a,5(i2,1x),a,3(i2,1x))", & 'block ',iarr,' dim = ',isize(iarr),' nint =',nint,nint1,nint2,nint4,nint8,& 'nreal =',nreal,nreal4,nreal8 endif !--we are going to read all real arrays but need to convert them all to default real if (iarr.ne.2 .and. isize(iarr).eq.isize(1) .and. iblock.eq.1) then ncolstep = ncolstep + nreal + nreal4 + nreal8 endif end subroutine read_block_header !---------------------------------------------------------------------- ! Extract and print relevant variables from the header block !---------------------------------------------------------------------- subroutine extract_variables_from_header(tags,realarr,nreals,iverbose,debug,& gotbinary,nblocks,nptmasstot,npartoftypei,ntypes,& time,gamma,hfact,npart,ntotal,npartoftype,massoftype,dat,ix,ih,ipmass,ivx) character(len=lentag), intent(in) :: tags(maxinblock) real, intent(in) :: realarr(maxinblock) integer, intent(in) :: nreals,iverbose,nblocks,nptmasstot,npartoftypei(:),ntypes integer, intent(in) :: ix(3),ih,ipmass,ivx real, intent(out) :: time,gamma,hfact,massoftype(:) real, intent(inout) :: dat(:,:) integer, intent(out) :: npartoftype(:) integer, intent(inout) :: npart,ntotal logical, intent(in) :: debug logical, intent(out) :: gotbinary real :: rhozero,tfreefall,tff,radL1,PhiL1,Er,RK2,dtmax,tolh real :: massoftypei(ntypes) integer :: i,ierrs(10) integer :: itype real, parameter :: pi=3.141592653589 if (phantomdump) then call extract('time',time,realarr,tags,nreals,ierrs(1)) else call extract('gt',time,realarr,tags,nreals,ierrs(1)) endif call extract('gamma',gamma,realarr,tags,nreals,ierrs(2)) call extract('rhozero',rhozero,realarr,tags,nreals,ierrs(3)) !--extract required information from the first block header if (rhozero.gt.0.) then tfreefall = SQRT((3. * pi) / (32. * rhozero)) tff = time/tfreefall else tfreefall = 0. tff = 0. endif if (phantomdump) then call extract('massoftype',massoftypei(1:ntypes),realarr,tags,nreals,ierrs(4)) npartoftype(:) = 0 do i=1,ntypes !--map from phantom types to splash types itype = itypemap_phantom(int(i,kind=1)) if (debug) print*,'DEBUG: npart of type ',itype,' += ',npartoftypei(i) npartoftype(itype) = npartoftype(itype) + npartoftypei(i) massoftype(itype) = massoftypei(i) enddo npartoftype(itypemap_sink_phantom) = nptmasstot ! sink particles if (nblocks.gt.1) then print "(a)",' setting ngas=npart for MPI code ' npartoftype(1) = npart npartoftype(2:) = 0 endif ! !--if Phantom calculation uses the binary potential ! then read this as two point mass particles ! if (any(tags(1:nreals)=='x1')) then gotbinary = .true. npartoftype(itypemap_sink_phantom) = npartoftype(itypemap_sink_phantom) + 2 ntotal = ntotal + 2 call extract('x1',dat(npart+1,ix(1)),realarr,tags,nreals,ierrs(1)) call extract('y1',dat(npart+1,ix(2)),realarr,tags,nreals,ierrs(2)) call extract('z1',dat(npart+1,ix(3)),realarr,tags,nreals,ierrs(3)) if (ipmass.gt.0) call extract('m1',dat(npart+1,ipmass),realarr,tags,nreals,ierrs(4)) call extract('h1',dat(npart+1,ih),realarr,tags,nreals,ierrs(4)) if (debug) print *,npart+1,npart+2 if (iverbose.ge.1) print *,'binary position: primary: ',dat(npart+1,ix(1):ix(3)) call extract('x2',dat(npart+2,ix(1)),realarr,tags,nreals,ierrs(1)) call extract('y2',dat(npart+2,ix(2)),realarr,tags,nreals,ierrs(2)) call extract('z2',dat(npart+2,ix(3)),realarr,tags,nreals,ierrs(3)) if (ipmass.gt.0) call extract('m2',dat(npart+2,ipmass),realarr,tags,nreals,ierrs(4)) call extract('h2',dat(npart+2,ih),realarr,tags,nreals,ierrs(5)) if (iverbose.ge.1 .and. ipmass > 0 .and. ih > 0) then print *,' secondary: ',dat(npart+2,ix(1):ix(3)) print *,' m1: ',dat(npart+1,ipmass),' m2:',dat(npart+2,ipmass),& ' h1: ',dat(npart+2,ipmass),' h2:',dat(npart+2,ih) endif if (ivx.gt.0) then call extract('vx1',dat(npart+1,ivx),realarr,tags,nreals,ierrs(1)) call extract('vy1',dat(npart+1,ivx+1),realarr,tags,nreals,ierrs(2)) call extract('vz1',dat(npart+1,ivx+2),realarr,tags,nreals,ierrs(3)) call extract('vx2',dat(npart+2,ivx),realarr,tags,nreals,ierrs(4)) call extract('vy2',dat(npart+2,ivx+1),realarr,tags,nreals,ierrs(5)) call extract('vz2',dat(npart+2,ivx+2),realarr,tags,nreals,ierrs(6)) endif npart = npart + 2 endif else npartoftype(:) = 0 npartoftype(1) = npart npartoftype(2) = max(ntotal - npart,0) endif hfact = 1.2 if (phantomdump) then call extract('hfact',hfact,realarr,tags,nreals,ierrs(1)) call extract('tolh',tolh,realarr,tags,nreals,ierrs(2)) print "(a,es12.4,a,f6.3,a,f5.2,a,es8.1)", & ' time = ',time,' gamma = ',gamma, & ' hfact = ',hfact,' tolh = ',tolh elseif (batcode) then call extract('radL1',radL1,realarr,tags,nreals,ierrs(1)) call extract('PhiL1',PhiL1,realarr,tags,nreals,ierrs(2)) call extract('Er',Er,realarr,tags,nreals,ierrs(3)) print "(a,es12.4,a,f9.5,a,f8.4,/,a,es12.4,a,es9.2,a,es10.2)", & ' time: ',time, ' gamma: ',gamma, ' tsph: ',realarr(2), & ' radL1: ',radL1,' PhiL1: ',PhiL1,' Er: ',Er else call extract('RK2',RK2,realarr,tags,nreals,ierrs(1)) call extract('dtmax',dtmax,realarr,tags,nreals,ierrs(2)) print "(a,es12.4,a,f9.5,a,f8.4,/,a,es12.4,a,es9.2,a,es10.2)", & ' time: ',time, ' gamma: ',gamma, ' RK2: ',RK2, & ' t/t_ff: ',tff,' rhozero: ',rhozero,' dtmax: ',dtmax endif end subroutine extract_variables_from_header !--------------------------------------------------------------- ! old subroutine for guessing labels in non-tagged sphNG format !--------------------------------------------------------------- subroutine guess_labels(ncolumns,iamvec,label,labelvec,istartmhd,istart_extra_real4,nmhd,nhydroreal4, & ndimV,irho,iBfirst,ivx,iutherm,idivB,iJfirst,iradenergy,icv,& udist,utime,units,unitslabel) use geometry, only:labelcoord integer, intent(in) :: ncolumns,istartmhd,istart_extra_real4,nmhd,nhydroreal4,ndimV,irho integer, intent(out) :: iBfirst,ivx,iutherm,idivB,iJfirst,iradenergy,icv integer, intent(inout) :: iamvec(:) character(len=*), intent(inout) :: label(:),labelvec(:),unitslabel(:) real(doub_prec), intent(in) :: udist,utime real, intent(inout) :: units(:) integer :: i real(doub_prec) :: uergg !--the following only for mhd small dumps or full dumps if (ncolumns.ge.7) then if (mhddump) then iBfirst = irho+1 if (.not.smalldump) then ivx = iBfirst+ndimV iutherm = ivx+ndimV if (phantomdump) then !--phantom MHD full dumps if (nmhd.ge.4) then iamvec(istartmhd:istartmhd+ndimV-1) = istartmhd labelvec(istartmhd:istartmhd+ndimV-1) = 'A' do i=1,ndimV label(istartmhd+i-1) = trim(labelvec(istartmhd))//'\d'//labelcoord(i,1) enddo if (nmhd.ge.7) then label(istartmhd+3) = 'Euler beta\dx' label(istartmhd+4) = 'Euler beta\dy' label(istartmhd+5) = 'Euler beta\dz' idivB = istartmhd+2*ndimV else idivB = istartmhd+ndimV endif elseif (nmhd.ge.3) then label(istartmhd) = 'Euler alpha' label(istartmhd+1) = 'Euler beta' idivB = istartmhd + 2 elseif (nmhd.ge.2) then label(istartmhd) = 'Psi' idivB = istartmhd + 1 elseif (nmhd.ge.1) then idivB = istartmhd endif iJfirst = 0 if (ncolumns.ge.idivB+1) then label(idivB+1) = 'alpha\dB\u' endif else !--sphNG MHD full dumps label(iutherm+1) = 'grad h' label(iutherm+2) = 'grad soft' label(iutherm+3) = 'alpha' if (nmhd.ge.7 .and. usingvecp) then iamvec(istartmhd:istartmhd+ndimV-1) = istartmhd labelvec(istartmhd:istartmhd+ndimV-1) = 'A' do i=1,ndimV label(istartmhd+i-1) = trim(labelvec(16))//'\d'//labelcoord(i,1) enddo idivB = istartmhd+ndimV elseif (nmhd.ge.6 .and. usingeulr) then label(istartmhd) = 'Euler alpha' label(istartmhd+1) = 'Euler beta' idivB = istartmhd + 2 elseif (nmhd.ge.6) then label(istartmhd) = 'psi' idivB = istartmhd + 1 if (nmhd.ge.8) then label(istartmhd+2+ndimV+1) = '\eta_{real}' label(istartmhd+2+ndimV+2) = '\eta_{art}' units(istartmhd+2+ndimV+1:istartmhd+2+ndimV+2) = udist*udist/utime unitslabel(istartmhd+2+ndimV+1:istartmhd+2+ndimV+2) = ' [cm\u2\d/s]' endif if (nmhd.ge.14) then label(istartmhd+2+ndimV+3) = 'fsym\dx' label(istartmhd+2+ndimV+4) = 'fsym\dy' label(istartmhd+2+ndimV+5) = 'fsym\dz' labelvec(istartmhd+ndimV+5:istartmhd+ndimV+7) = 'fsym' iamvec(istartmhd+ndimV+5:istartmhd+ndimV+7) = istartmhd+ndimV+5 label(istartmhd+2+ndimV+6) = 'faniso\dx' label(istartmhd+2+ndimV+7) = 'faniso\dy' label(istartmhd+2+ndimV+8) = 'faniso\dz' labelvec(istartmhd+ndimV+8:istartmhd+ndimV+10) = 'faniso' iamvec(istartmhd+ndimV+8:istartmhd+ndimV+10) = istartmhd+ndimV+8 endif elseif (nmhd.ge.1) then idivB = istartmhd endif iJfirst = idivB + 1 if (ncolumns.ge.iJfirst+ndimV) then label(iJfirst+ndimV) = 'alpha\dB\u' endif endif else ! mhd small dump if (nhydroreal4.ge.3) iutherm = iBfirst+ndimV endif elseif (.not.smalldump) then ! pure hydro full dump ivx = irho+1 iutherm = ivx + ndimV if (phantomdump) then if (istart_extra_real4.gt.0 .and. istart_extra_real4.lt.100) then label(istart_extra_real4) = 'alpha' label(istart_extra_real4+1) = 'alphau' endif else if (istart_extra_real4.gt.0 .and. istart_extra_real4.lt.100) then label(istart_extra_real4) = 'grad h' label(istart_extra_real4+1) = 'grad soft' label(istart_extra_real4+2) = 'alpha' endif endif endif if (phantomdump .and. h2chem) then if (smalldump) then label(nhydroarrays+nmhdarrays+1) = 'H_2 ratio' elseif (.not.smalldump .and. iutherm.gt.0) then label(iutherm+1) = 'H_2 ratio' label(iutherm+2) = 'HI abundance' label(iutherm+3) = 'proton abundance' label(iutherm+4) = 'e^- abundance' label(iutherm+5) = 'CO abundance' endif endif if (istartrt.gt.0 .and. istartrt.le.ncolumns .and. rtdump) then ! radiative transfer dump iradenergy = istartrt label(iradenergy) = 'radiation energy' uergg = (udist/utime)**2 units(iradenergy) = uergg if (smalldump) then icv = istartrt+1 else label(istartrt+1) = 'opacity' units(istartrt+1) = udist**2/umass icv = istartrt+2 label(istartrt+3) = 'lambda' units(istartrt+3) = 1.0 label(istartrt+4) = 'eddington factor' units(istartrt+4) = 1.0 endif if (icv.gt.0) then label(icv) = 'u/T' units(icv) = uergg endif else iradenergy = 0 icv = 0 endif endif end subroutine guess_labels integer function assign_column(tag,iarr,ipos,ikind,imaxcolumnread,idustarr) result(icolumn) use labels, only:ih,irho,ix,ipmass character(len=lentag), intent(in) :: tag integer, intent(in) :: iarr,ipos,ikind integer, intent(inout) :: imaxcolumnread integer, intent(inout) :: idustarr if (tagged .and. len_trim(tag) > 0) then ! ! use the tags to put certain arrays in an assigned place ! no matter what type is used for the variable in the file ! and no matter what order they appear in the dump file ! select case(trim(tag)) case('x') icolumn = ix(1) case('y') icolumn = ix(2) case('z') icolumn = ix(3) case('m') icolumn = ipmass case('h') icolumn = ih case('rho') icolumn = irho case('dustfracsum') idustarr = idustarr + 1 icolumn = nhydroarrays + idustarr case('dustfrac') if (ndustarrays > 0) then idustarr = idustarr + 1 icolumn = nhydroarrays + idustarr else icolumn = max(nhydroarrays + ndustarrays + nmhdarrays + 1,imaxcolumnread + 1) endif case('Bx') icolumn = nhydroarrays + ndustarrays + 1 case('By') icolumn = nhydroarrays + ndustarrays + 2 case('Bz') icolumn = nhydroarrays + ndustarrays + 3 case default icolumn = max(nhydroarrays + ndustarrays + nmhdarrays + 1,imaxcolumnread + 1) if (iarr==1) then if (ikind==4) then ! real*4 array istart_extra_real4 = min(istart_extra_real4,icolumn) if (debug) print*,' istart_extra_real4 = ',istart_extra_real4 endif endif end select else ! ! this is old code handling the non-tagged format where ! particular arrays are assumed to be in particular places ! if (ikind==6) then ! default reals if (iarr.eq.1.and.((phantomdump.and.ipos.eq.4) & .or.(.not.phantomdump.and.ipos.eq.6))) then ! read x,y,z,m,h and then place arrays after always-present ones ! (for phantom read x,y,z only) icolumn = nhydroarrays+nmhdarrays + 1 elseif (.not.phantomdump .and. (iarr.eq.4 .and. ipos.le.3)) then icolumn = nhydroarrays + ipos else icolumn = imaxcolumnread + 1 endif elseif (ikind==4) then ! real*4s if (phantomdump) then if (iarr.eq.1 .and. ipos.eq.1) then icolumn = ih ! h is always first real4 in phantom dumps !!--density depends on h being read !required(ih) = .true. elseif (iarr.eq.4 .and. ipos.le.3) then icolumn = nhydroarrays + ipos else icolumn = max(nhydroarrays+nmhdarrays + 1,imaxcolumnread + 1) if (iarr.eq.1) then istart_extra_real4 = min(istart_extra_real4,icolumn) if (debug) print*,' istart_extra_real4 = ',istart_extra_real4 endif endif else if (iarr.eq.1 .and. ipos.eq.1) then icolumn = irho ! density elseif (iarr.eq.1 .and. smalldump .and. ipos.eq.2) then icolumn = ih ! h which is real4 in small dumps !--this was a bug for sphNG files... !elseif (iarr.eq.4 .and. i.le.3) then ! icolumn = nhydroarrays + ipos else icolumn = max(nhydroarrays+nmhdarrays + 1,imaxcolumnread + 1) if (iarr.eq.1) then istart_extra_real4 = min(istart_extra_real4,icolumn) if (debug) print*,' istart_extra_real4 = ',istart_extra_real4 endif endif endif else ! used for untagged format with real*8's icolumn = imaxcolumnread + 1 endif endif imaxcolumnread = max(imaxcolumnread,icolumn) end function assign_column !--------------------------------------------------------------- ! function to extract the number of dust arrays !--------------------------------------------------------------- integer function extract_ndusttypes(tags,tagsreal,intarr,nints) result(ndusttypes) character(len=lentag), intent(in) :: tags(maxinblock),tagsreal(maxinblock) integer, intent(in) :: intarr(:),nints integer :: i,idust,ierr logical :: igotndusttypes = .false. ! Look for ndusttypes in the header do i = 1,maxinblock if (tags(i)=='ndusttypes') then igotndusttypes = .true. endif enddo ! Retreive/guess the value of ndusttypes if (igotndusttypes) then call extract('ndusttypes',idust,intarr,tags,nints,ierr) else ! For older files where ndusttypes is not output to the header idust = 0 do i = 1,maxinblock if (tagsreal(i)=='grainsize') idust = idust + 1 enddo write(*,"(a)") ' Warning! Could not find ndusttypes in header' write(*,"(a,I4)") ' ...counting grainsize arrays...ndusttypes =',idust endif ndusttypes = idust end function extract_ndusttypes end module sphNGread !---------------------------------------------------------------------- ! Main read_data routine for splash !---------------------------------------------------------------------- subroutine read_data(rootname,indexstart,iposn,nstepsread) use particle_data, only:dat,gamma,time,iamtype,npartoftype,maxpart,maxstep,maxcol,masstype !use params, only:int1,int8 use settings_data, only:ndim,ndimV,ncolumns,ncalc,required,ipartialread,& lowmemorymode,ntypes,iverbose,ndusttypes use mem_allocation, only:alloc use system_utils, only:lenvironment,renvironment use labels, only:ipmass,irho,ih,ix,ivx,labeltype,print_types use calcquantities, only:calc_quantities use sphNGread implicit none integer, intent(in) :: indexstart,iposn integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,k,ierr,iunit integer :: intg1,int2,int3,ilocvx,iversion integer :: i1,iarr,i2,iptmass1,iptmass2 integer :: npart_max,nstep_max,ncolstep,icolumn,idustarr,nptmasstot integer :: narrsizes integer :: nskip,ntotal,npart,n1,ngas,nreals integer :: iblock,nblocks,ntotblock,ncolcopy integer :: ipos,nptmass,nptmassi,nstar,nunknown,ilastrequired integer :: imaxcolumnread,nhydroarraysinfile,nremoved integer :: itype,iphaseminthistype,iphasemaxthistype,nthistype,iloc integer, dimension(maxparttypes) :: npartoftypei real, dimension(maxparttypes) :: massoftypei real :: pmassi,hi,rhoi,hrlim,rad2d logical :: iexist, doubleprec,imadepmasscolumn,gotbinary,gotiphase character(len=len(rootname)+10) :: dumpfile character(len=100) :: fileident integer*8, dimension(maxarrsizes) :: isize integer, dimension(maxarrsizes) :: nint,nint1,nint2,nint4,nint8,nreal,nreal4,nreal8 integer*1, dimension(:), allocatable :: iphase integer, dimension(:), allocatable :: listpm real(doub_prec), dimension(:), allocatable :: dattemp real*4, dimension(:), allocatable :: dattempsingle real(doub_prec) :: r8 real(sing_prec) :: r4 real, dimension(:,:), allocatable :: dattemp2 real, dimension(maxinblock) :: dummyreal real :: hfact,omega logical :: skip_corrupted_block_3 character(len=lentag) :: tagsreal(maxinblock), tagtmp integer, parameter :: splash_max_iversion = 1 nstepsread = 0 nstep_max = 0 npart_max = maxpart npart = 0 iunit = 15 ipmass = 4 idivvcol = 0 icurlvxcol = 0 icurlvycol = 0 icurlvzcol = 0 nhydroreal4 = 0 umass = 1.d0 utime = 1.d0 udist = 1.d0 umagfd = 1.d0 istartmhd = 0 istartrt = 0 istart_extra_real4 = 100 nmhd = 0 igotmass = .false. tfreefall = 1.d0 gotbinary = .false. gotiphase = .false. skip_corrupted_block_3 = .false. dumpfile = trim(rootname) ! !--check if data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 j = indexstart nstepsread = 0 doubleprec = .true. ilastrequired = 0 do i=1,size(required)-1 if (required(i)) ilastrequired = i enddo if (iverbose.ge.1) print "(1x,a)",'reading sphNG format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) debug = lenvironment('SSPLASH_DEBUG') if (debug) iverbose = 1 ! !--open the (unformatted) binary file ! open(unit=iunit,iostat=ierr,file=dumpfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return else ! !--read header key to work out precision ! doubleprec = .true. read(iunit,iostat=ierr) intg1,r8,int2,iversion,int3 if (intg1.ne.690706 .and. intg1.ne.060769) then print "(a)",'*** ERROR READING HEADER: corrupt file/zero size/wrong endian?' close(iunit) return endif if (int2.ne.780806 .and. int2.ne.060878) then print "(a)",' single precision dump' rewind(iunit) read(iunit,iostat=ierr) intg1,r4,int2,iversion,int3 if (int2.ne.780806 .and. int2.ne.060878) then print "(a)",'ERROR determining single/double precision in file header' endif doubleprec = .false. elseif (int3.ne.690706) then print*,' got ',intg1,r4,int2,iversion,int3 print "(a)",'*** WARNING: default int appears to be int*8: not implemented' else if (debug) print "(a)",' double precision dump' ! no need to print this endif if (iversion==690706) then ! handle old-format files (without version number) gracefully iversion = 0 endif endif if (iversion > splash_max_iversion) then print "(/a,i2,/,a,i2)",& ' *** WARNING: this copy of splash can only read version ',splash_max_iversion, & ' but the file format version is ',iversion if (.not.lenvironment('SSPLASH_IGNORE_IVERSION')) then print "(2(/,a))",' ** press any key to bravely proceed anyway ** ', & ' (set SSPLASH_IGNORE_IVERSION=yes to silence this warning)' read* endif endif ! !--read file ID ! read(iunit,iostat=ierr) fileident if (ierr /=0) then print "(a)",'*** ERROR READING FILE ID ***' close(iunit) return else print "(a)",' File ID: '//trim(fileident) endif mhddump = .false. rtdump = .false. call get_options_from_fileident(fileident,smalldump,tagged,phantomdump,& usingvecp,usingeulr,cleaning,h2chem,onefluid_dust,rt_in_header,batcode) if (tagged .and. iversion < 1) print "(a)",'ERROR: got tagged format but iversion is ',iversion ! !--read variables from header ! call read_header(iunit,iverbose,debug,doubleprec, & npart,npartoftypei,n1,ntypes,nblocks,narrsizes,dummyreal,tagsreal,nreals,ierr) if (ierr /= 0) then print "(a)",' *** ERROR READING HEADER ***' close(iunit) return endif ! !--Attempt to read all MPI blocks ! ntotal = 0 ntotblock = 0 nptmasstot = 0 i2 = 0 iptmass2 = 0 igotmass = .true. imadepmasscolumn = .false. massoftypei(:) = 0. over_MPIblocks: do iblock=1,nblocks ! !--read array header from this block ! if (iblock.eq.1) ncolstep = 0 do iarr=1,narrsizes call read_block_header(iunit,iblock,iarr,iverbose,debug, & isize,nint(iarr),nint1(iarr),nint2(iarr),nint4(iarr),nint8(iarr),& nreal(iarr),nreal4(iarr),nreal8(iarr),& ntotblock,npart,ntotal,nptmasstot,ncolstep,ierr) if (ierr /= 0) then print "(a)",' *** ERROR READING ARRAY SIZES ***' close(iunit) return endif enddo if (debug) print*,'DEBUG: ncolstep=',ncolstep,' from file header, also nptmasstot = ',nptmasstot ! !--this is a bug fix for a corrupt version of wdump outputting bad ! small dump files ! if (smalldump .and. nreal(1).eq.5 .and. iblock.eq.1 .and. lenvironment('SSPLASH_FIX_CORRUPT')) then print*,'FIXING CORRUPT HEADER ON SMALL DUMPS: assuming nreal=3 not 5' nreal(1) = 3 ncolstep = ncolstep - 2 endif npart_max = maxval(isize(1:narrsizes)) npart_max = max(npart_max,npart+nptmasstot,ntotal) ! !--work out from array header how many columns we are going to read ! in order to allocate memory ! if (iblock.eq.1) then igotmass = .true. if (smalldump .or. phantomdump) then if (phantomdump) then if (tagged) then call extract('massoftype',massoftypei(1:ntypes),dummyreal,tagsreal,nreals,ierr) else ! old phantom dumps had only 5 types call extract('massoftype',massoftypei(1:5),dummyreal,tagsreal,nreals,ierr) endif else call extract('pmassinitial',massoftypei(1),dummyreal,tagsreal,nreals,ierr) if (ierr /= 0) then print "(a)",' error extracting particle mass from small dump file' massoftypei(1) = 0. igotmass = .false. endif endif if (debug) print*,'DEBUG: got massoftype(gas) = ',massoftypei(1) if (any(massoftypei(1:ntypes).gt.tiny(0.)) .and. .not.lowmemorymode) then ncolstep = ncolstep + 1 ! make an extra column to contain particle mass imadepmasscolumn = .true. elseif (lowmemorymode) then igotmass = .false. else igotmass = .false. endif if (all(abs(massoftypei(1:ntypes)).lt.tiny(0.)) .and. nreal(1).lt.4) then print "(a)",' error: particle masses not present in small dump file' igotmass = .false. endif endif if (debug) print*,'DEBUG: gotmass = ',igotmass, ' ncolstep = ',ncolstep ! !-- to handle both small and full dumps, we need to place the quantities dumped ! in both small and full dumps at the start of the dat array ! quantities only in the full dump then come after ! also means that hydro/MHD are "semi-compatible" in the sense that x,y,z,m,h and rho ! are in the same place for both types of dump ! ix(1) = 1 ix(2) = 2 ix(3) = 3 if (igotmass) then ipmass = 4 ih = 5 irho = 6 nhydroarrays = 6 ! x,y,z,m,h,rho else ipmass = 0 ih = 4 irho = 5 nhydroarrays = 5 ! x,y,z,h,rho endif nhydroarraysinfile = nreal(1) + nreal4(1) + nreal8(1) nhydroreal4 = nreal4(1) if (imadepmasscolumn) nhydroarraysinfile = nhydroarraysinfile + 1 if (nhydroarraysinfile .lt.nhydroarrays .and. .not.phantomdump) then print "(a)",' ERROR: one of x,y,z,m,h or rho missing in small dump read' nhydroarrays = nreal(1)+nreal4(1)+nreal8(1) elseif (phantomdump .and. (nreal(1).lt.3 .or. nreal4(1).lt.1)) then print "(a)",' ERROR: x,y,z or h missing in phantom read' endif if (onefluid_dust) then if (ndusttypes>1) then ndustarrays = ndusttypes + 1 !--the extra column is for dustfracsum else ndustarrays = 1 endif else ndustarrays = 0 endif if (narrsizes.ge.4) then nmhdarrays = 3 ! Bx,By,Bz nmhd = nreal(4) + nreal4(4) + nreal8(4) - nmhdarrays ! how many "extra" mhd arrays if (debug) print*,'DEBUG: ',nmhd,' extra MHD arrays' else nmhdarrays = 0 endif !--radiative transfer dump? if (narrsizes.ge.3 .and. isize(3).eq.isize(1)) rtdump = .true. !--mhd dump? if (narrsizes.ge.4) mhddump = .true. if (.not.(mhddump.or.smalldump)) then ivx = nhydroarrays+1 elseif (mhddump .and. .not.smalldump) then ivx = nhydroarrays+nmhdarrays+1 else ivx = 0 endif !--need to force read of velocities e.g. for corotating frame subtraction if (any(required(ivx:ivx+ndimV-1))) required(ivx:ivx+ndimV-1) = .true. !--for phantom dumps, also make a column for density ! and divv, if a .divv file exists if (phantomdump) then ncolstep = ncolstep + 1 inquire(file=trim(dumpfile)//'.divv',exist=iexist) if (iexist) then idivvcol = ncolstep + 1 icurlvxcol = ncolstep + 2 icurlvycol = ncolstep + 3 icurlvzcol = ncolstep + 4 ncolstep = ncolstep + 4 endif endif endif ! !--allocate memory now that we know the number of columns ! if (iblock.eq.1) then ncolumns = ncolstep + ncalc if (ncolumns.gt.maxplot) then print*,'ERROR with ncolumns = ',ncolumns,' in data read' return endif ilastrequired = 0 do i=1,ncolumns if (required(i)) ilastrequired = i enddo endif if (npart_max.gt.maxpart .or. j.gt.maxstep .or. ncolumns.gt.maxcol) then if (lowmemorymode) then call alloc(max(npart_max+2,maxpart),j,ilastrequired) else call alloc(max(npart_max+2,maxpart),j,ncolumns,mixedtypes=.true.) endif endif ! !--now that memory has been allocated, copy info from the header into ! the relevant arrays ! if (iblock.eq.1) then call extract_variables_from_header(tagsreal,dummyreal,nreals,iverbose,debug, & gotbinary,nblocks,nptmasstot,npartoftypei,ntypes,& time(j),gamma(j),hfact,npart,ntotal,npartoftype(:,j),masstype(:,j), & dat(:,:,j),ix,ih,ipmass,ivx) nstepsread = nstepsread + 1 ! !--stop reading file here if no columns required ! if (ilastrequired.eq.0) exit over_MPIblocks if (allocated(iphase)) deallocate(iphase) allocate(iphase(npart_max+2)) if (phantomdump) then iphase(:) = 1 else iphase(:) = 0 endif if (gotbinary) then iphase(npart-1) = -3 iphase(npart) = -3 endif endif ! !--Arrays ! imaxcolumnread = 0 icolumn = 0 idustarr = 0 istartmhd = 0 istartrt = 0 i1 = i2 + 1 i2 = i1 + isize(1) - 1 if (debug) then print "(1x,a10,i4,3(a,i12))",'MPI block ',iblock,': particles: ',i1,' to ',i2,' of ',npart elseif (nblocks.gt.1) then if (iblock.eq.1) write(*,"(a,i1,a)",ADVANCE="no") ' reading MPI blocks: .' write(*,"('.')",ADVANCE="no") endif iptmass1 = iptmass2 + 1 iptmass2 = iptmass1 + isize(2) - 1 nptmass = nptmasstot if (nptmass.gt.0 .and. debug) print "(15x,3(a,i12))",' pt. masses: ',iptmass1,' to ',iptmass2,' of ',nptmass do iarr=1,narrsizes if (nreal(iarr) + nreal4(iarr) + nreal8(iarr).gt.0) then if (iarr.eq.4) then istartmhd = imaxcolumnread + 1 if (debug) print*,' istartmhd = ',istartmhd elseif (iarr.eq.3 .and. rtdump) then istartrt = max(nhydroarrays+nmhdarrays+1,imaxcolumnread + 1) if (debug) print*,' istartrt = ',istartrt endif endif !--read iphase from array block 1 if (iarr.eq.1) then !--skip default int nskip = nint(iarr) do i=1,nskip if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) enddo if (nint1(iarr).lt.1) then if (.not.phantomdump .or. any(npartoftypei(2:).gt.0)) then if (iverbose > 0) print "(a)",' WARNING: can''t locate iphase in dump' elseif (phantomdump) then if (iverbose > 0) print "(a)",' WARNING: can''t locate iphase in dump' endif gotiphase = .false. !--skip remaining integer arrays nskip = nint1(iarr) + nint2(iarr) + nint4(iarr) + nint8(iarr) else gotiphase = .true. if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) iphase(i1:i2) !--skip remaining integer arrays nskip = nint1(iarr) - 1 + nint2(iarr) + nint4(iarr) + nint8(iarr) endif elseif (smalldump .and. iarr.eq.2 .and. isize(iarr).gt.0 .and. .not.phantomdump) then !--read listpm from array block 2 for small dumps (needed here to extract sink masses) if (allocated(listpm)) deallocate(listpm) allocate(listpm(isize(iarr))) if (nint(iarr).lt.1) then print "(a)",'ERROR: can''t locate listpm in dump' nskip = nint(iarr) + nint1(iarr) + nint2(iarr) + nint4(iarr) + nint8(iarr) else if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) listpm(1:isize(iarr)) nskip = nint(iarr) - 1 + nint1(iarr) + nint2(iarr) + nint4(iarr) + nint8(iarr) endif else !--otherwise skip all integer arrays (not needed for plotting) nskip = nint(iarr) + nint1(iarr) + nint2(iarr) + nint4(iarr) + nint8(iarr) endif if (iarr.eq.3 .and. lenvironment('SSPLASH_BEN_HACKED')) then nskip = nskip - 1 print*,' FIXING HACKED DUMP FILE' endif !print*,'skipping ',nskip do i=1,nskip if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) enddo ! !--real arrays ! if (iarr.eq.2) then !--read sink particles from phantom dumps if (phantomdump .and. iarr.eq.2 .and. isize(iarr).gt.0) then if (nreal(iarr).lt.5) then print "(a)",'ERROR: not enough arrays written for sink particles in phantom dump' nskip = nreal(iarr) else iphase(npart+1:npart+isize(iarr)) = -3 ilocvx = nreal(iarr)-2 ! velocity is always last 3 numbers for phantom sinks if (doubleprec) then !--convert default real to single precision where necessary if (debug) print*,'DEBUG: reading sink data, converting from double precision ',isize(iarr) if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(isize(iarr)),stat=ierr) if (ierr /= 0) then print "(a)",'ERROR in memory allocation' return endif tagtmp = '' do k=1,nreal(iarr) if (tagged) read(iunit,end=33,iostat=ierr) tagtmp if (debug) print*,'DEBUG: reading sink array ',k,isize(iarr),' tag = ',trim(tagtmp) read(iunit,end=33,iostat=ierr) dattemp(1:isize(iarr)) if (ierr /= 0) print*,' ERROR during read of sink particle data, array ',k select case(k) case(1:3) iloc = ix(k) case(4) iloc = ipmass case(5) iloc = ih case default if (k >= ilocvx .and. k < ilocvx+3 .and. ivx > 0) then iloc = ivx + k-ilocvx ! put velocity into correct arrays else iloc = 0 endif end select if (iloc.gt.size(dat(1,:,j))) then; print*,' error iloc = ',iloc,ivx; stop; endif if (iloc.gt.0) then do i=1,isize(iarr) dat(npart+i,iloc,j) = real(dattemp(i)) enddo else if (debug) print*,'DEBUG: skipping sink particle array ',k endif enddo else if (debug) print*,'DEBUG: reading sink data, converting from single precision ',isize(iarr) if (allocated(dattempsingle)) deallocate(dattempsingle) allocate(dattempsingle(isize(iarr)),stat=ierr) if (ierr /= 0) then print "(a)",'ERROR in memory allocation' return endif do k=1,nreal(iarr) select case(k) case(1:3) iloc = ix(k) case(4) iloc = ipmass case(5) iloc = ih case default if (k >= ilocvx .and. k < ilocvx+3 .and. ivx > 0) then iloc = ivx + k-ilocvx ! put velocity into correct arrays else iloc = 0 endif end select if (iloc.gt.0) then if (debug) print*,'DEBUG: reading sinks into ',npart+1,'->',npart+isize(iarr),iloc if (tagged) read(iunit,end=33,iostat=ierr) !tagarr(iloc) read(iunit,end=33,iostat=ierr) dattempsingle(1:isize(iarr)) do i=1,isize(iarr) dat(npart+i,iloc,j) = real(dattempsingle(i)) enddo if (ierr /= 0) print*,' ERROR during read of sink particle data, array ',k else if (debug) print*,'DEBUG: skipping sink particle array ',k if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) endif enddo endif npart = npart + isize(iarr) endif elseif (smalldump .and. iarr.eq.2 .and. allocated(listpm)) then !--for sphNG, read sink particle masses from block 2 for small dumps if (nreal(iarr).lt.1) then if (isize(iarr).gt.0) print "(a)",'ERROR: sink masses not present in small dump' nskip = nreal(iarr) + nreal4(iarr) + nreal8(iarr) else if (doubleprec) then !--convert default real to single precision where necessary if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(isize(iarr)),stat=ierr) if (ierr /=0) print "(a)",'ERROR in memory allocation' if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) dattemp(1:isize(iarr)) if (nptmass.ne.isize(iarr)) print "(a)",'ERROR: nptmass.ne.block size' if (ipmass.gt.0) then do i=1,isize(iarr) dat(listpm(iptmass1+i-1),ipmass,j) = real(dattemp(i)) enddo else print*,'WARNING: sink particle masses not read because no mass array allocated' endif else !--convert default real to double precision where necessary if (allocated(dattempsingle)) deallocate(dattempsingle) allocate(dattempsingle(isize(iarr)),stat=ierr) if (ierr /=0) print "(a)",'ERROR in memory allocation' if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) dattempsingle(1:isize(iarr)) if (nptmass.ne.isize(iarr)) print "(a)",'ERROR: nptmass.ne.block size' if (ipmass.gt.0) then do i=1,isize(iarr) dat(listpm(iptmass1+i-1),ipmass,j) = real(dattempsingle(i)) enddo else print*,'WARNING: sink particle masses not read because no mass array allocated' endif endif nskip = nreal(iarr) - 1 + nreal4(iarr) + nreal8(iarr) endif else !--for other blocks, skip real arrays if size different nskip = nreal(iarr) + nreal4(iarr) + nreal8(iarr) endif do i=1,nskip if (tagged) read(iunit,end=33,iostat=ierr) ! skip tags read(iunit,end=33,iostat=ierr) enddo ! deallocate dattempsingle if (allocated(dattempsingle)) deallocate(dattempsingle) elseif (isize(iarr).eq.isize(1)) then ! !--read all real arrays defined on all the particles (same size arrays as block 1) ! if ((doubleprec.and.nreal(iarr).gt.0).or.nreal8(iarr).gt.0) then if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(isize(iarr)),stat=ierr) if (ierr /=0) print "(a)",'ERROR in memory allocation (read_data_sphNG: dattemp)' elseif (nreal(iarr).gt.0 .or. nreal8(iarr).gt.0) then if (allocated(dattempsingle)) deallocate(dattempsingle) allocate(dattempsingle(isize(iarr)),stat=ierr) if (ierr /=0) print "(a)",'ERROR in memory allocation (read_data_sphNG: dattempsingle)' endif ! default reals may need converting do i=1,nreal(iarr) tagtmp = '' if (tagged) read(iunit,end=33,iostat=ierr) tagtmp icolumn = assign_column(tagtmp,iarr,i,6,imaxcolumnread,idustarr) if (tagged) tagarr(icolumn) = tagtmp if (debug) print*,' reading real ',icolumn,' tag = ',trim(tagtmp) if (required(icolumn)) then if (doubleprec) then read(iunit,end=33,iostat=ierr) dattemp(1:isize(iarr)) dat(i1:i2,icolumn,j) = real(dattemp(1:isize(iarr))) else read(iunit,end=33,iostat=ierr) dattempsingle(1:isize(iarr)) dat(i1:i2,icolumn,j) = real(dattempsingle(1:isize(iarr))) endif else read(iunit,end=33,iostat=ierr) endif enddo ! ! set masses for equal mass particles (not dumped in small dump or in phantom) ! if (((smalldump.and.nreal(1).lt.ipmass).or.phantomdump).and. iarr.eq.1) then if (abs(masstype(1,j)).gt.tiny(masstype)) then icolumn = ipmass if (required(ipmass) .and. ipmass.gt.0) then if (phantomdump) then dat(i1:i2,ipmass,j) = masstype(itypemap_phantom(iphase(i1:i2)),j) else where (iphase(i1:i2).eq.0) dat(i1:i2,icolumn,j) = masstype(1,j) endif endif !--dust mass for phantom particles if (phantomdump .and. npartoftypei(itypemap_dust_phantom).gt.0 .and. ipmass.gt.0) then print*,'dust particle mass = ',masstype(itypemap_dust_phantom,j),& ' ratio m_dust/m_gas = ',masstype(itypemap_dust_phantom,j)/masstype(1,j) endif if (debug) print*,'mass ',icolumn elseif (phantomdump .and. npartoftypei(1).gt.0) then print*,' ERROR: particle mass zero in Phantom dump file!' endif endif ! ! real4 arrays (may need converting if splash is compiled in double precision) ! if (nreal4(iarr).gt.0 .and. kind(dat).eq.doub_prec) then if (allocated(dattempsingle)) deallocate(dattempsingle) allocate(dattempsingle(isize(iarr)),stat=ierr) if (ierr /=0) print "(a)",'ERROR in memory allocation (read_data_sphNG: dattempsingle)' endif if (debug) print*,'DEBUG: SIZE of dattempsingle',size(dattempsingle) ! real4s may need converting imaxcolumnread = max(imaxcolumnread,icolumn) if ((nreal(iarr)+nreal4(iarr)).gt.6) imaxcolumnread = max(imaxcolumnread,6) do i=1,nreal4(iarr) tagtmp = '' if (tagged) read(iunit,end=33,iostat=ierr) tagtmp icolumn = assign_column(tagtmp,iarr,i,4,imaxcolumnread,idustarr) if (debug) print*,'reading real4 ',icolumn,' tag = ',trim(tagtmp) if (tagged) tagarr(icolumn) = tagtmp if (phantomdump .and. icolumn==ih) required(ih) = .true. ! h always required for density if (required(icolumn)) then if (allocated(dattempsingle)) then read(iunit,end=33,iostat=ierr) dattempsingle(1:isize(iarr)) dat(i1:i2,icolumn,j) = real(dattempsingle(1:isize(iarr))) else read(iunit,end=33,iostat=ierr) dat(i1:i2,icolumn,j) endif else read(iunit,end=33,iostat=ierr) endif !--construct density for phantom dumps based on h, hfact and particle mass if (phantomdump .and. icolumn.eq.ih) then icolumn = irho ! density ! !--dead particles have -ve smoothing lengths in phantom ! so use abs(h) for these particles and hide them ! if (any(npartoftypei(2:).gt.0)) then if (.not.required(ih)) print*,'ERROR: need to read h, but required=F' !--need masses for each type if not all gas if (debug) print*,'DEBUG: phantom: setting h for multiple types ',i1,i2 if (debug) print*,'DEBUG: massoftype = ',masstype(:,j) do k=i1,i2 itype = itypemap_phantom(iphase(k)) pmassi = masstype(itype,j) hi = dat(k,ih,j) if (hi > 0.) then if (required(irho)) dat(k,irho,j) = pmassi*(hfact/hi)**3 elseif (hi < 0.) then npartoftype(itype,j) = npartoftype(itype,j) - 1 npartoftype(itypemap_unknown_phantom,j) = npartoftype(itypemap_unknown_phantom,j) + 1 if (required(irho)) dat(k,irho,j) = pmassi*(hfact/abs(hi))**3 else if (required(irho)) dat(k,irho,j) = 0. endif enddo else if (.not.required(ih)) print*,'ERROR: need to read h, but required=F' if (debug) print*,'debug: phantom: setting rho for all types' !--assume all particles are gas particles do k=i1,i2 hi = dat(k,ih,j) if (hi.gt.0.) then rhoi = massoftypei(1)*(hfact/hi)**3 elseif (hi.lt.0.) then rhoi = massoftypei(1)*(hfact/abs(hi))**3 iphase(k) = -1 else ! if h = 0. rhoi = 0. iphase(k) = -2 endif if (required(irho)) dat(k,irho,j) = rhoi enddo endif if (debug) print*,'debug: making density ',icolumn endif enddo ! real 8's need converting do i=1,nreal8(iarr) tagtmp = '' if (tagged) read(iunit,end=33,iostat=ierr) tagtmp icolumn = assign_column(tagtmp,iarr,i,8,imaxcolumnread,idustarr) if (debug) print*,'reading real8 ',icolumn,' tag = ',trim(tagtmp) if (tagged) tagarr(icolumn) = tagtmp if (required(icolumn)) then read(iunit,end=33,iostat=ierr) dattemp(1:isize(iarr)) dat(i1:i2,icolumn,j) = real(dattemp(1:isize(iarr))) else read(iunit,end=33,iostat=ierr) endif enddo endif enddo ! over array sizes enddo over_MPIblocks ! !--reached end of file (during data read) ! goto 34 33 continue print "(/,1x,a,/)",'*** WARNING: END OF FILE DURING READ ***' print*,'Press any key to continue (but there is likely something wrong with the file...)' read* 34 continue ! !--read .divv file for phantom dumps ! if (phantomdump .and. idivvcol.ne.0 .and. any(required(idivvcol:icurlvzcol))) then print "(a)",' reading divv from '//trim(dumpfile)//'.divv' open(unit=66,file=trim(dumpfile)//'.divv',form='unformatted',status='old',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR opening '//trim(dumpfile)//'.divv' else read(66,iostat=ierr) dat(1:ntotal,idivvcol,j) if (ierr /= 0) print "(a)",' WARNING: ERRORS reading divv from file' if (any(required(icurlvxcol:icurlvzcol))) then read(66,iostat=ierr) dat(1:ntotal,icurlvxcol,j) read(66,iostat=ierr) dat(1:ntotal,icurlvycol,j) read(66,iostat=ierr) dat(1:ntotal,icurlvzcol,j) endif if (ierr /= 0) print "(a)",' WARNING: ERRORS reading curlv from file' close(66) endif endif ! !--reset centre of mass to zero if environment variable "SSPLASH_RESET_CM" is set ! if (allocated(dat) .and. n1.GT.0 .and. n1 <= size(dat(:,1,1)) & .and. lenvironment('SSPLASH_RESET_CM') .and. allocated(iphase)) then call reset_centre_of_mass(dat(1:n1,1:3,j),dat(1:n1,4,j),iphase(1:n1),n1) endif ! !--remove particles at large H/R is "SSPLASH_REMOVE_LARGE_HR" is set ! if (lenvironment('SSPLASH_REMOVE_LARGE_HR')) then hrlim = renvironment('SSPLASH_HR_LIMIT') print "(a)", 'SSPLASH_REMOVE_LARGE_HR set:' print "(a)", 'Removing particles at large H/R values' print "(a,F7.4)", 'H/R limit set to ',hrlim nremoved = 0 do i = 1,npart if (int(iphase(i)) == 0) then rad2d = sqrt(dat(i,1,j)**2 + dat(i,2,j)**2) if (abs(dat(i,3,j) / rad2d) >= hrlim) then iphase(i) = -1 nremoved = nremoved + 1 endif endif enddo print "(I5,a)", nremoved, ' particles removed at large H/R' endif ! !--reset corotating frame velocities if environment variable "SSPLASH_OMEGA" is set ! if (allocated(dat) .and. n1.GT.0 .and. all(required(1:2))) then omega = renvironment('SSPLASH_OMEGAT') if (abs(omega).gt.tiny(omega) .and. ndim.ge.2) then call reset_corotating_positions(n1,dat(1:n1,1:2,j),omega,time(j)) endif if (.not. smalldump) then if (abs(omega).lt.tiny(omega)) omega = renvironment('SSPLASH_OMEGA') if (abs(omega).gt.tiny(omega) .and. ivx.gt.0) then if (.not.all(required(1:2)) .or. .not.all(required(ivx:ivx+1))) then print*,' ERROR subtracting corotating frame with partial data read' else call reset_corotating_velocities(n1,dat(1:n1,1:2,j),dat(1:n1,ivx:ivx+1,j),omega) endif endif endif endif !--set flag to indicate that only part of this file has been read if (.not.all(required(1:ncolstep))) ipartialread = .true. nptmassi = 0 nunknown = 0 ngas = 0 nstar = 0 !--can only do this loop if we have read the iphase array iphasealloc: if (allocated(iphase)) then ! !--translate iphase into particle types (mixed type storage) ! if (size(iamtype(:,j)).gt.1) then if (phantomdump) then ! !--phantom: translate iphase to splash types ! do i=1,npart itype = itypemap_phantom(iphase(i)) iamtype(i,j) = itype select case(itype) case(1,2,4) ! remove accreted particles if (ih.gt.0 .and. required(ih)) then if (dat(i,ih,j) <= 0.) then iamtype(i,j) = itypemap_unknown_phantom endif endif case(itypemap_unknown_phantom) nunknown = nunknown + 1 end select enddo else ! !--sphNG: translate iphase to splash types ! do i=1,npart itype = itypemap_sphNG(iphase(i)) iamtype(i,j) = itype select case(itype) case(1) ngas = ngas + 1 case(3) nptmassi = nptmassi + 1 case(4) nstar = nstar + 1 case default nunknown = nunknown + 1 end select enddo do i=npart+1,ntotal iamtype(i,j) = 2 enddo endif !print*,'mixed types: ngas = ',ngas,nptmassi,nunknown elseif (any(iphase(1:ntotal).ne.0)) then if (phantomdump) then print*,'ERROR: low memory mode will not work correctly with phantom + multiple types' print*,'press any key to ignore this and continue anyway (at your own risk...)' read* endif ! !--place point masses after normal particles ! if not storing the iamtype array ! print "(a)",' sorting particles by type...' nunknown = 0 do i=1,npart if (iphase(i).ne.0) nunknown = nunknown + 1 enddo ncolcopy = min(ncolstep,maxcol) allocate(dattemp2(nunknown,ncolcopy)) iphaseminthistype = 0 ! to avoid compiler warnings iphasemaxthistype = 0 do itype=1,3 nthistype = 0 ipos = 0 select case(itype) case(1) ! ptmass iphaseminthistype = 1 iphasemaxthistype = 9 case(2) ! star iphaseminthistype = 10 iphasemaxthistype = huge(iphasemaxthistype) case(3) ! unknown iphaseminthistype = -huge(iphaseminthistype) iphasemaxthistype = -1 end select do i=1,ntotal ipos = ipos + 1 if (iphase(i).ge.iphaseminthistype .and. iphase(i).le.iphasemaxthistype) then nthistype = nthistype + 1 !--save point mass information in temporary array if (nptmassi.gt.size(dattemp2(:,1))) stop 'error: ptmass array bounds exceeded in data read' dattemp2(nthistype,1:ncolcopy) = dat(i,1:ncolcopy,j) ! print*,i,' removed', dat(i,1:3,j) ipos = ipos - 1 endif !--shuffle dat array if (ipos.ne.i .and. i.lt.ntotal) then ! print*,'copying ',i+1,'->',ipos+1 dat(ipos+1,1:ncolcopy,j) = dat(i+1,1:ncolcopy,j) !--must also shuffle iphase (to be correct for other types) iphase(ipos+1) = iphase(i+1) endif enddo !--append this type to end of dat array do i=1,nthistype ipos = ipos + 1 ! print*,ipos,' appended', dattemp2(i,1:3) dat(ipos,1:ncolcopy,j) = dattemp2(i,1:ncolcopy) !--we make iphase = 1 for point masses (could save iphase and copy across but no reason to) iphase(ipos) = iphaseminthistype enddo select case(itype) case(1) nptmassi = nthistype if (nptmassi.ne.nptmass) print *,'WARNING: nptmass from iphase =',nptmassi,'not equal to nptmass =',nptmass case(2) nstar = nthistype case(3) nunknown = nthistype end select enddo endif endif iphasealloc if (allocated(dattemp)) deallocate(dattemp) if (allocated(dattempsingle)) deallocate(dattempsingle) if (allocated(dattemp2)) deallocate(dattemp2) if (allocated(iphase)) deallocate(iphase) if (allocated(listpm)) deallocate(listpm) call set_labels if (.not.phantomdump) then npartoftype(:,j) = 0 npartoftype(1,j) = npart - nptmassi - nstar - nunknown npartoftype(2,j) = ntotal - npart npartoftype(3,j) = nptmassi npartoftype(4,j) = nstar npartoftype(5,j) = nunknown else npartoftype(1,j) = npartoftype(1,j) - nunknown npartoftype(itypemap_unknown_phantom,j) = npartoftype(itypemap_unknown_phantom,j) + nunknown endif call print_types(npartoftype(:,j),labeltype) close(15) if (debug) print*,' finished data read, npart = ',npart, ntotal, npartoftype(1:ntypes,j) return contains ! !--reset centre of mass to zero ! subroutine reset_centre_of_mass(xyz,pmass,iphase,np) implicit none integer, intent(in) :: np real, dimension(np,3), intent(inout) :: xyz real, dimension(np), intent(in) :: pmass integer(kind=int1), dimension(np), intent(in) :: iphase real :: masstot,pmassi real, dimension(3) :: xcm integer :: i ! !--get centre of mass ! xcm(:) = 0. masstot = 0. do i=1,np if (iphase(i).ge.0) then pmassi = pmass(i) masstot = masstot + pmass(i) where (required(1:3)) xcm(:) = xcm(:) + pmassi*xyz(i,:) endif enddo xcm(:) = xcm(:)/masstot print*,'RESETTING CENTRE OF MASS (',pack(xcm,required(1:3)),') TO ZERO ' if (required(1)) xyz(1:np,1) = xyz(1:np,1) - xcm(1) if (required(2)) xyz(1:np,2) = xyz(1:np,2) - xcm(2) if (required(3)) xyz(1:np,3) = xyz(1:np,3) - xcm(3) return end subroutine reset_centre_of_mass subroutine reset_corotating_velocities(np,xy,velxy,omeg) implicit none integer, intent(in) :: np real, dimension(np,2), intent(in) :: xy real, dimension(np,2), intent(inout) :: velxy real, intent(in) :: omeg integer :: ip print*,'SUBTRACTING COROTATING VELOCITIES, OMEGA = ',omeg do ip=1,np velxy(ip,1) = velxy(ip,1) + xy(ip,2)*omeg enddo do ip=1,np velxy(ip,2) = velxy(ip,2) - xy(ip,1)*omeg enddo return end subroutine reset_corotating_velocities subroutine reset_corotating_positions(np,xy,omeg,t) implicit none integer, intent(in) :: np real, dimension(np,2), intent(inout) :: xy real, intent(in) :: omeg,t real :: phii,phinew,r integer :: ip print*,'SUBTRACTING COROTATING POSITIONS, OMEGA = ',omeg,' t = ',t !$omp parallel default(none) & !$omp shared(xy,np) & !$omp firstprivate(omeg,t) & !$omp private(ip,r,phii,phinew) !$omp do do ip=1,np r = sqrt(xy(ip,1)**2 + xy(ip,2)**2) phii = atan2(xy(ip,2),xy(ip,1)) phinew = phii + omeg*t xy(ip,1) = r*COS(phinew) xy(ip,2) = r*SIN(phinew) enddo !$omp end do !$omp end parallel return end subroutine reset_corotating_positions end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,unitslabel,labelzintegration,labeltype,labelvec,iamvec, & ix,ipmass,irho,ih,iutherm,ivx,iBfirst,idivB,iJfirst,icv,iradenergy,& idustfrac,ideltav,idustfracsum,ideltavsum,igrainsize,igraindens use params use settings_data, only:ndim,ndimV,ntypes,ncolumns,UseTypeInRenderings,debugmode use geometry, only:labelcoord use settings_units, only:units,unitzintegration,get_nearest_length_unit,get_nearest_time_unit use sphNGread use asciiutils, only:lcase use system_commands, only:get_environment use system_utils, only:lenvironment implicit none integer :: i,j real(doub_prec) :: unitx character(len=20) :: string,unitlabelx character(len=20) :: dustfrac_string,deltav_string if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif !--all formats read the following columns do i=1,ndim ix(i) = i enddo if (igotmass) then ipmass = 4 ! particle mass ih = 5 ! smoothing length else ipmass = 0 ih = 4 ! smoothing length endif irho = ih + 1 ! density iutherm = 0 idustfrac = 0 ! !--translate array tags into column labels, where necessary ! if (tagged) then do i=1,ncolumns label(i) = tagarr(i) select case(trim(tagarr(i))) case('m') ipmass = i case('h') ih = i case('rho') irho = i case('vx') ivx = i case('u') iutherm = i case('divv') idivvcol = i case('curlvx') icurlvxcol = i case('curlvy') icurlvycol = i case('curlvz') icurlvzcol = i case('Bx') iBfirst = i case('divB') idivB = i case('curlBx') iJfirst = i case('psi') label(i) = '\psi' case('dustfracsum') idustfracsum = i case('dustfrac') idustfrac = i case('deltavsumx') ideltavsum = i case('deltavx') ideltav = i case('alpha') label(i) = '\alpha' case('alphaB') label(i) = '\alpha_B' case('EulerAlpha') label(i) = 'Euler \alpha' case('EulerBeta') label(i) = 'Euler \beta' case('EtaReal') label(i) = '\eta_{real}' case('EtaArtificial') label(i) = '\eta_{art}' case('Erad') iradenergy = i label(i) = 'radiation energy' units(iradenergy) = (udist/utime)**2 case('opacity') label(i) = 'opacity' units(i) = udist**2/umass case('EddingtonFactor') label(i) = 'Eddington Factor' case('Cv') label(i) = 'u/T' icv = i units(icv) = (udist/utime)**2 case('h2ratio') label(i) = 'H_2 ratio' case('abH1q','abHIq') label(i) = 'HI abundance' case('abhpq') label(i) = 'proton abundance' case('abeq') label(i) = 'e^- abundance' case('abco') label(i) = 'CO abundance' case('grainsize') igrainsize = i case('graindens') igraindens = i case default if (debugmode) print "(a,i2)",' DEBUG: Unknown label '''//trim(tagarr(i))//''' in column ',i label(i) = tagarr(i) end select enddo else if (smalldump .and. nhydroreal4.ge.3) iutherm = irho+1 call guess_labels(ncolumns,iamvec,label,labelvec,istartmhd,istart_extra_real4,& nmhd,nhydroreal4,ndimV,irho,iBfirst,ivx,iutherm,idivB,iJfirst,& iradenergy,icv,udist,utime,units,unitslabel) endif label(ix(1:ndim)) = labelcoord(1:ndim,1) if (irho.gt.0) label(irho) = 'density' if (iutherm.gt.0) label(iutherm) = 'u' if (ih.gt.0) label(ih) = 'h ' if (ipmass.gt.0) label(ipmass) = 'particle mass' if (idivB.gt.0) label(idivB) = 'div B' if (idivvcol.gt.0) label(idivvcol) = 'div v' if (icurlvxcol.gt.0) label(icurlvxcol) = 'curl v_x' if (icurlvycol.gt.0) label(icurlvycol) = 'curl v_y' if (icurlvzcol.gt.0) label(icurlvzcol) = 'curl v_z' if (icurlvxcol.gt.0 .and. icurlvycol.gt.0 .and. icurlvzcol.gt.0) then iamvec(icurlvxcol:icurlvzcol) = icurlvxcol labelvec(icurlvxcol:icurlvzcol) = 'curl v' endif if (idustfracsum.gt.0) then ! Make N dustfrac labels do i = idustfracsum+1,idustfrac write(dustfrac_string,'(I10)') i-idustfracsum write(dustfrac_string,'(A)') 'dustfrac'//trim(adjustl(dustfrac_string)) label(i) = dustfrac_string enddo endif if (ideltavsum.gt.0) then ! Modify the deltavsum labels to have vector subscripts do j=1,ndimV label(ideltavsum+j-1) = 'deltavsum'//'_'//labelcoord(j,1) enddo ! Make N deltav labels with vector subscripts do i = ideltavsum+ndimV,ideltav,ndimV write(deltav_string,'(I10)') (i-ideltavsum)/ndimV write(deltav_string,'(A)') 'deltav'//trim(adjustl(deltav_string)) do j=1,ndimV label(i+j-1) = trim(deltav_string)//'_'//labelcoord(j,1) enddo enddo endif ! !--set labels for vector quantities ! if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'_'//labelcoord(i,1) enddo endif if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'_'//labelcoord(i,1) enddo endif if (iJfirst.gt.0) then iamvec(iJfirst:iJfirst+ndimV-1) = iJfirst labelvec(iJfirst:iJfirst+ndimV-1) = 'J' do i=1,ndimV label(iJfirst+i-1) = trim(labelvec(iJfirst))//'_'//labelcoord(i,1) enddo endif ! !--set units for plot data ! ! npower = int(log10(udist)) ! udist = udist/10.**npower ! udistAU = udist/1.495979e13 call get_nearest_length_unit(udist,unitx,unitlabelx) if (ndim.ge.3) then units(1:3) = unitx unitslabel(1:3) = unitlabelx endif ! do i=1,3 ! write(unitslabel(i),"('[ 10\u',i2,'\d cm]')") npower ! enddo if (ipmass.gt.0) then units(ipmass) = umass unitslabel(ipmass) = ' [g]' endif units(ih) = unitx unitslabel(ih) = unitlabelx if (ivx.gt.0) then units(ivx:ivx+ndimV-1) = udist/utime unitslabel(ivx:ivx+ndimV-1) = ' [cm/s]' endif if (ideltavsum.gt.0) then units(ideltavsum:ideltav+ndimV-1) = udist/utime unitslabel(ideltavsum:ideltav+ndimV-1) = ' [cm/s]' endif if (ideltav.gt.0) then units(ideltav:ideltav+ndimV-1) = udist/utime unitslabel(ideltav:ideltav+ndimV-1) = ' [cm/s]' endif if (iutherm.gt.0) then units(iutherm) = (udist/utime)**2 unitslabel(iutherm) = ' [erg/g]' endif units(irho) = umass/udist**3 unitslabel(irho) = ' [g/cm\u3\d]' if (iBfirst.gt.0) then units(iBfirst:iBfirst+ndimV-1) = umagfd unitslabel(iBfirst:iBfirst+ndimV-1) = ' [G]' endif if (igrainsize.gt.0) then units(igrainsize) = udist unitslabel(igrainsize) = ' [cm]' endif if (igraindens.gt.0) then units(igraindens) = umass/udist**3 unitslabel(igraindens) = ' [g/cm\u3\d]' endif !--use the following two lines for time in years call get_environment('SSPLASH_TIMEUNITS',string) select case(trim(lcase(adjustl(string)))) case('s','seconds') units(0) = utime unitslabel(0) = trim(string) case('min','minutes','mins') units(0) = utime/60.d0 unitslabel(0) = trim(string) case('h','hr','hrs','hours','hour') units(0) = utime/3600.d0 unitslabel(0) = trim(string) case('y','yr','yrs','years','year') units(0) = utime/3.1536d7 unitslabel(0) = trim(string) case('d','day','days') units(0) = utime/(3600.d0*24.d0) unitslabel(0) = trim(string) case('tff','freefall','tfreefall') !--or use these two lines for time in free-fall times units(0) = 1./tfreefall unitslabel(0) = ' ' case default call get_nearest_time_unit(utime,unitx,unitslabel(0)) units(0) = unitx ! convert to real*4 end select unitzintegration = udist labelzintegration = ' [cm]' ! !--set labels for each particle type ! if (phantomdump) then ! phantom ntypes = itypemap_unknown_phantom labeltype(1) = 'gas' labeltype(2) = 'dust' labeltype(3) = 'sink' labeltype(4) = 'ghost' labeltype(5) = 'star' labeltype(6) = 'dark matter' labeltype(7) = 'bulge' labeltype(8) = 'unknown/dead' UseTypeInRenderings(:) = .true. UseTypeInRenderings(3) = .false. if (lenvironment('SSPLASH_PLOT_DUST')) then UseTypeInRenderings(2) = .false. endif if (lenvironment('SSPLASH_PLOT_STARS')) then UseTypeInRenderings(5) = .false. endif if (lenvironment('SSPLASH_PLOT_DM')) then UseTypeInRenderings(6) = .false. endif else ntypes = 5 labeltype(1) = 'gas' labeltype(2) = 'ghost' labeltype(3) = 'sink' labeltype(4) = 'star' labeltype(5) = 'unknown/dead' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. UseTypeInRenderings(4) = .true. UseTypeInRenderings(5) = .true. ! only applies if turned on endif !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_tipsy.F90000644 000766 000000 00000034071 13261626263 017174 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING TIPSY FILES ! ! => HANDLES BOTH BINARY AND ASCII TIPSY FORMATS ! (DETECTS WHICH ONE AUTOMATICALLY) ! ! BINARY FORMAT READING REQUIRES F2003 STREAM I/O ! WHICH MAY NOT BE IMPLEMENTED ON OLDER COMPILERS ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,time,npartoftype,gamma,maxpart use params use settings_data, only:ndim,ndimV,ncolumns use mem_allocation, only:alloc use labels, only:label,ih,ipmass,irho use exact, only:hfact implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer, parameter :: iunit = 16 integer :: j,ierr integer :: nprint,ngas,ndark,nptmass,npart_max,nstep_max integer :: ncol,nread,iambinaryfile logical :: iexist character(len=len(rootname)) :: dumpfile character(len=11) :: fmt real :: timei nstepsread = 0 nstep_max = 0 npart_max = maxpart dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--determine whether file is binary or ascii and open it ! inquire(file=dumpfile,form=fmt) !print*,'fmt = ',fmt select case(trim(adjustl(fmt))) case('UNFORMATTED') iambinaryfile = 1 #ifdef __INTEL_COMPILER #if __INTEL_COMPILER<1010 !--this is how stream access is implemented for ifort 9 and lower open(unit=iunit,file=dumpfile,status='old',form='unformatted',recordtype='stream',iostat=ierr) #else open(unit=iunit,file=dumpfile,status='old',form='unformatted',access='stream',iostat=ierr) #endif #else open(unit=iunit,file=dumpfile,status='old',form='unformatted',access='stream',iostat=ierr) #endif case('FORMATTED') iambinaryfile = 0 open(unit=iunit,file=dumpfile,status='old',form='formatted',iostat=ierr) case default !--if compiler cannot distinguish the two, try ascii first, then binary iambinaryfile = -1 open(unit=iunit,file=dumpfile,status='old',form='formatted',iostat=ierr) end select if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return endif ! !--read the file header ! try ascii format first, and if unsuccessful try binary ! if (iambinaryfile.eq.1) then print "(a)",' reading binary tipsy format ' call read_tipsyheader_binary(iunit,ierr) else if (iambinaryfile.eq.0) print "(a)",' reading ascii tipsy format ' call read_tipsyheader_ascii(iunit,ierr,iambinaryfile) if (iambinaryfile.lt.0) then if (ierr.eq.0) then !--if successful ascii header read, file is ascii iambinaryfile = 0 print "(a)",' reading ascii tipsy format ' else !--otherwise, close ascii file, and assume file is binary close(unit=iunit) iambinaryfile = 1 #ifdef __INTEL_COMPILER #if __INTEL_COMPILER<1010 !--this is how stream access is implemented for ifort 9 and lower open(unit=iunit,file=dumpfile,status='old',form='unformatted',recordtype='stream',iostat=ierr) #else open(unit=iunit,file=dumpfile,status='old',form='unformatted',access='stream',iostat=ierr) #endif #else open(unit=iunit,file=dumpfile,status='old',form='unformatted',access='stream',iostat=ierr) #endif print "(a)",' reading binary tipsy format ' call read_tipsyheader_binary(iunit,ierr) endif endif endif if (ierr /= 0) then print* ndim = 0 ncolumns = 0 close(unit=iunit) return endif print "(a,f10.2,1(a,i1))",' time: ',timei,' ndim: ',ndim print "(4(a,i10))",' ntot: ',nprint,' ngas: ',ngas,' ndark: ',ndark,' nstar: ',nptmass ndimV = ndim ncol = 2*ndim + 4 ncolumns = ncol ! !--allocate memory ! if (.not.allocated(dat) .or. nprint.gt.npart_max) then npart_max = max(npart_max,nprint) call alloc(npart_max,nstep_max,ncolumns) endif ! !--now read the timestep data in the dumpfile ! dat(:,:,j) = 0. time(j) = timei nread = 0 call set_labels if (iambinaryfile.eq.1) then call read_tipsybody_binary(iunit,ierr,nread) else call read_tipsybody_ascii(iunit,ierr,nread) endif close(unit=iunit) if (nread.lt.ncol) then print "(a,i2)",' WARNING: END OF FILE: READ TO COLUMN ',nread ncolumns = nread endif ! !--often tipsy dumps contain only a (fixed) gravitational softening length ! for sph particles. In this case we need to create a sensible smoothing length ! (and warn people about the evils of using fixed softening lengths for sph particles) ! if (ngas.ge.0 .and. nread.ge.irho .and. all(abs(dat(1:ngas,ih,j)-dat(1,ih,j)).le.tiny(dat))) then print "(a)",'WARNING: fixed softening lengths detected: simulation may contain artificial fragmentation!' print "(a,f5.2,a,i1,a)",' : creating SPH smoothing lengths using h = ',hfact,'*(m/rho)**(1/',ndim,')' dat(1:ngas,ih,j) = hfact*(dat(1:ngas,ipmass,j)/(dat(1:ngas,irho,j) + tiny(dat)))**(1./ndim) endif nstepsread = nstepsread + 1 npartoftype(1,j) = ngas npartoftype(2,j) = ndark npartoftype(3,j) = nptmass gamma(j) = 1.666666666667 j = j + 1 if (allocated(npartoftype)) then print*,'>> end of dump file: nsteps =',j-1,'ntot = ',sum(npartoftype(:,j-1)) endif return contains !---------------------------------------------------- ! ascii header read !---------------------------------------------------- subroutine read_tipsyheader_ascii(iunit,ierr,iwarn) implicit none integer, intent(in) :: iunit,iwarn integer, intent(out) :: ierr read(iunit,*,end=55,iostat=ierr) nprint,ngas,nptmass read(iunit,*,end=55,iostat=ierr) ndim read(iunit,*,end=55,iostat=ierr) timei ndark = nprint - ngas - nptmass !--errors in header read if (nprint.le.0 .or. nprint.gt.1e10 .or. ndim.le.0 .or. ndim.gt.3 .or. ndark.lt.0) then if (iwarn.ge.0) print "(a)",' ERROR reading ascii file header ' ierr = 2 return endif return 55 continue if (iwarn.ge.0) print "(a)",' ERROR: end of file in ascii header read ' ierr = -1 return end subroutine read_tipsyheader_ascii !---------------------------------------------------- ! binary header read !---------------------------------------------------- subroutine read_tipsyheader_binary(iunitb,ierr) implicit none integer, intent(in) :: iunitb integer, intent(out) :: ierr real(doub_prec) :: timedb integer :: ipad ierr = 0 read(iunitb,iostat=ierr,end=55) timedb,nprint,ndim,ngas,ndark,nptmass,ipad !print*,'header = ',timedb,nprint,ndim,ngas,ndark,nptmass timei = real(timedb) !--check for wrong endianness if (ierr /= 0 .or. timedb.lt.0. .or. ndim.lt.0 .or. ndim.gt.3 & .or. nprint.le.0 .or. ngas.lt.0 .or. ndark.lt.0 .or. nptmass.lt.0 & .or. nprint.gt.1e10 .or. ngas.gt.1.e10 .or. ndark.gt.1.e10 .or. nptmass.gt.1.e8) then print "(a)",' ERROR reading binary file header: wrong endian? ' ierr = 2 endif if (ndim.eq.0) ndim = 3 return 55 continue print "(a)",' ERROR: end of file in binary header read' ierr = -1 return end subroutine read_tipsyheader_binary !---------------------------------------------------- ! ascii body read !---------------------------------------------------- subroutine read_tipsybody_ascii(iunit,ierr,nread) implicit none integer, intent(in) :: iunit integer, intent(out) :: ierr, nread integer :: i,ic,icol,nerr !--pmass,x,y,z,vx,vy,vz do ic=1,2*ndim+1 nerr = 0 nread = nread + 1 if (ic.eq.1) then ! pmass icol = ndim + 1 elseif (ic.ge.2 .and. ic.le.ndim+1) then ! x, y, z icol = ic - 1 else ! everything after icol = ic endif !print "(1x,a)",trim(label(icol)) nerr = 0 do i=1,nprint read(iunit,*,end=44,iostat=ierr) dat(i,icol,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print *,'*** WARNING: ERRORS READING '//trim(label(icol))//' ON ',nerr,' LINES' enddo !--h dark matter if (ndark.gt.0) then nerr = 0 do i=ngas+1,ngas+ndark-1 read(iunit,*,end=44,iostat=ierr) dat(i,ih,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print *,'*** WARNING: ERRORS READING DARK MATTER H ON ',nerr,' LINES' endif !--h star particles if (nptmass.gt.0) then nerr = 0 do i=ngas+ndark+1,ngas+ndark+nptmass read(iunit,*,end=44,iostat=ierr) dat(i,ih,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print *,'*** WARNING: ERRORS READING PTMASS H ON ',nerr,' LINES' endif !--density, temperature, sph smoothing length do icol=2*ndim+2,ncol nread = nread + 1 !print "(1x,a)",trim(label(icol)) do i=1,ngas read(iunit,*,end=44,iostat=ierr) dat(i,icol,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print *,'*** WARNING: ERRORS READING '//trim(label(icol))//' ON ',nerr,' LINES' enddo ierr = 0 return 44 continue ierr = -1 end subroutine read_tipsybody_ascii !---------------------------------------------------- ! binary body read !---------------------------------------------------- subroutine read_tipsybody_binary(iunitb,ierr,nread) integer, intent(in) :: iunitb integer, intent(out) :: ierr,nread integer :: i,nerr real :: dummy !--gas particles nerr = 0 do i=1,ngas !--pmass,x,y,z,vx,vy,vz,rho,temp,h read(iunitb,end=44,iostat=ierr) dat(i,ipmass,j),dat(i,1:ndim,j),dat(i,ndim+2:ncolumns,j),dummy,dummy !print*,' gas mass = ',i,dat(i,ipmass,j), ' xyz = ',dat(i,1:ndim,j) if (ierr /= 0) nerr = nerr + 1 enddo nread = ncolumns if (nerr.gt.0) print *,'*** WARNING: ERRORS READING GAS PARTICLES ON ',nerr,' LINES' !--dark matter if (ndark.gt.0) then nerr = 0 do i=ngas+1,ngas+ndark !--only read as far as velocities, then eps as smoothing length read(iunitb,end=44,iostat=ierr) dat(i,ipmass,j),dat(i,1:ndim,j),dat(i,ndim+2:2*ndim+1,j),dat(i,ih,j),dummy !print*,' DM mass = ',i,dat(i,ipmass,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print *,'*** WARNING: ERRORS READING DARK MATTER PARTICLES ON ',nerr,' LINES' endif !--star particles if (nptmass.gt.0) then nerr = 0 do i=ngas+ndark+1,ngas+ndark+nptmass !--only read as far as velocities, then eps as smoothing length read(iunitb,end=44,iostat=ierr) dat(i,ipmass,j),dat(i,1:ndim,j),dat(i,ndim+2:2*ndim+1,j),dummy,dummy,dat(i,ih,j),dummy !print*,' star mass = ',i,dat(i,ipmass,j),' xyz = ',dat(i,1:ndim,j),dat(i,ndim+2:2*ndim+1,j),crap,crap,dat(i,ih,j),crap if (ierr /= 0) nerr = nerr + 1 enddo if (nerr.gt.0) print *,'*** WARNING: ERRORS READING STAR PARTICLES ON ',nerr,' LINES' endif ierr = 0 return 44 continue ierr = -1 end subroutine read_tipsybody_binary end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,labelvec,labeltype,iamvec,& ix,ivx,ih,irho,ipmass !,iutherm use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord !use settings_units, only:units,unitslabel implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ipmass = ndim + 1 ivx = ndim + 2 irho = ivx + ndim !iutherm = irho + 1 label(irho+1) = 'temperature' ih = irho + 2 label(ix(1:ndim)) = labelcoord(1:ndim,1) label(ih) = 'h' !if (iutherm.gt.0) label(iutherm) = 'temperature' label(ipmass) = 'particle mass' label(irho) = 'density' if (ivx.ne.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//trim(labelcoord(i,1)) enddo endif ! !--set labels for each particle type ! ntypes = 3 labeltype(1) = 'gas' labeltype(2) = 'dark matter' labeltype(3) = 'star' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. UseTypeInRenderings(3) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_falcON_hdf5.f90000644 000766 000000 00000037710 13261626263 020137 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR HDF5 OUTPUT FROM W. DEHNEN'S FALCON CODE ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Columns with the 'required' flag set to false are not read !------------------------------------------------------------------------- ! ! The module below contains interface routines to c functions ! that perform the actual calls to the HDF5 libs ! !------------------------------------------------------------------------- module falcONhdf5read use params, only:maxplot,doub_prec use labels, only:lenlabel use, intrinsic :: iso_c_binding, only:c_int,c_double,c_char implicit none character(len=lenlabel), dimension(maxplot) :: blocklabel integer, parameter :: maxtypes = 6 integer :: i_current_step interface ! opens a falcON HDF5 snapshot file subroutine open_falcON_file(filename,ierr) bind(c,name="open_falcON_file") import character(c_char), intent(in) :: filename(*) integer(c_int), intent(out) :: ierr end subroutine open_falcON_file ! queries whether a file is open function falcON_file_is_open() bind(c,name="falcON_file_is_open") import integer(c_int) :: falcON_file_is_open end function falcON_file_is_open ! closes the currently open (if any) falcON HDF5 snapshot file. subroutine close_falcON_file() bind(c,name="close_falcON_file") ! no arguments end subroutine close_falcON_file ! queries if there is another snapshot present the currently open file function num_falcON_snapshots(ierr) bind(c,name="num_falcON_snapshots") import integer(c_int) :: num_falcON_snapshots integer(c_int), intent(out) :: ierr end function num_falcON_snapshots ! set falcON debugging level subroutine set_falcON_debugging_level(level) bind(c,name="set_falcON_debugging_level") import integer(c_int), intent(in) :: level end subroutine set_falcON_debugging_level ! read falcON header subroutine open_falcON_snapshot(ntype,npart,ncol,dimX,dimV,time,hper,ierr) & bind(c,name="open_falcON_snapshot") import integer(c_int), intent(out) :: ntype,ncol,dimX,dimV,ierr integer(c_int), intent(out) :: npart(*) real(c_double), intent(out) :: time,hper(3) end subroutine open_falcON_snapshot ! read falcON data subroutine read_falcON_snapshot(ierr) bind(c,name="read_falcON_snapshot") import integer(c_int), intent(out) :: ierr end subroutine read_falcON_snapshot end interface contains ! map types from falcON to splash integer function itypemap_falcON(itype) integer, intent(in) :: itype select case(itype) case(1) ! sinks itypemap_falcON = 2 case(2) ! gas itypemap_falcON = 1 case(3:4) itypemap_falcON = itype case(5:maxtypes) itypemap_falcON = itype+1 case default itypemap_falcON = 5 ! unknown end select end function itypemap_falcON ! get starting position in particle array integer function ioffset(itype,npartoftype) integer, intent(in) :: itype integer, intent(in) :: npartoftype(:) integer :: i ioffset = 0 do i=1,size(npartoftype) if (i < itype) ioffset = ioffset + npartoftype(i) enddo end function ioffset end module falcONhdf5read !------------------------------------------------------------------------- ! ! The routine that reads the data into splash's internal arrays ! !------------------------------------------------------------------------- subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol use params, only:doub_prec,maxparttypes !,maxplot use settings_data, only:ndim,ndimV,ncolumns,ncalc,ipartialread, & ntypes,debugmode,iverbose,buffer_steps_in_file use mem_allocation, only:alloc use labels, only:print_types,labeltype use system_utils, only:lenvironment use asciiutils, only:cstring use dataread_utils, only:check_range use falcONhdf5read implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile integer :: i,j,ierr,ierror(8),istep,nsteps_to_read integer :: ncolstep,npart_max,nstep_max,ntoti,ntotall integer :: npartoftypei(maxparttypes) logical :: iexist,reallocate,debug,goterrors real(doub_prec) :: timetemp,hperiodic(3) !integer, dimension(maxplot) :: isrequired nstepsread = 0 goterrors = .false. if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! if (iverbose==1 .and. ipos==1) print "(1x,a)",'reading FalcON hdf5 format' inquire(file=datfile,exist=iexist) if (.not.iexist) then ! !--append .h5 on the end if not already present ! datfile=trim(rootname)//'.h5' inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(rootname)//': file not found ***' return endif endif ! ! set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 debug = (debugmode .or. lenvironment('FSPLASH_DEBUG')) ! ! read data from snapshots ! i = istepstart write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! ! open file and read header information ! if (debug) print*,'DEBUG: reading header...' call open_falcON_file(cstring(datfile),ierr) if (ierr /= 0) then print "(a)", '*** ERROR OPENING FALCON FILE ***' return endif if (falcON_file_is_open() /= 1) then print "(a)", '*** ERROR: falcON_file_is_open /= 1 after opening ***' return endif if (debug) call set_falcON_debugging_level(3); nstep_max = num_falcON_snapshots(ierr); if (debug) print*,'got ',nstep_max,' falcON snapshots in file' if (nstep_max <= 0) then print "(a)",'*** ERROR: no falcON snapshots found in file ***' return endif ntotall = 0 if (buffer_steps_in_file) then nsteps_to_read = nstep_max else nsteps_to_read = 1 endif i = istepstart over_snapshots: do istep=1,nstep_max ! ! read falcON header ! if (debug) print*,'DEBUG: opening snapshot ',i npartoftypei(:) = 0 call open_falcON_snapshot(ntypes, npartoftypei, & ncolstep, ndim, ndimV, timetemp, & hperiodic, ierr) ! ! error checking on header info ! ierror(:) = 0 call check_range(ntypes,'ntypes',min=1,err=ierror(1)) call check_range(npartoftypei(1:ntypes),'npartoftype',min=0,err=ierror(2)) call check_range(sum(npartoftypei(1:ntypes)),'ntot',min=1,err=ierror(3)) call check_range(ndim,'ndim',min=1,max=3,err=ierror(4)) call check_range(ndimV,'ndimV',min=ndim,max=3,err=ierror(5)) call check_range(timetemp,'time',min=0.d0,err=ierror(6)) call check_range(ierr,'error during header read',min=0,max=0,err=ierror(7)) if (any(ierror(1:7) > 0)) then print*,'*** ERROR during falcON header read ***' return endif ncolumns = ncolstep ntoti = sum(npartoftypei(1:ntypes)) ntotall = max(ntoti,ntotall) ! ! print header information ! if (iverbose >= 1 .and. buffer_steps_in_file .or. istep.eq.ipos) then !print "(2(a,1x,i10))",' npart: ',ntoti,' ncolumns: ',ncolstep !print "(a,i2)",' ntypes: ',ntypes,' !print*,' npartoftype = ',(npartoftypei(itypemap_falcON(j)),j=1,ntypes) !print*,' ncolstep = ',ncolstep,' ndim = ',ndim,ndimV print*,' time = ',timetemp !,' hper = ',hperiodic(:) endif ! ! now read data ! reallocate = .false. npart_max = maxpart if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntotall) else ! if first time, save on memory npart_max = int(ntoti) endif endif ! ! reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nsteps_to_read,max(ncolumns+ncalc,maxcol)) endif ! ! copy header data into allocated arrays ! if (buffer_steps_in_file .or. istep.eq.ipos) then do j=1,ntypes npartoftype(itypemap_falcON(j),i) = npartoftypei(j) enddo time(i) = real(timetemp) masstype(:,i) = 0. ! all masses read from file endif ! ! read particle data ! got_particles: if (ntoti > 0) then !isrequired(:) = 0 !where (required(1:ncolumns)) isrequired(1:ncolumns) = 1 if (buffer_steps_in_file .or. istep.eq.ipos) then i_current_step = i call read_falcON_snapshot(ierr); if (ierr /= 0) then print "(/,1x,a,/)",' *** ERROR reading falcON snapshot ***' print*,'Press any key to continue (but there is likely something wrong with the file...)' read* endif call print_types(npartoftype(:,i),labeltype) i = i + 1 endif nstepsread = nstepsread + 1 else ! ! cover the special case where no particles have been read ! npartoftype(1,i) = 1 dat(:,:,i) = 0. endif got_particles enddo over_snapshots ! ! now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. ! ! set flag to indicate that only part of this file has been read ! ipartialread = .false. !if (.not.all(required(1:ncolstep))) ipartialread = .true. ! ! call set labels to identify location of smoothing length ! call set_labels !if (nstepsread.gt.0) then ! print "(a,i10,a)",' >> read ',sum(npartoftype(:,i)),' particles' !endif call close_falcON_file() return end subroutine read_data subroutine read_falcON_data_into_splash(icol,npartoftypei,temparr,itypec) bind(c,name="read_falcON_data_into_splash") use, intrinsic :: iso_c_binding, only:c_int,c_double use particle_data, only:dat,iamtype,npartoftype use settings_data, only:debugmode use labels, only:label use falcONhdf5read, only:itypemap_falcON,ioffset,i_current_step implicit none integer(kind=c_int), intent(in) :: icol,npartoftypei,itypec real(kind=c_double), intent(in) :: temparr(*) integer(kind=c_int) :: icolput integer :: istart,iend,nmax,itype,i itype = itypec + 1 ! convert from c to Fortran indexing icolput = icol + 1 if (debugmode) print "(3(a,i2),a,i8)",'DEBUG: Step ',i_current_step,' column ',icol,& ' type ',itypemap_falcON(itype),' -> '//trim(label(icolput)) ! check column is within array limits if (icolput.gt.size(dat(1,:,1)) .or. icolput.eq.0) then print "(a,i2,a)",' ERROR: column = ',icolput,' out of range in receive_data_fromc' return endif ! ensure no array overflows istart = ioffset(itypemap_falcON(itype),npartoftype(:,i_current_step)) + 1 iend = min(istart + npartoftypei - 1,size(dat(:,1,1))) nmax = iend - istart + 1 ! copy data into main splash array if (debugmode) print*,'DEBUG: COPYING TO ',istart,iend,' total = ',1,nmax ! this should never happen if (i_current_step < 1 .or. i_current_step > size(dat(1,1,:))) then print*,'INTERNAL ERROR in indexing during falcON read' return endif dat(istart:iend,icolput,i_current_step) = real(temparr(1:nmax),kind=kind(dat)) ! set particle type if (size(iamtype(:,1)).gt.1) then print*,' SETTING TYPES ',istart,iend do i=istart,iend iamtype(i,i_current_step) = int(itypemap_falcON(itype),kind=kind(iamtype)) enddo endif return end subroutine read_falcON_data_into_splash !------------------------------------------------------------ ! set labels for each column of data !------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,ix,ivx,ipmass, & ih,irho,iax,iutherm !ipr,iutherm use settings_data, only:ndim,ndimV,UseTypeInRenderings use geometry, only:labelcoord use falcONhdf5read, only:blocklabel use asciiutils, only:lcase implicit none integer :: i,icol if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif ix = 0 iutherm = 0 do icol=1,size(blocklabel) select case(trim(lcase(blocklabel(icol)))) case('x') ix(1) = icol case('y') ix(2) = icol case('z') ix(3) = icol case('vx') ivx = icol case('ax') iax = icol case('h') ih = icol case('mass') ipmass = icol case('srho') irho = icol end select label(icol) = trim(blocklabel(icol)) enddo ! set labels of the quantities read in if (ix(1).gt.0) label(ix(1:ndim)) = labelcoord(1:ndim,1) if (irho.gt.0) label(irho) = 'density' !if (iutherm.gt.0) label(iutherm) = 'u' if (ipmass.gt.0) label(ipmass) = 'particle mass' ! set labels for vector quantities if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'_'//labelcoord(i,1) enddo endif if (iax.gt.0) then iamvec(iax:iax+ndimV-1) = iax labelvec(iax:iax+ndimV-1) = 'a' do i=1,ndimV label(iax+i-1) = trim(labelvec(iax))//'_'//labelcoord(i,1) enddo endif ! labels for each particle type already set UseTypeInRenderings(:) = .false. UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels subroutine set_splash_block_label(icol,name) bind(c) use, intrinsic :: iso_c_binding, only:c_int, c_char use falcONhdf5read, only:blocklabel use asciiutils, only:fstring implicit none integer(kind=c_int), intent(in) :: icol character(kind=c_char), intent(in) :: name(256) blocklabel(icol+1) = trim(fstring(name)) !print*,icol,' name = ',trim(blocklabel(icol)) end subroutine set_splash_block_label subroutine set_splash_particle_label(itypec,name) bind(c) use, intrinsic :: iso_c_binding, only:c_int, c_char use asciiutils, only:fstring use labels, only:labeltype use falcONhdf5read, only:itypemap_falcON implicit none integer(kind=c_int), intent(in) :: itypec character(kind=c_char), intent(in) :: name(256) !print*,' got type = ',itypec,' setting ',itypemap_falcON(itypec+1),'= ',trim(fstring(name)) labeltype(itypemap_falcON(itypec+1)) = trim(fstring(name)) end subroutine set_splash_particle_label splash/src/interpolate3D_projection.F90000644 000766 000000 00000070156 13261626263 021015 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! ! Module containing all of the routines required for 3D projections ! (where rendered quantity is integrated along the line of sight) ! !---------------------------------------------------------------------- module projections3D implicit none integer, parameter :: maxcoltable = 1000 real, dimension(0:maxcoltable) :: coltable real, private :: dq2table = 4./maxcoltable real, private :: ddq2table = maxcoltable/4. public :: setup_integratedkernel public :: interpolate3D_projection public :: interpolate3D_proj_vec,interp3D_proj_vec_synctron public :: wfromtable contains subroutine setup_integratedkernel !------------------------------------------------------------- ! tabulates the integral through the cubic spline kernel ! tabulated in (r/h)**2 so that sqrt is not necessary !------------------------------------------------------------- use kernels, only:wfunc,radkernel2,cnormk3D implicit none integer :: i,j real :: rxy2,deltaz,dz,z,q2,wkern,coldens integer, parameter :: npts = 100 !print "(1x,a)",'setting up integrated kernel table...' dq2table = radkernel2/maxcoltable ddq2table = 1./dq2table do i=0,maxcoltable-1 ! !--tabulate for (cylindrical) r**2 between 0 and radkernel**2 ! rxy2 = i*dq2table ! !--integrate z between 0 and sqrt(radkernel^2 - rxy^2) ! deltaz = sqrt(radkernel2 - rxy2) dz = deltaz/real(npts-1) coldens = 0. if (deltaz.ne.deltaz) print "(a)",'WARNING: NaN in kernel table setup' do j=1,npts z = (j-1)*dz q2 = rxy2 + z*z wkern = wfunc(q2) if (j.eq.1 .or. j.eq.npts) then coldens = coldens + 0.5*wkern*dz ! trapezoidal rule else coldens = coldens + wkern*dz endif enddo coltable(i)=2.0*coldens*cnormk3D end do coltable(maxcoltable) = 0. return end subroutine setup_integratedkernel ! ! This function interpolates from the table of integrated kernel values ! to give w(q) ! real function wfromtable(q2) implicit none real, intent(in) :: q2 real :: dxx,dwdx integer :: index, index1 ! !--find nearest index in table ! index = max(int(q2*ddq2table),0) ! the max prevents seg faults on NaNs for q2 !index = min(index,maxcoltable) ! should be unnecessary if q2 < radkernel checked index1 = min(index + 1,maxcoltable) ! !--find increment along from this index ! dxx = q2 - index*dq2table ! !--find gradient ! dwdx = (coltable(index1) - coltable(index))*ddq2table ! !--compute value of integrated kernel ! wfromtable = coltable(index) + dwdx*dxx end function wfromtable !-------------------------------------------------------------------------- ! subroutine to interpolate from particle data to even grid of pixels ! ! The data is smoothed using the SPH summation interpolant, ! that is, we compute the smoothed array according to ! ! datsmooth(pixel) = sum_b weight_b dat_b W(r-r_b, h_b) ! ! where _b is the quantity at the neighbouring particle b and ! W is the smoothing kernel, for which we use the usual cubic spline ! ! ** In this version 3D data is interpolated to a 2D grid by use of an ! ** integrated form of the kernel (that is W_ab in this case is ! ** the integral through the 3D kernel to give a 2D kernel) ! ** This results in a column density map of the interpolated quantity ! ** From a similar routine by Matthew Bate. ! ! The (dimensionless) weight for each particle should be ! ! weight = pmass/(rho*h^3) ! ! the interface is written in this form to avoid floating exceptions ! on physically scaled data. ! ! Input: particle coordinates : x,y,z (npart) - note that z is only required for perspective ! smoothing lengths : hh (npart) ! weight for each particle : weight (npart) ! scalar data to smooth : dat (npart) ! ! Output: smoothed data : datsmooth (npixx,npixy) ! ! Written by Daniel Price September 2003 ! 3D perspective added Nov 2005 !-------------------------------------------------------------------------- subroutine interpolate3D_projection(x,y,z,hh,weight,dat,itype,npart, & xmin,ymin,datsmooth,npixx,npixy,pixwidthx,pixwidthy,normalise,zobserver,dscreen, & useaccelerate) use kernels, only:radkernel,radkernel2 use timing, only:wall_time,print_time implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,z,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy,zobserver,dscreen real, intent(out), dimension(npixx,npixy) :: datsmooth logical, intent(in) :: normalise real, dimension(npixx,npixy) :: datnorm logical, intent(in) :: useaccelerate real :: row(npixx) integer :: ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax,npixpartx,npixparty integer :: iprintinterval, iprintnext,ipixi,jpixi,jpixcopy integer :: nsubgrid,nfull,nok,ncpus #ifdef _OPENMP integer :: omp_get_num_threads,i #else integer(kind=selected_int_kind(10)) :: iprogress,i ! up to 10 digits #endif real :: hi,hi1,hi21,radkern,wab,q2,xi,yi,xminpix,yminpix real :: term,termnorm,dy,dy2,ypix,zfrac,hsmooth,horigi real :: xpixmin,xpixmax,xmax,ypixmin,ypixmax,ymax real :: hmin,fac,hminall !,dhmin3 real, dimension(npixx) :: xpix,dx2i real :: t_start,t_end,t_used logical :: iprintprogress,use3Dperspective,accelerate character(len=32) :: string datsmooth = 0. term = 0. string = 'projecting' if (normalise) then string = trim(string)//' (normalised)' datnorm = 0. elseif (useaccelerate) then string = trim(string)//' (fast)' endif ncpus = 0 !$omp parallel !$omp master !$ ncpus = omp_get_num_threads() !$omp end master !$omp end parallel if (ncpus > 0) then write (*,"(1x,a,': ',i4,' x ',i4,' on ',i3,' cpus')") trim(string),npixx,npixy,ncpus else write (*,"(1x,a,': ',i4,' x ',i4)") trim(string),npixx,npixy endif if (pixwidthx.le.0. .or. pixwidthy.le.0) then print "(1x,a)",'ERROR: pixel width <= 0' return endif !nout = count(hh(1:npart).le.0.) !if (nout.gt.0) then ! print*,'interpolate3D_projection: warning: ignoring ',nout,' particles with h <= 0' !endif ! !--check column density table has actually been setup ! if (abs(coltable(1)).le.1.e-5) then call setup_integratedkernel endif ! !--print a progress report if it is going to take a long time ! (a "long time" is, however, somewhat system dependent) ! iprintprogress = (npart .ge. 100000) .or. (npixx*npixy .gt.100000) ! !--loop over particles ! iprintinterval = 25 if (npart.ge.1e6) iprintinterval = 10 iprintnext = iprintinterval use3Dperspective = abs(dscreen).gt.tiny(dscreen) ! !--get starting CPU time ! call wall_time(t_start) xminpix = xmin - 0.5*pixwidthx yminpix = ymin - 0.5*pixwidthy xmax = xmin + npixx*pixwidthx ymax = ymin + npixy*pixwidthy ! !--use a minimum smoothing length on the grid to make ! sure that particles contribute to at least one pixel ! hmin = 0.5*max(pixwidthx,pixwidthy) !dhmin3 = 1./(hmin*hmin*hmin) ! !--store x value for each pixel (for optimisation) ! do ipix=1,npixx xpix(ipix) = xminpix + ipix*pixwidthx enddo nsubgrid = 0 nok = 0 hminall = huge(hminall) !$omp parallel default(none) & !$omp shared(hh,z,x,y,weight,dat,itype,npart) & !$omp shared(xmin,ymin,xmax,ymax,xminpix,yminpix,xpix,pixwidthx,pixwidthy) & !$omp shared(npixx,npixy,dscreen,zobserver,use3dperspective,useaccelerate) & !$omp shared(normalise,radkernel,radkernel2,datsmooth,datnorm) & !$omp firstprivate(hmin) & !,dhmin3) & !$omp private(hi,zfrac,xi,yi,radkern,xpixmin,xpixmax,ypixmin,ypixmax) & !$omp private(hsmooth,horigi,hi1,hi21,term,termnorm,npixpartx,npixparty,jpixi,ipixi) & !$omp private(ipixmin,ipixmax,jpixmin,jpixmax,accelerate) & !$omp private(dx2i,row,q2,ypix,dy,dy2,wab) & !$omp private(i,ipix,jpix,jpixcopy,fac) & !$omp reduction(+:nsubgrid,nok) & !$omp reduction(min:hminall) !$omp do schedule (guided, 2) over_particles: do i=1,npart ! !--report on progress ! #ifndef _OPENMP if (iprintprogress) then iprogress = 100*i/npart if (iprogress.ge.iprintnext) then write(*,"('(',i3,'% -',i12,' particles done)')") iprogress,i iprintnext = iprintnext + iprintinterval endif endif #endif ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_particles ! !--set h related quantities ! hi = hh(i) horigi = hi if (hi.le.0.) then cycle over_particles elseif (use3Dperspective) then if (z(i).gt.zobserver) cycle over_particles zfrac = abs(dscreen/(z(i)-zobserver)) hi = hi*zfrac endif radkern = radkernel*hi ! radius of the smoothing kernel !--cycle as soon as we know the particle does not contribute xi = x(i) xpixmin = xi - radkern if (xpixmin.gt.xmax) cycle over_particles xpixmax = xi + radkern if (xpixmax.lt.xmin) cycle over_particles yi = y(i) ypixmin = yi - radkern if (ypixmin.gt.ymax) cycle over_particles ypixmax = yi + radkern if (ypixmax.lt.ymin) cycle over_particles !--take resolution length as max of h and 1/2 pixel width if (hi.lt.hmin) then hminall = min(hi,hminall) nsubgrid = nsubgrid + 1 hsmooth = hmin fac = 1. !(horigi*horigi*horigi)*dhmin3 ! factor by which to adjust the weight else fac = 1. hsmooth = hi nok = nok + 1 endif radkern = radkernel*hsmooth ! !--set kernel related quantities ! hi1 = 1./hsmooth hi21 = hi1*hi1 termnorm = weight(i)*fac*horigi term = termnorm*dat(i) ! h gives the z length scale (NB: no perspective) ! !--for each particle work out which pixels it contributes to ! npixpartx = int(radkern/pixwidthx) + 1 npixparty = int(radkern/pixwidthy) + 1 jpixi = int((yi-ymin)/pixwidthy) + 1 ipixi = int((xi-xmin)/pixwidthx) + 1 ipixmin = ipixi - npixpartx ipixmax = ipixi + npixpartx jpixmin = jpixi - npixparty jpixmax = jpixi + npixparty ! ipixmin = int((xi - radkern - xmin)/pixwidth) ! jpixmin = int((yi - radkern - ymin)/pixwidth) ! ipixmax = ipixmin + npixpart !!int((xi + radkern - xmin)/pixwidth) + 1 ! jpixmax = jpixmin + npixpart !!int((yi + radkern - ymin)/pixwidth) + 1 ! !--loop over pixels, adding the contribution from this particle ! copy by quarters if all pixels within domain ! accelerate = useaccelerate .and. (.not.normalise) & .and. npixpartx.gt.5 .and. npixparty.gt.5 & .and. ipixmin.ge.1 .and. ipixmax.le.npixx & .and. jpixmin.ge.1 .and. jpixmax.le.npixy if (accelerate) then !--adjust xi, yi to centre of pixel xi = xminpix + ipixi*pixwidthx yi = yminpix + jpixi*pixwidthy ! !--precalculate an array of dx2 for this particle (optimisation) ! do ipix=ipixmin,ipixmax dx2i(ipix) = ((xpix(ipix) - xi)**2)*hi21 enddo do jpix = jpixi,jpixmax ypix = yminpix + jpix*pixwidthy dy = ypix - yi dy2 = dy*dy*hi21 do ipix = ipixi,ipixmax q2 = dx2i(ipix) + dy2 ! !--SPH kernel - integral through cubic spline ! interpolate from a pre-calculated table ! if (q2.lt.radkernel2) then wab = wfromtable(q2) ! !--calculate data value at this pixel using the summation interpolant ! !$omp atomic datsmooth(ipix,jpix) = datsmooth(ipix,jpix) + term*wab row(ipix) = term*wab else row(ipix) = 0. endif enddo !--NB: the following actions can and should be vectorized (but I don't know how...) !--copy top right -> top left do ipix=ipixmin,ipixi-1 !$omp atomic datsmooth(ipix,jpix) = datsmooth(ipix,jpix) + row(ipixmax-(ipix-ipixmin)) enddo if (jpix.ne.jpixi) then jpixcopy = jpixi - (jpix-jpixi) !--copy top right -> bottom left do ipix=ipixmin,ipixi-1 !$omp atomic datsmooth(ipix,jpixcopy) = datsmooth(ipix,jpixcopy) + row(ipixmax-(ipix-ipixmin)) enddo !--copy top right -> bottom right do ipix=ipixi,ipixmax !$omp atomic datsmooth(ipix,jpixcopy) = datsmooth(ipix,jpixcopy) + row(ipix) enddo endif enddo else ipixmin = int((xi - radkern - xmin)/pixwidthx) ipixmax = int((xi + radkern - xmin)/pixwidthx) jpixmin = int((yi - radkern - ymin)/pixwidthy) jpixmax = int((yi + radkern - ymin)/pixwidthy) if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (jpixmin.lt.1) jpixmin = 1 ! to pixels in the image if (ipixmax.gt.npixx) ipixmax = npixx ! (note that this optimises if (jpixmax.gt.npixy) jpixmax = npixy ! much better than using min/max) ! !--precalculate an array of dx2 for this particle (optimisation) ! do ipix=ipixmin,ipixmax dx2i(ipix) = ((xpix(ipix) - xi)**2)*hi21 enddo do jpix = jpixmin,jpixmax ypix = yminpix + jpix*pixwidthy dy = ypix - yi dy2 = dy*dy*hi21 do ipix = ipixmin,ipixmax !xpix = xminpix + ipix*pixwidthx !dx = xpix - xi !rab2 = (xminpix + ipix*pixwidthx - xi)**2 + dy2 q2 = dx2i(ipix) + dy2 ! dx2 pre-calculated; dy2 pre-multiplied by hi21 ! !--SPH kernel - integral through cubic spline ! interpolate from a pre-calculated table ! if (q2.lt.radkernel2) then wab = wfromtable(q2) ! !--calculate data value at this pixel using the summation interpolant ! !$omp atomic datsmooth(ipix,jpix) = datsmooth(ipix,jpix) + term*wab if (normalise) then !$omp atomic datnorm(ipix,jpix) = datnorm(ipix,jpix) + termnorm*wab endif endif enddo enddo endif enddo over_particles !$omp end do !$omp end parallel ! !--normalise dat array ! if (normalise) then !--normalise everywhere (required if not using SPH weighting) where (datnorm > tiny(datnorm)) datsmooth = datsmooth/datnorm end where endif ! !--warn about subgrid interpolation ! if (nsubgrid.gt.1) then nfull = int((xmax-xmin)/(hminall)) + 1 if (nsubgrid.gt.0.1*nok) & print "(a,i9,a,/,a,i6,a)",' Warning: pixel size > 2h for ',nsubgrid,' particles', & ' need',nfull,' pixels for full resolution' endif ! !--get/print timings ! call wall_time(t_end) t_used = t_end - t_start if (t_used.gt.10.) call print_time(t_used) return end subroutine interpolate3D_projection !-------------------------------------------------------------------------- ! ! Same as previous but for a vector quantity ! ! Input: particle coordinates : x,y (npart) ! smoothing lengths : hh (npart) ! weight for each particle : weight (npart) ! vector data to smooth : vecx (npart) ! vecy (npart) ! ! Output: smoothed vector field : vecsmoothx (npixx,npixy) ! : vecsmoothy (npixx,npixy) ! ! Daniel Price 23/12/04 !-------------------------------------------------------------------------- subroutine interpolate3D_proj_vec(x,y,z,hh,weight,vecx,vecy,itype,npart,& xmin,ymin,vecsmoothx,vecsmoothy,npixx,npixy,pixwidthx,pixwidthy,normalise,zobserver,dscreen) use kernels, only:radkernel,radkernel2 implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,z,hh,weight,vecx,vecy integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy,zobserver,dscreen real, intent(out), dimension(npixx,npixy) :: vecsmoothx, vecsmoothy logical, intent(in) :: normalise real, dimension(:,:), allocatable :: datnorm integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax,ierr real :: hi,hi1,hi21,radkern,q2,wab,rab2,const,zfrac,hsmooth real :: termx,termy,termnorm,dx,dy,dy2,xpix,ypix vecsmoothx = 0. vecsmoothy = 0. termx = 0. termy = 0. if (normalise) then print "(1x,a)",'projecting vector (normalised) from particles to pixels...' allocate(datnorm(npixx,npixy),stat=ierr) if (ierr /= 0) then print "(a)",'interpolate3D_proj_vec: error allocating memory' return endif datnorm = 0. else print "(1x,a)",'projecting vector from particles to pixels...' endif if (pixwidthx.le.0. .or. pixwidthy.le.0.) then print "(a)",'interpolate3D_proj_vec: error: pixel width <= 0' return endif ! !--loop over particles ! !$omp parallel default(none) & !$omp shared(hh,z,x,y,weight,vecx,vecy,itype,vecsmoothx,vecsmoothy,npart) & !$omp shared(xmin,ymin,pixwidthx,pixwidthy,zobserver,dscreen,datnorm) & !$omp shared(npixx,npixy,normalise,radkernel,radkernel2) & !$omp private(hi,radkern,const,zfrac,ypix,xpix) & !$omp private(hsmooth,hi1,hi21,termx,termy,termnorm) & !$omp private(ipixmin,ipixmax,jpixmin,jpixmax) & !$omp private(dy,dy2,dx,rab2,q2,wab) & !$omp private(i,ipix,jpix) !$omp do schedule(guided, 2) over_particles: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_particles ! !--set kernel related quantities ! hi = hh(i) const = weight(i)*hi ! h gives the z length scale (NB: no perspective) if (hi.le.0.) then cycle over_particles elseif (abs(dscreen).gt.tiny(dscreen)) then if (z(i).gt.zobserver) cycle over_particles zfrac = abs(dscreen/(z(i)-zobserver)) hi = hi*zfrac endif !--take resolution length as max of h and 1/2 pixel width hsmooth = max(hi,0.5*min(pixwidthx,pixwidthy)) radkern = radkernel*hsmooth ! radius of the smoothing kernel hi1 = 1./hsmooth hi21 = hi1*hi1 termx = const*vecx(i) termy = const*vecy(i) termnorm = const ! !--for each particle work out which pixels it contributes to ! ipixmin = int((x(i) - radkern - xmin)/pixwidthx) jpixmin = int((y(i) - radkern - ymin)/pixwidthy) ipixmax = int((x(i) + radkern - xmin)/pixwidthx) + 1 jpixmax = int((y(i) + radkern - ymin)/pixwidthy) + 1 ! PRINT*,'particle ',i,' x, y, z = ',x(i),y(i),z(i),dat(i),rho(i),hi ! PRINT*,'pixels = ',ipixmin,ipixmax,jpixmin,jpixmax if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (jpixmin.lt.1) jpixmin = 1 ! to pixels in the image if (ipixmax.gt.npixx) ipixmax = npixx if (jpixmax.gt.npixy) jpixmax = npixy ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax ypix = ymin + (jpix-0.5)*pixwidthy dy = ypix - y(i) dy2 = dy*dy do ipix = ipixmin,ipixmax xpix = xmin + (ipix-0.5)*pixwidthx dx = xpix - x(i) rab2 = dx**2 + dy2 q2 = rab2*hi21 ! !--SPH kernel - integral through cubic spline ! interpolate from a pre-calculated table ! if (q2.lt.radkernel2) then wab = wfromtable(q2) ! !--calculate data value at this pixel using the summation interpolant ! vecsmoothx(ipix,jpix) = vecsmoothx(ipix,jpix) + termx*wab vecsmoothy(ipix,jpix) = vecsmoothy(ipix,jpix) + termy*wab if (normalise) datnorm(ipix,jpix) = datnorm(ipix,jpix) + termnorm*wab endif enddo enddo enddo over_particles !$omp end do !$omp end parallel if (normalise .and. allocated(datnorm)) then !--normalise everywhere where (datnorm > tiny(datnorm)) vecsmoothx = vecsmoothx/datnorm vecsmoothy = vecsmoothy/datnorm end where endif if (allocated(datnorm)) deallocate(datnorm) return end subroutine interpolate3D_proj_vec !-------------------------------------------------------------------------- ! ! Computes synchrotron emission (Stokes Q, U) for a given B field ! at present assuming no faraday rotation ! ! For references see: ! ! Urbanik et al. (1997), A&A 326, 465 ! Sokoloff et al. (1998), MNRAS, 299, 189 ! Gomez & Cox (2004), ApJ 615, 744 (for Cosmic Ray distribution esp.) ! ! Faraday rotation could be included easily but ! I have not yet done so. ! ! Input: particle coordinates : x,y (npart) ! smoothing lengths : hh (npart) ! weight for each particle : weight (npart) ! vector data to smooth : vecx (npart) ! vecy (npart) ! ! Output: smoothed vector field : stokesQ (npixx,npixy) ! : stokesU (npixx,npixy) ! : stokesI (npixx,npixy) ! ! DOES NOT WORK FOR ROTATED CONFIGURATIONS YET!! ! (ie. z is assumed to be z_galaxy in the cosmic ray distribution) ! ! Daniel Price 14/03/07 !-------------------------------------------------------------------------- subroutine interp3D_proj_vec_synctron(x,y,z,hh,weight,vecx,vecy,itype,npart,& xmin,ymin,stokesQ,stokesU,stokesI,npixx,npixy,pixwidth,rcrit,zcrit,alpha, & qpixwidth,getIonly,utherm,uthermcutoff) use kernels, only:radkernel,radkernel2 implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,z,hh,weight,vecx,vecy integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidth,rcrit,zcrit,alpha,qpixwidth logical, intent(in) :: getIonly real, intent(out), dimension(npixx,npixy) :: stokesQ,stokesU,stokesI real, intent(in), dimension(npart), optional :: utherm real, intent(in), optional :: uthermcutoff integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax real :: hi,hi1,hi21,radkern,q2,wab,const,hsmooth real :: termx,termy,term,dy,dy2,ypix,xi,yi,zi real :: crdens,emissivity,Bperp,angle,pintrinsic,rcyl real, dimension(npixx) :: dx2i if (getIonly) then stokesI = 0. else stokesU = 0. stokesQ = 0. endif termx = 0. termy = 0. term = 0. pintrinsic = (3. + 3.*alpha)/(5. + 3.*alpha) if (getIonly) then print "(1x,a)",'getting synchrotron intensity map from B field...' else print "(1x,a)",'getting synchrotron polarisation map from B field...' print*,' assuming cosmic ray electron distribution exp(-r/',rcrit,' -z/',zcrit,') (kpc)' print*,' synchrotron spectral index I_nu = nu^-',alpha print*,' intrinsic polarisation fraction = ',pintrinsic endif if (present(utherm) .and. present(uthermcutoff)) then print*,' using only particles with utherm > ',uthermcutoff endif if (pixwidth.le.0.) then print "(a)",'interpolate3D_proj_vec_synchrotron: error: pixel width <= 0' return endif ! !--loop over particles ! !$omp parallel default(none) & !$omp shared(hh,z,x,y,weight,vecx,vecy,itype,stokesq,stokesu,stokesi,npart) & !$omp shared(xmin,ymin,pixwidth,rcrit,zcrit,alpha,radkernel,radkernel2) & !$omp shared(npixx,npixy,pintrinsic,qpixwidth,getionly,utherm,uthermcutoff) & !$omp private(hi,xi,yi,zi,radkern,const) & !$omp private(hsmooth,hi1,hi21,term,termx,termy) & !$omp private(rcyl,crdens,bperp,emissivity,angle) & !$omp private(ipixmin,ipixmax,jpixmin,jpixmax) & !$omp private(dy,dy2,dx2i,ypix,q2,wab) & !$omp private(i,ipix,jpix) !$omp do schedule(guided, 2) over_particles: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_particles ! !--skip particles with utherm < uthermcutoff ! if (present(utherm) .and. present(uthermcutoff)) then if (utherm(i).lt.uthermcutoff) cycle over_particles endif ! !--set kernel related quantities ! hi = hh(i) if (hi.le.0.) cycle over_particles const = weight(i)*hi ! h gives the z length scale (NB: no perspective) zi = z(i) !--take resolution length as max of h and 1/2 pixel width ! (for intensity calculation, qpixwidth is pixel width of Q,U calculation) hsmooth = max(hi,0.5*pixwidth,0.5*qpixwidth) hi1 = 1./hsmooth hi21 = hi1*hi1 radkern = radkernel*hsmooth ! radius of the smoothing kernel xi = x(i) yi = y(i) !--assumed distribution of cosmic ray electrons in galaxy ! (should use UNROTATED x,y if rotation added) rcyl = sqrt(xi**2 + yi**2) crdens = exp(-rcyl/rcrit - abs(zi)/zcrit) !--calculate synchrotron emissivity based on Bperp and a spectral index alpha Bperp = sqrt(vecx(i)**2 + vecy(i)**2) emissivity = crdens*Bperp**(1. + alpha) if (getIonly) then term = emissivity*const termx = 0. termy = 0. else term = 0. !--faraday rotation would change angle here angle = atan2(vecy(i),vecx(i)) termx = pintrinsic*emissivity*const*COS(angle) termy = pintrinsic*emissivity*const*SIN(angle) endif ! !--for each particle work out which pixels it contributes to ! ipixmin = int((xi - radkern - xmin)/pixwidth) jpixmin = int((yi - radkern - ymin)/pixwidth) ipixmax = int((xi + radkern - xmin)/pixwidth) + 1 jpixmax = int((yi + radkern - ymin)/pixwidth) + 1 ! PRINT*,'particle ',i,' x, y, z = ',x(i),y(i),z(i),dat(i),rho(i),hi ! PRINT*,'pixels = ',ipixmin,ipixmax,jpixmin,jpixmax if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (jpixmin.lt.1) jpixmin = 1 ! to pixels in the image if (ipixmax.gt.npixx) ipixmax = npixx if (jpixmax.gt.npixy) jpixmax = npixy ! !--precalculate an array of dx2 for this particle (optimisation) ! do ipix=ipixmin,ipixmax dx2i(ipix) = ((xmin + (ipix-0.5)*pixwidth - xi)**2)*hi21 enddo ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax ypix = ymin + (jpix-0.5)*pixwidth dy = ypix - yi dy2 = dy*dy*hi21 do ipix = ipixmin,ipixmax !xpix = xmin + (ipix-0.5)*pixwidth !dx = xpix - xi q2 = dx2i(ipix) + dy2 ! !--SPH kernel - integral through cubic spline ! interpolate from a pre-calculated table ! if (q2.lt.radkernel2) then wab = wfromtable(q2) ! !--calculate data value at this pixel using the summation interpolant ! if (getIonly) then stokesI(ipix,jpix) = stokesI(ipix,jpix) + term*wab else stokesQ(ipix,jpix) = stokesQ(ipix,jpix) + termx*wab stokesU(ipix,jpix) = stokesU(ipix,jpix) + termy*wab endif endif enddo enddo enddo over_particles !$omp end do !$omp end parallel return end subroutine interp3D_proj_vec_synctron end module projections3D splash/src/read_data_gadget.f90000644 000766 000000 00000141663 13261626263 017325 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR OUTPUT FROM THE GADGET CODE ! (works with GADGET v1.0, v2.0 and v3.0) ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! GSPLASH_FORMAT if = 2 then reads the block-labelled GADGET format ! rather than the default format. ! GSPLASH_USE_Z if 'YES' uses redshift in the legend instead of time ! GSPLASH_DARKMATTER_HSOFT if given a value > 0.0 will assign a ! smoothing length to dark matter particles which can then be ! used in the rendering ! GSPLASH_EXTRACOLS if set to a comma separated list of column labels, ! will attempt to read additional columns containing gas particle ! properties beyond the end of file ! GSPLASH_STARPARTCOLS if set to a comma separated list of column labels, ! will attempt to read additional columns containing star particle ! properties beyond the end of file ! GSPLASH_CHECKIDS if 'YES','yes','TRUE' or 'true' then reads and checks ! particle IDs for negative values and flags these as accreted particles ! GSPLASH_HSML_COLUMN if set to a positive integer, specifies the location ! of the smoothing length in the columns, overriding any default settings. ! GSPLASH_IGNORE_IFLAGCOOL if set to 'YES' or `TRUE', does not assume that ! extra columns are present even if the cooling flag is set in the header. ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Partial data read implemented Nov 2006 means that columns with ! the 'required' flag set to false are not read (read is therefore much faster) !------------------------------------------------------------------------- module gadgetread use params, only:maxplot implicit none real :: hsoft character(len=4), dimension(maxplot) :: blocklabelgas logical :: havewarned = .false. end module gadgetread subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol,maxstep use params, only:doub_prec,sing_prec,maxparttypes use settings_data, only:ndim,ndimV,ncolumns,ncalc,iformat,required,ipartialread, & ntypes,debugmode,iverbose use settings_page, only:legendtext use mem_allocation, only:alloc use labels, only:ih,irho,ipmass,labeltype use system_utils, only:renvironment,lenvironment,ienvironment,envlist use gadgetread, only:hsoft,blocklabelgas,havewarned implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile,densfile,hfile character(len=4) :: blocklabel character(len=20) :: string integer, dimension(maxparttypes) :: npartoftypei,Nall integer, dimension(:), allocatable :: iamtemp integer :: i,j,k,n,itype,icol,ierr,ierrh,ierrrho,nhset,nvec,ifile integer :: index1,index2,indexstart,indexend,nmassesdumped,ntypesused integer :: ncolstep,npart_max,nstep_max,ntoti,nacc,ntotall,idot integer :: iFlagSfr,iFlagFeedback,iFlagCool,nfiles,istart,nhfac integer :: nextracols,nstarcols,i1,i2,i3,i4,lenblock,idumpformat integer, dimension(6) :: i0,i1all,i2all integer, parameter :: iunit = 11, iunitd = 102, iunith = 103 logical :: iexist,reallocate,checkids,usez,goterrors logical, dimension(6) :: ireadtype real(doub_prec) :: timetemp,ztemp real(doub_prec), dimension(6) :: massoftypei real(sing_prec), dimension(:), allocatable :: dattemp1 real(sing_prec), dimension(:,:), allocatable :: dattemp real :: hfact,hfactmean real, parameter :: pi = 3.1415926536 nstepsread = 0 goterrors = .false. if (maxparttypes.lt.6) then print*,' *** ERROR: not enough particle types for GADGET data read ***' print*,' *** you need to edit splash parameters and recompile ***' stop endif if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! inquire(file=datfile,exist=iexist) if (.not.iexist) then ! !--look for a file with .0 on the end for multiple-file reads ! datfile=trim(rootname)//'.0' inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(rootname)//': file not found ***' return endif endif ! !--set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 idumpformat = 0 idumpformat = ienvironment('GSPLASH_FORMAT') checkids = lenvironment('GSPLASH_CHECKIDS') usez = lenvironment('GSPLASH_USE_Z') ! !--read data from snapshots ! i = istepstart ! !--i0 is the offset used to read the data into the arrays ! (non-zero for read from multiple files) ! The offset is different for each particle type, somewhat ! complicating the data read -- we shuffle the particles from ! multiple files so that they are in type order. ! i0(:) = 0 ! !--loop over the number of files ! ifile = 0 ntotall = 0 over_files: do while(iexist) write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ifile = ifile + 1 ! !--open data file and read data ! open(iunit,iostat=ierr,file=datfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)", '*** ERROR OPENING FILE ***' return endif !if (any(i0.gt.0)) print*,'starting position for each type in data array: ',i0(:) ! !--read header for this timestep ! if (idumpformat.eq.2) then print "(a)",' >> reading block labelled Gadget format <<' read(iunit,iostat=ierr) blocklabel,lenblock !print*,ierr,blocklabel,lenblock if (ierr /= 0 .or. lenblock.ne.264) then print "(/,a,/)",'*** ERROR READING HEADER: wrong endian? or wrong format? ***' close(iunit) if (ifile.eq.1) then return else exit over_files endif endif else if (ifile.eq.1) print "(a)",' >> reading default Gadget format <<' endif npartoftypei(:) = 0 Nall(:) = 0 massoftypei(:) = 0. iFlagCool = 0 nfiles = 0 read(iunit,iostat=ierr) npartoftypei(1:6),massoftypei(1:6),timetemp,ztemp, & iFlagSfr,iFlagFeedback,Nall(1:6),iFlagCool,nfiles ntoti = int(sum(npartoftypei(1:6))) ! int here is unnecessary, but avoids compiler warnings if (nfiles.gt.1) then ntotall = int(sum(Nall(1:6))) else ntotall = ntoti endif if (debugmode) then print*,'DEBUG: ierr = ',ierr print*,'DEBUG: ntoti = ',ntoti,' ntotall = ',ntotall,' nfiles = ',nfiles print*,'DEBUG: npartoftype = ',npartoftypei(1:6),' Nall = ',Nall(1:6) print*,'DEBUG: iFlagSfr = ',iFlagSfr,' iFlagFeedback = ',iFlagFeedback,' iFlagCool = ',iFlagCool print*,'DEBUG: time = ',timetemp,' z = ',ztemp endif if (ierr /= 0 .or. ntoti.le.0 .or. ntotall.le.0 .or. any(npartoftypei.lt.0) .or. nfiles.lt.0 & .or. nfiles.gt.1e6) then print "(/,a)", '*** ERROR READING TIMESTEP HEADER: wrong endian? ***' print "(/,a)", ' (see splash userguide for compiler-dependent' print "(a)", ' ways to change endianness on the command line)' print "(/,a)", ' (set environment variable GSPLASH_FORMAT to 2 ' print "(a,/)", ' if you are using the block-labelled Gadget format)' close(iunit) if (ifile.eq.1) then return else exit over_files endif endif ! !--if we are reading from multiple files, ! check that the sequence starts from the correct file ! if (nfiles.gt.1) then idot = len_trim(datfile)-1 if (ifile.eq.1 .and. datfile(idot:idot+1).ne.'.0') then if (nfiles.lt.100) then string = "(/,a,i2,a,/,a,/)" else string = "(/,a,i7,a,/,a,/)" endif if (nfiles.gt.10000) then !--this is the most likely scenario here print "(a)",'*** ERROR reading timestep header: wrong endian? ***' else print string,' ERROR: read is from multiple files (nfiles = ',nfiles,')',& ' but this is not the first file (does not end in .0): skipping...' endif close(iunit) return endif endif if (idumpformat.eq.2) then ncolstep = 1 do while (ierr.eq.0) call read_blockheader(idumpformat,iunit,0,index2,blocklabelgas(ncolstep),lenblock,nvec) read(iunit,iostat=ierr) if ((ierr.eq.0 .and. index2.gt.0) .and. (index2.eq.ntoti & .or. index2.eq.npartoftypei(1) & .or. index2.eq.npartoftypei(2) & .or. index2.eq.npartoftypei(5) & .or. index2.eq.(npartoftypei(1)+npartoftypei(5)) & .or. index2.eq.(npartoftypei(1)+npartoftypei(2)))) then select case(blocklabelgas(ncolstep)) case('ID ') ! not a column case default ncolstep = ncolstep + nvec end select endif enddo ncolstep = ncolstep - 1 rewind(iunit) read(iunit,iostat=ierr) read(iunit,iostat=ierr) iformat = 2 nextracols = 0 nstarcols = 0 else iformat = 0 if (iFlagCool.eq.1 .and. .not.lenvironment('GSPLASH_IGNORE_IFLAGCOOL')) then iformat = 1 ncolstep = 12 ! 3 x pos, 3 x vel, pmass, utherm, rho, Ne, Nh, h if (ifile.eq.1) print "(a)",' cooling flag on : assuming Ne, Nh dumped before h' else iformat = 0 ncolstep = 10 ! 3 x pos, 3 x vel, pmass, utherm, rho, h endif if (iFlagSfr.eq.1) then if (ifile.eq.1) print "(a)",' star formation flag on: assuming star formation rate dumped ' ncolstep = ncolstep + 1 iformat = iformat + 10 endif call envlist('GSPLASH_EXTRACOLS',nextracols) if (nextracols.gt.0) then print "(a,i2,a)",' READING ',nextracols,' EXTRA COLUMNS ' ncolstep = ncolstep + nextracols endif call envlist('GSPLASH_STARPARTCOLS',nstarcols) if (nstarcols.gt.0) then print "(a,i2,a)",' READING ',nstarcols,' STAR PARTICLE COLUMN(S) ' ncolstep = ncolstep + nstarcols endif !call envlist('GSPLASH_EXTRAVECCOLS',nextraveccols) !if (nextraveccols.gt.0) then ! print "(a,i2,a)",' READING ',nextraveccols,' EXTRA COLUMNS ' ! ncolstep = ncolstep + nextraveccols !endif endif if (ifile.eq.1) then ncolumns = ncolstep ! !--call set labels to get ih, ipmass, irho for use in the read routine ! hsoft = 0. ! to avoid unset variable call set_labels endif if (ifile.eq.1) then print*,'time : ',timetemp if (usez) then print "(1x,a,f8.2,a)",'z (redshift) : ',ztemp,' (using in legend from GSPLASH_USE_Z setting)' else print "(1x,a,f8.2,a)",'z (redshift) : ',ztemp,' (set GSPLASH_USE_Z=yes to use in legend)' endif endif print*,'Npart (by type) : ',npartoftypei(1:6) if (ifile.eq.1) print*,'Mass (by type) : ',massoftypei(1:6) ! print "(10x,'|',6(1x,a12,'|'))", (labeltype(itype),itype=1,ntypes) ! print "(a10,'|',6(i11,2x,'|'))", 'Npart : ',npartoftypei ! print "(a10,'|',6(es11.3,2x,'|'))",'Mass : ',massoftypei print*,'N_gas : ',npartoftypei(1) print*,'N_total : ',ntoti if (ifile.eq.1) print*,'N data columns : ',ncolstep if (nfiles.gt.1 .and. ifile.eq.1) then print*,'Nall : ',Nall(1:6) endif if (nfiles.gt.1) then if (ifile.eq.1) print "(a,i4,a)",' reading from ',nfiles,' files' elseif (nfiles.lt.0) then print*,'*** ERROR: nfiles = ',nfiles,' in file header: aborting' return endif if (ifile.eq.1) then !--Softening lengths for Dark Matter Particles... hsoft = renvironment('GSPLASH_DARKMATTER_HSOFT') ! !--try to read dark matter and star particle smoothing lengths and/or density from a separate ! one column ascii file. If only density, use this to compute smoothing lengths. ! densfile = trim(rootname)//'.dens' hfile = trim(rootname)//'.hsml' hfact = 1.2 ! related to the analytic neighbour number (hfact=1.2 gives 58 neighbours in 3D) open(unit=iunitd,file=densfile,iostat=ierrrho,status='old',form='formatted') open(unit=iunith,file=hfile,iostat=ierrh,status='old',form='formatted') if (idumpformat.eq.2) then if (ih.eq.0 .and. (hsoft.gt.tiny(hsoft) .or. ierrrho.eq.0 .or. ierrh.eq.0)) then ncolumns = ncolumns + 1 blocklabelgas(ncolumns) = 'HSML' ih = ncolumns call set_labels endif if (irho.eq.0 .and. (hsoft.gt.tiny(hsoft) .or. ierrrho.eq.0 .or. ierrh.eq.0)) then ncolumns = ncolumns + 1 blocklabelgas(ncolumns) = 'RHO ' irho = ncolumns call set_labels endif endif ! !--if successfully read header, increment the nstepsread counter ! nstepsread = nstepsread + 1 endif ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntotall) else ! if first time, save on memory npart_max = int(ntotall) endif endif if (i.ge.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol)) endif ! !--copy npartoftypei into allocated header arrays ! and set the offset position of particle types in the main data arrays ! if (nfiles.eq.1 .or. ifile.eq.1) then i0(1) = 0 do itype=2,ntypes if (nfiles.eq.1) then i0(itype) = sum(npartoftypei(1:itype-1)) ! this is avoid depending on Nall at all for single file read else i0(itype) = sum(Nall(1:itype-1)) endif enddo npartoftype(:,i) = npartoftypei else i0(1) = npartoftype(1,i) do itype=2,ntypes i0(itype) = sum(Nall(1:itype-1)) + npartoftype(itype,i) enddo npartoftype(:,i) = npartoftype(:,i) + npartoftypei endif if (debugmode) print*,'DEBUG: starting position for each type in data array: ',i0(:) ! !--set time to be used in the legend ! if (ifile.eq.1) then if (usez) then !--use this line for redshift legendtext = 'z=' time(i) = real(ztemp) else !--use this line for code time time(i) = real(timetemp) endif else if (usez) then if (abs(real(ztemp)-time(i)).gt.tiny(0.)) print*,'ERROR: redshift different between files in multiple-file read' else if (abs(real(timetemp)-time(i)).gt.tiny(0.)) print*,'ERROR: time different between files in multiple-file read' endif if (sum(Nall).ne.ntotall) then print*,' ERROR: Nall differs between files' goterrors = .true. endif endif ! !--read particle data ! got_particles: if (ntoti.gt.0) then ! !--read positions of all particles ! (note that errors on position read are fatal) ! call read_blockheader(idumpformat,iunit,ntoti,index2,blocklabel,lenblock,nvec) if (iformat.eq.2 .and. blocklabel.ne.'POS ') then print "(a)",' WARNING: expecting positions, got '//blocklabel//' in data read' endif if (any(required(1:3))) then print*,'positions ',index2 if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(3,ntoti)) read(iunit,iostat=ierr) (dattemp(:,j),j=1,index2) if (nfiles.gt.1) then ! !--read data into type order if multiple files are present: ! this means the offset position is different for each type ! if (sum(npartoftypei).ne.index2) print*,' ERROR: number of positions .ne. sum of types' n = 0 do itype=1,ntypes do j=i0(itype)+1,i0(itype)+npartoftypei(itype) n = n + 1 dat(j,1:3,i) = dattemp(1:3,n) enddo enddo !read (iunit, iostat=ierr) ((dat(j,1:3,i),j=i0(itype)+1,i0(itype)+npartoftypei(itype)),itype=1,ntypes) else do j=1,index2 dat(j,1:3,i) = dattemp(1:3,j) enddo ! read (iunit, iostat=ierr) (dat(j,1:3,i),j=1,index2) endif if (ierr /= 0) then print "(a)",'error encountered whilst reading positions ' deallocate(dattemp) return endif else read(iunit, iostat=ierr) if (ierr /= 0) then print "(a)",'error skipping positions ' return endif endif ! !--same for velocities ! call read_blockheader(idumpformat,iunit,ntoti,index2,blocklabel,lenblock,nvec) if (iformat.eq.2 .and. blocklabel.ne.'VEL ') then print "(a)",' WARNING: expecting velocity, got '//blocklabel//' in data read' endif if (any(required(4:6))) then print*,'velocities ',index2 if (.not.allocated(dattemp)) allocate(dattemp(3,ntoti)) read (iunit, iostat=ierr) (dattemp(:,j),j=1,index2) if (nfiles.gt.1) then !--see above re: type order if (sum(npartoftypei).ne.index2) print*,' ERROR: number of velocities .ne. sum of types' n = 0 do itype=1,ntypes do j=i0(itype)+1,i0(itype)+npartoftypei(itype) n = n + 1 dat(j,4:6,i) = dattemp(1:3,n) enddo enddo !read (iunit, iostat=ierr) ((dat(j,4:6,i),j=i0(itype)+1,i0(itype)+npartoftypei(itype)),itype=1,ntypes) else do j=1,index2 dat(j,4:6,i) = dattemp(1:3,j) enddo !read (iunit, iostat=ierr) (dat(j,4:6,i),j=1,index2) endif if (ierr /= 0) then print "(a)",'error encountered whilst reading velocities' goterrors = .true. endif else read(iunit, iostat=ierr) if (ierr /= 0) then print "(a)",'error skipping velocities ' if (allocated(dattemp)) deallocate(dattemp) return endif endif if (allocated(dattemp)) deallocate(dattemp) ! !--skip read of particle ID (only required if we sort the particles ! back into their correct order, which is not implemented at present) ! OR if using particle ID to flag dead particles ! ! For multiple files we only allocate and read the IDs for one file ! if (checkids) then print*,'particle ID ',ntoti if (allocated(iamtemp)) deallocate(iamtemp) allocate(iamtemp(ntoti)) endif call read_blockheader(idumpformat,iunit,ntoti,index2,blocklabel,lenblock,nvec) if (iformat.eq.2 .and. blocklabel.ne.'ID ') then print "(a)",' WARNING: expecting particle ID, got '//blocklabel//' in data read' endif if (index2.gt.0) then if (checkids .and. required(ih)) then !--particle IDs are currently only used to set h -ve for accreted particles ! so do not read if h not required read (iunit,iostat=ierr) iamtemp(1:index2) else read (iunit,iostat=ierr) ! skip this line endif if (ierr /= 0) then print "(a)",'error encountered whilst reading particle ID' goterrors = .true. endif endif ! !--read particle masses ! !--work out total number of masses dumped nmassesdumped = 0 do itype = 1,6 if (abs(massoftypei(itype)).lt.tiny(massoftypei)) then nmassesdumped = nmassesdumped + npartoftypei(itype) endif enddo if (ipmass.eq.0) then masstype(1:6,i) = real(massoftypei(1:6)) else if (required(ipmass)) then print*,'particle masses ',nmassesdumped !--read this number of entries if (nmassesdumped.gt.0) then if (allocated(dattemp1)) deallocate(dattemp1) allocate(dattemp1(nmassesdumped)) call read_blockheader(idumpformat,iunit,nmassesdumped,index2,blocklabel,lenblock,nvec) if (iformat.eq.2 .and. blocklabel.ne.'MASS') then print "(a)",' WARNING: expecting particle masses, got '//blocklabel//' in data read' endif else index2 = 0 endif if (index2.gt.0) then read(iunit,iostat=ierr) dattemp1(1:index2) endif if (ierr /= 0) then print "(a)",'error reading particle masses' goterrors = .true. endif !--now copy to the appropriate sections of the dat array indexstart = 1 !index1 = 1 do itype=1,6 if (npartoftypei(itype).ne.0) then !--work out the appropriate section of the dat array for this particle type index1 = i0(itype) + 1 index2 = i0(itype) + npartoftypei(itype) if (abs(massoftypei(itype)).lt.tiny(massoftypei)) then ! masses dumped indexend = indexstart + npartoftypei(itype) - 1 if (debugmode) & print*,' read ',npartoftypei(itype),' masses for '//trim(labeltype(itype))// & ' particles',index1,'->',index2,indexstart,'->',indexend dat(index1:index2,ipmass,i) = dattemp1(indexstart:indexend) indexstart = indexend + 1 else ! masses not dumped if (debugmode) print "(a,es10.3,i10,a,i10)",& ' setting masses for '//trim(labeltype(itype))//' particles = ', & real(massoftypei(itype)),index1,'->',index2 dat(index1:index2,ipmass,i) = real(massoftypei(itype)) endif !index1 = index2 + 1 endif enddo if (allocated(dattemp1)) deallocate(dattemp1) elseif (nmassesdumped.gt.0) then read(iunit,iostat=ierr) if (ierr /= 0) then print "(a)",'error reading particle masses' goterrors = .true. endif endif endif ! !--read other quantities for rest of particles ! print*,'gas properties ',npartoftypei(1) if (ipmass.eq.0) then istart = 7 else istart = 8 endif icol = istart-1 gas_properties: do while (icol.lt.ncolstep) !icol=istart,ncolstep !-nextraveccols !!print*,icol i3 = 0 i4 = 0 ireadtype(:) = .false. if (idumpformat.eq.2) then if (icol+1.le.ih) then call read_blockheader(idumpformat,iunit,npartoftypei(1),index2,blocklabel,lenblock,nvec) else call read_blockheader(idumpformat,iunit,0,index2,blocklabel,lenblock,nvec) endif icol = icol + nvec ! !--work out from the number of entries what mix of particle types ! the quantity is defined on ! if (index2.eq.ntoti) then i1 = i0(1) + 1 i2 = i1 + ntoti - 1 print*,blocklabel//' (',index2,': all particles)' ireadtype(:) = .true. elseif (index2.eq.npartoftypei(1)) then i1 = i0(1) + 1 i2 = i1 + index2 - 1 print*,blocklabel//' (',index2,': gas particles only)' ireadtype(1) = .true. elseif (index2.eq.npartoftypei(2)) then i1 = i0(2) + 1 i2 = i1 + index2 - 1 print*,blocklabel//' (',index2,': dark matter particles only)' ireadtype(2) = .true. elseif (index2.eq.npartoftypei(1)+npartoftypei(2)) then i1 = i0(1) + 1 i2 = i1 + index2 - 1 print*,blocklabel//' (',index2,': gas+dark matter particles only)' ireadtype(1:2) = .true. elseif (index2.eq.npartoftypei(5)) then i1 = i0(5) + 1 i2 = i1 + index2 - 1 print*,blocklabel//' (',index2,': star particles only)' ireadtype(5) = .true. elseif (index2.eq.npartoftypei(1)+npartoftypei(5)) then i1 = i0(1) + 1 i2 = i1 + npartoftypei(1) - 1 i3 = i0(5) + 1 i4 = i3 + npartoftypei(5) - 1 print*,blocklabel//' (',index2,': gas+star particles only)' ireadtype(1) = .true. ireadtype(5) = .true. else print*,blocklabel//': ERROR in block length/quantity defined on unknown mix of types n = (',index2,')' i1 = i0(1)+1 i2 = i0(1)+index2 endif else nvec = 1 icol = icol + nvec if (icol.gt.ncolstep-nstarcols) then i1 = i0(5) + 1 i2 = i1 + npartoftypei(5) - 1 print*,'star particle properties ',icol,i1,i2 ireadtype(5) = .true. else !--default is a quantity defined only on gas particles i1 = i0(1) + 1 i2 = i1 + npartoftypei(1) - 1 ireadtype(1) = .true. endif endif ! !--construct the array offsets required when reading from multiple files ! ntypesused = 0 do itype=1,6 if (ireadtype(itype) .and. npartoftypei(itype).gt.0) then ntypesused = ntypesused + 1 i1all(ntypesused) = i0(itype) + 1 i2all(ntypesused) = i0(itype) + npartoftypei(itype) endif enddo if (npartoftypei(1).gt.0) then if (required(icol)) then if (i3.gt.0) then if (nfiles.gt.1) then read (iunit,iostat=ierr) (dat(i1all(itype):i2all(itype),icol,i),itype=1,ntypesused) else read (iunit,iostat=ierr) dat(i1:i2,icol,i),dat(i3:i4,icol,i) endif else if (nvec.gt.1) then if (nfiles.gt.1) then read (iunit,iostat=ierr) & (((dat(k,j,i),j=icol-nvec+1,icol),k=i1all(itype),i2all(itype)),itype=1,ntypesused) else read (iunit,iostat=ierr) ((dat(k,j,i),j=icol-nvec+1,icol),k=i1,i2) endif else if (nfiles.gt.1) then read (iunit,iostat=ierr) (dat(i1all(itype):i2all(itype),icol,i),itype=1,ntypesused) else read (iunit,iostat=ierr) dat(i1:i2,icol,i) endif endif endif else read (iunit,iostat=ierr) endif if (ierr /= 0) then print "(1x,a,i3)",'ERROR READING PARTICLE DATA from column ',icol goterrors = .true. endif endif enddo gas_properties !if (nextraveccols.gt.0) then ! print*,'chemical species ',index2 ! read (iunit, iostat=ierr) (dat(j,4:6,i),j=1,index2) ! if (ierr /= 0) then ! print "(a)",'error encountered whilst reading velocities' ! endif !endif ! !--close data file now that we have finished reading data ! close(unit=iunit) ! !--DEAL WITH ACCRETED PARTICLES (in this file only) ! if particle ID is less than zero, treat this as an accreted particle ! (give it a negative smoothing length) ! if (checkids) then nacc = 0 !--only do this if the smoothing length is required in the data read if (required(ih)) then n = 0 !do itype=1,ntypes itype = 1 do j=1,npartoftypei(itype) n = n + 1 if (iamtemp(n) < 0) then !if (itype.gt.1) print*,' id -ve on non-gas particle ',itype,j dat(i0(itype)+j,ih,i) = -abs(dat(i0(itype)+j,ih,i)) nacc = nacc + 1 endif enddo !enddo if (nacc.gt.0) then print "(a,i10,a,/,a)",' marking ',nacc,' '//trim(labeltype(1))// & ' particles with negative ID as accreted/dead', & ' (giving them a negative smoothing length so they will be ignored in renderings)' else print "(a)",' no particles with negative ID (i.e. accreted particles) found' endif endif if (allocated(iamtemp)) deallocate(iamtemp) endif endif got_particles ! !--now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. ! !--set flag to indicate that only part of this file has been read ! if (.not.all(required(1:ncolstep))) ipartialread = .true. ! !--for read from multiple files, work out the next file in the sequence ! iexist = .false. if (nfiles.gt.1 .and. ifile.lt.nfiles) then !--see if the next file exists idot = index(datfile,'.',back=.true.) if (idot.le.0) then print "(a)",' ERROR: read from multiple files but could not determine next file in sequence' goterrors = .true. else write(string,*) ifile write(datfile,"(a,i1)") trim(datfile(1:idot))//trim(adjustl(string)) iexist = .false. inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' ERROR: read from multiple files '// & 'but could not find '//trim(datfile)//': next in sequence' goterrors = .true. endif endif endif enddo over_files ! !--for some reason the smoothing length output by GADGET is ! twice the usual SPH smoothing length ! (do this after we have read data from all of the files) ! if (required(ih) .and. ih.gt.0 .and. size(dat(1,:,:)).ge.ih .and. npartoftype(1,i).gt.0) then print "(a)",' converting GADGET smoothing length on gas particles to usual SPH definition (x 0.5)' dat(1:npartoftype(1,i),ih,i) = 0.5*dat(1:npartoftype(1,i),ih,i) endif if (nfiles.gt.1. .and. any(npartoftype(:,i).ne.Nall(:))) then print*,'ERROR: sum of Npart across multiple files .ne. Nall in data read ' print*,'Npart = ',npartoftype(:,i) print*,'Nall = ',Nall(:) goterrors = .true. endif ! !--look for dark matter smoothing length/density files ! if (ierrh.eq.0 .or. ierrrho.eq.0) then if (ierrh.eq.0) then print "(a)",' READING DARK MATTER SMOOTHING LENGTHS from '//trim(hfile) ierr = 0 index1 = npartoftype(1,i)+1 index2 = npartoftype(1,i)+sum(npartoftype(2:,i)) read(iunith,*,iostat=ierr) (dat(j,ih,i),j=index1,index2) close(unit=iunith) if (ierr.lt.0) then nhset = 0 do j=index1,index2 if (dat(j,ih,i).gt.0.) nhset = nhset + 1 enddo print "(a,i10,a,/)",' *** END-OF-FILE: GOT ',nhset,' SMOOTHING LENGTHS ***' elseif (ierr.gt.0) then print "(a)", ' *** ERROR reading smoothing lengths from file' goterrors = .true. else print "(a,i10,a)",' SMOOTHING LENGTHS READ OK for ',index2-index1+1,' dark matter / star particles ' endif hsoft = 1.0 ! just so dark matter rendering is allowed in set_labels routine endif if (ierrrho.eq.0) then print "(a)",' READING DARK MATTER DENSITIES FROM '//trim(densfile) ierr = 0 index1 = npartoftype(1,i)+1 index2 = npartoftype(1,i)+sum(npartoftype(2:,i)) read(iunitd,*,iostat=ierr) (dat(j,irho,i),j=index1,index2) close(iunitd) if (ierr.lt.0) then nhset = 0 do j=index1,index2 if (dat(j,irho,i).gt.0.) nhset = nhset + 1 enddo print "(a,i10,a,/)",' *** END-OF-FILE: GOT ',nhset,' DENSITIES ***' elseif (ierr.gt.0) then print "(a)", ' *** ERROR reading dark matter densities from file' goterrors = .true. else print "(a,i10,a)",' DENSITY READ OK for ',index2-index1+1,' dark matter / star particles ' endif if (ierrh.ne.0 .and. ipmass.gt.0) then where(dat(:,irho,i) > tiny(dat)) dat(:,ih,i) = hfact*(dat(:,ipmass,i)/dat(:,irho,i))**(1./3.) elsewhere dat(:,ih,i) = 0. end where print "(a,i10,a,f5.2,a)", & ' SMOOTHING LENGTHS SET for ',j-1-index1,' DM/star particles using h = ',hfact,'*(m/rho)**(1/3)' endif hsoft = 1.0 ! just so dark matter rendering is allowed in set_labels routine endif else ! !--if a value for the dark matter smoothing length is set ! via the environment variable GSPLASH_DARKMATTER_HSOFT, ! give dark matter particles this smoothing length ! and a density of 1 (so column density plots work) ! if (hsoft.gt.tiny(hsoft)) then if (required(ih)) then print "(a,1pe10.3,a)",' ASSIGNING SMOOTHING LENGTH of h = ',hsoft, & ' to dark matter particles' !print*,'ih = ',ih,' npartoftype = ',npartoftype(1:2,i), shape(dat) if (ih.gt.0) then dat(npartoftype(1,i)+1:npartoftype(1,i)+npartoftype(2,i),ih,i) = hsoft else print*,' ERROR: smoothing length not found in data arrays' goterrors = .true. endif endif if (required(irho)) then if (irho.gt.0) then dat(npartoftype(1,i)+1:npartoftype(1,i)+npartoftype(2,i),irho,i) = 1.0 else print*,' ERROR: place for density not found in data arrays' goterrors = .true. endif endif else if (npartoftype(1,i).le.0 .and. sum(npartoftype(:,i)).gt.0) then print "(66('*'),4(/,a),/)",'* NOTE!! For GADGET data using dark matter only, column density ',& '* plots can be produced by setting the GSPLASH_DARKMATTER_HSOFT ',& '* environment variable to give the dark matter smoothing length', & '* (for a fixed smoothing length)' hsoft = (maxval(dat(:,1,i)) - minval(dat(:,1,i)))/sum(npartoftype(2:,i))**(1./3.) print*,' suggested value for GSPLASH_DARKMATTER_HSOFT = ',hsoft hsoft = 0. print "(7(/,a),/)",'* Alternatively, and for best results, calculate a number density', & '* on dark matter particles, set individual smoothing lengths from', & '* this using h = hfact*(n)**(-1/3), with hfact=1.2 and either ', & '* dump the results back into the HSML array in the original dump ', & '* file (if using the block-labelled format), or create an ascii ',& '* file called '//trim(hfile)//' containing the smoothing length ',& '* values for the dark matter particles.' print "(2(/,a),/,66('*'),/)", '* Also make sure normalised interpolations are OFF when plotting ',& '* dark matter density ' endif endif endif ! !--pause with fatal errors ! if (goterrors .and. .not.lenvironment('GSPLASH_IGNORE_ERRORS')) then print "(/,a)",'*** ERRORS detected during data read: data will be corrupted' print "(a,/)",' Please REPORT this and/or fix your file ***' print "(a)",' (set GSPLASH_IGNORE_ERRORS=yes to skip this message)' if (iverbose.ge.1) then print "(a)",' > Press any key to bravely proceed anyway <' read* endif endif ! !--give a friendly warning about using too few or too many neighbours ! (only works with equal mass particles because otherwise we need the number density estimate) ! if (ih.gt.0 .and. required(ih) .and. ipmass.gt.0 .and. required(ipmass) & .and. abs(massoftypei(1)).lt.tiny(0.) .and. ndim.eq.3 .and. .not.havewarned) then nhfac = 100 if (npartoftype(1,i).gt.nhfac) then hfactmean = 0. do j=1,nhfac hfact = dat(j,ih,i)*(dat(j,irho,i)/(dat(j,ipmass,i)))**(1./ndim) hfactmean = hfactmean + hfact enddo hfact = hfactmean/real(nhfac) havewarned = .true. if (hfact.lt.1.125 .or. hfact.gt.1.45) then print "(/,a)",'** FRIENDLY NEIGHBOUR WARNING! **' print "(3x,a,f5.1,a,/,3x,a,f5.2,a,i1,a)", & 'It looks like you are using around ',4./3.*pi*(2.*hfact)**3,' neighbours,', & 'corresponding to h = ',hfact,'*(m/rho)^(1/',ndim,') in 3D:' if (hfact.lt.1.15) then print "(4(/,3x,a))",'This is a quite a low number of neighbours for the cubic spline and ', & 'may result in increased noise and inaccurate wave propagation speeds', & '(a cubic lattice is also an unstable initial configuration for the ',& ' particles in this regime -- see Morris 1996, Borve et al. 2004).' elseif (hfact.gt.1.45) then print "(4(/,3x,a))",'Using h >~ 1.5*(m/rho)^(1/3) with the cubic spline results in the', & 'particle pairing instability due to the first neighbour being placed under', & 'the hump in the kernel gradient. Whilst not fatal, it results in a', & 'loss of resolution so is a bit of a waste of cpu time.' print "(4(/,3x,a))",'If you are attempting to perform a "resolution study" by increasing the', & 'neighbour number, this is a *bad idea*, as you are also increasing h.', & '(a better way is to increase the smoothness of the integrals without changing h', & ' by adopting a smoother kernel such as the M5 Quintic that goes to 3h).' endif print "(/,3x,a,/,3x,a,/)", & 'A good default range is h = 1.2-1.3 (m/rho)^1/ndim ', & 'corresponding to around 58-75 neighbours in 3D.' else print "(/,1x,a,f5.1,a,/,1x,a,f5.2,a,i1,a,/)", & 'Simulations employ ',4./3.*pi*(2.*hfact)**3,' neighbours,', & 'corresponding to h = ',hfact,'*(m/rho)^(1/',ndim,') in 3D' endif endif else !print*,'not true' endif ! !--cover the special case where no particles have been read ! if (ntotall.le.0) then npartoftype(1,i) = 1 dat(:,:,i) = 0. endif if (nstepsread.gt.0) then print*,'>> last step ntot =',sum(npartoftype(:,istepstart+nstepsread-1)) endif return contains !!----------------------------------------------------------------- !! small utility to transparently handle block labelled data read !!----------------------------------------------------------------- subroutine read_blockheader(idumpfmt,lun,nexpected,ndumped,blklabel,lenblk,nvec) implicit none integer, intent(in) :: idumpfmt,lun,nexpected integer, intent(out) :: ndumped character(len=4), intent(out) :: blklabel integer, intent(out) :: lenblk integer, intent(out) :: nvec blklabel = ' ' if (idumpfmt.eq.2) then read(lun, iostat=ierr) blklabel,lenblk if (ierr /= 0) then ndumped = 0 return endif if (blklabel.eq.'POS ' .OR. blklabel.eq.'VEL ' .OR. blklabel.eq.'ACCE' .OR. blklabel.eq.'BFLD' .OR. & blklabel.eq.'BPOL' .OR. blklabel.eq.'BTOR') then ndumped = (lenblk-8)/12 nvec = 3 else ndumped = (lenblk-8)/4 nvec = 1 endif !print*,blklabel,lenblk,ndumped !if (nexpected.gt.0) then ! if (ndumped.ne.nexpected) then ! !print*,'warning: number of '//blklabel//' dumped (',ndumped,') /= expected (',nexpected,')' ! endif !endif else ndumped = nexpected endif return end subroutine read_blockheader end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass, & ih,irho,ipr,iutherm,iBfirst,iBpol,iBtor,idivB,iax use params use settings_data, only:ndim,ndimV,ncolumns,ntypes,UseTypeInRenderings,iformat use geometry, only:labelcoord use system_utils, only:envlist,ienvironment use gadgetread, only:hsoft,blocklabelgas use asciiutils, only:lcase implicit none integer :: i,nextracols,nstarcols,icol,ihset character(len=30), dimension(10) :: labelextra if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif if (iformat.eq.2) then icol = 0 do i=1,size(blocklabelgas) icol = icol + 1 select case(blocklabelgas(i)) case('POS ') ix(1) = icol ix(2) = icol+1 ix(3) = icol+2 case('VEL ') ivx = icol case('ACCE') iax = icol case('BFLD') iBfirst = icol case('BPOL') iBpol = icol case('BTOR') iBtor = icol case('MASS') ipmass = icol case('U ') iutherm = icol case('RHO ') irho = icol case('NE ') label(icol) = 'N\de\u' case('NH ') label(icol) = 'N\dH\u' case('HSML') ih = icol case('NHP ') label(icol) = 'N\dH+\u' case('NHE ') label(icol) = 'N\dHe\u' case('NHEP') label(icol) = 'N\dHe+\u' case('elec') label(icol) = 'N\de\u' case('HI ') label(icol) = 'HI' case('HII ') label(icol) = 'HII' case('HeI ') label(icol) = 'HeI' case('HeII') label(icol) = 'HeII' case('H2I ') label(icol) = 'H\d2\uI' case('H2II') label(icol) = 'H\d2\uII' case('HM ') label(icol) = 'HM' case('SFR ') label(icol) = 'Star formation rate' case('TEMP') label(icol) = 'temperature' case('POT ') label(icol) = 'potential' case('AGE ') label(icol) = 'Stellar formation time' case('Z ') label(icol) = 'Metallicity' case('ENDT') label(icol) = 'd(Entropy)/dt' case('STRD') label(icol) = 'Stress (diagonal)' case('STRO') label(icol) = 'Stress (off-diagonal)' case('STRB') label(icol) = 'Stress (bulk)' case('SHCO') label(icol) = 'Shear coefficient' case('TSTP') label(icol) = 'Time step' case('DBDT') label(icol) = 'dB/dt' case('DIVB') label(icol) = 'div B' idivB = icol case('ABVC') label(icol) = 'alpha\dvisc\u' case('AMDC') label(icol) = 'alpha\dresist\u' case('PHI ') label(icol) = 'div B cleaning function' case('COOR') label(icol) = 'Cooling Rate' case('CONR') label(icol) = 'Conduction Rate' case('BFSM') label(icol) = 'B\dsmooth\u' case('DENN') label(icol) = 'Denn' case('CRC0') label(icol) = 'Cosmic Ray C0' case('CRP0') label(icol) = 'Cosmic Ray P0' case('CRE0') label(icol) = 'Cosmic Ray E0' case('CRn0') label(icol) = 'Cosmic Ray n0' case('CRco') label(icol) = 'Cosmic Ray Thermalization Time' case('CRdi') label(icol) = 'Cosmic Ray Dissipation Time' case('BHMA') label(icol) = 'Black hole mass' case('BHMD') label(icol) = 'black hole mass accretion rate' case('MACH') label(icol) = 'Mach number' case('DTEG') label(icol) = 'dt (energy)' case('PSDE') label(icol) = 'Pre-shock density' case('PSEN') label(icol) = 'Pre-shock energy' case('PSXC') label(icol) = 'Pre-shock X\d\u' case('DJMP') label(icol) = 'Density jump' case('EJMP') label(icol) = 'Energy jump' case('CRDE') label(icol) = 'Cosmic Ray injection' case('PRES') label(icol) = 'pressure' case('ID ') icol = icol - 1 case default label(icol) = trim(lcase(blocklabelgas(i))) end select enddo else do i=1,ndim ix(i) = i enddo ivx = 4 ipmass = 7 irho = 9 ! location of rho in data array ipr = 0 iutherm = 8 ! thermal energy if (iformat.eq.1 .or. iformat.eq.11 .and. ncolumns.gt.10) then label(10) = 'Ne' label(11) = 'Nh' ih = 12 ! smoothing length if (iformat.eq.11) label(13) = 'Star formation rate' else ih = 10 if (iformat.eq.1) label(11) = 'Star formation rate' endif ihset = ienvironment('GSPLASH_HSML_COLUMN',errval=-1) if (ihset.gt.0) ih = ihset ! !--deal with extra columns ! if (ncolumns.gt.ih) then call envlist('GSPLASH_EXTRACOLS',nextracols,labelextra) do i=ih+1,ih+nextracols label(i) = trim(labelextra(i-ih)) enddo call envlist('GSPLASH_STARPARTCOLS',nstarcols,labelextra) do i=ih+nextracols+1,ih+nextracols+nstarcols label(i) = trim(labelextra(i-ih-nextracols)) enddo endif endif ! !--set labels of the quantities read in ! if (ix(1).gt.0) label(ix(1:ndim)) = labelcoord(1:ndim,1) if (irho.gt.0) label(irho) = 'density' if (iutherm.gt.0) label(iutherm) = 'u' if (ipmass.gt.0) label(ipmass) = 'particle mass' if (ih.gt.0) label(ih) = 'h' ! !--set labels for vector quantities ! if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo endif if (iax.gt.0) then iamvec(iax:iax+ndimV-1) = iax labelvec(iax:iax+ndimV-1) = 'a' do i=1,ndimV label(iax+i-1) = trim(labelvec(iax))//'\d'//labelcoord(i,1) enddo endif if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'\d'//labelcoord(i,1) enddo endif if (iBpol.gt.0) then iamvec(iBpol:iBpol+ndimV-1) = iBpol labelvec(iBpol:iBpol+ndimV-1) = 'B\dpol' do i=1,ndimV label(iBpol+i-1) = trim(labelvec(iBpol))//'\d'//labelcoord(i,1) enddo endif if (iBtor.gt.0) then iamvec(iBtor:iBtor+ndimV-1) = iBtor labelvec(iBtor:iBtor+ndimV-1) = 'B\dtor' do i=1,ndimV label(iBtor+i-1) = trim(labelvec(iBtor))//'\d'//labelcoord(i,1) enddo endif !--set labels for each particle type ! ntypes = 6 labeltype(1) = 'gas' labeltype(2) = 'dark matter' labeltype(3) = 'boundary 1' labeltype(4) = 'boundary 2' labeltype(5) = 'star' labeltype(6) = 'sink / black hole' UseTypeInRenderings(1) = .true. ! !--dark matter particles are of non-SPH type (ie. cannot be used in renderings) ! unless they have had a smoothing length defined ! if (hsoft.gt.tiny(hsoft)) then UseTypeInRenderings(2) = .true. else UseTypeInRenderings(2) = .false. endif UseTypeInRenderings(3:6) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/discplot.f90000644 000766 000000 00000017471 13261626263 015726 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! module handling plotting of azimuthally-averaged quantities ! for disc simulations !----------------------------------------------------------------- module disc implicit none integer, parameter, private :: maxbins = 1001 integer, dimension(maxbins), private :: ninbin real, dimension(maxbins), private :: radius,sigma,spsound integer, private :: nbins public :: disccalc,discplot private contains subroutine disccalc(iplot,npart,rpart,npmass,pmass,unit_mass,unit_r,unit_dz,rminin,rmaxin,ymin,ymax,& itransx,itransy,icolourpart,iamtype,usetype,noftype,gamma,unit_u,u,u_is_spsound) use transforms, only:transform_limits_inverse,transform_inverse,transform use params, only:int1,maxparttypes,doub_prec use part_utils, only:igettype implicit none integer, intent(in) :: iplot,npart,npmass,itransx,itransy real, dimension(npart), intent(in) :: rpart real, dimension(npmass), intent(in) :: pmass real(doub_prec), intent(in) :: unit_mass,unit_r,unit_dz real, intent(in) :: rminin,rmaxin,gamma real, intent(out) :: ymin,ymax integer, dimension(npart), intent(in) :: icolourpart integer(kind=int1), dimension(:), intent(in) :: iamtype logical, dimension(maxparttypes), intent(in) :: usetype integer, dimension(maxparttypes), intent(in) :: noftype real(doub_prec), intent(in), optional :: unit_u real, dimension(npart), intent(in), optional :: u logical, intent(in), optional :: u_is_spsound integer :: i,ibin real, parameter :: pi = 3.1415926536 real :: pmassi,rbin,deltar,area,rmin,rmax real(doub_prec) :: sigmai,toomreq,epicyclic,Omegai,spsoundi,unit_cs2 real, dimension(1) :: rad logical :: mixedtypes,gotspsound integer :: itype,np ninbin(:) = 0 sigma(:) = 0. spsound(:) = 0. pmassi = 0 if (npmass.le.0) then print*,' INTERNAL ERROR in discplot: dimension of mass array <= 0' return endif gotspsound = .false. if (present(u_is_spsound)) gotspsound = u_is_spsound if (present(unit_u)) then unit_cs2 = unit_u else unit_cs2 = 1.d0 endif ! !--print info ! select case(iplot) case(1) print "(a,i4,a)",' calculating disc surface density profile using',nbins,' bins' case(2) if (present(u)) then print "(a)",' calculating Toomre Q parameter (assuming Mstar=1 and a Keplerian rotation profile)' if (.not.gotspsound) then if (gamma.lt.1.00001) then print "(a)",' isothermal equation of state: using cs^2 = 2/3*utherm' else print "(a,f6.3,a,f6.3,a)",' ideal gas equation of state: using cs^2 = ',gamma*(gamma-1),'*u (gamma = ',gamma,')' endif endif else print "(a)",' ERROR: cannot calculate Toomre Q parameter: thermal energy/sound speed not present in dump file' return endif case default print "(a)",' ERROR: unknown plot in discplot. ' return end select ! !--if transformations (e.g. log) are applied to r, then limits ! will already be set in transformed space - need to obtain ! limits in non-transformed space. ! rmin = rminin rmax = rmaxin if (itransx.gt.0) call transform_limits_inverse(rmin,rmax,itransx) ! !--try to get appropriate value for nbins ! nbins = min(4*int(npart**(1./3.)) + 1,maxbins) ! !--set array of radius values for plotting ! deltar = (rmax - rmin)/(nbins - 1) do ibin=1,nbins radius(ibin) = rmin + (ibin-0.5)*deltar enddo mixedtypes = size(iamtype).ge.npart ! !--calculate surface density in each radial bin ! np = 0 !$omp parallel do default(none) & !$omp shared(npart,rpart,sigma,npmass,pmass,itransx,icolourpart,rmin,deltar,nbins) & !$omp shared(ninbin,spsound,gamma,u,iamtype,mixedtypes,usetype,noftype,gotspsound,unit_cs2) & !$omp private(i,rad,pmassi,ibin,rbin,area,itype) & !$omp reduction(+:np) over_parts: do i=1,npart !--skip particles with itype < 0 if (icolourpart(i) < 0) cycle over_parts if (mixedtypes) then itype = int(iamtype(i)) else itype = igettype(i,noftype) endif if (.not.usetype(itype)) cycle over_parts np = np + 1 if (itransx.eq.0) then rad(1) = rpart(i) else rad(1) = rpart(i) call transform_inverse(rad,itransx) endif if (npmass.ge.npart) then pmassi = pmass(i) else pmassi = pmass(1) endif ibin = int((rad(1) - rmin)/deltar) + 1 if (ibin.gt.0 .and. ibin.le.nbins) then rbin = rmin + (ibin-0.5)*deltar area = pi*((rbin + 0.5*deltar)**2 - (rbin - 0.5*deltar)**2) !$omp atomic sigma(ibin) = sigma(ibin) + pmassi/area if (present(u)) then if (gotspsound) then !$omp atomic spsound(ibin) = spsound(ibin) + real((u(i))**2/unit_cs2) else if (gamma.lt.1.00001) then !$omp atomic spsound(ibin) = spsound(ibin) + real(2./3.*(u(i)/unit_cs2)) else !$omp atomic spsound(ibin) = spsound(ibin) + real(gamma*(gamma-1.)*(u(i)/unit_cs2)) endif endif !$omp atomic ninbin(ibin) = ninbin(ibin) + 1 endif endif enddo over_parts !$omp end parallel do print "(1x,a,i10,a,i10,a,i4,a)",'used ',np,' of ',npart,' particles in ',nbins,' bins' ! !--calculate Toomre Q parameter in each bin using surface density ! if (iplot.eq.2) then epicyclic = 0. do ibin=1,nbins sigmai = sigma(ibin)*(unit_r**2/unit_mass) ! convert back to code units ! !--for Toomre Q need the epicyclic frequency ! in a Keplerian disc kappa = Omega ! Omegai = sqrt(1./(radius(ibin)/unit_r)**3) epicyclic = Omegai ! !--spsound is RMS sound speed for all particles in the annulus ! if (ninbin(ibin).gt.0) then spsoundi = sqrt(spsound(ibin)/real(ninbin(ibin))) ! unit conversion already done else spsoundi = 0. endif ! !--now calculate Toomre Q ! if (sigmai > 0.) then toomreq = spsoundi*epicyclic/(pi*sigmai) else toomreq = 0. endif sigma(ibin) = real(toomreq,kind=kind(sigma)) enddo else ! !--return surface density in units of [g/cm^2], not [g/cm^3 au] ! sigma = sigma*(unit_r/unit_dz)**2 endif sigma(1:nbins) = max(sigma(1:nbins),epsilon(0.)) if (itransx.gt.0) call transform(radius,itransx) if (itransy.gt.0) call transform(sigma,itransy) ! !--return min and max of y axis so adaptive plot limits can be set ! ymin = minval(sigma(1:nbins),mask=(sigma(1:nbins).ne.0.)) ymax = maxval(sigma(1:nbins),mask=(sigma(1:nbins).ne.0.)) return end subroutine disccalc !--------------------------------------------------- ! ! subroutine to actually perform the disc plotting ! !--------------------------------------------------- subroutine discplot use plotlib, only:plot_line implicit none call plot_line(nbins,radius,sigma) end subroutine discplot end module disc splash/src/options_powerspec.f90000644 000766 000000 00000010051 13261626263 017652 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2010 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing settings and options relating to power spectrum ! and Probability Distribution Function plots ! includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module settings_powerspec implicit none integer :: ipowerspecy, ipowerspecx, nfreqspec integer :: nwavelengths,npdfbins logical :: idisordered real :: freqmax,freqmin namelist /powerspecopts/ ipowerspecy,idisordered,nwavelengths,nfreqspec,npdfbins,freqmin,freqmax contains !--------------------------------------------- ! set default values for these options !--------------------------------------------- subroutine defaults_set_powerspec use settings_data, only:ndim implicit none idisordered = .true. ipowerspecy = max(ndim+1,2) ipowerspecx = 0 ! reset later nwavelengths = 128 freqmin = 1.0 freqmax = nwavelengths*freqmin nfreqspec = 1 npdfbins = 0 return end subroutine defaults_set_powerspec !---------------------------------------------------------------------- ! sets options and parameters for power spectrum calculation/plotting !---------------------------------------------------------------------- subroutine options_powerspec use settings_data, only:ndim,ndataplots,numplot use limits, only:lim use labels, only:ipowerspec use prompting, only:prompt implicit none real :: boxsize if (ipowerspecy.lt.ndim+1) ipowerspecy = ndim+1 if (ipowerspecy.gt.ndataplots) ipowerspecy = ndataplots call prompt('enter data to take power spectrum of',ipowerspecy,ndim+1,ndataplots) if (ipowerspecx.ne.1) then if (ipowerspecx.lt.1) ipowerspecx = 1 if (ipowerspecx.gt.ndataplots) ipowerspecx = ndataplots call prompt('enter column to use as "time" or "space"',ipowerspecx,1,ndataplots) endif ! !--if box size has not been set then use x limits ! if (abs(freqmin-1.0).lt.tiny(1.)) then boxsize = abs(lim(1,2) - lim(1,1)) if (boxsize.gt.tiny(boxsize)) freqmin = 1./boxsize endif call prompt('enter min frequency (default=1/box size)',freqmin,0.0) call prompt('enter max frequency ',freqmax,min=freqmin) if (ipowerspec.le.ndataplots .or. ipowerspec.gt.numplot) then !--this should never happen print*,'*** ERROR: something wrong in powerspectrum limit setting' else print*,' wavelength range ',1./freqmax,'->',1./freqmin lim(ipowerspec,1) = freqmin lim(ipowerspec,2) = freqmax print*,' frequency range ',lim(ipowerspec,1),'->',lim(ipowerspec,2) if (nfreqspec.le.1) nfreqspec = 2*nwavelengths call prompt('how many frequency points between these limits? ',nfreqspec,nwavelengths) endif !! call prompt('use Lomb periodogram? (no=interpolate and fourier) ',idisordered) return end subroutine options_powerspec !----------------------------------------------------------------- ! ! settings for PDF calculation ! !----------------------------------------------------------------- subroutine options_pdf use prompting, only:prompt implicit none call prompt(' Enter number of bins between min and max of plot (0=auto)',npdfbins,0) end subroutine options_pdf end module settings_powerspec splash/src/options_page.f90000644 000766 000000 00000066607 13261626263 016601 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing page settings and options ! includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module settings_page use settings_limits, only:iadapt,iadaptcoords,adjustlimitstodevice,xminoffset_track,xmaxoffset_track use labels, only:lenlabel implicit none integer :: iaxis,nacross,ndown,ipapersize,nstepsperpage,linewidth,iscalepanel,linepalette integer :: iPlotLegendOnlyOnPanel,modlinestyle,modcolour,maxlinestyle,maxcolour integer :: iPageColours,ipapersizeunits logical :: iColourEachStep,iChangeStyles,tile,interactive,nomenu logical :: iPlotLegend,iPlotStepLegend,iPlotTitles,usecolumnorder logical :: iPlotScale,iUseBackgroundColourForAxes,usesquarexy real :: papersizex,aspectratio real :: hposlegend,vposlegend,fjustlegend,hpostitle,vpostitle,fjusttitle real :: charheight,alphalegend real :: dxscale,hposscale,vposscale,yscalealt real :: xminpagemargin,xmaxpagemargin,yminpagemargin,ymaxpagemargin character(len=lenlabel) :: legendtext, scaletext character(len=60) :: device character(len=lenlabel) :: labelyalt integer, parameter :: maxc = 16 real :: colourpalette(3,maxc) namelist /pageopts/ iaxis,nacross,ndown,interactive,iadapt,iadaptcoords, & nstepsperpage,iColourEachStep,iChangeStyles,tile,ipapersize,papersizex,aspectratio, & iPlotLegend,iPlotStepLegend,hposlegend,vposlegend,iPlotTitles,hpostitle, & vpostitle,fjusttitle,legendtext,iPageColours,charheight,linewidth,& fjustlegend,iPlotLegendOnlyOnPanel, & iPlotScale,dxscale,scaletext,hposscale,vposscale,iscalepanel,iUseBackgroundColourForAxes, & usesquarexy,maxlinestyle,modlinestyle,maxcolour,modcolour,usecolumnorder,ipapersizeunits,& adjustlimitstodevice,alphalegend,yscalealt,labelyalt,xminoffset_track,xmaxoffset_track, & xminpagemargin,xmaxpagemargin,yminpagemargin,ymaxpagemargin,linepalette contains !--------------------------------------------- ! set default values for these options !--------------------------------------------- subroutine defaults_set_page use shapes, only:defaults_set_shapes use plotlib, only:plotlib_maxlinecolour interactive = .true. ! default for interactive mode iaxis = 0 ! turns axes off/on nstepsperpage = 1 iColourEachStep = .true. ! change colours if nstepsperpage > 1 iChangeStyles = .false. ! change marker/ line styles if nstepsperpage > 1 tile = .true. usecolumnorder = .true. nacross = 1 ! number of plots across page ndown = 1 ! number of plots down page ipapersize = 0 ! paper size option papersizex = 0.0 ! size of x paper (no call to PGPAP if zero) aspectratio = 0.0 ! aspect ratio of paper (no call to PGPAP if zero) ipapersizeunits = 1 ! units in which the paper size is set iPlotLegend = .true. ! whether or not to plot legend iPlotStepLegend = .false. ! timestep legend hposlegend = 0.95 ! horizontal legend position as fraction of viewport vposlegend = 2.0 ! vertical legend position in character heights fjustlegend = 1.0 ! justification factor for legend alphalegend = 0.5 ! transparency of overlaid annotation legendtext = 't=' iPlotLegendOnlyOnPanel = 0 iPlotTitles = .false. ! whether or not to plot titles hpostitle = 0.5 ! horizontal title position as fraction of viewport vpostitle = 1.0 ! vertical title position in character heights fjusttitle = 0.5 ! justification factor for title iPageColours = 0 charheight = 1.0 ! character height linewidth = 0 ! line width linepalette = 0 iPlotScale = .false. hposscale = 0.5 vposscale = 1.0 dxscale = 1.0 scaletext = '1 unit' iscalepanel = 0 maxlinestyle = 5 modlinestyle = 1 modcolour = 1 maxcolour = plotlib_maxlinecolour yscalealt = 1. labelyalt = ' ' usesquarexy = .true. ! spatial dimensions have same scale call defaults_set_shapes xminpagemargin = 0. xmaxpagemargin = 0. yminpagemargin = 0. ymaxpagemargin = 0. colourpalette = reshape((/0.,0.,0., & 0.933,0.173,0.173, & 0.18,0.545,0.341, & 0., 0., 1., & 0.192, 0.31, 0.31, & 0.58, 0., 0.827, & 0.0, 0.825, 0.825, & 1., 0.31, 0., & 0.373, 0.62, 0.627, & 0.933, 0.796, 0.678,& 0.0, 1.0, 0.5, & 0.0, 0.5, 1.0, & 0.5, 0.0, 0.0, & 1.0, 0.0, 0.5, & 0.333, 0.333, 0.333,& 0.667, 0.667, 0.667/),shape(colourpalette)) return end subroutine defaults_set_page !--------------------------------------------- ! changed default values for evsplash !--------------------------------------------- subroutine defaults_set_page_ev nstepsperpage = 1000 iColourEachStep = .true. ! change colours if nstepsperpage > 1 iChangeStyles = .true. ! change marker/ line styles if nstepsperpage > 1 iPlotLegend = .true. ! whether or not to plot legend iPlotStepLegend = .true. ! timestep legend hposlegend = 0.1 ! horizontal legend position as fraction of viewport vposlegend = 2.0 ! vertical legend position in character heights fjustlegend = 0.0 ! justification factor for legend end subroutine defaults_set_page_ev !--------------------------------------------- ! changed default values for 360 video !--------------------------------------------- subroutine defaults_set_page_360 iPageColours = 2 iaxis = -2 ipapersize = 18 ipapersizeunits = 0 papersizex = 1080. !4320. aspectratio = 0.5 iPageColours = 2 adjustlimitstodevice = .true. end subroutine defaults_set_page_360 !---------------------------------------------------------------------- ! submenu with options relating to page setup !---------------------------------------------------------------------- subroutine submenu_page(ichoose) use params, only:maxplot use prompting, only:prompt,print_logical use pagecolours, only:pagecolourscheme,colour_fore,colour_back,maxpagecolours,& write_coloursfile,read_coloursfile use plotlib, only:plotlib_supports_alpha,plotlib_maxlinecolour,plotlib_maxlinestyle,& plotlib_is_pgplot,plotlib_maxpalette use filenames, only:coloursfile integer, intent(in) :: ichoose integer :: iaction,i,iunitsprev,ierr,nc real :: papersizey,mnraslength character(len=15) :: paperfmtstr character(len=3) :: string iaction = ichoose papersizey = papersizex*aspectratio if (ipapersizeunits.gt.0) then write(paperfmtstr,"(f5.2,1x,f5.2)") papersizex,papersizey else write(paperfmtstr,"(i5,' x ',i4)") nint(papersizex),nint(papersizey) endif print "(a)",'---------------- page setup options -------------------' if (iaction.le.0 .or. iaction.gt.9) then print "( "// & "' 0) exit ',/, "// & "' 1) plot n steps on top of each other (n =',i4,')',/, "// & "' 2) axes options (',i2,')',/, "// & "' 3) change paper size ("//trim(paperfmtstr)//" )',/, "// & "' 4) subdivide page into panels (',i2,1x,'x',1x,i2,', tiling is ',a,')',/, "// & "' 5) spatial dimensions have same scale ( ',a,' )',/,"// & "' 6) set character height (',f4.1,')',/,"// & "' 7) adjust line width (',i2, ')',/,"// & "' 8) adjust page margins ( ',f4.1,' )',/,"// & "' 9) set page and line colours ( ',a,' )')", & nstepsperpage,iaxis,nacross,ndown,print_logical(tile), & trim(print_logical(usesquarexy)),charheight,linewidth,& xminpagemargin, & trim(pagecolourscheme(iPageColours,short=.true.)) call prompt('enter option ',iaction,0,9) endif select case(iaction) !------------------------------------------------------------------------ case(1) call prompt('Enter number of timesteps per panel ',nstepsperpage,0) print*,'Plotting up to ',nstepsperpage,' timesteps per panel' if (nstepsperpage.gt.1) then if (iadapt .or. iadaptcoords) then print "(a)",'(note that adaptive plot limits are now off)' iadapt = .false. iadaptcoords = .false. endif if (nstepsperpage.gt.14) then print "(a)",'(warning: steps per panel > number of colours, ie. colours will repeat)' endif call prompt('Use different colours for each step?',iColourEachStep) if (iColourEachStep) then call prompt('How often to change colour? (1=every step, 2=every 2nd step etc.)',modcolour,1) call prompt('Enter max number of colours to use before repeating (16=plot lib max)',& maxcolour,1,plotlib_maxlinecolour) endif !! if (.not.iColourEachStep) icolourthisstep = 1 call prompt('Use different markers/line style for each step? ',iChangeStyles) if (iChangeStyles) then call prompt('How often to change line style (1=every step, 2=every 2nd step etc.)',modlinestyle,1) write(string,"(i3)",iostat=ierr) plotlib_maxlinestyle call prompt('Enter max number of line styles to cycle through before repeating ('// & trim(adjustl(string))//'=plot lib max)',maxlinestyle,1,plotlib_maxlinestyle) endif if (iColourEachStep .or. iChangeStyles) then print "(/,a,/,a)",' (to change the legend text, create a file called', & ' ''legend'' in the working directory, with one label per line)' call prompt('Plot legend of marker styles/colours?',iPlotStepLegend) endif endif return !------------------------------------------------------------------------ case(2) print*,'-4 : draw box and major tick marks only;' print*,'-3 : draw box and tick marks (major and minor) only;' print*,'-2 : draw no box, axes or labels;' print*,'-1 : draw box only;' print*,' 0 : draw box and label it with coordinates;' print*,' 1 : same as AXIS=0, but also draw the coordinate axes (X=0, Y=0);' print*,' 2 : same as AXIS=1, but also draw grid lines at major increments of the coordinates;' print*,' 3 : draw box, ticks and numbers but no axes labels;' print*,' 4 : same as AXIS=0, but with a second y-axis scaled and labelled differently' print*,' 5 : draw box, major ticks and numbers but no axes labels;' print*,'10 : draw box and label X-axis logarithmically;' print*,'20 : draw box and label Y-axis logarithmically;' print*,'30 : draw box and label both axes logarithmically.' call prompt('enter axis option ',iaxis,-4,30) if (iaxis.eq.4) then call prompt('enter scale factor for alternative y axis',yscalealt,0.) call prompt('enter label for alternative y axis',labelyalt) endif print *,' axis = ',iaxis return !------------------------------------------------------------------------ case(3) print*,' 0) plotting library default ' print*,' 1) small square : 2.92 x 2.92 inches' print*,' 2) medium square : 5.85 x 5.85 inches' print*,' 3) large square : 8.00 x 8.00 inches' print*,' 4) single small graph : 5.85 x 4.13 inches' print*,' 5) duo small graph : 11.70 x 4.13 inches' print*,' 6) duo graph : 11.70 x 6.00 inches' if (plotlib_is_pgplot) then print*,' 7) Custom size ' call prompt(' Enter option for paper size ',ipapersize,0,7) else print*,' 7) 800 x 600 pixels' print*,' 8) 640 x 360 pixels (360p)' print*,' 9) 1280 x 720 pixels (720p)' print*,'10) 1920 x 1080 pixels (1080p/Full HD)' print*,'11) 1024 x 768 pixels' print*,'12) 1440 x 900 pixels' print*,'13) 2560 x 1440 pixels' print*,'14) 2560 x 1600 pixels' print*,'15) 3840 x 2160 pixels (4KTV/Ultra HD)' print*,'16) 4096 x 2160 pixels (Cinema 4K)' print*,'17) 5120 x 2880 pixels (5K)' print*,'18) 4320 x 2160 pixels (2160s)' print*,'19) 8640 x 4320 pixels (4320s)' print*,'20) 27320 x 3072 pixels (CAVE-2)' print*,'21) 1/3 of A4 journal page 79mm x 180 mm' print*,'22) 1/2 of A4 journal page 118mm x 180 mm' print*,'23) full A4 journal page 236mm x 180 mm' print*,'24) Custom size ' call prompt(' Enter option for paper size ',ipapersize,0,24) endif select case(ipapersize) case(1) ipapersizeunits = 1 papersizex = 0.25*11.7 aspectratio = 1.0 case(2) ipapersizeunits = 1 papersizex = 0.5*11.7 aspectratio = 1.0 case(3) ipapersizeunits = 1 papersizex = 8.0 aspectratio = 1.0 case(4) ipapersizeunits = 1 papersizex = 0.5*11.7 aspectratio = 1./sqrt(2.) case(5) ipapersizeunits = 1 papersizex = 11.7 aspectratio = 0.5/sqrt(2.) case(6) ipapersizeunits = 1 papersizex = 11.7 papersizey = 6.0 aspectratio = papersizey/papersizex case(7) if (plotlib_is_pgplot) then ipapersizeunits = 1 call prompt(' x size (inches) ',papersizex,0.0) call prompt(' y size (inches) or aspect ratio (-ve)',papersizey) if (papersizey.lt.0.0) then aspectratio = abs(papersizey) else aspectratio = papersizey/papersizex endif else ipapersizeunits = 0 papersizex = 800. papersizey = 600. aspectratio = papersizey/papersizex endif case(8:23) if (plotlib_is_pgplot) then ipapersizeunits = 1 papersizex = 0. ! use PGPLOT default aspectratio = 0. else ipapersizeunits = 0 mnraslength = 56.*0.42175 ! 56pc = 672pts converted to cm select case(ipapersize) case(8) papersizex = 640. papersizey = 360. case(9) papersizex = 1280. papersizey = 720. case(10) papersizex = 1920. papersizey = 1080. case(11) papersizex = 1024. papersizey = 768. case(12) papersizex = 1440. papersizey = 900. case(13) papersizex = 2560. papersizey = 1440. case(14) papersizex = 2560. papersizey = 1600. case(15) papersizex = 3840. papersizey = 2160. case(16) papersizex = 4096. papersizey = 2160. case(17) papersizex = 5120. papersizey = 2880. case(18) papersizex = 4320. papersizey = 2160. case(19) papersizex = 8640. papersizey = 4320. case(20) papersizex = 27320. papersizey = 3072. case(21) ! 1/3 of MNRAS page ipapersizeunits = 2 papersizex = 18. papersizey = mnraslength/3. case(22) ! 1/2 of MNRAS page ipapersizeunits = 2 papersizex = 18. papersizey = 0.5*mnraslength case(23) ! full MNRAS page, allowing 2cm for caption ipapersizeunits = 2 papersizex = 18. papersizey = mnraslength - 2. end select aspectratio = papersizey/papersizex endif case(24) if (plotlib_is_pgplot) then ipapersizeunits = 1 papersizex = 0. ! use PGPLOT default aspectratio = 0. else iunitsprev = ipapersizeunits print*,' 0) pixels ' print*,' 1) inches ' print*,' 2) cm ' call prompt(' choose units for paper size',ipapersizeunits,0,2) if (ipapersizeunits.ne.iunitsprev) then select case(ipapersizeunits) case(2) papersizex = 29.7 papersizey = 21.0 case(1) papersizex = 11.0 papersizey = 8.5 case(0) papersizex = 800. papersizey = -0.75 case default papersizex = 0. papersizey = 0. end select endif call prompt(' x size in above units ',papersizex,1.) call prompt(' y size or aspect ratio (-ve)',papersizey) if (papersizey.lt.0.0) then aspectratio = abs(papersizey) else aspectratio = papersizey/papersizex endif endif case default ipapersizeunits = 1 papersizex = 0.0 ! no call to PGPAP if they are zero aspectratio = 0.0 end select call prompt('Adjust plot limits to match device aspect ratio?',adjustlimitstodevice) return !------------------------------------------------------------------------ case(4) call prompt('Enter number of plots across (columns):',nacross,1,maxplot) call prompt('Enter number of plots down (rows):',ndown,1,maxplot/nacross) if (nacross*ndown.gt.1) then call prompt('Tile plots on the page where possible?',tile) call prompt('Plot panels across-then-down? (no=down-then-across)',usecolumnorder) endif return !------------------------------------------------------------------------ case(5) usesquarexy = .not.usesquarexy print "(a)",' Same scale for spatial dimensions is '//print_logical(usesquarexy) !------------------------------------------------------------------------ case(6) call prompt('Enter character height ',charheight,0.1,10.) return !------------------------------------------------------------------------ case(7) print "(3(/,a))",' Setting line width to 0 means automatic line width choice:', & ' This gives width = 2 for vector devices (/ps,/cps etc)', & ' and width = 1 elsewhere (e.g. for pixel devices)' print* call prompt('Enter line width (0=auto)',linewidth,0) return !------------------------------------------------------------------------ case(8) call prompt('Enter xmin page margin as fraction of viewport ',xminpagemargin,-1.,1.) call prompt('Enter xmax page margin as fraction of viewport ',xmaxpagemargin,-1.,1.) call prompt('Enter ymin page margin as fraction of viewport ',yminpagemargin,-1.,1.) call prompt('Enter ymax page margin as fraction of viewport ',ymaxpagemargin,-1.,1.) !interactive = .not.interactive !print "(a)",' Interactive mode is '//print_logical(interactive) !------------------------------------------------------------------------ case(9) print "(3(/,i1,')',1x,a))",(i,pagecolourscheme(i),i=0,maxpagecolours) call prompt(' Choose page colour scheme ',iPageColours,0,maxpagecolours) write(*,"(3(/,a))",advance='no') & ' Overlaid (that is, drawn inside the plot borders) axis ',& ' ticks, legend text and titles are by default plotted ', & ' in the foreground colour' if (iPageColours.gt.0) then print "(a,/)",' [i.e. '//trim(colour_fore(iPageColours))//'].' call prompt('Do you want to plot these in background colour [i.e. '& //trim(colour_back(iPageColours))//'] instead?',& iUseBackgroundColourForAxes) else print "(a,/)",'.' call prompt('Use background colour for these? ',iUseBackgroundColourForAxes) endif if (iUseBackgroundColourForAxes .and. plotlib_supports_alpha) then call prompt('Enter opacity for overlaid text and annotation ',alphalegend,0.0,1.0) endif print "(9(/,a))",& '-1: custom', & ' 0: giza default line palette ', & ' 1: default PGPLOT line palette', & ' 2: modified Tricco palette', & ' 3: cheer up emo kid / grandma''s pillow', & ' 4: outback desert moonscape',& ' 5: colourblind safe line palette', & ' 6: optimum line palette', & ' 7: graph-a-licious' call prompt('Enter line palette',linepalette,-1,plotlib_maxpalette) if (linepalette < 0) then call read_coloursfile(coloursfile,maxc,colourpalette,nc,ierr) if (ierr /= 0 .or. nc <= 0) then print*,' writing colours to file '//trim(coloursfile) call write_coloursfile(trim(coloursfile),maxc,colourpalette) else print*,' colours written to file '//trim(coloursfile) endif print*,' edit this file to change the line palette' print*,' ** press any key to continue **' read* endif return end select return end subroutine submenu_page !---------------------------------------------------------------------- ! submenu with options relating to legend and title settings !---------------------------------------------------------------------- subroutine submenu_legend(ichoose) use filenames, only:fileprefix use prompting, only:prompt,print_logical use shapes, only:nshapes,labelshapetype,shape,submenu_shapes use legends, only:prompt_panelselect integer, intent(in) :: ichoose integer :: iaction,i,ierr,i1,i2 character(len=50) :: string iaction = ichoose print "(a)",'---------------- legend and title options -------------------' if (iPlotStepLegend) then print "(/,a,/,a,/)",' Hint: to change the step legend text, create a file called', & ' '''//trim(fileprefix)//'.legend'' in the working directory, with one label per line' endif if (iPlotTitles) then if (.not.iPlotStepLegend) print "(a)" print "(a,/,a,/)",' To set the plot titles, create a file called', & ' '''//trim(fileprefix)//'.titles'' in the working directory, with one title per line' endif if (iaction.le.0 .or. iaction.gt.6) then !--format shape settings string if (nshapes.gt.0) then i2 = 2 string = ': ' else i2 = 0 string = ' ' endif do i=1,nshapes i1 = i2 + 1 i2 = min(i1 + len_trim(labelshapetype(shape(i)%itype)),len(string)) write(string(i1:i2),"(a)",iostat=ierr) trim(labelshapetype(shape(i)%itype)(1:i2-i1)) if (i.lt.nshapes .and. i2.lt.len(string)) then write(string(i2:i2+1),"(', ')",iostat=ierr) i2 = i2 + 1 endif enddo !--print menu print 20,print_logical(iPlotLegend),hposlegend,vposlegend,fjustlegend,trim(legendtext), & print_logical(iPlotTitles),hpostitle,vpostitle,fjusttitle, & print_logical(iPlotStepLegend), print_logical(iPlotScale),iPlotLegendOnlyOnPanel, & nshapes,trim(string) 20 format(' 0) exit ',/, & ' 1) time legend on/off/settings (',1x,a,1x,f5.2,1x,f5.2,1x,f5.2,1x,'"',a,'")',/, & ' 2) titles on/off/settings (',1x,a,1x,f5.2,1x,f5.2,1x,f5.2,')',/, & ' 3) legend for multiple steps per page on/off (',1x,a,1x,')',/, & ' 4) plot scale |---| on coordinate plots (',1x,a,1x,')',/, & ' 5) legend only on nth panel/first row/column (',1x,i2,1x,')',/, & ' 6) annotate plot (e.g. arrow,square,circle,text) (',1x,i2,a,')') iaction = 0 call prompt(' Enter option ',iaction,0,6) endif select case(iaction) case(1) call prompt('Plot time legend? ',iPlotLegend) print "(a)",'Time legend is '//print_logical(iPlotLegend) if (iPlotLegend) then print "(7(/,a),/)", & ' Example format strings: ', & ' t = : this is the default format "t = 0.1 years"', & ' t = %t.5 : with time to 5 significant figures', & ' Time: %t dog-%ut : gives "Time: 0.1 dog-years"', & ' %(t + 2013) : prints time offset by 2013', & ' %(t + 2013).5 : as above, to 5 sig. figs.', & ' %(t*100) : multiplied by 100' call prompt('Enter legend text ',legendtext) print "(a)",'------ set legend position (can also be done interactively) --------' call prompt('Enter horizontal position as fraction of viewport', & hposlegend,0.0,1.0) call prompt('Enter vertical position in character heights from top',vposlegend) call prompt('Enter justification factor (0.0=left 1.0=right)',fjustlegend,0.0,1.0) call prompt_panelselect('legend',iPlotLegendOnlyOnPanel) endif case(2) print "(/,a,/,a,/)",' To set the plot titles, create a file called', & ' '''//trim(fileprefix)//'.titles'' in the working directory, with one title per line' call prompt('Use plot titles? ',iPlotTitles) print "(a)",'Titles are '//print_logical(iPlotTitles) if (iPlotTitles) then print "(a)",'------ set title position (can also be done interactively) --------' call prompt('Enter horizontal position as fraction of viewport', & hpostitle,0.0,1.0) call prompt('Enter vertical position in character heights above top',vpostitle) call prompt('Enter justification factor (0.0=left 1.0=right)',fjusttitle,0.0,1.0) endif case(3) iPlotStepLegend = .not.iPlotStepLegend print "(a)",'Step legend is '//print_logical(iPlotStepLegend) if (iPlotStepLegend) then print "(/,a,/,a,/)",' Hint: to change the step legend text, create a file called', & ' '''//trim(fileprefix)//'.legend'' in the working directory, with one label per line' print*,' press return to continue ' read* endif case(4) call prompt('Plot scale on co-ordinate plots? ',iPlotScale) if (iPlotScale) then call prompt('Enter length of scale in the current x,y,z units ',dxscale) call prompt('Enter text to appear below scale (e.g. ''10 AU'')',scaletext) call prompt('Enter horizontal position as fraction of viewport', & hposscale,0.0,1.0) call prompt('Enter vertical position in character heights above bottom',vposscale) if (nacross*ndown.gt.1) then call prompt('Enter which panel on the plotting page the scale should appear on '// & '(0=all co-ordinate plots)',iscalepanel,0,nacross*ndown) endif endif case(5) call prompt_panelselect('legend',iPlotLegendOnlyOnPanel) case(6) call submenu_shapes() end select return end subroutine submenu_legend end module settings_page splash/src/units.f90000644 000766 000000 00000032206 13261626263 015240 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !-------------------------------------------------------------------- ! module containing subroutines to do with setting of physical units !-------------------------------------------------------------------- module settings_units use params use labels, only:unitslabel,labelzintegration implicit none real, dimension(0:maxplot), public :: units real, public :: unitzintegration real(doub_prec), public :: unit_interp public :: set_units,read_unitsfile,write_unitsfile,defaults_set_units public :: get_nearest_length_unit,get_nearest_time_unit private contains !--------------------------------------------------------------- ! ! initialise the units arrays to some harmless default values ! !--------------------------------------------------------------- subroutine defaults_set_units implicit none units(:) = 1.0 unitzintegration = 1.0 unit_interp = 1.0d0 unitslabel(:) = ' ' labelzintegration = ' ' return end subroutine defaults_set_units !------------------------------------------- ! ! find the nearest 'sensible' length unit ! !------------------------------------------- subroutine get_nearest_length_unit(udist,unit,unitlabel) real(doub_prec), intent(in) :: udist real(doub_prec), intent(out) :: unit character(len=*), intent(out) :: unitlabel integer, parameter :: nu = 7 real(doub_prec), parameter :: unit_length(nu) = & (/1.d0, & 1.d5, & 6.96d10, & 1.496d13,& 3.086d18,& 3.086d21,& 3.086d24/) character(len=*), parameter :: unit_labels(nu) = & (/' [cm] ',& ' [km] ',& ' [R_{Sun}]',& ' [au] ',& ' [pc] ',& ' [kpc] ',& ' [Mpc] '/) call get_nearest_unit(nu,unit_length,unit_labels,udist,unit,unitlabel) end subroutine get_nearest_length_unit !------------------------------------------- ! ! find the nearest 'sensible' time unit ! !------------------------------------------- subroutine get_nearest_time_unit(utime,unit,unitlabel) real(doub_prec), intent(in) :: utime real(doub_prec), intent(out) :: unit character(len=*), intent(out) :: unitlabel integer, parameter :: nu = 7 real(doub_prec), parameter :: units_time(nu) = & (/1.d-3, & 1.d0, & 3.6d3, & 8.64d4, & 3.15568926d7, & 3.15568926d13,& 3.15568926d16/) character(len=*), parameter :: unit_labels(nu) = & (/' ms ',& ' s ',& ' hrs ',& ' days',& ' yrs ',& ' Myr ',& ' Gyr '/) call get_nearest_unit(nu,units_time,unit_labels,utime,unit,unitlabel) end subroutine get_nearest_time_unit !------------------------------------------------------- ! ! find the nearest unit from a list of possible units ! !------------------------------------------------------- subroutine get_nearest_unit(nu,units,unit_labels,unit_in,unit_out,unitlabel_out) integer, intent(in) :: nu real(doub_prec), intent(in) :: units(nu),unit_in character(len=*), intent(in) :: unit_labels(nu) real(doub_prec), intent(out) :: unit_out character(len=*), intent(out) :: unitlabel_out real(doub_prec) :: err,erri integer :: i err = huge(err) do i = 1,nu ! find nearest unit in log space erri = abs(log10(unit_in)-log10(units(i))) if (erri < err) then unit_out = unit_in/units(i) unitlabel_out = unit_labels(i) err = erri endif enddo end subroutine get_nearest_unit !------------------------------------------------------- ! ! set units ! !------------------------------------------------------- subroutine set_units(ncolumns,numplot,UnitsHaveChanged) use prompting, only:prompt use labels, only:label,ix,ih,iamvec,labelvec use settings_data, only:ndim,ndimV implicit none integer, intent(in) :: ncolumns,numplot logical, intent(out) :: UnitsHaveChanged integer :: icol real :: unitsprev,dunits logical :: applytoall icol = 1 do while(icol.ge.0) icol = -1 call prompt('enter column to change units (-2=reset all,-1=quit,0=time)',icol,-2,numplot) if (icol.ge.0) then unitsprev = units(icol) if (icol.gt.ncolumns) then print "(a)",' WARNING: calculated quantities are automatically calculated in physical units ' print "(a)",' this means that units set here will be re-scalings of these physical values' endif if (icol.eq.0) then call prompt('enter time units (new=old*units)',units(icol)) else call prompt('enter '//trim(label(icol))//' units (new=old*units)',units(icol)) endif if (abs(units(icol)).gt.tiny(units)) then if (abs(units(icol) - unitsprev).gt.tiny(units)) UnitsHaveChanged = .true. if (len_trim(unitslabel(icol)).eq.0) then !--suggest a label amendment if none already set dunits = 1./units(icol) if (dunits.gt.100 .or. dunits.lt.1.e-1) then write(unitslabel(icol),"(1pe8.1)") dunits else write(unitslabel(icol),"(f5.1)") dunits endif unitslabel(icol) = ' [ x '//trim(adjustl(unitslabel(icol)))//' ]' endif !--label amendment can be overwritten call prompt('enter label amendment ',unitslabel(icol)) else UnitsHaveChanged = .true. units(icol) = 1.0 unitslabel(icol) = ' ' endif if (UnitsHaveChanged .and. icol.gt.0) then ! !--prompt to apply same units to coordinates and h for consistency ! if (any(ix(1:ndim).eq.icol) .or. (icol.eq.ih .and. ih.gt.0)) then applytoall = .true. !--try to make prompts apply to whichever situation we have if (ndim.eq.1) then if (icol.eq.ix(1) .and. ih.gt.0) then call prompt(' Apply these units to h?',applytoall) else call prompt(' Apply these units to '//trim(label(ix(1)))//'?',applytoall) endif elseif (any(ix(1:ndim).eq.icol) .and. ih.gt.0) then call prompt(' Apply these units to all coordinates and h?',applytoall) else call prompt(' Apply these units to all coordinates?',applytoall) endif if (applytoall) then units(ix(1:ndim)) = units(icol) unitslabel(ix(1:ndim)) = unitslabel(icol) if (ih.gt.0) then units(ih) = units(icol) unitslabel(ih) = unitslabel(icol) endif endif ! !--set units for z integration in 3D ! so for example can have x,y,z in kpc but column density in g/cm^2 ! if (abs(unitzintegration-1.0).le.tiny(unitzintegration)) then unitzintegration = units(icol) labelzintegration = unitslabel(icol) endif if (ndim.eq.3) then call prompt(' Enter unit for ''z'' in 3D column integrated plots ',unitzintegration) call prompt(' Enter label for z integration unit (e.g. [cm])',labelzintegration) endif endif ! !--also sensible to apply same units to all components of a vector ! if (ndimV.gt.1 .and. iamvec(icol).gt.0) then applytoall = .true. call prompt(' Apply these units to all components of '//trim(labelvec(icol))//'?',applytoall) if (applytoall) then where (iamvec(1:ncolumns).eq.iamvec(icol)) units(1:ncolumns) = units(icol) unitslabel(1:ncolumns) = unitslabel(icol) end where endif endif endif elseif (icol.eq.-2) then UnitsHaveChanged = .true. print "(/a)",' resetting all units to unity...' units = 1.0 unitslabel = ' ' endif print* enddo end subroutine set_units !------------------------------------------------------- ! ! save units for all columns to a file ! !------------------------------------------------------- subroutine write_unitsfile(unitsfile,ncolumns) implicit none character(len=*), intent(in) :: unitsfile integer, intent(in) :: ncolumns integer :: i,ierr print "(1x,a)",'saving units to '//trim(unitsfile) open(unit=77,file=unitsfile,status='replace',form='formatted',iostat=ierr) if (ierr /=0) then print "(1x,a)",'ERROR: cannot write units file' else write(77,*,iostat=ierr) units(0),';',trim(unitslabel(0)),' ;',unitzintegration,';',trim(labelzintegration) do i=1,ncolumns write(77,*,iostat=ierr) units(i),';',trim(unitslabel(i)) if (ierr /= 0) then print "(1x,a)",'ERROR whilst writing units file' close(unit=77) return endif enddo endif close(unit=77) return end subroutine write_unitsfile !------------------------------------------------------- ! ! read units for all columns from a file ! !------------------------------------------------------- subroutine read_unitsfile(unitsfile,ncolumns,ierr,iverbose) use settings_data, only:ndim implicit none character(len=*), intent(in) :: unitsfile integer, intent(in) :: ncolumns integer, intent(out) :: ierr integer, intent(in), optional :: iverbose character(len=2*len(unitslabel)+40) :: line integer :: i,itemp,isemicolon,isemicolon2,isemicolon3 logical :: ierrzunits,iexist,verbose if (present(iverbose)) then verbose= (iverbose.gt.0) else verbose = .true. endif ierr = 0 ierrzunits = .false. inquire(file=unitsfile,exist=iexist) if (.not.iexist) then if (verbose) print "(1x,a)",trim(unitsfile)//' not found' ierr = 1 return endif open(unit=78,file=unitsfile,status='old',form='formatted',err=997) if (verbose) print "(a)",' read '//trim(unitsfile) do i=0,maxplot ! read all units possibly present in file ! ! read a line from the file ! read(78,"(a)",err=998,end=999) line ! ! now get units from the first part of the line ! read(line,*,iostat=itemp) units(i) if (itemp /= 0) print*,'error reading units for column ',i ! ! units label is what comes after the semicolon ! isemicolon = index(line,';') if (i.eq.0) then !--time line also contains unit of z integration isemicolon2 = index(line(isemicolon+1:),';') if (isemicolon2.gt.0) then isemicolon2 = isemicolon + isemicolon2 unitslabel(i) = trim(line(isemicolon+1:isemicolon2-1)) isemicolon3 = isemicolon2 + index(line(isemicolon2+1:),';') read(line(isemicolon2+1:isemicolon3-1),*,iostat=itemp) unitzintegration if (itemp /= 0) then print*,'error reading unit for z integration' ierrzunits = .true. else labelzintegration = trim(line(isemicolon3+1:)) endif else ierrzunits = .true. print*,'error: could not read z integration unit from units file' if (isemicolon.gt.0) then unitslabel(i) = trim(line(isemicolon+1:)) else print*,'error reading units label for column ',i endif endif else if (isemicolon.gt.0) then unitslabel(i) = trim(line(isemicolon+1:)) else print*,'error reading units label for column ',i endif endif ! print*,i,'units = ',units(i),'label = ',unitslabel(i) enddo if (ierrzunits .and. ndim.eq.3) then unitzintegration = units(3) labelzintegration = unitslabel(3) endif close(unit=78) return 997 continue if (verbose) print*,trim(unitsfile),' not found' ierr = 1 return 998 continue print*,'*** error reading units from '//trim(unitsfile) ierr = 2 if (ierrzunits .and. ndim.eq.3) then unitzintegration = units(3) labelzintegration = unitslabel(3) endif close(unit=78) return 999 continue !--only give error if we really do not have enough columns ! (on first call nextra is not set) if (i.le.ncolumns) then print "(1x,a,i2)",'end of file in '//trim(unitsfile)//': units read to column ',i ierr = -1 endif if (ierrzunits .and. ndim.eq.3) then unitzintegration = units(3) labelzintegration = unitslabel(3) endif close(unit=78) return end subroutine read_unitsfile end module settings_units splash/src/read_data_egaburov.f90000644 000766 000000 00000024737 13261626263 017706 0ustar00dpricewheel000000 000000 !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR OUTPUT FROM THE GADGET CODE ! ! NOTE THAT THIS ONLY "OFFICIALLY" WORKS WITH THE PARALLEL CODE AS WE ! REQUIRE KNOWLEDGE OF THE PARTICLE SMOOTHING LENGTHS ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! GSPLASH_USE_Z if 'YES' uses redshift in the legend instead of time ! GSPLASH_DARKMATTER_HSOFT if given a value > 0.0 will assign a ! smoothing length to dark matter particles which can then be ! used in the rendering ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Partial data read implemented Nov 2006 means that columns with ! the 'required' flag set to false are not read (read is therefore much faster) !------------------------------------------------------------------------- subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,time,gamma,maxpart,maxcol,maxstep use params use settings_data, only:ndim,ndimV,ncolumns,ncalc,iformat,required,ipartialread use settings_page, only:legendtext use mem_allocation, only:alloc use labels, only:ih,irho use system_utils, only:renvironment,lenvironment implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile ! integer, dimension(maxparttypes) :: npartoftypei,Nall ! integer, dimension(:), allocatable :: iamtemp integer :: i,j,ierr ! integer :: index1,index2,indexstart,indexend,Nmassesdumped integer :: ncolstep,npart_max,nstep_max ! integer :: iFlagSfr,iFlagFeedback,iFlagCool,nfiles logical :: iexist,reallocate ! real(doub_prec) :: timetemp,ztemp, dummy ! real(doub_prec), dimension(6) :: massoftypei ! real, dimension(:), allocatable :: dattemp1 ! real :: hsoft ! integer :: ntot, nnopt, nout, nit, nav, ngr, nrelax ! real(doub_prec) :: hmin, hmax, sep0, tf, dtout, alpha, beta, eta2, trelax, dt, omega2 ! real(doub_prec) :: dx, dy, dz, dm, dh, drho, dvx, dvy, dvz ! real(doub_prec) :: duth, dmmu ! real(doub_prec) :: rscale, mscale !!!!!!!!!!!!!!!! integer :: proc, nread integer :: myproc, nproc, npx, npy, npz; integer :: global_n, local_n, ndim_data real(sing_prec) :: t_global, dt_global integer :: iteration real(sing_prec) :: cfl_no, gamma_gas integer :: periodic_flag real(sing_prec) :: xmin, ymin, zmin, xmax, ymax, zmax integer :: idx real(sing_prec) :: posx, posy, posz, pvelx, pvely, pvelz real(sing_prec) :: dens, ethm, pres, pmag, vabs real(sing_prec) :: velx, vely, velz, Bx, By, Bz real(sing_prec) :: hsml, wght, Bpsi, divB, v1, v2, v3, v4 !!!!!!!!!!!!!!!!!! nstepsread = 0 npart_max = maxpart if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(datfile)//': file not found ***' STOP endif ! !--set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 ! !--read data from snapshots ! i = istepstart write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! open(11,iostat=ierr,file=datfile,status='old', form='unformatted') if (ierr /= 0) then print "(a)", '*** ERROR OPENING FILE ***' STOP endif ! !--read header for this timestep ! read(11, iostat=ierr) myproc, nproc, npx, npy, npz, & global_n, local_n, ndim_data, t_global, dt_global, iteration, & cfl_no, gamma_gas, periodic_flag, & xmin, ymin, zmin, xmax, ymax, zmax print *, global_n print *, local_n print *, nproc if (ierr /= 0) then print "(a)", '*** ERROR READING TIMESTEP HEADER ***' STOP endif ! t_global = t_global - 0.13 iformat = 0 ncolstep = 30 ncolumns = ncolstep print*,'nproc : ',nproc print*,'npx, npy, npz : ',npx, npy, npz print*,'time : ',t_global print*,'gamma_gas : ',gamma_gas print*,'N_total : ',global_n print*,'N data columns : ',ncolstep ! !--if successfully read header, increment the nstepsread counter ! nstepsread = nstepsread + 1 ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if (global_n .gt. maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*global_n) else ! if first time, save on memory npart_max = int(global_n) endif endif if (i.ge.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncolstep+ncalc,maxcol)) endif npartoftype(:,i) = 0 npartoftype(1,i) = global_n !--use this line for code time time(i) = real(t_global) ! !--read particle data ! nread = 0 do proc = 1, nproc do j = 1, local_n read(11, iostat=ierr) & idx, & posx, posy, posz, pvelx, pvely, pvelz, & dens, ethm, pres, pmag, vabs, & velx, vely, velz, Bx, By, Bz, & hsml, wght, Bpsi, divB, & v1, v2, v3, v4 ! if (pres > 10) then ! print *, posx, posy, posz ! end if if (ierr /= 0) then print *, '*** ERROR READING PARTICLE', i, 'PROC:', myproc STOP endif dat(j + nread, 1, i) = posx dat(j + nread, 2, i) = posy dat(j + nread, 3, i) = posz dat(j + nread, 4, i) = velx dat(j + nread, 5, i) = vely dat(j + nread, 6, i) = velz dat(j + nread, 7, i) = dens dat(j + nread, 8, i) = pres dat(j + nread, 9, i) = pmag dat(j + nread, 10, i) = vabs dat(j + nread, 11, i) = Bx dat(j + nread, 12, i) = By dat(j + nread, 13, i) = Bz if (pmag > 0) then dat(j + nread, 14, i) = abs(divB/sqrt(pmag*2.0)) else dat(j + nread, 14, i) = 0.0; end if dat(j + nread, 15, i) = Bpsi dat(j + nread, 16, i) = hsml*2 dat(j + nread, 17, i) = wght dat(j + nread, 18, i) = pvelx dat(j + nread, 19, i) = pvely dat(j + nread, 20, i) = pvelz dat(j + nread, 21, i) = abs(divB) dat(j + nread, 22, i) = dens*wght dat(j + nread, 23, i) = pres/dens/(gamma_gas - 1.0) ! uthermal dat(j + nread, 24, i) = sqrt(pmag*2.0) dat(j + nread, 25, i) = pres + pmag dat(j + nread, 26, i) = v1; dat(j + nread, 27, i) = v2; dat(j + nread, 28, i) = v3; dat(j + nread, 29, i) = v4; dat(j + nread, 30, i) = idx; end do nread = nread + local_n; if (proc < nproc) then read(11, iostat=ierr) myproc, nproc, npx, npy, npz, & global_n, local_n, ndim_data, t_global, dt_global, iteration, & cfl_no, gamma_gas, periodic_flag, & xmin, ymin, zmin, xmax, ymax, zmax if (ierr /= 0) then print *, '*** ERROR READING TIMESTEP HEADER ***, proc=', proc STOP endif end if end do if (nread .ne. global_n) then print *, 'nread= ', nread print *, 'global_n= ', global_n print "(a)", ' *** SOMETHING WENT WRONG ***' STOP end if !!!!!!!!!!!!!!!!!!!!! gamma = gamma_gas irho = 7 ih = 16 ! !--set flag to indicate that only part of this file has been read ! if (.not.all(required(1:ncolstep))) ipartialread = .true. ! !--close data file and return ! close(unit=11) if (nstepsread.gt.0) then print*,'>> last step ntot =',sum(npartoftype(:,istepstart+nstepsread-1)) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass,ih,irho,ipr,iutherm, idivb, iBfirst use params use settings_data, only:ndim,ndimV,ncolumns,ntypes,UseTypeInRenderings use geometry, only:labelcoord use system_utils, only:renvironment implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 irho = 7 iutherm = 23 ipr = 8 ih = 16 iBfirst = 11 ipmass = 22 idivb = 14 label(ix(1:ndim)) = labelcoord(1:ndim,1) label(irho) = 'density' label(iutherm) = 'cs' label(ih) = 'h' label(ipmass) = 'particle mass' label(ipr) = 'pressure' label(9) = 'Pmag' label(10) = 'vabs' label(15) = "\gpsi" label(18) = 'pvelx' label(19) = 'pvely' label(20) = 'pvelz' label(11) = 'Bx' label(12) = 'By' label(13) = 'Bz' label(14) = 'divB' label(24) = '|B|' label(25) = 'Ptot' label(26) = 'scal0' label(27) = 'scal1' label(28) = 'scal2' label(29) = 'scal3' label(30) = 'idx' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo !--mag field if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 1 labeltype(1) = 'gas' UseTypeInRenderings(1) = .true. return end subroutine set_labels splash/src/exact_mhdshock.f90000644 000766 000000 00000025467 13261626263 017075 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! Plot exact solution for a magnetohydrodynamic shock ! (ie. one dimensional MHD Riemann problem) ! ! For want of a better solution these are just taken from the tables in ! Ryu & Jones (1995), ApJ 442, 228 or from ruler and pencil ! on the results in Balsara (1998), or from running the Athena code via ! http://rainman.astro.illinois.edu/ddr/oned/cgi-bin/athena.pl ! ! ---------------------------------------------------------------------- module mhdshock implicit none public :: exact_mhdshock integer, parameter, public :: nmhdshocksolns = 7 character(len=23), dimension(nmhdshocksolns), parameter, public :: mhdprob = & (/'Brio/Wu (gamma=2) ', & 'fast/slow shock (RJ95)', & '7 jump shock (RJ95)', & 'isothermal shock (B98) ', & 'rarefaction wave (RJ95)', & 'Mach 25 shock (DW94)', & 'Toth shock (RJ95/T00)'/) contains subroutine exact_mhdshock(iplot,ishk,time,gamma,xmin,xmax,xshock,xpts,ypts,npts,ierr) implicit none integer, intent(in) :: iplot,ishk integer, intent(out) :: npts,ierr real, intent(in) :: time,gamma,xmin,xmax,xshock real, dimension(:), intent(inout) :: xpts real, dimension(size(xpts)), intent(out) :: ypts real, dimension(16) :: rho,pr,vx,vy,vz,By,Bz real :: const, Bxzero,tfac print*,' Plotting exact mhd shock #',ishk,' at t = ',time ! !--set up grid for exact solution ! const = 1./SQRT(4.*3.1415926536) ierr = 0 select case(ishk) ! which solution to plot case(1) ! !--Brio & Wu problem with gamma = 2 ! tfac = time/0.1 vz = 0. Bz = 0. Bxzero = 0.75 npts = 14 xpts(1) = xmin xpts(2) = -0.18*tfac xpts(3) = -0.08*tfac xpts(4:6) = -0.03*tfac xpts(7) = -0.005*tfac xpts(8:9) = 0.06*tfac xpts(10:11) = 0.147*tfac xpts(12) = 0.33*tfac xpts(13) = 0.36*tfac xpts(14) = xmax rho(1:2) = 1.0 rho(3:4) = 0.67623 rho(5) = 0.827 rho(6) = 0.775 rho(7:8) = 0.6962 rho(9:10) = 0.2352 rho(11:12) = 0.117 rho(13:14) = 0.125 pr(1:2) = 1.0 pr(3:4) = 0.447 pr(5) = 0.727219 pr(6) = 0.6 pr(7:10) = 0.5160 pr(11:12) = 0.0876 pr(13:14) = 0.1 vx(1:2) = 0.0 vx(3:4) = 0.63721 vx(5) = 0.48 vx(6) = 0.52 vx(7:10) = 0.600 vx(11:12) = -0.24 vx(13:14) = 0.0 vy(1:2) = 0.0 vy(3:4) = -0.23345 vy(5) = -1.3 vy(6) = -1.4 vy(7:10) = -1.584 vy(11:12) = -0.166 vy(13:14) = 0. By(1:2) = 1.0 By(3:4) = 2.1*const By(5) = -1.2*const By(6) = -1.3*const By(7:10) = -1.9*const By(11:12) = -3.25*const By(13:14) = -1.0 case(2) ! !--fast/slow shock from RJ95 ! tfac = time/0.15 vz = 0. Bz = 0. Bxzero = 1.0 npts = 12 xpts(1) = xmin xpts(2) = -0.27*tfac xpts(3) = -0.09*tfac xpts(4) = -0.03*tfac xpts(5) = -0.01*tfac xpts(6:7) = 0.135*tfac xpts(8:9) = 0.25*tfac xpts(10:11) = 0.35*tfac xpts(12) = xmax rho(1:2) = 1.0 rho(3:4) = 0.5955 rho(5:6) = 0.55151 rho(7:8) = 0.41272 rho(9:10) = 0.2337 rho(11:12) = 0.2 pr(1:2) = 1.0 pr(3:4) = 0.42629 pr(5:8) = 0.37090 pr(9:10) = 0.12402 pr(11:12) = 0.1 vx(1:2) = 0.0 vx(3:4) = 0.81237 vx(5:8) = 0.89416 vx(9:10) = 0.24722 vx(11:12) = 0.0 vy(1:2) = 0.0 vy(3:4) = -0.59961 vy(5:8) = -0.5447 vy(9:10) = -0.91164 vy(11:12) = 0. By(1:2) = 1.0 By(3:4) = 0.28431 By(5:8) = 0.31528 By(9:10) = 0.43086 By(11:12) = 0.0 case(3) ! !--problem with 7 discontinuities from RJ95 ! tfac = time/0.2 Bxzero = 2.*const npts = 16 xpts(1) = xmin xpts(2:3) = -0.19*tfac xpts(4:5) = 0.03*tfac xpts(6:7) = 0.051*tfac xpts(8:9) = 0.12*tfac ! contact discontinuity xpts(10:11) = 0.18*tfac xpts(12:13) = 0.205*tfac xpts(14:15) = 0.45*tfac xpts(16) = xmax rho(1:2) = 1.08 rho(3:4) = 1.4903 rho(5:8) = 1.6343 rho(9:10) = 1.4735 rho(11:14) = 1.3090 rho(15:16) = 1.0 pr(1:2) = 0.95 pr(3:4) = 1.6558 pr(5:10) = 1.9317 pr(11:14) = 1.5844 pr(15:16) = 1.0 vx(1:2) = 1.2 vx(3:5) = 0.60588 vx(6:11) = 0.57538 vx(12:14) = 0.53432 vx(15:16) = 0.0 vy(1:2) = 0.01 vy(3:4) = 0.11235 vy(5:6) = 0.22157 vy(7:8) = 0.047602 vy(9:10) = 0.047601 vy(11:12) = -0.18411 vy(13:14) = -0.094572 vy(15:16) = 0.0 vz(1:2) = 0.5 vz(3:4) = 0.55686 vz(5:6) = 0.30125 vz(7:10) = 0.24734 vz(11:12) = 0.17554 vz(13:14) = -0.047286 vz(15:16) = 0.0 By(1:2) = 1.0155 By(3:4) = 1.4383 By(5:6) = 1.5716 By(7:10) = 1.4126 By(11:12) = 1.6103 By(13:14) = 1.5078 By(15:16) = 1.1284 Bz(1:2) = 0.56419 Bz(3:4) = 0.79907 Bz(5:6) = 0.48702 Bz(7:10) = 0.43772 Bz(11:12) = 0.49899 Bz(13:14) = 0.75392 Bz(15:16) = 0.56419 case(4) ! !--isothermal MHD problem from Balsara (1998) ! tfac = time/0.2 Bxzero = 2.*const npts = 14 xpts(1) = xmin xpts(2:3) = -0.15*tfac xpts(4:5) = 0.035*tfac xpts(6:7) = 0.07*tfac xpts(8:9) = 0.17*tfac xpts(10:11) = 0.2*tfac xpts(12:13) = 0.41*tfac xpts(14) = xmax rho(1:2) = 1.08 rho(3:6) = 1.515 rho(7:8) = 1.745 rho(9:12) = 1.36 rho(13:14) = 1.0 vx(1:2) = 1.2 vx(3:6) = 0.65 vx(7:8) = 0.62 vx(9:12) = 0.54 vx(13:14) = 0.0 vy(1:2) = 0.01 vy(3:4) = 0.13 vy(5:6) = 0.24 vy(7:8) = 0.071 vy(9:10) = -0.215 vy(11:12) = -0.125 vy(13:14) = 0.0 vz(1:2) = 0.5 vz(3:4) = 0.57 vz(5:6) = 0.31 vz(7:8) = 0.255 vz(9:10) = 0.165 vz(11:12) = -0.06 vz(13:14) = 0.0 By(1:2) = 3.6*const By(3:4) = 5.2*const By(5:6) = 5.7*const By(7:8) = 5.22*const By(9:10) = 5.96*const By(11:12) = 5.58*const By(13:14) = 4.0*const Bz(1:2) = 2.0*const Bz(3:4) = 2.885*const Bz(5:6) = 1.76*const Bz(7:8) = 1.62*const Bz(9:10) = 1.85*const Bz(11:12) = 2.79*const Bz(13:14) = 2.0*const pr = rho case(5) ! !--rarefaction from RJ95 ! tfac = time/0.1 npts = 6 vy = 0. vz = 0. Bz = 0. Bxzero = 0. xpts(1) = xmin xpts(2) = -0.27*tfac xpts(3) = -0.12*tfac xpts(4) = 0.12*tfac xpts(5) = 0.27*tfac xpts(6) = xmax rho(1:2) = 1.0 rho(3:4) = 0.49653 rho(5:6) = 1.0 pr(1:2) = 1.0 pr(3:4) = 0.31134 pr(5:6) = 1.0 vx(1:2) = -1.0 vx(3:4) = 0. ! this is approximate (to 10-7) vx(5:6) = 1.0 By(1:2) = 1.0 By(3:4) = 0.49638 By(5:6) = 1.0 case(6) ! !--mach 25 shocks from Dai and Woodward (1994) ! tfac = time/0.03 Bxzero = 4.*const npts = 6 rho(1:2) = 1.0 rho(3:4) = 3.982 rho(5:6) = 0.1 pr(1:2) = 1.0 pr(3:4) = 1806.0 pr(5:6) = 1.0 ! machno = 0.5*25.5 ! vs = SQRT(gamma*pr(1)/rho(1)) ! xpts(1) = xmin xpts(2:3) = -0.35*tfac xpts(4:5) = 0.35*tfac xpts(6) = xmax vx(1:2) = 36.87 vx(3:4) = 0.0 vx(5:6) = -36.87 vy(1:2) = -0.1546 vy(3:4) = -0.07727 vy(5:6) = 0.0 vz(1:2) = -0.03864 vz(3:4) = -0.01932 vz(5:6) = 0.0 By(1:2) = 4.0*const By(3:4) = 15.95*const By(5:6) = 4.0*const Bz(1:2) = 1.0*const Bz(3:4) = 3.988*const Bz(5:6) = 1.0*const case(7) ! !--Problem 1A in Ryu and Jones (1995) ! Bxzero = 5.*const npts = 12 tfac = time/0.08 xpts(1) = xmin xpts(2:3) = -0.386*tfac xpts(4:5) = -0.01*tfac xpts(6:7) = 0.0505*tfac xpts(8:9) = 0.12*tfac xpts(10:11) = 0.37*tfac xpts(12) = xmax rho(1:2) = 1.0 rho(3:4) = 2.6797 rho(5:6) = 2.6713 rho(7:8) = 3.8508 rho(9:10) = 3.7481 rho(11:12) = 1.0 pr(1:2) = 20.0 pr(3:4) = 150.98 pr(5:8) = 150.19 pr(9:10) = 143.57 pr(11:12) = 1.0 vx(1:2) = 10.0 vx(3:4) = 0.72113 vx(5:8) = 0.72376 vx(9:10) = 0.70505 vx(11:12) = -10.0 vy(1:2) = 0.0 vy(3:4) = 0.23139 vy(5:8) = 0.35684 vy(9:10) = -0.38804 vy(11:12) = 0.0 vz(1:12) = 0.0 By(1:2) = 1.4105 By(3:4) = 3.8389 By(5:8) = 4.0380 By(9:10) = 5.4272 By(11:12) = 1.4105 Bz(1:12) = 0.0 case default ierr = 1 npts = 0 ypts = 0. xpts = 0. return end select ! !--plot just the initial conditions at t=0 ! if (abs(time).le.0.) then rho(1:2) = rho(1) pr(1:2) = pr(1) vx(1:2) = vx(1) vy(1:2) = vy(1) vz(1:2) = vz(1) By(1:2) = By(1) Bz(1:2) = Bz(1) xpts(3) = 0. xpts(4) = xpts(npts) rho(3:4) = rho(npts) pr(3:4) = pr(npts) vx(3:4) = vx(npts) vy(3:4) = vy(npts) vz(3:4) = vz(npts) By(3:4) = By(npts) Bz(3:4) = Bz(npts) npts = 4 endif ! !--translate positions if shock is not at x=0 ! xpts(:) = xpts(:) + xshock ! !--determine which parameter to plot ! select case(iplot) case(1) ypts(1:npts) = rho(1:npts) case(2) ypts(1:npts) = pr(1:npts) case(3) ypts(1:npts) = vx(1:npts) case(4) ypts(1:npts) = vy(1:npts) case(5) ypts(1:npts) = vz(1:npts) case(6) ypts(1:npts) = By(1:npts) case(7) ypts(1:npts) = Bz(1:npts) case(8) print*,'gamma = ',gamma if (abs(gamma-1.).gt.1.e-5) then where (abs(rho(1:npts)) > 0.) ypts(1:npts) = pr(1:npts) / ((gamma-1.)*rho(1:npts)) end where else print*,' ***isothermal: utherm solution not valid' ypts(1:npts) = 0. endif case(9) ypts(1:npts) = Bxzero case default print*,'error: unknown solution to plot' end select return end subroutine exact_mhdshock end module mhdshock splash/src/convert.f90000644 000766 000000 00000012053 13261626263 015554 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! interface routine to convert all of the dump files ! on the SPLASH command line to a specified output format ! ! (c) D. Price 22/01/08 !----------------------------------------------------------------- module convert implicit none contains subroutine convert_all(outformat,igotfilenames,useall) use particle_data, only:time,gamma,dat,npartoftype,masstype,iamtype use settings_data, only:ncolumns,ncalc,required,ntypes,ndim,ndimV,lowmemorymode,& buffer_steps_in_file use filenames, only:rootname,nstepsinfile,nfiles,limitsfile use write_sphdata, only:write_sphdump use readwrite_griddata, only:isgridformat use analysis, only:isanalysis,open_analysis,write_analysis,close_analysis use convert_grid, only:convert_to_grid use getdata, only:get_data use asciiutils, only:ucase use limits, only:read_limits implicit none character(len=*), intent(in) :: outformat logical, intent(inout) :: igotfilenames logical, intent(in) :: useall logical :: doanalysis,converttogrid integer :: ifile,idump,ntotal,ierr,iloc character(len=len(rootname)+4) :: filename character(len=10) :: string required = .true. ! read whole dump file by default doanalysis = isanalysis(outformat,noprint=.true.) converttogrid = isgridformat(outformat) lowmemorymode = .false. ! must not be true for first file if (.not.doanalysis) then ! !--for format conversion each dump file is independent ! print "(/,5('-'),a,/)",'> CONVERTING SNAPSHOTS TO '//trim(ucase(outformat))//' FORMAT ' endif ! !--if nfiles = 0 (ie. no files read from command line), then call get_data here ! to also get nfiles correctly prior to the loop ! if (nfiles.eq.0) then call get_data(1,igotfilenames) igotfilenames = .true. endif do ifile=1,nfiles !--read data from dump file + calculate extra columns if (ifile.eq.1) then call get_data(ifile,igotfilenames,firsttime=.true.) ! ! read plot limits from file (overrides get_data limits settings) ! call read_limits(trim(limitsfile),ierr) ! !--for analysis we need to initialise the output file ! and close it at the end - do this here so we know ! the first filename and ndimV, labels etc. ! if (doanalysis) then call open_analysis(outformat,required,ncolumns+ncalc,ndim,ndimV) endif else call get_data(ifile,.true.) endif !--dump each step in file to an output file do idump = 1,nstepsinfile(ifile) if (nstepsinfile(ifile) > 1 .and. .not.buffer_steps_in_file) then call get_data(ifile,igotfilenames,.false.,iposinfile=idump) iloc = 1 else iloc = idump endif if (nstepsinfile(ifile).gt.1) then write(filename,"(a,'_',i5.5)") trim(rootname(ifile)),idump else filename = trim(rootname(ifile)) endif ntotal = sum(npartoftype(1:ntypes,iloc)) if (doanalysis) then call write_analysis(time(iloc),dat(1:ntotal,:,iloc),ntotal,ntypes, & npartoftype(1:ntypes,iloc),masstype(1:ntypes,iloc),iamtype(:,iloc), & ncolumns+ncalc,ndim,ndimV,outformat) elseif (converttogrid) then call convert_to_grid(time(iloc),dat(:,:,iloc),ntypes,& npartoftype(1:ntypes,iloc),masstype(1:ntypes,iloc),iamtype(:,iloc), & ncolumns+ncalc,filename,outformat,useall) else call write_sphdump(time(iloc),gamma(iloc),dat(1:ntotal,1:ncolumns+ncalc,iloc),ntotal,ntypes, & npartoftype(1:ntypes,iloc),masstype(1:ntypes,iloc),iamtype(:,iloc), & ncolumns+ncalc,filename,outformat) endif enddo enddo !--for analysis we need to start and end differently if (doanalysis) then call close_analysis(outformat) write(string,"(i10)") nfiles print "(/,5('-'),a,i5,a,/)",'> FINISHED CALCULATING '//trim(ucase(outformat))//' (USED '//trim(adjustl(string))//' DUMP FILES)' else print "(/,5('-'),a,/)",'> FINISHED CONVERTING DUMP FILES ' endif return end subroutine convert_all end module convert splash/src/write_griddata.F90000644 000766 000000 00000040516 13261626263 017032 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! Module implementing "splash to grid" operation, writing ! 3D gridded data in various output formats !----------------------------------------------------------------- module readwrite_griddata implicit none public :: isgridformat,print_gridformats public :: open_gridfile_w,open_gridfile_r public :: write_grid,read_gridcolumn,write_gridlimits ! !--generic interface for reading grid column data ! into 1D and 3D arrays ! interface read_gridcolumn module procedure read_gridcolumn3D,read_gridcolumn1D end interface read_gridcolumn private contains !----------------------------------------------------------------- ! utility to check if a format selection is valid !----------------------------------------------------------------- logical function isgridformat(string) use asciiutils, only:lcase implicit none character(len=*), intent(in) :: string isgridformat = .false. select case(trim(lcase(string))) case('grid') isgridformat = .true. case('gridascii') isgridformat = .true. case('gridbinary','gridbin') isgridformat = .true. case('gridascii2') isgridformat = .true. end select end function isgridformat !----------------------------------------------------------------- ! print grid limits !----------------------------------------------------------------- subroutine write_gridlimits(ndim,xmin,xmax,labelx) integer, intent(in) :: ndim real, intent(in) :: xmin(ndim),xmax(ndim) character(len=*), intent(in) :: labelx(ndim) integer :: i print "(a)",' grid dimensions:' do i=1,ndim if (maxval(abs(xmax)).lt.1.e7) then print "(1x,a,': ',f14.6,' -> ',f14.6)",trim(labelx(i)),xmin(i),xmax(i) else print "(1x,a,': ',es14.6,' -> ',es14.6)",trim(labelx(i)),xmin(i),xmax(i) endif enddo end subroutine write_gridlimits !----------------------------------------------------------------- ! print usage if format selection not valid !----------------------------------------------------------------- subroutine print_gridformats implicit none print "(/,a)",' Grid conversion mode ("splash to X dumpfiles"): ' print "(a)",' splash to grid : interpolate basic SPH data (density, plus velocity if present in data)' print "(a)",' to 2D or 3D grid, write grid data to file (using default output=ascii)' print "(a)",' to gridascii : as above, grid data written in ascii format' print "(a)",' to gridascii2 : grid data written in ascii format, all in one file' print "(a)",' to gridbinary : as above, grid data in simple unformatted binary format:' print "(a)",' write(unit) nx,ny,nz,ncolumns,time [ 4 bytes each ]' print "(a)",' write(unit) (((rho(i,j,k),i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ]' print "(a)",' write(unit) (((vx(i,j,k), i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ]' print "(a)",' write(unit) (((vy(i,j,k), i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ]' print "(a)",' write(unit) (((...(i,j,k),i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ]' print "(a)",' allto grid : as above, interpolating *all* columns to the grid (and output file)' print "(a)",' allto gridascii : as above, with ascii output' print "(a)",' allto gridbinary : as above, with binary output' return end subroutine print_gridformats !------------------------------------------------------ ! open grid file for (write) output, write header !------------------------------------------------------ subroutine open_gridfile_w(iunit,filenamein,outformat,ndim,ncolumns,npixels,time,ierr) use asciiutils, only:lcase implicit none integer, intent(in) :: iunit character(len=*), intent(in) :: filenamein,outformat character(len=len(filenamein)+10) :: filename integer, intent(in) :: ndim,ncolumns integer, dimension(ndim), intent(in) :: npixels real, intent(in) :: time integer, intent(out) :: ierr ! !--Only have to do something here for formats ! that have all columns in the same file ! ierr = 0 select case(trim(lcase(outformat))) case('gridascii','grid') ! !--ascii output uses individual files ! print "(/,a,i2)",'-----> WRITING TO ASCII OUTPUT FILES' case('gridbinary','gridbin') ! !--simple unformatted binary format ! filename = trim(filenamein)//'.grid' print "(/,a,i2)",'----> WRITING TO '//trim(filename)//' on unit ',iunit print "(a)", ' (using unformatted binary format)' open(unit=iunit,file=trim(filename),form='unformatted',status='replace',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR opening '//trim(filename)//' for output!' return endif write(iunit,iostat=ierr) npixels(1:ndim),ncolumns,time if (ierr /= 0) then print "(a)",' ERROR writing header to file!' return endif case('gridascii2') print "(/,a,i2)",'-----> WRITING TO ASCII OUTPUT FILES (WITH X, Y, Z, COL)' case('hdf5') case default ! return error if bad format print "(a)",' ERROR: unknown output format '''//trim(outformat)//''' in open_gridfile' ierr = 1 return end select end subroutine open_gridfile_w !------------------------------------------------------ ! open grid file for reading, read header !------------------------------------------------------ subroutine open_gridfile_r(iunit,filename,informat,ndim,ncolumns,npixels,time,ierr) use asciiutils, only:lcase implicit none integer, intent(in) :: iunit,ndim character(len=*), intent(in) :: filename character(len=*), intent(inout) :: informat integer, intent(out) :: ncolumns integer, dimension(ndim), intent(out) :: npixels real, intent(out) :: time integer, intent(out) :: ierr ! !--read only implemented for binary grid format at present ! ierr = 0 select case(trim(lcase(informat))) case('gridbinary','gridbin') ! !--simple unformatted binary format ! print "(/,a,i2)",'----> READING '//trim(filename)//' on unit ',iunit print "(a)", ' (using unformatted binary format)' open(unit=iunit,file=trim(filename),form='unformatted',status='old',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR opening '//trim(filename)//' for reading!' return endif read(iunit,iostat=ierr) npixels(1:ndim),ncolumns,time if (ierr /= 0) then print "(a)",' ERROR reading header to file!' return endif case default ! return error if bad format print "(a)",' ERROR: cannot read grid format '''//trim(informat)//''' in open_gridfile_r' ierr = 1 return end select end subroutine open_gridfile_r !------------------------------------------------------ ! write a particular column to the grid output file !------------------------------------------------------ subroutine write_grid(iunit,filenamein,outformat,ndim,ncolgrid,npixels,label,time,& pixwidth,xmin,ierr,dat,dat3D,dat2D,label3D) use asciiutils, only:ucase,lcase,safename use filenames, only:tagline implicit none integer, intent(in) :: iunit character(len=*), intent(in) :: filenamein,outformat integer, intent(in) :: ndim,ncolgrid integer, dimension(ndim), intent(in) :: npixels character(len=*), intent(in) :: label real, intent(in) :: time,pixwidth real, dimension(3), intent(in) :: xmin integer, intent(out) :: ierr character(len=len(filenamein)+20) :: filename real, dimension(:,:,:), intent(in), optional :: dat real, dimension(:,:,:,:), intent(in), optional :: dat3D real, dimension(:,:), intent(in), optional :: dat2D character(len=*), intent(in), optional :: label3D(ncolgrid) integer :: i,j,k,n real :: xi,yi,zi ierr = 0 if (ndim.eq.3 .and. .not.(present(dat3D) .or. present(dat))) then print "(a)",' ERROR in call to write_grid: ndim=3 but 3D grid not passed' ierr = 1 elseif (ndim.eq.2 .and. .not.present(dat2D)) then print "(a)",' ERROR in call to write_grid: ndim=2 but 2D grid not passed' ierr = 1 elseif (.not.(ndim.eq.2 .or. ndim.eq.3)) then print "(a,i2,a)",' ERROR in call to write_grid: cannot write grid for ',ndim,' dimensions' ierr = 2 endif if (ierr /= 0) return select case(trim(lcase(outformat))) case('gridascii','grid') if (ncolgrid > 1) then filename = trim(filenamein)//'_grid.dat' else filename = trim(filenamein)//'_'//trim(safename(label))//'_grid.dat' endif print "(/,a)",'-----> WRITING to '//trim(filename) ! !--open ascii file ! open(unit=iunit,file=trim(filename),form='formatted',status='replace',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR OPENING FILE FOR WRITING' return endif write(iunit,"(a)",err=100) '# '//trim(tagline) write(iunit,"(a)",err=100) & '# '//trim(filename)//' produced using "splash to '//trim(outformat)// & '" on file '//trim(filenamein) write(iunit,"(a)",err=100) '#' write(iunit,"(a)",err=100) '# time:' write(iunit,"(a,es15.7)",iostat=ierr) '# ',time write(iunit,"(a)",err=100) '#' write(iunit,"(a)",err=100) '# file contains:' write(iunit,"(a,i1,a)",err=100) '# '//trim(label)//' interpolated to ',ndim,'D grid ' write(iunit,"(a)",err=100) '#' write(iunit,"(a)",err=100) '# written in the form: ' if (ndim.eq.3) then write(iunit,"(a)",err=100) '# do k=1,nz' write(iunit,"(a)",err=100) '# do j=1,ny' write(iunit,"(a)",err=100) '# write(*,*) (dat(i,j,k),i=1,nx)' write(iunit,"(a)",err=100) '# enddo' write(iunit,"(a)",err=100) '# enddo' else write(iunit,"(a)",err=100) '# do j=1,ny' write(iunit,"(a)",err=100) '# write(*,*) (dat(i,j),i=1,nx)' write(iunit,"(a)",err=100) '# enddo' endif write(iunit,"(a)",err=100) '#' write(iunit,"(a)",err=100) '# grid dimensions:' if (present(dat)) then write(iunit,"(a)",err=100) '# nx ny nz' write(iunit,*,err=100) npixels(1:ndim) do k=1,npixels(3) do j=1,npixels(2) write(iunit,"(2048(es14.6,1x))",err=100) (dat(i,j,k),i=1,npixels(1)) enddo enddo elseif (present(dat3D)) then write(iunit,"(a)",err=100) '# nx ny nz' write(iunit,*,err=100) npixels(1:ndim) do k=1,npixels(3) do j=1,npixels(2) write(iunit,"(2048(es14.6,1x))",err=100) (dat3D(1,i,j,k),i=1,npixels(1)) enddo enddo elseif (present(dat2D)) then write(iunit,"(a)",err=100) '# nx ny' write(iunit,*,err=100) npixels(1:ndim) do j=1,npixels(2) write(iunit,"(2048(es14.6,1x))",err=100) (dat2D(i,j),i=1,npixels(1)) enddo endif close(unit=iunit) return case('gridbinary','gridbin') print "(a)",'-----> WRITING '//trim(ucase(label)) if (present(dat)) then write(iunit,iostat=ierr) (((dat(i,j,k),i=1,npixels(1)),j=1,npixels(2)),k=1,npixels(3)) elseif (present(dat3D)) then write(iunit,iostat=ierr) ((((dat3D(n,i,j,k),i=1,npixels(1)),j=1,npixels(2)),k=1,npixels(3)),n=1,ncolgrid) elseif (present(dat2D)) then write(iunit,iostat=ierr) ((dat2D(i,j),i=1,npixels(1)),j=1,npixels(2)) endif case('gridascii2') if (ncolgrid > 1) then filename = trim(filenamein)//'_grid.dat' print "(a)",'-----> WRITING to '//trim(filename) else filename = trim(filenamein)//'_'//trim(safename(label))//'_grid.dat' print "(a)",'-----> WRITING '//trim(ucase(label))//' to '//trim(filename) endif ! !--open ascii file ! open(unit=iunit,file=trim(filename),form='formatted',status='replace',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR OPENING FILE FOR WRITING' return endif write(iunit,"(a)",err=100) '# '//trim(tagline) write(iunit,"(a)",err=100) & '# '//trim(filename)//' produced using "splash to '//trim(outformat)// & '" on file '//trim(filenamein) write(iunit,"(a)",err=100) '# grid dimensions:' if (present(dat3D) .and. present(dat)) then write(iunit,"(a)",err=100) '# nx ny nz' write(iunit,"(a,3(i5,1x))",err=100) '# ',npixels(1:3) write(iunit,"('#',64('[',a13,']'))",err=100) 'x','y','z',trim(label),(trim(label3D(n)),n=1,ncolgrid) do k=1,npixels(3) write(*,"('.')",ADVANCE='NO') zi = xmin(3) + (k-0.5)*pixwidth do j=1,npixels(2) yi = xmin(2) + (j-0.5)*pixwidth do i=1,npixels(1) xi = xmin(1) + (i-0.5)*pixwidth write(iunit,"(64(es14.6,1x))") xi,yi,zi,dat(i,j,k),(dat3D(n,i,j,k),n=1,ncolgrid) enddo enddo enddo elseif (present(dat3D)) then write(iunit,"(a)",err=100) '# nx ny nz' write(iunit,"(a,3(i5,1x))",err=100) '# ',npixels(1:3) write(iunit,"('#',64('[',a13,']'))",err=100) 'x','y','z',(trim(label3D(n)),n=1,ncolgrid) do k=1,npixels(3) write(*,"('.')",ADVANCE='NO') zi = xmin(3) + (k-0.5)*pixwidth do j=1,npixels(2) yi = xmin(2) + (j-0.5)*pixwidth do i=1,npixels(1) xi = xmin(1) + (i-0.5)*pixwidth write(iunit,"(64(es14.6,1x))") xi,yi,zi,(dat3D(n,i,j,k),n=1,ncolgrid) enddo enddo enddo elseif (present(dat2D)) then write(iunit,"(a)",err=100) '# nx ny ' write(iunit,"(a,2(i5,1x))",err=100) '# ',npixels(1:2) write(iunit,"('#',3('[',a13,']'))",err=100) 'x','y',trim(label) do j=1,npixels(2) write(*,"('.')",ADVANCE='NO') yi = xmin(2) + (j-0.5)*pixwidth do i=1,npixels(1) xi = xmin(1) + (i-0.5)*pixwidth write(iunit,"(3(es14.6,1x))") xi,yi,dat2D(i,j) enddo enddo endif write(*,*) case('hdf5') case default print "(a)",' ERROR: unknown output format '''//trim(outformat)//''' in write_grid' return end select return ! !--error handling during write ! 100 continue print "(a)",' ERROR writing grid file' close(unit=iunit) return end subroutine write_grid !------------------------------------------------------------------ ! read a particular column from the grid output file into 3D array !------------------------------------------------------------------ subroutine read_gridcolumn3D(iunit,dat,npixels,ierr) implicit none integer, intent(in) :: iunit real, dimension(:,:,:), intent(out) :: dat integer, dimension(3), intent(in) :: npixels integer, intent(out) :: ierr integer :: i,j,k print "(a,i4,'x',i4,'x',i4,a)",'-----> READING ',npixels(:),' data points' read(iunit,iostat=ierr) (((dat(i,j,k),i=1,npixels(1)),j=1,npixels(2)),k=1,npixels(3)) end subroutine read_gridcolumn3D !------------------------------------------------------------------ ! read a particular column from the grid output file into 1D array !------------------------------------------------------------------ subroutine read_gridcolumn1D(iunit,dat,ngrid,ierr) implicit none integer, intent(in) :: iunit real, dimension(:), intent(out) :: dat integer, intent(in) :: ngrid integer, intent(out) :: ierr integer :: i print "(a,i10,a)",'-----> READING ',ngrid,' data points' read(iunit,iostat=ierr) (dat(i),i=1,ngrid) end subroutine read_gridcolumn1D end module readwrite_griddata splash/src/promptlist.f90000644 000766 000000 00000007135 13261626263 016316 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module implementing generic menu where objects can be added ! to/deleted from a list !------------------------------------------------------------------------- module promptlist implicit none interface subroutine print_objlist(nobj) integer, intent(in) :: nobj end subroutine end interface interface subroutine add_obj(istart,iend,nobj) integer, intent(in) :: istart,iend integer, intent(inout) :: nobj end subroutine add_obj end interface interface subroutine delete_obj(iobj,nobj) integer, intent(in) :: iobj,nobj end subroutine delete_obj end interface contains subroutine prompt_list(nobj,maxobj,objname,print_objlist,add_obj,delete_obj) use prompting, only:prompt implicit none integer, intent(inout) :: nobj integer, intent(in) :: maxobj character(len=*), intent(in) :: objname procedure(), pointer, intent(in) :: print_objlist,add_obj,delete_obj character(len=1) :: charp logical :: done,first integer :: istart,iend,ipick ipick = nobj + 1 done = .false. first = .true. charp = 'a' objmenu: do while(.not.done) call print_objlist(nobj) iend = maxobj if (nobj.gt.0 .or. .not.first) then charp='q' print* call prompt(' a)dd '//trim(objname)//', e)dit, d)elete, c)lear all or q)uit/finish?',& charp,list=(/'a','e','d','c','q','s','S','Q'/),noblank=.true.) select case(charp) case('a') istart = nobj iend = nobj + 1 case('e') if (nobj.gt.0) then ipick = 0 call prompt(' pick a '//objname//' to edit ',ipick,0,nobj) if (ipick.gt.0) then istart = ipick - 1 iend = istart + 1 else istart = 0 iend = 1 first = .false. cycle objmenu endif else istart = 0 iend = 1 endif first = .false. case('d') if (nobj.gt.0) then ipick = 0 call prompt(' pick a '//objname//' to delete ',ipick,0,nobj) call delete_obj(ipick,nobj) else print*,'nothing to delete!' endif first = .false. cycle objmenu case('c') nobj = 0 first = .false. cycle objmenu case('q','Q','s','S') done = .true. case default istart = 0 iend = maxobj end select else istart = 0 iend = 1 endif if (.not.done) call add_obj(istart,iend,nobj) first = .false. enddo objmenu return end subroutine prompt_list end module promptlist splash/src/prompting.f90000644 000766 000000 00000052671 13261626263 016125 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !This is a small f90 module containing a generic subroutine for prompting !integer, real, double and logical variables and strings. !I use them quite often and found the solution below very useful. ! !The general syntax is: ! ! call prompt(PROMPT,VAR,...) ! !Action: writes the string PROMPT on the terminal plus the current ! value of the variable VAR and reads VAR. If default ! is pressed instead of a new value, ! the variable VAR stays untouched. ! !In addition there are a few optional parameter to the routine like !setting defaults or limits etc... ! !In principle three f90 features are used which are not available !in f77: recursion, non-advancing I/O and overloading. ! !A detailed description of the syntax can be found in the header !of the module below. !---------------------------- please cut here ------------------------------------- ! ! f90 Module 'prompting' ! ! Definition of Generic Subroutine: prompt ! ! Syntax: prompt(text, value, [min], [max]) ! text character string ! value integer, real or double ! min, max allowed range of same type as value (optional) ! [DJP] min2, max2 allowed 2nd range of same type as value (optional) ! ! prompt(text, string, [length], [case]) ! text character string ! string character string ! length length of string (optional on return) ! case option ! 1 -> convert string to lower case ! 2 -> convert string to upper case ! lower=1, upper=2 are defined public ! within this module ! ! prompt(text, value, [default]) ! text character string ! value logical ! default logical (optional) ! will always overwrite the current value ! ! Author: Th. S. Ullrich, University Heidelberg ! e-mail: ullrich@ceres1.physi.uni-heidelberg.de ! Last mod: 18 Aug 94 ! ! Changes by D.Price, University of Exeter, dprice@astro.ex.ac.uk: ! 19/10/04 : problem with if (present(min) .and. min < newvalue) ! on some compilers ! ! 31/10/06: D. Price: ! Function print_logical added for displaying logicals: takes in a logical ! variable and returns a string 'on' or 'off' as appropriate. ! ! 20/06/07: D. Price: ! Default part of prompt changed from "=" to the more human "default=" ! Also the character string prompt puts the default value in quotes ! ! 09/05/08: D. Price: ! String prompt accepts "blank" to set empty string, unless optional ! argument noblank is set to .true. ! ! 06/02/09: D. Price, Monash University, daniel.price@monash.edu ! Added optional "mask" argument to print_logical routine ! ! 27/01/10: D. Price: ! Added optional "list" argument to string_prompt routine, now recursive ! ! 24/02/10: D. Price: ! When noblank=.true., string prompt does not accept blank string ! (e.g. where it is the default input) and gives an error message ! ! 23/07/10: D. Price: ! Integer prompt accepts 2nd sub-range [min:max] [min2:max2] ! ! 06/05/11: D. Price: ! Added prompt for integer arrays ! ! 21/08/12: D. Price: ! Real/double prompting interfaces compile and work with -r8 ! ! 08/02/13: D. Price ! Integer prompt enforces default value to be between min and max ! ! 25/06/13: D. Price ! Real and double prompts print values using scientific notation 1.e8 instead of 0.1e9 ! module prompting private ! ! Options for string prompting routine ! integer, parameter, public :: lower = 1, upper = 2 ! ! Create generic name 'prompt' ! interface prompt module procedure & integer_prompt, real_prompt, string_prompt, double_prompt, logical_prompt, intarr_prompt end interface public :: prompt,print_logical contains ! ! Integer prompting routine ! recursive subroutine integer_prompt(text, value, min, max, min2, max2) character(len=*), intent(in) :: text integer, intent(inout) :: value integer :: newvalue character(len=64) :: string character(len=16) :: chmin, chmax, chmin2, chmax2 integer :: ios, ierr integer, optional, intent(in) :: min, max, min2, max2 logical :: error chmin = '' chmax = '' chmin2 = '' chmax2 = '' error = .false. ! ! Make sure default argument is within range! ! if (present(min)) then if (value < min) value = min endif if (present(max)) then if (value > max) value = max endif ! ! Pack arguments in strings for compact and nicer prompt ! write(string,*) value if (present(min)) write(chmin,"(g10.0)") min if (present(max)) write(chmax,"(g10.0)") max if (present(min2)) write(chmin2,"(g10.0)") min2 if (present(max2)) write(chmax2,"(g10.0)") max2 ! ! Write prompt string to terminal ! if (present(min).or.present(max)) then if (present(min2).or.present(max2)) then write(*,"(a,1x,'([',a,':',a,',',a,':',a,'],',1x,'default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(chmin)), & trim(adjustl(chmax)),trim(adjustl(chmin2)),& trim(adjustl(chmax2)),trim(adjustl(string)) else write(*,"(a,1x,'([',a,':',a,'],',1x,'default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(chmin)), & trim(adjustl(chmax)), trim(adjustl(string)) endif else write(*,"(a,1x,'(default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(string)) endif ! ! Read new value, quit and keep old value if zero sized string ! read(*,"(a)",iostat=ierr) string if (len(trim(adjustl(string))) == 0) return read(string,"(g10.0)",iostat=ios) newvalue ! ! Check if new string is of right type and within given range ! if (ios /= 0) then print "(a)", "Error, not an integer number" error = .true. else if (present(min)) then if (newvalue < min) then if (present(max2)) then if (newvalue > max2) then print "(a)", "Error, value out of range" error = .true. elseif (newvalue < min2) then print "(a)", "Error, value out of range" error = .true. endif else print "(a)", "Error, value out of range" error = .true. endif endif endif if (present(max)) then if (newvalue > max) then if (present(min2)) then if (newvalue < min2) then print "(a)", "Error, value out of range" error = .true. elseif (newvalue > max2) then print "(a)", "Error, value out of range" error = .true. endif else print "(a)", "Error, value out of range" error = .true. endif endif endif endif ! ! Assign new value if everything is ok, else prompt again ! if (error) then call integer_prompt(text, value, min, max, min2, max2) else value = newvalue endif end subroutine integer_prompt ! ! Real prompting routine ! recursive subroutine real_prompt(text, value, min, max) integer, parameter :: sp = selected_real_kind(p=6) character(len=*), intent(in) :: text real(kind=sp), intent(inout) :: value real(kind=sp) :: newvalue character(len=64) :: string character(len=16) :: chmin, chmax integer :: ios real(kind=sp), optional, intent(in) :: min, max logical :: error chmin = '' chmax = '' error = .false. ! ! Pack arguments in strings for compact and nicer prompt ! if (abs(value) < 0.1 .or. abs(value) >= 1.d4) then write(string,"(es13.4)") value else write(string,"(g13.4)") value endif if (present(min)) write(chmin,"(g13.4)") min if (present(max)) write(chmax,"(g13.4)") max ! ! Write prompt string to terminal ! if (present(min).or.present(max)) then write(*,"(a,1x,'([',a,':',a,'],',1x,'default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(chmin)), & trim(adjustl(chmax)), trim(adjustl(string)) else write(*,"(a,1x,'(default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(string)) endif ! ! Read new value, quit and keep old value if zero sized string ! read(*,"(a)") string if (len(trim(adjustl(string))) == 0) return read(string,*,iostat=ios) newvalue ! ! Check if new string is of right type and within given range ! if (ios /= 0) then print "(a)", "Error, not a real number" error = .true. else if (present(min)) then if (newvalue < min) then print "(a)", "Error, value out of range" error = .true. endif endif if (present(max)) then if (newvalue > max) then print "(a)", "Error, value out of range" error = .true. endif endif endif ! ! Assign new value if everything is ok, else prompt again ! if (error) then call real_prompt(text, value, min, max) else value = newvalue endif end subroutine real_prompt ! ! Double precision prompting routine ! recursive subroutine double_prompt(text, value, min, max) integer, parameter :: db = kind(0.d0) character(len=*), intent(in) :: text real(kind=db), intent(inout) :: value real(kind=db) :: newvalue character(len=64) :: string character(len=16) :: chmin, chmax integer :: ios real(kind=db), optional, intent(in) :: min, max logical :: error chmin = '' chmax = '' error = .false. ! ! Pack arguments in strings for compact and nicer prompt ! if (abs(value) < 0.1d0 .or. abs(value) >= 1.d4) then write(string,"(es13.4)") value else write(string,"(g13.4)") value endif if (present(min)) write(chmin,"(g13.4)") min if (present(max)) write(chmax,"(g13.4)") max ! ! Write prompt string to terminal ! if (present(min).or.present(max)) then write(*,"(a,1x,'([',a,':',a,'],',1x,'default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(chmin)), & trim(adjustl(chmax)), trim(adjustl(string)) else write(*,"(a,1x,'(default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(string)) endif ! ! Read new value, quit and keep old value if zero sized string ! read(*,"(a)") string if (len(trim(adjustl(string))) == 0) return read(string,*,iostat=ios) newvalue ! ! Check if new string is of right type and within given range ! if (ios /= 0) then print "(a)", "Error, not a real number" error = .true. else if (present(min)) then if (newvalue < min) then print "(a)", "Error, value out of range" error = .true. endif endif if (present(max)) then if (newvalue > max) then print "(a)", "Error, value out of range" error = .true. endif endif endif ! ! Assign new value if everything is ok, else prompt again ! if (error) then call double_prompt(text, value, min, max) else value = newvalue endif end subroutine double_prompt ! ! Logical prompting routine ! recursive subroutine logical_prompt(text, lvalue, default) character(len=*), intent(in) :: text logical, intent(inout) :: lvalue logical, optional, intent(in) :: default character(len=32) :: string ! ! If present, set default ! if (present(default)) lvalue = default ! ! Default answer yes/no ! if (lvalue) then string='yes' else string='no' endif ! ! Write prompt string to terminal ! write(*,"(a,1x,'(default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(string)) ! ! Read new value, quit and keep old value if zero sized string ! read(*,"(a)") string if (len(trim(adjustl(string))) == 0) return ! ! Translate answer in .true./.false., if invalid prompt again ! select case (adjustl(string)) case ('y') lvalue = .true. case ('yes') lvalue = .true. case ('on') lvalue = .true. case ('t') lvalue = .true. case ('true') lvalue = .true. case ('n') lvalue = .false. case ('no') lvalue = .false. case ('off') lvalue = .false. case ('f') lvalue = .false. case ('false') lvalue = .false. case default print "(a)", "Error, answer y(es)/t(rue)/on or n(o)/f(alse)/off" call logical_prompt(text, lvalue, default) end select end subroutine logical_prompt ! ! String prompting routine ! recursive subroutine string_prompt(text, string, length, case, noblank, list) character(len=*), intent(in) :: text character(len=*), intent(inout) :: string character(len=128) :: newstring integer, optional, intent(out) :: length integer, optional, intent(in) :: case logical, optional, intent(in) :: noblank integer :: is, ia integer, parameter :: aoffset = 32 logical :: allowblank,inlist character(len=*), dimension(:), intent(in), optional :: list ! ! Write prompt string to terminal ! if (present(noblank)) then allowblank = .not.noblank else allowblank = .true. endif if (allowblank .and. len_trim(adjustl(string)).gt.0) then write(*,"(a,1x,'(blank=""blank"",default=""',a,'""):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(string)) else write(*,"(a,1x,'(default=""',a,'""):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(string)) endif ! ! Read new value, quit and keep old value if zero sized string ! read(*,"(a)") newstring if (len_trim(newstring) > len(string)) then print "(a)", "Warning: string too long, will be truncated" endif if (allowblank .and. trim(adjustl(newstring)).eq.'blank') then string = ' ' elseif ( len_trim(adjustl(newstring)) /= 0 ) then string = newstring elseif ( .not.allowblank .and. len_trim(adjustl(string)).eq.0 ) then print "(a)", "Error, cannot enter blank string" if (present(list)) then call string_prompt(text,string,noblank=.not.allowblank,list=list) else call string_prompt(text,string,noblank=.not.allowblank) endif endif if (present(length)) length = len_trim(string) ! ! Convert string to upper/lower case if requested ! if (present(case)) then if (case == upper) then do is = 1, len(string) ia = iachar(string(is:is)) if (ia >= iachar('a').and.ia <= iachar('z')) & string(is:is) = achar(ia-aoffset) enddo endif if (case == lower) then do is = 1, len(string) ia = iachar(string(is:is)) if (ia >= iachar('A').and.ia <= iachar('Z')) & string(is:is) = achar(ia+aoffset) enddo endif endif if (present(list)) then inlist = .false. do i=1,size(list) if (trim(adjustl(list(i)))==trim(adjustl(string))) inlist = .true. enddo if (.not.inlist) then print "(a)", "Error, value not in list" call string_prompt(text,string,noblank=.not.allowblank,list=list) endif endif end subroutine string_prompt ! ! Integer array prompting routine (D. Price) ! recursive subroutine intarr_prompt(text, value, nvalues, min, max) character(len=*), intent(in) :: text integer, dimension(:), intent(inout) :: value integer, intent(inout) :: nvalues integer, dimension(size(value)) :: newvalue character(len=64) :: valstring character(len=120) :: string character(len=16) :: chmin, chmax integer :: ios integer, optional, intent(in) :: min, max logical :: error integer :: ival,nvaluesnew chmin = '' chmax = '' error = .false. ! ! Pack arguments in strings for compact and nicer prompt ! string = ' ' do ival=1,nvalues-1 write(valstring,*,iostat=ios) value(ival) string = trim(string)//trim(adjustl(valstring))//',' enddo if (nvalues.gt.0) then write(valstring,*,iostat=ios) value(nvalues) endif string = trim(string)//trim(adjustl(valstring)) if (present(min)) write(chmin,"(g10.0)") min if (present(max)) write(chmax,"(g10.0)") max ! ! Write prompt string to terminal ! if (present(min).or.present(max)) then write(*,"(a,1x,'([',a,':',a,'],',1x,'default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(chmin)), & trim(adjustl(chmax)), trim(adjustl(string)) else write(*,"(a,1x,'(default=',a,'):',1x)",advance='no') & trim(adjustl(text)), trim(adjustl(string)) endif ! ! Read new value, quit and keep old value if zero sized string ! read(*,"(a)") string if (len(trim(adjustl(string))) == 0) return ! !--register how many new values read ! newvalue = -huge(0) read(string,*,iostat=ios) newvalue(:) nvaluesnew = 0 do ival=1,size(newvalue) if (newvalue(ival).ne.-huge(0)) nvaluesnew = nvaluesnew + 1 enddo ! ! Check if new string is of right type and within given range ! if (nvaluesnew <= 0) then print "(a)", "Error, no integer numbers could be read" error = .true. else if (present(min)) then if (any(newvalue(1:nvaluesnew) < min)) then print "(a)", "Error, value(s) out of range (min)" error = .true. endif endif if (present(max)) then if (any(newvalue(1:nvaluesnew) > max)) then print "(a)", "Error, value(s) out of range (max)" error = .true. endif endif endif ! ! Assign new value if everything is ok, else prompt again ! if (error) then call intarr_prompt(text, value, nvalues, min, max) else value = newvalue nvalues = nvaluesnew endif end subroutine intarr_prompt ! ! Routine added by D.Price (31/10/06) ! Takes in a logical variable and returns a string 'on' or 'off' as appropriate ! function print_logical(lvalue,mask) implicit none logical, intent(in) :: lvalue logical, intent(in), optional :: mask character(len=3) :: print_logical logical :: maskval maskval = .true. if (present(mask)) maskval = mask if (maskval) then if (lvalue) then print_logical = 'ON' else print_logical = 'OFF' endif else print_logical = ' -' endif end function print_logical end module prompting splash/src/asciiutils.f90000644 000766 000000 00000064322 13261626263 016253 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !--------------------------------------------------------------------------- ! module containing various utility subroutines ! related to reading from ascii files and dealing with string variables ! ! written by Daniel Price, University of Exeter 2007 24th April '07 ! revised at Monash University, Nov '08. ! daniel.price@monash.edu ! ! this is a standalone module with no dependencies !--------------------------------------------------------------------------- module asciiutils implicit none public :: read_asciifile,get_ncolumns,get_nrows,ncolumnsline,safename,basename public :: cstring,fstring,add_escape_chars public :: string_replace, string_delete, nheaderlines, string_sub public :: ucase,lcase public :: get_line_containing public :: enumerate,isdigit,split public :: get_column_labels private !-------------------------------------------------- ! Generic interface to ascii file read for either ! character arrays (ie. each line is an element) ! or an array of real numbers !-------------------------------------------------- interface read_asciifile module procedure read_asciifile_char, read_asciifile_real,& read_asciifile_real_string, read_asciifile_realarr end interface read_asciifile contains !--------------------------------------------------------------------------- ! Generic subroutine to read all lines of an ascii file ! returns array of character strings (one per line) ! up to a maximum corresponding to the size of the array !--------------------------------------------------------------------------- subroutine read_asciifile_char(filename,nlinesread,charline,ierror) implicit none character(len=*), intent(in) :: filename integer, intent(out) :: nlinesread character(len=*), dimension(:), intent(out) :: charline integer, intent(out), optional :: ierror integer, parameter :: iunit = 66 ! logical unit number for read operation integer :: ierr,i,maxlines logical :: iexist nlinesread = 0 if (present(ierror)) ierror = 0 !--if file does not exist, do nothing and return inquire(file=filename,exist=iexist) if (.not.iexist) then if (present(ierror)) ierror = -1 return endif open(unit=iunit,file=filename,status='old',form='formatted',iostat=ierr) !--error opening file (but file does exist) if (ierr /= 0) then print "(a)",' ERROR opening '//trim(filename) if (present(ierror)) ierror = ierr return endif maxlines = size(charline) do i=1,maxlines read(iunit,"(a)",err=66,end=99) charline(i) enddo !--end of array limits ! check to see if there is anything more in the file. Report error if there is. read(iunit,"(a)",iostat=ierr) if (ierr.eq.0) then print "(a,i6)",' WARNING: array limits reached reading '//trim(filename)//', max = ',maxlines endif nlinesread = maxlines close(unit=iunit) return !--error encountered 66 continue print "(a,i6)",' ERROR reading '//trim(filename)//' at line ',i-1 if (present(ierror)) ierror = 1 nlinesread = i-1 close(unit=iunit) return !--reached end of file (the expected behaviour) 99 continue nlinesread = i-1 close(unit=iunit) return end subroutine read_asciifile_char !--------------------------------------------------------------------------- ! Generic subroutine to read all lines of an ascii file ! returns array of real numbers (either one per line or all on same line) ! up to a maximum corresponding to the size of the array !--------------------------------------------------------------------------- subroutine read_asciifile_real(filename,nlinesread,realarr,ierror) implicit none character(len=*), intent(in) :: filename integer, intent(out) :: nlinesread real, dimension(:), intent(out) :: realarr integer, intent(out), optional :: ierror integer, parameter :: iunit = 66 ! logical unit number for read operation integer :: ierr,i,maxlines logical :: iexist nlinesread = 0 if (present(ierror)) ierror = 0 !--if file does not exist, do nothing and return inquire(file=filename,exist=iexist) if (.not.iexist) then if (present(ierror)) ierror = -1 return endif open(unit=iunit,file=filename,status='old',form='formatted',iostat=ierr) !--error opening file (but file does exist) if (ierr /= 0) then print "(a)",' ERROR opening '//trim(filename) if (present(ierror)) then ierror = ierr endif return endif realarr(:) = -666. maxlines = size(realarr) read(iunit,*,err=66,end=99) (realarr(i),i=1,maxlines) !--end of array limits print "(a,i6)",' WARNING: array limits reached reading '//trim(filename)//', max = ',maxlines nlinesread = maxlines close(unit=iunit) return !--error encountered 66 continue print "(a,i6)",' ERROR reading '//trim(filename)//' at line ',i-1 if (present(ierror)) ierror = 1 do i=1,maxlines if (abs(realarr(i)+666.).gt.tiny(0.)) nlinesread = nlinesread + 1 enddo close(unit=iunit) return !--reached end of file (the expected behaviour) 99 continue do i=1,maxlines if (abs(realarr(i)+666.).gt.tiny(0.)) nlinesread = nlinesread + 1 enddo close(unit=iunit) return end subroutine read_asciifile_real !--------------------------------------------------------------------------- ! Generic subroutine to read all lines of an ascii file ! returns 2D array of real numbers (i.e. tabulated data) !--------------------------------------------------------------------------- subroutine read_asciifile_realarr(filename,nlinesread,realarr,ierror) implicit none character(len=*), intent(in) :: filename integer, intent(out) :: nlinesread real, dimension(:,:), intent(out) :: realarr integer, intent(out), optional :: ierror integer, parameter :: iunit = 66 ! logical unit number for read operation integer :: ierr,i,ncols,ncolsfile,nheader logical :: iexist nlinesread = 0 if (present(ierror)) ierror = 0 !--if file does not exist, do nothing and return inquire(file=filename,exist=iexist) if (.not.iexist) then if (present(ierror)) ierror = -1 return endif open(unit=iunit,file=filename,status='old',form='formatted',iostat=ierr) !--error opening file (but file does exist) if (ierr /= 0) then print "(a)",' ERROR opening '//trim(filename) if (present(ierror)) ierror = ierr return else ! get number of columns call get_ncolumns(iunit,ncolsfile,nheader) ! skip header lines do i=1,nheader read(iunit,*,iostat=ierr) enddo ! read 2D array from file ncols = min(ncolsfile,size(realarr(:,1))) nlinesread = 0 do while (ierr==0) nlinesread = nlinesread + 1 read(iunit,*,iostat=ierr) realarr(1:ncols,nlinesread) enddo nlinesread = max(nlinesread - 1,0) close(iunit) endif end subroutine read_asciifile_realarr !--------------------------------------------------------------------------- ! Generic subroutine to read all lines of an ascii file ! returns array of real numbers and corresponding string ! up to a maximum corresponding to the size of the array !--------------------------------------------------------------------------- subroutine read_asciifile_real_string(filename,nlinesread,realarr,charline,ierror) implicit none character(len=*), intent(in) :: filename integer, intent(out) :: nlinesread real, dimension(:), intent(out) :: realarr character(len=*), dimension(:), intent(out) :: charline integer, intent(out), optional :: ierror integer, parameter :: iunit = 66 ! logical unit number for read operation integer :: ierr,i,maxlines logical :: iexist nlinesread = 0 if (present(ierror)) ierror = 0 !--if file does not exist, do nothing and return inquire(file=filename,exist=iexist) if (.not.iexist) then if (present(ierror)) ierror = -1 return endif open(unit=iunit,file=filename,status='old',form='formatted',iostat=ierr) !--error opening file (but file does exist) if (ierr /= 0) then print "(a)",' ERROR opening '//trim(filename) if (present(ierror)) then ierror = ierr endif return endif if (size(realarr) /= size(charline)) then print "(a)",' WARNING: array size mismatch in call to read_asciifile' endif realarr(:) = -666. maxlines = min(size(realarr),size(charline)) read(iunit,*,err=66,end=99) (realarr(i),charline(i),i=1,maxlines) !--end of array limits print "(a,i6)",' WARNING: array limits reached reading '//trim(filename)//', max = ',maxlines nlinesread = maxlines close(unit=iunit) return !--error encountered 66 continue print "(a,i6)",' ERROR reading '//trim(filename)//' at line ',i-1 if (present(ierror)) ierror = 1 do i=1,maxlines if (abs(realarr(i)+666.).gt.tiny(0.)) nlinesread = nlinesread + 1 enddo close(unit=iunit) return !--reached end of file (the expected behaviour) 99 continue do i=1,maxlines if (abs(realarr(i)+666.).gt.tiny(0.)) nlinesread = nlinesread + 1 enddo close(unit=iunit) return end subroutine read_asciifile_real_string !--------------------------------------------------------------------------- ! utility to work out number of columns of real numbers ! in an ascii file ! ! file must already be open and at the start ! slightly ad-hoc but its the best way I could think of! !--------------------------------------------------------------------------- subroutine get_ncolumns(lunit,ncolumns,nheaderlines,maxheaderlines) implicit none integer, intent(in) :: lunit integer, intent(out) :: ncolumns,nheaderlines integer, intent(in), optional :: maxheaderlines integer :: ierr,ncolprev,ncolsthisline,maxlines character(len=5000) :: line logical :: nansinfile,infsinfile if (present(maxheaderlines)) then maxlines = maxheaderlines else maxlines = 1000 endif nheaderlines = 0 line = ' ' ierr = 0 ncolumns = 0 ncolprev = -100 ncolsthisline = 0 nansinfile = .false. infsinfile = .false. ! !--loop until we find two consecutive lines with the same number of columns (but non zero) ! do while ((len_trim(line).eq.0 .or. ncolsthisline.ne.ncolprev .or. ncolumns.le.0) & .and. ierr.eq.0 .and. nheaderlines <= maxlines) ncolprev = ncolumns read(lunit,"(a)",iostat=ierr) line if (index(line,'NaN').gt.0) nansinfile = .true. if (index(line,'Inf').gt.0) infsinfile = .true. if (len_trim(line).eq.0) then ncolsthisline = -1 else if (ierr.eq.0) ncolsthisline = ncolumnsline(line) ncolumns = ncolsthisline endif nheaderlines = nheaderlines + 1 !print*,'DEBUG: header line ',nheaderlines,' ncols = ',ncolsthisline,'"'//trim(line)//'"' enddo !--subtract 2 from the header line count (the last two lines which were the same) nheaderlines = max(nheaderlines - 2,0) if (ierr .gt.0 .or. ncolumns.le.0) then ncolumns = 0 elseif (ierr .lt. 0) then !print*,ncolumns,ncolprev endif if (nansinfile) print "(a)",' INDIAN BREAD WARNING!! NaNs in file!!' if (infsinfile) print "(a)",' WARNING!! Infs in file!!' rewind(lunit) if (ncolumns.eq.0) print "(a)",' ERROR: no columns of real numbers found' end subroutine get_ncolumns !--------------------------------------------------------------------------- ! utility to work out number of rows in file !--------------------------------------------------------------------------- subroutine get_nrows(lunit,nheaderlines,nlines) implicit none integer, intent(in) :: lunit,nheaderlines integer, intent(out) :: nlines integer :: ierr,i rewind(lunit) ierr = 0 do i=1,nheaderlines read(lunit,*,iostat=ierr) enddo nlines = 0 do while (ierr==0) read(lunit,*,iostat=ierr) if (ierr==0) nlines = nlines + 1 enddo end subroutine get_nrows !--------------------------------------------------------------------------- ! ! function returning the number of columns of real numbers from a given line ! !--------------------------------------------------------------------------- integer function ncolumnsline(line) implicit none character(len=*), intent(in) :: line real :: dummyreal(1000) integer :: ierr,i dummyreal = -666666.0 ierr = 0 read(line,*,iostat=ierr) (dummyreal(i),i=1,size(dummyreal)) i = 1 ncolumnsline = 0 do while(abs(dummyreal(i)+666666.).gt.tiny(0.) .or. dummyreal(i).ne.dummyreal(i)) ncolumnsline = ncolumnsline + 1 i = i + 1 if (i.gt.size(dummyreal)) then print "(a)",'*** ERROR: too many columns in file' ncolumnsline = size(dummyreal) return endif enddo end function ncolumnsline !---------------------------------------------------------------------- ! ! Small utility to return the number of comment lines in an ascii ! file. These are lines that do not begin with a number. ! ! This is slightly different to what is done in the get_ncolumns ! routine, where header lines are any lines not having the same number ! of columns. Here we do not attempt to evaluate the number of data ! columns. ! ! File must be open and at the desired starting position !---------------------------------------------------------------------- integer function nheaderlines(lunit) integer, intent(in) :: lunit real :: dum integer :: ierr dum = -666. nheaderlines = 0 ierr = -1 do while (abs(dum+666.).lt.tiny(0.) .or. ierr.ne.0) nheaderlines = nheaderlines + 1 read(lunit,*,iostat=ierr) dum enddo nheaderlines = nheaderlines - 1 end function nheaderlines !--------------------------------------------------------------------------- ! ! function stripping '/', '\' and spaces out of filenames ! !--------------------------------------------------------------------------- function safename(string) implicit none character(len=*), intent(in) :: string character(len=len(string)) :: safename integer :: ipos safename = string !--remove forward slashes which can be mistaken for directories: replace with '_' call string_replace(safename,'/','_') call string_replace(safename,' ','_') !--delete brackets and operators of all kinds call string_delete(safename,'{') call string_delete(safename,'}') call string_delete(safename,'(') call string_delete(safename,')') call string_delete(safename,'[') call string_delete(safename,']') call string_delete(safename,'<') call string_delete(safename,'>') call string_delete(safename,'*') call string_delete(safename,'?') call string_delete(safename,'^') call string_delete(safename,'''') call string_delete(safename,'"') call string_delete(safename,'&') call string_delete(safename,'#') call string_delete(safename,'|') !--remove escape sequences: remove '\' and position following ipos = index(trim(safename),'\') do while (ipos.ne.0) safename = safename(1:ipos-1)//safename(ipos+2:len_trim(safename)) ipos = index(trim(safename),'\') enddo end function safename !--------------------------------------------------------------------------- ! ! function to insert escape characters so filenames appear correctly in legend ! !--------------------------------------------------------------------------- function add_escape_chars(string) implicit none character(len=*), intent(in) :: string character(len=len(string)) :: add_escape_chars add_escape_chars = string call string_replace(add_escape_chars,'_','\_') call string_replace(add_escape_chars,'^','\^') end function add_escape_chars !--------------------------------------------------------------------------- ! ! function stripping the directory off a filename ! !--------------------------------------------------------------------------- function basename(string) implicit none character(len=*), intent(in) :: string character(len=len(string)) :: basename integer :: i,iposmax basename = string !--find the last forward slash iposmax = 0 i = len_trim(string) do while(i.ge.2 .and. iposmax.eq.0) i = i - 1 if (string(i:i).eq.'/') iposmax = i enddo basename = trim(string(iposmax+1:)) end function basename !--------------------------------------------------------------------------- ! ! function to safely convert a string to c format (ie. with a terminating ! ascii null character) ! !--------------------------------------------------------------------------- function cstring(string) implicit none character(len=*), intent(in) :: string character(len=len(string)+1) :: cstring cstring = trim(string)//achar(0) end function cstring !--------------------------------------------------------------------------- ! ! function to safely convert a string from c format (ie. with a terminating ! ascii null character) back to a normal Fortran string ! !--------------------------------------------------------------------------- function fstring(array) use, intrinsic :: iso_c_binding, only:c_char implicit none character(kind=c_char), dimension(:), intent(in) :: array character(len=size(array)-1) :: fstring integer :: i fstring = '' do i=1,size(array) if (array(i).eq.achar(0)) exit fstring(i:i) = array(i) enddo end function fstring !--------------------------------------------------------------------------- ! ! subroutine to replace a matching section of a string with another ! string, possibly of differing length ! !--------------------------------------------------------------------------- subroutine string_replace(string,skey,sreplacewith) implicit none character(len=*), intent(inout) :: string character(len=*), intent(in) :: skey,sreplacewith character(len=len(string)) :: remstring integer :: ipos,ioffset,lensub ipos = index(trim(string),skey) lensub = len(skey) do while(ipos.gt.0) remstring = string(ipos+lensub:len_trim(string)) ioffset = ipos - 1 + len(sreplacewith) string = string(1:ipos-1)//sreplacewith//remstring ipos = index(trim(remstring),skey) if (ipos > 0) ipos = ipos + ioffset enddo end subroutine string_replace !--------------------------------------------------------------------------- ! ! subroutine to replace a specified section of a string with a ! replacement string, possibly of differing length ! !--------------------------------------------------------------------------- subroutine string_sub(string,i1,i2,sreplacewith) implicit none character(len=*), intent(inout) :: string integer, intent(in) :: i1,i2 character(len=*), intent(in) :: sreplacewith character(len=len(string)) :: oldstring oldstring = string if (i2 < len_trim(string)) then string = oldstring(1:i1-1)//sreplacewith//oldstring(i2+1:len_trim(oldstring)) else string = oldstring(1:i1-1)//sreplacewith endif end subroutine string_sub !--------------------------------------------------------------------------- ! ! subroutine to delete all matching occurrences of key from string ! !--------------------------------------------------------------------------- pure subroutine string_delete(string,skey) implicit none character(len=*), intent(inout) :: string character(len=*), intent(in) :: skey integer :: ipos,lensub ipos = index(trim(string),skey) lensub = len(skey) do while(ipos.gt.0) string = string(1:ipos-1)//string(ipos+lensub:len_trim(string)) ipos = index(trim(string),skey) enddo end subroutine string_delete !--------------------------------------------------------------------------- ! ! Converts a string to upper case ! !--------------------------------------------------------------------------- function ucase(string) implicit none character(len=*), intent(in) :: string character(len=len(string)) :: ucase integer :: is,ia integer, parameter :: aoffset = 32 ucase = string do is = 1, len(ucase) ia = iachar(ucase(is:is)) if (ia >= iachar('a').and.ia <= iachar('z')) & ucase(is:is) = achar(ia-aoffset) enddo end function ucase !--------------------------------------------------------------------------- ! ! Converts a string to lower case ! !--------------------------------------------------------------------------- function lcase(string) implicit none character(len=*), intent(in) :: string character(len=len(string)) :: lcase integer :: is,ia integer, parameter :: aoffset = 32 lcase = string do is = 1, len(lcase) ia = iachar(lcase(is:is)) if (ia >= iachar('A').and.ia <= iachar('Z')) & lcase(is:is) = achar(ia+aoffset) enddo end function lcase !--------------------------------------------------------------------------- ! ! indicate if a character is a digit (number) or not ! !--------------------------------------------------------------------------- logical function isdigit(string) character(len=1), intent(in) :: string integer :: ia isdigit = .false. ia = iachar(string) if (ia >= iachar('0').and.ia <= iachar('9')) isdigit = .true. end function isdigit !--------------------------------------------------------------------------- ! ! search a file for the line containing a particular string ! !--------------------------------------------------------------------------- integer function get_line_containing(filename,string) character(len=*), intent(in) :: filename, string character(len=130) :: line integer :: i,ierr integer, parameter :: lu=95 get_line_containing = 0 open(unit=lu,file=filename,status='old',iostat=ierr) i = 0 do while(ierr.eq.0) i = i + 1 read(lu,"(a)",iostat=ierr) line if (index(line,string).ne.0) get_line_containing = i enddo close(lu) end function get_line_containing !--------------------------------------------------------------------------- ! ! Convert an integer into the corresponding entry in a list of strings ! !--------------------------------------------------------------------------- function enumerate(i,stringarr,default) result(string) integer, intent(in) :: i character(len=*), intent(in), dimension(:) :: stringarr integer, intent(in), optional :: default character(len=len(stringarr)) :: string string = '' if (i >= 1 .and. i <= size(stringarr)) then string = trim(stringarr(i)) elseif (present(default)) then if (default >= 1 .and. i <= size(stringarr)) then string = trim(stringarr(default)) endif endif end function enumerate !--------------------------------------------------------------------------- ! ! Split a string into substrings based on a delimiter ! !--------------------------------------------------------------------------- subroutine split(string,delim,stringarr,nsplit) character(len=*), intent(in) :: string character(len=*), intent(in) :: delim character(len=*), intent(out), dimension(:) :: stringarr integer, intent(out) :: nsplit integer :: i,j i = 1 nsplit = 0 do while(nsplit < size(stringarr) .and. i < len(string)) ! find next non-blank character do while (string(i:i)==' ') i = i + 1 if (i > len(string)) exit enddo i = i - 1 if (i < 1) i = 1 ! first character is non-blank ! look for next occurrence of delimiter j = index(string(i:),delim) if (j==0) j = len(string(i:)) nsplit = nsplit + 1 if (nsplit <= size(stringarr)) then stringarr(nsplit) = string(i:min(i+j,len(string))) endif i = i + j + 1 enddo end subroutine split !--------------------------------------------------------------------------- ! ! extract a list of labels from the header line of a file ! !--------------------------------------------------------------------------- subroutine get_column_labels(line,nlabels,labels) character(len=*), intent(in) :: line integer, intent(out) :: nlabels character(len=*), dimension(:), intent(out) :: labels integer :: i1,i2,i,nlabelstmp character(len=1) :: leadingchar nlabels = 0 i1 = 1 ! ! strip leading comment character ('#') ! leadingchar = trim(adjustl(line)) if (leadingchar=='#') then i1 = index(line,'#') + 1 endif ! strip anything preceding an equals sign i1 = max(i1,index(line,'=')+1) i2 = i1 if (index(line,']') > 0) then ! ! format style 1: # [ mylabel1 ] [ mylabel2 ] [ mylabel3 ] ! call split(line(i1:),']',labels,nlabels) elseif (index(line,',') > 1) then ! ! format style 2: mylabel1,mylabel2,mylabel3 ! call split(line(i1:),',',labels,nlabelstmp) nlabels = count_sensible_labels(nlabelstmp,labels) else ! ! format style 3: # mylabel1 mylabel2 mylabel3 ! call split(line(i1:),' ',labels,nlabelstmp) ! ! this style is dangerous, so perform sanity checks ! on the labels to ensure they are sensible ! nlabels = count_sensible_labels(nlabelstmp,labels) endif ! ! clean up ! do i=1,nlabels ! delete brackets if (nlabels <= size(labels)) then call string_delete(labels(i),',') call string_delete(labels(i),'[') call string_delete(labels(i),']') labels(i) = trim(adjustl(labels(i))) ! delete leading numbers i1 = 1 do while (isdigit(labels(i)(i1:i1))) labels(i)(i1:i1) = ' ' i1 = i1 + 1 enddo labels(i) = trim(adjustl(labels(i))) endif enddo end subroutine get_column_labels !--------------------------------------------------------------------------- ! ! count the number of sensible labels in a list of possible labels ! !--------------------------------------------------------------------------- integer function count_sensible_labels(n,labels) result(m) integer, intent(in) :: n character(len=*), dimension(n), intent(in) :: labels integer :: i m = 0 do i=1,n if (is_sensible_label(labels(i))) m = m + 1 enddo end function count_sensible_labels !--------------------------------------------------------------------------- ! ! determine if a particular string makes sense as a column label or not ! !--------------------------------------------------------------------------- logical function is_sensible_label(string) character(len=*), intent(in) :: string real :: dum integer :: ierr is_sensible_label = .true. ! should not contain equals sign !if (index(string,'=') > 0) is_sensible_label = .false. ! should not be able to read it as a real number read(string,*,iostat=ierr) dum if (ierr==0) is_sensible_label = .false. end function is_sensible_label end module asciiutils splash/src/geomutils.f90000644 000766 000000 00000023475 13261626263 016116 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! Utility module containing wrapper routines for coordinate ! transformations !----------------------------------------------------------------- module geomutils implicit none public :: change_coords, changecoords, changeveccoords public :: set_coordlabels private contains !----------------------------------------------------------------- ! transform all columns of data for a given particle ! to new coordinate system (does both coordinates ! and components of vectors) ! this version uses DOUBLE PRECISION for vals !----------------------------------------------------------------- subroutine change_coords(vals,ncols,ndim,icoords,icoordsnew,x0,v0) use params, only:doub_prec use geometry, only:coord_transform,vector_transform use labels, only:ix,iamvec,ivx implicit none integer, intent(in) :: ncols,ndim,icoords,icoordsnew real(kind=doub_prec), dimension(ncols), intent(inout) :: vals real, dimension(ndim), intent(in) :: x0,v0 real, dimension(ndim) :: xcoords,xcoordsnew,vec,vecnew integer :: iamvecprev,icol !--perform transformations on coordinates xcoords(1:ndim) = vals(ix(1:ndim)) - x0(1:ndim) call coord_transform(xcoords(1:ndim),ndim,icoords,xcoordsnew(1:ndim),ndim,icoordsnew) vals(ix(1:ndim)) = xcoordsnew(1:ndim) !--transform all vector quantities to new coord system iamvecprev = 0 do icol=1,ncols - ndim + 1 if (iamvec(icol).gt.0 .and. iamvec(icol).ne.iamvecprev) then iamvecprev = iamvec(icol) if (icol.eq.ivx) then vec(1:ndim) = vals(iamvec(icol):iamvec(icol)+ndim-1) - v0(1:ndim) else vec(1:ndim) = vals(iamvec(icol):iamvec(icol)+ndim-1) endif call vector_transform(xcoords,vec,ndim,icoords,vecnew,ndim,icoordsnew) vals(iamvec(icol):iamvec(icol)+ndim-1) = vecnew(1:ndim) endif enddo end subroutine change_coords !------------------------------------------------------------------- ! interface to coordinate-system transformations !------------------------------------------------------------------- subroutine changecoords(iplotx,iploty,iplotz,xplot,yplot,zplot,ntot,ndim,itrackpart,dat) use geometry, only:coord_transform,labelcoordsys use settings_data, only:xorigin,icoords,icoordsnew,debugmode use labels, only:is_coord,ix implicit none integer, intent(in) :: iplotx,iploty,iplotz,ntot,ndim,itrackpart real, dimension(:), intent(inout) :: xplot,yplot,zplot real, dimension(:,:), intent(in) :: dat real, dimension(ndim) :: xcoords,xcoordsnew integer :: j,ixcoord,iycoord,izcoord logical :: iscoordx,iscoordy,iscoordz iscoordx = is_coord(iplotx,ndim) iscoordy = is_coord(iploty,ndim) iscoordz = is_coord(iplotz,ndim) ! should always be true if x and y are coords if (iscoordx .or. iscoordy) then if (debugmode) print*,'changing coords from ',trim(labelcoordsys(icoords)), & ' to ',trim(labelcoordsys(icoordsnew)) if (itrackpart.gt.0) print*,'coords relative to particle ',itrackpart !--get offsets in range 1->ndim for the case where particle ! coords are not first in plot arrays ixcoord = iplotx - ix(1) + 1 if (iscoordx .and. (ixcoord.le.0 .or. ixcoord.gt.ndim)) then print*,'ERROR in x coordinate offset in arrays: cannot change coordinate system' return endif iycoord = iploty - ix(1) + 1 if (iscoordy .and. (iycoord.le.0 .or. iycoord.gt.ndim)) then print*,'ERROR in y coordinate offset in arrays: cannot change coordinate system' return endif izcoord = iplotz - ix(1) + 1 if (iscoordz .and. (izcoord.le.0 .or. izcoord.gt.ndim)) then print*,'ERROR in z coordinate offset in arrays: cannot change coordinate system' return endif do j=1,ntot if (itrackpart.gt.0 .and. itrackpart.le.ntot) then xcoords(1:ndim) = dat(j,ix(1:ndim)) - dat(itrackpart,ix(1:ndim)) else xcoords(1:ndim) = dat(j,ix(1:ndim)) - xorigin(1:ndim) endif call coord_transform(xcoords(1:ndim),ndim,icoords, & xcoordsnew(1:ndim),ndim,icoordsnew) if (iscoordx) xplot(j) = xcoordsnew(ixcoord) if (iscoordy) yplot(j) = xcoordsnew(iycoord) if (iscoordz) zplot(j) = xcoordsnew(izcoord) enddo endif end subroutine changecoords !------------------------------------------------------------------- ! interface to coordinate-system transformations for vectors !------------------------------------------------------------------- subroutine changeveccoords(iplot,xploti,ntot,ndim,itrackpart,dat) use geometry, only:vector_transform,labelcoordsys use settings_data, only:xorigin,icoords,icoordsnew,debugmode use labels, only:ivx,iamvec,ix implicit none integer, intent(in) :: iplot,ntot,ndim,itrackpart real, dimension(:), intent(inout) :: xploti real, dimension(ndim) :: xcoords,vecnew,vecin real, dimension(:,:), intent(in) :: dat integer :: j if (iamvec(iplot).gt.0) then if (iplot-iamvec(iplot)+1 .le. ndim) then if (debugmode) print*,'changing vector component from ', & trim(labelcoordsys(icoords)),' to ',trim(labelcoordsys(icoordsnew)) if (itrackpart.gt.0 .and. iamvec(iplot).eq.ivx) then print*,'velocities relative to particle ',itrackpart endif do j=1,ntot if (itrackpart.gt.0 .and. itrackpart.le.ntot) then xcoords(1:ndim) = dat(j,ix(1:ndim)) - dat(itrackpart,ix(1:ndim)) if (iamvec(iplot).eq.ivx) then vecin(1:ndim) = dat(j,iamvec(iplot):iamvec(iplot)+ndim-1) & - dat(itrackpart,iamvec(iplot):iamvec(iplot)+ndim-1) else vecin(1:ndim) = dat(j,iamvec(iplot):iamvec(iplot)+ndim-1) endif else xcoords(1:ndim) = dat(j,ix(1:ndim)) - xorigin(1:ndim) vecin(1:ndim) = dat(j,iamvec(iplot):iamvec(iplot)+ndim-1) endif call vector_transform(xcoords(1:ndim),vecin(1:ndim), & ndim,icoords,vecnew(1:ndim),ndim,icoordsnew) xploti(j) = vecnew(iplot-iamvec(iplot)+1) enddo else print*,'error: can''t convert vector components with ndimV > ndim' endif endif return end subroutine changeveccoords !---------------------------------------------------------------- ! ! routine to set labels for vector quantities and spatial ! coordinates depending on the coordinate system used. ! !---------------------------------------------------------------- subroutine set_coordlabels(numplot) use geometry, only:labelcoord,coord_is_length use labels, only:label,unitslabel,iamvec,labelvec,ix,labeldefault use settings_data, only:icoords,icoordsnew,ndim,iRescale,debugmode implicit none integer, intent(in) :: numplot integer :: i integer, save :: icoordsprev = -1 ! !--sanity check on icoordsnew... ! (should not be zero) ! if (icoordsnew.le.0) then if (icoords.gt.0) then icoordsnew = icoords else icoordsnew = 1 endif endif ! !--store the previous value of icoordsnew that was used ! last time we adjusted the labels ! if (icoordsprev.lt.0) icoordsprev = icoordsnew ! !--set coordinate and vector labels (depends on coordinate system) ! if (icoordsnew.ne.icoords .or. icoordsnew.ne.icoordsprev) then ! !--here we are using a coordinate system that differs from the original ! one read from the code (must change labels appropriately) ! if (debugmode) print*,'DEBUG: changing coordinate labels ...' do i=1,ndim if (ix(i).gt.0) then label(ix(i)) = labelcoord(i,icoordsnew) if (iRescale .and. coord_is_length(i,icoordsnew)) then label(ix(i)) = trim(label(ix(i)))//trim(unitslabel(ix(i))) endif endif enddo ! elseif (icoordsnew.ne.icoordsprev) then !! !!--here we are reverting back to the original coordinate system !! so we have to re-read the original labels from the data read !! ! call get_labels endif ! !--set vector labels if iamvec is set and the labels are the default ! if (icoordsnew.gt.0) then do i=1,numplot if (iamvec(i).ne.0 .and. & (icoordsnew.ne.icoords .or. icoordsnew.ne.icoordsprev & .or. index(label(i),trim(labeldefault)).ne.0)) then if (i-iamvec(i)+1 .gt. 0) then if (icoordsnew.eq.1) then label(i) = trim(labelvec(iamvec(i)))//'_'//trim(labelcoord(i-iamvec(i)+1,icoordsnew)) else label(i) = trim(labelvec(iamvec(i)))//'_{'//trim(labelcoord(i-iamvec(i)+1,icoordsnew))//'}' endif else print "(a,i2,a,i2)",' ERROR with vector labels, referencing '// & trim(labelvec(iamvec(i)))//' in column ',i,' iamvec = ',iamvec(i) endif if (iRescale) then label(i) = trim(label(i))//'\u'//trim(unitslabel(i)) endif endif enddo endif icoordsprev = icoordsnew return end subroutine set_coordlabels end module geomutils splash/src/read_data_ascii.f90000644 000766 000000 00000043030 13261626263 017147 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR GENERAL ASCII DATA FORMATS ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! ASPLASH_COLUMNSFILE gives the location of the default 'columns' file ! (overridden by the presence of a `columns' file in the working directory) ! ! ASPLASH_NCOLUMNS can be used to override the automatic ncolumns choice ! ! e.g. setenv ASPLASH_NCOLUMNS=10 ! ! ASPLASH_NHEADERLINES can be used to override the automatic number of header line determination ! ! e.g. setenv ASPLASH_NHEADERLINES=1 ! ! ASPLASH_TIMEVAL can be used to set the time (fixed for all files) ! ASPLASH_GAMMAVAL can be used to set gamma (fixed for all files) ! ASPLASH_HEADERLINE_TIME can be used to set the header line where the time is listed ! ASPLASH_HEADERLINE_GAMMA can be used to set the header line where gamma is listed ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- module asciiread integer :: icoltype end module asciiread subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,time,gamma,maxpart,maxcol,maxstep,iamtype use params use settings_data, only:ndim,ndimV,ncolumns,ncalc,iverbose,ntypes use mem_allocation, only:alloc use asciiutils, only:get_ncolumns,get_column_labels,isdigit use system_utils, only:ienvironment,renvironment use asciiread, only:icoltype use labels, only:lenlabel,labeltype,print_types,label implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,ierr,iunit,ncolstep,ncolenv,nerr,iheader_time,iheader_gamma integer :: nprint,npart_max,nstep_max,icol,nheaderlines,nheaderenv,itype,nlabels integer :: noftype(maxparttypes),iverbose_was logical :: iexist,timeset,gammaset,got_labels real :: dummyreal character(len=len(rootname)+4) :: dumpfile character(len=2048) :: line character(len=lenlabel), dimension(size(label)) :: tmplabel character(len=30) :: string integer, parameter :: notset = -66 nstepsread = 0 nstep_max = 0 npart_max = maxpart iunit = 15 ! logical unit number for input dumpfile = trim(rootname) if (iverbose.gt.0) print "(1x,a)",'reading ascii format' print "(26('>'),1x,a,1x,26('<'))",trim(dumpfile) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions (0 means no particle coords) ! ndim = 0 ndimV = 0 j = indexstart nstepsread = 0 icoltype = 0 ! no particle type defined by default got_labels = .false. ! !--open the file and read the number of particles ! open(unit=iunit,iostat=ierr,file=dumpfile,status='old',form='formatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return else call get_ncolumns(iunit,ncolstep,nheaderlines) !--override header lines setting nheaderenv = ienvironment('ASPLASH_NHEADERLINES',-1) if (nheaderenv.ge.0) then if (iverbose.gt.0) print*,' setting nheader lines = ',nheaderenv,' from ASPLASH_NHEADERLINES environment variable' nheaderlines = nheaderenv endif !--override columns setting with environment variable ncolenv = ienvironment('ASPLASH_NCOLUMNS',-1) if (ncolenv.gt.0) then if (iverbose.gt.0) print "(a,i3,a)",' setting ncolumns = ',ncolenv,' from ASPLASH_NCOLUMNS environment variable' ncolstep = ncolenv endif if (ncolstep.le.1) then print "(a)",'*** ERROR: could not determine number of columns in file ***' return endif do i=1,nheaderlines !--search through header for column labels read(iunit,"(a)",iostat=ierr) line if ((i.eq.1 .or. .not.got_labels) .and. ncolstep > 1) then call get_column_labels(trim(line),nlabels,tmplabel) if (nlabels>=ncolstep .and. .not.(isdigit(tmplabel(1)(1:1)) .or. tmplabel(1)(1:1)=='.')) then label(1:ncolstep) = tmplabel(1:ncolstep) got_labels = .true. endif endif enddo rewind(iunit) iverbose_was = iverbose iverbose = 0 ncolumns = ncolstep call set_labels() ! to see if types are defined iverbose = iverbose_was ! !--allocate memory initially ! nprint = 101 nstep_max = max(nstep_max,indexstart,1) if (.not.allocated(dat) .or. (nprint.gt.npart_max) .or. (ncolstep+ncalc).gt.maxcol) then npart_max = max(npart_max,INT(1.1*(nprint))) call alloc(npart_max,nstep_max,ncolstep+ncalc,mixedtypes=(icoltype > 0)) endif endif npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+1,maxcol,mixedtypes=(icoltype > 0)) endif ! !--can set either set the time and gamma explicitly ! using environment variables (fixed for all files) ! or can specify on which header line the time appears ! timeset = .false. gammaset = .false. dummyreal = renvironment('ASPLASH_TIMEVAL',errval=-1.) if (dummyreal.gt.0.) then time(j) = dummyreal timeset = .true. endif dummyreal = renvironment('ASPLASH_GAMMAVAL',errval=-1.) if (dummyreal.gt.0.) then gamma(j) = dummyreal gammaset = .true. endif iheader_time = ienvironment('ASPLASH_HEADERLINE_TIME',errval=notset) iheader_gamma = ienvironment('ASPLASH_HEADERLINE_GAMMA',errval=notset) ! !--read header lines, try to use it to set time ! if (nheaderlines.gt.0 .and. iverbose.gt.0) print*,'skipping ',nheaderlines,' header lines' do i=1,nheaderlines !--read header lines as character strings ! so that blank lines are counted in nheaderlines read(iunit,"(a)",iostat=ierr) line if (line(1:1)=='#') then read(line(2:),*,iostat=ierr) dummyreal else read(line,*,iostat=ierr) dummyreal endif if (i.eq.iheader_time .and. .not.timeset) then if (ierr.eq.0) then time(j) = dummyreal timeset = .true. print*,'setting time = ',dummyreal,' from header line ',i print*,'(determined from ASPLASH_HEADERLINE_TIME setting)' else print "(a,i2,a)",' ** ERROR reading time from header line ',i, & ' (using ASPLASH_HEADERLINE_TIME)' endif elseif (i.eq.iheader_gamma .and. .not.gammaset) then if (ierr.eq.0) then gamma(j) = dummyreal gammaset = .true. print*,'setting gamma = ',dummyreal,' from header line ',i print*,'(determined from ASPLASH_HEADERLINE_GAMMA setting)' else print "(a,i2,a)",' ** ERROR reading gamma from header line ',i, & ' (using ASPLASH_HEADERLINE_GAMMA)' endif elseif (timeset .and. .not.gammaset .and. ierr.eq.0 .and. iheader_gamma.eq.notset & .and. dummyreal.gt.1.0 .and. dummyreal.lt.2.000001) then print*,'setting gamma = ',dummyreal,' from header line ',i gamma(j) = dummyreal gammaset = .true. elseif (ierr.eq.0 .and. .not. timeset .and. iheader_time.eq.notset) then time(j) = dummyreal timeset = .true. print*,'setting time = ',dummyreal,' from header line ',i endif enddo ! !--now read the timestep data in the dumpfile ! i = 0 ierr = 0 nerr = 0 noftype(:) = 0 ntypes = 1 overparts: do while (ierr >= 0) i = i + 1 if (i.gt.npart_max) then ! reallocate memory if necessary npart_max = 10*npart_max call alloc(npart_max,nstep_max,ncolstep+ncalc,mixedtypes=(icoltype > 0)) endif read(iunit,*,iostat=ierr) (dat(i,icol,j),icol = 1,ncolstep) if (icoltype > 0 .and. icoltype <= ncolstep .and. ierr==0 .and. (size(iamtype(:,j)) > 1)) then !--set particle type from type column itype = nint(dat(i,icoltype,j)) if (itype > 0 .and. itype < maxparttypes) then iamtype(i,j) = int(itype,kind=1) else iamtype(i,j) = 1 endif itype = iamtype(i,j) noftype(itype) = noftype(itype) + 1 ntypes = max(itype,ntypes) endif if (ierr > 0) then nerr = nerr + 1 if (nerr .le. 10) print "(a,i8,a)",' ERROR reading data from line ',i+nheaderlines,', skipping' i = i - 1 ! ignore lines with errors endif enddo overparts nprint = i - 1 nstepsread = nstepsread + 1 if (nerr > 10) then print "(a,i8,a)",' *** WARNING: errors whilst reading file on ',nerr,' lines: skipped these ***' endif if (ierr < 0) then print "(2(a,i10))",' read npts = ',nprint,' ncolumns = ',ncolstep endif npartoftype(:,j) = 0 if (icoltype > 0 .and. icoltype <= ncolstep) then npartoftype(1:ntypes,j) = noftype(1:ntypes) call print_types(npartoftype(:,j),labeltype) else npartoftype(1,j) = nprint endif close(iunit) return end subroutine read_data !!------------------------------------------------------------------- !! set labels for each column of data !! !! read these from a file called 'columns' in the current directory !! then take sensible guesses as to which quantities are which !! from the column labels !! !!------------------------------------------------------------------- subroutine set_labels use asciiutils, only:lcase use labels, only:label,labeltype,ix,irho,ipmass,ih,iutherm, & ipr,ivx,iBfirst,iamvec,labelvec,lenlabel !use params, only:maxparttypes use settings_data, only:ncolumns,ndim,ndimV,UseTypeInRenderings,iverbose use geometry, only:labelcoord use system_commands, only:get_environment use filenames, only:fileprefix use asciiread, only:icoltype implicit none integer :: i,ierr,ndimVtemp character(len=120) :: columnfile character(len=lenlabel) :: labeli logical :: iexist ! !--read column labels from the columns file if it exists ! ! first look for a columns file in the current directory ! either called splash.columns or just 'columns' ! columnfile=trim(fileprefix)//'.columns' inquire(file=trim(columnfile),exist=iexist) if (.not.iexist) then columnfile='columns' inquire(file=trim(columnfile),exist=iexist) endif ! ! if it does not exist see if the environment variable is set ! and the corresponding file exists ! if (.not.iexist) then call get_environment('ASPLASH_COLUMNSFILE',columnfile) if (len_trim(columnfile).gt.0) then inquire(file=trim(columnfile),exist=iexist) if (iexist) then if (iverbose.gt.0) print "(a)",' using ASPLASH_COLUMNSFILE='//trim(columnfile) else print "(a)",' ERROR: ASPLASH_COLUMNSFILE='//trim(columnfile)//' DOES NOT EXIST' columnfile = 'columns' endif else columnfile = 'columns' endif endif open(unit=51,file=trim(columnfile),status='old',iostat=ierr) if (ierr /=0) then if (iverbose > 0) then print "(3(/,a))",' WARNING: columns file not found: using default labels',& ' To change the labels, create a file called ''columns'' ',& ' in the current directory with one label per line' endif else overcols: do i=1,ncolumns read(51,"(a)",iostat=ierr) label(i) if (ierr < 0) then if (iverbose > 0) print "(a,i3)",' end of file in columns file: read to column ',i-1 exit overcols elseif (ierr > 0) then if (iverbose > 0) print "(a)",' *** error reading from columns file ***' exit overcols endif enddo overcols close(unit=51) endif do i=1,ncolumns ! !--compare all strings in lower case, trimmed and with no preceding spaces ! labeli = trim(adjustl(lcase(label(i)))) ! !--guess positions of various quantities from the column labels ! if (ndim.le.0 .and. (labeli(1:1).eq.'x' .or. labeli(1:1).eq.'r')) then ndim = 1 ix(1) = i endif if (ndim.eq.1 .and. i.eq.ix(1)+1 .and. (labeli(1:1).eq.'y' .or. labeli(1:1).eq.'z')) then ndim = 2 ix(2) = i endif if (ndim.eq.2 .and. i.eq.ix(2)+1 .and. labeli(1:1).eq.'z') then ndim = 3 ix(3) = i endif if (labeli(1:3).eq.'den' .or. index(labeli,'rho').ne.0 .or. labeli(1:3).eq.'\gr' .or. & (index(labeli,'density').ne.0 .and. irho==0)) then irho = i elseif (labeli(1:5).eq.'pmass' .or. labeli(1:13).eq.'particle mass' & .or. index(labeli,'mass').ne.0) then ipmass = i elseif (ipmass.eq.0 .and. trim(labeli).eq.'m') then ipmass = i !--use first column labelled h as smoothing length elseif (ih.eq.0 .and. (labeli(1:1).eq.'h' & .or. labeli(1:6).eq.'smooth')) then ih = i elseif (trim(labeli).eq.'u'.or.labeli(1:6).eq.'utherm' & .or.(index(labeli,'internal energy').ne.0 .and. iutherm==0)) then iutherm = i elseif (labeli(1:2).eq.'pr' .or. trim(labeli).eq.'p' .or. & (index(labeli,'pressure').ne.0 .and. ipr==0)) then ipr = i elseif (ivx.eq.0 .and. labeli(1:1).eq.'v') then ivx = i ndimV = 1 elseif (icoltype==0 .and. index(labeli,'type').ne.0) then icoltype = i endif !--set ndimV as number of columns with v as label if (ivx.gt.0 .and. i.gt.ivx .and. i.le.ivx+2) then if (labeli(1:1).eq.'v') ndimV = i - ivx + 1 endif if (iBfirst.eq.0 .and. (labeli(1:2).eq.'bx')) then iBfirst = i endif !--set ndimV as number of columns with v as label if (iBfirst.gt.0 .and. i.gt.iBfirst .and. i.le.iBfirst+2) then if (labeli(1:1).eq.'b') then ndimVtemp = i - iBfirst + 1 if (ndimV.gt.0 .and. ndimVtemp.gt.ndimV) then if (iverbose > 0) print "(a)",' WARNING: possible confusion with vector dimensions' ndimV = ndimVtemp endif endif endif enddo if (ndim.lt.1) ndimV = 0 if (iverbose > 0) then if (ndim.gt.0) print "(a,i1)",' Assuming number of dimensions = ',ndim if (ndim.gt.0) print "(a,i2,a,i2)",' Assuming positions in columns ',ix(1),' to ',ix(ndim) if (ndimV.gt.0) print "(a,i1)",' Assuming vectors have dimension = ',ndimV if (irho.gt.0) print "(a,i2)",' Assuming density in column ',irho if (ipmass.gt.0) print "(a,i2)",' Assuming particle mass in column ',ipmass if (ih.gt.0) print "(a,i2)",' Assuming smoothing length in column ',ih if (iutherm.gt.0) print "(a,i2)",' Assuming thermal energy in column ',iutherm if (ipr.gt.0) print "(a,i2)",' Assuming pressure in column ',ipr if (ivx.gt.0) then if (ndimV.gt.1) then print "(a,i2,a,i2)",' Assuming velocity in columns ',ivx,' to ',ivx+ndimV-1 else print "(a,i2)",' Assuming velocity in column ',ivx endif endif if (icoltype.gt.0) print "(a,i2)",' Assuming particle type in column ',icoltype if (ndim.eq.0 .or. irho.eq.0 .or. ipmass.eq.0 .or. ih.eq.0) then print "(4(/,a))",' NOTE: Rendering capabilities cannot be enabled', & ' until positions of density, smoothing length and particle', & ' mass are known (for the ascii read the simplest way is to ', & ' label the relevant columns appropriately in the columns file)' endif endif if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo endif if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = 'B\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! !ntypes = 1 !!maxparttypes labeltype(1) = 'gas' UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/setpage.f90000644 000766 000000 00000036424 13261626263 015534 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module pagesetup implicit none public :: redraw_axes, setpage2 real, parameter, public :: xlabeloffset = 2.5, ylabeloffset = 4.5 private contains ! !--this subroutine determines the setup of the plotting page ! sorts out labelling of axes, positioning of windows etc ! can be used as a replacement for PGENV and PGLABEL ! ! divides up a single page into subpanels ! ! ! option to tile graphs appropriately on a page: ! divides up a single panel into subpanels, with a margin at the edge ! should replace the call to pgenv and pglabel ! ! for tiled plots the page setup looks like this: ! ! | | | | ! --+---+---+---+-- ! | 1 | 2 | 3 | ! --+---+---+---+-- ! | 4 | 5 | 6 | ! --+---+---+---+-- ! | | | | ! ! (ie. with margins in x and y) ! note that we divide up a single panel, so pgbeg should be called with nx=1,ny=1 ! ! arguments: ! iplot : current plot number ! nx : number of panels in x direction ! ny : number of panels in y direction ! xmin, : xmax, ymin, ymax : plot limits (if tiled should be same for all plots) ! labelx : x axis label (if tiled should be same for all plots) ! labely : y axis label (if tiled should be same for all plots) ! title : current plot title (can differ between plots) ! just : just=1 gives equal aspect ratios (same as in pgenv) ! axis : axes options (same as in pgenv with a few extra) ! vmarginleft,right,bottom,top : initial margin sizes (% of page (if tiled) or panel (if not)) ! (default should be zero for these) ! tile : assumes all plots can be tiled ! ! This version by Daniel Price, July 2006 ! subroutine setpage2(iplotin,nx,ny,xmin,xmax,ymin,ymax,labelx,labely,title,just,axis, & vmarginleftin,vmarginrightin,vmarginbottomin,vmargintopin, & colourbarwidth,titleoffset,isamexaxis,tile,adjustlimits,lastrow,lastplot,& yscale,labelyalt,itransy) use plotlib,only:plot_svp,plot_swin,plot_box,plot_qvsz,plot_annotate, & plot_page,plot_qcs,plot_wnad,plot_set_exactpixelboundaries, & plot_qvp use asciiutils, only:string_delete implicit none integer, intent(in) :: iplotin,nx,ny,just,axis,itransy real, intent(inout) :: xmin, xmax, ymin, ymax real, intent(in) :: colourbarwidth, titleoffset real, intent(in) :: vmarginleftin,vmarginrightin,vmargintopin,vmarginbottomin real, intent(in) :: yscale character(len=*), intent(in) :: labelx,labely,title,labelyalt logical, intent(in) :: isamexaxis,tile,adjustlimits,lastrow,lastplot integer iplot,ix,iy real vptsizeeffx,vptsizeeffy,panelsizex,panelsizey real vmargintop,vmarginbottom,vmarginleft,vmarginright real vptxmin,vptxmax,vptymin,vptymax real aspectratio,devaspectratio,x1,x2,y1,y2 real xch,ych,dx,dy,xcen,ycen character(len=10) :: xopts, yopts logical, parameter :: useexactpixelboundaries = .true. logical :: plot_alt_y_axis if (axis.eq.4) then plot_alt_y_axis = .true. else plot_alt_y_axis = .false. endif ! ! new page if iplot > number of plots on page ! if (iplotin.gt.nx*ny) then if (mod(iplotin,nx*ny).eq.1) call plot_page iplot = iplotin - (nx*ny)*((iplotin-1)/(nx*ny)) elseif (iplotin.le.0) then return else iplot = iplotin endif ! ! check for errors in input ! if (nx.le.0 .or. ny.le.0) return ! ! for tiled plots, adjust effective viewport size if just=1 and graphs are not square ! if (tile .and. just.eq.1) then if (abs(ymax-ymin) < tiny(ymin)) then print*,'setpage: error tiling plots: ymax=ymin' return endif ! ! query the current aspect ratio of the device and set aspect ratio appropriately ! call plot_qvsz(3,x1,x2,y1,y2) devaspectratio = (x2-x1)/(y2-y1) aspectratio = ((xmax-xmin)*nx)/((ymax-ymin)*ny)/devaspectratio else aspectratio = 1.0 endif ! ! set positions of x and y labels in units of character height from edge ! ! xlabeloffset = 3.0 ! ylabeloffset = 4.5 ! ! query the character height as fraction of viewport ! call plot_qcs(0,xch,ych) ! ! set margin size in units of viewport dimensions ! allow enough room for the plot labels if they are drawn ! nb: pgplot sets the character height as some fraction of the smallest ! dimension ! ! for tiled plots, these margins apply to the whole page ! otherwise, these are applied to each panel individually ! vmargintop = vmargintopin vmarginright = vmarginrightin if (axis.ge.0) then !--if we are drawing an axis ! leave a minimum of half a character ! spacing (or 0.7 for antialiasing) ! so that axis numbers are not chopped ! in half at the edges of the viewport vmargintop = max(vmargintop,0.7*ych) vmarginright = max(vmarginright,0.7*xch) !--leave space for labels if (axis.ne.3) then vmarginleft = vmarginleftin + (ylabeloffset+1.5)*xch vmarginbottom = vmarginbottomin + (xlabeloffset+1.0)*ych else vmarginleft = vmarginleftin + 2.0*xch vmarginbottom = vmarginbottomin + 1.5*ych endif if (plot_alt_y_axis) vmarginright = vmarginleft if (.not.tile) then if (ny.gt.1 .and. .not.isamexaxis) then vmarginbottom = vmarginbottom + 0.5*ych elseif (ny.gt.1) then vmarginbottom = vmarginbottom + 0.25*ych endif endif else vmarginleft = vmarginleftin vmarginbottom = vmarginbottomin endif ! !--set size of each panel ! ix = iplot - ((iplot-1)/nx)*nx iy = (iplot-1)/nx + 1 if (tile) then !--also leave room for title if necessary if (titleoffset.ge.0.) then vmargintop = vmargintop + (titleoffset+1.)*ych endif ! ! effective viewport size = size - margins (only used for tiled ! vptsizeeffx = 1.0 - vmarginright - vmarginleft vptsizeeffy = 1.0 - vmargintop - vmarginbottom ! reduce x or y size if just=1 to get right aspect ratio if (aspectratio.le.1.0 .and. just.eq.1) then if (aspectratio*vptsizeeffy.lt.vptsizeeffx) then vptsizeeffx = aspectratio*vptsizeeffy ! but this could still be bigger than the margins allow... else vptsizeeffy = vptsizeeffx/aspectratio endif elseif (aspectratio.gt.1.0 .and. just.eq.1) then if (vptsizeeffx/aspectratio.lt.vptsizeeffy) then vptsizeeffy = vptsizeeffx/aspectratio ! but this could still be bigger than the margins allow... else vptsizeeffx = vptsizeeffy*aspectratio endif endif panelsizex = vptsizeeffx/nx panelsizey = vptsizeeffy/ny ! print*,ix,iy,nx,ny ! print*,panelsizex,panelsizey,vptsizeeffx,vptsizeeffy ! print*,'margins = ',vmarginleft,vmarginright vptxmin = vmarginleft + (ix-1)*panelsizex vptxmax = vptxmin + panelsizex vptymax = (1.0 - vmargintop) - (iy-1)*panelsizey vptymin = vptymax - panelsizey else !--use full page for non-tiled plots, then set margins inside each panel panelsizex = 1.0/nx panelsizey = 1.0/ny vptxmin = (ix-1)*panelsizex + vmarginleft vptxmax = ix*panelsizex - vmarginright vptymax = 1.0 - (iy-1)*panelsizey - vmargintop vptymin = 1.0 - iy*panelsizey + vmarginbottom !--also leave room for title if necessary if (titleoffset.ge.0.) then vptymax = vptymax - (titleoffset+1.)*ych endif !--also leave room for colour bar if necessary if (colourbarwidth.GT.0.) then vptxmax = vptxmax - (colourbarwidth + 1.6)*xch endif endif ! print*,vptxmin,vptxmax,vptymin,vptymax ! ! set viewport ! !print*,'setting ',vptxmin,vptxmax,vptymin,vptymax call plot_svp(vptxmin,vptxmax,vptymin,vptymax) ! ! set axes ! if (just.eq.1) then if (nx*ny.eq.1 .and. adjustlimits) then !--query viewport aspect ratio call plot_qvp(3,x1,x2,y1,y2) devaspectratio = (x2-x1)/(y2-y1) !--adjust limits to match viewport aspect ratio dx = xmax - xmin dy = ymax - ymin if (devaspectratio*dy/dx.ge.1.) then xcen = 0.5*(xmin + xmax) xmin = xcen - 0.5*devaspectratio*dy xmax = xcen + 0.5*devaspectratio*dy !print*,' auto-adjusting xmin = ',xmin,' xmax = ',xmax else ycen = 0.5*(ymin + ymax) ymin = ycen - 0.5*dx/devaspectratio ymax = ycen + 0.5*dx/devaspectratio !print*,' auto-adjusting ymin = ',ymin,' ymax = ',ymax endif endif call plot_wnad(xmin,xmax,ymin,ymax) else call plot_swin(xmin,xmax,ymin,ymax) endif ! ! adjust viewport to lie exactly on pixel boundaries ! if (useexactpixelboundaries) call plot_set_exactpixelboundaries() ! ! option to return before actually doing anything ! if (trim(title).eq.'NOPGBOX') return ! ! set options for call to pgbox (draws axes) and label axes where appropriate ! (options are exactly as in pgenv apart from axis=-3,-4 which i have added) ! yopts = '*' select case(axis) case(-4) xopts = 'BCT' case(-3) xopts = 'BCST' case(-2) xopts = ' ' case(-1) xopts = 'BC' case(0,4) xopts = 'BCST' case(1) xopts = 'ABCST' case(2) xopts = 'ABCGST' case(3) xopts = 'BCST' case(5) xopts = 'BCT' case(10) xopts = 'BCSTL' yopts = 'BCST' case(20) xopts = 'BCST' yopts = 'BCSTL' case(30) xopts = 'BCSTL' yopts = 'BCSTL' case default print*,'setpage: illegal axis argument.' xopts = 'BCNST' end select if (yopts.eq.'*') yopts = xopts if (plot_alt_y_axis) call string_delete(yopts,'C') ! ! label plot ! if (tile) then ! ! decide whether to number and label the y axis ! if (ix.eq.nx .and. axis.ge.0) then ! !--apply label to right hand side axis if used ! if (plot_alt_y_axis) then call plot_second_y_axis(yopts,just,axis,itransy,yscale,ylabeloffset,labelyalt) endif endif if (ix.eq.1 .and. axis.ge.0) then ! !--label "normal" y axis ! if (axis.eq.3) then yopts = '1N'//trim(yopts) else yopts = '1VN'//trim(yopts) call plot_annotate('L',ylabeloffset,0.5,0.5,labely) endif elseif (axis.ge.0) then !yopts = trim(yopts)//'N' endif ! ! decide whether to number and label the x axis ! if ((iy.eq.ny .or. lastplot .or. lastrow) .and. axis.ge.0) then xopts = 'N'//trim(xopts) if (axis.ne.3) call plot_annotate('B',xlabeloffset,0.5,0.5,labelx) endif ! ! plot the title if inside the plot boundaries ! if (titleoffset.lt.0.) call plot_annotate('t',-titleoffset,0.96,1.0,title) elseif (axis.ge.0) then ! !--label x axis only if on last row ! or if x axis quantities are different ! if (((ny*nx-iplot).lt.nx).or.(.not.isamexaxis).or.lastplot) then if (axis.ne.3) call plot_annotate('B',xlabeloffset,0.5,0.5,labelx) endif !--always plot numbers xopts = 'N'//trim(xopts) ! !--apply label to right hand side axis if used ! if (plot_alt_y_axis) then call plot_second_y_axis(yopts,just,axis,itransy,yscale,ylabeloffset,labelyalt) endif ! !--always label y axis ! if (axis.eq.3) then yopts = '1N'//trim(yopts) else yopts = '1VN'//trim(yopts) call plot_annotate('L',ylabeloffset,0.5,0.5,labely) endif ! !--always plot title ! call plot_annotate('T',-titleoffset,0.5,0.5,title) endif call plot_box(xopts,0.0,0,yopts,0.0,0) return end subroutine ! !--this subroutine is a cut down version of the above, which ONLY redraws the axes ! (so that axes can be redrawn on *top* of what has been plotted). ! ! inputs: ! axis : axes options (same as in PGENV, with axis=-4,-3,+3 added) ! subroutine redraw_axes(iaxis,just,yscale,itransy) use plotlib, only:plot_box implicit none integer, intent(in) :: iaxis,just,itransy character(len=10) :: xopts, yopts real, intent(in) :: yscale ! !--set plot axes (options are exactly as in PGENV, with axis=-4,-3,+3 added) ! yopts = '*' select case(iaxis) case(-4) xopts = 'BCT' case(-3) xopts = 'BCST' case(-2) xopts = ' ' case(-1) xopts = 'BC' case(0) xopts = 'BCST' case(1) xopts = 'ABCST' case(2) xopts = 'ABCGST' case(3) xopts = 'BCST' case(4) xopts = 'BCST' yopts = 'BST' case(5) xopts = 'BCT' case(10) xopts = 'BCSTL' yopts = 'BCST' case(20) xopts = 'BCST' yopts = 'BCSTL' case(30) xopts = 'BCSTL' yopts = 'BCSTL' case default print*,'redraw_axes: illegal AXIS argument.' xopts = 'BCST' end select if (yopts.eq.'*') yopts = xopts if (iaxis.eq.4) call plot_second_y_axis(yopts,just,iaxis,itransy,yscale) call plot_box(xopts,0.0,0,yopts,0.0,0) return end subroutine redraw_axes subroutine plot_second_y_axis(yopts,just,iaxis,itransy,yscale,ylabeloffset,labely) use plotlib, only:plot_box,plot_annotate,plot_qwin,plot_swin,plot_wnad use asciiutils, only:string_delete use transforms, only:transform,transform_inverse,transform_label implicit none character(len=*), intent(in) :: yopts real, intent(in) :: yscale real, intent(in), optional :: ylabeloffset character(len=*), intent(in), optional :: labely integer, intent(in) :: just,iaxis,itransy character(len=10) :: yoptsi character(len=120) :: labelyalt real :: xmin,xmax,ymin,ymax,yminalt,ymaxalt yoptsi = yopts call string_delete(yoptsi,'B') call string_delete(yoptsi,'N') !--save plot window settings call plot_qwin(xmin,xmax,ymin,ymax) !--scaling of y axis: multiplication in un-transformed space yminalt = ymin ymaxalt = ymax if (itransy.gt.0) call transform_inverse(yminalt,ymaxalt,itransy) yminalt = yminalt*yscale ymaxalt = ymaxalt*yscale if (itransy.gt.0) call transform(yminalt,ymaxalt,itransy) !--set plot window to new scaled y axis call plot_swin(xmin,xmax,yminalt,ymaxalt) !--draw axes and label on right hand side of box if (iaxis.eq.3) then call plot_box(' ',0.0,0,'1MC'//trim(yoptsi),0.0,0) else call plot_box(' ',0.0,0,'1VMC'//trim(yoptsi),0.0,0) endif if (present(labely) .and. present(ylabeloffset)) then labelyalt = labely if (itransy.gt.0) labelyalt = transform_label(labely,itransy) call plot_annotate('R',ylabeloffset,0.5,0.5,labelyalt) endif !--reset plot window if (just.eq.1) then call plot_wnad(xmin,xmax,ymin,ymax) else call plot_swin(xmin,xmax,ymin,ymax) endif end subroutine plot_second_y_axis end module pagesetup splash/src/read_data_foulkes.f90000644 000766 000000 00000017727 13261626263 017545 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR STEVE FOULKES' ASCII DATA FORMAT ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,time,gamma,maxpart,maxcol,maxstep use params use settings_data, only:ndim,ndimV,ncolumns,ncalc,required,ipartialread,xorigin use mem_allocation, only:alloc use system_utils, only:lenvironment use labels, only:ih implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,ierr,nerr,iunit,ncolstep integer :: nprint,npart_max,nstep_max,icol integer :: nmodel,nstar,ncolread logical :: iexist real :: tread,hmax,dtmin,tdg,hfac real, dimension(3) :: xptmass,yptmass,vxptmass,vyptmass character(len=len(rootname)+4) :: dumpfile nstepsread = 0 nstep_max = 0 npart_max = maxpart iunit = 15 ! logical unit number for input dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions (0 means no particle coords) ! ndim = 3 ndimV = 3 nstar = 2 j = indexstart nstepsread = 0 print "(a)",' Steve Foulkes/Carol Haswell/James Murray ascii data format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the file and read the number of particles ! open(unit=iunit,iostat=ierr,file=dumpfile,status='old',form='formatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return endif ! !--read header line, set time ! read(iunit,*,iostat=ierr) nmodel,nprint,hmax,tread,dtmin,tdg if (ierr /= 0) print "(a)",' WARNING: error(s) reading first header line' print "(a,1pe10.3,a,i10,a,i10)",' time = ',tread,' npart =',nprint,' model number = ',nmodel print "(3(a,1pe10.4))",' hmax = ',hmax,' dtmin = ',dtmin,' tdg = ',tdg ! !--determine how many columns we are going to read ! if (lenvironment('FSPLASH_READALL')) then ncolstep = 26 print "(a)",' reading all columns' else ncolstep = 15 print "(a,i2)",' reading only to column 15 (setenv FSPLASH_READALL ''yes'' to read all)' endif ! !--(re)allocate memory ! nstep_max = max(maxstep,nstep_max,indexstart,1) if (.not.allocated(dat) .or. (nprint.gt.maxpart) .or. (ncolstep+ncalc).gt.maxcol) then npart_max = max(npart_max,nprint+nstar,maxpart) !--allow extra room if reallocating if (allocated(dat)) npart_max = max(npart_max,INT(1.1*(nprint+nstar)),maxpart) call alloc(npart_max,nstep_max,max(ncolstep+ncalc,maxcol)) endif ! !--set the necessary parameters ! ncolumns = ncolstep nstepsread = nstepsread + 1 npartoftype(:,j) = 0 npartoftype(1,j) = nprint npartoftype(2,j) = nstar time(j) = tread gamma(j) = 5./3. ! !--now read the timestep data in the dumpfile ! nerr = 0 ! !--only read required columns ! ncolread = 0 do i=1,ncolstep if (required(i)) ncolread = i enddo if (ncolread.ne.ncolstep) then ipartialread = .true. print*,' reading only up to column ',ncolread endif do i=1,nprint read(iunit,*,iostat=ierr) (dat(i,icol,j),icol = 1,ncolread) if (ierr.ne.0) nerr = nerr + 1 enddo if (nerr > 0) print *,' ERRORS reading particle data on ',nerr,' lines' ! !--read point mass information ! read(iunit,*,iostat=ierr) xptmass(1),yptmass(1),xptmass(2),yptmass(2),hfac !--point mass velocities not read, though would put them here if they were vxptmass(1) = 0. vyptmass(1) = 0. vxptmass(2) = 0. vyptmass(2) = 0. if (ierr /= 0) print *,' ERROR reading primary and secondary positions' close(iunit) !--set labels to get ih for setting smoothing length of stars call set_labels !--copy star particle properties into main data array do i=nprint+1,nprint+nstar dat(i,1,j) = xptmass(i-nprint) dat(i,2,j) = yptmass(i-nprint) dat(i,3,j) = 0. dat(i,4,j) = vxptmass(i-nprint) dat(i,5,j) = vyptmass(i-nprint) dat(i,6,j) = 0. dat(i,7:ncolstep,j) = 0. if (ih.gt.0) dat(i,ih,j) = epsilon(0.) ! small but non-zero smoothing length enddo print "(' primary (x,y) = (',1pe10.2,',',1pe10.2,')')",xptmass(1),yptmass(1) print "(' secondary (x,y) = (',1pe10.2,',',1pe10.2,')')",xptmass(2),yptmass(2) print "(a)",' setting origin to primary position... ' xorigin(1) = xptmass(1) xorigin(2) = yptmass(1) xorigin(3) = 0. return end subroutine read_data !!------------------------------------------------------------------- !! set labels for each column of data !! !! read these from a file called 'columns' in the current directory !! then take sensible guesses as to which quantities are which !! from the column labels !! !!------------------------------------------------------------------- subroutine set_labels use labels, only:label,labeltype,ix,irho,ipmass,ih,ivx,iamvec,labelvec use params use settings_data, only:ntypes,ndim,ndimV,UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i,ifx do i=1,ndim ix(i) = i enddo ivx = ndim+1 ipmass = ivx+ndimV ifx = ipmass+1 irho = ifx+ndimV label(irho+1) = 'du/dt' label(irho+2) = 'C\d\s\u' label(irho+3) = 'alpha' ih = irho+4 label(irho+5) = 'kpc' label(irho+6) = 'schb' label(irho+7) = 'dtp' label(irho+8) = 'sigma' label(irho+9) = 'pdr2' label(irho+10) = 'av_sep' label(irho+11) = 'radius' label(irho+12) = 'viscosity flag' label(irho+13) = 'neighbour number' label(irho+14) = 'iwas' label(irho+15) = 'll' if (irho.gt.0) label(irho) = 'density' if (ih.gt.0) label(ih) = 'smoothing length' if (ipmass.gt.0) label(ipmass) = 'particle mass' if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo endif if (ifx.gt.0) then iamvec(ifx:ifx+ndimV-1) = ifx labelvec(ifx:ifx+ndimV-1) = 'f' do i=1,ndimV label(ifx+i-1) = 'f\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 2 labeltype(1) = 'gas' labeltype(2) = 'star' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/legends.f90000644 000766 000000 00000033711 13261626263 015521 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! module containing routines for plotting legends in PGPLOT ! subroutines: ! legend : plots time on plots ! legend_vec : plots legend with vector arrow ! legend_markers : plots different particle types ! legend_scale : plots a scale on co-ordinate plots !----------------------------------------------------------------- module legends implicit none public :: legend, legend_vec, legend_markers, legend_scale public :: prompt_panelselect, ipanelselect private contains !----------------------------------------------------------------- ! plots time on plot ! arguments: ! t : current time ! hpos : horizontal position as fraction of viewport ! vpos : vertical position in character heights from top !----------------------------------------------------------------- subroutine legend(legendtext,t,unitslabel,hpos,vpos,fjust,usebox) use plotlib, only:plot_annotate use asciiutils, only:string_replace use parsetext, only:parse_text,rn implicit none real, intent(in) :: t,hpos,vpos,fjust character(len=*), intent(in) :: legendtext,unitslabel logical, intent(in) :: usebox character(len=len(legendtext)+len(unitslabel)+20) :: label integer, parameter :: nvars = 1 real(kind=rn), dimension(nvars) :: vals character(len=1), dimension(nvars) :: vars label = trim(legendtext) ! ! if string does not contain any formatting ! append the time variable to it ! if (index(label,'%') <= 0) then if (t < 1.) then label = trim(label)//'%t.2' else label = trim(label)//'%t.3' endif endif ! ! parse string for functions of time and formatting ! i.e. %t.5 ! vars = (/'t'/) vals(1) = real(t,kind=rn) call parse_text(label,vars,vals) if (index(label,'%ut').gt.0) then call string_replace(label,'%ut',trim(unitslabel)) else label = trim(label)//trim(unitslabel) endif if (usebox) call plot_box_around_text(trim(label),hpos,vpos,fjust) call plot_annotate('T',-vpos,hpos,fjust,trim(label)) return end subroutine legend !----------------------------------------------------------------- ! utility routine for plotting translucent box in legends !----------------------------------------------------------------- subroutine plot_box_around_text(string,hpos,vpos,fjust) use plotlib, only:plot_qwin,plot_qcs,plot_qtxt,plot_qci,plot_sci,plot_sfs, & plot_set_opacity, plot_rect implicit none character(len=*), intent(in) :: string real, intent(in) :: hpos,vpos,fjust real :: xmin,xmax,ymin,ymax,xpos,ypos real :: xbuf,ybuf,dx,dy,xch,ych,x1,x2,y1,y2 real, dimension(4) :: xbox,ybox integer :: ic ! !--convert hpos and vpos to x, y to plot arrow ! call plot_qwin(xmin,xmax,ymin,ymax) xpos = xmin + hpos*(xmax-xmin) call plot_qcs(4,xch,ych) ypos = ymax - (vpos + 1.)*ych ! !--enquire bounding box of string ! call plot_qtxt(xpos,ypos,0.0,0.0,trim(string),xbox,ybox) xbuf = 0.25*xch ybuf = 0.5*ych dx = xbox(3) - xbox(1) dy = ybox(3) - ybox(1) + 0.25*ych x1 = xpos - fjust*dx - xbuf x2 = x1 + dx + 2.*xbuf y1 = ypos y2 = y1 + dy + ybuf ! !--draw box around the string ! call plot_qci(ic) ! query colour index call plot_sci(0) ! background colour call plot_sfs(1) ! solid fill style call plot_set_opacity(0.5) call plot_rect(x1,x2,y1,y2,0.2*ych) ! draw a (rounded) rectangle call plot_set_opacity(1.0) call plot_sci(ic) ! restore colour index end subroutine plot_box_around_text !----------------------------------------------------------------- ! plots vector plot legend ! arguments: ! t : current time ! hpos : horizontal position as fraction of viewport ! vpos : vertical position in character heights from top ! charheight : this is the text character height ! (legend_vec is called directly after plotting the arrows, ! which may use a different character size - so we plot ! the arrow here in the same way, but then revert to ! the text character height to write the text) !----------------------------------------------------------------- subroutine legend_vec(label,unitslabel,vecmax,dx,hpos,vpos,charheight) use plotlib, only:plot_qwin,plot_qch,plot_sch,plot_qcs,plot_numb,plot_qtxt, & plot_qci,plot_sci,plot_sfs,plot_rect,plot_sci,plot_text, & plot_qvp,plot_svp,plot_swin,plot_arro,plot_set_opacity implicit none real, intent(in) :: vecmax,dx,hpos,vpos,charheight character(len=*), intent(in) :: label,unitslabel real :: xmin,xmax,ymin,ymax real :: xch,ych,charheightarrow,adjustlength,vecmaxnew real :: xpos,ypos,xbox(4),ybox(4),dxlabel,dxstring real :: dxbuffer,dybuffer,dxbox,dybox real :: xminnew,xmaxnew,yminnew,ymaxnew,x1,x2,y1,y2 integer :: icolindex,mm,pp,nc,ndec character(len=len(label)+20) :: string ! !--convert hpos and vpos to x, y to plot arrow ! call plot_qwin(xmin,xmax,ymin,ymax) call plot_qch(charheightarrow) call plot_sch(charheight) xpos = xmin + hpos*(xmax-xmin) call plot_qcs(4,xch,ych) ypos = ymax - (vpos + 1.)*ych ! !--format string containing numerical value ! vecmax corresponds to arrow of length dx ! we will draw an arrow of length sqrt(dx^2 + ych^2) ! so adjust vecmax accordingly ! adjustlength = sqrt(0.5*dx**2 + ych**2)/dx vecmaxnew = adjustlength*vecmax ndec = 2 if (vecmaxnew.lt.tiny(vecmaxnew)) then string = '0' nc = 1 else mm=int(vecmaxnew/10.**(int(log10(vecmaxnew)-ndec))) pp=int(log10(vecmaxnew)-ndec) call plot_numb(mm,pp,0,string,nc) endif string = '='//trim(string) ! write(string,"('=',1pe7.1)") vecmax ! !--enquire size of label ! call plot_qtxt(xpos,ypos,0.0,0.0,trim(label),xbox,ybox) dxlabel = xbox(3) - xbox(2) + 0.5*xch ! !--enquire size of string ! call plot_qtxt(xpos,ypos,0.0,0.0,trim(string),xbox,ybox) dxstring = xbox(3) - xbox(2) ! !--set size of box in x direction ! dxbuffer = 0.25*xch ! these are size of margins (x and y) dybuffer = 0.25*ych dxbox = dxlabel + dxstring + 1.1*dx/sqrt(2.) + dxbuffer dybox = ych + 0.5*dybuffer ! !--draw box around all of the legend ! call plot_qci(icolindex) ! draw a (rounded) rectangle in the background colour with solid fill style call plot_sci(0) call plot_sfs(1) call plot_set_opacity(0.66) call plot_rect(xpos-dxbuffer,xpos+dxbox,ypos-dybuffer,ypos + dybox,0.33*ych) call plot_set_opacity(1.0) ! change to foreground colour index call plot_sci(1) ! draw an outline around the box ! call pgsfs(2) ! call pgrect(xpos-dxbuffer,xpos+dxbox,ypos-dybuffer,ypos + dybox) ! call pgsfs(1) ! !--write label ! call plot_text(xpos,ypos,trim(label)) xpos = xpos + dxlabel ! !--Draw arrow. Here we have to perform tricks to get the arrow ! to appear even if outside the usual plotting area ! !--save viewport settings call plot_qvp(0,x1,x2,y1,y2) !--now allow the whole screen to be the viewport... call plot_svp(0.0,1.0,0.0,1.0) ! ...but correspondingly adjust window so that x and y positions ! are the same as in the old viewport xminnew = xmin - x1*(xmax-xmin)/(x2-x1) xmaxnew = xmax + (1.-x2)*(xmax-xmin)/(x2-x1) yminnew = ymin - y1*(ymax-ymin)/(y2-y1) ymaxnew = ymax + (1.-y2)*(ymax-ymin)/(y2-y1) call plot_swin(xminnew,xmaxnew,yminnew,ymaxnew) !--use character height original arrows were drawn with ! (this is to get the arrow head size right) call plot_sch(charheightarrow) !--draw arrow call plot_arro(xpos,ypos,xpos + dx/sqrt(2.),ypos + ych) !--restore viewport settings call plot_svp(x1,x2,y1,y2) call plot_swin(xmin,xmax,ymin,ymax) xpos = xpos + 1.1*dx/sqrt(2.) ! !--write numerical value and units label ! call plot_sch(charheight) !! call pgmtext('t',-vpos,hpos+0.02,0.0,trim(string)) call plot_text(xpos,ypos,trim(string)//trim(unitslabel)) ! !--restore colour index call plot_sci(icolindex) return end subroutine legend_vec !------------------------------------------------------------------------- ! draw a legend for different line/marker styles ! uses current line style and colour ! plots this below the time legend !------------------------------------------------------------------------- subroutine legend_markers(icall,icolour,imarkerstyle,ilinestyle, & iplotpts,iplotline,text,hposlegend,vposlegend,alphalegend) use plotlib, only:plot_qwin,plot_qcs,plot_qci,plot_qls,plot_sci,plot_sls, & plot_line,plot_pt,plot_text,plot_stbg,plot_slc,plot_qlc,plot_set_opacity implicit none integer, intent(in) :: icall,icolour,imarkerstyle,ilinestyle logical, intent(in) :: iplotpts,iplotline character(len=*), intent(in) :: text real, intent(in) :: hposlegend,vposlegend,alphalegend integer :: icolourprev, ilinestyleprev,ilinecapprev real, dimension(3) :: xline,yline real :: xch, ych, xmin, xmax, ymin, ymax real :: vspace, vpos ! !--do not plot anything if string is blank ! if (len_trim(text).le.0) return !call pgstbg(0) ! opaque text to overwrite previous ! !--set horizontal and vertical position and spacing ! in units of the character height ! vspace = 1.5 ! (in units of character heights) vpos = vposlegend + icall*vspace + 0.5 ! distance from top, in units of char height call plot_qwin(xmin,xmax,ymin,ymax) ! query xmax, ymax call plot_qcs(4,xch,ych) ! query character height in x and y units call plot_qci(icolourprev) ! save current colour index call plot_qls(ilinestyleprev) ! save current line style call plot_qlc(ilinecapprev) ! save the current line cap yline(:) = ymax - ((vpos - 0.5)*ych) xline(1) = xmin + hposlegend*(xmax-xmin) xline(2) = xline(1) + 1.5*xch xline(3) = xline(1) + 3.*xch call plot_sci(icolour) call plot_set_opacity(alphalegend) call plot_sls(ilinestyle) ! !--set round caps ! !call plot_slc(1) ! !--draw a small line segment ! if (iplotline) call plot_line(3,xline,yline) call plot_slc(ilinecapprev) call plot_sls(ilinestyleprev) ! !--draw points, only two if line is also plotted so that you can see the line ! three otherwise ! if (iplotpts .and. iplotline) then xline(2) = xline(3) call plot_pt(2,xline(1:2),yline(1:2),imarkerstyle) elseif (iplotpts) then call plot_pt(3,xline,yline,imarkerstyle) endif ! !--add text ! if (iplotline .or. iplotpts .and. len_trim(text).gt.0) then call plot_text(xline(3) + 0.75*xch,yline(1)-0.25*ych,trim(text)) endif call plot_sci(icolourprev) ! reset colour index call plot_set_opacity(1.0) call plot_stbg(-1) ! reset text background to transparent end subroutine legend_markers !------------------------------------------------------------------- ! plots labelled scale (horizontal error bar of a given length) ! can be used on co-ordinate plots to give a length scale ! ! e.g. would produce something like: ! ! |----| ! 10 AU ! ! arguments: ! dxscale : length of scale in current x units ! hpos : horizontal position as fraction of viewport ! vpos : vertical position in character heights from top ! text : label to print above scale !----------------------------------------------------------------- subroutine legend_scale(dxscale,hpos,vpos,text) use plotlib, only:plot_qwin,plot_qcs,plot_err1,plot_annotate implicit none real, intent(in) :: dxscale,hpos,vpos character(len=*), intent(in) :: text real :: xmin,xmax,ymin,ymax,xch,ych,xpos,ypos call plot_qwin(xmin,xmax,ymin,ymax) if (dxscale.gt.(xmax-xmin)) then print "(a)",'Error: scale size exceeds x dimensions: scale not plotted' else call plot_qcs(4,xch,ych) !--draw horizontal "error bar" above text ypos = ymin + (vpos+1.25)*ych xpos = xmin + hpos*(xmax-xmin) call plot_err1(5,xpos,ypos,0.5*dxscale,1.0) !--write text at the position specified call plot_annotate('B',-vpos,hpos,0.5,trim(text)) endif end subroutine legend_scale !------------------------------------------------------------------- ! The following subroutines handle the plotting of annotation ! and legends only on particular panels !------------------------------------------------------------------- subroutine prompt_panelselect(string,iselect) use prompting, only:prompt implicit none character(len=*), intent(in) :: string integer, intent(inout) :: iselect print "(4(/,a))", & ' 0 : plot '//trim(string)//' on every panel ', & ' n : plot '//trim(string)//' on nth panel only ', & ' -1 : plot '//trim(string)//' on first row only ', & ' -2 : plot '//trim(string)//' on first column only ' call prompt('Enter selection ',iselect,-2) end subroutine prompt_panelselect !------------------------------------------------------------------- ! Function that evaluates the logic required to determine ! whether the annotation should be plotted on the current panel ! as per the prompts in prompt_panelselect !------------------------------------------------------------------- logical function ipanelselect(iselect,ipanel,irow,icolumn) implicit none integer, intent(in) :: iselect,ipanel,irow,icolumn ipanelselect = ((iselect.gt.0 .and. ipanel.eq.iselect) & .or.(iselect.eq.-1 .and. irow.eq.1) & .or.(iselect.eq.-2 .and. icolumn.eq.1) & .or.(iselect.eq.0)) end function ipanelselect end module legends splash/src/exact_shock.f90000644 000766 000000 00000032124 13261626263 016370 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! compute exact solution for the one dimensional Riemann problem ! (hydrodynamic shock) ! ! input parameters are initial left and right states of ! density, pressure and velocity ! ! Computes shock profile at time t ! ! Calls a separate subroutine to calculate the post-shock pressure ! and velocity (this is the difficult bit). ! ! Handles all cases of left and right-going shocks and rarefactions ! ! Daniel Price, Institute of Astronomy, Cambridge, 2004 ! University of Exeter 2004-2008 ! Monash University 2008- ! ! dprice@astro.ex.ac.uk !----------------------------------------------------------------------- module shock implicit none public :: exact_shock private :: get_pstar, get_pstar_isothermal, f_and_df contains subroutine exact_shock(iplot,time,gammain,xshock,rho_L,rho_R,p_L,p_R,v_L,v_R,rdust_to_gas,xplot,yplot,ierr) implicit none integer, intent(in) :: iplot integer, intent(out) :: ierr real, intent(in) :: time,gammain,xshock real, intent(in) :: rho_L,rho_R,p_L,p_R,v_L,v_R,rdust_to_gas real, dimension(:), intent(in) :: xplot real, dimension(size(xplot)), intent(out) :: yplot integer :: i real, dimension(size(xplot)) :: dens, pr, vel real :: cs_L,cs_R, gamfac real :: ppost, vpost, vleft, vright, gamma real :: xzero,xleft,xleftleft,xcontact,xright,xrightright logical :: useisothermal,leftisshock,rightisshock gamma = gammain print*,'Plotting exact Riemann solution at t = ',time,' gamma = ',gamma ! ! check for errors in input ! ierr = 0 if (rho_L.le.0. .or. rho_R.le.0.) then print*,'error: rho <= 0 on input : ',rho_L,rho_R ierr = 1 return elseif (p_L .le.0. .or. p_R .le.0.) then print*,'error: pr <= 0 on input ',p_L, p_R ierr = 2 return endif if (gamma < 1.) then print*,'Error: gamma = ',gamma,' setting to 5/3' gamma = 5./3. endif ! ! xzero is the position of the shock at t=0 ! xzero = xshock ! ! define sound speeds to left and right of shock tube ! cs_L = sqrt(gamma*p_L/rho_L) cs_R = sqrt(gamma*p_R/rho_R) if (rdust_to_gas .gt.epsilon(rdust_to_gas)) then cs_L = cs_L*sqrt(1./(1.+rdust_to_gas)) cs_R = cs_R*sqrt(1./(1.+rdust_to_gas)) endif gamfac = (gamma-1.)/(gamma + 1.) !------------------------------------------------------------ ! find post-shock pressure via the Riemann solver ! (this version also returns the post shock speed vpost ! although this can be calculated from ppost) !------------------------------------------------------------ if (gamma.gt.1.0001) then call get_pstar(gamma,p_L,p_R,v_L,v_R,cs_L,cs_R,ppost,vpost) useisothermal = .false. else print*,'using isothermal solver...',p_L/rho_L, p_R/rho_R useisothermal = .true. call get_pstar_isothermal(cs_L*cs_L,v_L,v_R,rho_L,rho_R,ppost,vpost) endif !------------------------------------------------------------ ! using this, calculate various speeds needed in order to ! reconstruct the shock profile !------------------------------------------------------------ ! ! check whether solutions are shocks or rarefactions ! if (ppost .gt. p_L) then leftisshock = .true. else leftisshock = .false. endif if (ppost .gt. p_R) then rightisshock = .true. else rightisshock = .false. endif if (leftisshock) then write(*,"(a)",advance='no') ' left-going wave is a shock; ' else write(*,"(a)",advance='no') ' left-going wave is a rarefaction; ' endif if (rightisshock) then write(*,"(a)") 'right-going wave is a shock' else write(*,"(a)") 'right-going wave is a rarefaction' endif if (rightisshock) then ! right hand wave is a shock ! ! speed of the shock front ! vright = v_R + cs_R**2*(ppost/p_R - 1.)/(gamma*(vpost-v_R)) else ! right hand wave is a rarefaction ! ! speed at which the right end of rarefaction fan moves ! vright = cs_R + 0.5*(gamma+1.)*vpost - 0.5*(gamma-1.)*v_R endif ! ! repeat for left-going wave ! if (leftisshock) then vleft = -(v_L + cs_L**2*(ppost/p_L - 1.)/(gamma*(vpost-v_L))) else vleft = cs_L - 0.5*(gamma+1.)*vpost + 0.5*(gamma-1.)*v_L endif !------------------------------------------------------------- ! now work out the locations of various features in the shock !------------------------------------------------------------- ! ! position of left-going shock or back end of left-going rarefaction ! xleft = xzero - vleft*time if (leftisshock) then xleftleft = xleft else ! ! front end of left-going expansion fan (propagates at sound speed into "left" fluid) ! xleftleft = xzero - (cs_L - v_L)*time endif ! ! position of the interface between the "left" fluid from the "right" fluid ! (the contact discontinuity) ! xcontact = xzero + vpost*time ! ! position of right-going shock or back end of right-going rarefaction ! xright = xzero + vright*time if (rightisshock) then xrightright = xright else ! ! right end of right-going expansion fan (propagates at sound speed into "right" fluid) ! xrightright = xzero + (cs_R + v_R)*time endif !-------------------------------------------------------------- ! reconstruct the shock profile for all x !-------------------------------------------------------------- !--here is a cheap, dirty f90 version for crap compilers do i=1,size(xplot) if (xplot(i) <= xleftleft) then ! undisturbed medium to the left pr(i) = p_L dens(i) = rho_L vel(i) = v_L elseif (xplot(i) < xleft) then if (leftisshock) then pr(i) = ppost dens(i) = rho_L*(gamfac+ppost/p_L)/(1+gamfac*ppost/p_L) ! dens(i) = rho_L*(ppost/p_L)**(1./gamma) vel(i) = vpost else ! inside expansion fan if (useisothermal) then ! this is a bit of a guess dens(i) = rho_L*exp((xleftleft-xplot(i))/(cs_L*time) + v_L/cs_L) else dens(i) = rho_L*(gamfac*(xzero-xplot(i))/(cs_L*time) + gamfac*v_L/cs_L + (1.-gamfac))**(2./(gamma-1.)) endif pr(i) = p_L*(dens(i)/rho_L)**gamma vel(i) = (1.-gamfac)*(cs_L -(xzero-xplot(i))/time) + gamfac*v_L endif elseif (xplot(i) < xcontact) then ! between left expansion fan/shock and contact discontinuity ! post-shock, ahead of contact discontinuity but before right going wave pr(i) = ppost if (leftisshock) then dens(i) = rho_L*(gamfac+ppost/p_L)/(1+gamfac*ppost/p_L) else dens(i) = rho_L*(ppost/p_L)**(1./gamma) endif vel(i) = vpost elseif (xplot(i) < xright) then ! post-shock, ahead of contact discontinuity but before right going wave pr(i) = ppost if (rightisshock) then dens(i) = rho_R*(gamfac+ppost/p_R)/(1+gamfac*ppost/p_R) else dens(i) = rho_R*(ppost/p_R)**(1./gamma) endif vel(i) = vpost elseif (xplot(i) < xrightright) then if (rightisshock) then ! irrelevant as in this case xrightright = xright else ! inside expansion fan to right if (useisothermal) then ! this is a bit of a guess dens(i) = rho_R*exp(-(xrightright-xplot(i))/(cs_R*time) - v_R/cs_R) else dens(i) = rho_R*(gamfac*(xplot(i)-xzero)/(cs_R*time) - gamfac*v_R/cs_R + (1.-gamfac))**(2./(gamma-1.)) endif pr(i) = p_R*(dens(i)/rho_R)**gamma vel(i) = (1.-gamfac)*(-cs_R - (xzero-xplot(i))/time) + gamfac*v_R endif else ! undisturbed medium to the right pr(i) = p_R dens(i) = rho_R vel(i) = v_R endif enddo !--this is the beautiful, f95 version (which won't compile on pgf90) ! where(xplot <= xleft) ! <= otherwise problems at t=0 !! undisturbed medium to the left ! pr = p_L ! dens = rho_L ! vel = v_L ! elsewhere(xplot < xfan) !! inside expansion fan ! dens = rho_L*(gamfac*(xzero-xplot)/(cs_L*time) + (1.-gamfac))**(2./(gamma-1.)) ! pr = p_L*(dens/rho_L)**gamma ! vel = (1.-gamfac)*(cs_L -(xzero-xplot)/time) ! elsewhere(xplot < xcontact) !! between expansion fan and contact discontinuity ! pr = ppost ! dens = rho_L*(ppost/p_L)**(1./gamma) ! vel = vpost ! elsewhere(xplot < xshock) !! post-shock, ahead of contact discontinuity ! pr = ppost ! dens = rho_R*(gamfac+ppost/p_R)/(1+gamfac*ppost/p_R) ! vel = vpost ! elsewhere !! undisturbed medium to the right ! pr = p_R ! dens = rho_R ! vel = v_R ! end where !------------------------------------ ! determine which solution to plot !------------------------------------ select case(iplot) case(1) yplot = dens case(2) yplot = pr case(3) yplot = vel case(4) if (gamma.gt.1.0001) then yplot = pr/((gamma-1.)*dens) else yplot = pr/dens endif case(5) ! deltav, where vd = 0 yplot = -vel case(6) ! eps, where rhod = const yplot = rho_R/(dens + rho_R) end select return end subroutine exact_shock !------------------------------------------------------------------- ! Implementation of the exact Riemann solver given in Toro (1992) ! ! Solves for the post-shock pressure (pr) and velocity (vstar) ! given the initial left and right states ! ! Does not matter if high P / high rho is on left or right ! ! Daniel Price, Institute of Astronomy, Cambridge, UK, 2004 ! dprice@ast.cam.ac.uk !------------------------------------------------------------------- subroutine get_pstar(gamma,p_L,p_R,v_L,v_R,c_L,c_R,pr,vstar) implicit none real, parameter :: tol = 1.5e-6 real, intent(in) :: gamma,p_L,p_R,v_L,v_R,c_L,c_R real, intent(out) :: pr,vstar integer, parameter :: maxits = 30 integer :: its real :: prnew, f_L, f_R, dfdp_L, dfdp_R, f, df, dp real :: power, denom ! !--get an initial starting estimate of intermediate pressure ! this one is from Toro(1992) - gives basically the right answer ! for pressure jumps below about 4 ! power = (gamma-1.)/(2.*gamma) denom = c_L/p_L**power + c_R/p_R**power prnew = ((c_L + c_R + (v_L - v_R)*0.5*(gamma-1.))/denom)**(1./power) pr = p_L its = 0 !!print*,'initial guess = ',prnew do while (abs(prnew-pr).gt.tol .and. its.lt.maxits) its = its + 1 pr = prnew ! !--evaluate the function and its derivatives ! call f_and_df(pr,p_L,c_L,gamma,f_L,dfdp_L) call f_and_df(pr,p_R,c_R,gamma,f_R,dfdp_R) ! !--then get new estimate of pr ! f = f_L + f_R + (v_R - v_L) df = dfdp_L + dfdp_R ! !--Newton-Raphson iterations ! dp = -f/df prnew = pr + dp enddo if (its.eq.maxits) print*,'WARNING: its not converged in riemann solver' pr = prnew vstar = v_L - f_L print*,'its =',its,' p* =',prnew,'v* =',vstar,v_R + f_R end subroutine get_pstar ! !--pressure function ! H is pstar/p_L or pstar/p_R ! subroutine f_and_df(prstar,pr,cs,gam,fp,dfdp) implicit none real, intent(in) :: prstar, pr, gam, cs real, intent(out) :: fp,dfdp real :: H,term, power, gamm1, denom H = prstar/pr gamm1 = gam - 1. if (H.gt.1.) then ! shock denom = gam*((gam+1.)*H + gamm1) term = sqrt(2./denom) fp = (H - 1.)*cs*term dfdp = cs*term/pr + (H - 1.)*cs/term*(-1./denom**2)*gam*(gam+1.)/pr else ! rarefaction power = gamm1/(2.*gam) fp = (H**power - 1.)*(2.*cs/gamm1) dfdp = 2.*cs/gamm1*power*H**(power-1.)/pr endif end subroutine f_and_df !------------------------------------------------------------- ! Non-iterative isothermal Riemann solver ! from Balsara (1994), ApJ 420, 197-212 ! ! See also Cha & Whitworth (2003), MNRAS 340, 73-90 !------------------------------------------------------------- subroutine get_pstar_isothermal(cs2,v_L,v_R,rho_L,rho_R,pstar,vstar) implicit none real, intent(in) :: cs2,v_L,v_R,rho_L,rho_R real, intent(out) :: pstar,vstar real :: sqrtrho_L, sqrtrho_R, X, vdiff, determinant, vstar2 sqrtrho_L = sqrt(rho_L) sqrtrho_R = sqrt(rho_R) X = sqrtrho_L*sqrtrho_R/(sqrtrho_L + sqrtrho_R) vdiff = v_L - v_R determinant = (X*vdiff)**2 + 4.*cs2*X*(sqrtrho_L + sqrtrho_R) pstar = 0.25*(X*vdiff + sqrt(determinant))**2 vstar = v_L - (pstar - cs2*rho_L)/(sqrt(pstar*rho_L)) vstar2 = v_R + (pstar - cs2*rho_R)/(sqrt(pstar*rho_R)) print*,' pstar = ',pstar,' vstar = ',vstar,vstar2 end subroutine get_pstar_isothermal end module shock splash/src/read_data_sro.f90000644 000766 000000 00000064111 13261626263 016665 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM STEPHAN ROSSWOG'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! RSPLASH_FORMAT can be 'MHD' or 'HYDRO' ! RSPLASH_RESET_COM if 'YES' then centre of mass is reset for n2=0 (ie single objects) ! RSPLASH_COROTATING if 'YES' then velocities are transformed to corotating frame ! RSPLASH_HFACT can be changed to give correct hfact value for particle masses ! on minidumps: e.g. setenv RSPLASH_HFACT=1.2 ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,time,npartoftype,gamma,maxpart,maxcol,maxstep use params use settings_data, only:ndim,ndimV,ncolumns,iformat use mem_allocation, only:alloc use system_utils, only:lenvironment use system_commands, only:get_environment implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer, parameter :: max_spec = 7 ! number of species in abundance file real :: hfact, dhfact3,hfacttemp integer :: i,j,k,ierr,ierr1 integer :: nprint,nptmass,npart_max,nstep_max integer :: n1,n2,idump,ncol logical :: iexist,magfield,minidump,doubleprec,iabunfileopen character(len=len(rootname)) :: dumpfile character(len=13) :: abunfile character(len=10) :: string real :: timei,tkin,tgrav,tterm,escap,rstar,mstar,Etot_burn_cgs real(doub_prec) :: timedb,tkindb,tgravdb,ttermdb real(doub_prec) :: escapdb,rstardb,mstardb,Etot_burn_cgsdb real(doub_prec), dimension(:,:), allocatable :: datdb nstepsread = 0 nstep_max = 0 npart_max = maxpart iabunfileopen = .false. hfact = 1.5 dhfact3 = 1./hfact**3 dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--use minidump format if minidump ! minidump = .false. if (index(dumpfile,'minidump').ne.0) minidump = .true. ! !--get hfact for minidumps ! if (minidump) then call get_environment('RSPLASH_HFACT',string) read(string,*,iostat=ierr) hfacttemp if (hfacttemp.gt.0.5 .and. hfacttemp .lt.10.0 .and. ierr.eq.0) then hfact = hfacttemp print *,'setting hfact =',hfact,' from RSPLASH_HFACT environment variable' else print "(1x,a)",'error reading hfact from RSPLASH_HFACT environment variable' endif endif ! !--try to guess full dump format from file names ! magfield = .true. ! if (.not.minidump) then ! if (index(dumpfile,'SMBH').gt.0) magfield = .false. ! if (index(dumpfile,'nsbh').gt.0) magfield = .false. ! if (index(dumpfile,'NSBH').gt.0) magfield = .false. ! if (index(dumpfile,'WD').gt.0) magfield = .false. ! endif ! !--override this with environment variable ! call get_environment('RSPLASH_FORMAT',string) select case(trim(adjustl(string))) case('MHD','mhd','ns','NS') magfield = .true. case('WD','hydro','HYDRO','ns_bh_v2') magfield = .false. end select if (magfield) then print "(1x,a)",'reading MAGMA code format (set RSPLASH_FORMAT=hydro for hydro format)' else print "(1x,a)",'reading Stephan Rosswog (hydro) code format (set RSPLASH_FORMAT=MHD for MAGMA)' endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 if (magfield) then if (minidump) then ncol = 11 iformat = 1 else ncol = 27 iformat = 2 endif else if (minidump) then ncol = 7 ! number of columns in file iformat = 3 else ncol = 16 iformat = 4 endif endif n1 = 0 n2 = 0 ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return else ! !--read the number of particles in the first step, ! allocate memory and rewind ! doubleprec = .true. if (minidump) then !--try double precision first read(15,end=55,iostat=ierr) timedb,nprint,nptmass !--change to single precision if stupid answers if (nprint.le.0.or.nprint.gt.1e10 & .or.nptmass.lt.0.or.nptmass.gt.1e6) then doubleprec = .false. rewind(15) read(15,end=55,iostat=ierr) timei,nprint,nptmass if (magfield) then print "(a)",' single precision MHD minidump' else print "(a)",' single precision hydro minidump' endif else if (magfield) then print "(a)",' double precision MHD minidump' else print "(a)",' double precision hydro minidump' endif timei = real(timedb) endif else !--try double precision first read(15,end=55,iostat=ierr) nprint,rstardb,mstardb,n1,n2, & nptmass,timedb !--change to single precision if stupid answers if (n1.lt.0.or.n1.gt.1e10.or.n2.lt.0.or.n2.gt.1e10 & .or.nptmass.lt.0.or.nptmass.gt.1.e6) then doubleprec = .false. rewind(15) read(15,end=55,iostat=ierr) nprint,rstar,mstar,n1,n2, & nptmass,timei if (magfield) then print "(a)",' single precision full MHD dump' else print "(a)",' single precision full hydro dump' endif else if (magfield) then print "(a)",' double precision full MHD dump' else print "(a)",' double precision full hydro dump' endif timei = real(timedb) endif endif print "(a,f10.2,a,i9,a,i6)",' time: ',timei,' npart: ',nprint,' nptmass: ',nptmass !--barf if stupid answers in single and double precision if (nptmass.lt.0.or.nptmass.gt.1.e6 .or. nprint.lt.0 & .or. nprint.gt.1e10 .or. (nprint.eq.0 .and. nptmass.eq.0)) then print "(a)",' *** ERRORS IN TIMESTEP HEADER: NO DATA READ ***' close(15) return endif ncolumns = ncol ! !--check if abundance files are present and read from them ! if (.not.magfield) then !--extract dump number from filename (last 5 characters) read(dumpfile(len_trim(dumpfile)-4:len_trim(dumpfile)),*,iostat=ierr1) idump if (ierr1 /= 0) then print "(a)",' error extracting dump number from filename' else write(abunfile,"(a,'.',i5.5)") 'abun_a7',idump inquire(file=abunfile,exist=iexist) if (.not.iexist) then print "(a)",' abundance file '//trim(abunfile)//' NOT FOUND' else open(unit=41,file=abunfile,status='old',form='unformatted',iostat=ierr1) if (ierr1 /= 0) then print "(a)",'*** ERROR OPENING '//trim(abunfile) else ncolumns = ncol + max_spec + 2 iabunfileopen = .true. endif endif endif endif if (.not.allocated(dat) .or. (nprint+nptmass).gt.npart_max) then npart_max = max(npart_max,INT(1.1*(nprint+nptmass))) call alloc(npart_max,nstep_max,ncolumns) endif rewind(15) endif if (ierr /= 0) then print "(a)",'*** ERROR READING TIMESTEP HEADER ***' else ! !--loop over the timesteps in this file ! npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+1,maxcol) endif ! !--now read the timestep data in the dumpfile ! if (magfield) then if (minidump) then dat(:,:,j) = 0. if (doubleprec) then allocate(datdb(maxpart,ncol),stat=ierr) if (ierr /= 0) then print*,"(a)",'*** error allocating memory for double conversion ***' return else datdb = 0. endif read(15,end=55,iostat=ierr) timedb,nprint,nptmass, & ((datdb(i,k),i=1,nprint),k=1,6), & ((datdb(i,k),i=1,nprint),k=8,ncol), & (datdb(i,7), i=nprint+1, nprint+nptmass), & ((datdb(i,k), i=nprint+1, nprint+nptmass),k=1,3) if (ierr /= 0) print "(a)",'*** WARNING: ERRORS DURING READ ***' dat(:,1:ncol,j) = real(datdb(:,1:ncol)) time(j) = real(timedb) else read(15,end=55,iostat=ierr) time(j),nprint,nptmass, & ((dat(i,k,j),i=1,nprint),k=1,6), & ((dat(i,k,j),i=1,nprint),k=8,ncol), & (dat(i,7,j), i=nprint+1, nprint+nptmass), & ((dat(i,k,j), i=nprint+1, nprint+nptmass),k=1,3) endif ! !--because masses are not dumped, we need to reconstruct them ! from density and h (only strictly true for grad h code) ! dat(1:nprint,7,j) = dat(1:nprint,4,j)**3*dat(1:nprint,5,j)*dhfact3 print "(a,f5.2,a)", & ' WARNING: setting particle masses assuming h = ',hfact,'*(m/rho)^(1/3)' else dat(:,:,j) = 0. ! because ptmasses don't have all quantities ! !--read full dump ! if (doubleprec) then allocate(datdb(maxpart,27),stat=ierr) if (ierr /= 0) then print*,"(a)",'*** error allocating memory for double conversion ***' return else datdb = 0. endif read(15,iostat=ierr) nprint,rstardb,mstardb,n1,n2, & nptmass,timedb,(datdb(i,7),i=1,nprint), & escapdb,tkindb,tgravdb,ttermdb, & ((datdb(i,k),i=1,nprint),k=1,6), & ((datdb(i,k),i=1,nprint),k=8,ncol), & (datdb(i,9), i=nprint+1, nprint+nptmass), & ((datdb(i,k), i=nprint+1, nprint+nptmass),k=1,6), & ((datdb(i,k), i=nprint+1, nprint+nptmass),k=21,23) if (ierr < 0) print "(a)",'*** WARNING: END OF FILE DURING READ ***' if (ierr > 0) print "(a)",'*** WARNING: ERRORS DURING READ ***' dat(:,1:ncol,j) = real(datdb(:,1:ncol)) time(j) = real(timedb) else read(15,iostat=ierr) nprint,rstar,mstar,n1,n2, & nptmass,time(j),(dat(i,7,j),i=1,nprint), & escap,tkin,tgrav,tterm, & ((dat(i,k,j),i=1,nprint),k=1,6), & ((dat(i,k,j),i=1,nprint),k=8,ncol), & (dat(i,9,j), i=nprint+1, nprint+nptmass), & ((dat(i,k,j), i=nprint+1, nprint+nptmass),k=1,6), & ((dat(i,k,j), i=nprint+1, nprint+nptmass),k=21,23) if (ierr < 0) print "(a)",'*** WARNING: END OF FILE DURING READ ***' if (ierr > 0) print "(a)",'*** WARNING: ERRORS DURING READ ***' endif endif else ! !--hydro minidumps ! if (minidump) then if (doubleprec) then allocate(datdb(maxpart,7),stat=ierr) if (ierr /= 0) then print*,"(a)",'*** error allocating memory for double conversion ***' return else datdb = 0. endif read(15,end=55,iostat=ierr) timedb,nprint,nptmass, & ((datdb(i,k), i=1, nprint),k=1,6), & (datdb(i,7), i=nprint+1, nprint+nptmass), & ((datdb(i,k), i=nprint+1, nprint+nptmass),k=1,3) if (ierr < 0) print "(a)",'*** WARNING: END OF FILE DURING READ ***' if (ierr > 0) print "(a)",'*** WARNING: ERRORS DURING READ ***' dat(:,1:ncol,j) = real(datdb(:,1:ncol)) time(j) = real(timedb) else read(15,end=55,iostat=ierr) time(j),nprint,nptmass, & ((dat(i,k,j), i=1, nprint),k=1,6), & (dat(i,7,j), i=nprint+1, nprint+nptmass), & ((dat(i,k,j), i=nprint+1, nprint+nptmass),k=1,3) endif ! !--because masses are not dumped, we need to reconstruct them ! from density and h (only strictly true for grad h code) ! dat(1:nprint,7,j) = dat(1:nprint,4,j)**3*dat(1:nprint,5,j)*dhfact3 print "(a,f5.2,a)", & ' WARNING: setting particle masses assuming h = ',hfact,'*(m/rho)^(1/3)' else ! !--hydro full dumps ! dat(:,:,j) = 0. ! because ptmasses don't have all quantities if (doubleprec) then allocate(datdb(maxpart,ncol),stat=ierr) if (ierr /= 0) then print*,"(a)",'*** error allocating memory for double conversion ***' return else datdb = 0. endif read(15,iostat=ierr) nprint,rstardb,mstardb,n1,n2, & nptmass,timedb,(datdb(i,7),i=1,nprint), & escapdb,tkindb,tgravdb,ttermdb, & ((datdb(i,k),i=1,nprint),k=1,6), & ((datdb(i,k),i=1,nprint),k=8,ncol), & (datdb(i,9), i=nprint+1, nprint+nptmass), & ((datdb(i,k), i=nprint+1, nprint+nptmass),k=1,6), & ((datdb(i,k), i=nprint+1, nprint+nptmass),k=13,15) if (ierr < 0) print "(a)",'*** WARNING: END OF FILE DURING READ ***' if (ierr > 0) print "(a)",'*** WARNING: ERRORS DURING READ ***' dat(:,1:ncol,j) = real(datdb(:,1:ncol)) time(j) = real(timedb) else read(15,iostat=ierr) nprint,rstar,mstar,n1,n2, & nptmass,time(j),(dat(i,7,j),i=1,nprint), & escap,tkin,tgrav,tterm, & ((dat(i,k,j),i=1,nprint),k=1,6), & ((dat(i,k,j),i=1,nprint),k=8,ncol), & (dat(i,9,j), i=nprint+1, nprint+nptmass), & ((dat(i,k,j), i=nprint+1, nprint+nptmass),k=1,6), & ((dat(i,k,j), i=nprint+1, nprint+nptmass),k=13,15) if (ierr < 0) print "(a)",'*** WARNING: END OF FILE DURING READ ***' if (ierr > 0) print "(a)",'*** WARNING: ERRORS DURING READ ***' endif endif endif if (ierr /= 0 ) then print "(a)",'|*** ERROR READING TIMESTEP ***' ! return ! else ! nstepsread = nstepsread + 1 endif nstepsread = nstepsread + 1 npartoftype(1,j) = nprint npartoftype(2,j) = nptmass !! print*,j,' time = ',time(j) gamma(j) = 1.666666666667 ! !--read abundances from abundance file ! if (iabunfileopen) then print "(a)",' ... reading abundances from '//trim(abunfile)//' ...' read(41,iostat=ierr1) nprint if (ierr1 /= 0) then print "(a)",' *** ERROR READING ABUNDANCE FILE ***' elseif (nprint.ne.npartoftype(1,j)) then print "(a)",' *** ERROR: npart in abundance file differs from full dump ***' else rewind(41) if (doubleprec) then read(41,iostat=ierr1) nprint,((datdb(i,k),k=1,max_spec+2),i=1,nprint),Etot_burn_cgsdb dat(:,ncol+1:ncolumns,j) = real(datdb(:,1:max_spec+2)) print*,' Etot_burn (cgs) = ',Etot_burn_cgsdb else read(41,iostat=ierr1) nprint,((dat(i,k,j),k=1,max_spec+2),i=1,nprint),Etot_burn_cgs print*,' Etot_burn (cgs) = ',Etot_burn_cgs endif if (ierr1 < 0) then print "(a)",' *** END OF FILE REACHED IN ABUNDANCE FILE ***' elseif (ierr1 > 0) then print "(a)",' *** ERRORS DURING ABUNDANCE FILE READ ***' elseif (nprint.ne.npartoftype(1,j)) then print "(a)",' *** ERROR: npart in abundance file differs from full dump ***' endif endif close(unit=41) endif j = j + 1 if (allocated(datdb)) deallocate(datdb) endif 55 continue ! !--reached end of file ! close(15) ! !--reset centre of mass to zero ! if (allocated(dat) .and. n2.eq.0 .and. lenvironment('RSPLASH_RESET_CM')) then if (minidump) then call reset_centre_of_mass(dat(1:nprint,1:3,j-1),dat(1:nprint,7,j-1),nprint) else ! full dumps ipmass = 9 call reset_centre_of_mass(dat(1:nprint,1:3,j-1),dat(1:nprint,9,j-1),nprint) endif endif ! !--transform velocities to corotating frame ! if (.not.minidump .and. allocated(dat) .and. lenvironment('RSPLASH_COROTATING')) then print*,'TRANSFORMING VELOCITIES TO CORORATING FRAME' call set_corotating_vels(dat(1:nprint,9,j-1),dat(1:nprint,4:5,j-1),n1,nprint) endif if (allocated(npartoftype)) then print*,'>> end of dump file: nsteps =',j-1,'ntot = ', & sum(npartoftype(:,j-1)),'nptmass=',npartoftype(2,j-1) endif return contains ! !--reset centre of mass to zero ! subroutine reset_centre_of_mass(xyz,pmass,npart) implicit none integer, intent(in) :: npart real, dimension(npart,3), intent(inout) :: xyz real, dimension(npart) :: pmass real :: masstot,xcm,ycm,zcm ! !--get centre of mass ! masstot = SUM(pmass(1:npart)) xcm = SUM(pmass(1:npart)*xyz(1:npart,1))/masstot ycm = SUM(pmass(1:npart)*xyz(1:npart,2))/masstot zcm = SUM(pmass(1:npart)*xyz(1:npart,3))/masstot print*,' centre of mass is at ',xcm,ycm,zcm print*,' resetting to zero...' xyz(1:npart,1) = xyz(1:npart,1) - xcm xyz(1:npart,2) = xyz(1:npart,2) - ycm xyz(1:npart,3) = xyz(1:npart,3) - zcm return end subroutine reset_centre_of_mass ! !--adjust velocities to corotating frame ! subroutine set_corotating_vels(pmass,vxy,n1,npart) implicit none integer, intent(in) :: n1,npart real, dimension(npart,2), intent(inout) :: vxy !, xy real, dimension(npart) :: pmass real :: mass1,mass2 !,xcm1,ycm1,xcm2,ycm2 real :: vxcm1,vycm1,vxcm2,vycm2 ! !--get centre of mass of star 1 and star 2 ! mass1 = SUM(pmass(1:n1)) ! xcm1 = SUM(pmass(1:n1)*xy(1:n1,1))/mass1 ! ycm1 = SUM(pmass(1:n1)*xy(1:n1,2))/mass1 mass2 = SUM(pmass(n1+1:npart)) ! xcm2 = SUM(pmass(n1+1:npart)*xy(n1+1:npart,1))/mass2 ! ycm2 = SUM(pmass(n1+1:npart)*xy(n1+1:npart,2))/mass2 ! !--work out centre of mass velocities for each star ! vxcm1 = SUM(pmass(1:n1)*vxy(1:n1,1))/mass1 vycm1 = SUM(pmass(1:n1)*vxy(1:n1,2))/mass1 vxcm2 = SUM(pmass(n1+1:npart)*vxy(n1+1:npart,1))/mass2 vycm2 = SUM(pmass(n1+1:npart)*vxy(n1+1:npart,2))/mass2 ! !--subtract centre of mass velocities appropriately ! vxy(1:n1,1) = vxy(1:n1,1) - vxcm1 vxy(1:n1,2) = vxy(1:n1,2) - vycm1 vxy(n1+1:npart,1) = vxy(n1+1:npart,1) - vxcm2 vxy(n1+1:npart,2) = vxy(n1+1:npart,2) - vycm2 end subroutine set_corotating_vels end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use filenames, only:rootname use labels, only:label,unitslabel,labelvec,labeltype,iamvec,& ix,ivx,ih,irho,iutherm,ipmass,iBfirst,idivB use settings_data, only:ndim,ndimV,ncolumns,ntypes,UseTypeInRenderings,iformat use geometry, only:labelcoord use settings_units, only:units implicit none integer :: i logical :: minidump real :: udistcm,udistkm,utime,umass,uvelkms minidump = .false. if (index(rootname(1),'minidump').ne.0) minidump = .true. if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo if (minidump) then ivx = 0 ih = 4 ! smoothing length irho = 5 ! location of rho in data array iutherm = 0 ! thermal energy ipmass = 7 ! particle mass label(6) = 'T' if (ncolumns.gt.7) then iBfirst = 8 idivB = 11 endif if (ncolumns.gt.11) then label(12) = 'grad h' label(13) = 'grad soft' label(14) = 'dsoft' endif ! !--set transformation factors between code units/real units ! udistkm = 1.5 ! km udistcm = 1.5e5 utime = 5.0415e-6 umass = 1.99e33 units(1:3) = udistkm unitslabel(1:3) = ' [km]' units(ih) = udistkm unitslabel(ih) = ' [km]' units(ipmass) = umass unitslabel(ipmass) = ' [g]' units(irho) = umass/udistcm**3 unitslabel(irho) = ' [g/cm\u3\d]' if (iBfirst.gt.0) then units(iBfirst:iBfirst+ndimV-1) = 8.0988e14 unitslabel(iBfirst:iBfirst+ndimV-1) = ' [G]' units(idivB) = units(iBfirst)/udistcm unitslabel(idivB) = ' [G/cm]' endif else !--full dump ivx = ndim+1 ih = 7 iutherm = 8 ipmass = 9 irho = 10 label(11) = 'temperature [ MeV ]' label(12) = 'electron fraction (y\de\u)' if (iformat.eq.2) then ! MHD full dump iBfirst = 13 label(16) = 'psi' idivB = 17 iamvec(18:20) = 18 labelvec(18:20) = 'force' do i=1,ndimV label(18+i-1) = trim(labelvec(18))//'\d'//labelcoord(i,1) enddo label(21) = 'Euler alpha' label(22) = 'Euler beta' label(23) = 'Bevol\dz' label(24) = 'grad h' label(25) = 'grad soft' label(26) = 'av ' label(27) = 'avB' ! !--set transformation factors between code units/real units ! udistkm = 1.5 ! km udistcm = 1.5e5 utime = 5.0415e-6 umass = 1.99e33 units(iBfirst:iBfirst+ndimV-1) = 8.0988e14 unitslabel(iBfirst:iBfirst+ndimV-1) = ' [G]' units(idivB) = units(iBfirst)/udistcm unitslabel(idivB) = ' [G/cm]' units(1:3) = udistkm unitslabel(1:3) = ' [km]' units(4:6) = 1.0 unitslabel(4:6) = '/c' units(7) = udistkm unitslabel(7) = ' [km]' units(8) = (udistcm/utime)**2 unitslabel(8) = ' [erg/g]' units(9) = umass unitslabel(9) = ' [g]' units(10) = umass/udistcm**3 unitslabel(10) = ' [g/cm\u3\d]' else iamvec(13:15) = 13 labelvec(13:15) = 'force' do i=1,ndimV label(13+i-1) = trim(labelvec(13))//'\d'//labelcoord(i,1) enddo label(16) = 'dgrav' if (ncolumns.gt.16) then label(11) = 'temperature [ 10\u6\dK ]' do i=17,ncolumns write(label(i),"('species ',i2)") i-16 enddo if (ncolumns.ge.25) then label(17) = 'He' label(18) = 'C' label(19) = 'O' label(20) = 'Ne' label(21) = 'Mg' label(22) = 'Si' label(23) = 'Fe' label(24) = 'mean A' label(25) = 'mean Z' endif endif udistcm = 1.0e9 utime = 2.7443 umass = 1.99e33 uvelkms = (udistcm/utime)/1e5 units(1:3) = 1.0 !!udistcm unitslabel(1:3) = ' [10\u9\d cm]' units(4:6) = uvelkms unitslabel(4:6) = ' [km/s]' units(ih) = units(1) unitslabel(ih) = unitslabel(1) units(8) = (udistcm/utime)**2 unitslabel(8) = ' [erg/g]' units(9) = umass unitslabel(9) = ' [g]' units(10) = umass/udistcm**3 unitslabel(10) = ' [g/cm\u3\d]' endif endif units(0) = utime*1000. unitslabel(0) = ' ms' if (ivx.ne.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo endif if (iBfirst.ne.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' do i=1,ndimV label(iBfirst+i-1) = trim(labelvec(iBfirst))//'\d'//labelcoord(i,1) enddo label(idivB) = 'div B' endif label(ix(1:ndim)) = labelcoord(1:ndim,1) label(irho) = '\gr' if (iutherm.gt.0) label(iutherm) = 'u' label(ih) = 'h ' label(ipmass) = 'particle mass' ! !--set labels for each particle type ! ntypes = 2 !!maxparttypes labeltype(1) = 'gas' labeltype(2) = 'point mass' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/powerspectrums.f90000644 000766 000000 00000022134 13261626263 017177 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ! contains subroutines for taking power spectrums on particle data ! module powerspectrums implicit none real, parameter, private :: pi = 3.141592653589 real, parameter, private :: twopi = 2.*pi public :: powerspectrum,powerspec3D_sph private contains subroutine powerspectrum(npts,x,dat,nfreqpts,freq,power,idisordered) implicit none integer, intent(in) :: npts, nfreqpts real, intent(in), dimension(npts) :: x real, intent(in), dimension(npts) :: dat real, intent(in), dimension(nfreqpts) :: freq real, intent(out), dimension(nfreqpts) :: power logical, intent(in) :: idisordered integer :: ifreq real :: datmean, datvar, omega if (.not.idisordered) then print*,' evaluating fourier transform' do ifreq=1,nfreqpts omega = twopi*freq(ifreq) !--get power at this frequency call power_fourier(npts,x,dat,omega,power(ifreq)) enddo else print*,'evaluating lomb periodogram...' ! !--calculate the mean and variance of the data ! call mean_variance(dat,npts,datmean,datvar) print*,'data mean = ',datmean,' std. dev = ',sqrt(datvar) if (datvar.le.0.) then print*,'error: variance = 0' power = 0. return endif do ifreq=1,nfreqpts omega = twopi*freq(ifreq) call power_lomb(npts,x,dat,datmean,datvar,omega,power(ifreq)) enddo endif end subroutine powerspectrum !------------------------------------------------------- ! subroutine to compute the power spectrum ! of evenly sampled data via a (slow) fourier transform !-------------------------------------------------------- subroutine power_fourier(npts,x,dat,omega,power) implicit none integer, intent(in) :: npts real, intent(in), dimension(npts) :: x, dat real, intent(in) :: omega real, intent(out) :: power integer :: i real :: sum1,sum2 power = 0. sum1 = 0. sum2 = 0. do i=1,npts sum1 = sum1 + dat(i)*COS(-omega*x(i)) sum2 = sum2 + dat(i)*SIN(-omega*x(i)) enddo power= sqrt(sum1**2 + sum2**2)/REAL(npts) return end subroutine power_fourier !---------------------------------------------------------- ! Subroutine to compute the power spectrum (periodogram) ! of unevenly sampled data via the Lomb (1976) method ! (algorithm described in Press et al, Numerical Recipes, sec 13.8, p569) ! ! Given the data (dat) on a set of points (x), ! returns an array of nfreq frequencies (freq) between freqmin and freqmax ! together with the power at each frequency (power) !---------------------------------------------------------- subroutine power_lomb(npts,x,dat,datmean,datvar,omega,power) implicit none integer, intent(in) :: npts real, intent(in), dimension(npts) :: x, dat real, intent(in) :: datmean,datvar,omega real, intent(out) :: power integer :: i real :: ddat real :: tau, tau_numerator, tau_denominator real :: term1_numerator, term1_denominator real :: term2_numerator, term2_denominator real :: omega_dx, cos_term, sin_term ! !--calculate tau for this frequency ! tau_numerator = 0. tau_denominator = 0. do i=1,npts tau_numerator = tau_numerator + SIN(2.*omega*x(i)) tau_denominator = tau_denominator + COS(2.*omega*x(i)) enddo tau = ATAN(tau_numerator/tau_denominator)/(2.*omega) ! !--calculate the terms in the power ! term1_numerator = 0. term1_denominator = 0. term2_numerator = 0. term2_denominator = 0. do i=1,npts ddat = dat(i) - datmean omega_dx = omega*(x(i) - tau) cos_term = COS(omega_dx) sin_term = SIN(omega_dx) term1_numerator = term1_numerator + ddat*cos_term term1_denominator = term1_denominator + cos_term**2 term2_numerator = term2_numerator + ddat*sin_term term2_denominator = term2_denominator + sin_term**2 enddo ! !--calculate the power at this frequency ! power = 1./(2.*datvar)*(term1_numerator**2/term1_denominator + & term2_numerator**2/term2_denominator) return end subroutine power_lomb !------------------------------------------------- ! Subroutine to calculate the mean and variance ! of a set of data points ! Mean is trivial but variance uses a special ! formula to reduce round-off error ! see Press et al Numerical Recipes, section 14.2 ! this is similar to their subroutine avevar !------------------------------------------------- subroutine mean_variance(x,npts,xmean,xvariance) implicit none integer, intent(in) :: npts real, intent(in), dimension(npts) :: x real, intent(out) :: xmean, xvariance real :: roundoff, delta integer :: i ! !--calculate average ! xmean = 0. do i=1,npts xmean = xmean + x(i) enddo xmean = xmean/real(npts) ! !--calculate variance using the corrected two-pass formula ! ! var = 1/(n-1)*( sum (x-\bar{x}) - 1/n * (sum(x-\bar{x}) )^2 ) ! ! where the last term corrects for the roundoff error ! in the first term ! xvariance = 0. roundoff = 0. do i=1,npts delta = x(i) - xmean roundoff = roundoff + delta xvariance = xvariance + delta*delta enddo xvariance = (xvariance - roundoff**2/npts)/real(npts-1) return end subroutine mean_variance ! ! interface to 3D powerspectrum calculation on particles ! assumes box size is the same in all directions ! subroutine powerspec3D_sph(x,y,z,dat,hh,weight,icolours,npart, & ngrid,xmin,xmax,freq,power,normalise) use interpolations3D, only:interpolate3D implicit none integer, intent(in) :: npart,ngrid real, dimension(npart), intent(in) :: x,y,z,dat,hh,weight integer, dimension(npart), intent(in) :: icolours real, intent(in) :: xmin,xmax real, dimension(ngrid), intent(out) :: freq,power logical, intent(in) :: normalise real, dimension(ngrid,ngrid,ngrid) :: dat3D real :: dx integer :: logngrid,ik logical :: periodicx,periodicy,periodicz ! !--make sure than ngrid is a factor of 2 ! logngrid = int(log(real(ngrid))/log(2.)) if (2**logngrid.ne.ngrid) then print*,' ERROR: ngrid not a power of 2 in powerspectrum interpolation ',2**logngrid,ngrid endif dx = (xmax - xmin)/real(ngrid) ! !--interpolate (normalised) from particles to 3D grid suitable for FFT ! print*,'ngrid = ',ngrid periodicx = .false. periodicy = .false. periodicz = .false. call interpolate3D(x,y,z,hh,weight,dat,icolours,npart, & xmin,xmin,xmin,dat3D,ngrid,ngrid,ngrid,dx,dx,normalise,& periodicx,periodicy,periodicz) ! !--setup grid of frequencies for plotting ! freq(1) = 0. do ik=2,ngrid freq(ik) = ik - 1. enddo ! !--calculate powerspectrum using fft ! call power3d_fft(dat3D,ngrid,ngrid,ngrid,power,ngrid) return end subroutine powerspec3D_sph ! !--power spectrum routine using Fast Fourier Transform ! subroutine power3d_fft(dat,nx,ny,nz,power,nk) implicit none ! include 'fftw3.f' integer, intent(in) :: nx,ny,nz,nk real, intent(in), dimension(nx,ny,nz) :: dat real, intent(out), dimension(nk) :: power integer, dimension(nk) :: numk complex :: dati(nx,ny,nz) real :: ddenom,ptot integer :: ierr,k,j,i,kz,ky,kx,kk,ik !--this is for ACML ! complex :: comm(nx*ny*nz+5*(nx+ny+nz)) !--this is for FFTW ! integer*8 :: plan ierr = 0 ! !--convert data to complex ! dati = cmplx(dat,0.0) print*,' starting 3D fft...' ! !--do fast fourier transform via AMD Core Math Library function ! ! call cfft3d(-1,nx,ny,nz,dati,comm,ierr) ! !--do fft via fftw ! ! call fftwf_plan_dft_3d(plan,nx,ny,nz,dati,dati,FFTW_FORWARD,FFTW_ESTIMATE) ! call fftwf_execute(plan) ! call fftwf_destroy_plan(plan) if (ierr /= 0) then write(*,*) 'error on powerspectrum output!' endif power = 0. numk = 0 ! !--get power from fourier coefficients ! do k=1,nz kz = min(k-1,nz-k+1) do j=1,ny ky = min(j-1,ny-j+1) do i=1,nx kx = min(i-1,nx-i+1) kk = sqrt(real(kx**2 + ky**2 + kz**2)) ik = 1.5 + kk !--only return requested number of frequencies if (ik .le. nk) then power(ik) = power(ik) + abs(dati(i,j,k))**2 numk(ik) = numk(ik) + 1 ! sum contributions at that frequency endif enddo enddo enddo ddenom = 1./(real(nx)*real(ny)*real(nz)) power = power*ddenom**2 ptot = sum(power) ! !--normalise according to power in k-space "shells" ! and number of contributions in that shell from kx,ky and kz ! do ik=1,nk power(ik) = power(ik)/(numk(ik) + 1.e-8)*4./3.*pi*((ik-0.5)**3 - (ik-1.5)**3) enddo !--rescale so that it has the same total power as before power = power*ptot/sum(power) return end subroutine power3d_fft end module powerspectrums splash/src/read_data_mbate_hydro.f90000644 000766 000000 00000024311 13261626263 020355 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM MATTHEW BATE'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:maxparttypes,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer, parameter :: maxptmass = 1000 real, parameter :: pi=3.141592653589 integer :: i,j,ifile,ierr integer :: npart_max,nstep_max,ncolstep logical :: iexist character(len=3) :: fileno character(len=len(rootname)+10) :: dumpfile integer :: nprint, nghosti, n1, n2, nptmass integer, dimension(:), allocatable :: isteps, iphase integer, dimension(maxptmass) :: listpm !--use these lines if dump is double precision real(doub_prec), dimension(:,:), allocatable :: dattemp real(doub_prec), dimension(:), allocatable :: dummy real(doub_prec) :: udisti,umassi,utimei real(doub_prec) :: timei, gammai real(doub_prec) :: rhozero, RK2 real(doub_prec) :: escap,tkin,tgrav,tterm real(doub_prec) :: dtmax, tcomp !--use these lines for single precision !real, dimension(:,:), allocatable :: dattemp !real, dimension(:), allocatable :: dummy !real(doub_prec) :: udisti,umassi,utimei !real :: timei, gammai !real :: rhozero, RK2 !real :: escap,tkin,tgrav,tterm !real :: dtmax,tcomp nstepsread = 0 nstep_max = 0 npart_max = maxpart ifile = 1 ! !--for rootnames without the '00', read all files starting at #1 ! if (len_trim(rootname).lt.7) then ifile = 1 if (len_trim(rootname).eq.4) then write(fileno,"(i1,i1,i1)") ifile/100,mod(ifile,100)/10,mod(ifile,10) dumpfile = rootname(1:4)//fileno elseif (len_trim(rootname).eq.5) then write(fileno,"(i1,i1)") ifile/10,mod(ifile,10) dumpfile = rootname(1:5)//trim(fileno) endif else dumpfile = trim(rootname) endif ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 ncolstep = 12 ! number of columns in file ncolumns = ncolstep ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,4) j = indexstart nstepsread = 0 print "(1x,a)",'reading Matthew Bate''s/Willy Benz''s old SPH code format (hydro)' do while (iexist) write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' else ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,end=55,iostat=ierr) udisti,umassi,utimei,nprint if (.not.allocated(dat) .or. nprint.gt.npart_max) then npart_max = max(npart_max,INT(1.1*nprint)) call alloc(npart_max,nstep_max,ncolstep+ncalc) endif rewind(15) endif if (ierr /= 0) then print*,'*** ERROR READING TIMESTEP HEADER ***' else ! !--loop over the timesteps in this file ! over_steps_in_file: do npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+2*nstepsread,maxcol) endif ! !--allocate a temporary array for double precision variables ! if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(npart_max,ncolstep),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' ! !--allocate a dummy arrays for data I want to throw away ! if (allocated(dummy)) deallocate(dummy) allocate(dummy(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' if (allocated(isteps)) deallocate(isteps) allocate(isteps(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' if (allocated(iphase)) deallocate(iphase) allocate(iphase(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' ! !--now read the timestep data in the dumpfile ! write(*,"(a,i5,a)",advance="no") '| step ',j,': ' read(15,end=55,iostat=ierr) udisti, umassi, utimei, & nprint, nghosti, n1, n2, timei, gammai, rhozero, RK2, & (dattemp(i,7), i=1, nprint), (dattemp(i,8), i=1,nprint), & escap, tkin, tgrav, tterm, & (dattemp(i,1), i=1, nprint), (dattemp(i,2), i=1, nprint), & (dattemp(i,3), i=1, nprint), (dattemp(i,4), i=1, nprint), & (dattemp(i,5), i=1, nprint), (dattemp(i,6), i=1, nprint), & (dattemp(i,9), i=1, nprint), (dattemp(i,10), i=1, nprint), & (dattemp(i,11), i=1, nprint), (dattemp(i,12),i=1,nprint), & dtmax, (isteps(i), i=1,nprint), (iphase(i),i=1,nprint), & nptmass, (listpm(i), i=1,nptmass) if (ierr /= 0) then print "(a)",'*** INCOMPLETE DATA (CHECK PRECISION) ***' nstepsread = nstepsread + 1 exit over_steps_in_file else nstepsread = nstepsread + 1 endif ! !--convert to single precision ! print *,'t = ',timei,' ntotal = ',nprint print "(a)",'| converting to single precision... ' dat(1:nprint,1:ncolstep,j) = real(dattemp(1:nprint,1:ncolstep)) ! !--convert to physical units ! dat(1:nprint,11,j) = dat(1:nprint,11,j)*real(umassi/udisti**3) if (allocated(dattemp)) deallocate(dattemp) if (allocated(dummy)) deallocate(dummy) if (allocated(isteps)) deallocate(isteps) if (allocated(iphase)) deallocate(iphase) npartoftype(1,j) = nprint-nghosti npartoftype(2,j) = nghosti gamma(j) = real(gammai) tcomp = sqrt((3.*pi)/(32*rhozero)) time(j) = real(timei)/tcomp j = j + 1 enddo over_steps_in_file endif 55 continue ! !--reached end of file ! close(15) if (j-1 .gt. 0) then print*,'>> end of dump file: nsteps =',j-1,'ntot = ', & sum(npartoftype(:,j-1)),'nghost=',npartoftype(2,j-1) endif ! !--if just the rootname has been input, ! set next filename and see if it exists ! ifile = ifile + 1 if (len_trim(rootname).eq.4) then write(fileno,"(i1,i1,i1)") ifile/100,mod(ifile,100)/10,mod(ifile,10) dumpfile = rootname(1:4)//fileno inquire(file=dumpfile,exist=iexist) elseif (len_trim(rootname).eq.5) then write(fileno,"(i1,i1)") ifile/10,mod(ifile,10) dumpfile = rootname(1:5)//trim(fileno) inquire(file=dumpfile,exist=iexist) else iexist = .false. ! exit loop endif enddo return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ih = 7 ! smoothing length label(8) = 'alpha' iutherm = 9 ! thermal energy ipmass = 10 ! particle mass irho = 11 ! location of rho in data array if (ncolumns.gt.11) then label(12) = 'dgrav' endif label(ix(1:ndim)) = labelcoord(1:ndim,1) do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo label(irho) = 'density (g/cm\u3\d)' label(iutherm) = 'u' label(ih) = 'h ' label(ipmass) = 'particle mass' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 3 !!maxparttypes labeltype(1) = 'gas' labeltype(2) = 'ghost' labeltype(3) = 'sink' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_cactus_hdf5.f90000644 000766 000000 00000026073 13261626263 020257 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR HDF5 OUTPUT FROM THE CACTUS CODE ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Columns with the 'required' flag set to false are not read !------------------------------------------------------------------------- !------------------------------------------------------------------------- ! ! The routine that reads the data into splash's internal arrays ! !------------------------------------------------------------------------- subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol,maxstep,iamtype use params, only:doub_prec use settings_data, only:ndim,ndimV,ncolumns,ncalc,ipartialread,iverbose,buffer_steps_in_file use settings_page, only:legendtext use mem_allocation, only:alloc use labels, only:ih,irho,ipmass use system_utils, only:renvironment,lenvironment,ienvironment,envlist use asciiutils, only:cstring use cactushdf5read, only:open_cactus_hdf5_file,read_cactus_hdf5_data,close_cactus_hdf5_file,& datfileprev,file_is_open,ntoti_prev,ncol_prev,nstep_prev,compute_extra_columns use dataread_utils, only:count_types implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile integer :: i,istep,ierr,nextra integer :: nunknown,ignoretl integer :: ncolstep,npart_max,nstep_max,nsteps_to_read,ntoti logical :: iexist,reallocate,goterrors,ignore_time_levels real(doub_prec) :: timetemp,dx,vol nstepsread = 0 goterrors = .false. if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! if (iverbose==1 .and. ipos==1) print "(1x,a)",'reading CACTUS HDF5 format' inquire(file=datfile,exist=iexist) if (.not.iexist) then ! !--append .h5 on the end if not already present ! datfile=trim(rootname)//'.h5' inquire(file=datfile,exist=iexist) endif ! !--close previous file if filenames do not match ! if (trim(datfile)/=trim(datfileprev) .and. file_is_open) then call close_cactus_hdf5_file(ierr) file_is_open = .false. endif if (.not.iexist) then print "(a)",' *** error: '//trim(rootname)//': file not found ***' return endif ! !--set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 ignore_time_levels = lenvironment('CSPLASH_IGNORE_TIME_LEVELS') ignoretl = 0 if (ignore_time_levels) ignoretl = 1 nextra = 0 ! !--read data from snapshots ! i = istepstart if (.not.file_is_open) write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open file and read header information ! if (file_is_open) then ntoti = ntoti_prev ncolstep = ncol_prev nstep_max = nstep_prev else call open_cactus_hdf5_file(cstring(datfile),ipos,ntoti,ncolstep,nstep_max,ndim,ndimV,timetemp,ignoretl,ierr) if (ierr /= 0) then print "(a)", '*** ERROR READING HEADER ***' call close_cactus_hdf5_file(ierr) return endif file_is_open = .true. datfileprev = datfile ntoti_prev = ntoti ncol_prev = ncolstep nstep_prev = nstep_max endif call compute_extra_columns(ncolstep,nextra) ncolumns = ncolstep + nextra if (iverbose >= 1) print "(3(a,1x,i10))",' npart: ',ntoti,' ncolumns: ',ncolstep,' nsteps: ',nstep_max istep = 1 over_snapshots: do istep=1,nstep_max ! !--now read data ! reallocate = .false. npart_max = maxpart if (buffer_steps_in_file) then nsteps_to_read = nstep_max else nsteps_to_read = max(maxstep,1) endif if (nsteps_to_read > maxstep) reallocate = .true. if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntoti) else ! if first time, save on memory npart_max = int(ntoti) endif endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nsteps_to_read,max(ncolumns+ncalc,maxcol),mixedtypes=.true.) endif ! !--read particle data ! got_particles: if (ntoti > 0) then if (buffer_steps_in_file .or. ipos.eq.istep) then call read_cactus_hdf5_data(cstring(datfile),istep,ntoti,timetemp,dx,ignoretl,ierr) call set_labels ! set smoothing length and particle mass !print*,' Setting h = ',dx, 'ndim = ',ndim,' in column ',ih,' step ',i if (ih > 0) dat(:,ih,i) = real(dx) vol = dx**ndim if (ipmass > 0 .and. irho > 0) dat(:,ipmass,i) = dat(:,irho,i)*real(vol) ! ! compute extra quantities (tr K, 3^R, etc) ! call compute_extra_columns(ncolstep,nextra,dat(:,:,i)) ! ! get number of cells of each type (normal, ghost) ! call count_types(ntoti,iamtype(:,i),npartoftype(:,i),nunknown) masstype(:,i) = 0. ! all masses read from file time(i) = real(timetemp) i = i + 1 endif nstepsread = nstepsread + 1 endif got_particles ! !--now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. ! !--set flag to indicate that only part of this file has been read ! ipartialread = .false. ! !--call set labels to identify location of smoothing length ! call set_labels enddo over_snapshots if (nstepsread.gt.0) then print "(a,i10,a)",' >> read ',sum(npartoftype(:,istepstart)),' cells' endif !if (ipos==nstep_max) then ! call close_cactus_hdf5_file(ierr) ! file_is_open = .false. !endif end subroutine read_data subroutine read_cactus_hdf5_data_fromc(icol,ntot,np,temparr) bind(c) use, intrinsic :: iso_c_binding, only:c_int,c_double use particle_data, only:dat use settings_data, only:debugmode use labels, only:label implicit none integer(kind=c_int), intent(in) :: icol,ntot,np real(kind=c_double), intent(in) :: temparr(np) integer(kind=c_int) :: icolput integer :: nmax,i1,i2 icolput = icol i1 = ntot-np+1 i2 = ntot if (debugmode) print "(a,i2,a,i8,a,i8)",& 'DEBUG: reading column ',icol,' -> '//trim(label(icolput))//' parts ',i1,' to ',i2 ! check column is within array limits if (icolput.gt.size(dat(1,:,1)) .or. icolput.eq.0) then print "(a,i2,a)",' ERROR: column = ',icolput,' out of range in receive_data_fromc' return endif if (i2 > size(dat(:,1,1))) then print*,' ERROR with index range: ',i1,':',i2,' exceeds size ',size(dat(:,1,1)),' for column ',icol read* return endif ! ensure no array overflows nmax = min(i2,size(dat(:,1,1))) ! copy data into main splash array dat(i1:i2,icolput,1) = real(temparr(1:np)) return end subroutine read_cactus_hdf5_data_fromc subroutine read_cactus_itype_fromc(ntot,np,itype) bind(c) use, intrinsic :: iso_c_binding, only:c_int use particle_data, only:iamtype use params, only:int1 implicit none integer(kind=c_int), intent(in) :: ntot,np integer(kind=c_int), intent(in) :: itype(np) integer :: i1,i2,len_type i1 = ntot-np+1 i2 = ntot ! set particle type len_type = size(iamtype(:,1)) if (len_type.gt.1) then if (i2 > len_type) then print*,'error with itype length',i2,len_type return endif iamtype(i1:i2,1) = int(itype(1:np),kind=int1) endif return end subroutine read_cactus_itype_fromc !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass,iutherm,ih,irho use params use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord use system_utils, only:envlist,ienvironment use cactushdf5read, only:blocklabel use asciiutils, only:lcase implicit none integer :: i,icol if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif blocklabel(1:5) = (/'x ','y ','z ','dx','m '/) ix(1) = 1 ix(2) = 2 ix(3) = 3 ih = 4 ipmass = 5 iutherm = 0 irho = 0 do icol=1,size(blocklabel) select case(trim(blocklabel(icol))) case('vel[0]') ivx = icol case('dens','density') if (irho==0) irho = icol case('rho') irho = icol end select label(icol) = trim(blocklabel(icol)) enddo ! set labels of the quantities read in if (ix(1).gt.0) label(ix(1:ndim)) = labelcoord(1:ndim,1) !if (irho.gt.0) label(irho) = 'density' !if (iutherm.gt.0) label(iutherm) = 'u' !if (ipmass.gt.0) label(ipmass) = 'particle mass' !if (ih.gt.0) label(ih) = 'h' ! set labels for vector quantities if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'_'//labelcoord(i,1) enddo endif ! set labels for each particle type ntypes = 2 labeltype(1) = 'gas' labeltype(2) = 'ghost' UseTypeInRenderings(:) = .true. UseTypeInRenderings(2) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/exact_rhoh.f90000644 000766 000000 00000004153 13261626263 016222 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !---------------------------------------------------------- ! plots the relation between smoothing length and density ! ! ie. h = h_fact*(pmass/rho)^(1/ndim) ! !---------------------------------------------------------- module rhoh implicit none public :: exact_rhoh contains subroutine exact_rhoh(iplot,ndim,hfact,pmassval,xplot,yplot,ierr) implicit none integer, intent(in) :: iplot,ndim integer, intent(out) :: ierr real, intent(in) :: hfact,pmassval real, dimension(:), intent(in) :: xplot real, dimension(size(xplot)), intent(out) :: yplot if (hfact.gt.0.01) then ierr = 0 if (iplot.eq.2) then ! x axis is h where (xplot > tiny(xplot)) yplot(:) = pmassval*(hfact/xplot(:))**ndim elsewhere yplot(:) = huge(yplot) end where else ! y axis is h where (xplot > tiny(xplot)) yplot(:) = hfact*(pmassval/xplot(:))**(1./FLOAT(ndim)) elsewhere yplot(:) = huge(yplot) end where endif write(*,"(a,f5.2,a,es9.2,a,i1,a)") ' plotting h = ',hfact, & '*(',pmassval,'/rho)**(1/',ndim,')' else print "(a)",'error: hfact = 0: can''t plot h vs rho exact solution' ierr = 1 endif return end subroutine exact_rhoh end module rhoh splash/src/exact_fromfile.f90000644 000766 000000 00000005522 13261626263 017066 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------ ! reads an exact solution from a file ! ! file should contain two or more columns containing ! x axis data and y axis data ! ! this is plotted as a line on the chosen graph !------------------------------------------------ module exactfromfile implicit none contains subroutine exact_fromfile(filename,xexact,yexact,ixcolfile,iycolfile,iexactpts,ierr) use asciiutils, only:get_ncolumns implicit none character(len=*), intent(in) :: filename real, intent(out), dimension(:) :: xexact, yexact integer, intent(in) :: ixcolfile,iycolfile integer, intent(out) :: iexactpts, ierr integer :: i,j,ncolumns,nheaderlines integer, parameter :: lu = 33 character(len=10) :: str real :: dum ierr = 0 open(unit=lu,file=filename,iostat=ierr,status='old',form='formatted') if (ierr /= 0) then ierr = 1 print*,'error opening ',filename return endif !--query number of header lines call get_ncolumns(lu,ncolumns,nheaderlines) !--skip header lines do i=1,nheaderlines read(lu,*) enddo !--read data from file do i=1,size(xexact) if (ixcolfile.gt.iycolfile) then read(lu,*,end=10,err=20) (dum,j=1,iycolfile-1),yexact(i),(dum,j=iycolfile+1,ixcolfile-1),xexact(i) elseif (ixcolfile.eq.iycolfile) then read(lu,*,end=10,err=20) (dum,j=1,ixcolfile-1),xexact(i) yexact(i) = xexact(i) else read(lu,*,end=10,err=20) (dum,j=1,ixcolfile-1),xexact(i),(dum,j=ixcolfile+1,iycolfile-1),yexact(i) endif enddo print*,'WARNING: reached array limits in ',trim(filename),': partial solution read' ierr = -1 close(lu) return 10 continue iexactpts = i-1 write(str,"(i10)") iexactpts print "(a)",' finished reading '//trim(filename)//': '//trim(adjustl(str))//' read' close(lu) return 20 print*,'error reading ',trim(filename),': partial solution read' iexactpts = i - 1 ierr = -2 close(lu) return end subroutine exact_fromfile end module exactfromfile splash/src/exact_polytrope.f90000644 000766 000000 00000006262 13261626263 017322 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !--------------------------------------- ! numerically integrate a polytrope ! with the density = 1 at the centre ! This uses scaled variables (no sigma in the equation) ! then the radius is scaled to give the correct mass ! Based on an old subroutine from Joe Monaghan !----------------------------------------- module polytrope implicit none public :: exact_polytrope contains subroutine exact_polytrope(gamma,polyk,totmass,rplot,denplot,npts,ierr) implicit none integer, intent(out) :: npts,ierr real, intent(in) :: gamma real, intent(in) :: polyk,totmass real, dimension(:), intent(inout) :: rplot real, dimension(size(rplot)), intent(out) :: denplot integer :: i,j real, parameter :: pi = 3.1415926536 real, dimension(size(rplot)) :: r,v,den real :: dr,an,rhs,rstar,totmassf real :: rhocentre,fac,rfac,G ierr = 0 print*,' gamma :',gamma dr = 0.001 G = 1. an = 1./(gamma-1.) v(1) = 0.0 v(2) = dr*(1.0 - dr*dr/6. ) r(1) = 0. i = 2 do while (v(i).ge.0.) r(i) = (i-1)*dr rhs = - r(i)*(v(i)/r(i))**an v(i+1) = 2*v(i) - v(i-1) + dr*dr*rhs i = i + 1 if (i+1.gt.size(rplot)) then dr = dr*2. r(2) = dr v(2) = dr*(1.0 - dr*dr/6. ) i = 2 endif enddo npts = i-1 rstar = r(npts) !-------------------------------------- ! calculate the mass out to radius r ! using the density without the central ! density multiplier- call this totmassf ! the true scaled totmass = 1. !---------------------------------------- den(1) = 1.0 totmassf = 0. do j = 2,npts den(j) = (v(j)/r(j))**an totmassf = totmassf + 4.*pi*r(j)*r(j)*den(j)*dr enddo !--------------------------------------------------- ! rescale the central density to give desired massq ! then rescale the radius to match this !--------------------------------------------------- fac = (gamma*polyk)/(4.*pi*G*(gamma - 1.)) rhocentre = ((totmass/totmassf)/fac**1.5)**(2./(3.*gamma - 4.)) rfac = sqrt(fac*rhocentre**(gamma - 2.)) print*,' Rstar = ',rstar*rfac print*,' central density :',rhocentre print*,' total mass :',totmass rplot = r * rfac denplot = rhocentre * den return end subroutine exact_polytrope end module polytrope splash/src/timestepping.f90000644 000766 000000 00000033551 13261626263 016612 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module timestepping implicit none public :: timestep_loop private contains ! ! This subroutine drives the main plotting loop ! subroutine timestep_loop(ipicky,ipickx,irender,icontourplot,ivecplot) use filenames, only:nsteps,ifileopen use particle_data, only:iamtype,npartoftype,masstype,time,gamma,dat,time_was_read use settings_data, only:istartatstep,iendatstep,nfreq,DataIsBuffered, & iUsesteplist,isteplist,ncolumns,ipartialread use settings_page, only:interactive,nstepsperpage,iColourEachStep,iChangeStyles,nomenu use timestep_plotting, only:initialise_plotting,plotstep use plotlib, only:plot_close implicit none integer, intent(in) :: ipicky,ipickx,irender,icontourplot,ivecplot integer :: ipos, istep, ilocindat, iadvance, istepsonpage, istepprev logical :: ipagechange call initialise_plotting(ipicky,ipickx,irender,icontourplot,ivecplot) !---------------------------------------------------------------------------- ! loop over timesteps (flexible to allow going forwards/backwards in ! interactive mode) ! ! bookkeeping is as follows: ! ipos : current step number or position in steplist array ! ilocindat : current or requested location in the dat array ! (usually 1, but can be > 1 if more than one step per file ! or data is buffered to memory) ! (requested location is sent to get_nextstep which skips files ! or steps appropriately and sets actual location) ! istep : current step number ! (as if all steps were in memory in sequential order) ! istepsonpage : number of steps which have been plotted on current page ! this is used because steps are coloured/marked differently ! from this routine (call to colour_timestep) ! !---------------------------------------------------------------------------- ipos = istartatstep iadvance = nfreq ! amount to increment timestep by (changed in interactive) istepsonpage = 0 istep = istartatstep istepprev = 0 !--if the current file has only been partially read, ! make sure we read the file again now that we may have different plotting options if (ipartialread) ifileopen = 0 over_timesteps: do while (ipos.le.iendatstep) ipos = max(ipos,1) !--can't go further back than ipos=1 if (iUseStepList) then istep = isteplist(ipos) if (istep.gt.nsteps) then print*,'ERROR: step > nsteps in step list, setting step = last' istep = nsteps elseif (istep.le.0) then !--this should never happen stop 'internal error: corrupted step list: please send bug report' endif else istep = ipos if (istep.ge.nsteps) then istep = nsteps iendatstep = istep ipos = min(ipos,iendatstep) endif endif ! !--make sure we have data for this timestep ! if (DataIsBuffered) then !--if data is in memory, we just go to the position in dat if (istep.gt.nsteps) then print*,'error: step # > nsteps, setting step = last' istep = nsteps endif ilocindat = istep else !--otherwise read file containing this step into memory and get position in dat array ! (note that nsteps can change in get_nextstep, so may need to re-evaluate ! whether we are on the last step or not, and adjust iendatstep to last step) ! call get_nextstep(istep,ilocindat) if (.not.iUseStepList .and. istep.ge.nsteps) then !--reset step position to last useable timestep (ie. nsteps) istep = nsteps iendatstep = istep !--use interactive halt at last step (ie. set position = last position) if (interactive) then ipos = min(ipos,iendatstep) !--if istep has changed, may need to re-read step ! (get_nextstep does nothing if istep is the same) call get_nextstep(istep,ilocindat) endif endif !--this is a general "catch all" when step cannot be located if (ilocindat.le.0) then print*,'ERROR: could not locate timestep' exit over_timesteps endif endif ! !--write timestepping log ! if (.not.time_was_read(time(ilocindat))) then print 32, istep elseif (time(ilocindat).lt.1.e-2 .or. time(ilocindat).gt.1.e2) then print 33, time(ilocindat),istep else print 34, time(ilocindat),istep endif 32 format (5('-'),' t = (not read), dump #',i5,1x,18('-')) 33 format (5('-'),' t = ',es9.2,', dump #',i5,1x,18('-')) 34 format (5('-'),' t = ',f8.2,', dump #',i5,1x,18('-')) istepsonpage = istepsonpage + 1 ! if ((nstepsperpage.gt.1 .and. istepsonpage > 1 .and. istepsonpage.le.nstepsperpage).or.nstepsperpage.eq.0) then if ((nstepsperpage.gt.1 .and. istepsonpage.le.nstepsperpage).or.nstepsperpage.eq.0) then ipagechange = .false. else istepsonpage = 1 ipagechange = .true. endif !--colour the timestep if appropriate if ((nstepsperpage.eq.0 .or. nstepsperpage.gt.1) .and. (iColourEachStep .or. iChangeStyles)) then call colour_timestep(istepsonpage,iColourEachStep,iChangeStyles) else !--otherwise set default colours for each particle type ! (do not call if repeating same step so interactive colours stick for same step) if (istep.ne.istepprev) call colourparts_default(npartoftype(:,ilocindat),iamtype(:,ilocindat)) istepprev = istep endif ! print*,'ipos = ',ipos,' istep = ',istep,' iposindat = ',ilocindat call plotstep(ipos,istep,istepsonpage,irender,icontourplot,ivecplot,iamtype(:,ilocindat), & npartoftype(:,ilocindat),masstype(:,ilocindat),dat(:,:,ilocindat), & time(ilocindat),gamma(ilocindat),ipagechange,iadvance) ! !--increment timestep -- iadvance can be changed interactively ! if (iadvance.eq.-666) exit over_timesteps ! this is the interactive quit signal ipos = ipos + iadvance ! if ipos goes over iendatstep, this ends the loop enddo over_timesteps if (.not.interactive) then if (nomenu) then !--gracefully exit print "(/,a,/)",'Finished plotting: Many thankyous for your kind custom.' else ! prepare to return to main menu print*,'press return to finish' read* !--if somehow the data has become corrupted (e.g. last file full of rubbish) ! read in the first dump again if (ncolumns.le.0) then print*,'data is corrupted: re-reading first data file' call get_nextstep(1,ilocindat) endif endif endif !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! call plot_close return end subroutine timestep_loop !------------------------------------------------------------------- ! works out whether or not we need to read another dump into memory !------------------------------------------------------------------- recursive subroutine get_nextstep(istep,ilocindat) use filenames, only:nstepsinfile,nfiles,ifileopen,iposopen,nsteps use getdata, only:get_data use settings_data, only:buffer_steps_in_file implicit none integer, intent(in) :: istep integer, intent(out) :: ilocindat integer :: ifile,nstepstotal,nstepsprev,iposinfile ! !--request is for step istep ! need to determine which file step i is in, ! whether or not it is already in memory (and read it if not) ! and finally determine position of requested step in dat array ! ifile = 0 nstepstotal = 0 nstepsprev = 0 iposinfile = 1 ! this should always be overwritten anyway do while (nstepstotal.lt.istep .and. ifile.lt.nfiles) ifile = ifile + 1 nstepsprev = nstepstotal nstepstotal = nstepstotal + nstepsinfile(ifile) enddo if (nstepstotal.ge.istep) then !--set position in dat array depending on how many steps are in the file iposinfile = istep - nstepsprev else !--this is where we cannot locate the timestep in the data (not enough steps) ! ie. ifile > nfiles print*,'reached last useable timestep' ilocindat = 0 return endif !print*,'step ',istep,' in file ',ifile,' nsteps = ',nsteps !print*,'position in file = ',iposinfile if (istep.gt.nsteps) then ilocindat = 0 return endif ilocindat = iposinfile ! !--if data is not stored in memory, read next step from file ! At the moment assumes number of steps in each file are the same ! !--neither or these two error conditions should occur if (ifile.gt.nfiles) then print*,'*** get_nextstep: error: ifile > nfiles' elseif (ifile.lt.1) then print*,'*** get_nextstep: error: request for file < 1' elseif (ifile.ne.ifileopen) then ! !--read next data file and determine position in file ! call get_data(ifile,.true.) ! !--because nstepsinfile is predicted for files which have ! not been opened, we may have the situation where ! iposinfile does not point to a real timestep (ie. iposinfile > nstepsinfile). ! In this case we query the step again with our better knowledge of nstepsinfile. ! if (iposinfile.gt.nstepsinfile(ifile)) then print*,'not enough steps in file... trying next file' call get_nextstep(istep,ilocindat) endif elseif (.not.buffer_steps_in_file .and. nstepsinfile(ifile) > 1) then if (iposinfile <= nstepsinfile(ifile) .and. iposinfile /= iposopen) then ! if all steps have not been read from the file, read just the next one call get_data(ifile,.true.,iposinfile=iposinfile) ilocindat = 1 else ilocindat = 1 endif endif return end subroutine get_nextstep !------------------------------------------------------------- ! colours all the particles a given colour for this timestep ! and/or changes the marker type for type 1 particles !------------------------------------------------------------- subroutine colour_timestep(istep,iChangeColours,iChangeStyles) use particle_data, only:icolourme use settings_part, only:linecolourthisstep,linestylethisstep,imarktype use settings_page, only:maxlinestyle,modlinestyle,maxcolour,modcolour,linepalette use plotlib, only:plotlib_maxlinestyle,plotlib_maxlinecolour implicit none integer, intent(in) :: istep logical, intent(in) :: iChangeColours, iChangeStyles integer :: icolour,imarkernumber if (iChangeColours) then if (allocated(icolourme)) then icolour = istep + 1 icolour = (icolour-2)/modcolour + 1 if (icolour.gt.plotlib_maxlinecolour) then print "(a,i2,a)",'warning: step colour > ',plotlib_maxlinecolour,': re-using colours' icolour = mod(icolour-1,plotlib_maxlinecolour) + 1 endif icolour = mod(icolour-1,min(maxcolour,plotlib_maxlinecolour)) + 1 icolourme = icolour if (linepalette < 0) then ! for custom line colours do not use foreground/background colours - allow these to be changed icolour = icolour + 1 icolourme = icolour linecolourthisstep = icolour else linecolourthisstep = icolour endif else print "(a)",'***error: array not allocated in colour_timestep***' endif endif if (iChangeStyles) then !--modlinestyle should not be greater than max line styles in plotting library linestylethisstep = mod((istep-1)/modlinestyle,min(maxlinestyle,plotlib_maxlinestyle)) + 1 imarkernumber = istep select case(imarkernumber) case(1) imarktype(1) = 4 case(2) imarktype(1) = 17 case(3) imarktype(1) = 2 case(4) imarktype(1) = 3 case(5:16) imarktype(1) = imarkernumber case(17:) imarktype(1) = imarkernumber + 1 end select endif return end subroutine colour_timestep !--------------------------------------------------------------------------------------- ! colours all the particles using the default colour for their type !--------------------------------------------------------------------------------------- subroutine colourparts_default(npartoftype,iamtype) use params, only:int1 use settings_data, only:ntypes use particle_data, only:icolourme use settings_part, only:idefaultcolourtype implicit none integer, dimension(:), intent(in) :: npartoftype integer(kind=int1), dimension(:), intent(in) :: iamtype integer :: i,index1,index2,itype if (size(iamtype).gt.1) then do i=1,sum(npartoftype(1:ntypes)) itype = iamtype(i) if (itype.gt.0 .and. itype.le.ntypes) then if (idefaultcolourtype(itype).ge.0) then icolourme(i) = idefaultcolourtype(itype) endif endif enddo else index1 = 1 do itype=1,ntypes index2 = index1 + npartoftype(itype) - 1 if (idefaultcolourtype(itype).ge.0) then icolourme(index1:index2) = idefaultcolourtype(itype) endif index1 = index2 + 1 enddo endif end subroutine colourparts_default end module timestepping splash/src/system_unix_NAG.f90000644 000766 000000 00000003403 13261626263 017147 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- ! ! this module contains wrappers for all of the ! system and compiler dependent routines ! ! these are called from the main program by their generic names, ! and in here the actual call to the system is performed ! ! THIS VERSION IS FOR THE NAG f95 COMPILER ! module system_commands use f90_unix ! uncomment this for NAG f95 compiler implicit none contains subroutine get_number_arguments(nargs) integer, intent(out) :: nargs nargs = iargc() end subroutine get_number_arguments subroutine get_argument(iarg,argstring) integer, intent(in) :: iarg character(len=*), intent(out) :: argstring call getarg(iarg,argstring) end subroutine get_argument subroutine get_environment(variable,value) character(len=*), intent(in) :: variable character(len=*), intent(out) :: value call getenv(variable,value) end subroutine get_environment end module system_commands splash/src/calc_quantities.f90000644 000766 000000 00000113145 13261626263 017250 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ! This module allows arbitrary functions to be computed from the ! particle data. Uses the fparser module to parse the functions. ! !----------------------------------------------------------------- module calcquantities use labels, only:lenlabel,lenunitslabel use params, only:maxplot implicit none public :: calc_quantities,setup_calculated_quantities public :: calc_quantities_use_x0,get_calc_data_dependencies integer, parameter, private :: maxcalc = 35 character(len=lenlabel), dimension(maxcalc) :: calcstring = ' ' character(len=lenlabel), dimension(maxcalc) :: calclabel = ' ' character(len=lenunitslabel), dimension(maxcalc) :: calcunitslabel = ' ' integer, parameter, private :: lenvars = 20 ! max length for any variable integer, parameter, private :: nextravars = 5 character(len=lenvars), dimension(nextravars), parameter, private :: & extravars=(/'t ','gamma','x0 ','y0 ','z0 '/) logical, save :: firstcall = .true. namelist /calcopts/ calcstring,calclabel,calcunitslabel public :: calcopts,calcstring,calclabel,calcunitslabel,firstcall private contains !----------------------------------------------------------------- ! ! Allow the user to edit the list of function strings used ! to compute additional quantities from the particle data ! !----------------------------------------------------------------- subroutine setup_calculated_quantities(ncalc,quiet) use settings_data, only:ncolumns use prompting, only:prompt implicit none integer, intent(out) :: ncalc integer :: ncalctot,istart,iend, ipick, ninactive, i logical :: done,first character(len=1) :: charp integer, dimension(maxcalc) :: incolumn logical, intent(in), optional :: quiet logical :: verbose = .true. ipick = ncolumns + 1 done = .false. first = .true. if (present(quiet)) verbose = .false. ! !--on the first call to setup, prefill the list of calculated ! quantities with ALL of the valid examples. ! if (ncalc.eq.0 .and. firstcall) call print_example_quantities(verbose,ncalc) firstcall = .false. charp = 'a' calcmenu: do while (.not.done) call check_calculated_quantities(ncalc,ncalctot,incolumn,verbose) ninactive = ncalctot - ncalc iend = maxcalc if (ncalctot.gt.0 .or. .not.first) then charp='q' !'a' if (.not.first) charp = 'q' if (verbose) then print* call prompt(' a)dd to, e)dit, c)lear current list or q)uit/finish? ',& charp,list=(/'a','e','c','q','s','S','Q'/),noblank=.true.) endif select case(charp) case('a') istart = ncalctot iend = ncalctot+1 case('e') if (ninactive.gt.0 .and. ncalc.gt.0) then call prompt(' pick a function to edit ',ipick,-ninactive,-1,ncolumns+1,ncolumns+ncalc) elseif (ncalc.gt.0) then call prompt(' pick a function to edit ',ipick,ncolumns+1,ncolumns+ncalc) endif istart = 0 do i=1,ncalctot if (incolumn(i).eq.ipick) istart = i - 1 enddo iend = istart + 1 case('c') calcstring(:) = ' ' calclabel(:) = ' ' calcunitslabel(:) = ' ' first = .false. cycle calcmenu case('q','Q','s','S') done = .true. case default istart = 0 iend = maxcalc end select else istart = 0 iend = 1 endif if (.not.done) call add_calculated_quantities(istart,iend,ncalc,first,incolumn,verbose) first = .false. enddo calcmenu if (ncalc.lt.10) then print "(a,i1,a)",' setup ',ncalc,' additional quantities' else print "(a,i2,a)",' setup ',ncalc,' additional quantities' endif ! !--reset verbose so we can edit the list the next time through ! if (present(quiet)) verbose = .true. end subroutine setup_calculated_quantities !----------------------------------------------------------------- ! ! utility (private) to add one or more calculated quantities ! to the current list and/or edit previous settings ! !----------------------------------------------------------------- subroutine add_calculated_quantities(istart,iend,ncalc,printhelp,incolumn,verbose) use prompting, only:prompt use fparser, only:checkf use settings_data, only:ncolumns,iRescale,required use labels, only:shortstring implicit none integer, intent(in) :: istart,iend integer, intent(out) :: ncalc logical, intent(in) :: printhelp integer, dimension(maxcalc), intent(in) :: incolumn logical, intent(in) :: verbose integer :: i,j,ntries,ierr,iequal,nvars logical :: iask character(len=120) :: string character(len=lenvars), dimension(maxplot+nextravars) :: vars i = istart + 1 ntries = 0 ncalc = istart if (i.gt.maxcalc) then print "(/,a,i2,a)",' *** Error, maximum number of calculated quantities (',maxcalc,') reached, cannot add any more.' print "(a)", ' *** If you hit this limit, *please email me* so I can change the default limits!' print "(a)", ' *** (and then edit calc_quantities.f90, changing the parameter "maxcalc" to something higher...)' return endif if (printhelp) then print "(/,a)",' Specify a function to calculate from the data ' print "(10(a))",' Valid variables are the column labels',(', '''//trim(extravars(j))//'''',j=1,nextravars-1),& ' and '''//trim(extravars(nextravars))//'''' print "(a)",' Spaces, escape sequences (\d), arithmetic operators and units labels' print "(a)",' are removed from variable names. Note that previously calculated' print "(a)",' quantities can be used in subsequent calculations.' endif call print_example_quantities(verbose) overfuncs: do while(ntries.lt.3 .and. i.le.iend .and. i.le.maxcalc) if (len_trim(calcstring(i)).ne.0 .or. ncalc.gt.istart) then if (incolumn(i).gt.0) then write(*,"(a,i2,a)") '[Column ',incolumn(i),']' else write(*,"(a)") '[Currently inactive]' endif endif if (len_trim(calclabel(i)).gt.0) then string = trim(calclabel(i))//' = '//trim(calcstring(i)) else string = trim(calcstring(i)) endif call prompt('Enter function string to calculate (blank for none) ',string) if (len_trim(string).eq.0) then ! !--if editing list and get blank string at prompt, ! remove the entry and shuffle the list appropriately ! do j=i,maxcalc-1 !print*,' shuffling ',j,' = '//trim(calcstring(j+1)) calcstring(j) = trim(calcstring(j+1)) if (j+1.lt.size(calclabel)) then calclabel(j) = calclabel(j+1) endif if (len_trim(calcstring(j)).eq.0) then if (j.eq.i) then exit overfuncs else cycle overfuncs endif endif enddo else ! !--set label from what lies to the left of the equal sign ! and the calcstring from what lies to the right ! (if no equals sign, then prompt later for the label) ! iequal = index(string,'=') call splitstring(string,calclabel(i),calcstring(i)) ! !--fill variable array with the list of valid variable ! names for this column ! call get_variables(ncolumns+i-1,nvars,vars) ! !--check for errors parsing function ! ierr = checkf(shortstring(calcstring(i)),vars(1:nvars)) if (ierr.ne.0 ) then ntries = ntries + 1 print "(a,i1,a)",' error parsing function string: try again (',ntries,' of 3)' if (ntries.eq.3) then iask = .false. call prompt(' Cannot parse function (after 3 attempts). Set as inactive function anyway?',iask) if (iask) then ierr = 0 else calcstring(i) = ' ' endif endif else write(*,"(a)",advance='no') 'Function parses OK: ' required(ncolumns+i) = .true. endif if (ierr.eq.0) then ! !--prompt for label if not set ! if (iequal.eq.0) then call prompt(' Enter label for this quantity ',calclabel(i),noblank=.true.) endif if (iRescale) then call prompt(' Enter units label for this quantity ',calcunitslabel(i)) endif !print "(a,a,i2,/)",'Setting '//trim(calclabel(i)), & ! ' = '//trim(calcstring(i))//' in column ',ncolumns+i ncalc = i i = i + 1 endif endif enddo overfuncs end subroutine add_calculated_quantities !--------------------------------------------------------------------- ! ! utility to split input string into label and function at the ! equals sign ! !--------------------------------------------------------------------- subroutine splitstring(string,calclabel,calcstring) implicit none character(len=*), intent(in) :: string character(len=*), intent(inout) :: calclabel character(len=*), intent(out) :: calcstring integer :: iequal iequal = index(string,'=') if (iequal.ne.0) then calclabel = string(1:iequal-1) calcstring = string(iequal+1:len_trim(string)) else calcstring = trim(string) endif !--remove preceding spaces calcstring = trim(adjustl(calcstring)) end subroutine splitstring !--------------------------------------------------------------------- ! ! utility to give a nice list of examples to follow / cut and paste ! [ this basically replaces what was hardwired into the ! old calc_quantities routine ] ! !--------------------------------------------------------------------- subroutine print_example_quantities(verbose,ncalc) use labels, only:label,unitslabel,shortlabel,lenlabel,irho,iutherm,iBfirst,& ix,icv,idivB,ih,iradenergy,iamvec,labelvec,idustfrac,& idustfracsum,ideltav,ivx use settings_data, only:ncolumns,ndim,icoordsnew,ndimV,ndusttypes,idustfrac_plot use geometry, only:labelcoord implicit none logical :: verbose integer, intent(inout), optional :: ncalc logical :: prefill character(len=lenlabel) :: string,temp,labelprev character(len=lenlabel) :: ldfrac,ldfake,ldfracsum,idust_string integer :: i,j,ivecstart,ierr,ilen logical :: gotpmag,gotpressure gotpmag = .false. gotpressure = .false. prefill = .false. if (present(ncalc)) prefill = .true. if (verbose) then if (prefill) then print "(/,a)",' Prefilling list with useful quantities from current data...' else print "(/,a)",' Examples based on current data: ' endif endif if (idustfrac > 0) then if (idustfracsum == 0) then !--one dust phase ldfracsum = shortlabel(label(idustfrac),unitslabel(idustfrac)) else !--multiple dust phases if (verbose) print*,' Warning! Density refers to total density: rhogas+rhodust' ldfracsum = shortlabel(label(idustfracsum),unitslabel(idustfracsum)) endif endif !--radius string = ' ' if (ndim.gt.0 .and. icoordsnew.eq.1 .and. ncolumns.ge.ndim) then write(string,"(a)") 'r = sqrt(('// & trim(shortlabel(label(ix(1)),unitslabel(ix(1))))//'-'//trim(labelcoord(1,1))//'0)**2' ilen = len_trim(string) if (ndim.gt.1) then write(string(ilen+1:),"(a,a,a)",iostat=ierr) & (' + ('//trim(shortlabel(label(ix(i)),unitslabel(ix(i))))// & '-'//trim(labelcoord(i,1))//'0)**2',i=2,ndim),')' else write(string(ilen+1:),"(a)") ')' endif if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif elseif (ncolumns.ge.2 .and. .not.prefill) then !--if ndim=0 give random example to give at least one print "(11x,a)",trim(shortlabel(label(1)))//'*'//trim(shortlabel(label(2))) endif !--pressure string = ' ' if (irho.gt.0 .and. iutherm.gt.0) then gotpressure = .true. if (idustfrac>0 .and. ndusttypes>1) then write(string,"(a)",iostat=ierr) & 'pressure = (gamma-1)*(1 - '//trim(ldfracsum)//')*' & //trim(shortlabel(label(irho),unitslabel(irho)))// & '*'//trim(shortlabel(label(iutherm),unitslabel(iutherm))) else write(string,"(a)",iostat=ierr) & 'pressure = (gamma-1)*'//trim(shortlabel(label(irho),unitslabel(irho)))// & '*'//trim(shortlabel(label(iutherm),unitslabel(iutherm))) endif if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif endif ! !--one-fluid dust stuff ! if (idustfrac.gt.0 .and. irho.gt.0) then string = ' ' !--gas density write(string,"(a)",iostat=ierr) '\rho_{g} = ' & //trim(shortlabel(label(irho),unitslabel(irho))) & //'*(1 - '//trim(ldfracsum)//')' if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) labelprev = calclabel(ncalc) calcunitslabel(ncalc) = unitslabel(irho) else print "(11x,a)",trim(string) call splitstring(string,labelprev,temp) endif !--dust density do i = 1,ndusttypes if (ndusttypes>1) then write(idust_string,"(I10)") i write(idust_string,"(a)") '{d,'//trim(adjustl(idust_string))//'}' ldfrac = shortlabel(label(idustfracsum+i),unitslabel(idustfracsum+i)) ldfake = shortlabel(label(idustfrac_plot),unitslabel(idustfrac)) else write(idust_string,"(a)") '{d}' ldfrac = shortlabel(label(idustfrac),unitslabel(idustfrac)) ldfake = '1' endif write(string,"(a)",iostat=ierr) '\rho_'//trim(adjustl(idust_string))//' = ' & //trim(ldfrac)//'*'//trim(shortlabel(label(irho),unitslabel(irho))) if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) calcunitslabel(ncalc) = unitslabel(irho) else print "(11x,a)",trim(string) endif enddo !--dust-to-gas ratio write(string,"(a)",iostat=ierr) 'dust-to-gas ratio = ' & //trim(ldfracsum)//'/(1. - '//trim(ldfracsum)//')' if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif if (ideltav.gt.0 .and. ivx.gt.0 .and. ndimV.gt.0) then if (ndusttypes==1) then !--gas velocities do i=1,ndimV write(string,"(a)",iostat=ierr) trim(labelvec(ivx))//'_{gas,'//trim(labelcoord(i,icoordsnew))//'} = ' & //trim(shortlabel(label(ivx + i-1),unitslabel(ivx + i-1))) & //' - '//trim(shortlabel(label(idustfrac),unitslabel(idustfrac))) & //'*'//trim(shortlabel(label(ideltav + i-1),unitslabel(ideltav + i-1))) ! //trim(shortlabel(label(ivx + i-1),unitslabel(ivx + i-1))) & ! //' - r_{dust}/'//trim(shortlabel(label(irho),unitslabel(irho))) & ! //'*'//trim(shortlabel(label(ideltav + i-1),unitslabel(ideltav + i-1))) if (prefill) then ncalc = ncalc + 1 !if (i.eq.1) labelvec(ncalc) = 'v_{gas}' !iamvec(ncalc) = ncolumns + ncalc - i + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif enddo !--dust velocities do i=1,ndimV write(string,"(a)",iostat=ierr) trim(labelvec(ivx))//'_{dust,'//trim(labelcoord(i,icoordsnew))//'} = ' & //trim(shortlabel(label(ivx + i-1),unitslabel(ivx + i-1))) & //' + (1 - '//trim(shortlabel(label(idustfrac),unitslabel(idustfrac))) & //')*'//trim(shortlabel(label(ideltav + i-1),unitslabel(ideltav + i-1))) ! //trim(shortlabel(label(ivx + i-1),unitslabel(ivx + i-1))) & ! //' + r_{gas}/'//trim(shortlabel(label(irho),unitslabel(irho))) & ! //'*'//trim(shortlabel(label(ideltav + i-1),unitslabel(ideltav + i-1))) if (prefill) then ncalc = ncalc + 1 !if (i.eq.1) labelvec(ncalc) = 'v_{dust}' !iamvec(ncalc) = ncolumns + ncalc - i + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif enddo else ! Still needs to be implemented... endif endif endif ! !--magnitudes of all vector quantities (only if cartesian coords are set) ! ivecstart = 0 if (icoordsnew.eq.1 .and. ndim.gt.0 .and. ndimV.gt.0) then do i=1,ncolumns if (iamvec(i).gt.0 .and. iamvec(i).le.ncolumns .and. iamvec(i).ne.ivecstart) then ivecstart = iamvec(i) string = ' ' write(string,"(a)",iostat=ierr) '|'//trim(labelvec(ivecstart))//'| '// & '= sqrt('//trim(shortlabel(label(ivecstart),unitslabel(ivecstart)))//'**2' ilen = len_trim(string) if (ndimV.gt.1) then write(string(ilen+1:),"(a,a,a)",iostat=ierr) & (' + '//trim(shortlabel(label(j),unitslabel(j)))//'**2',& j=ivecstart+1,ivecstart+ndimV-1),')' else write(string(ilen+1:),"(a)",iostat=ierr) ')' endif if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif endif enddo endif !--magnetic pressure string = ' ' if (ndim.gt.0 .and. ndimV.gt.0 .and. iBfirst.gt.0 .and. icoordsnew.eq.1) then gotpmag = .true. write(string,"(a)",iostat=ierr) & 'P_{mag} = 0.5*('//trim(shortlabel(label(iBfirst),unitslabel(iBfirst)))//'**2' ilen = len_trim(string) if (ndimV.gt.1) then write(string(ilen+1:),"(a,a,a)",iostat=ierr) & (' + '//trim(shortlabel(label(i),unitslabel(i)))//'**2',i=iBfirst+1,iBfirst+ndimV-1),')' else write(string(ilen+1:),"(a)",iostat=ierr) ')' endif if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif endif !--h*div B / B if (ndim.gt.0 .and. ndimV.gt.0 .and. ih.gt.0 .and. iBfirst.gt.0 .and. & icoordsnew.eq.1 .and. idivB.gt.0) then write(string,"(a)",iostat=ierr) & 'h|div B|/|B| = '//trim(shortlabel(label(ih),unitslabel(ih)))//'*abs(' & //trim(shortlabel(label(idivB),unitslabel(idivB)))//')/' & //'sqrt(('//trim(shortlabel(label(iBfirst),unitslabel(iBfirst)))//'^2' ilen = len_trim(string) if (ndimV.gt.1) then write(string(ilen+1:),"(a,a,a)",iostat=ierr) & (' + '//trim(shortlabel(label(i),unitslabel(i)))//'^2',i=iBfirst+1,iBfirst+ndimV-1),'))' else write(string(ilen+1:),"(a)",iostat=ierr) '))' endif if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif endif !--Plasma beta if (ndim.gt.0 .and. ndimV.gt.0 .and. iBfirst.gt.0 .and. gotpmag .and. gotpressure) then write(string,"(a)",iostat=ierr) 'plasma \beta = pressure/P_mag' if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string)//' [ assuming pressure and Pmag calculated ]' endif endif !--gas temperature if cv present if (ndim.gt.0 .and. iutherm.gt.0 .and. icv.gt.0) then string = ' ' write(string,"(a)",iostat=ierr) 'T_{gas} = '//trim(shortlabel(label(iutherm),unitslabel(iutherm)))//'/' & //trim(shortlabel(label(icv),unitslabel(icv))) if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif endif !--radiation temperature if (ndim.gt.0 .and. irho.gt.0 .and. iradenergy.gt.0) then string = ' ' write(string,"(a)",iostat=ierr) 'T_{rad} = ('//trim(shortlabel(label(irho),unitslabel(irho)))//'*' & //trim(shortlabel(label(iradenergy),unitslabel(iradenergy)))//'/7.5646e-15)**0.25' if (prefill) then ncalc = ncalc + 1 call splitstring(string,calclabel(ncalc),calcstring(ncalc)) else print "(11x,a)",trim(string) endif endif if (.not.prefill) print "(a)" end subroutine print_example_quantities !----------------------------------------------------------------- ! ! utility (private) to print the current list of calculated ! quantities, checking that they parse correctly ! !----------------------------------------------------------------- subroutine check_calculated_quantities(ncalcok,ncalctot,incolumn,verbose) use settings_data, only:ncolumns,iRescale use fparser, only:checkf use labels, only:label,unitslabel,shortstring implicit none integer, intent(out) :: ncalcok,ncalctot integer, dimension(maxcalc), intent(out), optional :: incolumn logical, intent(in), optional :: verbose integer :: i,ierr,nvars,indexinactive character(len=lenvars), dimension(maxplot+nextravars) :: vars logical :: isverbose if (present(verbose)) then isverbose = verbose else isverbose = .true. endif ncalcok = 0 ncalctot = 0 indexinactive = 0 i = 1 if (present(incolumn)) incolumn(:) = 0 if (isverbose) print "(/,a)", ' Current list of calculated quantities:' do while(i.le.maxcalc .and. len_trim(calcstring(i)).ne.0) ! !--get the list of valid variable names for this column ! call get_variables(ncolumns+ncalcok,nvars,vars) ! !--check that the function parses ! ierr = checkf(shortstring(calcstring(i)),vars(1:nvars),Verbose=.false.) if (ierr.eq.0) then ncalcok = ncalcok + 1 if (isverbose) then print "(1x,i2,') ',a50,' [OK]')",ncolumns+ncalcok,trim(calclabel(i))//' = '//calcstring(i) endif if (present(incolumn)) incolumn(i) = ncolumns + ncalcok ! !--set the label for the proposed column here ! so that subsequent calculations can use this variable ! (note that we don't need to set the units label here ! as this is done in the actual calc_quantities call) ! label(ncolumns+ncalcok) = trim(calclabel(i)) unitslabel(ncolumns+ncalcok) = trim(calcunitslabel(i)) if (iRescale) label(ncolumns+ncalcok) = trim(label(ncolumns+ncalcok))//trim(unitslabel(ncolumns+ncalcok)) else indexinactive = indexinactive - 1 if (isverbose) then print "(i3,') ',a50,' [INACTIVE]')",indexinactive,trim(calclabel(i))//' = '//calcstring(i) endif if (present(incolumn)) incolumn(i) = indexinactive endif ncalctot = i i = i + 1 enddo if (ncalcok.eq.0 .and. isverbose) print "(a)",' (none)' end subroutine check_calculated_quantities !----------------------------------------------------------------- ! ! utility (private) to check dependencies of calculated ! quantities, so that we only read what is necessary ! from the dump file ! !----------------------------------------------------------------- subroutine get_calc_data_dependencies(required) use params, only:maxplot use settings_data, only:debugmode use fparser, only:checkf use labels, only:label,shortlabel,shortstring logical, dimension(0:maxplot), intent(inout) :: required character(len=lenvars), dimension(maxplot+nextravars) :: vars integer, dimension(maxcalc) :: incolumn integer :: ncalcok,ncalctot,nvars,i,j call check_calculated_quantities(ncalcok,ncalctot,incolumn,verbose=.false.) do i=ncalctot,1,-1 ! go in REVERSE order to get recursive dependencies properly if (incolumn(i).gt.0) then if (required(incolumn(i))) then if (debugmode) then print*,'DEBUG: computing dependencies for '//trim(label(incolumn(i)))//& ' = '//trim(shortstring(calcstring(i))) endif ! !--get the list of valid variable names for this column ! call get_variables(incolumn(i),nvars,vars) ! !--check if the string contains any preceding variables ! do j=1,incolumn(i)-1 ! !--this could be smarter here (at the moment we just check for ! matching substrings, but we should check for the use of the ! string as an actual variable -- this is mainly an issue for ! single letter variables like x,y,z etc) ! if (index(shortlabel(calcstring(i)),trim(vars(j))).ne.0) then if (debugmode) print*,'DEBUG: -> depends on '//trim(label(j)) required(j) = .true. endif enddo endif endif enddo end subroutine get_calc_data_dependencies !----------------------------------------------------------------- ! ! actually compute the extra quantities from the particle data ! !----------------------------------------------------------------- subroutine calc_quantities(ifromstep,itostep,dontcalculate) use labels, only:label,unitslabel,labelvec,iamvec,ix,ivx,shortstring, & irho,idustfracsum use particle_data, only:dat,npartoftype,gamma,time,maxpart,maxstep,maxcol,iamtype use settings_data, only:ncolumns,ncalc,iRescale,xorigin,debugmode,ndim,required,iverbose, & icoords,icoordsnew,ipartialread,itracktype,itrackoffset,ndusttypes, & idustfrac_plot use mem_allocation, only:alloc use settings_units, only:units use fparser, only:checkf,parsef,evalf,EvalerrMsg,EvalErrType,rn,initf,endf use params, only:maxplot use timing, only:wall_time,print_time use geomutils, only:change_coords use part_utils, only:get_tracked_particle implicit none integer, intent(in) :: ifromstep, itostep logical, intent(in), optional :: dontcalculate integer :: i,j,ncolsnew,ierr,icalc,ntoti,nvars,ncalctot,nused,itrackpart,ndust,icolumn logical :: skip ! real, parameter :: mhonkb = 1.6733e-24/1.38e-16 ! real, parameter :: radconst = 7.5646e-15 ! real, parameter :: lightspeed = 3.e10 ! in cm/s (cgs) real(kind=rn), dimension(maxplot+nextravars) :: vals character(len=lenvars), dimension(maxplot+nextravars) :: vars real, dimension(3) :: x0,v0 real :: t1,t2 ! !--allow dummy call to set labels without actually calculating stuff ! if (present(dontcalculate)) then skip = dontcalculate else skip = .false. endif ierr = 0 ncalc = 0 call check_calculated_quantities(ncalc,ncalctot,verbose=(.not.skip .and. iverbose.gt.0)) if (.not.skip .and. ncalc.gt.0) then nused = 0 if (.not.ipartialread) then ! !--need to be careful if data file has been read fully ! as in this case we also assume all calculated quantities ! have been done. So need to make sure that all quantities ! *are* actually calculated in this case. ! required(:) = .true. endif do i=1,ncalc if (required(ncolumns+i)) nused = nused + 1 enddo if (iverbose > 0) print "(2(a,i2),a,/)",' Calculating ',nused,' of ',ncalctot,' additional quantities...' endif ncolsnew = ncolumns + ncalc if (ncolsnew.gt.maxcol) call alloc(maxpart,maxstep,ncolsnew) ! !--reset iamvec to zero for calculated columns ! iamvec(ncolumns+1:ncolsnew) = 0 labelvec(ncolumns+1:ncolsnew) = ' ' ! !--evaluate functions in turn ! if (.not.skip .and. ncalc.gt.0) then call initf(ncalc) ! !--compile each function into bytecode ! icalc = 1 do i=1,maxcalc if (icalc.le.ncalc) then ! !--get the list of valid variable names for this column ! call get_variables(ncolumns+icalc-1,nvars,vars) ! !--now actually parse the function ! call parsef(icalc,shortstring(calcstring(i)),vars(1:nvars),err=ierr,Verbose=.false.) if (ierr.eq.0) then icalc = icalc + 1 endif endif enddo ! !--evaluate functions from particle data ! call wall_time(t1) do i=ifromstep,itostep ntoti = SUM(npartoftype(:,i)) ndust = npartoftype(2,i) ! !--set origin position ! v0(:) = 0. itrackpart = get_tracked_particle(itracktype,itrackoffset,npartoftype(:,i),iamtype(:,i)) if (itrackpart.gt.0 .and. itrackpart.le.ntoti) then x0(:) = 0. if (ix(1).gt.0 .and. ix(1).le.ncolumns) then x0(1) = dat(itrackpart,ix(1),i) else print*,'** internal error: tracking particle set but cannot locate x coordinate' endif if (ix(2).gt.0 .and. ix(2).le.ncolumns) x0(2) = dat(itrackpart,ix(2),i) if (ix(3).gt.0 .and. ix(3).le.ncolumns) x0(3) = dat(itrackpart,ix(3),i) if (i.eq.ifromstep) then print "(a,i10)",' using position of tracked particle ',itrackpart print "(a,3(e11.3),/)",' (x0,y0,z0) = ',dat(itrackpart,ix(1:ndim),i) endif if (ivx.gt.0 .and. ivx+ndim-1.le.ncolumns) then v0(1:ndim) = dat(itrackpart,ivx:ivx+ndim-1,i) endif else x0(:) = xorigin(:) endif do icalc=1,ncalc if (required(ncolumns+icalc)) then if (debugmode) print*,'DEBUG: ',icalc,' calculating '//trim(label(ncolumns+icalc)) ! !--additional settings allowed in function evaluations ! i.e., time and gamma from dump file and current origin settings ! make sure the number here aligns with the "nextravars" setting ! vals(ncolumns+icalc) = time(i) vals(ncolumns+icalc+1) = gamma(i) vals(ncolumns+icalc+2) = x0(1) vals(ncolumns+icalc+3) = x0(2) vals(ncolumns+icalc+4) = x0(3) if (icoordsnew.ne.icoords .and. ndim.gt.0 .and. all(ix(1:ndim).gt.0)) then ! !--if alternative coordinate system is in use, then we need to apply ! the coordinate transformations to the data BEFORE using it ! to calculate additional quantities ! do j=1,ntoti vals(1:ncolumns+icalc-1) = dat(j,1:ncolumns+icalc-1,i) call change_coords(vals(1:ncolumns+icalc-1),ncolumns+icalc-1,ndim,icoords,icoordsnew,x0(1:ndim),v0(1:ndim)) !--evaluate function with transformed values dat(j,ncolumns+icalc,i) = real(evalf(icalc,vals(1:ncolumns+icalc+nextravars-1))) enddo else !!$omp parallel do default(none) private(j,vals,icolumn) shared(dat,i,icalc,ncolumns,ndusttypes,iamtype,idustfracsum,idustfrac_plot) do j=1,ntoti do icolumn = 1,ncolumns+icalc-1 if (ndusttypes>1 .and. icolumn==irho) then ! !--reverse the fake data calculations if needed in order to make ! the input equations in the calculated extra quantities consistent ! with the physical equations ! select case(iamtype(j,i)) case(1) !--gas vals(icolumn) = dat(j,icolumn,i)/(1.-dat(j,idustfracsum,i)) case(2) !--dust vals(icolumn) = dat(j,icolumn,i)/dat(j,idustfrac_plot,i) case default !--other stuff vals(icolumn) = dat(j,icolumn,i) endselect else vals(icolumn) = dat(j,icolumn,i) endif enddo !vals(1:ncolumns+icalc-1) = dat(j,1:ncolumns+icalc-1,i) dat(j,ncolumns+icalc,i) = real(evalf(icalc,vals(1:ncolumns+icalc+nextravars-1))) enddo !!$omp end parallel do endif if (EvalErrType.ne.0) then print "(a)",' ERRORS evaluating '//trim(calcstring(icalc))//': ' & //trim(EvalerrMsg()) endif ! !--identify calculated quantities based on the label ! if (i.eq.ifromstep) then call identify_calculated_quantity(label(ncolumns+icalc),ncolumns,ncolumns+icalc) endif else if (debugmode) print*,'DEBUG: ',icalc,' skipping '//trim(label(ncolumns+icalc))//' (not required)' endif enddo enddo call endf call wall_time(t2) if (t2-t1.gt.1.) call print_time(t2-t1) endif ! !--override units of calculated quantities if necessary ! if (iRescale .and. any(abs(units(ncolumns+1:ncolumns+ncalc)-1.0).gt.tiny(0.)) & .and. .not.skip) then !write(*,"(/a)") ' rescaling data...' do i=ncolumns+1,ncolumns+ncalc if (abs(units(i)-1.0).gt.tiny(0.) .and. abs(units(i)).gt.tiny(0.)) then dat(:,i,ifromstep:itostep) = dat(:,i,ifromstep:itostep)*units(i) endif if (index(label(i),trim(unitslabel(i))).eq.0) label(i) = trim(label(i))//trim(unitslabel(i)) enddo elseif (iRescale) then do i=ncolumns+1,ncolumns+ncalc if (index(label(i),trim(unitslabel(i))).eq.0) label(i) = trim(label(i))//trim(unitslabel(i)) enddo endif return end subroutine calc_quantities !----------------------------------------------------------------- ! ! utility (private) to internally identify a calculated quantity ! so that the relevant exact solutions can be plotted for that ! quantity. This is mainly just the radius, but can include ! other things also. ! !----------------------------------------------------------------- subroutine identify_calculated_quantity(labelcol,ncolumns,icolumn) use asciiutils, only:lcase use labels, only:irad,ike,ipr use settings_data, only:debugmode implicit none character(len=*), intent(in) :: labelcol integer, intent(in) :: ncolumns,icolumn ! !--identify quantities based on the label ! only do this if the location flags are not already set ! (e.g. in the data read) - but DO overwrite if they ! are calculated quantities as the locations can change ! select case(lcase(trim(labelcol))) case('r','radius','rad') if (irad.le.0 .or. irad.gt.ncolumns) then irad = icolumn if (debugmode) print "(1x,a,i2,a)",'identifying column ',icolumn,' as the radius' endif case('kinetic energy','ke','1/2 v^2','v^2/2') if (ike.le.0 .or. irad.gt.ncolumns) then ike = icolumn if (debugmode) print "(1x,a,i2,a)",'identifying column ',icolumn,' as the kinetic energy' endif case('pressure','pr','p') if (ipr.le.0 .or. ipr.gt.ncolumns) then ipr = icolumn if (debugmode) print "(1x,a,i2,a)",'identifying column ',icolumn,' as the pressure' endif end select end subroutine identify_calculated_quantity !----------------------------------------------------------------- ! ! utility (private) to fill the array of variable names ! !----------------------------------------------------------------- subroutine get_variables(maxlabel,nvars,variables) use labels, only:label,shortlabel,unitslabel implicit none integer, intent(in) :: maxlabel integer, intent(out) :: nvars character(len=*), dimension(:), intent(out) :: variables integer :: i ! !--can use column labels up to the previous quantity calculated ! variables(:) = ' ' nvars = maxlabel + nextravars do i=1,maxlabel variables(i) = trim(adjustl(shortlabel(label(i),unitslabel(i)))) enddo do i=1,nextravars variables(maxlabel+i) = trim(extravars(i)) enddo end subroutine get_variables !----------------------------------------------------------------- ! ! utility (public) to query whether or not the origin position ! is actually used in the currently set quantities ! !----------------------------------------------------------------- logical function calc_quantities_use_x0() implicit none integer :: i calc_quantities_use_x0 = .false. do i=1,maxcalc if (index(calcstring(i),trim(extravars(3))).gt.0) calc_quantities_use_x0 = .true. if (index(calcstring(i),trim(extravars(4))).gt.0) calc_quantities_use_x0 = .true. if (index(calcstring(i),trim(extravars(5))).gt.0) calc_quantities_use_x0 = .true. enddo end function calc_quantities_use_x0 end module calcquantities splash/src/parsetext.f90000644 000766 000000 00000015006 13261626263 016114 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! module containing routines to parse text strings containing ! variables. ! ! For example: ! %(t + 10) ! %(t*2) ! t = %t ! time = %t.5 ! specifying formatting to 5 sig-figs ! ! Uses the function parser module to evaluate the functions ! once extracted from the text. Functions that cannot be ! correctly evaluated are left intact in the text ! ! Dependencies: ! fparser module ! plot_numb from the plotting library !----------------------------------------------------------------- module parsetext use fparser, only:rn implicit none contains subroutine parse_text(string,vars,vals) use asciiutils, only:string_replace,string_sub,lcase character(len=*), intent(inout) :: string real(kind=rn), dimension(:), intent(in) :: vals character(len=*), dimension(:), intent(in) :: vars character(len=1) :: ch character(len=len(string)+128) :: newstring integer :: ia,iz,i0,i9,lenstr,npar,i,istart,iend,ndecimal,ierr,i1,i2 logical :: in_variable,parse real :: r integer, parameter :: ndecimal_default = 3 character(len=32) :: varstring ! !--look for strings of the form: ! ! %var ! %(var) ! %var.n ! %(var + 10).n ! %(var1*var2 + 10) ! %(var1*var2) ! ia = iachar('a') iz = iachar('z') i0 = iachar('0') i9 = iachar('9') lenstr = len(newstring) parse = .false. in_variable = .false. ! print*,'parsing ',trim(string) newstring = string npar = 0 ndecimal = ndecimal_default i1 = 0 i2 = 0 ! ! In the following we extract two substrings: ! ! string(istart:iend) is the string to replace, i.e. %(blah).5 ! string(i1:i2) is the variable/function to evaluate, i.e. blah ! i = 0 do while (i < lenstr) i = i + 1 ch = lcase(newstring(i:i)) select case(ch) case('%') in_variable = .true. if (i.gt.1) then if (newstring(i-1:i-1).eq.'\') in_variable = .false. endif if (in_variable) then istart = i iend = 0 i1 = i + 1 i2 = 0 endif case('(') if (in_variable) then npar = npar + 1 endif case(')') if (in_variable) then npar = max(npar - 1,0) if (i.ge.lenstr) then iend = i if (i2 < i1) i2 = iend parse = .true. elseif (npar.eq.0) then if (newstring(i+1:i+1).ne.'.') then iend = i if (i2 <= i1) i2 = iend parse = .true. endif endif endif case('.') if (in_variable .and. npar <= 0 .and. i < lenstr) then read(newstring(i+1:i+1),"(i1)",iostat=ierr) ndecimal if (ierr.ne.0) ndecimal = 3 iend = i+1 if (i2 < i1) i2 = i - 1 parse = .true. endif case default if ((.not.((iachar(ch) >= ia .and. iachar(ch) <= iz) & .or.(iachar(ch) >= i0 .and. iachar(ch) <= i9)) & .or. i.eq.lenstr) .and. npar <= 0) then if (in_variable) then if (i.eq.lenstr) then iend = i else iend = i - 1 endif if (i2 < i1) i2 = iend parse = .true. endif endif end select if (parse) then in_variable = .false. !print*,'variable = ',newstring(istart:iend), ', ndecimal = ',ndecimal !print*,'formula = ',newstring(i1:i2),i1,i2 r = parse_formula(newstring(i1:i2),vars,vals,ierr) if (ierr.eq.0) then !print*,' r = ',r,' ierr = ',ierr call get_varstring(r,ndecimal,varstring) !print*,'varstring: "',varstring,'"' call string_sub(newstring,istart,iend,trim(varstring)) endif i = i + (len_trim(varstring) - (iend - istart)) - 1 !print*,'newstring = ',newstring !(1:i),len_trim(varstring),iend-istart parse = .false. ndecimal = ndecimal_default endif enddo ! ! get rid of escape sequence on % ! call string_replace(newstring,'\%','%') ! ! replace original string (possibly truncated) ! string = trim(newstring) ! print*,' string: "',trim(newstring),'"' end subroutine parse_text !--------------------------------------------------------------------------- ! ! write the real number r to a string ! with ndec decimal places ! ! uses plot_numb to do the formatting ! !--------------------------------------------------------------------------- subroutine get_varstring(r,ndec,string) use plotlib, only:plot_numb real, intent(in) :: r integer, intent(in) :: ndec character(len=*), intent(out) :: string real :: rtmp integer :: mm,pp,nc if (abs(r) < tiny(r)) then string = '0' nc = 1 else rtmp = abs(r) mm = nint(r/10.**(int(log10(rtmp)-ndec))) pp = int(log10(rtmp) - ndec) call plot_numb(mm,pp,1,string,nc) endif end subroutine get_varstring !--------------------------------------------------------------------------- ! ! evaluate the variable or function via the function parser ! ! i.e. (t + 10) or (t*10) etc. ! ! OUTPUT: a real number ! ! unknown variables or un-parsable syntax return an error and a zero value ! !--------------------------------------------------------------------------- real function parse_formula(string,vars,vals,ierr) use fparser, only:initf,evalf,endf,checkf,parsef character(len=*), intent(in) :: string character(len=*), dimension(:), intent(in) :: vars real(kind=rn), dimension(:), intent(in) :: vals integer, intent(out) :: ierr call initf(1) ierr = checkf(string,vars,verbose=.false.) if (ierr.eq.0) then call parsef(1,string,vars) parse_formula = real(evalf(1,vals)) else parse_formula = 0. endif call endf end function parse_formula end module parsetext splash/src/read_data_rsph.f90000644 000766 000000 00000025563 13261626263 017046 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! the data is stored in the global array dat ! ! THIS VERSION FOR RSPH BINARY DUMPS ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use exact, only:hfact use particle_data, only:npartoftype,time,gamma,dat,maxpart,maxstep,maxcol use params ! use labels use filenames, only:nfiles use settings_data, only:ndim,ndimV,ncolumns,ncalc,ntypes, & buffer_data use mem_allocation, only:alloc use geometry, only:labelcoordsys implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+4) :: datfile integer :: i,icol,ierr,iunit,isizeheader integer :: npart_max,nstep_max integer :: ntoti logical :: singleprecision integer*2, dimension(10) :: sheader integer, dimension(10) :: iheader integer*8, dimension(10):: lheader real, dimension(10) :: rheader real(doub_prec), dimension(10) :: dheader character(len=100) :: headerstring character(len=10), dimension(maxplot) :: cheader real, dimension(:,:), allocatable :: dattemp real(doub_prec), dimension(:,:), allocatable :: dattempd common /chead/ cheader iunit = 11 ! file unit number nstepsread = 0 if (rootname(1:1).ne.' ') then datfile = trim(rootname) !print*,'rootname = ',rootname else print*,' **** no data read **** ' return endif print "(1x,a)",'reading RSPH format' write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! open(unit=iunit,iostat=ierr,file=datfile,status='old',form='unformatted') if (ierr /= 0) then print*,'*** Error opening '//trim(datfile)//' ***' return endif i = indexstart ! !--read first header line ! read(iunit,iostat=ierr,end=80) headerstring print "(1x,a)",'header string="'//trim(headerstring)//'"' ! !--read other header lines (short/normal/long ints, reals, doubles) ! read(iunit,iostat=ierr,end=80) sheader(1:10) if (ierr /= 0) then print*,'WARNING: errors during sheader read' endif print "(1x,a,10(1x,i2))",'sheader = ',sheader(1:10) isizeheader = sheader(1) ndim = sheader(2) ndimV = sheader(3) ntypes = sheader(4) ncolumns = sheader(5) + ndim ! ncolumns in sheader + positions ! !--check for errors in sheader ! if (ndim.gt.3 .or. ndimV.gt.3 .or. ndim.le.0 .or. ndimV.le.0 .or. & ncolumns.le.0 ) then print*,'*** ERROR: header corrupted: ndim = ',ndim,' ndimV = ', ndimV ndim = 0 ndimV = 0 ntypes = 0 ncolumns = 0 close(iunit) return elseif (ncolumns.gt.maxplot) then print "(1x,a)",'*** WARNING: too many columns for array limits' ncolumns = maxplot print "(1x,a,i2,a)",' reading only first ',ncolumns,' columns' endif read(iunit,iostat=ierr,end=80) iheader(1:isizeheader) if (ierr /= 0) then print*,'WARNING: errors during iheader read' endif print "(1x,a,20(1x,i6))",'iheader = ',iheader(1:isizeheader) ntoti = sum(iheader(1:ntypes)) read(iunit,iostat=ierr,end=80) lheader(1:isizeheader) if (ierr /= 0) then print*,'WARNING: errors during lheader read' endif print "(1x,a,20(1x,i6))",'lheader = ',lheader(1:isizeheader) read(iunit,iostat=ierr,end=80) rheader(1:isizeheader) if (ierr /= 0) then print*,'WARNING: errors during rheader read' endif print "(1x,a,20(1x,f8.2))",'rheader = ',rheader read(iunit,iostat=ierr,end=80) dheader(1:isizeheader) if (ierr /= 0) then print*,'WARNING: errors during dheader read' endif print "(1x,a,20(1x,1pe8.2))",'dheader = ',dheader(1:isizeheader) do icol=1,ncolumns-ndim read(iunit,iostat=ierr,end=80) cheader(icol)(1:isizeheader) enddo ! print "(a)",(trim(cheader(icol)),icol=1,ncolumns-ndim) ! !--allocate/reallocate memory for data arrays ! if (buffer_data) then nstep_max = max(nfiles,maxstep,indexstart) else nstep_max = max(1,maxstep,indexstart) endif npart_max = max(int(2.0*ntoti),maxpart) if (.not.allocated(dat) .or. ntoti.gt.maxpart & .or. nstep_max.gt.maxstep .or. ncolumns.gt.maxcol) then call alloc(npart_max,nstep_max,ncolumns+ncalc) endif ! !--count this as a successful read ! nstepsread = nstepsread + 1 ! !--copy header data into now-allocated arrays ! npartoftype(1,i) = iheader(1) npartoftype(2,i) = iheader(2) npartoftype(3,i) = iheader(3) ntoti = sum(npartoftype(1:ntypes,i)) ! !--determine whether dump is single or double precision ! singleprecision = .true. if (all(abs(rheader(1:isizeheader)).lt.tiny(rheader))) then singleprecision = .false. print "(1x,a)",'double precision dump' time(i) = real(dheader(1)) print*,'particles per smoothing length = ',dheader(2) print*,'kernel range = ',dheader(3) gamma(i) = real(dheader(4)) else print "(1x,a)",'single precision dump' time(i) = rheader(1) print*,'particles per smoothing length = ',rheader(2) print*,'kernel range = ',rheader(3) gamma(i) = rheader(4) endif hfact = 0. print "(/a14,':',f8.4,a8,':',i8,a8,':',i8)",' time',time(i),'npart',npartoftype(1,i),'ntotal',ntoti print "(a14,':',i8,a8,':',f8.4,a8,':',f8.4)",' ncolumns',ncolumns,'gamma',gamma(i),'hfact',hfact print "(a14,':',i8,a8,':',i8)",'ndim',ndim,'ndimV',ndimV ! !--read data arrays ! if (singleprecision) then if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(ndim,ntoti)) ! !--read positions of all particles ! read(iunit,iostat=ierr) dattemp(1:ndim,1:ntoti) if (ierr /=0 ) then print "(a)",'error reading particle positions' else do icol=1,ndim dat(1:ntoti,icol,i) = dattemp(icol,1:ntoti) enddo endif ! !--read rest of data columns ! do icol=ndim+1,ncolumns read(iunit,iostat=ierr) dat(1:ntoti,icol,i) if (ierr /= 0) print "(a,i2)", 'error reading column ',icol enddo else if (allocated(dattempd)) deallocate(dattempd) allocate(dattempd(ndim,ntoti)) ! !--read positions of all particles ! read(iunit,iostat=ierr) dattempd(1:ndim,1:ntoti) if (ierr /=0 ) then print "(a)",'error reading particle positions' else do icol=1,ndim dat(1:ntoti,icol,i) = real(dattempd(icol,1:ntoti)) enddo endif ! !--read rest of data columns ! do icol=ndim+1,ncolumns read(iunit,iostat=ierr) dattempd(1,1:ntoti) if (ierr /= 0) print "(a,i2)", 'error reading column ',icol !--convert to single precision dat(1:ntoti,icol,i) = real(dattempd(1,1:ntoti)) enddo endif if (allocated(dattemp)) deallocate(dattemp) if (allocated(dattempd)) deallocate(dattempd) ! !--close data file and return ! close(unit=iunit) print "(a)",' finished data read ' return 80 continue print*,' *** data file empty : no timesteps ***' return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:ix,ivx,ih,irho,iutherm,ipmass,ipr,iBfirst, & iamvec,labelvec,label,labeltype use params use settings_data, only:ndim,ndimV,ncolumns,ntypes, & UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i,j character(len=10), dimension(maxplot) :: cheader common /chead/ cheader if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo do i=ndim+1,ncolumns label(i) = cheader(i-ndim) !--blank characters in c are ascii zero - correct these to spaces do j=1,len(label(i)) if (iachar(label(i)(j:j)).eq.0) label(i)(j:j) = ' ' enddo !--set positions of various quantities depending on labels if (label(i)(1:1)=='m' .or. label(i)(1:4)=='mass') then ipmass = i elseif (label(i)(1:3)=='rho' .or. label(i)(1:4)=='dens') then irho = i elseif (label(i)(1:1)=='h' .or. label(i)(1:6)=='smooth') then ih = i elseif (label(i)(1:2)=='u ' .or. label(i)(1:1)=='e') then iutherm = i elseif (label(i)(1:2)=='pr' .or. trim(label(i))=='P') then ipr = i elseif (label(i)(1:1)=='v') then if (ivx.eq.0 .or. i.lt.ivx) ivx = i elseif (label(i)(1:1)=='B') then if (iBfirst.eq.0 .or. i.lt.iBfirst) iBfirst = i endif enddo label(ix(1:ndim)) = labelcoord(1:ndim,1) ! !--label vector quantities (e.g. velocity) appropriately ! if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx+i-1))//'\d'//labelcoord(i,1) enddo endif if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = iBfirst labelvec(iBfirst:iBfirst+ndimV-1) = 'B' endif ! !--set labels for each type of particles ! labeltype(1) = 'gas' UseTypeInRenderings(1) = .true. if (ntypes.ge.2) then labeltype(2) = 'auxiliary' UseTypeInRenderings(2) = .true. endif if (ntypes.ge.3) then labeltype(3) = 'mirror' UseTypeInRenderings(3) = .true. endif !----------------------------------------------------------- return end subroutine set_labels splash/src/exact_densityprofiles.f90000644 000766 000000 00000006644 13261626263 020514 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! Plots various analytic density profiles ! ! Currently implemented: ! ! 1) Plummer sphere ! 2) Hernquist sphere ! ! Added by D. Price 5/9/05 ! ---------------------------------------------------------------------- module densityprofiles implicit none contains subroutine exact_densityprofiles(iplot,iprofile,Msphere,rsoft,xplot,yplot,ierr) implicit none real, parameter :: pi = 3.1415926536 integer, intent(in) :: iplot,iprofile real, intent(in), dimension(2) :: Msphere,rsoft real, intent(in), dimension(:) :: xplot real, intent(out), dimension(size(xplot)) :: yplot integer, intent(out) :: ierr integer :: i ! ! check for errors ! ierr = 0 if (all(Msphere.le.0.)) then print*,'error: mass <= 0 in exact_densityprofile' ierr = 2 return endif if (any(rsoft.lt.0.)) then print*,'error: rsoft < 0 in exact_densityprofile' ierr = 3 return endif select case(iprofile) case(1) ! !--Plummer sphere ! select case(iplot) case(2) !--potential do i=1,size(xplot) yplot(i) = -Msphere(1)/sqrt(rsoft(1)**2 + xplot(i)**2) & -Msphere(2)/sqrt(rsoft(2)**2 + xplot(i)**2) enddo !--force case(3) do i=1,size(xplot) yplot(i) = Msphere(1)*xplot(i)/((rsoft(1)**2 + xplot(i)**2)**1.5) & + Msphere(2)*xplot(i)/((rsoft(2)**2 + xplot(i)**2)**1.5) enddo !--density case default do i=1,size(xplot) yplot(i) = 3.*Msphere(1)*rsoft(1)**2/(4.*pi*(rsoft(1)**2 + xplot(i)**2)**2.5) & + 3.*Msphere(2)*rsoft(2)**2/(4.*pi*(rsoft(2)**2 + xplot(i)**2)**2.5) enddo end select case(2) ! !--Hernquist model (use tiny to prevent divergences in cusp) ! select case(iplot) case(2) !--potential do i=1,size(xplot) yplot(i) = -Msphere(1)/(rsoft(1) + xplot(i)) & -Msphere(2)/(rsoft(2) + xplot(i)) enddo !--force case(3) do i=1,size(xplot) yplot(i) = Msphere(1)/(rsoft(1) + xplot(i))**2 & + Msphere(2)/(rsoft(2) + xplot(i))**2 enddo !--density case default do i=1,size(xplot) yplot(i) = Msphere(1)*rsoft(1)/ & ((2.*pi*xplot(i)*(rsoft(1) + xplot(i))**3)) & + Msphere(2)*rsoft(2)/ & ((2.*pi*xplot(i)*(rsoft(2) + xplot(i))**3)) enddo end select case default ierr = 1 end select return end subroutine exact_densityprofiles end module densityprofiles splash/src/interpolate3D.F90000644 000766 000000 00000046404 13261626263 016560 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! ! Module containing all of the routines required for interpolation ! from 3D data to a 3D grid (SLOW!) ! !---------------------------------------------------------------------- module interpolations3D use kernels, only:radkernel2,radkernel,cnormk3D implicit none public :: interpolate3D,interpolate3D_vec contains !-------------------------------------------------------------------------- ! subroutine to interpolate from particle data to even grid of pixels ! ! The data is interpolated according to the formula ! ! datsmooth(pixel) = sum_b weight_b dat_b W(r-r_b, h_b) ! ! where _b is the quantity at the neighbouring particle b and ! W is the smoothing kernel, for which we use the usual cubic spline. ! ! For a standard SPH smoothing the weight function for each particle should be ! ! weight = pmass/(rho*h^3) ! ! this version is written for slices through a rectangular volume, ie. ! assumes a uniform pixel size in x,y, whilst the number of pixels ! in the z direction can be set to the number of cross-section slices. ! ! Input: particle coordinates : x,y,z (npart) ! smoothing lengths : hh (npart) ! weight for each particle : weight (npart) ! scalar data to smooth : dat (npart) ! ! Output: smoothed data : datsmooth (npixx,npixy,npixz) ! ! Daniel Price, Institute of Astronomy, Cambridge 16/7/03 ! Revised for "splash to grid", Monash University 02/11/09 !-------------------------------------------------------------------------- subroutine interpolate3D(x,y,z,hh,weight,dat,itype,npart,& xmin,ymin,zmin,datsmooth,npixx,npixy,npixz,pixwidth,zpixwidth,& normalise,periodicx,periodicy,periodicz) implicit none integer, intent(in) :: npart,npixx,npixy,npixz real, intent(in), dimension(npart) :: x,y,z,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,zmin,pixwidth,zpixwidth real, intent(out), dimension(npixx,npixy,npixz) :: datsmooth logical, intent(in) :: normalise,periodicx,periodicy,periodicz real, dimension(npixx,npixy,npixz) :: datnorm integer :: i,ipix,jpix,kpix integer :: iprintinterval,iprintnext integer :: ipixmin,ipixmax,jpixmin,jpixmax,kpixmin,kpixmax integer :: ipixi,jpixi,kpixi,nxpix,nwarn real :: xminpix,yminpix,zminpix,hmin !,dhmin3 real, dimension(npixx) :: dx2i real :: xi,yi,zi,hi,hi1,hi21,radkern,wab,q2,const,dyz2,dz2 real :: term,termnorm,dy,dz,ypix,zpix,xpixi !real :: t_start,t_end logical :: iprintprogress #ifdef _OPENMP integer :: omp_get_num_threads #else integer(kind=selected_int_kind(10)) :: iprogress ! up to 10 digits #endif datsmooth = 0. datnorm = 0. if (normalise) then print "(1x,a)",'interpolating from particles to 3D grid (normalised) ...' else print "(1x,a)",'interpolating from particles to 3D grid (non-normalised) ...' endif if (pixwidth.le.0.) then print "(1x,a)",'interpolate3D: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate3D: WARNING: ignoring some or all particles with h < 0' endif ! !--print a progress report if it is going to take a long time ! (a "long time" is, however, somewhat system dependent) ! iprintprogress = (npart .ge. 100000) .or. (npixx*npixy .gt.100000) ! !--loop over particles ! iprintinterval = 25 if (npart.ge.1e6) iprintinterval = 10 iprintnext = iprintinterval ! !--get starting CPU time ! !call cpu_time(t_start) xminpix = xmin - 0.5*pixwidth yminpix = ymin - 0.5*pixwidth zminpix = zmin - 0.5*zpixwidth ! !--use a minimum smoothing length on the grid to make ! sure that particles contribute to at least one pixel ! hmin = 0.5*pixwidth !dhmin3 = 1./(hmin*hmin*hmin) const = cnormk3D ! normalisation constant (3D) nwarn = 0 ! !--loop over particles ! !$omp parallel default(none) & !$omp shared(hh,z,x,y,weight,dat,itype,datsmooth,npart) & !$omp shared(xmin,ymin,zmin,radkernel,radkernel2) & !$omp shared(xminpix,yminpix,zminpix,pixwidth,zpixwidth) & !$omp shared(npixx,npixy,npixz,const) & !$omp shared(datnorm,normalise,periodicx,periodicy,periodicz) & !$omp shared(hmin) & !,dhmin3) & !$omp private(hi,xi,yi,zi,radkern,hi1,hi21) & !$omp private(term,termnorm,xpixi) & !$omp private(ipixmin,ipixmax,jpixmin,jpixmax,kpixmin,kpixmax) & !$omp private(ipix,jpix,kpix,ipixi,jpixi,kpixi) & !$omp private(dx2i,nxpix,zpix,dz,dz2,dyz2,dy,ypix,q2,wab) & !$omp reduction(+:nwarn) !$omp master #ifdef _OPENMP print "(1x,a,i3,a)",'Using ',omp_get_num_threads(),' cpus' #endif !$omp end master !$omp do schedule (guided, 2) over_parts: do i=1,npart ! !--report on progress ! #ifndef _OPENMP if (iprintprogress) then iprogress = 100*i/npart if (iprogress.ge.iprintnext) then write(*,"('(',i3,'% -',i12,' particles done)')") iprogress,i iprintnext = iprintnext + iprintinterval endif endif #endif ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts hi = hh(i) if (hi.le.0.) then cycle over_parts elseif (hi.lt.hmin) then ! !--use minimum h to capture subgrid particles ! (get better results *without* adjusting weights) ! termnorm = const*weight(i) !*(hi*hi*hi)*dhmin3 hi = hmin else termnorm = const*weight(i) endif ! !--set kernel related quantities ! xi = x(i) yi = y(i) zi = z(i) hi1 = 1./hi hi21 = hi1*hi1 radkern = radkernel*hi ! radius of the smoothing kernel !termnorm = const*weight(i) term = termnorm*dat(i) ! !--for each particle work out which pixels it contributes to ! ipixmin = int((xi - radkern - xmin)/pixwidth) jpixmin = int((yi - radkern - ymin)/pixwidth) kpixmin = int((zi - radkern - zmin)/zpixwidth) ipixmax = int((xi + radkern - xmin)/pixwidth) + 1 jpixmax = int((yi + radkern - ymin)/pixwidth) + 1 kpixmax = int((zi + radkern - zmin)/zpixwidth) + 1 if (.not.periodicx) then if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (ipixmax.gt.npixx) ipixmax = npixx ! to pixels in the image endif if (.not.periodicy) then if (jpixmin.lt.1) jpixmin = 1 if (jpixmax.gt.npixy) jpixmax = npixy endif if (.not.periodicz) then if (kpixmin.lt.1) kpixmin = 1 if (kpixmax.gt.npixz) kpixmax = npixz endif ! !--precalculate an array of dx2 for this particle (optimisation) ! nxpix = 0 do ipix=ipixmin,ipixmax nxpix = nxpix + 1 ipixi = ipix if (periodicx) then if (ipixi.lt.1) ipixi = mod(ipixi,npixx) + npixx if (ipixi.gt.npixx) ipixi = mod(ipixi-1,npixx) + 1 endif xpixi = xminpix + ipix*pixwidth !--watch out for errors with perioic wrapping... if (nxpix.le.size(dx2i)) then dx2i(nxpix) = ((xpixi - xi)**2)*hi21 endif enddo !--if particle contributes to more than npixx pixels ! (i.e. periodic boundaries wrap more than once) ! truncate the contribution and give warning if (nxpix.gt.npixx) then nwarn = nwarn + 1 ipixmax = ipixmin + npixx - 1 endif ! !--loop over pixels, adding the contribution from this particle ! do kpix = kpixmin,kpixmax kpixi = kpix if (periodicz) then if (kpixi.lt.1) kpixi = mod(kpixi,npixz) + npixz if (kpixi.gt.npixz) kpixi = mod(kpixi-1,npixz) + 1 endif zpix = zminpix + kpix*zpixwidth dz = zpix - zi dz2 = dz*dz*hi21 do jpix = jpixmin,jpixmax jpixi = jpix if (periodicy) then if (jpixi.lt.1) jpixi = mod(jpixi,npixy) + npixy if (jpixi.gt.npixy) jpixi = mod(jpixi-1,npixy) + 1 endif ypix = yminpix + jpix*pixwidth dy = ypix - yi dyz2 = dy*dy*hi21 + dz2 nxpix = 0 do ipix = ipixmin,ipixmax nxpix = nxpix + 1 ipixi = ipix if (periodicx) then if (ipixi.lt.1) ipixi = mod(ipixi,npixx) + npixx if (ipixi.gt.npixx) ipixi = mod(ipixi-1,npixx) + 1 endif q2 = dx2i(nxpix) + dyz2 ! dx2 pre-calculated; dy2 pre-multiplied by hi21 ! !--SPH kernel - standard cubic spline ! if (q2.lt.radkernel2) then wab = wkernel(q2) ! !--calculate data value at this pixel using the summation interpolant ! !$omp atomic datsmooth(ipixi,jpixi,kpixi) = datsmooth(ipixi,jpixi,kpixi) + term*wab if (normalise) then !$omp atomic datnorm(ipixi,jpixi,kpixi) = datnorm(ipixi,jpixi,kpixi) + termnorm*wab endif endif enddo enddo enddo enddo over_parts !$omp end do !$omp end parallel if (nwarn.gt.0) then print "(a,i11,a,/,a)",' interpolate3D: WARNING: contributions truncated from ',nwarn,' particles',& ' that wrap periodic boundaries more than once' endif ! !--normalise dat array ! if (normalise) then where (datnorm > tiny(datnorm)) datsmooth = datsmooth/datnorm end where endif return end subroutine interpolate3D subroutine interpolate3D_vec(x,y,z,hh,weight,datvec,itype,npart,& xmin,ymin,zmin,datsmooth,npixx,npixy,npixz,pixwidth,zpixwidth,& normalise,periodicx,periodicy,periodicz) implicit none integer, intent(in) :: npart,npixx,npixy,npixz real, intent(in), dimension(npart) :: x,y,z,hh,weight real, intent(in), dimension(npart,3) :: datvec integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,zmin,pixwidth,zpixwidth real, intent(out), dimension(3,npixx,npixy,npixz) :: datsmooth logical, intent(in) :: normalise,periodicx,periodicy,periodicz real, dimension(npixx,npixy,npixz) :: datnorm integer :: i,ipix,jpix,kpix integer :: iprintinterval,iprintnext integer :: ipixmin,ipixmax,jpixmin,jpixmax,kpixmin,kpixmax integer :: ipixi,jpixi,kpixi,nxpix,nwarn real :: xminpix,yminpix,zminpix real, dimension(npixx) :: dx2i real :: xi,yi,zi,hi,hi1,hi21,radkern,wab,q2,const,dyz2,dz2 real :: termnorm,dy,dz,ypix,zpix,xpixi,ddatnorm real, dimension(3) :: term !real :: t_start,t_end logical :: iprintprogress #ifdef _OPENMP integer :: omp_get_num_threads #else integer(kind=selected_int_kind(10)) :: iprogress ! up to 10 digits #endif datsmooth = 0. datnorm = 0. if (normalise) then print "(1x,a)",'interpolating from particles to 3D grid (normalised) ...' else print "(1x,a)",'interpolating from particles to 3D grid (non-normalised) ...' endif if (pixwidth.le.0.) then print "(1x,a)",'interpolate3D: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate3D: WARNING: ignoring some or all particles with h < 0' endif ! !--print a progress report if it is going to take a long time ! (a "long time" is, however, somewhat system dependent) ! iprintprogress = (npart .ge. 100000) .or. (npixx*npixy .gt.100000) ! !--loop over particles ! iprintinterval = 25 if (npart.ge.1e6) iprintinterval = 10 iprintnext = iprintinterval ! !--get starting CPU time ! !call cpu_time(t_start) xminpix = xmin - 0.5*pixwidth yminpix = ymin - 0.5*pixwidth zminpix = zmin - 0.5*zpixwidth ! xmax = xmin + npixx*pixwidth ! ymax = ymin + npixy*pixwidth const = cnormk3D ! normalisation constant (3D) nwarn = 0 !$omp parallel default(none) & !$omp shared(hh,z,x,y,weight,datvec,itype,datsmooth,npart) & !$omp shared(xmin,ymin,zmin,radkernel,radkernel2) & !$omp shared(xminpix,yminpix,zminpix,pixwidth,zpixwidth) & !$omp shared(npixx,npixy,npixz,const) & !$omp shared(datnorm,normalise,periodicx,periodicy,periodicz) & !$omp private(hi,xi,yi,zi,radkern,hi1,hi21) & !$omp private(term,termnorm,xpixi) & !$omp private(ipixmin,ipixmax,jpixmin,jpixmax,kpixmin,kpixmax) & !$omp private(ipix,jpix,kpix,ipixi,jpixi,kpixi) & !$omp private(dx2i,nxpix,zpix,dz,dz2,dyz2,dy,ypix,q2,wab) & !$omp reduction(+:nwarn) !$omp master #ifdef _OPENMP print "(1x,a,i3,a)",'Using ',omp_get_num_threads(),' cpus' #endif !$omp end master ! !--loop over particles ! !$omp do schedule (guided, 2) over_parts: do i=1,npart ! !--report on progress ! #ifndef _OPENMP if (iprintprogress) then iprogress = 100*i/npart if (iprogress.ge.iprintnext) then write(*,"('(',i3,'% -',i12,' particles done)')") iprogress,i iprintnext = iprintnext + iprintinterval endif endif #endif ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts hi = hh(i) if (hi.le.0.) cycle over_parts ! !--set kernel related quantities ! xi = x(i) yi = y(i) zi = z(i) hi1 = 1./hi hi21 = hi1*hi1 radkern = radkernel*hi ! radius of the smoothing kernel termnorm = const*weight(i) term(:) = termnorm*datvec(i,:) ! !--for each particle work out which pixels it contributes to ! ipixmin = int((xi - radkern - xmin)/pixwidth) jpixmin = int((yi - radkern - ymin)/pixwidth) kpixmin = int((zi - radkern - zmin)/zpixwidth) ipixmax = int((xi + radkern - xmin)/pixwidth) + 1 jpixmax = int((yi + radkern - ymin)/pixwidth) + 1 kpixmax = int((zi + radkern - zmin)/zpixwidth) + 1 if (.not.periodicx) then if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (ipixmax.gt.npixx) ipixmax = npixx ! to pixels in the image endif if (.not.periodicy) then if (jpixmin.lt.1) jpixmin = 1 if (jpixmax.gt.npixy) jpixmax = npixy endif if (.not.periodicz) then if (kpixmin.lt.1) kpixmin = 1 if (kpixmax.gt.npixz) kpixmax = npixz endif ! !--precalculate an array of dx2 for this particle (optimisation) ! nxpix = 0 do ipix=ipixmin,ipixmax nxpix = nxpix + 1 ipixi = ipix if (periodicx) then if (ipixi.lt.1) ipixi = mod(ipixi,npixx) + npixx if (ipixi.gt.npixx) ipixi = mod(ipixi-1,npixx) + 1 endif xpixi = xminpix + ipix*pixwidth !--watch out for errors with perioic wrapping... if (nxpix.le.size(dx2i)) then dx2i(nxpix) = ((xpixi - xi)**2)*hi21 endif enddo !--if particle contributes to more than npixx pixels ! (i.e. periodic boundaries wrap more than once) ! truncate the contribution and give warning if (nxpix.gt.npixx) then nwarn = nwarn + 1 ipixmax = ipixmin + npixx - 1 endif ! !--loop over pixels, adding the contribution from this particle ! do kpix = kpixmin,kpixmax kpixi = kpix if (periodicz) then if (kpixi.lt.1) kpixi = mod(kpixi,npixz) + npixz if (kpixi.gt.npixz) kpixi = mod(kpixi-1,npixz) + 1 endif zpix = zminpix + kpix*zpixwidth dz = zpix - zi dz2 = dz*dz*hi21 do jpix = jpixmin,jpixmax jpixi = jpix if (periodicy) then if (jpixi.lt.1) jpixi = mod(jpixi,npixy) + npixy if (jpixi.gt.npixy) jpixi = mod(jpixi-1,npixy) + 1 endif ypix = yminpix + jpix*pixwidth dy = ypix - yi dyz2 = dy*dy*hi21 + dz2 nxpix = 0 do ipix = ipixmin,ipixmax ipixi = ipix if (periodicx) then if (ipixi.lt.1) ipixi = mod(ipixi,npixx) + npixx if (ipixi.gt.npixx) ipixi = mod(ipixi-1,npixx) + 1 endif nxpix = nxpix + 1 q2 = dx2i(nxpix) + dyz2 ! dx2 pre-calculated; dy2 pre-multiplied by hi21 ! !--SPH kernel - standard cubic spline ! if (q2.lt.radkernel2) then wab = wkernel(q2) ! !--calculate data value at this pixel using the summation interpolant ! !$omp atomic datsmooth(1,ipixi,jpixi,kpixi) = datsmooth(1,ipixi,jpixi,kpixi) + term(1)*wab !$omp atomic datsmooth(2,ipixi,jpixi,kpixi) = datsmooth(2,ipixi,jpixi,kpixi) + term(2)*wab !$omp atomic datsmooth(3,ipixi,jpixi,kpixi) = datsmooth(3,ipixi,jpixi,kpixi) + term(3)*wab if (normalise) then !$omp atomic datnorm(ipixi,jpixi,kpixi) = datnorm(ipixi,jpixi,kpixi) + termnorm*wab endif endif enddo enddo enddo enddo over_parts !$omp end do !$omp end parallel if (nwarn.gt.0) then print "(a,i11,a,/,a)",' interpolate3D: WARNING: contributions truncated from ',nwarn,' particles',& ' that wrap periodic boundaries more than once' endif ! !--normalise dat array ! if (normalise) then !$omp parallel do default(none) schedule(static) & !$omp shared(datsmooth,datnorm,npixz,npixy,npixx) & !$omp private(kpix,jpix,ipix,ddatnorm) do kpix=1,npixz do jpix=1,npixy do ipix=1,npixx if (datnorm(ipix,jpix,kpix).gt.tiny(datnorm)) then ddatnorm = 1./datnorm(ipix,jpix,kpix) datsmooth(1,ipix,jpix,kpix) = datsmooth(1,ipix,jpix,kpix)*ddatnorm datsmooth(2,ipix,jpix,kpix) = datsmooth(2,ipix,jpix,kpix)*ddatnorm datsmooth(3,ipix,jpix,kpix) = datsmooth(3,ipix,jpix,kpix)*ddatnorm endif enddo enddo enddo !$omp end parallel do endif return end subroutine interpolate3D_vec !------------------------------------------------------------ ! interface to kernel routine to avoid problems with openMP !----------------------------------------------------------- real function wkernel(q2) use kernels, only:wfunc implicit none real, intent(in) :: q2 wkernel = wfunc(q2) end function wkernel end module interpolations3D splash/src/options_vecplot.f90000644 000766 000000 00000016645 13261626263 017336 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing settings and options relating to vector plots ! includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module settings_vecplot implicit none integer :: npixvec,minpartforarrow,iVecLegendOnPanel logical :: UseBackgndColorVecplot, iplotpartvec logical :: iVecplotLegend,iplotstreamlines,iplotarrowheads logical :: iplotsynchrotron,ihidearrowswherenoparts,iallarrowssamelength real :: hposlegendvec,vposlegendvec real :: rcrit,zcrit,synchrotronspecindex,uthermcutoff namelist /vectoropts/ npixvec, UseBackgndColorVecplot,iplotpartvec,& iVecplotLegend,hposlegendvec,vposlegendvec,iplotstreamlines, & iplotarrowheads,iplotsynchrotron,rcrit,zcrit,synchrotronspecindex, & uthermcutoff,ihidearrowswherenoparts,minpartforarrow,iallarrowssamelength,& iVecLegendOnPanel contains !--------------------------------------------- ! set default values for these options !--------------------------------------------- subroutine defaults_set_vecplot implicit none npixvec = 40 ! pixels in x direction on vector plots UseBackgndColorVecplot = .false. ! plot vector plot using black/white iplotpartvec = .true. ! whether to plot particles on vector plot iVecplotLegend = .true. iVecLegendOnPanel = 0 ! all panels hposlegendvec = 0.02 vposlegendvec = -1.5 iplotstreamlines = .false. ! plot stream lines instead of arrows iplotarrowheads = .true. iplotsynchrotron = .false. zcrit = 2.5 ! kpc rcrit = 13. ! kpc synchrotronspecindex = 0.8 uthermcutoff = -1. ! flags this as uninitialised ihidearrowswherenoparts = .false. minpartforarrow = 1 iallarrowssamelength = .false. return end subroutine defaults_set_vecplot !---------------------------------------------------------------------- ! sets options relating to vector plots !---------------------------------------------------------------------- subroutine submenu_vecplot(ichoose) use prompting, only:prompt,print_logical use settings_data, only:ndim,numplot use labels, only:iutherm use limits, only:lim use legends, only:prompt_panelselect implicit none integer, intent(in) :: ichoose integer :: ians ians = ichoose print "(a)",'--------------- vector plot options -------------------' if (ians.le.0 .or. ians.gt.7) then print 10,npixvec,print_logical(UseBackgndColorVecplot), & print_logical(iVecplotLegend),print_logical(iplotstreamlines), & print_logical(iplotarrowheads), & print_logical(ihidearrowswherenoparts), & print_logical(iallarrowssamelength) 10 format( & ' 0) exit ',/, & ' 1) change number of pixels (',i4,' )',/, & ' 2) use background colour for arrows ( ',a,' )',/, & ' 3) vector plot legend settings ( ',a,' )',/, & ' 4) plot stream/field lines instead of arrows ( ',a,' )',/, & ' 5) turn arrow heads on/off ( ',a,' )',/, & ' 6) hide arrows where there are no particles ( ',a,' )',/, & ' 7) all arrows same length - ie. direction only ( ',a,' )') call prompt('enter option',ians,0,7) endif ! !--options ! select case(ians) !------------------------------------------------------------------------ case(1) call prompt('enter number of pixels',npixvec,1,1000) !------------------------------------------------------------------------ case(2) UseBackgndColorVecplot = .not.UseBackgndColorVecplot print*,'use background colour on vector plots = ', & print_logical(UseBackgndColorVecplot) !------------------------------------------------------------------------ case(3) call prompt('plot vector legend?',iVecplotLegend) if (iVecplotLegend) then print*,'note: H key in interactive mode can also be used to set positions' call prompt('Enter horizontal position as fraction of viewport', & hposlegendvec,0.0,1.0) call prompt('Enter vertical position in character heights from top', & vposlegendvec) call prompt_panelselect('vector legend',iVecLegendOnPanel) endif !------------------------------------------------------------------------ case(4) iplotstreamlines = .not.iplotstreamlines print "(2(a,/))",' Note: the number of stream lines plotted is determined by', & ' the "change number of contours" option in the r)ender menu' call prompt('use stream lines instead of arrows? ',iplotstreamlines) !------------------------------------------------------------------------ case(5) iplotarrowheads = .not.iplotarrowheads call prompt('plot arrow heads? ',iplotarrowheads) if (ndim.eq.3 .and. .not.iplotarrowheads) then call prompt(' plot synchrotron map? ',iplotsynchrotron) if (iplotsynchrotron) then if (iutherm.lt.0 .or. iutherm.gt.numplot) then print "(a)",' Warning: cannot use thermal energy cutoff in synchrotron plots' print "(a)",' (could not locate thermal energy in data columns)' endif call prompt(' enter rcrit for cosmic ray electron distribution exp(-r/rcrit -z/zcrit)',rcrit,0.) call prompt(' enter zcrit for cosmic ray electron distribution exp(-r/rcrit -z/zcrit)',zcrit,0.) call prompt(' enter synchrotron spectral index I_nu = nu^-alpha ',synchrotronspecindex,0.) if (iutherm.gt.0 .and. iutherm.le.numplot) then !--set sensible default value for uthermcutoff if (uthermcutoff.lt.-tiny(uthermcutoff)) then uthermcutoff = 0.5*(lim(iutherm,1) + lim(iutherm,2)) endif call prompt(' enter threshold thermal energy in current units (u < utherm not used) ',uthermcutoff,0.) endif endif endif !------------------------------------------------------------------------ case(6) call prompt('hide vector arrows where there are no particles ? ',ihidearrowswherenoparts) if (ihidearrowswherenoparts) then call prompt(' enter minimum number of particles in pixel cell for arrow to be plotted ',minpartforarrow,1) endif !------------------------------------------------------------------------ case(7) iallarrowssamelength = .not.iallarrowssamelength call prompt('make all arrows same length (ie. only show direction, not magnitude) ?',iallarrowssamelength) end select return end subroutine submenu_vecplot end module settings_vecplot splash/src/read_data_bauswein.f90000644 000766 000000 00000017537 13261626263 017711 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM ANDREAS BAUSWEIN'S CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! BSPLASH_R8 if 'YES' or 'TRUE' then assumes data is double precision ! BSPLASH_NCOL to change the number of columns read from the file, ! e.g. setenv BSPLASH_NCOL=22 ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,time,npartoftype,gamma,maxpart,maxcol use params use settings_data, only:ndim,ndimV,ncolumns use mem_allocation, only:alloc use system_utils, only:lenvironment,ienvironment implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,k,ierr integer :: nprint,n1,npart_max,nstep_max integer :: ncol,nread,nerr,ncoltemp logical :: iexist,doubleprec character(len=len(rootname)) :: dumpfile real :: timei,dti real(doub_prec), dimension(maxcol) :: datdb real(doub_prec) :: timedb,dtdb nstepsread = 0 nstep_max = 0 npart_max = maxpart dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 !--number of columns to read from file ncol = 21 doubleprec = .false. !--can override these settings with environment variables if (lenvironment('BSPLASH_R8')) doubleprec = .true. ncoltemp = ienvironment('BSPLASH_NCOL') if (ncoltemp.gt.0) ncol = ncoltemp ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 print "(1x,a)",'reading Andreas Bauswein format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,file=dumpfile,status='old',form='unformatted',iostat=ierr) if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return else ! !--read the number of particles in the header and allocate memory ! if (doubleprec) then read(15,end=55,iostat=ierr) nprint,n1,timedb,dtdb timei = real(timedb) dti = real(dtdb) else read(15,end=55,iostat=ierr) nprint,n1,timei,dti endif print "(a,f10.2,a,i10,a,i10,a,f10.4)",' time: ',timei,' npart: ',nprint,' n1: ',n1,' dt = ',dti !--barf if stupid values read if (nprint.le.0 .or. nprint.gt.1e10) then print "(a)",' *** ERRORS IN TIMESTEP HEADER: WRONG ENDIAN? ***' close(15) return elseif (ierr /= 0) then print "(a)",'*** WARNING: ERRORS READING HEADER ***' endif if (timei.lt.0. .or. dti.lt.0.) print "(a)",'*** ERROR: t < 0: use setenv BSPLASH_R8=TRUE for double precision' ncolumns = ncol if (.not.allocated(dat) .or. nprint.gt.npart_max) then npart_max = max(npart_max,nprint) call alloc(npart_max,nstep_max,ncolumns) endif ! !--now read the timestep data in the dumpfile ! dat(:,:,j) = 0. time(j) = timei if (doubleprec) then nread = 0 nerr = 0 do i=1,nprint nread = nread + 1 read(15,end=44,iostat=ierr) (datdb(k),k=1,ncol) if (ierr /= 0) nerr = nerr + 1 dat(i,4,j) = real(datdb(1)) dat(i,1:3,j) = real(datdb(2:4)) dat(i,5:ncol,j) = real(datdb(5:ncol)) enddo else nread = 0 nerr = 0 do i=1,nprint nread = nread + 1 read(15,end=44,iostat=ierr) dat(i,4,j),dat(i,1:3,j),(dat(i,k,j),k=5,ncol) if (ierr /= 0) nerr = nerr + 1 enddo endif 44 continue if (nerr.gt.0) print *,'*** WARNING: ERRORS DURING READ ON ',nerr,' LINES' if (nread.lt.nprint) then print "(a)",' WARNING: END OF FILE: read to particle ',nread nprint = nread endif nstepsread = nstepsread + 1 npartoftype(1,j) = nprint gamma(j) = 1.666666666667 j = j + 1 endif 55 continue ! !--reached end of file during header read ! close(15) if (allocated(npartoftype)) then print*,'>> end of dump file: nsteps =',j-1,'ntot = ',sum(npartoftype(:,j-1)) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,labelvec,labeltype,iamvec,& ix,ivx,ih,irho,iutherm,ipmass use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord !use settings_units, only:units,unitslabel implicit none integer :: i,ipmom if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ih = 4 ivx = 5 ipmom = 8 iutherm = 11 ipmass = 14 irho = 17 label(ix(1:ndim)) = labelcoord(1:ndim,1) label(ih) = 'h' if (iutherm.gt.0) label(iutherm) = 'u' label(12) = 'psi\dpot\u' label(13) = 'alpha\dpot\u' label(ipmass) = 'particle mass' label(15) = '\gr' label(16) = 'P\deff' label(irho) = '\gr*' label(18) = 'tau' label(19) = '\ga\dvisc' label(20) = 'Ye' label(21) = 'temperature' if (ivx.ne.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = labelvec(ivx)//'\d'//labelcoord(i,1) enddo endif if (ipmom.ne.0) then iamvec(ipmom:ipmom+ndimV-1) = ipmom labelvec(ipmom:ipmom+ndimV-1) = 'momentum' do i=1,ndimV label(ipmom+i-1) = labelvec(ipmom)//'\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 1 labeltype(1) = 'gas' UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/adjust_data.f90000644 000766 000000 00000037520 13261626263 016365 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module adjustdata implicit none contains !---------------------------------------------------- ! ! amend data after the data read based on ! various environment variable settings ! ! must be called AFTER the data has been read ! but BEFORE rescaling to physical units is applied ! !---------------------------------------------------- subroutine adjust_data_codeunits use system_utils, only:renvironment,envlist,ienvironment,lenvironment use labels, only:ih,ix,ivx,label,get_sink_type,ipmass,idustfrac,irho use settings_data, only:ncolumns,ndimV,icoords,ndim,debugmode,ntypes,iverbose,fakedust use particle_data, only:dat,npartoftype,iamtype use geometry, only:labelcoord use filenames, only:ifileopen,nstepsinfile use part_utils, only:locate_first_two_of_type,locate_nth_particle_of_type,get_binary implicit none real :: hmin,dphi real, dimension(3) :: vsink,xyzsink,x0,v0 character(len=20), dimension(3) :: list integer :: i,j,nlist,nerr,ierr,isink,isinkpos,itype integer :: isink1,isink2,ntot logical :: centreonsink ! !--environment variable setting to enforce a minimum h ! if (ih.gt.0 .and. ih.le.ncolumns) then hmin = renvironment('SPLASH_HMIN_CODEUNITS',errval=-1.) if (hmin.gt.0.) then if (.not.allocated(dat)) then print*,' INTERNAL ERROR: dat not allocated in adjust_data_codeunits' return endif print "(/,a,es10.3)",' >> SETTING MINIMUM H TO ',hmin where (dat(:,ih,:) < hmin .and. dat(:,ih,:).gt.0.) dat(:,ih,:) = hmin end where endif endif ! !--environment variable setting to subtract a mean velocity ! if (ivx.gt.0 .and. ivx+ndimV-1.le.ncolumns) then call envlist('SPLASH_VZERO_CODEUNITS',nlist,list) nerr = 0 if (nlist.gt.0 .and. nlist.lt.ndimV) then print "(/,2(a,i1))",' >> ERROR in SPLASH_VZERO_CODEUNITS setting: number of components = ',nlist,', needs to be ',ndimV nerr = 1 elseif (nlist.gt.0) then if (nlist.gt.ndimV) print "(a,i1,a,i1)",' >> WARNING! SPLASH_VZERO_CODEUNITS setting has ',nlist, & ' components: using only first ',ndimV nerr = 0 do i=1,ndimV read(list(i),*,iostat=ierr) v0(i) if (ierr.ne.0) then print "(a)",' >> ERROR reading v'//trim(labelcoord(i,icoords))//& ' component from SPLASH_VZERO_CODEUNITS setting' nerr = ierr endif enddo if (nerr.eq.0) then print "(a)",' >> SUBTRACTING MEAN VELOCITY (from SPLASH_VZERO_CODEUNITS setting):' if (.not.allocated(dat) .or. size(dat(1,:,1)).lt.ivx+ndimV-1) then print*,' INTERNAL ERROR: dat not allocated in adjust_data_codeunits' return endif do i=1,ndimV print "(4x,a,es10.3)",trim(label(ivx+i-1))//' = '//trim(label(ivx+i-1))//' - ',v0(i) dat(:,ivx+i-1,:) = dat(:,ivx+i-1,:) - v0(i) enddo endif endif if (nerr.ne.0) then print "(4x,a)",'SPLASH_VZERO_CODEUNITS setting not used' endif endif if (ndim.gt.0) then ! !--environment variable to corotate with first two sink particles ! if (lenvironment('SPLASH_COROTATE')) then itype = get_sink_type(ntypes) if (itype.gt.0) then if (all(npartoftype(itype,:).lt.2)) then print "(a)",' ERROR: SPLASH_COROTATE set but less than 2 sink particles' else if (iverbose.ge.1) print* print "(a,i3,a)",' :: COROTATING FRAME WITH FIRST 2 SINKS from SPLASH_COROTATE setting' do j=1,nstepsinfile(ifileopen) ! find first two sink particles in the data call locate_first_two_of_type(isink1,isink2,itype,iamtype(:,j),npartoftype(:,j),ntot) ! get properties of the binary call get_binary(isink1,isink2,dat(:,:,j),x0,v0,dphi,ndim,ndimV,ncolumns,ix,ivx,ipmass,iverbose,ierr) ! rotate all the particles into this frame if (ierr.eq.0) call rotate_particles(dat(:,:,j),ntot,dphi,x0(1:ndim),ndim,ndimV,v0) enddo endif else print "(a,/,a)",' ERROR: SPLASH_COROTATE set but could not determine type ', & ' corresponding to sink particles' endif endif ! !--environment variable setting to centre plots on a selected sink particle ! !--can specify either just "true" for sink #1, or specify a number for a particular sink centreonsink = lenvironment('SPLASH_CENTRE_ON_SINK') .or. lenvironment('SPLASH_CENTER_ON_SINK') isink = max(ienvironment('SPLASH_CENTRE_ON_SINK'),ienvironment('SPLASH_CENTER_ON_SINK')) if (isink.gt.0 .or. centreonsink) then if (isink.eq.0) isink = 1 itype = get_sink_type(ntypes) if (itype.gt.0) then if (all(npartoftype(itype,:).lt.isink)) then print "(a,i10,a)",' ERROR: SPLASH_CENTRE_ON_SINK = ',isink,' but not enough sink particles' else if (iverbose.ge.1) print* if (isink.lt.10) then print "(a,i1,a)",' :: CENTREING ON SINK ',isink,' from SPLASH_CENTRE_ON_SINK setting' else print "(a,i3,a)",' :: CENTREING ON SINK ',isink,' from SPLASH_CENTRE_ON_SINK setting' endif do j=1,nstepsinfile(ifileopen) call locate_nth_particle_of_type(isink,isinkpos,itype,iamtype(:,j),npartoftype(:,j),ntot) if (isinkpos.eq.0) then print "(a)",' ERROR: could not locate sink particle in dat array' else if (debugmode) print*,' SINK POSITION = ',isinkpos,npartoftype(1:itype,j) !--make positions relative to sink particle xyzsink(1:ndim) = dat(isinkpos,ix(1:ndim),j) if (iverbose.ge.1) print "(a,3(1x,es10.3))",' :: sink position =',xyzsink(1:ndim) !--make velocities relative to sink particle if (ivx.gt.0 .and. ivx+ndimV-1.le.ncolumns) then vsink(1:ndimV) = dat(isinkpos,ivx:ivx+ndimV-1,j) if (iverbose.ge.1) print "(a,3(1x,es10.3))",' :: sink velocity =',vsink(1:ndimV) else vsink = 0. endif call shift_particles(dat(:,:,j),ntot,ndim,ndimV,ncolumns,xyzsink,vsink) endif enddo endif else print "(a,/,a)",' ERROR: SPLASH_CENTRE_ON_SINK set but could not determine type ', & ' corresponding to sink particles' endif endif endif ! !--fake a set of dust particles from the one-fluid dust formulation ! if (idustfrac.gt.0 .and. irho.gt.0 .and. & .not.(lenvironment('SPLASH_BARYCENTRIC') & .or. lenvironment('NSPLASH_BARYCENTRIC'))) then fakedust = .true. call fake_twofluids(1,nstepsinfile(ifileopen),ndim,ndimV,dat,npartoftype,iamtype) endif end subroutine adjust_data_codeunits !----------------------------------------------------------------- ! routine to rotate particles with a given cylindrical angle dphi !----------------------------------------------------------------- pure subroutine rotate_particles(dat,np,dphi,x0,ndim,ndimV,v0) use labels, only:ix,ivx integer, intent(in) :: np,ndim,ndimV real, intent(in) :: dphi real, dimension(:,:), intent(inout) :: dat real, dimension(ndim), intent(in) :: x0 real, dimension(ndimV), intent(in) :: v0 real, dimension(ndim) :: xi real, dimension(ndimV) :: vi real :: r,phi,xnew,ynew,cosp,sinp,vr,vphi integer :: i !--rotate positions do i=1,np xi = dat(i,ix(1:ndim)) - x0(1:ndim) r = sqrt(xi(1)**2 + xi(2)**2) phi = atan2(xi(2),xi(1)) phi = phi + dphi cosp = cos(phi) sinp = sin(phi) xnew = r*cosp ynew = r*sinp dat(i,ix(1)) = xnew dat(i,ix(2)) = ynew !--rotate velocities, if present if (ivx > 0) then vi = dat(i,ivx:ivx+ndimV-1) - v0 vr = vi(1)*xi(1)/r + vi(2)*xi(2)/r vphi = vi(1)*(-xi(2)/r) + vi(2)*xi(1)/r dat(i,ivx) = vr*cosp - vphi*sinp dat(i,ivx+1) = vr*sinp + vphi*cosp endif enddo end subroutine rotate_particles !------------------------------------------------------ ! routine to shift particle positions and velocities ! to new location !------------------------------------------------------ pure subroutine shift_particles(dat,np,ndim,ndimV,ncol,x0,v0) integer, intent(in) :: np,ndim,ndimV,ncol real, dimension(:,:), intent(inout) :: dat real, dimension(ndim), intent(in) :: x0 real, dimension(ndimV), intent(in) :: v0 call shift_positions(dat,np,ndim,x0) call shift_velocities(dat,np,ndimV,ncol,v0) end subroutine shift_particles !------------------------------------------------------ ! routine to shift particle positions to new location !------------------------------------------------------ pure subroutine shift_positions(dat,np,ndim,x0) use labels, only:ix integer, intent(in) :: np,ndim real, dimension(:,:), intent(inout) :: dat real, dimension(ndim), intent(in) :: x0 integer :: icol !--shift positions do icol=1,ndim dat(1:np,ix(icol)) = dat(1:np,ix(icol)) - x0(icol) enddo end subroutine shift_positions !------------------------------------------------------ ! routine to shift particle velocities by constant !------------------------------------------------------ pure subroutine shift_velocities(dat,np,ndimV,ncol,v0) use labels, only:ivx integer, intent(in) :: np,ndimV,ncol real, dimension(:,:), intent(inout) :: dat real, dimension(ndimV), intent(in) :: v0 integer :: icol !--make velocities relative to sink particle if (ivx > 0 .and. ivx+ndimV-1 <= ncol) then do icol=1,ndimV dat(1:np,ivx+icol-1) = dat(1:np,ivx+icol-1) - v0(icol) enddo endif end subroutine shift_velocities !------------------------------------------------------ ! ! routine to fake a set of dust particles from ! the one fluid dust method ! !------------------------------------------------------ subroutine fake_twofluids(istart,iend,ndim,ndimV,dat,npartoftype,iamtype) use params, only:int1 use labels, only:idustfrac,idustfracsum, & irho,ix,ih,ipmass,ivx,ideltav,ideltavsum use mem_allocation, only:alloc use particle_data, only:maxpart,maxstep,maxcol use settings_data, only:iverbose,required,ndusttypes,idustfrac_plot,ideltav_plot integer, intent(in) :: istart,iend,ndim,ndimV real, dimension(:,:,:), intent(inout), allocatable :: dat integer, dimension(:,:), intent(inout), allocatable :: npartoftype integer(int1), dimension(:,:), intent(inout), allocatable :: iamtype integer :: ndust,jdust,ntoti,i,j integer :: idustfrac_temp,ideltav_temp real :: rhodust,rhogas,rhotot,dustfraci,gasfraci,pmassgas,pmassdust,pmassj real, dimension(ndimV) :: veli,vgas,vdust,deltav,deltavsum logical :: use_vels if (idustfrac.gt.0 .and. irho.gt.0) then ! !--determine which dust fraction is being used to create the fake dust particles ! !if (iverbose >= 0) print*,' got dustfrac in column ',idustfrac if (ndusttypes>1) then if (idustfrac_plot == 0 ) then idustfrac_temp = idustfracsum idustfrac_plot = idustfracsum ideltav_temp = ideltavsum ideltav_plot = ideltavsum else idustfrac_temp = idustfrac_plot ideltav_temp = ideltav_plot endif else idustfrac_temp = idustfrac ideltav_temp = ideltav endif ! !--create explicit dust phase from gas and one-fluid dust properties ! do i=istart,iend ntoti = sum(npartoftype(:,i)) if (.not.allocated(dat) .or. (ntoti + npartoftype(1,i)).gt.maxpart) then call alloc(ntoti + npartoftype(1,i),maxstep,maxcol,mixedtypes=.true.) endif if (npartoftype(2,i) > 0) cycle ndust = 0 !--zero the properties of newly created dust particles dat(ntoti+1:ntoti+npartoftype(1,i),:,i) = 0. if (idustfrac_temp > size(dat(1,:,1))) then print*,' ERROR: idustfrac out of range: cannot create fake dust particles' return endif use_vels = (ideltav_temp.gt.0 .and. ivx.gt.0 .and. ndimV.gt.0) do j=1,ntoti if (iamtype(j,i).eq.1) then ndust = ndust + 1 ! one dust particle for every gas particle rhotot = dat(j,irho,i) dustfraci = dat(j,idustfrac_temp,i) if (idustfracsum == 0) then gasfraci = 1. - dustfraci else gasfraci = 1. - dat(j,idustfracsum,i) endif rhogas = rhotot*gasfraci rhodust = rhotot*dustfraci !--replace global properties with gas-only stuff dat(j,irho,i) = rhogas !--copy x, smoothing length onto dust particle jdust = ntoti + ndust !--fill in dust properties if (ndim.gt.0) dat(jdust,ix(1:ndim),i) = dat(j,ix(1:ndim),i) if (ih.gt.0) dat(jdust,ih,i) = dat(j,ih,i) if (irho.gt.0) dat(jdust,irho,i) = rhodust if (idustfracsum == 0) then dat(jdust,idustfrac,i) = dustfraci else dat(jdust,idustfracsum:idustfracsum+ndusttypes,i) = & dat(j,idustfracsum:idustfracsum+ndusttypes,i) endif iamtype(ntoti + ndust,i) = 2 !--particle masses if (ipmass.gt.0) then pmassj = dat(j,ipmass,i) pmassgas = pmassj*gasfraci pmassdust = pmassj*dustfraci dat(j,ipmass,i) = pmassgas dat(jdust,ipmass,i) = pmassdust endif !--velocities if (use_vels) then veli(:) = dat(j,ivx:ivx+ndimV-1,i) deltav(:) = dat(j,ideltav_temp:ideltav_temp+ndimV-1,i) if (ndusttypes>1 .and. idustfrac_temp/=idustfracsum) then deltavsum(:) = dat(j,ideltavsum:ideltavsum+ndimV-1,i) vgas(:) = veli(:) - (1. - gasfraci)*deltavsum(:) vdust(:) = veli(:) + deltav(:) - (1. - gasfraci)*deltavsum(:) else vgas(:) = veli(:) - (1. - gasfraci)*deltav(:) vdust(:) = veli(:) + gasfraci*deltav(:) endif dat(j,ivx:ivx+ndimV-1,i) = vgas(:) dat(jdust,ivx:ivx+ndimV-1,i) = vdust(:) endif endif enddo if (iverbose.ge.1) then print "(a,i8,a)",' Creating ',ndust,' fake dust particles (set SPLASH_BARYCENTRIC=yes to plot barycentric values)' if (.not.use_vels .and. ivx > 0) print "(a)",' WARNING: deltav not found in one fluid dust data: cannot get vels' endif npartoftype(2,i) = npartoftype(2,i) + ndust enddo else print "(a)",' ERROR: could not locate dust-to-gas ratio and/or density' endif end subroutine fake_twofluids end module adjustdata splash/src/read_data_snsph.f90000644 000766 000000 00000012055 13261626263 017215 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM THE SNSPH CODE ! USING THE SELF-DESCRIBING FORMAT ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: j,ierr,ntoti,getcol integer :: npart_max,nstep_max,ncolstep real :: timei, gammai logical :: iexist character(len=len(rootname)+10) :: dumpfile nstepsread = 0 npart_max = maxpart dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: ',trim(dumpfile),' file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 ! !--allocate memory initially ! nstep_max = max(indexstart,1) j = indexstart nstepsread = 0 print "(1x,a)",'reading SNSPH format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--read number of columns and number of particles ! ncolstep = getcol() call getdata(dumpfile,len_trim(dumpfile),npart_max,timei,gammai) ! !--get number of particles from header and allocate memory ! ntoti = npart_max ncolumns = ncolstep if (.not.allocated(dat) .or. ntoti.gt.npart_max) then npart_max = max(npart_max,INT(1.1*ntoti)) call alloc(npart_max,nstep_max,ncolstep+ncalc) endif npart_max = max(npart_max,ntoti) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+2*nstepsread,maxcol) endif ! !--now read the timestep data in the dumpfile ! time(j) = timei print "(a,i5,a,f10.3,a,i8)",'| step ',j,': t = ',time(j),' ntotal = ',ntoti nstepsread = nstepsread + 1 ! !--set particle numbers ! npartoftype(:,j) = 0 npartoftype(1,j) = ntoti ! !--now read data ! call readsdf(dumpfile,len_trim(dumpfile),dat(:,:,j),maxpart,maxcol,ierr) if (nstepsread .gt. 0 .and. j.gt.0) then print*,'>> end of dump file: ntotal = ',sum(npartoftype(:,j)) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ih = 10 ! smoothing length iutherm = 8 ! thermal energy ipmass = 7 ! particle mass irho = 9 ! location of rho in data array label(ix(1:ndim)) = labelcoord(1:ndim,1) label(irho) = 'density' label(iutherm) = 'temperature' label(ih) = 'h' label(ipmass) = 'particle mass' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 1 labeltype(1) = 'gas' !----------------------------------------------------------- return end subroutine set_labels splash/src/convert_grid.f90000644 000766 000000 00000073705 13261626263 016574 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! Module containing routines for converting 3D SPH dump ! files to 3D gridded data. !----------------------------------------------------------------- module convert_grid private public :: convert_to_grid contains !----------------------------------------------------------------- ! interpolate 3D SPH data to grid and interface to grid ! data output routines !----------------------------------------------------------------- subroutine convert_to_grid(time,dat,ntypes,npartoftype,masstype,itype,ncolumns,filename,& outformat,interpolateall) use labels, only:label,labelvec,irho,ih,ipmass,ix,ivx,iBfirst use limits, only:lim,get_particle_subset use settings_units, only:units,unit_interp use settings_data, only:ndim,ndimV,UseTypeInRenderings,iRescale,required,lowmemorymode,debugmode use settings_part, only:iplotpartoftype use settings_render, only:npix,inormalise_interpolations,idensityweightedinterpolation use params, only:int1 use interpolation, only:set_interpolation_weights use interpolations3D, only:interpolate3D,interpolate3D_vec use interpolations2D, only:interpolate2D,interpolate2D_vec use system_utils, only:lenvironment,renvironment,envlist,lenvstring,ienvstring use readwrite_griddata, only:open_gridfile_w,write_grid,write_gridlimits use particle_data, only:icolourme use params, only:int8 implicit none integer, intent(in) :: ntypes,ncolumns integer, intent(in), dimension(:) :: npartoftype integer(kind=int1), intent(in), dimension(:) :: itype real, intent(in) :: time real, intent(in), dimension(:,:) :: dat real, intent(in), dimension(:) :: masstype character(len=*), intent(in) :: filename,outformat logical, intent(in) :: interpolateall integer, parameter :: iunit = 89 integer :: ierr,i,k,ncolsgrid,ivec,nvec,iloc,j,nzero integer :: npixx,ntoti,ninterp,nstring character(len=40) :: fmtstring character(len=64) :: fmtstring1 real, dimension(:,:,:), allocatable :: datgrid real, dimension(:,:), allocatable :: datgrid2D real, dimension(:,:,:,:), allocatable :: datgridvec real, dimension(:,:,:), allocatable :: datgridvec2D real, dimension(:), allocatable :: weight real, dimension(3) :: xmin,xmax real, dimension(3) :: partmin,partmax,partmean real, dimension(3) :: datmin,datmax,datmean integer, dimension(3) :: npixels integer(kind=int8), dimension(3) :: npixels8 integer, dimension(12) :: icoltogrid integer :: ncolstogrid,icol real :: hmin,pixwidth,rhominset,rhomin,gridmin,gridmax,gridmean logical :: inormalise,lowmem logical, dimension(3) :: isperiodic character(len=30), dimension(12) :: strings character(len=1), dimension(3), parameter :: xlab = (/'x','y','z'/) ! !--check for errors in input settings ! if (ndim.lt.2 .or. ndim.gt.3) then print "(/,a,i2,a,/)",' ERROR: SPH data has ',ndim,' spatial dimensions: cannot convert to 3D grid' return endif print "(/,'----->',1x,a,i1,a,/)",'CONVERTING SPH DATA -> ',ndim,'D GRID' xmin(1:ndim) = lim(ix(1:ndim),1) xmax(1:ndim) = lim(ix(1:ndim),2) ! !--print limits information ! call write_gridlimits(ndim,xmin,xmax,label(ix(1:ndim))) ! !--SPLASH_TO_GRID can be set to comma separated list of columns ! in order to select particular quantities for interpolation to grid ! ncolstogrid = 0 icoltogrid(:) = 0 call envlist('SPLASH_TO_GRID',nstring,strings) if (nstring.gt.0) then do i=1,nstring icol = ienvstring(strings(i)) if (ienvstring(strings(i)).gt.0) then ncolstogrid = ncolstogrid + 1 icoltogrid(ncolstogrid) = icol endif enddo endif ! !--for backwards compatibility, support the SPLASH_TO_GRID_DENSITY_ONLY option ! but only if SPLASH_TO_GRID is not set ! if (ncolstogrid.eq.0 .and. lenvironment('SPLASH_TO_GRID_DENSITY_ONLY')) then ncolstogrid = 1 icoltogrid(1) = irho endif ! !--whether or not to wrap particle contributions across boundaries ! isperiodic(:) = .false. call envlist('SPLASH_TO_GRID_PERIODIC',nstring,strings) if (nstring.gt.ndim) then print "(a)",' ERROR in SPLASH_TO_GRID_PERIODIC setting' nstring = ndim endif do i=1,nstring isperiodic(i) = lenvstring(strings(i)) enddo if (nstring.eq.1) isperiodic(2:ndim) = isperiodic(1) if (all(isperiodic(1:ndim))) then print "(/,a)",' using PERIODIC boundaries (from SPLASH_TO_GRID_PERIODIC setting)' elseif (isperiodic(1) .or. isperiodic(2) .or. isperiodic(3)) then print* do i=1,ndim if (isperiodic(i)) then print "(a)",' using PERIODIC boundaries in '//xlab(i)//' (from SPLASH_TO_GRID_PERIODIC setting)' else print "(a)",' using NON-PERIODIC bounds in '//xlab(i)//' (from SPLASH_TO_GRID_PERIODIC setting)' endif enddo else print "(/,a)",' using NON-PERIODIC boundaries' print "(a)",' (set SPLASH_TO_GRID_PERIODIC=yes for periodic' if (ndim.eq.3) then print "(a)",' or SPLASH_TO_GRID_PERIODIC=yes,no,yes for mixed)' else print "(a)",' or SPLASH_TO_GRID_PERIODIC=yes,no for mixed)' endif endif ierr = 0 do i=1,ndim if ((xmax(i)-xmin(i)).lt.tiny(0.)) then print "(a)",' ERROR: min = max in '//trim(label(ix(i)))//& ' coordinate: cannot interpolate to zero-sized grid!' ierr = 1 endif enddo if (irho.le.0 .or. irho.gt.ncolumns) then print "(a)",' ERROR: density not found in data read.' ierr = 2 endif if (ih.le.0 .or. ih.gt.ncolumns) then print "(a)",' ERROR: smoothing length not found in data read.' ierr = 3 endif if (ipmass.le.0 .or. ipmass.gt.ncolumns) then if (all(masstype(:).lt.tiny(0.))) then print "(a)",' ERROR: particle masses not read as column, and mass per type not set.' ierr = 4 endif endif if (ierr /= 0) then print "(a,i1,a)",' cannot perform SPH interpolation to ',ndim,'D grid, skipping file...' return endif ierr = 0 ! !--set number of particles to use in the interpolation routines ! (by default, only the gas particles) ! ntoti = sum(npartoftype) ninterp = npartoftype(1) if (any(UseTypeInRenderings(2:ntypes).and.iplotpartoftype(2:ntypes)) & .or. size(itype).gt.1) ninterp = ntoti allocate(weight(ninterp),stat=ierr) if (ierr /= 0) then print*,' ERROR allocating memory for interpolation weights, aborting...' return endif ! !--set interpolation weights (w = m/(rho*h^ndim) ! inormalise = inormalise_interpolations call set_interpolation_weights(weight,dat,itype,(iplotpartoftype .and. UseTypeInRenderings),& ninterp,npartoftype,masstype,ntypes,ncolumns,irho,ipmass,ih,ndim,iRescale,& idensityweightedinterpolation,inormalise,units,unit_interp,required,.false.) ! !--set colours (just in case) ! icolourme(:) = 1 ! !--apply range restrictions to data ! call get_particle_subset(icolourme,dat,ncolumns) ! !--work out how many pixels to use ! npixx = npix if (npixx.le.0) then print "(/,a)",' WARNING: number of pixels = 0, using automatic pixel numbers' hmin = 0. call minmaxmean_part(dat(:,ih:ih),weight,ninterp,partmin,partmax,partmean,nonzero=.true.) hmin = partmin(1) if (hmin.gt.0.) then print*,'based on the minimum smoothing length of hmin = ',hmin npixels8(1:ndim) = int((xmax(1:ndim) - xmin(1:ndim))/hmin,kind=int8) + 1 if (ndim.eq.3) then print "(a,i6,2(' x',i6),a)",' requires ',npixels8(1:ndim),' pixels to capture the full resolution' if (product(npixels8(1:ndim)).gt.512**3 .or. product(npixels8(1:ndim)).le.0) then npixx = 512 print "(a,i4)",' but this is ridiculous, so instead we choose ',npixx else npixx = npixels8(1) endif else print "(a,i6,1(' x',i6),a)",' requires ',npixels8(1:ndim),' pixels to capture the full resolution' if (product(npixels8(1:ndim)).gt.1024**ndim .or. product(npixels8(1:ndim)).le.0) then npixx = 1024 print "(a,i4)",' but this is very large, so instead we choose ',npixx else npixx = npixels8(1) endif endif else npixx = 512 print "(a)",' ...but cannot get auto pixel number because hmin = 0' print "(a)",' so instead we choose npixels = ',npixx endif endif print* pixwidth = (xmax(1)-xmin(1))/npixx npixels(1:ndim) = int((xmax(1:ndim)-xmin(1:ndim) - 0.5*pixwidth)/pixwidth) + 1 ! !--work out how many columns will be written to file ! nvec = 0 if (ncolstogrid.gt.0) then ncolsgrid = ncolstogrid elseif (interpolateall) then ncolsgrid = 0 do i=1,ncolumns if (.not.any(ix(1:ndim).eq.i) .and. i.ne.ih .and. i.ne.ipmass) then ncolsgrid = ncolsgrid + 1 endif enddo else if (ndimV.eq.ndim) then if (ivx.gt.0 .and. ivx+ndimV-1.le.ncolumns) nvec = nvec + 1 if (iBfirst.gt.0 .and. iBfirst+ndimV-1.le.ncolumns) nvec = nvec + 1 endif ncolsgrid = 1 + ndimV*nvec endif ! !--use low memory mode for large grids ! if (trim(outformat)=='gridascii2') then lowmem = .false. else lowmem = .true. endif ! if ((ndim.eq.3 .and. product(npixels).gt.256**3)) then ! lowmem = .true. ! else ! lowmem = lowmemorymode ! endif if (lowmem .and. nvec.gt.0) & print "(a,/)",' [doing velocity field components separately (low memory mode)]' ! !--allocate memory for the grid ! if (allocated(datgrid)) deallocate(datgrid) if (allocated(datgrid2D)) deallocate(datgrid2D) if (ndim.eq.3) then write(*,"(a,i5,2(' x',i5),a)",advance='no') ' >>> allocating memory for ',npixels(1:ndim),' grid ...' allocate(datgrid(npixels(1),npixels(2),npixels(3)),stat=ierr) elseif (ndim.eq.2) then write(*,"(a,i5,1(' x',i5),a)",advance='no') ' >>> allocating memory for ',npixels(1:ndim),' grid ...' allocate(datgrid2D(npixels(1),npixels(2)),stat=ierr) endif if (ierr /= 0) then write(*,*) 'FAILED: NOT ENOUGH MEMORY' if (allocated(weight)) deallocate(weight) return else write(*,*) 'OK' endif ! !--open grid file for output (also checks format is OK) ! call open_gridfile_w(iunit,filename,outformat,ndim,ncolsgrid,npixels(1:ndim),time,ierr) if (ierr /= 0) then print "(a)",' ERROR: could not open grid file for output, skipping...' if (allocated(datgrid)) deallocate(datgrid) if (allocated(datgrid)) deallocate(datgrid2D) if (allocated(weight)) deallocate(weight) return endif fmtstring1 = "(12x,a20,1x,' min ',1x,' max ',1x,' mean ')" fmtstring = "(22x,a10,1x,3(es10.2,1x))" ! !--interpolate density to the 3D grid ! print "(/,a,i1,a)",' interpolating density to ',ndim,'D grid...' if (debugmode) print*,'DEBUG: density in column ',irho,' vals = ',dat(1:10,irho) call minmaxmean_part(dat(1:ninterp,irho:irho),weight,ninterp,partmin,partmax,partmean) print fmtstring1,trim(label(irho)) print fmtstring,' on parts:',partmin(1),partmax(1),partmean(1) if (ndim.eq.3) then call interpolate3D(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,irho),icolourme,ninterp,& xmin(1),xmin(2),xmin(3),datgrid,npixels(1),npixels(2),npixels(3),& pixwidth,pixwidth,inormalise,isperiodic(1),isperiodic(2),isperiodic(3)) ! !--set minimum density on the grid ! call minmaxmean_grid(datgrid,npixels,gridmin,gridmax,gridmean,nonzero=.true.) else call interpolate2D(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,irho),icolourme,ninterp,& xmin(1),xmin(2),datgrid2D,npixels(1),npixels(2),& pixwidth,pixwidth,inormalise,isperiodic(1),isperiodic(2)) ! !--set minimum density on the grid ! call minmaxmean_grid2D(datgrid2D,npixels,gridmin,gridmax,gridmean,nonzero=.true.) endif print fmtstring1,trim(label(irho)) print fmtstring,' on grid :',gridmin,gridmax,gridmean rhomin = gridmin rhominset = renvironment('SPLASH_TO_GRID_RHOMIN',errval=-1.) print* if (rhominset.ge.0.) then rhomin = rhominset print*,'enforcing minimum density on grid = ',rhomin print*,'(based on SPLASH_TO_GRID_RHOMIN setting)' elseif (rhomin.gt.0.) then print*,'enforcing minimum density on grid = ',rhomin print*,'set SPLASH_TO_GRID_RHOMIN=minval to manually set this (e.g. to zero)' endif if (rhomin.gt.0.) then nzero = 0 if (ndim.eq.3) then !$omp parallel do private(k,j,i) reduction(+:nzero) schedule(static) do k=1,npixels(3) do j=1,npixels(2) do i=1,npixels(1) if (datgrid(i,j,k).le.tiny(datgrid)) then datgrid(i,j,k) = rhomin nzero = nzero + 1 endif enddo enddo enddo else !$omp parallel do private(j,i) reduction(+:nzero) schedule(static) do j=1,npixels(2) do i=1,npixels(1) if (datgrid2D(i,j).le.tiny(datgrid2D)) then datgrid2D(i,j) = rhomin nzero = nzero + 1 endif enddo enddo endif print "(a,i8,a)",' minimum density enforced on ',nzero,' grid cells' else print*,'minimum density NOT enforced' endif ! !--write density to grid data file ! print* if (ndim.eq.3) then if (lowmem .or. interpolateall .or. ncolstogrid.gt.0) then call write_grid(iunit,filename,outformat,ndim,1,npixels,trim(label(irho)),& time,pixwidth,xmin,ierr,dat=datgrid) endif else call write_grid(iunit,filename,outformat,ndim,1,npixels,trim(label(irho)),& time,pixwidth,xmin,ierr,dat2D=datgrid2D) endif ! !--interpolate remaining quantities to the 3D grid ! if (interpolateall .or. ncolstogrid.gt.0) then if (ncolstogrid.gt.0) then print "(/,a,i2,a)",' Interpolating ',ncolstogrid,' columns to grid from SPLASH_TO_GRID setting:' print "(' got SPLASH_TO_GRID=',10(i2,1x))",icoltogrid(1:ncolstogrid) endif do i=1,ncolumns if ((ncolstogrid.gt.0 .and. any(icoltogrid.eq.i) .and. i.ne.irho) .or. & (interpolateall .and. & .not.any(ix(:).eq.i) .and. i.ne.ih .and. i.ne.ipmass .and. i.ne.irho)) then print "(/,a)",' interpolating '//trim(label(i)) print fmtstring1,trim(label(i)) call minmaxmean_part(dat(1:ninterp,i:i),weight,ninterp,partmin,partmax,partmean) print fmtstring,' on parts:',partmin(1),partmax(1),partmean(1) if (iszero(partmin,partmax,1)) then datgrid = 0. else if (ndim.eq.3) then call interpolate3D(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,i),icolourme,ninterp,& xmin(1),xmin(2),xmin(3),datgrid,npixels(1),npixels(2),npixels(3),& pixwidth,pixwidth,.true.,isperiodic(1),isperiodic(2),isperiodic(3)) else call interpolate2D(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,i),icolourme,ninterp,& xmin(1),xmin(2),datgrid2D,npixels(1),npixels(2),& pixwidth,pixwidth,.true.,isperiodic(1),isperiodic(2)) endif endif ! !--write gridded data to file ! if (ndim.eq.3) then call minmaxmean_grid(datgrid,npixels,gridmin,gridmax,gridmean,.false.) print fmtstring,' on grid :',gridmin,gridmax,gridmean call write_grid(iunit,filename,outformat,ndim,1,npixels,trim(label(i)),& time,pixwidth,xmin,ierr,dat=datgrid) else call minmaxmean_grid2D(datgrid2D,npixels,gridmin,gridmax,gridmean,.false.) print fmtstring,' on grid :',gridmin,gridmax,gridmean call write_grid(iunit,filename,outformat,ndim,1,npixels,trim(label(i)),& time,pixwidth,xmin,ierr,dat2D=datgrid2D) endif endif enddo else if (nvec.gt.0) then print "(/,a,i2,a)",' set SPLASH_TO_GRID=',irho,' to interpolate density ONLY and skip remaining columns' print "(a,i2,a)", ' SPLASH_TO_GRID=6,8,10 to select particular columns' if (.not.lowmem) then if (ndim.eq.3) then write(*,"(/,a,i5,2(' x',i5),a)",advance='no') ' >>> allocating memory for ',npixels(1:ndim),' x 3 grid ...' allocate(datgridvec(3,npixels(1),npixels(2),npixels(3)),stat=ierr) else write(*,"(/,a,i5,1(' x',i5),a)",advance='no') ' >>> allocating memory for ',npixels(1:ndim),' x 3 grid ...' allocate(datgridvec2D(2,npixels(1),npixels(2)),stat=ierr) endif if (ierr /= 0) then write(*,*) 'FAILED: NOT ENOUGH MEMORY' return else write(*,*) 'OK' endif endif ! !--interpolate velocity field and magnetic fields and write to file ! over_vec: do ivec=1,nvec select case(ivec) case(1) iloc = ivx print "(a,i1,a)",' interpolating velocity field to ',ndim,'D grid...' case(2) iloc = iBfirst print "(a,i1,a)",' interpolating magnetic field to ',ndim,'D grid...' case default iloc = 0 exit over_vec end select if (iloc.le.0 .or. iloc.ge.ncolumns) cycle over_vec if (lowmem) then do i=iloc,iloc+ndimV-1 print "(/,a)",' interpolating '//trim(label(i)) print fmtstring1,trim(label(i)) call minmaxmean_part(dat(1:ninterp,i:i),weight,ninterp,partmin,partmax,partmean) print fmtstring,' on parts:',partmin(1),partmax(1),partmean(1) if (iszero(partmin,partmax,1)) then datgrid = 0. else if (ndim.eq.3) then call interpolate3D(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,i),icolourme,ninterp,& xmin(1),xmin(2),xmin(3),datgrid,npixels(1),npixels(2),npixels(3),& pixwidth,pixwidth,.true.,isperiodic(1),isperiodic(2),isperiodic(3)) else call interpolate2D(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,i),icolourme,ninterp,& xmin(1),xmin(2),datgrid2D,npixels(1),npixels(2),& pixwidth,pixwidth,.true.,isperiodic(1),isperiodic(2)) endif endif ! !--write gridded data to file ! if (ndim.eq.3) then call minmaxmean_grid(datgrid,npixels,gridmin,gridmax,gridmean,.false.) print fmtstring,' on grid :',gridmin,gridmax,gridmean call write_grid(iunit,filename,outformat,ndim,1,npixels,trim(label(i)),& time,pixwidth,xmin,ierr,dat=datgrid) else call minmaxmean_grid2D(datgrid2D,npixels,gridmin,gridmax,gridmean,.false.) print fmtstring,' on grid :',gridmin,gridmax,gridmean call write_grid(iunit,filename,outformat,ndim,1,npixels,trim(label(i)),& time,pixwidth,xmin,ierr,dat2D=datgrid2D) endif enddo else print fmtstring1,trim(labelvec(iloc)) call minmaxmean_part(dat(1:ninterp,iloc:iloc+ndimV-1),weight,ninterp,partmin,partmax,partmean) do i=1,ndimV print fmtstring,' on parts:',partmin(i),partmax(i),partmean(i) enddo if (iszero(partmin,partmax,ndimV)) then datgridvec = 0. else if (ndim.eq.3) then call interpolate3D_vec(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,iloc:iloc+ndimV-1),icolourme,ninterp,& xmin(1),xmin(2),xmin(3),datgridvec,npixels(1),npixels(2),npixels(3),& pixwidth,pixwidth,.true.,isperiodic(1),isperiodic(2),isperiodic(3)) else call interpolate2D_vec(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),& dat(1:ninterp,ih),weight(1:ninterp),dat(1:ninterp,iloc),dat(1:ninterp,iloc+1), & icolourme,ninterp,xmin(1),xmin(2),datgridvec2D(1,:,:),datgridvec2D(2,:,:), & npixels(1),npixels(2),pixwidth,pixwidth,.true.,isperiodic(1),isperiodic(2)) endif endif if (ndim.eq.3) then call minmaxmean_gridvec(datgridvec,npixels,ndimV,datmin,datmax,datmean) else call minmaxmean_gridvec2D(datgridvec2D,npixels,ndimV,datmin,datmax,datmean) endif do i=1,ndimV print fmtstring,' on grid :',datmin(i),datmax(i),datmean(i) enddo ! !--write result to grid file ! if (ndim.eq.3) then call write_grid(iunit,filename,outformat,ndim,ndimV,npixels,& label(irho),time,pixwidth,xmin,ierr,& dat=datgrid,dat3D=datgridvec,label3D=label(iloc:iloc+ndimV)) else do i=1,ndimV call write_grid(iunit,filename,outformat,ndim,ndimV,npixels,& label(iloc+i-1),time,pixwidth,xmin,ierr,dat2D=datgridvec2D(i,:,:)) enddo endif endif print* enddo over_vec endif ! else ! print "(/,a)",' skipping remaining quantities (from SPLASH_TO_GRID_DENSITY_ONLY setting)' endif close(iunit) if (allocated(datgrid)) deallocate(datgrid) if (allocated(datgrid2D)) deallocate(datgrid2D) if (allocated(datgridvec)) deallocate(datgridvec) if (allocated(datgridvec2D)) deallocate(datgridvec2D) if (allocated(weight)) deallocate(weight) return end subroutine convert_to_grid !----------------------------------------------- ! calculate max and min and mean values on grid !----------------------------------------------- subroutine minmaxmean_grid(datgrid,npixels,gridmin,gridmax,gridmean,nonzero) implicit none real, dimension(:,:,:), intent(in) :: datgrid integer, dimension(3), intent(in) :: npixels real, intent(out) :: gridmin,gridmax,gridmean logical, intent(in) :: nonzero real :: dati integer :: i,j,k gridmax = -huge(gridmax) gridmin = huge(gridmin) gridmean = 0. !$omp parallel do schedule(static) & !$omp reduction(min:gridmin) & !$omp reduction(max:gridmax) reduction(+:gridmean) & !$omp private(k,j,i,dati) do k=1,npixels(3) do j=1,npixels(2) do i=1,npixels(1) dati = datgrid(i,j,k) gridmax = max(gridmax,dati) if (nonzero) then if (dati.gt.tiny(0.)) gridmin = min(gridmin,dati) else gridmin = min(gridmin,dati) endif gridmean = gridmean + dati enddo enddo enddo gridmean = gridmean/product(npixels(1:3)) return end subroutine minmaxmean_grid !---------------------------------------------------- ! calculate max and min and mean values on grid (2D) !---------------------------------------------------- subroutine minmaxmean_grid2D(datgrid,npixels,gridmin,gridmax,gridmean,nonzero) implicit none real, dimension(:,:), intent(in) :: datgrid integer, dimension(2), intent(in) :: npixels real, intent(out) :: gridmin,gridmax,gridmean logical, intent(in) :: nonzero real :: dati integer :: i,j gridmax = -huge(gridmax) gridmin = huge(gridmin) gridmean = 0. !$omp parallel do schedule(static) & !$omp reduction(min:gridmin) & !$omp reduction(max:gridmax) reduction(+:gridmean) & !$omp private(j,i,dati) do j=1,npixels(2) do i=1,npixels(1) dati = datgrid(i,j) gridmax = max(gridmax,dati) if (nonzero) then if (dati.gt.tiny(0.)) gridmin = min(gridmin,dati) else gridmin = min(gridmin,dati) endif gridmean = gridmean + dati enddo enddo gridmean = gridmean/product(npixels(1:2)) return end subroutine minmaxmean_grid2D !----------------------------------------------- ! calculate max and min and mean values on grid ! (for vector quantities) !----------------------------------------------- subroutine minmaxmean_gridvec(datgridvec,npixels,jlen,gridmin,gridmax,gridmean) implicit none real, dimension(:,:,:,:), intent(in) :: datgridvec integer, dimension(3), intent(in) :: npixels integer, intent(in) :: jlen real, dimension(jlen), intent(out) :: gridmin,gridmax,gridmean real :: dati integer :: ivec,i,j,k gridmax(:) = -huge(gridmax) gridmin(:) = huge(gridmin) gridmean(:) = 0. !$omp parallel do schedule(static) & !$omp reduction(min:gridmin) & !$omp reduction(max:gridmax) reduction(+:gridmean) & !$omp private(k,j,i,dati) do k=1,npixels(3) do j=1,npixels(2) do i=1,npixels(1) do ivec=1,jlen dati = datgridvec(ivec,i,j,k) gridmax(ivec) = max(gridmax(ivec),dati) gridmin(ivec) = min(gridmin(ivec),dati) gridmean(ivec) = gridmean(ivec) + dati enddo enddo enddo enddo gridmean(1:jlen) = gridmean(1:jlen)/real(product(npixels(1:3))) return end subroutine minmaxmean_gridvec !----------------------------------------------- ! calculate max and min and mean values on grid ! (for vector quantities) !----------------------------------------------- subroutine minmaxmean_gridvec2D(datgridvec,npixels,jlen,gridmin,gridmax,gridmean) implicit none real, dimension(:,:,:), intent(in) :: datgridvec integer, dimension(2), intent(in) :: npixels integer, intent(in) :: jlen real, dimension(jlen), intent(out) :: gridmin,gridmax,gridmean real :: dati integer :: ivec,i,j gridmax(:) = -huge(gridmax) gridmin(:) = huge(gridmin) gridmean(:) = 0. !$omp parallel do schedule(static) & !$omp reduction(min:gridmin) & !$omp reduction(max:gridmax) reduction(+:gridmean) & !$omp private(j,i,dati) do j=1,npixels(2) do i=1,npixels(1) do ivec=1,jlen dati = datgridvec(ivec,i,j) gridmax(ivec) = max(gridmax(ivec),dati) gridmin(ivec) = min(gridmin(ivec),dati) gridmean(ivec) = gridmean(ivec) + dati enddo enddo enddo gridmean(1:jlen) = gridmean(1:jlen)/real(product(npixels(1:2))) return end subroutine minmaxmean_gridvec2D !---------------------------------------------------- ! calculate max and min and mean values on particles !---------------------------------------------------- subroutine minmaxmean_part(dat,weight,npart,partmin,partmax,partmean,nonzero) implicit none real, dimension(:,:), intent(in) :: dat real, dimension(:), intent(in) :: weight integer, intent(in) :: npart real, dimension(3), intent(out) :: partmin,partmax,partmean logical, intent(in), optional :: nonzero real :: partval integer :: np,jlen,i,j logical :: usenonzero usenonzero = .false. if (present(nonzero)) usenonzero = nonzero partmax(:) = -huge(partmax) partmin(:) = huge(partmin) partmean(:) = 0. np = 0 jlen = min(size(dat(1,:)),3) !--could do this in parallel but reduction on arrays ! does not seem to work in ifort !!$omp parallel do default(none) schedule(static) & !!$omp shared(dat,weight,jlen,npart,usenonzero) & !!$omp reduction(min:partmin) & !!$omp reduction(max:partmax) & !!$omp reduction(+:partmean,np) & !!$omp private(i,j,partval) do i=1,npart !--only count particles used in the rendering if (weight(i).gt.tiny(0.)) then np = np + 1 do j=1,jlen partval = dat(i,j) if (usenonzero) then if (partval.gt.tiny(0.)) partmin(j) = min(partmin(j),partval) else partmin(j) = min(partmin(j),partval) endif partmax(j) = max(partmax(j),partval) partmean(j) = partmean(j) + partval enddo endif enddo !!$omp end parallel do if (np.gt.0) then partmean(:) = partmean(:)/real(np) endif return end subroutine minmaxmean_part !---------------------------------------------------- ! calculate max and min and mean values on particles !---------------------------------------------------- logical function iszero(partmin,partmax,ndim) implicit none real, dimension(:), intent(in) :: partmin,partmax integer, intent(in) :: ndim if (all(abs(partmin(1:ndim)).lt.tiny(0.)) .and. & all(abs(partmax(1:ndim)).lt.tiny(0.))) then iszero = .true. print "(a)",' min=max=0 on particles: skipping pointless interpolation and setting dat = 0.' else iszero = .false. endif end function iszero end module convert_grid splash/src/contours.f90000644 000766 000000 00000004013 13261626263 015745 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2011 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! ! This module contributed by Andrew McLeod !----------------------------------------------------------------- module contours_module implicit none integer, parameter, private :: maxcontours = 50 integer, parameter, public :: lencontourtitles = 60 real, dimension(maxcontours), public :: contours_list character(len=lencontourtitles), dimension(maxcontours), public :: contourtitles logical, public :: fixed_contours public :: read_contours private contains ! !--reads a list of contours (one per line), to be used on contour plots ! subroutine read_contours(ncontours,ierr) use asciiutils, only:read_asciifile use filenames, only:fileprefix implicit none integer, intent(out) :: ncontours, ierr character(len=50) :: contourfile logical :: iexist contourfile = trim(fileprefix)//'.contours' ncontours = 0 ierr = 0 inquire(file=contourfile,exist=iexist) if (iexist) then call read_asciifile(contourfile,ncontours,contours_list,contourtitles) else contours_list(:) = 0. contourtitles(:) = '' ierr = 1 endif if (ncontours.gt.0) then print "(1x,a)",'read contours and titles from file '//trim(contourfile) else ierr = -1 endif return end subroutine read_contours end module contours_module splash/src/read_data_aly.f90000644 000766 000000 00000021756 13261626263 016657 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! the data is stored in the global array dat ! ! THIS VERSION FOR ABDEL REHEAM's SPH CODE ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- !--local module to store header information so we can later set the labels module alydataread use params use labels, only:lenlabel implicit none character(len=16), dimension(maxplot) :: compName end module alydataread subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:npartoftype,masstype,time,gamma,dat,maxpart,maxstep,maxcol,iamtype use params use filenames, only:nfiles use settings_data, only:ndim,ndimV,ncolumns,ncalc, & buffer_data,iverbose,debugmode,ntypes use mem_allocation, only:alloc use labels, only:ipr,ivx,ih,irho,labeltype use alydataread, only:compName implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+4) :: datfile integer :: i,ierr,iunit,j,iblock integer :: npart_max,nstep_max integer, dimension(:), allocatable :: itype character(len=20) :: geomfile character(len=7) :: keyword character(len=70) :: title character(len=1) :: dumchar character(len=16) :: unitsys integer :: istep,jtype,np,ione,kk,idum,nblock real(kind=sing_prec) :: version,timesingle,dum real(kind=doub_prec) :: versiond,timedbl,dumd real :: timein,dx,dy logical :: singleprecision iunit = 11 ! file unit number nstepsread = 0 if (rootname(1:1).ne.' ') then datfile = trim(rootname) else print*,' **** no data read **** ' return endif if (iverbose.ge.1) print "(1x,a)",'reading Aly format' write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ndim = 2 ndimV = 2 ! !--open data file and read data ! open(unit=iunit,iostat=ierr,file=datfile,status='old',form='unformatted',access='stream') if (ierr /= 0) then print*,' *** Error opening '//trim(datfile)//' ***' return endif ! !--read first header line ! read(iunit,iostat=ierr,end=80) keyword read(iunit,iostat=ierr,end=80) version read(iunit,iostat=ierr,end=80) title read(iunit,iostat=ierr,end=80) istep read(iunit,iostat=ierr,end=80) timesingle timein = timesingle read(iunit,iostat=ierr,end=80) np read(iunit,iostat=ierr,end=80) ione if (ierr /= 0 .or. np <= 0 .or. timesingle.lt.0.) then ! !--try single precision ! rewind(iunit) read(iunit,iostat=ierr,end=80) keyword read(iunit,iostat=ierr,end=80) versiond version = versiond read(iunit,iostat=ierr,end=80) title read(iunit,iostat=ierr,end=80) istep read(iunit,iostat=ierr,end=80) timedbl timein = timedbl read(iunit,iostat=ierr,end=80) np read(iunit,iostat=ierr,end=80) ione singleprecision = .false. if (ierr /= 0 .or. np < 0 .or. timedbl < 0.) then print "(a)",' *** Error reading first header ***' close(iunit) return endif endif print "(a,f4.2)",' keyword = '//trim(keyword)//' version = ',version !print "(a)",' title = '//trim(title) print "(a,i6,a,es10.3,a,i6)",' step = ',istep,' time = ',timein,' np = ',np ! !--allocate memory for data arrays ! if (buffer_data) then nstep_max = max(nfiles,maxstep,indexstart) else nstep_max = max(1,maxstep,indexstart) endif npart_max = max(int(1.1*np),maxpart) ncolumns = 7 if (.not.allocated(dat) .or. npart_max.gt.maxpart & .or. nstep_max.gt.maxstep .or. ncolumns+ncalc.gt.maxcol) then call alloc(npart_max,nstep_max,ncolumns+ncalc,mixedtypes=.true.) endif i = indexstart nstepsread = 0 time(i) = timein gamma(i) = 5./3. npartoftype(1,i) = np do j=1,np read(iunit,iostat=ierr,end=67) idum,(dat(j,kk,i),kk=1,2),dum enddo read(iunit,iostat=ierr,end=67) nblock read(iunit,iostat=ierr,end=67) (idum,j=1,nblock) ntypes = 4 allocate(itype(np)) read(iunit,iostat=ierr,end=67) (itype(j),j=1,nblock) npartoftype(:,i) = 0 do j=1,np jtype = itype(j) !--map types from code types to splash types select case(jtype) case(1) jtype = 2 ! boundary case(2) jtype = 1 ! water case default ! unknown jtype = 4 end select iamtype(j,i) = jtype npartoftype(jtype,i) = npartoftype(jtype,i) + 1 enddo deallocate(itype) read(iunit,iostat=ierr,end=67) (dumchar,j=1,nblock) read(iunit,iostat=ierr,end=67) (idum,j=1,nblock) call set_labels do iblock=1,1 read(iunit,iostat=ierr,end=67) nblock read(iunit,iostat=ierr,end=67) idum do j=1,nblock read(iunit,iostat=ierr,end=67) compName(j),unitsys,idum,idum,dum !print*,trim(compName(j)) enddo do j=1,np read(iunit,iostat=ierr,end=67) dum,dat(j,ipr,i),dat(j,ivx,i),dat(j,ivx+1,i),dat(j,1,i),dat(j,2,i) if (abs(dum).lt.tiny(0.)) then npartoftype(iamtype(j,i),i) = npartoftype(iamtype(j,i),i) - 1 ! remove from previous type iamtype(j,i) = 3 npartoftype(3,i) = npartoftype(3,i) + 1 ! add to "box" type endif enddo enddo ! !--fake other properties: density, mass, smoothing length etc. ! masstype(1,i) = 1./npartoftype(1,i) ! !--assume smoothing length to be the max dimension divided by the number of particles^(1/ndim) ! dx = maxval(dat(1:np,1,i)) - minval(dat(1:np,1,i)) dy = maxval(dat(1:np,2,i)) - minval(dat(1:np,2,i)) dat(:,ih,i) = dx/(npartoftype(1,i))**(1./ndim)*(dy/dx) print*,' WARNING: ASSUMING SMOOTHING LENGTH = ',dat(1,ih,i),' AND ARBITRARY PARTICLE MASSES' dat(:,irho,i) = 1. nstepsread = 1 do jtype=1,ntypes write(*,"(' n(',a,') = ',i6)",advance="no") trim(labeltype(jtype)),npartoftype(jtype,i) enddo write(*,*) !read* goto 68 67 continue print "(a)",' > end of file reached <' 68 continue ! !--close data file and return ! close(unit=11) if (debugmode) print*,'DEBUG> Read steps ',indexstart,'->',indexstart + nstepsread - 1, & ' last step ntot = ',sum(npartoftype(:,indexstart+nstepsread-1)) return 80 continue print*,' *** data file empty : no timesteps ***' return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:ix,ivx,ih,irho,ipr,& iamvec,labelvec,label,labeltype use params use settings_data, only:ndim,ndimV,UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = ndim + 1 ipr = ndim + ndimV + 1 irho = ipr + 1 ih = irho + 1 label(ipr) = 'pressure' label(irho) = 'density' label(ih) = 'h' label(ix(1:ndim)) = labelcoord(1:ndim,1) ! !--label vector quantities (e.g. velocity) appropriately ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' ! !--set labels for each type of particles ! labeltype(1) = 'water' labeltype(2) = 'boundary' labeltype(3) = 'box' labeltype(4) = 'unknown' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. UseTypeInRenderings(3) = .false. UseTypeInRenderings(4) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/shapes.f90000644 000766 000000 00000064530 13261626263 015366 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! module which handles plotting of arbitrary shapes ! written by Daniel Price 2008 ! as part of the SPLASH SPH visualisation package !----------------------------------------------------------------- module shapes implicit none integer, parameter, private :: maxshapes = 32 integer, parameter, private :: maxshapetype = 8 integer :: nshapes integer, parameter, private :: lentext = 120 type shapedef integer :: itype integer :: icolour integer :: linestyle integer :: linewidth integer :: ifillstyle integer :: iunits integer :: iplotonpanel real :: xpos real :: ypos real :: xlen,ylen real :: angle,fjust real :: opacity character(len=lentext) :: text end type type(shapedef), dimension(maxshapes), public :: shape character(len=9), dimension(maxshapetype), & parameter, public :: labelshapetype = & (/'square ', & 'rectangle', & 'arrow ', & 'circle ', & 'line ', & 'text ', & 'f(x) ', & 'marker '/) namelist /shapeopts/ nshapes,shape integer, parameter, private :: maxunits = 2 character(len=20), dimension(maxunits), & parameter, private :: labelunits = & (/'units of plot ', & 'viewport coordinates'/) ! 'inches ', & ! 'millimeters ', & ! 'pixels '/) procedure(check_shapes), pointer, private :: checkshapes => null() procedure(add_shape), pointer, private :: addshape => null() procedure(delete_shape), pointer, private :: delshape => null() real, parameter, private :: pi = 4.*atan(1.) contains !----------------------------------------------------------------- ! shape default settings !----------------------------------------------------------------- subroutine defaults_set_shapes implicit none nshapes = 0 shape(:)%itype = 0 shape(:)%icolour = 1 shape(:)%linestyle = 1 shape(:)%linewidth = 1 shape(:)%ifillstyle = 2 shape(:)%iunits = 1 shape(:)%iplotonpanel = 0 shape(:)%xpos = 0.5 shape(:)%ypos = 0.5 shape(:)%xlen = 1. shape(:)%ylen = 1. shape(:)%angle = 0. shape(:)%text = ' ' shape(:)%fjust = 0. shape(:)%opacity = 1. return end subroutine defaults_set_shapes !----------------------------------------------------------------- ! shape submenu !----------------------------------------------------------------- subroutine submenu_shapes() use promptlist, only:prompt_list implicit none checkshapes => check_shapes addshape => add_shape delshape => delete_shape call prompt_list(nshapes,maxshapes,'shape',checkshapes,addshape,delshape) end subroutine submenu_shapes !----------------------------------- ! print the current list of shapes !----------------------------------- subroutine check_shapes(nshape) implicit none integer, intent(in) :: nshape integer :: ishape print "(/,a)", ' Current list of plot annotations:' if (nshape.gt.0) then do ishape=1,nshape call print_shapeinfo(ishape,shape(ishape)%itype,shape(ishape)) enddo else print "(a)",' (none)' endif end subroutine check_shapes !---------------------------------------- ! pretty-print information about a shape !---------------------------------------- subroutine print_shapeinfo(inum,itype,shapein) implicit none integer, intent(in) :: inum,itype type(shapedef), intent(in), optional :: shapein character(len=20), parameter :: fmtstring = "('Shape ',i2,': ',a)" select case(itype) case(1) print "(10x,a)",' --' write(*,fmtstring,advance='no') inum,' | | '//labelshapetype(itype) if (present(shapein)) then print "(5x,es10.2,' x ',es10.2)",shapein%xlen,shapein%ylen else print* endif print "(10x,a)",' --' case(2) print "(10x,a)",' -----' write(*,fmtstring,advance='no') inum,' | | '//labelshapetype(itype) if (present(shapein)) then print "(5x,es10.2,' x ',es10.2)",shapein%xlen,shapein%ylen else print* endif print "(10x,a)",' -----' case(3) print "(10x,a)" write(*,fmtstring,advance='no') inum,' ------> '//labelshapetype(itype) if (present(shapein)) then print "(6x,'length = ',es10.2,', angle = ',f5.1,' deg.')",& shapein%xlen,shapein%angle else print* endif print "(10x,a)" case(4) print "(10x,a)",' ___ ' print "(10x,a)",' / \ ' write(*,fmtstring,advance='no') inum,' ( ) '//labelshapetype(itype) if (present(shapein)) then print "(6x,'radius = ',es10.2)",& shapein%xlen else print* endif print "(10x,a)",' \___/ ' case(5) print "(10x,a)" write(*,fmtstring,advance='no') inum,' -------- '//labelshapetype(itype) if (present(shapein)) then print "(6x,'length = ',es10.2)",shapein%xlen else print* endif print "(10x,a)" case(6) print "(10x,a)", ' ' write(*,fmtstring,advance='no') inum,' TEXT ' if (present(shapein)) then print "('""',a,'""')",trim(shapein%text) else print* endif print "(10x,a)", ' ' case(7) print "(10x,a)",' _ / ' write(*,fmtstring,advance='no') inum,' / \ / '//trim(labelshapetype(itype)) if (present(shapein)) then print "(' = ',a)",trim(shapein%text) else print* endif print "(10x,a)",'/ \_/ ' case(8) write(*,fmtstring,advance='no') inum,' (x) '//labelshapetype(itype) print "(a)" case default print "(a)" write(*,fmtstring,advance='no') inum,' '//labelshapetype(itype) print "(a)" end select end subroutine print_shapeinfo !------------------------------------------ ! utility routine to add new shape object !------------------------------------------ subroutine add_shape(istart,iend,nshape) use params, only:maxplot use prompting, only:prompt use exactfunction, only:check_function use plotlib, only:plotlib_maxlinestyle,plotlib_maxlinecolour,plotlib_maxfillstyle,plotlib_supports_alpha implicit none integer, intent(in) :: istart,iend integer, intent(inout) :: nshape integer :: i,ishape,itype,indexi,iunits,ierr,itry character(len=10) :: poslabel character(len=80) :: string itype = 1 ishape = istart + 1 if (ishape.gt.maxshapes) then print "(/,a,i2,a)",' *** Error, maximum number of shapes (',maxshapes,') reached, cannot add any more.' print "(a)", ' *** If you hit this limit, *please email me* so I can change the default limits!' print "(a)", ' *** (and then edit shapes.f90, changing the parameter "maxshapes" to something higher...)' return endif ! !--fill prompt string with list of shapes ! indexi = 1 do i=1,maxshapetype if (i.lt.10) then write(string(indexi:),"(1x,i1,') ',a)") i,trim(labelshapetype(i)) else write(string(indexi:),"(1x,i2,') ',a)") i,trim(labelshapetype(i)) endif indexi = len_trim(string) + 1 enddo print "(/,a)",trim(string) !print "(i2,a)",(i,') '//trim(labelshapetype(i)),i=1,maxshapetype) over_shapes: do while(ishape.le.iend .and. i.le.maxshapes) if (istart.eq.0 .or. shape(ishape)%itype.le.0 .or. shape(ishape)%itype.gt.maxshapetype) then call prompt('choose an object type (0=none) ',shape(ishape)%itype,0,maxshapetype) endif itype = shape(ishape)%itype if (itype.eq.0) then call delete_shape(ishape,nshape) exit over_shapes else call print_shapeinfo(ishape,itype) if (itype.eq.7) then shape(ishape)%iunits = 1 else !--choose units do i=1,maxunits print "(i1,')',1x,a)",i,trim(labelunits(i)) enddo call prompt('enter units to use for plotting shape',shape(ishape)%iunits,0,maxunits) endif iunits = shape(ishape)%iunits select case(itype) case(1) ! square call prompt('enter length of side (in '//trim(labelunits(iunits))//')',shape(ishape)%xlen,0.) shape(ishape)%ylen = shape(ishape)%xlen poslabel = ' centre' case(2) ! rectangle call prompt('enter x position of left edge (in '//trim(labelunits(iunits))//')',shape(ishape)%xpos,0.) call prompt('enter x position of right edge (in '//trim(labelunits(iunits))//')',shape(ishape)%xlen,0.) call prompt('enter y position of bottom edge (in '//trim(labelunits(iunits))//')',shape(ishape)%ypos,0.) call prompt('enter y position of top edge (in '//trim(labelunits(iunits))//')',shape(ishape)%ylen,0.) poslabel = '' case(3) ! arrow call prompt('enter arrow length (in '//trim(labelunits(iunits))//')',shape(ishape)%xlen,0.) call prompt('enter angle in degrees (0 = horizontal) ',shape(ishape)%angle) call prompt('enter justification factor (0.0=tail at x,y 1.0=head at x,y)',shape(ishape)%fjust) poslabel = '' case(4) ! circle call prompt('enter radius (in '//trim(labelunits(iunits))//')',shape(ishape)%xlen,0.) poslabel = ' centre' case(5) ! line call prompt('enter line length (in '//trim(labelunits(iunits))//')',shape(ishape)%xlen,0.) call prompt('enter angle of line in degrees (0.0 = horizontal) ',shape(ishape)%angle) poslabel = ' starting' case(6) ! text call prompt('enter text string ',shape(ishape)%text) call prompt('enter angle for text in degrees (0 = horizontal) ',shape(ishape)%angle) call prompt('enter justification factor (0.0=left 1.0=right)',shape(ishape)%fjust) poslabel = ' starting' case(7) ! arbitrary function ierr = 1 itry = 1 do while(ierr /= 0 .and. itry.le.3) if (itry.gt.1) print "(a,i1,a)",'attempt ',itry,' of 3:' print "(a,6(/,11x,a),/)",' Examples: sin(2*pi*x)','sqrt(0.5*x)','x^2', & 'exp(-2*x**2)','log10(x/2)','exp(p),p=sin(pi*x)','cos(z/d),z=acos(d),d=x^2' call prompt('enter function f(x) to plot ',shape(ishape)%text) call check_function(shape(ishape)%text,ierr) if (ierr /= 0 .and. len(shape(ishape)%text).eq.len_trim(shape(ishape)%text)) then print "(a,i3,a)",' (errors are probably because string is too long, max length = ',len(shape(ishape)%text),')' endif itry = itry + 1 enddo if (ierr.ne.0) then print "(a)",' *** too many tries, aborting ***' ishape = ishape - 1 cycle over_shapes endif poslabel = ' starting' case(8) print "(/,' Marker options (for all from -8->31, see plot library userguide):',12(/,i2,') ',a))", & 0,'square',1,'.',2,'+',3,'*',4,'o',5,'x',12,'5-pointed star',17,'bold circle',-8,'large bold circle' call prompt('Enter marker type ',shape(ishape)%ifillstyle) poslabel = '' end select if (itype.ne.7 .and. itype.ne.2) then call prompt('enter'//trim(poslabel)//' x position (in '//trim(labelunits(iunits))//') ',shape(ishape)%xpos) call prompt('enter'//trim(poslabel)//' y position (in '//trim(labelunits(iunits))//') ',shape(ishape)%ypos) elseif (itype.eq.7) then call prompt('enter xmin for line segment (0=ignore)',shape(ishape)%xpos) call prompt('enter xmax for line segment (0=ignore)',shape(ishape)%xlen) endif if (itype.eq.1 .or. itype.eq.2 .or. itype.eq.4) then call prompt('enter fill style (1=solid,2=outline,3=hatch,4=crosshatch) for '// & trim(labelshapetype(itype)),shape(ishape)%ifillstyle,0,plotlib_maxfillstyle) endif if (itype.ne.6 .and. itype.ne.8) then call prompt('enter line style (1=solid,2=dash,3=dotdash,4=dot,5=dashdot) for '// & trim(labelshapetype(itype)),shape(ishape)%linestyle,0,plotlib_maxlinestyle) endif if (itype.eq.6 .or. itype.eq.8) then call prompt('enter character height for '//trim(labelshapetype(itype)),shape(ishape)%xlen,0.,10.) else call prompt('enter line width for '//trim(labelshapetype(itype)),shape(ishape)%linewidth,0) endif call prompt('enter '//trim(labelshapetype(itype))//' colour (0=background, 1=foreground, 2-16=plot lib colour indices)', & shape(ishape)%icolour,0,plotlib_maxlinecolour) if (plotlib_supports_alpha) then call prompt('enter '//trim(labelshapetype(itype))//' opacity (0.0-1.0)', & shape(ishape)%opacity,0.,1.) endif print "(/,' 0 : plot on every panel ',/,"// & "' -1 : plot on first row only ',/,"// & "' -2 : plot on first column only ',/,"// & "' n : plot on nth panel only ')" !--make sure the current setting falls within the allowed bounds if (shape(ishape)%iplotonpanel.lt.-2 .or. & shape(ishape)%iplotonpanel.gt.maxplot) shape(:)%iplotonpanel = 0 call prompt('Enter selection ',shape(ishape)%iplotonpanel,-2,maxplot) if (ishape.gt.nshape) nshape = ishape ishape = ishape + 1 endif enddo over_shapes end subroutine add_shape !------------------------------------------ ! utility routine to delete a shape object !------------------------------------------ subroutine delete_shape(ishape,nshape) implicit none integer, intent(in) :: ishape integer, intent(inout) :: nshape integer :: i if (ishape.gt.0 .and. nshape.gt.0 .and. ishape.le.maxshapes) then do i=ishape+1,nshape shape(i-1) = shape(i) enddo print "(a)",'> deleted shape: '//trim(labelshapetype(shape(ishape)%itype)) !--restore defaults shape(nshape)%itype = 0 shape(nshape)%icolour = 1 shape(nshape)%linestyle = 1 shape(nshape)%linewidth = 1 shape(nshape)%ifillstyle = 2 shape(nshape)%iunits = 1 shape(nshape)%iplotonpanel = 0 shape(nshape)%xpos = 0.5 shape(nshape)%ypos = 0.5 shape(nshape)%xlen = 1. shape(nshape)%ylen = 1. shape(nshape)%angle = 0. shape(nshape)%text = ' ' shape(nshape)%fjust = 0. nshape = nshape - 1 endif end subroutine delete_shape !------------------------------------------------------------ ! actual routine that implements plotting of various shapes !------------------------------------------------------------ subroutine plot_shapes(ipanel,irow,icolumn,itransx,itransy,time) use exactfunction, only:exact_function use transforms, only:transform_inverse,transform use asciiutils, only:string_replace use plotlib, only:plot_qci,plot_qls,plot_qlw,plot_qfs,plot_qwin,plot_sci,plot_sfs,plot_slw, & plot_sci,plot_rect,plot_sls,plot_line,plot_arro,plot_circ,plot_ptxt,plot_numb,& plotlib_supports_alpha,plot_set_opacity,plot_pt1,plot_sch,plot_qch implicit none integer, intent(in) :: ipanel,irow,icolumn,itransx,itransy real, intent(in) :: time integer :: icolourprev,linestyleprev,linewidthprev,ifillstyle integer :: i,j,ierr,iplotonthispanel,ndec,nc integer, parameter :: maxfuncpts = 1000 real :: xmin,xmax,ymin,ymax,dxplot,dyplot,charheightprev real :: xpos,ypos,xlen,ylen,anglerad,dx,dy,fjust,xmini,xmaxi real, dimension(2) :: xline,yline real, dimension(maxfuncpts) :: xfunc,yfunc character(len=lentext) :: text character(len=30) :: string ! !--store current settings ! call plot_qci(icolourprev) call plot_qls(linestyleprev) call plot_qlw(linewidthprev) call plot_qfs(ifillstyle) call plot_qch(charheightprev) ! !--convert hpos and vpos to x, y to plot arrow ! call plot_qwin(xmin,xmax,ymin,ymax) dxplot = xmax - xmin dyplot = ymax - ymin ! !--query window size in a variety of other units ! do i=1,nshapes iplotonthispanel = shape(i)%iplotonpanel if (iplotonthispanel.eq.0 & .or.(iplotonthispanel.gt.0 .and. ipanel.eq.iplotonthispanel) & .or.(iplotonthispanel.eq.-1 .and. irow.eq.1) & .or.(iplotonthispanel.eq.-2 .and. icolumn.eq.1)) then call plot_sci(shape(i)%icolour) call plot_sls(shape(i)%linestyle) if (shape(i)%itype==6 .or. shape(i)%itype==8) call plot_sch(shape(i)%xlen) call plot_slw(shape(i)%linewidth) call plot_sfs(shape(i)%ifillstyle) if (plotlib_supports_alpha) call plot_set_opacity(shape(i)%opacity) anglerad = shape(i)%angle*(pi/180.) call convert_units(shape(i),xpos,ypos,xlen,ylen, & xmin,ymin,dxplot,dyplot,itransx,itransy) !call print_shapeinfo(i,shape(i)%itype,shape(i)) !print "(a)",'> plotting shape: '//trim(labelshapetype(shape(i)%itype)) select case(shape(i)%itype) case(1,2) ! square, rectangle if (xlen.gt.dxplot .or. ylen.gt.dyplot) then print "(2x,a)",'Error: shape size exceeds plot dimensions: not plotted' else if (shape(i)%itype==1) then call plot_rect(xpos-0.5*xlen,xpos+0.5*xlen,ypos-0.5*ylen,ypos + 0.5*ylen) else call plot_rect(xpos,xlen,ypos,ylen) endif endif case(3) ! arrow dx = xlen*cos(anglerad) dy = xlen*sin(anglerad) !--do not plot if length > size of plot if (dx.gt.dxplot .or. dy.gt.dyplot) then print "(2x,a)",'Error: arrow length exceeds plot dimensions: arrow not plotted' else fjust = shape(i)%fjust call plot_arro(xpos-fjust*dx,ypos-fjust*dy,xpos+(1.-fjust)*dx,ypos+(1.-fjust)*dy) endif case(4) ! circle if (xlen.gt.dxplot .or. xlen.gt.dyplot) then print "(2x,a)",'Error: circle radius exceeds plot dimensions: circle not plotted' else call plot_circ(xpos,ypos,xlen) if (shape(i)%ifillstyle > 2) then call plot_sfs(2) ! also plot outline if fill style is hatched call plot_circ(xpos,ypos,xlen) endif endif case(5) ! line xline(1) = xpos yline(1) = ypos xline(2) = xpos + xlen*cos(anglerad) yline(2) = ypos + xlen*sin(anglerad) call plot_line(2,xline,yline) case(6) ! text text = trim(shape(i)%text) !--handle special characters in text strings (e.g. replace %t with time) if (index(text,'%t').ne.0) then ndec = 3 call plot_numb(nint(time/10.**(int(log10(time)-ndec))),int(log10(time)-ndec),1,string,nc) call string_replace(text,'%t',string(1:nc)) endif call plot_ptxt(xpos,ypos,shape(i)%angle,shape(i)%fjust,trim(text)) case(7) ! arbitrary function !--set x to be evenly spaced in transformed (plot) coordinates xmini = xmin xmaxi = xmax if (abs(shape(i)%xpos) > 0.) xmini = shape(i)%xpos if (abs(shape(i)%xlen) > 0.) xmaxi = shape(i)%xlen dx = (xmaxi-xmini)/real(maxfuncpts - 1) do j=1,maxfuncpts xfunc(j) = xmini + (j-1)*dx enddo !--transform x array back to untransformed space to evaluate f(x) if (itransx.gt.0) call transform_inverse(xfunc,itransx) call exact_function(shape(i)%text,xfunc,yfunc,0.,ierr) if (ierr.eq.0) then !--reset x values do j=1,maxfuncpts xfunc(j) = xmini + (j-1)*dx enddo !--transform y if necessary if (itransy.gt.0) call transform(yfunc,itransy) !--plot the line call plot_line(maxfuncpts,xfunc,yfunc) endif case(8) ! marker call plot_pt1(xpos,ypos,shape(i)%ifillstyle) end select endif enddo call plot_sci(icolourprev) call plot_sls(linestyleprev) call plot_slw(linewidthprev) call plot_sfs(ifillstyle) call plot_sch(charheightprev) if (plotlib_supports_alpha) call plot_set_opacity(1.0) end subroutine plot_shapes !------------------------------------------------------------ ! query function asking whether or not a point falls within ! a shape object !------------------------------------------------------------ integer function inshape(xpt,ypt,itransx,itransy) use plotlib, only:plot_qwin,plot_qtxt implicit none real, intent(in) :: xpt,ypt integer, intent(in) :: itransx,itransy integer :: i real :: xpos,ypos,xlen,ylen real :: xmin,ymin,xmax,ymax,dxplot,dyplot real, dimension(4) :: xbox,ybox call plot_qwin(xmin,xmax,ymin,ymax) dxplot = xmax - xmin dyplot = ymax - ymin inshape = 0 do i=1,nshapes call convert_units(shape(i),xpos,ypos,xlen,ylen, & xmin,ymin,dxplot,dyplot,itransx,itransy) select case(shape(i)%itype) case(1,2) ! square, rectangle case(3) ! arrow case(4) ! circle case(5) ! line case(6) ! text call plot_qtxt(xpos,ypos,shape(i)%angle,shape(i)%fjust,trim(shape(i)%text),xbox,ybox) if (xpt.gt.minval(xbox) .and. xpt.le.maxval(xbox) & .and. ypt.gt.minval(ybox) .and. ypt.le.maxval(ybox)) then inshape = i endif end select enddo end function inshape !--------------------------------------- ! routine to edit shapes interactively !--------------------------------------- subroutine edit_shape(i,xpt,ypt,itransx,itransy) use plotlib, only:plot_qwin implicit none integer, intent(in) :: i,itransx,itransy real, intent(in) :: xpt,ypt real :: xmin,xmax,ymin,ymax,dxplot,dyplot,xlen,ylen real :: xpos,ypos call plot_qwin(xmin,xmax,ymin,ymax) dxplot = xmax - xmin dyplot = ymax - ymin call convert_units(shape(i),xpos,ypos,xlen,ylen, & xmin,ymin,dxplot,dyplot,itransx,itransy) select case(shape(i)%itype) case(6) call edit_textbox(xpos,ypos,shape(i)%angle,shape(i)%text) case default end select end subroutine edit_shape !-------------------------------------------------------- ! utility routine to add a new text shape interactively !-------------------------------------------------------- subroutine add_textshape(xpt,ypt,itransx,itransy,ipanel,ierr) use plotlib, only:plot_qwin implicit none real, intent(in) :: xpt,ypt integer, intent(in) :: itransx,itransy,ipanel integer, intent(out) :: ierr integer :: i real :: xmin,xmax,ymin,ymax,xposi,yposi ierr = 0 nshapes = nshapes + 1 if (nshapes.gt.maxshapes) then print*,' *** cannot add shape: array limits reached, delete some shapes first ***' nshapes = maxshapes ierr = 1 return endif i = nshapes shape(i)%itype = 6 print*,' adding shape '//trim(labelshapetype(shape(i)%itype)) shape(i)%icolour = 1 shape(i)%linestyle = 1 shape(i)%linewidth = 1 shape(i)%ifillstyle = 2 shape(i)%iplotonpanel = ipanel ! !--position text relative to viewport ! shape(i)%iunits = 2 call plot_qwin(xmin,xmax,ymin,ymax) xposi = (xpt - xmin)/(xmax-xmin) yposi = (ypt - ymin)/(ymax-ymin) shape(i)%xpos = xposi shape(i)%ypos = yposi shape(i)%xlen = 1. shape(i)%ylen = 1. shape(i)%angle = 0. shape(i)%text = 'click to edit' shape(i)%fjust = 0. call edit_shape(i,xposi,yposi,itransx,itransy) end subroutine add_textshape !----------------------------------------------------------------- ! utility routine to convert between units used in shape plotting !----------------------------------------------------------------- subroutine convert_units(shape,xpos,ypos,xlen,ylen,xmin,ymin,dxplot,dyplot,itransx,itransy) use transforms, only:transform implicit none type(shapedef), intent(in) :: shape real, intent(out) :: xpos,ypos,xlen,ylen real, intent(in) :: xmin,ymin,dxplot,dyplot integer, intent(in) :: itransx,itransy xpos = shape%xpos ypos = shape%ypos xlen = shape%xlen ylen = shape%ylen select case(shape%iunits) case(2) ! translate from viewport coordinates into plot coordinates xpos = xmin + xpos*dxplot ypos = ymin + ypos*dyplot xlen = xlen*dxplot ylen = ylen*dyplot case(1) if (itransx.gt.0) then call transform(xpos,itransx) call transform(xlen,itransx) endif if (itransy.gt.0) then call transform(ypos,itransy) call transform(ylen,itransy) endif ! do nothing here case default ! should never happen print "(a)",' INTERNAL ERROR: unknown units whilst plotting shape' end select end subroutine convert_units !-------------------------------------------------------- ! utility routine to edit a text object interactively !-------------------------------------------------------- subroutine edit_textbox(xpt,ypt,angle,string) use plotlib, only:plot_stbg,plot_ptxt,plot_curs implicit none real, intent(in) :: xpt,ypt,angle character(len=1) :: mychar real :: xpt2,ypt2 character(len=*), intent(inout) :: string character(len=len(string)) :: oldstring integer :: i,ierr print*,'editing text box, esc or ctrl-c to quit' call plot_stbg(0) mychar = ' ' oldstring = string i = max(len_trim(string)+1,1) call plot_ptxt(xpt,ypt,angle,0.,string(1:i)//'_') xpt2 = xpt ypt2 = ypt ierr = plot_curs(xpt2,ypt2,mychar) do while (mychar.ne.achar(13) & ! carriage return .and. mychar.ne.achar(27) & ! ctrl-c .and. mychar.ne.achar(3)) ! esc if (mychar.eq.achar(8)) then ! backspace i = max(i - 1,1) string(i:i) = '_' call plot_ptxt(xpt,ypt,angle,0.,string(1:i)) string(i:i) = ' ' else if (trim(string).eq.'click to edit') then !print*,'erasing string' string = ' ' i = 1 endif string(i:i) = mychar call plot_ptxt(xpt,ypt,angle,0.,string(1:i)) i = min(i + 1,len(string)) if (i.eq.len(string)) print*,' reached end of string' endif ierr = plot_curs(xpt2,ypt2,mychar) enddo !--if ctrl-c or esc, restore original string if (mychar.eq.achar(3) .or. mychar.eq.achar(27)) then string = oldstring print*,'cancelled' else print*,'done: text = "'//trim(string)//'"' endif call plot_stbg(-1) end subroutine edit_textbox end module shapes splash/src/read_data_sphysics.f90000644 000766 000000 00000021642 13261626263 017731 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! the data is stored in the global array dat ! ! THIS VERSION FOR THE DUAL SPHYSICS CODE ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- !--local module to store header information so we can later set the labels module sphysicsdata use params use labels, only:lenlabel implicit none end module sphysicsdata subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:npartoftype,masstype,time,gamma,dat,maxpart,maxstep,maxcol,iamtype use params use filenames, only:nfiles use settings_data, only:ndim,ndimV,ncolumns,ncalc, & buffer_data,iverbose,debugmode,ntypes use mem_allocation, only:alloc use labels, only:ipr,ivx,ih,irho,labeltype implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+4) :: datfile integer :: i,ierr,iunit,j,iblock integer :: npart_max,nstep_max integer, dimension(:), allocatable :: itype character(len=20) :: geomfile character(len=7) :: keyword character(len=70) :: title character(len=1) :: dumchar character(len=16) :: unitsys integer :: istep,jtype,np,ione,kk,idum,nblock real(kind=sing_prec) :: version,timesingle,dum real(kind=doub_prec) :: versiond,timedbl,dumd real :: timein,dx,dy logical :: singleprecision iunit = 11 ! file unit number nstepsread = 0 if (rootname(1:1).ne.' ') then datfile = trim(rootname) else print*,' **** no data read **** ' return endif if (iverbose.ge.1) print "(1x,a)",'reading Dual SPHysics format' write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ndim = 2 ndimV = 2 ! !--open data file and read data ! open(unit=iunit,iostat=ierr,file=datfile,status='old',form='unformatted',access='stream') if (ierr /= 0) then print*,' *** Error opening '//trim(datfile)//' ***' return endif ! !--read first header line ! read(iunit,iostat=ierr,end=80) keyword read(iunit,iostat=ierr,end=80) version read(iunit,iostat=ierr,end=80) title read(iunit,iostat=ierr,end=80) istep read(iunit,iostat=ierr,end=80) timesingle timein = timesingle read(iunit,iostat=ierr,end=80) np read(iunit,iostat=ierr,end=80) ione if (ierr /= 0 .or. np <= 0 .or. timesingle.lt.0.) then ! !--try single precision ! rewind(iunit) read(iunit,iostat=ierr,end=80) keyword read(iunit,iostat=ierr,end=80) versiond version = versiond read(iunit,iostat=ierr,end=80) title read(iunit,iostat=ierr,end=80) istep read(iunit,iostat=ierr,end=80) timedbl timein = timedbl read(iunit,iostat=ierr,end=80) np read(iunit,iostat=ierr,end=80) ione singleprecision = .false. if (ierr /= 0 .or. np < 0 .or. timedbl < 0.) then print "(a)",' *** Error reading first header ***' close(iunit) return endif endif print "(a,f4.2)",' keyword = '//trim(keyword)//' version = ',version !print "(a)",' title = '//trim(title) print "(a,i6,a,es10.3,a,i6)",' step = ',istep,' time = ',timein,' np = ',np ! !--allocate memory for data arrays ! if (buffer_data) then nstep_max = max(nfiles,maxstep,indexstart) else nstep_max = max(1,maxstep,indexstart) endif npart_max = max(int(1.1*np),maxpart) ncolumns = 7 if (.not.allocated(dat) .or. npart_max.gt.maxpart & .or. nstep_max.gt.maxstep .or. ncolumns+ncalc.gt.maxcol) then call alloc(npart_max,nstep_max,ncolumns+ncalc,mixedtypes=.true.) endif i = indexstart nstepsread = 0 time(i) = timein gamma(i) = 5./3. npartoftype(1,i) = np do j=1,np read(iunit,iostat=ierr,end=67) idum,(dat(j,kk,i),kk=1,2),dum enddo read(iunit,iostat=ierr,end=67) nblock read(iunit,iostat=ierr,end=67) (idum,j=1,nblock) ntypes = 4 allocate(itype(np)) read(iunit,iostat=ierr,end=67) (itype(j),j=1,nblock) npartoftype(:,i) = 0 do j=1,np jtype = itype(j) !--map types from code types to splash types select case(jtype) case(1) jtype = 2 ! boundary case(2) jtype = 1 ! water case default ! unknown jtype = 4 end select iamtype(j,i) = jtype npartoftype(jtype,i) = npartoftype(jtype,i) + 1 enddo deallocate(itype) read(iunit,iostat=ierr,end=67) (dumchar,j=1,nblock) read(iunit,iostat=ierr,end=67) (idum,j=1,nblock) call set_labels do iblock=1,1 read(iunit,iostat=ierr,end=67) nblock read(iunit,iostat=ierr,end=67) idum do j=1,nblock read(iunit,iostat=ierr,end=67) compName(j),unitsys,idum,idum,dum !print*,trim(compName(j)) enddo do j=1,np read(iunit,iostat=ierr,end=67) dum,dat(j,ipr,i),dat(j,ivx,i),dat(j,ivx+1,i),dat(j,1,i),dat(j,2,i) if (abs(dum).lt.tiny(0.)) then npartoftype(iamtype(j,i),i) = npartoftype(iamtype(j,i),i) - 1 ! remove from previous type iamtype(j,i) = 3 npartoftype(3,i) = npartoftype(3,i) + 1 ! add to "box" type endif enddo enddo ! !--fake other properties: density, mass, smoothing length etc. ! masstype(1,i) = 1./npartoftype(1,i) ! !--assume smoothing length to be the max dimension divided by the number of particles^(1/ndim) ! dx = maxval(dat(1:np,1,i)) - minval(dat(1:np,1,i)) dy = maxval(dat(1:np,2,i)) - minval(dat(1:np,2,i)) dat(:,ih,i) = dx/(npartoftype(1,i))**(1./ndim)*(dy/dx) print*,' WARNING: ASSUMING SMOOTHING LENGTH = ',dat(1,ih,i),' AND ARBITRARY PARTICLE MASSES' dat(:,irho,i) = 1. nstepsread = 1 do jtype=1,ntypes write(*,"(' n(',a,') = ',i6)",advance="no") trim(labeltype(jtype)),npartoftype(jtype,i) enddo write(*,*) !read* goto 68 67 continue print "(a)",' > end of file reached <' 68 continue ! !--close data file and return ! close(unit=11) if (debugmode) print*,'DEBUG> Read steps ',indexstart,'->',indexstart + nstepsread - 1, & ' last step ntot = ',sum(npartoftype(:,indexstart+nstepsread-1)) return 80 continue print*,' *** data file empty : no timesteps ***' return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:ix,ivx,ih,irho,ipr,& iamvec,labelvec,label,labeltype use params use settings_data, only:ndim,ndimV,UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = ndim + 1 ipr = ndim + ndimV + 1 irho = ipr + 1 ih = irho + 1 label(ipr) = 'pressure' label(irho) = 'density' label(ih) = 'h' label(ix(1:ndim)) = labelcoord(1:ndim,1) ! !--label vector quantities (e.g. velocity) appropriately ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' ! !--set labels for each type of particles ! labeltype(1) = 'water' labeltype(2) = 'boundary' labeltype(3) = 'box' labeltype(4) = 'unknown' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. UseTypeInRenderings(3) = .false. UseTypeInRenderings(4) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_snsph_utils.c000644 000766 000000 00000005127 13261626263 020263 0ustar00dpricewheel000000 000000 #include #include #include #include #include #include #include typedef struct { double x, y, z; float vx, vy, vz; float mass; float u; float rho; float h; } body; void readsdf_(char *filename, int *len, float *dat, int *maxpart, int *maxcol, int *ierr) { int xconf, yconf, zconf, vxconf, vyconf, vzconf, mconf, uconf, rhoconf, hconf; int gnobj, nobj, i; body *p; SDF *sdfp; char fname[128]; strncpy(fname, filename, *len); fname[*len] = '\0'; if ( (sdfp = SDFopen(NULL, fname)) == NULL ) { fprintf(stderr, "%s: %s", fname, SDFerrstring); exit(2); } SDFread(sdfp, (void **)(&p), &gnobj, &nobj, sizeof(body), "x", offsetof(body, x), &xconf, "y", offsetof(body, y), &yconf, "z", offsetof(body, z), &zconf, "vx", offsetof(body, vx), &vxconf, "vy", offsetof(body, vy), &vyconf, "vz", offsetof(body, vz), &vzconf, "mass", offsetof(body, mass), &mconf, "u", offsetof(body, u), &uconf, "rho", offsetof(body, rho), &rhoconf, "h", offsetof(body, h), &hconf, NULL); SDFclose(sdfp); if (!xconf || !yconf || !zconf || !vxconf || !vyconf || !vzconf || !mconf || !uconf || !rhoconf || !hconf) { fprintf(stderr, "No %s%s%s%s%s%s%s%s%s%s in %s\n", (xconf==0)? "x " : "", (yconf==0)? "y " : "", (zconf==0)? "z " : "", (vxconf==0)? "vx " : "", (vyconf==0)? "vy " : "", (vzconf==0)? "vz " : "", (mconf==0)? "m " : "", (uconf==0)? "u " : "", (rhoconf==0)? "rho " : "", (hconf==0)? "h " : "", fname); exit(3); } printf("nobj = %d\n", nobj); for(i = 0; i < nobj; ++i) { dat[i] = p[i].x; dat[(*maxpart)+i] = p[i].y; dat[2* (*maxpart)+i] = p[i].z; dat[3* (*maxpart)+i] = p[i].vx; dat[4* (*maxpart)+i] = p[i].vy; dat[5* (*maxpart)+i] = p[i].vz; dat[6* (*maxpart)+i] = p[i].mass; dat[7* (*maxpart)+i] = p[i].u; dat[8* (*maxpart)+i] = p[i].rho; dat[9* (*maxpart)+i] = p[i].h; } Free(p); *ierr = xconf || yconf || zconf || vxconf || vyconf || vzconf || mconf || uconf || rhoconf || hconf; } int getcol_() { return 10; } void getdata_(char *filename, int *len, int *nobj, float *tpos, float *gamma) { SDF *sdfp; char fname[128]; strncpy(fname, filename, *len); fname[*len] = '\0'; if ( (sdfp = SDFopen(NULL, fname)) == NULL ) { fprintf(stderr, "%s: %s", fname, SDFerrstring); exit(2); } SDFgetintOrDie(sdfp, "npart", nobj); SDFgetfloatOrDie(sdfp, "tpos", tpos); SDFgetfloatOrDie(sdfp, "gamma", gamma); SDFclose(sdfp); } splash/src/dataread_utils.f90000644 000766 000000 00000012222 13261626263 017057 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------- ! ! utility routines used during data read ! !----------------------------------------------------------- module dataread_utils use params, only:doub_prec,int1 implicit none public :: check_range,count_types integer, private :: iverbose_level = 1 ! can be changed private ! generic interface check_range interface check_range module procedure check_range_int, check_range_intarr,check_range_double end interface check_range contains !--------------------------------------------- ! set verboseness for remaining routines !--------------------------------------------- subroutine set_check_range_verboseness(ilevel) integer, intent(in) :: ilevel iverbose_level = ilevel end subroutine set_check_range_verboseness !----------------------------------------------- ! standardised print statement for range errors !----------------------------------------------- subroutine handle_range_error(tag,string,ierror) character(len=*), intent(in) :: tag,string integer, intent(inout) :: ierror if (iverbose_level > 0) then print "(1x,5a)",'ERROR: ',trim(tag),' value of ',trim(string),' out of range' endif ierror = ierror + 1 end subroutine handle_range_error !---------------------------------------------------- ! check that an integer is within a prescribed range !---------------------------------------------------- subroutine check_range_int(ivar,tag,min,max,err) integer, intent(in) :: ivar character(len=*), intent(in) :: tag integer, intent(in), optional :: min,max integer, intent(out), optional :: err integer :: ierror character(len=12) :: string write(string,"(i12)") ivar string = trim(adjustl(string)) ierror = 0 if (present(min)) then if (ivar < min) call handle_range_error(tag,string,ierror) endif if (present(max)) then if (ivar > max) call handle_range_error(tag,string,ierror) endif if (present(err)) then err = ierror endif end subroutine check_range_int !------------------------------------------------------------------------ ! check that all values of an integer array is within a prescribed range !------------------------------------------------------------------------ subroutine check_range_intarr(ivar,tag,min,max,err) integer, intent(in) :: ivar(:) character(len=*), intent(in) :: tag integer, intent(in), optional :: min,max integer, intent(out), optional :: err integer :: ierror,i character(len=12) :: string ierror = 0 do i=1,size(ivar) write(string,"(i12)") ivar(i) string = trim(adjustl(string)) if (present(min)) then if (ivar(i) < min) call handle_range_error(tag,string,ierror) endif if (present(max)) then if (ivar(i) > max) call handle_range_error(tag,string,ierror) endif enddo if (present(err)) then err = ierror endif end subroutine check_range_intarr !---------------------------------------------------- ! check that a real*8 is within a prescribed range !---------------------------------------------------- subroutine check_range_double(dvar,tag,min,max,err) real(doub_prec), intent(in) :: dvar character(len=*), intent(in) :: tag real(doub_prec), intent(in), optional :: min,max integer, intent(out), optional :: err integer :: ierror character(len=12) :: string write(string,"(1pg12.3)") dvar string = trim(adjustl(string)) ierror = 0 if (present(min)) then if (dvar < min) call handle_range_error(tag,string,ierror) endif if (present(max)) then if (dvar > max) call handle_range_error(tag,string,ierror) endif if (present(err)) then err = ierror endif end subroutine check_range_double !---------------------------------------------------- ! count particles of a given type !---------------------------------------------------- subroutine count_types(np,iamtype,noftype,nunknown) integer, intent(in) :: np integer(kind=int1), intent(in) :: iamtype(:) integer, intent(out) :: noftype(:),nunknown integer :: i,itype noftype(:) = 0 if (size(iamtype).ge.np) then do i=1,np itype = iamtype(i) if (itype.gt.0 .and. itype.le.size(noftype)) then noftype(itype) = noftype(itype) + 1 else nunknown = nunknown + 1 endif enddo endif end subroutine count_types end module dataread_utils splash/src/interpolate3D_xsec.f90000644 000766 000000 00000027125 13261626263 017641 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! ! Module containing all of the routines required for cross sections ! through 3D data ! !---------------------------------------------------------------------- module xsections3D use kernels, only:cnormk3D,radkernel,radkernel2,wfunc implicit none public :: interpolate3D_fastxsec, interpolate3D_xsec_vec contains !-------------------------------------------------------------------------- ! ! ** In this version 3D data is interpolated to a single 2D cross section ! ** This is much faster than interpolating to a 3D grid ! ** and is efficient if only one or two cross sections are needed. ! ! ** Note that the cross section is always taken in the z co-ordinate ! ** so should submit the appropriate arrays as x, y and z. ! ! Input: particle coordinates : x,y,z (npart) ! particle masses : pmass (npart) ! density on particles : rho (npart) - must be computed separately ! smoothing lengths : hh (npart) - could be computed from density ! scalar data to smooth : dat (npart) ! cross section location: zslice ! ! Output: smoothed data : datsmooth (npixx,npixy) ! ! Daniel Price, Institute of Astronomy, Cambridge, 23/9/03 !-------------------------------------------------------------------------- subroutine interpolate3D_fastxsec(x,y,z,hh,weight,dat,itype,npart,& xmin,ymin,zslice,datsmooth,npixx,npixy,pixwidthx,pixwidthy,normalise) implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,z,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy,zslice real, intent(out), dimension(npixx,npixy) :: datsmooth logical, intent(in) :: normalise real, dimension(npixx,npixy) :: datnorm integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax real :: hi,hi1,radkern,q2,wab,const,xi,yi,hi21 real :: termnorm,term,dy,dy2,dz,dz2,ypix,rescalefac real, dimension(npixx) :: dx2i datsmooth = 0. datnorm = 0. if (normalise) then print*,'taking fast cross section (normalised)...',zslice else print*,'taking fast cross section (non-normalised)...',zslice endif if (pixwidthx.le.0. .or. pixwidthy.le.0.) then print*,'interpolate3D_xsec: error: pixel width <= 0' return elseif (npart.le.0) then print*,'interpolate3D_xsec: error: npart = 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate3D_xsec: WARNING: ignoring some or all particles with h < 0' endif const = cnormk3D ! !--renormalise dat array by first element to speed things up ! if (dat(1).gt.tiny(dat)) then rescalefac = dat(1) else rescalefac = 1.0 endif ! !--loop over particles ! over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--set kernel related quantities ! hi = hh(i) if (hi.le.0.) cycle over_parts hi1 = 1./hi hi21 = hi1*hi1 radkern = radkernel*hi ! radius of the smoothing kernel ! !--for each particle, work out distance from the cross section slice. ! dz = zslice - z(i) dz2 = dz**2*hi21 ! !--if this is < 2h then add the particle's contribution to the pixels ! otherwise skip all this and start on the next particle ! if (dz2 .lt. radkernel2) then xi = x(i) yi = y(i) termnorm = const*weight(i) term = termnorm*dat(i)/rescalefac ! !--for each particle work out which pixels it contributes to ! ipixmin = int((xi - radkern - xmin)/pixwidthx) jpixmin = int((yi - radkern - ymin)/pixwidthy) ipixmax = int((xi + radkern - xmin)/pixwidthx) + 1 jpixmax = int((yi + radkern - ymin)/pixwidthy) + 1 if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (jpixmin.lt.1) jpixmin = 1 ! to pixels in the image if (ipixmax.gt.npixx) ipixmax = npixx if (jpixmax.gt.npixy) jpixmax = npixy ! !--precalculate an array of dx2 for this particle (optimisation) ! do ipix=ipixmin,ipixmax dx2i(ipix) = ((xmin + (ipix-0.5)*pixwidthx - xi)**2)*hi21 + dz2 enddo ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax ypix = ymin + (jpix-0.5)*pixwidthy dy = ypix - yi dy2 = dy*dy*hi21 do ipix = ipixmin,ipixmax q2 = dx2i(ipix) + dy2 ! !--SPH kernel - standard cubic spline ! if (q2.lt.radkernel2) then wab = wfunc(q2) ! !--calculate data value at this pixel using the summation interpolant ! datsmooth(ipix,jpix) = datsmooth(ipix,jpix) + term*wab if (normalise) datnorm(ipix,jpix) = datnorm(ipix,jpix) + termnorm*wab endif enddo enddo endif ! if particle within 2h of slice enddo over_parts ! over particles ! !--normalise dat array ! if (normalise) then !--normalise everywhere (required if not using SPH weighting) where (datnorm > tiny(datnorm)) datsmooth = datsmooth/datnorm end where endif datsmooth = datsmooth*rescalefac return end subroutine interpolate3D_fastxsec !-------------------------------------------------------------------------- ! program to interpolate from particle data to even grid of pixels ! ! The data is smoothed using the SPH summation interpolant, ! that is, we compute the smoothed array according to ! ! datsmooth(pixel) = sum_b m_b dat_b/rho_b W(r-r_b, h_b) ! ! where _b is the quantity at the neighbouring particle b and ! W is the smoothing kernel, for which we use the usual cubic spline ! ! ** In this version 3D data is interpolated to a single 2D cross section ! ** This is much faster than interpolating to a 3D grid ! ** and is efficient if only one or two cross sections are needed. ! ! ** Note that the cross section is always taken in the z co-ordinate ! ** so should submit the appropriate arrays as x, y and z. ! ! Input: particle coordinates : x,y,z (npart) ! particle masses : pmass (npart) ! density on particles : rho (npart) - must be computed separately ! smoothing lengths : hh (npart) - could be computed from density ! vector data to smooth : vecx (npart) ! vecy (npart) ! cross section location: zslice ! ! Output: smoothed vector field : vecsmoothx (npixx,npixy) ! : vecsmoothy (npixx,npixy) ! ! Daniel Price, Institute of Astronomy, Cambridge, 23/9/03 !-------------------------------------------------------------------------- subroutine interpolate3D_xsec_vec(x,y,z,hh,weight,vecx,vecy,itype,npart,& xmin,ymin,zslice,vecsmoothx,vecsmoothy,npixx,npixy,pixwidthx,pixwidthy,normalise) implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,z,hh,weight,vecx,vecy integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy,zslice real, intent(out), dimension(npixx,npixy) :: vecsmoothx, vecsmoothy logical, intent(in) :: normalise real, dimension(npixx,npixy) :: datnorm integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax real :: hi,hi1,radkern,q2,wab,const real :: termx,termy,termnorm,dx,dy,dz,dz2,xpix,ypix vecsmoothx = 0. vecsmoothy = 0. datnorm = 0. if (normalise) then print*,'taking fast cross section (normalised)...',zslice else print*,'taking fast cross section (non-normalised)...',zslice endif if (pixwidthx.le.0. .or. pixwidthy.le.0.) then print*,'interpolate3D_xsec_vec: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate3D_xsec_vec: WARNING: ignoring some or all particles with h < 0' endif const = cnormk3D ! normalisation constant (3D) ! !--loop over particles ! over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--set kernel related quantities ! hi = hh(i) if (hi.le.0.) cycle over_parts hi1 = 1./hi radkern = radkernel*hi ! radius of the smoothing kernel ! !--for each particle, work out distance from the cross section slice. ! dz = zslice - z(i) dz2 = dz**2 ! !--if this is < 2h then add the particle's contribution to the pixels ! otherwise skip all this and start on the next particle ! if (abs(dz) .lt. radkern) then termnorm = const*weight(i) termx = termnorm*vecx(i) termy = termnorm*vecy(i) ! !--for each particle work out which pixels it contributes to ! ipixmin = int((x(i) - radkern - xmin)/pixwidthx) jpixmin = int((y(i) - radkern - ymin)/pixwidthy) ipixmax = int((x(i) + radkern - xmin)/pixwidthx) + 1 jpixmax = int((y(i) + radkern - ymin)/pixwidthy) + 1 if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (jpixmin.lt.1) jpixmin = 1 ! to pixels in the image if (ipixmax.gt.npixx) ipixmax = npixx if (jpixmax.gt.npixy) jpixmax = npixy ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax ypix = ymin + (jpix-0.5)*pixwidthy dy = ypix - y(i) do ipix = ipixmin,ipixmax xpix = xmin + (ipix-0.5)*pixwidthx dx = xpix - x(i) q2 = (dx*dx + dy*dy + dz2)*hi1*hi1 ! !--SPH kernel - standard cubic spline ! if (q2.lt.radkernel2) then wab = wfunc(q2) ! !--calculate data value at this pixel using the summation interpolant ! vecsmoothx(ipix,jpix) = vecsmoothx(ipix,jpix) + termx*wab vecsmoothy(ipix,jpix) = vecsmoothy(ipix,jpix) + termy*wab if (normalise) datnorm(ipix,jpix) = datnorm(ipix,jpix) + termnorm*wab endif enddo enddo endif ! if particle within 2h of slice enddo over_parts ! over particles ! !--normalise dat array(s) ! if (normalise) then where (datnorm > tiny(datnorm)) vecsmoothx = vecsmoothx/datnorm vecsmoothy = vecsmoothy/datnorm end where endif return end subroutine interpolate3D_xsec_vec end module xsections3D splash/src/read_data_cactus_hdf5_futils.f90000644 000766 000000 00000017510 13261626263 021641 0ustar00dpricewheel000000 000000 !------------------------------------------------------------------------- ! ! Utility routines for the cactus hdf5 data read ! Module contains interface routines to c functions ! that perform the actual calls to the HDF5 libs ! !------------------------------------------------------------------------- module cactushdf5read use params, only:maxplot use labels, only:lenlabel use, intrinsic :: iso_c_binding, only:c_int,c_double,c_char implicit none character(len=lenlabel), dimension(maxplot) :: blocklabel character(len=130) :: datfileprev = ' ' logical :: file_is_open = .false. integer :: ntoti_prev,ncol_prev,nstep_prev interface subroutine open_cactus_hdf5_file(filename,istep,npart,ncol,nstep_max,ndim,ndimV,time,ignoretl,ierr) bind(c) import character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(in), value :: istep,ignoretl integer(kind=c_int), intent(out) :: npart,ncol,nstep_max,ndim,ndimV,ierr real(kind=c_double), intent(out) :: time end subroutine open_cactus_hdf5_file subroutine read_cactus_hdf5_data(filename,istep,npart,time,dx,ignoretl,ierr) bind(c) import character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(in), value :: istep,ignoretl integer(kind=c_int), intent(out) :: npart,ierr real(kind=c_double), intent(out) :: time,dx end subroutine read_cactus_hdf5_data subroutine close_cactus_hdf5_file(ierr) bind(c) import integer(kind=c_int), intent(out) :: ierr end subroutine close_cactus_hdf5_file end interface contains !------------------------------------------------------------------------- ! ! The following routines are callback routines called by the c ! utilities ! !------------------------------------------------------------------------- subroutine set_blocklabel(icol,name,lenname) bind(c) use, intrinsic :: iso_c_binding, only:c_int, c_char ! use cactushdf5read, only:blocklabel use asciiutils, only:fstring implicit none integer(kind=c_int), intent(in) :: icol,lenname character(kind=c_char), intent(in) :: name(lenname) character(len=24) :: temp integer :: ivar temp = fstring(name) ivar = index(temp,'::') if (ivar > 0) temp = temp(ivar+2:) if (icol <= size(blocklabel)) then blocklabel(icol) = trim(temp) else print*,'ERROR - too many columns in file' endif !print*,icol,' name = ',trim(blocklabel(icol)) end subroutine set_blocklabel subroutine sort_cactus_data(n,iter,iorder) bind(c) use, intrinsic :: iso_c_binding, only:c_int use sort, only:indexxi implicit none integer(kind=c_int), intent(in) :: n integer(kind=c_int), intent(in) :: iter(n) integer(kind=c_int), intent(out) :: iorder(n) call indexxi(n,iter,iorder) end subroutine sort_cactus_data !------------------------------------------------------------------------- ! ! The following routines compute useful things, e.g. tr K ! !------------------------------------------------------------------------- subroutine calc_trK(gxxd,gxyd,gxzd,gyyd,gyzd,gzzd,kxxd,kxyd,kxzd,kyyd,kyzd,kzzd,trk) ! ! Subroutine to calculate trace K ( trK = g^{ij} K_{ij} ) ! Takes g_{ij} and K_{ij} at one position and time, returns trK ! real, intent(in) :: gxxd,gxyd,gxzd,gyyd,gyzd,gzzd ! spatial down metric components real, intent(in) :: kxxd,kxyd,kxzd,kyyd,kyzd,kzzd ! down extrinsic curvature components real :: gxxu,gxyu,gxzu,gyyu,gyzu,gzzu ! spatial up metric components real, intent(out) :: trk real, dimension(3,3) :: gijd, giju ! 4x4 metric down and up respectively real :: det ! down components gijd(1,1) = gxxd gijd(1,2) = gxyd gijd(1,3) = gxzd gijd(2,1) = gxyd gijd(2,2) = gyyd gijd(2,3) = gyzd gijd(3,1) = gxzd gijd(3,2) = gyzd gijd(3,3) = gzzd call inv3x3(gijd,giju,det) ! up (inverse) components gxxu = giju(1,1) gxyu = giju(1,2) gxzu = giju(1,3) gyyu = giju(2,2) gyzu = giju(2,3) gzzu = giju(3,3) trk = (gxxu * kxxd) + (2. * gxyu * kxyd) + (2. * gxzu * kxzd) + & & (gyyu * kyyd) + (2. * gyzu * kyzd) + (gzzu * kzzd) end subroutine calc_trK pure subroutine inv3x3(A,B,det) real, intent(in), dimension(3,3) :: A real, intent(out), dimension(3,3) :: B ! inverse matrix real, intent(out) :: det det = A(1,1)*A(2,2)*A(3,3) - A(1,1)*A(2,3)*A(3,2) - & & A(1,2)*A(2,1)*A(3,3) + A(1,2)*A(2,3)*A(3,1) + & & A(1,3)*A(2,1)*A(3,2) - A(1,3)*A(2,2)*A(3,1) B(1,1) = A(2,2)*A(3,3) - A(2,3)*A(3,2) B(2,1) = A(2,3)*A(3,1) - A(2,1)*A(3,3) B(3,1) = A(2,1)*A(3,2) - A(2,2)*A(3,1) B(1,2) = A(1,3)*A(3,2) - A(1,2)*A(3,3) B(2,2) = A(1,1)*A(3,3) - A(1,3)*A(3,1) B(3,2) = A(1,2)*A(3,1) - A(1,1)*A(3,2) B(1,3) = A(1,2)*A(2,3) - A(1,3)*A(2,2) B(2,3) = A(1,3)*A(2,1) - A(1,1)*A(2,3) B(3,3) = A(1,1)*A(2,2) - A(1,2)*A(2,1) B(:,:) = B(:,:)/det end subroutine inv3x3 ! ! find location of metric and extrinsic curvature in columns ! subroutine find_metric(ncols,labelcol,igxx,igxy,igxz,igyy,igyz,igzz,ikxx,ikxy,ikxz,ikyy,ikyz,ikzz,& irho,ialp,ivel0,ivel1,ivel2,iHam,iM1,iM2,iM3,gotrho) integer, intent(in) :: ncols character(len=*), intent(in) :: labelcol(ncols) integer, intent(out) :: igxx,igxy,igxz,igyy,igyz,igzz integer, intent(out) :: ikxx,ikxy,ikxz,ikyy,ikyz,ikzz integer, intent(out) :: irho,ialp,ivel0,ivel1,ivel2 integer, intent(out) :: iHam,iM1,iM2,iM3 integer :: i, idens logical, intent(out) :: gotrho gotrho = .False. igxx = 0; igxy = 0; igxz = 0; igyy = 0; igyz = 0; igzz = 0 ikxx = 0; ikxy = 0; ikxz = 0; ikyy = 0; ikyz = 0; ikzz = 0 irho = 0; ialp = 0; ivel0 = 0; ivel1 = 0; ivel2 = 0 iHam = 0; iM1 = 0; iM2 = 0; iM3 = 0 do i=1,ncols select case(labelcol(i)) case('gxx') igxx = i case('gxy') igxy = i case('gxz') igxz = i case('gyy') igyy = i case('gyz') igyz = i case('gzz') igzz = i case('kxx') ikxx = i case('kxy') ikxy = i case('kxz') ikxz = i case('kyy') ikyy = i case('kyz') ikyz = i case('kzz') ikzz = i case('rho') irho = i gotrho = .True. case('dens') idens = i case('alp') ialp = i case('vel[0]') ivel0 = i case('vel[1]') ivel1 = i case('vel[2]') ivel2 = i case('H') iHam = i case('M1') iM1 = i case('M2') iM2 = i case('M3') iM3 = i end select enddo ! if we didn't find 'rho' in cols, use dens instead if (gotrho .eqv. .False.) irho = idens end subroutine find_metric subroutine compute_extra_columns(ncols,nextra,dat) integer, intent(in) :: ncols integer, intent(out) :: nextra real, intent(inout), optional :: dat(:,:) integer :: i,n,itrk integer :: igxx,igxy,igxz,igyy,igyz,igzz integer :: ikxx,ikxy,ikxz,ikyy,ikyz,ikzz integer :: irho,ialp,ivel0,ivel1,ivel2 integer :: iHam,iM1,iM2,iM3 logical :: gotrho nextra = 0 call find_metric(ncols,blocklabel,igxx,igxy,igxz,igyy,igyz,igzz,ikxx,ikxy,ikxz,ikyy,ikyz,ikzz,& irho,ialp,ivel0,ivel1,ivel2,iHam,iM1,iM2,iM3,gotrho) n = size(dat(:,1)) if (igxx > 0 .and. igxy > 0 .and. igxz > 0 .and. igyy > 0 .and. igyz > 0 .and. igzz > 0 .and. & ikxx > 0 .and. ikxy > 0 .and. ikxz > 0 .and. ikyy > 0 .and. ikyz > 0 .and. ikzz > 0) then itrk = ncols + 1 blocklabel(itrk) = 'tr K' nextra = 1 if (present(dat)) then !print*,' getting trk ',igxx,igxy,igxz,igyy,igyz,igzz,ikxx,ikxy,ikxz,ikyy,ikyz,ikzz,itrk do i=1,n call calc_trK(dat(i,igxx),dat(i,igxy),dat(i,igxz),dat(i,igyy),dat(i,igyz),dat(i,igzz),& dat(i,ikxx),dat(i,ikxy),dat(i,ikxy),dat(i,ikyy),dat(i,ikyz),dat(i,ikzz),dat(i,itrk)) enddo endif endif end subroutine compute_extra_columns end module cactushdf5read splash/src/plotstep.f90000644 000766 000000 00000544406 13261626263 015762 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------ ! ! This is the core routine for the whole code. ! Drives the plotting pipeline, ie. calls all the routines which ! do the work. ! ! I am gradually trying to make this routine more modular... ! !------------------------------------------------------------------------ module timestep_plotting use params, only:maxplot implicit none integer, private :: ninterp integer, private :: iplotx,iploty,iplotz,irender,irenderplot,icontourplot integer, private :: ivectorplot,ivecx,ivecy integer, private :: nyplots,npartdim,nyplotfirstonpage,ifirststeponpage integer, private :: ngrid,nframefirstonpage integer, private :: just,ntitles,nsteplegendlines integer, private :: iplots,ipanel integer, private :: iframesave integer, private :: npixx,npixy,npixz real, dimension(:), allocatable, private :: datpix1D, xgrid real, dimension(:,:), allocatable, private :: datpix,datpixcont,brightness real, dimension(:,:,:), allocatable, private :: datpix3D,datpixcont3D real, private :: xmin,xmax,ymin,ymax,zmin real, private :: rendermin,rendermax,vecmax,contmin,contmax real, private :: dz,zslicepos,zobservertemp,dzscreentemp,taupartdepthtemp,rkappafac real, private :: dxgrid,xmingrid,xmaxgrid real, private :: angletempx, angletempy, angletempz !--buffer for interactive mode on multiplots integer, dimension(maxplot) :: iplotxtemp,iplotytemp,irendertemp,icontourtemp,ivecplottemp real, dimension(maxplot) :: xminmulti,xmaxmulti,xminadapt,xmaxadapt real, dimension(maxplot) :: vptxmin,vptxmax,vptymin,vptymax,barwmulti real, private :: xminadapti,xmaxadapti,yminadapti,ymaxadapti,renderminadapt,rendermaxadapt real, private :: contminadapt,contmaxadapt real, parameter, private :: pi = 3.1415926536 logical, private :: iplotpart,iplotcont,x_sec,isamexaxis,isameyaxis,iamrendering,idoingvecplot logical, private :: inewpage, tile_plots, lastplot logical, private :: imulti,irerender,iAllowspaceforcolourbar,ihavesetweights logical, private :: interactivereplot,ihavesetcolours,vectordevice,gotcontours logical, private :: OneColourBarPerRow public :: initialise_plotting, plotstep private contains ! ! initialise plotting options ! called once for all steps ! subroutine initialise_plotting(ipicky,ipickx,irender_nomulti,icontour_nomulti,ivecplot) use params use colours, only:colour_set use labels, only:label,ipowerspec,ih,ipmass,irho,iamvec,isurfdens, & is_coord,itoomre,iutherm,ipdf,ix,icolpixmap,ivx, & idustfrac,idustfracsum,ideltav, & ideltavsum,get_z_dir use limits, only:lim,rangeset use multiplot, only:multiplotx,multiploty,irendermulti,icontourmulti, & nyplotmulti,x_secmulti,ivecplotmulti use prompting, only:prompt use titles, only:read_titles,read_steplegend use settings_data, only:ndim,ndimV,numplot,ncolumns,ncalc,ndataplots,required, & icoords,icoordsnew,debugmode,ntypes,usetypeinrenderings, & ndusttypes,idustfrac_plot,ideltav_plot,fakedust use settings_page, only:nacross,ndown,ipapersize,tile,papersizex,aspectratio,& iPageColours,iadapt,iadaptcoords,linewidth,linepalette,device,nomenu,& interactive,ipapersizeunits,usecolumnorder,colourpalette,maxc use pagecolours, only:set_pagecolours,set_linecolours use settings_part, only:linecolourthisstep,linecolour,linestylethisstep,linestyle,iexact,iplotpartoftype use settings_render, only:icolours,iplotcont_nomulti,iColourBarStyle,icolour_particles use settings_xsecrot, only:xsec_nomulti,xsecpos_nomulti,flythru,nxsec,irotate, & xseclineX1,xseclineX2,xseclineY1,xseclineY2,xsecwidth, & use3Dperspective,use3Dopacityrendering,zobserver,dzscreenfromobserver,taupartdepth use settings_powerspec, only:options_powerspec,options_pdf use particle_data, only:npartoftype,masstype use projections3D, only:coltable use plotlib, only:plot_init,plot_qcur,plot_slw,plot_env,plot_curs,plot_band, & plot_close,plot_qinf use system_utils, only:renvironment use calcquantities, only:get_calc_data_dependencies use filenames, only:coloursfile implicit none real, parameter :: pi=3.1415926536 integer, intent(in) :: ipicky,ipickx,irender_nomulti,icontour_nomulti,ivecplot integer :: i,j,ifirst,iplotzprev,ilen,ierr,irow,ntries logical :: iadapting,icoordplot,iallrendered,ians real :: hav,pmassav,dzsuggest integer, dimension(:), allocatable :: ifirstinrow character(len=1) :: char character(len=20) :: devstring !------------------------------------------------------------------------ ! initialisations ! should initialise all saved variables here !------------------------------------------------------------------------ isamexaxis = .true. ! same x axis on all plots? (only relevant for >1 plots per page) isameyaxis = .true. ! same y axis on all plots? tile_plots = .false. iplots = 0 ! counter for how many plots have been plotted in total ipanel = 0 ! counter for which panel we are in on plotting page irender = 0 irenderplot = 0 ivectorplot = 0 x_sec = xsec_nomulti iplotcont = iplotcont_nomulti lastplot = .false. iplotpart = .true. irerender = .false. interactivereplot = .false. nyplotfirstonpage = 1 ! should be unnecessary, but to be on the safe side ifirststeponpage = 1 ! again, should be unnecessary nframefirstonpage = 1 iplotxtemp(:) = 1 ! this is just to be safe, so any spurious array access iplotytemp(:) = 2 ! does not give an out-of-bounds error irendertemp(:) = 0 ivecplottemp(:) = 0 xmin = 0. xmax = 0. ymin = 0. ymax = 0. rendermin = 0. rendermax = 0. contmin = 0. contmax = 0. vecmax = 0. xminadapt = huge(xminadapt) xmaxadapt = -huge(xmaxadapt) renderminadapt = huge(renderminadapt) rendermaxadapt = -huge(rendermaxadapt) contminadapt = huge(contminadapt) contmaxadapt = -huge(contmaxadapt) !xminpagemargin = renvironment('SPLASH_MARGIN_XMIN') !xmaxpagemargin = renvironment('SPLASH_MARGIN_XMAX') !yminpagemargin = renvironment('SPLASH_MARGIN_YMIN') !ymaxpagemargin = renvironment('SPLASH_MARGIN_YMAX') if (ndim.eq.1) x_sec = .false. ! can't have xsec in 1D nxsec = 1 iamrendering = .false. idoingvecplot = .false. if (ipicky.eq.numplot+1) then ! multiplot imulti = .true. nyplots = nyplotmulti iplotx = 0 ! set these to zero by default for multiplots iploty = 0 ! (they should not be used in that case) ! !--if doing multiplot can only take a single cross section slice ! flythru = .false. nxsec = 1 ! !--work out whether to tile plots and make labelling decisions ! if (any(multiplotx(1:nyplotmulti).ne.multiplotx(1))) isamexaxis = .false. if (any(multiploty(1:nyplotmulti).ne.multiploty(1))) then isameyaxis = .false. if (any(multiploty(1:nyplotmulti).eq.icolpixmap)) then isameyaxis = .true. do i=1,nyplotmulti if (.not.(multiploty(i).eq.icolpixmap .or. multiploty(i).eq.multiploty(1))) isameyaxis = .false. enddo endif endif if (any(irendermulti(1:nyplotmulti).gt.0)) iamrendering = .true. if (any(x_secmulti(1:nyplotmulti))) x_sec = .true. if (any(ivecplotmulti(1:nyplotmulti).gt.0)) idoingvecplot = .true. else ! !--or else set number of plots = 1 and use ipicky and ipickx ! imulti = .false. nyplots = 1 iploty = ipicky iplotx = ipickx if (irender_nomulti.gt.0) iamrendering = .true. if (ivecplot.gt.0) idoingvecplot = .true. endif !------------------------------------------------------------------------ ! initialise options to be set before plotting ! !--determine z coordinate for 3D plots ! icoordplot = is_coord(iploty,ndim) .and. is_coord(iplotx,ndim) iallrendered = iamrendering iplotz = 0 if (imulti) then do i=1,nyplotmulti if (is_coord(multiplotx(i),ndim) .and. is_coord(multiploty(i),ndim)) then icoordplot = .true. !--this check is to see if any co-ordinate plots involve just particles ! (if so need to initialise the cross section slice width) if (irendermulti(i).le.0) iallrendered = .false. iplotzprev = iplotz !!--work out coordinate that is not being plotted on cross-section/ 3D plots iplotz = 0 if (ndim.ge.3 .and. (x_sec .or. use3Dperspective .or. irotate)) then iplotz = get_z_dir(ndim,multiplotx(i),multiploty(i)) !--use only first iplotz in the case of multiple slices ! (only effect is on default values for slice thickness etc below) if (iplotzprev.gt.0) iplotz = iplotzprev endif endif enddo elseif (icoordplot) then !!--work out coordinate that is not being plotted if (ndim.ge.3) then do j=1,ndim if ((iplotx.ne.iploty).and. & (ix(j).ne.iplotx).and.(ix(j).ne.iploty)) iplotz = ix(j) enddo endif endif if (debugmode) print*,'DEBUG: iplotz = ',iplotz ! !--work out whether or not to tile plots on the page ! if plots are coord plots, make tiling decisions based on iadaptcoords ! otherwise use iadapt ! if (icoordplot) then iadapting = iadaptcoords else iadapting = iadapt endif tile_plots = tile .and. (isamexaxis.and.isameyaxis .or. isameyaxis.and.ndown.eq.1 & .or. isamexaxis.and.nacross.eq.1) .and. (nacross*ndown.gt.1) !--do not tile if limits are adaptive if (tile_plots .and. (iadapting .or. (iamrendering .and. iadapt .and. iColourbarStyle.gt.0))) then print "(a)",'WARNING: cannot tile plots because limits are set to adaptive' tile_plots = .false. endif !--( a further constraint on plot tiling is required in the case of ! multiple renderings which would involve different colour bars ) OneColourBarPerRow = .false. if (iamrendering .and. icolours.ne.0 .and. iColourbarStyle.gt.0) then !--this option means that a margin is set aside for a colour bar on tiled plots iAllowspaceforcolourbar = .true. !--do not allow tiled plots if multiple (different) colour bars are plotted if (tile_plots) then ifirst = 0 do i=1,nyplots if (irendermulti(i).gt.0 .and. ifirst.eq.0) ifirst = i if (ifirst.gt.0) then if (irendermulti(i).gt.0 .and. irendermulti(i).ne.irendermulti(ifirst)) then tile_plots = .false. endif endif enddo !--this means colour bars are not the same, but we can still tile if ! all the colour bars in each row are the same if (.not.tile_plots .and. mod(nacross*ndown,nyplots).eq.0) then OneColourBarPerRow = .true. allocate(ifirstinrow(ndown)) ifirstinrow(:) = 0 do i=1,nyplots if (usecolumnorder) then irow = (i-1)/nacross + 1 else irow = i - ((i-1)/ndown)*ndown endif if (ifirstinrow(irow).eq.0) ifirstinrow(irow) = i if (irendermulti(i).ne.irendermulti(ifirstinrow(irow))) then OneColourBarPerRow = .false. endif enddo deallocate(ifirstinrow) if (OneColourBarPerRow) tile_plots = .true. endif if (.not.tile_plots) print "(a)",'WARNING: cannot tile plots because of multiple colour bars' endif else iAllowspaceforcolourbar = .false. endif if (icoordplot) then if (x_sec .and. iplotz.gt.0) then ! !--if series of cross sections (flythru), set position of first one ! if (flythru) then print 32,label(iplotz) 32 format('enter number of ',a1,' cross-section slices') read*,nxsec !!--dz is the distance between slices dz = (lim(iplotz,2)-lim(iplotz,1))/float(nxsec) zslicepos = lim(iplotz,1) - 0.5*dz xsecpos_nomulti = zslicepos else ! !--if single cross-section, read position of cross-section slice ! if (.not.imulti) then !--make sure position falls within the limits if (xsecpos_nomulti.lt.lim(iplotz,1) & .or.xsecpos_nomulti.gt.lim(iplotz,2)) then xsecpos_nomulti = (lim(iplotz,2)+lim(iplotz,1))/2. endif call prompt(' enter '//trim(label(iplotz))// & ' position for cross-section slice:', & xsecpos_nomulti,lim(iplotz,1),lim(iplotz,2)) endif ! !--set thickness if plotting particles ! (default thickness is half of the average particle spacing) ! if (.not.iallrendered .or. icolour_particles) then if (xsecwidth.gt.0. .and. xsecwidth.lt.(lim(iplotz,2)-lim(iplotz,1))) then !--already set dzsuggest = xsecwidth else !--xsecwidth not set; suggest a good value npartdim = int(maxval(npartoftype(:,1))**(1./real(ndim))) print*,'average # of particles in each dimension = ',npartdim if (npartdim.gt.0) then dzsuggest = (lim(iplotz,2)-lim(iplotz,1))/float(npartdim) else dzsuggest = 0.01*(lim(iplotz,2)-lim(iplotz,1)) endif endif dz = dzsuggest if (imulti) then call prompt(' enter thickness for cross section slice(s):', & dz,0.0,lim(iplotz,2)-lim(iplotz,1)) else call prompt(' enter thickness of cross section slice:', & dz,0.0,lim(iplotz,2)-lim(iplotz,1)) endif !--if dz has been set from the prompt, save the setting, ! otherwise suggest (possibly different) value again next time if (abs(dz-dzsuggest).gt.tiny(dz)) xsecwidth = dz elseif (ndim.eq.3) then ! !--for rendered cross sections in 3D, set thickness to 10% ! this is the distance slices are moved up and down in interactive mode ! dz = 0.1*(lim(iplotz,2)-lim(iplotz,1)) endif endif ! flythru or single ! !--set up for 1D cross sections through 2D data ! elseif (ndim.eq.2 .and. x_sec) then ians = .false. call prompt('set cross section position interactively?',ians) if (ians) then ! !--set cross section position interactively ! call plot_init('/xw',ierr) call plot_env(lim(1,1),lim(1,2),lim(2,1),lim(2,2),1,0) ierr = plot_curs(xseclineX1,xseclineY1,char) print*,'please select cross section line' ierr = plot_band(1,1,xseclineX1,xseclineY1,xseclineX2,xseclineY2,char) print*,'cross section line: xmin = ',xseclineX1,' xmax = ',xseclineX2 print*,' ymin = ',xseclineY1,' ymax = ',xseclineY2 call plot_close else ! !--set position manually ! if (abs(xseclineX2-xseclineX1).lt.1.e-5 .and. & abs(xseclineY2-xseclineY1).lt.1.e-5) then !--if not already set (ie. if all = 0.0) ! then set default line to diagonal across the domain xseclineX1 = lim(1,1) xseclineX2 = lim(1,2) xseclineY1 = lim(2,1) xseclineY2 = lim(2,2) endif print*,'enter position of cross section through 2D data:' call prompt('enter xmin of cross section line',xseclineX1) call prompt('enter xmax of cross section line',xseclineX2) call prompt('enter ymin of cross section line',xseclineY1) call prompt('enter ymax of cross section line',xseclineY2) endif endif if (iplotz.gt.0 .and. use3Dperspective) then ! !--initialise 3D perspective ! !--set default values if none set if (abs(zobserver).lt.tiny(zobserver)) zobserver = 10.*lim(iplotz,2) if (abs(dzscreenfromobserver).lt.tiny(dzscreenfromobserver)) dzscreenfromobserver = zobserver call prompt('enter z coordinate of observer ',zobserver) dzscreenfromobserver = zobserver ! call prompt('enter distance for unit magnification ',dzscreenfromobserver,0.) ! !--initialise opacity for 3D opacity rendering ! if (use3Dopacityrendering .and. (iamrendering .or. idoingvecplot)) then hav = lim(ih,1) !! 0.5*(lim(ih,2) + lim(ih,1)) if (hav.le.epsilon(hav)) hav = 0.5*lim(ih,2) ! take 0.5*max if min is zero if (ipmass.gt.0) then pmassav = lim(ipmass,1) if (pmassav.le.epsilon(hav)) pmassav = 0.5*lim(ipmass,2) ! take 0.5*max if min is zero else ! handle case where mass is not a data column pmassav = maxval(masstype) do i=1,ntypes if (iplotpartoftype(i) .and. usetypeinrenderings(i) & .and. any(masstype(i,:).gt.0.)) pmassav = min(pmassav,maxval(masstype(i,:))) enddo endif print*,'using current h and pmass limits to calculate kappa (cross section/unit mass)' print*,'min h = ',hav,' min particle mass = ',pmassav print*,'[ kappa = pi*h_min**2/(particle_mass*n_smoothing_lengths) ]' call prompt('enter approximate surface depth (number of smoothing lengths):',taupartdepth,0.) rkappafac = pi*hav*hav/(pmassav*coltable(0)) print*,'kappa (particle cross section per unit mass) = ',rkappafac/taupartdepth endif endif endif !!--prompt for options if plotting power spectrum if (iploty.eq.ipowerspec .and. .not. imulti & .or. (imulti.and.any(multiploty(1:nyplotmulti).eq.ipowerspec))) then call options_powerspec endif !!--prompt for options if plotting PDFs if (iploty.eq.ipdf .and. .not. imulti & .or. (imulti.and.any(multiploty(1:nyplotmulti).eq.ipdf))) then call options_pdf endif !!--for fast data read, set which columns are required from the file ! (note that required(0)= whatever is a valid statement, just has no effect) required = .false. ! by default, no columns required if (debugmode) print*,'DEBUG: imulti = ',imulti,' iamrendering = ',iamrendering ! if (fastdataread) then if (imulti) then required(multiplotx(1:nyplotmulti)) = .true. required(multiploty(1:nyplotmulti)) = .true. required(irendermulti(1:nyplotmulti)) = .true. required(icontourmulti(1:nyplotmulti)) = .true. else if (iploty.ne.icolpixmap) required(iplotx) = .true. required(iploty) = .true. endif required(iplotz) = .true. if ((iamrendering .or. idoingvecplot) .and. & (iploty.ne.icolpixmap .or. imulti .or. iploty.eq.0)) then required(ipmass) = .true. required(irho) = .true. required(ih) = .true. required(irender_nomulti) = .true. required(icontour_nomulti) = .true. endif !!--need to read columns used for range restrictions do i=1,ndataplots if (rangeset(i)) required(i) = .true. enddo !!--need mass for some exact solutions if (iexact.eq.7 .or. iploty.eq.isurfdens) required(ipmass) = .true. if (iploty.eq.itoomre .and. iploty.gt.0) required(iutherm) = .true. !!--only require actual dependencies of calculated quantities if (any(required(ncolumns+1:ncolumns+ncalc))) call get_calc_data_dependencies(required) !if (any(required(ncolumns+1:ncolumns+ncalc))) required = .true. !!--vectors if (imulti) then do i=1,nyplotmulti if (ivecplotmulti(i).gt.0) then required(ivecplotmulti(i):ivecplotmulti(i)+ndimV-1) = .true. endif enddo elseif (ivecplot.gt.0) then required(ivecplot:ivecplot+ndimV-1) = .true. endif !!--if geometry is not default must read all coords !! and if we are plotting a vector component, all components if (icoordsnew.ne.icoords) then required(ix(1:ndim)) = .true. if (iplotx.gt.0 .and. iplotx.le.numplot) then if (iamvec(iplotx).gt.0) required(iamvec(iplotx):iamvec(iplotx)+ndimV-1) = .true. endif if (iploty.gt.0 .and. iploty.le.numplot) then if (iamvec(iploty).gt.0) required(iamvec(iploty):iamvec(iploty)+ndimV-1) = .true. endif endif ! !--one-fluid dependencies ! if (fakedust) then ! !--dependencies for density ! if (required(irho)) then if (ndusttypes>1) then required(idustfracsum) = .true. required(idustfrac_plot) = .true. else required(idustfrac) = .true. endif endif ! !--dependencies for velocity ! if (any(required(ivx:ivx+ndimV-1))) then if (ndusttypes>1) then required(irho) = .true. required(idustfracsum) = .true. required(idustfrac_plot) = .true. required(ideltavsum:ideltavsum+ndimV-1) = .true. required(ideltav_plot:ideltav_plot+ndimV-1) = .true. else required(irho) = .true. required(idustfrac) = .true. required(ideltav:ideltav+ndimV-1) = .true. endif endif endif ! endif if (debugmode) print*,'DEBUG: required(1:ncolumns) = ',required(1:ncolumns+ncalc) !!--read step titles (don't need to store ntitles for this) nsteplegendlines = 0 call read_steplegend(nsteplegendlines) !!--read plot titles ntitles = 0 call read_titles(ntitles) !!------------------------------------------------------------------------ !! initialise the plotting library if (len_trim(device).le.0) then devstring = '?' ! prompt for device else devstring = trim(device) ! device specified on command line endif ierr = 1 ntries = 0 do while(ierr.ne.0) ntries = ntries + 1 if (ipapersize.gt.0 .and. papersizex.gt.0.0 .and. aspectratio.gt.0.0 ) then call plot_init(trim(devstring),ierr,papersizex,aspectratio,ipapersizeunits) else call plot_init(trim(devstring),ierr) ! use default paper size endif !--abort if device specified on command line returns an error if (len_trim(device).gt.0 .and. ierr.ne.0) then ! -ve indicates an error print "(a)",' ERROR: unknown device "'//trim(device)//'"' stop endif if (ierr.ne.0) print "(a)",' ERROR opening plotting device' if (ntries.gt.10) stop enddo !--query whether or not device is interactive if (plot_qcur()) then !--turn menu and interactive mode on if ! interactive device invoked from the command line if (nomenu) then interactive = .true. nomenu = .false. endif !--smoothing length is required if interactive device and coordinate plot if (icoordplot) required(ih) = .true. endif ! set line palette call set_linecolours(linepalette,coloursfile,maxc,colourpalette) !!--set background/foreground colours call set_pagecolours(iPageColours) !!--set colour table ! do this regardless of whether rendering or not if (iamrendering .and. icolours.ne.0) then call colour_set(icolours) ihavesetcolours = .true. else ihavesetcolours = .false. endif !!--determine whether or not the device is vector or not ! this affects the choice of line with (if auto line width is used -- see below) ! and also the automatic resolution determination (should not apply to vector devices) ! call plot_qinf('TYPE',devstring,ilen) select case(devstring(1:ilen)) case('PS','CPS','VPS','VCPS','NULL','LATEX') vectordevice = .true. case default vectordevice = .false. end select !!--set line width (0=auto based on whether device is vector or not) if (linewidth.le.0) then if (vectordevice) then print "(a)",' setting line width = 2 for '//devstring(1:ilen)//' device' call plot_slw(2) else call plot_slw(1) endif else call plot_slw(linewidth) endif linecolourthisstep = linecolour linestylethisstep = linestyle end subroutine initialise_plotting !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Internal subroutines !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! subroutine plotstep(ipos,istep,istepsonpage,irender_nomulti,icontour_nomulti,ivecplot, & iamtype,npartoftype,masstype,dat,timei,gammai,ipagechange,iadvance) use params, only:int1,maxparttypes,doub_prec use colours, only:colour_set use filenames, only:nsteps,rootname,ifileopen,tagline use exact, only:exact_solution,atstar,ctstar,sigma,iPlotExactOnlyOnPanel use toystar1D, only:exact_toystar_ACplane use toystar2D, only:exact_toystar_ACplane2D use labels, only:label,shortlabel,labelvec,iamvec,lenlabel,lenunitslabel,ih,irho,ipmass,ix,iacplane, & ipowerspec,isurfdens,itoomre,ispsound,iutherm,ipdf,icolpixmap,is_coord,labeltype,& labelzintegration,unitslabel,integrate_label,get_sink_type,get_unitlabel_coldens use limits, only:lim,get_particle_subset,lim2,lim2set use multiplot, only:multiplotx,multiploty,irendermulti,ivecplotmulti,itrans, & icontourmulti,x_secmulti,xsecposmulti,iusealltypesmulti,iplotpartoftypemulti use particle_data, only:maxpart,maxcol,icolourme use settings_data, only:numplot,ndataplots,icoords,icoordsnew,ndim,ndimV,nfreq,iRescale, & iendatstep,ntypes,UseTypeInRenderings,itracktype,itrackoffset,& required,ipartialread,xorigin,lowmemorymode,debugmode,iverbose use settings_limits, only:iadapt use settings_part, only:iexact,iplotpartoftype,imarktype,PlotOnRenderings,UseTypeInContours, & iplotline,linecolourthisstep,linestylethisstep,ifastparticleplot, & iploterrbars,ilocerrbars,ismooth_particle_plots use settings_page, only:nacross,ndown,interactive,iaxis,usesquarexy,yscalealt,labelyalt, & charheight,iPlotTitles,vpostitle,hpostitle,fjusttitle,nstepsperpage use settings_render, only:npix,ncontours,icolours,iColourBarStyle,icolour_particles,& inormalise_interpolations,ifastrender,ilabelcont,double_rendering,& projlabelformat,iapplyprojformat use settings_vecplot, only:npixvec,iplotpartvec use settings_xsecrot, only:nxsec,irotateaxes,xsec_nomulti,irotate,flythru,use3Dperspective, & use3Dopacityrendering,writeppm,anglex,angley,anglez,zobserver,& dzscreenfromobserver,taupartdepth,xsecpos_nomulti, & xseclineX1,xseclineX2,xseclineY1,xseclineY2, & nseq,nframes,getsequencepos,insidesequence,rendersinks use settings_powerspec, only:nfreqspec,freqmin,freqmax,ipowerspecx,ipowerspecy,& idisordered,npdfbins use settings_units, only:units,unitzintegration ! !--subroutines called from this routine ! use colourparts use transforms, only:transform,transform_limits,transform_label,transform_inverse,islogged use interactive_routines use part_utils, only:get_tracked_particle,locate_first_two_of_type,get_binary use particleplots, only:particleplot,plot_errorbarsx,plot_errorbarsy use powerspectrums, only:powerspectrum,powerspec3D_sph use interpolations1D, only:interpolate1D use interpolations2D, only:interpolate2D, interpolate2D_xsec, interpolate2D_pixels use interpolations3D, only:interpolate3D use projections3D, only:interpolate3D_projection use projections3Dgeom, only:interpolate3D_proj_geom,interpolate3D_xsec_geom use interpolate3D_opacity, only:interp3D_proj_opacity !,interp3D_proj_opacity_writeppm use xsections3D, only:interpolate3D_fastxsec,interpolate3D_xsec_vec use render, only:render_pix use pagesetup, only:redraw_axes use disc, only:disccalc,discplot use exactfromfile, only:exact_fromfile use exact, only:iexact_rochelobe,use_sink_data,mprim,msec,xprim,xsec use write_pixmap, only:iwritepixmap,writepixmap,write_pixmap_ppm,readpixmap use pdfs, only:pdf_calc,pdf_write use plotutils, only:plotline use geometry, only:coord_is_length use geomutils, only:changecoords,changeveccoords use legends, only:ipanelselect use plotlib, only:plot_sci,plot_page,plot_sch,plot_qci,plot_qls,plot_sls, & plot_line,plot_pt1,plotlib_is_pgplot,plotlib_supports_alpha implicit none integer, intent(inout) :: ipos, istepsonpage integer, intent(in) :: istep,irender_nomulti,icontour_nomulti,ivecplot integer(kind=int1), dimension(:), intent(in) :: iamtype integer, dimension(maxparttypes), intent(in) :: npartoftype real, dimension(maxparttypes), intent(in) :: masstype real, dimension(:,:), intent(in) :: dat real, intent(in) :: timei,gammai logical, intent(in) :: ipagechange integer, intent(inout) :: iadvance logical, dimension(maxparttypes) :: iusetype integer :: ntoti,iz,iseqpos,itrackpart integer :: i,j,k,icolumn,irow integer :: nyplot,nframesloop integer :: irenderpart integer :: npixyvec,nfreqpts,ipixxsec integer :: icolourprev,linestyleprev integer :: ierr,ipt,nplots,nyplotstart,iaxisy,iaxistemp,icol integer :: ivectemp,iamvecx,iamvecy,itransx,itransy,itemp integer :: iframe,isize,isinktype,isink1,isink2 real, parameter :: tol = 1.e-10 ! used to compare real numbers real, parameter :: error_in_log = -666. ! magic number used to flag error with log(0.) real(doub_prec) :: unit_mass, unit_r, unit_u, unit_dz real, dimension(:), allocatable :: xplot,yplot,zplot real, dimension(:), allocatable :: hh,weight real, dimension(:), allocatable :: renderplot real, dimension(:,:), allocatable :: vecplot real :: rkappa real :: zslicemin,zslicemax,dummy,pmassmin,pmassmax,pmassav(1) real :: pixwidth,pixwidthy,pixwidthvec,pixwidthvecy,dxfreq character(len=lenlabel+lenunitslabel) :: labelx,labely,labelz,labelrender,labelvecplot,labelcont character(len=lenunitslabel) :: labeltimeunits character(len=12) :: string logical :: iPlotColourBar, rendering, inormalise, logged, loggedcont logical :: dumxsec, isetrenderlimits, iscoordplot logical :: ichangesize, initx, inity, initz, isameweights, volweightedpdf, got_h logical, parameter :: isperiodicx = .false. ! feature not implemented logical, parameter :: isperiodicy = .false. ! feature not implemented logical, parameter :: isperiodicz = .false. ! feature not implemented logical, dimension(maxparttypes) :: PlotonRender_tmp 34 format (25(' -')) !--set labels to blank (just in case) labelx = ' ' labely = ' ' labelz = ' ' labelrender = ' ' labelvecplot = ' ' ! !--allocate temporary memory required for plotting ! isize = max(maxpart,2000) !--do not allocate the temporary arrays if the dat array has not been allocated if (lowmemorymode .and. maxcol.eq.0) then isize = 2000 endif if (debugmode) print*,'DEBUG: in plotstep, allocating local memory...' ierr = 0 allocate(xplot(isize),stat=ierr) if (ierr /= 0) stop 'out of memory in plotstep allocating temporary x array' allocate(yplot(isize),stat=ierr) if (ierr /= 0) stop 'out of memory in plotstep allocating temporary y array' allocate(zplot(isize),stat=ierr) if (ierr /= 0) stop 'out of memory in plotstep allocating temporary z array' if (allocated(xplot)) xplot = 0. if (allocated(yplot)) yplot = 0. if (allocated(zplot)) zplot = 0. allocate(hh(maxpart),weight(maxpart),stat=ierr) if (ierr /= 0) stop 'out of memory in plotstep allocating temporary h,weight arrays' hh = 0. if (debugmode) print*,'DEBUG: in plotstep, allocated local memory successfully' dummy = 0. labeltimeunits = ' ' dumxsec = .false. isetrenderlimits = .false. k = nxsec ! matters for lastplot in page_setup for non-coord plots if (iReScale) labeltimeunits = unitslabel(0) iaxistemp = iaxis !--set the arrays needed for rendering if they are present if (ih.gt.0 .and. ih.le.ndataplots .and. (required(ih) .or. .not.ipartialread)) then hh(:) = dat(:,ih) got_h = .true. else got_h = .false. endif if (ipmass.gt.0 .and. ipmass.le.ndataplots) then if (required(ipmass)) then pmassmin = minval(dat(:,ipmass)) pmassmax = maxval(dat(:,ipmass)) else pmassmin = 0. pmassmax = 0. endif else pmassmin = minval(masstype,mask=(masstype.gt.0.)) pmassmax = maxval(masstype) pmassav = masstype(1) if (pmassav(1).le.0.) then do i=ntypes,2,-1 if (UseTypeInRenderings(i) .and. iplotpartoftype(i) .and. masstype(i).gt.0.) then pmassav = masstype(i) endif enddo endif endif ! !--set number of particles to use in the interpolation routines ! (by default, only the gas particles) ! ntoti = sum(npartoftype) ninterp = npartoftype(1) if (any(UseTypeInRenderings(2:ntypes).and.iplotpartoftype(2:ntypes)) & .or. size(iamtype).gt.1 .or. (use3Dopacityrendering .and. rendersinks)) ninterp = ntoti !--work out the identity of a particle being tracked if (debugmode) print*,'DEBUG: itracktype = ',itracktype,' itrackoffset = ',itrackoffset itrackpart = get_tracked_particle(itracktype,itrackoffset,npartoftype,iamtype) if (itrackpart.eq.0) then write(string,"(i12)") itrackoffset string = adjustl(string) if (itracktype.gt.0 .and. itracktype.le.ntypes) then print "(/,a,/)",' WARNING: tracked '//trim(labeltype(itracktype))//' particle #'//trim(string)//' not found in data' elseif (itrackoffset.gt.0) then print "(/,a,/)",' WARNING: tracked particle #'//trim(string)//' not found in data' endif else write(string,"(i12)") itrackpart print "(/,a,/)",' Tracking particle #'//trim(adjustl(string)) endif !--non-SPH particle types cannot be used in contours where (.not.UseTypeInRenderings(:)) UseTypeInContours(:) = .false. end where !--check for consistency that if particles are not plotted, ! they are also not plotted on renderings do i=1,ntypes if (.not.iplotpartoftype(i)) PlotOnRenderings(i) = .false. enddo ! !--check whether or not the particle types used for contouring are ! the same as the particle types used for rendering ! isameweights = .true. do i=1,ntypes if (UseTypeInRenderings(i) .and. & .not.(iplotpartoftype(i).eqv.UseTypeInContours(i))) isameweights = .false. enddo ! !--set weight factor for interpolation routines ! ihavesetweights = .false. if (iamrendering .or. idoingvecplot) then if (debugmode) print*,'DEBUG: setting interpolation weights...' call set_weights(weight,dat,iamtype,(iplotpartoftype .and. UseTypeInRenderings)) else if (debugmode) print*,'DEBUG: interpolation weights not set because no rendering...' endif !--set the colour table if it has not been set and particles have been coloured previously if (.not.ihavesetcolours) call colour_set(icolours) ! !--exclude subset of particles if parameter range restrictions are set ! call get_particle_subset(icolourme,dat,ndataplots) ! !--add a loop over frames for animation sequences ! but only generate extra frames if we are inside a sequence ! iseqpos = (ipos-1)/(nacross*ndown) + 1 !print*,' iseqpos = ',iseqpos,ipos iframe = 0 if (nseq.gt.0 .and. insidesequence(iseqpos)) then if (nacross*ndown.eq.1) then nframesloop = nframes else nframesloop = max(iframesave+1,1) iframe = iframesave !print*,'iframe=',iframesave,'iseqpos = ',iseqpos,insidesequence(iseqpos) endif else nframesloop = 1 endif if (debugmode) print*,'DEBUG: starting frame loop...' !--loop over frames: flexible to allow forwards/backwards in interactive mode over_frames: do while (iframe.lt.nframesloop) if (interactivereplot .and. ipos.eq.ifirststeponpage .and. iframe.eq.0) then iframe = min(nframefirstonpage,nframesloop) else iframe = iframe + 1 endif !print*,'iframe = ',iframe, ipagechange,nstepsperpage !--sanity check on frame number, should never happen... if (iframe.eq.0) then print*,' Internal error in iframe, setting to 1 ' iframe = 1 endif !------------------------------------- ! loop over plots per timestep ! (jump to first on the page if replotting in interactivemode) !------------------------------------- if (interactivereplot .and. ipos.eq.ifirststeponpage .and. iframe.eq.nframefirstonpage) then nyplotstart = nyplotfirstonpage ipanel = 0 else nyplotstart = 1 endif over_plots: do nyplot=nyplotstart,nyplots if (nyplot.gt.1 .or. iframe.gt.1) print 34 !--make sure character height is set correctly call plot_sch(charheight) ! in PGPLOT scaled units iPlotColourBar = .false. ! should be false by default until set to true iaxistemp = iaxis !--set current x, y, render and vector plot from multiplot array if (imulti) then iploty = multiploty(nyplot) iplotx = multiplotx(nyplot) irender = irendermulti(nyplot) ivectorplot = ivecplotmulti(nyplot) icontourplot = icontourmulti(nyplot) iplotcont = .false. !iplotcontmulti(nyplot) x_sec = x_secmulti(nyplot) zslicepos = xsecposmulti(nyplot) if (iusealltypesmulti(nyplot)) then iusetype(:) = iplotpartoftype(:) else iusetype(:) = iplotpartoftypemulti(:,nyplot) endif if (irender.gt.0 .or. icontourplot.gt.0 .or. ivectorplot.gt.0) then if (debugmode) print*,'DEBUG: resetting interpolation weights for multiplot...' call set_weights(weight,dat,iamtype,(iusetype .and. UseTypeInRenderings)) endif else if (.not.interactivereplot) irender = irender_nomulti ivectorplot = ivecplot icontourplot = icontour_nomulti iplotcont = .false. !iplotcont_nomulti if (.not.interactivereplot) x_sec = xsec_nomulti if (.not.interactivereplot .and. x_sec) zslicepos = xsecpos_nomulti iusetype(:) = iplotpartoftype(:) endif !--if the contour plot is the same as the rendered plot, ! do not interpolate twice. Instead simply plot the contours ! of the rendered quantity when plotting the render plot. if (irender.gt.0 .and. irender.le.numplot) then if (icontourplot.eq.irender .and. isameweights) then icontourplot = 0 iplotcont = .true. !print "(a)",' contouring same as rendering' elseif (icolours.eq.0) then iplotcont = .true. endif else !--contours not allowed if not rendering ! (because this can be achieved by rendering with colour scheme 0) icontourplot = 0 endif !--flag to indicate that we have actually got the contoured quantity, ! set to true once interpolation has been done. if (.not.interactivereplot .or. irerender) gotcontours = .false. if (icolour_particles) then irenderpart = irender irenderplot = 0 else irenderpart = 0 irenderplot = irender endif if (ivectorplot.gt.0) iplotpart = iplotpartvec !--if replotting in interactive mode, use the temporarily stored plot limits ! (check iplot values are sensible though, otherwise will seg fault here) if (interactivereplot .and. (nacross*ndown.gt.1 .or. iploty.gt.ndataplots)) then if (iplotx.gt.0 .and. iplotx.le.numplot) then xmin = xminmulti(iplotx) xmax = xmaxmulti(iplotx) endif if (iploty.gt.0 .and. iploty.le.numplot) then ymin = xminmulti(iploty) ymax = xmaxmulti(iploty) endif if (irender.gt.0 .and. irender.le.numplot) then rendermin = xminmulti(irender) rendermax = xmaxmulti(irender) if (icontourplot.gt.0 .and. icontourplot.le.numplot) then contmin = xminmulti(icontourplot) contmax = xmaxmulti(icontourplot) endif endif endif !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! initialisation for plots of particle data ! copy from main dat array into xplot, yplot ! also set labels end plot limits !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! iscoordplot = (is_coord(iplotx,ndim) .and. is_coord(iploty,ndim)) iz = 0 if (iscoordplot .and. ndim.ge.3) then do j=1,ndim if ((iplotx.ne.iploty).and. & (ix(j).ne.iplotx).and.(ix(j).ne.iploty)) iz = ix(j) enddo endif iplotz = iz initx = (iplotx.gt.0 .and. iplotx.le.ndataplots) inity = (iploty.gt.0 .and. iploty.le.ndataplots) initz = (iplotz.gt.0 .and. iplotz.le.ndataplots) if (iplotx.gt.0 .and. iplotx.le.numplot) labelx = label(iplotx) if (iploty.gt.0 .and. iploty.le.numplot) labely = label(iploty) if (iplotz.gt.0 .and. iplotz.le.numplot) labelz = label(iplotz) initdataplots: if (initx .or. inity .or. initz) then if (debugmode) print*,'DEBUG: initialising data plots...',initx,inity,iplotx,iploty,ntoti,size(xplot) if (initx) then !--check for errors if (iplotx.gt.size(dat(1,:)) .or. iplotx.lt.1) then print*,'ERROR: Internal error with out-of-bounds x column = ',iplotx exit over_plots endif xplot(1:ntoti) = dat(1:ntoti,iplotx) iamvecx = iamvec(iplotx) else iamvecx = 0 endif if (inity) then !--check for errors if (iploty.gt.size(dat(1,:)) .or. iploty.lt.1) then print*,'ERROR: Internal error with out-of-bounds y column = ',iploty exit over_plots endif yplot(1:ntoti) = dat(1:ntoti,iploty) iamvecy = iamvec(iploty) else iamvecy = 0 endif if (initz) then zplot(1:ntoti) = dat(1:ntoti,iplotz) else zplot = 0. endif if (debugmode) print*,'DEBUG: iplotz = ',iplotz if (debugmode) print*,'DEBUG: setting itrans...' itransx = 0 itransy = 0 if (iplotx.gt.0 .and. iplotx.le.numplot) itransx = itrans(iplotx) if (iploty.gt.0 .and. iploty.le.numplot) itransy = itrans(iploty) zslicemin = -huge(zslicemax) !-- " " zslicemax = huge(zslicemax) if (.not.interactivereplot) then ! if (iplotx.gt.0 .and. iplotx.le.numplot .and. ipos.eq.ifirststeponpage) then if (iplotx.gt.0 .and. iplotx.le.numplot) then xmin = lim(iplotx,1) xmax = lim(iplotx,2) endif ! if (iploty.gt.0 .and. iploty.le.numplot .and. ipos.eq.ifirststeponpage) then if (iploty.gt.0 .and. iploty.le.numplot) then ymin = lim(iploty,1) ymax = lim(iploty,2) endif angletempx = anglex angletempy = angley angletempz = anglez if (ndim.eq.3 .and. use3Dperspective) then dzscreentemp = dzscreenfromobserver zobservertemp = zobserver taupartdepthtemp = taupartdepth else dzscreentemp = 0. zobservertemp = 0. taupartdepthtemp = 0. endif else if (ndim.eq.3 .and. use3Dperspective) dzscreentemp = zobservertemp endif ! !--flag for whether or not we have raw particle plot or not ! (not allowed to use transformations on coords otherwise) ! rendering = (iscoordplot .and.(irenderplot.gt.0 .or. ivectorplot.gt.0) & .and.(.not.icolour_particles)) ! !--change coordinate system if relevant ! if (icoordsnew.ne.icoords) then !--do this if one is a coord but not if rendering call changecoords(iplotx,iploty,iplotz,xplot,yplot,zplot,ntoti,ndim,itrackpart,dat) if (iamvecx.gt.0) call changeveccoords(iplotx,xplot,ntoti,ndim,itrackpart,dat) if (iamvecy.gt.0) call changeveccoords(iploty,yplot,ntoti,ndim,itrackpart,dat) endif !--apply transformations (log, 1/x etc) if appropriate ! also change labels and limits appropriately if (.not.(rendering)) then if (itransx.ne.0) call applytrans(xplot,xmin,xmax,labelx,itransx,'x',iplotx,iaxis,interactivereplot) if (itransy.ne.0) call applytrans(yplot,ymin,ymax,labely,itransy,'y',iploty,iaxis,interactivereplot) endif !--write username, date on plot ! if (nacross.le.2.and.ndown.le.2) call pgiden ! !--adjust plot limits if adaptive plot limits set ! (find minimum/maximum only on particle types actually plotted) ! if (itrackpart.le.0 .and. .not.(iscoordplot .and. irotate)) then if (initx) call adapt_limits(iplotx,xplot,xmin,xmax,xminadapti,xmaxadapti,'x',& iamtype,ntoti,npartoftype,iusetype,ipagechange) if (inity) call adapt_limits(iploty,yplot,ymin,ymax,yminadapti,ymaxadapti,'y',& iamtype,ntoti,npartoftype,iusetype,ipagechange) endif !!-reset co-ordinate plot limits if particle tracking if (itrackpart.gt.0 .and. .not.interactivereplot) then if (initx) call settrackinglimits(itrackpart,iplotx,xplot,xmin,xmax) if (inity) call settrackinglimits(itrackpart,iploty,yplot,ymin,ymax) endif !--override settings based on positions in sequence if (nseq.gt.0) then call getsequencepos(iseqpos,iframe,iplotx,iploty,irender, & angletempx,angletempy,angletempz,zobservertemp,dzscreentemp,taupartdepthtemp,& zslicepos,xmin,xmax,ymin,ymax,rendermin,rendermax,isetrenderlimits) endif !--for 3D perspective, do not plot particles behind the observer if (ndim.eq.3.and.use3Dperspective) then dzscreenfromobserver = zobserver zslicemax = zobservertemp if (use3Dopacityrendering) rkappa = rkappafac/taupartdepthtemp endif endif initdataplots !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! plots with co-ordinates as x and y axis !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (iscoordplot) then if (debugmode) print*,'DEBUG: starting coord plot...' if (.not.interactivereplot .or. irerender) then npixx = npix if (npixx.le.0) npixx = 500 ! default for other uses of npixx if auto pixels are used endif !!--page setup preliminaries if (usesquarexy) then just = 1 ! x and y axis have same scale ! unless 1D xsec through 2D data or non-cartesian if ((irender.gt.ndim .and. ndim.eq.2 .and. x_sec) & .or.(icoordsnew.gt.1 .and. .not.(coord_is_length(iplotx,icoordsnew) & .and. coord_is_length(iploty,icoordsnew)))) then just = 0 endif else just = 0 endif !--work out if colour bar is going to be plotted ! (leave space in page setup if so) iPlotColourBar = .false. if (irender.gt.ndim .and..not.(ndim.eq.2.and.x_sec)) iPlotColourBar = (iColourBarStyle.gt.0) if (.not.interactivereplot) then irerender = .false. endif ! !--rotate the particles about the z (and y and x) axes ! only applies to particle plots at the moment ! if (ndim.ge.2 .and. (irotate .or. (ndim.eq.3 .and.use3Dperspective)) & .and. icoordsnew.eq.1) then if ((irotate .and. (angletempx.gt.tiny(0.) .or. angletempy.gt.tiny(0.))) & .or.(ndim.eq.3 .and.use3Dperspective .and. dzscreentemp.gt.tiny(0.))) then if (iaxis.ge.0) iaxistemp = -3 endif ivectemp = 0 !--for vector plots with rotation, need to allocate temporary ! arrays to hold the rotated vector components if (ivectorplot.gt.0) then ichangesize = .false. if (allocated(vecplot)) then if (size(vecplot(1,:)).lt.ninterp) ichangesize = .true. endif if (.not.allocated(vecplot) .or. ichangesize) then if (allocated(vecplot)) deallocate(vecplot) allocate(vecplot(ndim,ninterp),stat=ierr) if (ierr /= 0) then print "(a)",' ERROR allocating memory for vector plot + rotation ' stop endif endif ivectemp = ivectorplot endif call rotationandperspective(angletempx,angletempy,angletempz,dzscreentemp,zobservertemp, & xplot,yplot,zplot,ntoti,iplotx,iploty,iplotz,dat,ivectemp,vecplot,itrackpart) !--adapt plot limits after rotations have been done if (.not.interactivereplot) then call adapt_limits(iplotx,xplot,xmin,xmax,xminadapti,xmaxadapti,'x',& iamtype,ntoti,npartoftype,iusetype,ipagechange) call adapt_limits(iploty,yplot,ymin,ymax,yminadapti,ymaxadapti,'y',& iamtype,ntoti,npartoftype,iusetype,ipagechange) endif !!-reset co-ordinate plot limits if particle tracking if (itrackpart.gt.0 .and. .not.interactivereplot) then call settrackinglimits(itrackpart,iplotx,xplot,xmin,xmax) call settrackinglimits(itrackpart,iploty,yplot,ymin,ymax) endif endif !------------------------------------------------------------------ ! rendering setup and interpolation (this is the rendering done ! *before* the cross sections are taken, e.g. to 3D grid) !------------------------------------------------------------------ if ((irenderplot.gt.ndim).and. & ((ndim.eq.3).or.(ndim.eq.2.and..not.x_sec))) then !!--determine number of pixels in rendered image (npix = pixels in x direction) if (npix.gt.0) then npixx = npix call page_setup(dummy=.true.) ! do this here in case limits are auto-adjusted pixwidth = (xmax-xmin)/real(npix) if (just.eq.1) then pixwidthy = pixwidth else pixwidthy = pixwidth*(ymax-ymin)/(xmax - xmin) endif else !!--automatically reset the pixel number to match the device call page_setup(dummy=.true.) !--npixx and npixy are determined here pixwidth = (xmax-xmin)/real(npixx) if (just.eq.1) then pixwidthy = pixwidth else pixwidthy = (ymax-ymin)/real(npixy) endif endif npixx = max(int((1. - epsilon(0.))*(xmax-xmin)/pixwidth) + 1,1) npixy = max(int((1. - epsilon(0.))*(ymax-ymin)/pixwidthy) + 1,1) !!--only need z pixels if working with interpolation to 3D grid ! (then number of z pixels is equal to number of cross sections) if ((ndim.ge.3).and.(x_sec.and.nxsec.gt.2)) then zmin = lim(iplotz,1) npixz = nxsec endif if (.not.interactivereplot .or. irerender) then if (allocated(datpix)) then if (npixx.ne.size(datpix(:,1)) .or. npixy.ne.size(datpix(1,:))) then deallocate(datpix) allocate (datpix(npixx,npixy)) if (ndim.eq.3 .and. use3Dperspective .and. use3Dopacityrendering) then if (allocated(brightness)) deallocate(brightness) allocate(brightness(npixx,npixy)) endif if (icontourplot.gt.ndim) then if (allocated(datpixcont)) deallocate(datpixcont) allocate(datpixcont(npixx,npixy)) endif endif else allocate (datpix(npixx,npixy)) if (ndim.eq.3 .and. use3Dperspective .and. use3Dopacityrendering) then if (allocated(brightness)) deallocate(brightness) allocate(brightness(npixx,npixy)) endif if (icontourplot.gt.ndim) then if (allocated(datpixcont)) deallocate(datpixcont) allocate(datpixcont(npixx,npixy)) endif endif select case(ndim) case(2) !!--interpolate to 2D grid !! allocate memory for rendering array if (.not. x_sec) then call interpolate2D(xplot(1:ninterp),yplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot), & icolourme(1:ninterp),ninterp,xmin,ymin,datpix,npixx,npixy, & pixwidth,pixwidthy,inormalise,isperiodicx,isperiodicy) !--also get contour plot data if (icontourplot.gt.0 .and. icontourplot.le.numplot) then if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) call interpolate2D(xplot(1:ninterp),yplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,icontourplot), & icolourme(1:ninterp),ninterp,xmin,ymin,datpixcont,npixx,npixy, & pixwidth,pixwidthy,inormalise,isperiodicx,isperiodicy) gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif endif case(3) !!--interpolation to 3D grid - then take multiple cross sections/projections !! do this if taking more than 2 cross sections, otherwise use fast xsec if (x_sec.and.nxsec.gt.2) then !!--allocate memory for 3D rendering array if (allocated(datpix3D)) deallocate(datpix3D) allocate ( datpix3D(npixx,npixy,npixz) ) !!--interpolate from particles to 3D grid call interpolate3D(xplot(1:ninterp),yplot(1:ninterp), & zplot(1:ninterp),hh(1:ninterp),weight(1:ninterp), & dat(1:ninterp,irenderplot),icolourme(1:ninterp), & ninterp,xmin,ymin,zmin,datpix3D,npixx,npixy,npixz,pixwidth,dz, & inormalise,isperiodicx,isperiodicy,isperiodicz) if (icontourplot.gt.0 .and. icontourplot.le.numplot) then !!--allocate memory for 3D contouring array if (allocated(datpixcont3D)) deallocate(datpixcont3D) allocate ( datpixcont3D(npixx,npixy,npixz) ) if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) !!--interpolate from particles to 3D grid call interpolate3D(xplot(1:ninterp),yplot(1:ninterp), & zplot(1:ninterp),hh(1:ninterp),weight(1:ninterp), & dat(1:ninterp,icontourplot),icolourme(1:ninterp), & ninterp,xmin,ymin,zmin,datpixcont3D,npixx,npixy,npixz,pixwidth,dz, & inormalise,isperiodicx,isperiodicy,isperiodicz) gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif endif end select endif endif ! !--if vector plot determine whether or not to plot the particles as well ! iplotpart = .true. if (ivectorplot.gt.0) iplotpart = iplotpartvec if (irenderplot.gt.0) iplotpart = .false. ! !%%%%%%%%%%%%%%% loop over cross-section slices %%%%%%%%%%%%%%%%%%%%%%% ! over_cross_sections: do k=1,nxsec if (k.gt.1) print 34 if (x_sec) then !!--for multislice cross section (flythru) !! increment the position of the current cross section slice if (flythru) zslicepos = zslicepos + dz !!--for cross sections of particle plots, need range of co-ordinates in which !! particles may lie zslicemin = zslicepos-0.5*dz zslicemax = zslicepos+0.5*dz endif !------------take projections/cross sections through 3D data-----------------! if (irenderplot.gt.0 .and. ndim.eq.3) then !!--allocate memory for 2D rendered array if (.not.interactivereplot) then if (allocated(datpix)) then if (npixx.ne.size(datpix(:,1)) .or. npixy.ne.size(datpix(1,:))) then deallocate(datpix) if (debugmode) print*,'reallocating datpix...' allocate ( datpix(npixx,npixy) ) endif else if (debugmode) print*,'allocating datpix...' allocate ( datpix(npixx,npixy) ) endif if (icontourplot.gt.ndim) then if (allocated(datpixcont)) then if (npixx.ne.size(datpixcont(:,1)) .or. npixy.ne.size(datpixcont(1,:))) then deallocate(datpixcont) if (debugmode) print*,'reallocating datpixcont...' allocate ( datpixcont(npixx,npixy) ) endif else if (debugmode) print*,'allocating datpixcont...' allocate ( datpixcont(npixx,npixy) ) endif endif endif !------------------------------------------------------------------------ ! if we have rendered to a 3D grid, take cross sections from this array !------------------------------------------------------------------------ if (x_sec .and. nxsec.gt.2) then ipixxsec = int(0.99999*(zslicepos-zmin)/dz) + 1 if (ipixxsec.gt.npixz) ipixxsec = npixz print*,TRIM(label(iplotz)),' = ',zslicepos, & ' cross section, pixel ',ipixxsec datpix = datpix3D(:,:,ipixxsec) ! slices are in 3rd dimension if (gotcontours) then datpixcont = datpixcont3D(:,:,ipixxsec) endif else !------------------------------------------------------------------- ! or do a fast projection/cross section of 3D data to 2D array !------------------------------------------------------------------- !--only rerender if absolutely necessary if (.not.interactivereplot .or. irerender) then if (x_sec) then if (use3Dperspective .and. use3Dopacityrendering) then !!--do surface-rendered cross-section with opacity if (iverbose > 0) print*,trim(label(ix(iplotz))),' = ',zslicepos, & ' : opacity-rendered cross section', xmin,ymin if (ipmass.gt.0) then if (icontourplot.gt.0 .and. icontourplot.le.numplot) then if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & dat(1:ninterp,ipmass),ninterp,hh(1:ninterp),weight(1:ninterp),& dat(1:ninterp,icontourplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpixcont,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,zslicepos) gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & dat(1:ninterp,ipmass),ninterp,hh(1:ninterp),weight(1:ninterp), & dat(1:ninterp,irenderplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpix,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,zslicepos) else if (icontourplot.gt.0 .and. icontourplot.le.numplot) then if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & pmassav,1,hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,icontourplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpixcont,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,zslicepos) gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & pmassav,1,hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpix,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,zslicepos) endif elseif (use3Dperspective) then print*,'ERROR: X_SEC WITH 3D PERSPECTIVE NOT IMPLEMENTED' datpix = 0. else !!--do fast cross-section if (iverbose > 0) print*,trim(label(ix(iplotz))),' = ',zslicepos, & ' : fast cross section', xmin,ymin if (icoordsnew.ne.icoords) then call interpolate3D_xsec_geom( & dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot),icolourme(1:ninterp),& ninterp,xmin,ymin,zslicepos,datpix,npixx,npixy,pixwidth, & pixwidthy,inormalise,icoordsnew,iplotx,iploty,iplotz,ix,xorigin) else call interpolate3D_fastxsec( & xplot(1:ninterp),yplot(1:ninterp), & zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,irenderplot),icolourme(1:ninterp), & ninterp,xmin,ymin,zslicepos,datpix,npixx,npixy,pixwidth, & pixwidthy,inormalise) endif !!--same but for contour plot if (icontourplot.gt.0 .and. icontourplot.le.numplot) then if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) if (icoordsnew.ne.icoords) then call interpolate3D_xsec_geom( & dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,icontourplot),icolourme(1:ninterp),& ninterp,xmin,ymin,zslicepos,datpixcont,npixx,npixy,pixwidth, & pixwidthy,inormalise,icoordsnew,iplotx,iploty,iplotz,ix,xorigin) else call interpolate3D_fastxsec( & xplot(1:ninterp),yplot(1:ninterp), & zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,icontourplot),icolourme(1:ninterp), & ninterp,xmin,ymin,zslicepos,datpixcont,npixx,npixy,pixwidth, & pixwidthy,inormalise) endif gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif endif else if (use3Dperspective .and. use3Dopacityrendering) then !!--do fast projection with opacity if (ipmass.gt.0) then !--contour plot first if (icontourplot.gt.0 .and. icontourplot.le.numplot) then if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & dat(1:ninterp,ipmass),ninterp,hh(1:ninterp),weight(1:ninterp),& dat(1:ninterp,icontourplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpixcont,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,huge(zslicepos)) gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & dat(1:ninterp,ipmass),ninterp,hh(1:ninterp),& weight(1:ninterp),dat(1:ninterp,irenderplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpix,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,huge(zslicepos)) else !--do contour plot first so brightness corresponds to render plot if (icontourplot.gt.0 .and. icontourplot.le.numplot) then if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & pmassav,1,hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpixcont,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,huge(zslicepos)) gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif call interp3D_proj_opacity( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & pmassav,1,hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot), & dat(1:ninterp,iz),icolourme(1:ninterp), & ninterp,xmin,ymin,datpix,brightness,npixx,npixy,pixwidth,zobservertemp, & dzscreentemp,rkappa,huge(zslicepos)) endif else !!--do fast projection of z integrated data (e.g. column density) if (icoordsnew.ne.icoords) then call interpolate3D_proj_geom( & dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot), & icolourme(1:ninterp),ninterp,xmin,ymin,datpix,npixx,npixy,pixwidth, & pixwidthy,inormalise,icoordsnew,iplotx,iploty,iplotz,ix,xorigin) else call interpolate3D_projection( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot), & icolourme(1:ninterp),ninterp,xmin,ymin,datpix,npixx,npixy,pixwidth, & pixwidthy,inormalise,zobservertemp,dzscreentemp,ifastrender) endif !!--same but for contour plot if (icontourplot.gt.0 .and. icontourplot.le.numplot) then if (.not.isameweights) & ! set contouring weights as necessary call set_weights(weight,dat,iamtype,UseTypeInContours) if (icoordsnew.ne.icoords) then call interpolate3D_proj_geom( & dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,icontourplot), & icolourme(1:ninterp),ninterp,xmin,ymin,datpixcont,npixx,npixy,pixwidth, & pixwidthy,inormalise,icoordsnew,iplotx,iploty,iplotz,ix,xorigin) else call interpolate3D_projection( & xplot(1:ninterp),yplot(1:ninterp),zplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,icontourplot), & icolourme(1:ninterp),ninterp,xmin,ymin,datpixcont,npixx,npixy,pixwidth, & pixwidthy,inormalise,zobservertemp,dzscreentemp,ifastrender) endif gotcontours = .true. if (.not.isameweights) & ! reset weights call set_weights(weight,dat,iamtype,UseTypeInRenderings) endif !!--adjust the units of the z-integrated quantity if (iRescale .and. units(ih).gt.0. .and. .not.inormalise) then datpix = datpix*(unitzintegration/units(ih)) if (gotcontours) then datpixcont = datpixcont*(unitzintegration/units(ih)) endif endif endif endif endif endif ! whether 3D grid or fast renderings !-------------take cross sections through 2D data------------------! elseif (irenderplot.gt.0 .and. ndim.eq.2 .and. x_sec) then !------------------------------------------------------------------- ! or do a fast cross section through 2D data to 1D array !------------------------------------------------------------------- !!--interpolate from 2D data to 1D line !! line is specified by giving two points, (x1,y1) and (x2,y2) !--set up 1D grid and allocate memory for datpix1D if (.not.interactivereplot) then xmin = 0. ! distance (r) along cross section xmax = SQRT((xseclineY2-xseclineY1)**2 + (xseclineX2-xseclineX1)**2) endif dxgrid = (xmax-xmin)/REAL(npixx) call set_grid1D(xmin,dxgrid,npixx) call interpolate2D_xsec( & dat(1:ninterp,iplotx),dat(1:ninterp,iploty),& hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,irenderplot), & icolourme(1:ninterp),ninterp,xseclineX1,xseclineY1,xseclineX2,xseclineY2, & datpix1D,npixx,inormalise) ! !--find limits of datpix1D for plotting ! do transformations on rendered array where appropriate ! set these as ymin,ymax and set labels of plot ! call transform(datpix1D,itrans(irenderplot)) labely = transform_label(label(irenderplot),itrans(irenderplot)) if (abs(xseclineY2-xseclineY1).gt.epsilon(0.)) then labelx = 'cross section' ! only if cross-section is oblique (otherwise keep x axis label) endif !!--if adaptive limits, find limits of datpix if (.not.interactivereplot) then ymin = minval(datpix1D) ymax = maxval(datpix1D) xminadapt(irenderplot) = min(ymin,xminadapt(irenderplot)) xmaxadapt(irenderplot) = max(ymax,xmaxadapt(irenderplot)) if (iadapt) then print*,' adapting y limits' else !!--or use fixed limits and apply transformations ymin = lim(irenderplot,1) ymax = lim(irenderplot,2) call transform_limits(ymin,ymax,itrans(irenderplot)) endif endif endif ! 2 or 3D and rendering !------------------------------------------------------------------ ! apply transformations to, and find limits for the 2D ! pixel array datpix resulting from the interpolation operations ! do this *before* the page setup so that rendermin,max ! can be stored in page_setup for interactive plots !------------------------------------------------------------------ if (irenderplot.gt.0 .and. irenderplot.le.numplot) then if (ndim.eq.3 .or. (ndim.eq.2 .and..not.x_sec)) then !!--determine whether rendered quantity is logged or not logged = islogged(itrans(irenderplot)) if (gotcontours) then loggedcont = islogged(itrans(icontourplot)) else loggedcont = .false. endif !!--do transformations on rendered array (but only the first time!) if (.not.interactivereplot .or. irerender) then if (logged) then !!--if log, then set zero values to some large negative number ! but exclude this value from adaptive limits determination call transform(datpix,itrans(irenderplot),errval=error_in_log) else call transform(datpix,itrans(irenderplot)) endif if (gotcontours) then if (loggedcont) then call transform(datpixcont,itrans(icontourplot),errval=error_in_log) else call transform(datpixcont,itrans(icontourplot)) endif endif endif !!--set label for rendered quantity labelrender = label(irenderplot) if (gotcontours) labelcont = label(icontourplot) !!--set label for column density (projection) plots if (ndim.eq.3 .and..not. x_sec .and..not.(use3Dperspective.and.use3Dopacityrendering)) then labelrender = integrate_label(labelrender,irender,iz,inormalise,iRescale,& labelzintegration,projlabelformat,iapplyprojformat) if (gotcontours) labelcont = integrate_label(labelcont,icontourplot,iz,inormalise,& iRescale,labelzintegration,projlabelformat,iapplyprojformat) endif !!--apply transformations to the label(s) for the rendered and contoured quantit(y,ies) labelrender = transform_label(labelrender,itrans(irenderplot)) if (gotcontours) labelcont = transform_label(labelcont,itrans(icontourplot)) !!--limits for rendered quantity if (.not.interactivereplot .or. irerender) then !!--find (adaptive) limits of rendered array if (logged) then renderminadapt = minval(datpix,mask=abs(datpix-error_in_log).gt.tiny(datpix)) ! see above else renderminadapt = minval(datpix) endif rendermaxadapt = maxval(datpix) !--fix case where no limits are set due to NaNs etc. if (renderminadapt.gt.rendermaxadapt) then print "(a)",' WARNING: NaNs in rendered quantity' renderminadapt = 0. rendermaxadapt = 0. endif if (gotcontours) then if (loggedcont) then contminadapt = minval(datpixcont,mask=abs(datpixcont+666.).gt.tiny(datpixcont)) else contminadapt = minval(datpixcont) endif contmaxadapt = maxval(datpixcont) !--fix case where no limits are set due to NaNs etc. if (contminadapt.gt.contmaxadapt) then print "(a)",' WARNING: NaNs in contoured quantity' contminadapt = 0. contmaxadapt = 1. endif endif if (.not.interactivereplot .and. .not.isetrenderlimits) then if (iadapt) then !print*,'adapting render limits' rendermin = renderminadapt rendermax = rendermaxadapt else !!--or apply transformations to fixed limits rendermin = lim(irenderplot,1) rendermax = lim(irenderplot,2) call transform_limits(rendermin,rendermax,itrans(irenderplot)) endif if (gotcontours) then if (iadapt) then print*,'adapting contour limits' contmin = contminadapt contmax = contmaxadapt elseif (icontourplot.eq.irenderplot .and. lim2set(icontourplot)) then contmin = lim2(icontourplot,1) contmax = lim2(icontourplot,2) call transform_limits(contmin,contmax,itrans(icontourplot)) else contmin = lim(icontourplot,1) contmax = lim(icontourplot,2) call transform_limits(contmin,contmax,itrans(icontourplot)) endif endif endif endif if (iplotcont .and. .not.gotcontours) then ! ! this is the case where contoured quantity=rendered quantity ! don't need to recalculate the pixel array but limits can be independent ! => do this even during interactive replotting as rendermin,max can be changed ! but contour limits should copy changes unless separate contour limits are set ! contmin = rendermin contmax = rendermax if (lim2set(irenderplot) .and. .not.iadapt) then contmin = lim2(irenderplot,1) contmax = lim2(irenderplot,2) call transform_limits(contmin,contmax,itrans(irenderplot)) endif endif !! do not let max=0 on log plots as this is suspiciously wrong if (logged) then if (iadapt .and. abs(rendermax).lt.tiny(datpix)) then !!print*,'max=0 on log plot, fixing' rendermax = maxval(datpix) endif endif if (gotcontours .and. loggedcont) then if (iadapt .and. abs(contmax).lt.tiny(datpixcont)) then contmax = maxval(datpixcont) endif endif endif !------------------------------------------------------------------------- ! similar but where particle colouring is used instead of interpolation !------------------------------------------------------------------------- elseif (irenderpart.gt.0 .and. iplotpart) then !--allocate memory for particle colouring if (.not.allocated(renderplot)) then allocate(renderplot(ntoti),stat=ierr) if (ierr /= 0) stop 'error allocating temporary array for particle colouring' endif !--apply transformations to render array and set label renderplot(1:ntoti) = dat(1:ntoti,irenderpart) call transform(renderplot(1:ntoti),itrans(irenderpart)) labelrender = label(irenderpart) labelrender = transform_label(labelrender,itrans(irenderpart)) call adapt_limits(irenderpart,renderplot(1:ntoti),rendermin,rendermax, & renderminadapt,rendermaxadapt,trim(labelrender),& iamtype,ntoti,npartoftype,iusetype,ipagechange) !!--limits for rendered quantity if (.not.interactivereplot .and. .not.isetrenderlimits) then !!--find (adaptive) limits of rendered array ! (note: something may be not quite right here with adapt during anim sequences) if (iadapt) then rendermin = renderminadapt rendermax = rendermaxadapt else !!--use fixed limits and apply transformations rendermin = lim(irenderpart,1) rendermax = lim(irenderpart,2) call transform_limits(rendermin,rendermax,itrans(irenderpart)) endif endif ! !--actually colour the particles ! call colour_particles(renderplot(1:ntoti), & rendermin,rendermax,icolourme(1:ntoti),ntoti) !--deallocate memory if (allocated(renderplot)) deallocate(renderplot) endif !------------------------------------------------------------------------- ! similarly, get limits to be used in the vector plot so we can store ! them during page_setup for interactive plots !------------------------------------------------------------------------- if (ivectorplot.ne.0 .and. ndim.ge.2) then if (iamvec(ivectorplot).ne.0) then !!--choose quantity to be plotted ivecx = iamvec(ivectorplot) + iplotx - 1 ivecy = iamvec(ivectorplot) + iploty - 1 if (.not.interactivereplot) then ! not if vecmax changed interactively if (iadapt) then vecmax = -1.0 ! plot limits then set in vectorplot else vecmax = max(lim(ivecx,2),lim(ivecy,2)) endif endif endif endif !-----end of preliminary muff for 2D/3D cross sections/renderings ------------------ !--------------------------------- ! setup page !--------------------------------- call page_setup !--add to log if (x_sec.and.iplotpart .and. iplotz.gt.0 .and. iverbose > 1) then print "(' cross section: ',a1,' = ',f7.3,' to ',a1,' = ',f7.3)",& label(iplotz),zslicemin,label(iplotz),zslicemax endif !------------------------------ ! now actually plot the data !------------------------------ if (irenderplot.gt.0) then if ((ndim.eq.3).or.(ndim.eq.2.and. .not.x_sec)) then !!--call subroutine to actually render the image if (gotcontours .and. double_rendering) then !--if double rendering, plot first image in greyscale call render_pix(datpix,rendermin,rendermax,trim(labelrender), & npixx,npixy,xmin,ymin,pixwidth,pixwidthy, & 1,iplotcont,0,ncontours,.false.,ilabelcont,contmin,contmax) else if (use3Dperspective .and. use3Dopacityrendering .and. ndim.eq.3 .and. writeppm) then call render_pix(datpix,rendermin,rendermax,trim(labelrender), & npixx,npixy,xmin,ymin,pixwidth,pixwidthy, & icolours,iplotcont,0,ncontours,.false.,ilabelcont,contmin,contmax,alpha=brightness) else call render_pix(datpix,rendermin,rendermax,trim(labelrender), & npixx,npixy,xmin,ymin,pixwidth,pixwidthy, & icolours,iplotcont,0,ncontours,.false.,ilabelcont,contmin,contmax) endif endif !!--contour/2nd render plot of different quantity on top of 1st rendering if (gotcontours) then if (double_rendering) then call render_pix(datpixcont,contmin,contmax,trim(labelcont), & npixx,npixy,xmin,ymin,pixwidth,pixwidthy,icolours,.false.,& 0,ncontours,.false.,ilabelcont,transparent=.true.) else call render_pix(datpixcont,contmin,contmax,trim(labelcont), & npixx,npixy,xmin,ymin,pixwidth,pixwidthy,0,.true.,0,ncontours,& .false.,ilabelcont) endif endif PlotOnRender_tmp(:) = PlotOnRenderings(:) isinktype = get_sink_type(ntypes) if (use3Dperspective .and. use3Dopacityrendering .and. rendersinks .and. isinktype > 0) then PlotOnRender_tmp(isinktype) = .false. endif !!--write ppm if interpolate3D_opacity if ((.not. plotlib_supports_alpha) .and. & (use3Dperspective .and. use3Dopacityrendering .and. ndim.eq.3 .and. writeppm)) then !!--plot non-gas particle types (e.g. sink particles) on top (and to ppm) call particleplot(xplot(1:ntoti),yplot(1:ntoti), & zplot(1:ntoti),hh(1:ntoti),ntoti,iplotx,iploty, & icolourme(1:ntoti),iamtype,npartoftype(:),PlotOnRender_tmp(:), & (x_sec.or.use3Dperspective),zslicemin,zslicemax,labelz, & xmin,xmax,ymin,ymax,ifastparticleplot,datpix,npixx,npixy,rendermax,brightness) call write_pixmap_ppm(datpix,npixx,npixy,xmin,ymin,pixwidth,rendermin,rendermax, & trim(labelrender),((istep-1)*nframesloop + iframe),brightness) !!--dump pixmap to file if option set elseif (iwritepixmap) then !!--plot non-gas particle types (e.g. sink particles) on top (and to ppm) call particleplot(xplot(1:ntoti),yplot(1:ntoti), & zplot(1:ntoti),hh(1:ntoti),ntoti,iplotx,iploty, & icolourme(1:ntoti),iamtype,npartoftype(:),PlotOnRender_tmp(:), & (x_sec.or.use3Dperspective),zslicemin,zslicemax,labelz, & xmin,xmax,ymin,ymax,ifastparticleplot,datpix,npixx,npixy,rendermax) call writepixmap(datpix,npixx,npixy,xmin,ymin,pixwidth,rendermin,rendermax,labelrender,& unitslabel(irenderplot),((istep-1)*nframesloop + iframe),x_sec,rootname(ifileopen)) !!--no ppm write else !!--plot non-gas particle types (e.g. sink particles) on top call particleplot(xplot(1:ntoti),yplot(1:ntoti), & zplot(1:ntoti),hh(1:ntoti),ntoti,iplotx,iploty, & icolourme(1:ntoti),iamtype,npartoftype(:),PlotOnRender_tmp(:), & (x_sec.or.use3Dperspective),zslicemin,zslicemax,labelz, & xmin,xmax,ymin,ymax,ifastparticleplot) endif elseif (ndim.eq.2 .and. x_sec) then !--------------------------------------------------------------- ! plot 1D cross section through 2D data (contents of datpix) !--------------------------------------------------------------- call plot_line(npixx,xgrid,datpix1D) endif else !----------------------- ! particle plots !----------------------- if (iplotpart) then if (debugmode .and. size(icolourme).ge.10) & print*,'DEBUG: starting particle plot with ',ntoti,' particles ',& zplot(1:10),icolourme(1:10),npartoftype(:),iusetype(:) !!--plot all particle types call particleplot(xplot(1:ntoti),yplot(1:ntoti), & zplot(1:ntoti),hh(1:ntoti),ntoti,iplotx,iploty, & icolourme(1:ntoti),iamtype,npartoftype(:),iusetype(:), & (x_sec.or.use3Dperspective),zslicemin,zslicemax,labelz, & xmin,xmax,ymin,ymax,ifastparticleplot) else !!--plot non-gas particle types on top of vector plots (e.g. sinks) call particleplot(xplot(1:ntoti),yplot(1:ntoti), & zplot(1:ntoti),hh(1:ntoti),ntoti,iplotx,iploty, & icolourme(1:ntoti),iamtype,npartoftype(:),PlotOnRenderings(:), & (x_sec.or.use3Dperspective),zslicemin,zslicemax,labelz, & xmin,xmax,ymin,ymax,ifastparticleplot) endif endif !-------------------------------------------------------------- ! vector maps (can be on top of particle plots and renderings) !-------------------------------------------------------------- if (ivecx.gt.0 .and. ivecy.gt.0 .and. ivectorplot.gt.0) then if (iRescale) then labelvecplot = trim(labelvec(ivectorplot))//trim(unitslabel(ivectorplot)) else labelvecplot = trim(labelvec(ivectorplot)) endif !!--set label for projection plots (2268 or 2412 for integral sign) if (ndim.eq.3 .and..not. x_sec) then if (iRescale) then labelvecplot = '\(2268) '//trim(labelvecplot)//' d'// & trim(label(iz)(1:index(label(iz),unitslabel(iz))-1))//' [code units]' else labelvecplot = '\(2268) '//trim(labelvecplot)//' d'//trim(label(iz)) endif endif pixwidthvec = (xmax-xmin)/real(npixvec) if (just.eq.1) then pixwidthvecy = pixwidthvec !(xmax-xmin)/real(npixvec) else pixwidthvecy = pixwidthvec endif npixyvec = int(0.999*(ymax-ymin)/pixwidthvecy) + 1 pixwidth = (xmax-xmin)/real(npixx) ! used in synchrotron plots if (.not.ihavesetweights) then call set_weights(weight,dat,iamtype,(iusetype.and.UseTypeInRenderings)) endif call vector_plot(ivecx,ivecy,npixvec,npixyvec,pixwidthvec,pixwidthvecy,vecmax,labelvecplot,got_h) !--vecmax is returned with the adaptive value if sent in -ve ! store this for use in interactive_multi if (xmaxmulti(ivectorplot).lt.0.) xmaxmulti(ivectorplot) = vecmax endif !--------------------------------- ! plot rotated axes !--------------------------------- if (irotate .and. irotateaxes.gt.0 .and. icoordsnew.eq.1) then call rotatedaxes(irotateaxes,iplotx,iploty,angletempx,angletempy,angletempz, & dzscreentemp,zobservertemp) endif ! !--redraw axes over what has been plotted ! if (irenderplot.gt.0 .or. plotlib_is_pgplot) then call redraw_axes(iaxistemp,just,yscalealt,itransy) endif ! !--annotate with time / marker legend and title ! call legends_and_title ! !--plot exact solution if relevant (before going interactive) ! if (iexact.ne.0 .and.nyplot.le.nacross*ndown .and. & ipanelselect(iPlotExactOnlyOnPanel,ipanel,irow,icolumn)) then iaxisy = iaxis if (tile_plots .and. icolumn.ne.1) iaxisy = -1 if (iexact.eq.iexact_rochelobe .and. use_sink_data .and. ipmass > 0 .and. ndim >= 2) then isinktype = get_sink_type(ntypes) call locate_first_two_of_type(isink1,isink2,isinktype,iamtype,npartoftype,ntoti) mprim = dat(isink1,ipmass) msec = dat(isink2,ipmass) xprim(1) = xplot(isink1) xprim(2) = yplot(isink1) xsec(1) = xplot(isink2) xsec(2) = yplot(isink2) endif call exact_solution(iexact,iplotx,iploty, & itrans(iplotx),itrans(iploty),icoordsnew, & ndim,ndimV,timei,xmin,xmax,gammai, & xplot(1:ntoti),yplot(1:ntoti),icolourme(1:ntoti),iamtype,npartoftype,iusetype, & pmassmin,pmassmax,ntoti,imarktype(1), & units(iplotx),units(iploty),irescale,iaxisy) endif !--the following line sets the number of steps on page to nstepsonpage ! in the case where we reach the last timestep before nstepsonpage is reached ! (makes interactive replotting behave better) if (lastplot) istepsonpage = nstepsperpage ! !--enter interactive mode ! if (interactive) then if (nacross*ndown.eq.1 .and. (nstepsperpage.eq.1 .or. nsteps.eq.1)) then iadvance = nfreq call interactive_part(ntoti,iplotx,iploty,iplotz,irender,icontourplot,ivecx,ivecy, & xplot(1:ntoti),yplot(1:ntoti),zplot(1:ntoti), & hh(1:ntoti),icolourme(1:ntoti),iamtype,iusetype,npartoftype, & xmin,xmax,ymin,ymax,rendermin,rendermax,renderminadapt,rendermaxadapt,contmin,contmax,& contminadapt,contmaxadapt,vecmax, & angletempx,angletempy,angletempz,ndim,xorigin(1:ndim),x_sec,zslicepos,dz, & zobservertemp,dzscreentemp,use3Dopacityrendering,taupartdepthtemp,& (double_rendering .and. gotcontours),irerender,itrackpart,icolours,& iColourBarStyle,labelrender,iadvance,ipos,iendatstep,iframe,nframesloop,interactivereplot) !--turn rotation on if necessary if (abs(angletempx-anglex).gt.tol) irotate = .true. if (abs(angletempy-angley).gt.tol) irotate = .true. if (abs(angletempz-anglez).gt.tol) irotate = .true. if (iadvance.eq.-666 .or. interactivereplot) exit over_frames elseif ((ipanel.eq.nacross*ndown .and. istepsonpage.eq.nstepsperpage) .or. lastplot) then ! !--slightly different interactive mode if multiple plots on page ! iadvance = nfreq ! call interactive_step(iadvance,ipos,iendatstep,xmin,xmax,ymin,ymax) nplots = ipanel irerender = .true. call interactive_multi(iadvance,ipos,ifirststeponpage,iendatstep,iframe,nframefirstonpage, & nframesloop,ipanel,iplotxtemp(1:nplots),iplotytemp(1:nplots),irendertemp(1:nplots),& icontourtemp(1:nplots),ivecplottemp(1:nplots),double_rendering,xminmulti(:),xmaxmulti(:),& vptxmin(1:nplots),vptxmax(1:nplots),vptymin(1:nplots),vptymax(1:nplots),barwmulti(1:nplots), & xminadapt(:),xmaxadapt(:),nacross,ndim,xorigin(1:ndim),icolours,iColourBarStyle,interactivereplot) if (iadvance.eq.-666 .or. interactivereplot) exit over_frames endif endif ! !--%%%%%%%%%%%%% end loop over cross-section slices %%%%%%%%%%%%%%%%%%%%%%% ! enddo over_cross_sections !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! not both coordinates - these are just particle plots !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! elseif ((.not.iscoordplot).and.(iploty.le.ndataplots .and. iplotx.le.ndataplots)) then if (debugmode) print*,'DEBUG: starting particle plot...' ! !--sort out particle colouring ! (at present this is NOT used -can't render if not co-ord plot) ! if (irenderpart.gt.0 .and. irenderpart.le.numplot) then !--allocate memory for particle colouring if (.not.allocated(renderplot)) then allocate(renderplot(ntoti),stat=ierr) if (ierr /= 0) stop 'error allocating temporary array for particle colouring' endif !--apply transformations to render array and set label renderplot(1:ntoti) = dat(1:ntoti,irenderpart) call transform(renderplot(1:ntoti),itrans(irenderpart)) labelrender = label(irenderpart) labelrender = transform_label(labelrender,itrans(irenderpart)) !--limits for rendered quantity if (.not.interactivereplot) then !--find (adaptive) limits of rendered array call adapt_limits(irenderpart,renderplot(1:ntoti),rendermin,rendermax, & renderminadapt,rendermaxadapt,trim(labelrender),& iamtype,ntoti,npartoftype,iusetype,ipagechange) if (.not.iadapt) then !!--use fixed limits and apply transformations rendermin = lim(irenderpart,1) rendermax = lim(irenderpart,2) call transform_limits(rendermin,rendermax,itrans(irenderpart)) endif endif !--actually colour the particles call colour_particles(renderplot(1:ntoti), & rendermin,rendermax,icolourme(1:ntoti),ntoti) endif !-------------------------------- ! setup page !-------------------------------- just = 0 call page_setup !-------------------------------- ! now plot particles !-------------------------------- if (ismooth_particle_plots > 0) then npixx = 1024 npixy = 1024 if (.not.allocated(datpix)) allocate(datpix(npixx,npixy)) if (.not.allocated(brightness)) allocate(brightness(npixx,npixy)) pixwidth = (xmax - xmin)/npixx pixwidthy = (ymax - ymin)/npixy !print*,'PIXWIDTH = ',pixwidth,pixwidthy if (irender > 0) then call interpolate2D_pixels(xplot,yplot,icolourme,ntoti,xmin,ymin,xmax,ymax,& datpix,npixx,npixy,.true.,.true.,renderplot,brightness) ! scale opacity based on density of points, but only slightly brightness = 1. - exp(-brightness**0.3) call render_pix(datpix,rendermin,rendermax,'blah', & npixx,npixy,xmin,ymin,pixwidth,pixwidthy,3,.false.,0,ncontours,& .false.,.false.,alpha=brightness,transparent=.true.) else call interpolate2D_pixels(xplot,yplot,icolourme,ntoti,xmin,ymin,xmax,ymax,& datpix,npixx,npixy,.false.,(ismooth_particle_plots==2)) datpix = log10(datpix) call render_pix(datpix,maxval(datpix)-6.,maxval(datpix),'blah', & npixx,npixy,xmin,ymin,pixwidth,pixwidthy,3,.false.,0,ncontours,& .false.,.false.) endif if (allocated(datpix)) deallocate(datpix) if (allocated(brightness)) deallocate(brightness) else call particleplot(xplot(1:ntoti),yplot(1:ntoti), & zplot(1:ntoti),hh(1:ntoti),ntoti,iplotx,iploty, & icolourme(1:ntoti),iamtype,npartoftype(:),iusetype,.false., & zslicemin,zslicemax,' ',xmin,xmax,ymin,ymax,ifastparticleplot) endif !--deallocate memory if (allocated(renderplot)) deallocate(renderplot) !-------------------------------- ! plot error bars !-------------------------------- if (iploterrbars) then call plot_qci(icolourprev) ! query line style and colour call plot_sci(linecolourthisstep) ! set colour to current line !--y error bars if (ilocerrbars(iploty).gt.0 .and. ilocerrbars(iploty).le.ndataplots) then call plot_errorbarsy(ntoti,xplot,yplot,dat(:,ilocerrbars(iploty)),itransy) endif !--x error bars if (ilocerrbars(iplotx).gt.0 .and. ilocerrbars(iplotx).le.ndataplots) then call plot_errorbarsx(ntoti,xplot,yplot,dat(:,ilocerrbars(iplotx)),itransx) endif call plot_sci(icolourprev) ! restore line colour endif ! !--redraw axes over what has been plotted ! if (plotlib_is_pgplot) call redraw_axes(iaxis,just,yscalealt,itransy) ! !--annotate with time / marker legend and title ! call legends_and_title ! !--plot exact solution (after redrawn axis for residual plots) ! if (iexact.ne.0 .and.nyplot.le.nacross*ndown .and. & ipanelselect(iPlotExactOnlyOnPanel,ipanel,irow,icolumn)) then iaxisy = iaxis if (tile_plots .and. icolumn.ne.1) iaxisy = -1 call exact_solution(iexact,iplotx,iploty,itrans(iplotx),itrans(iploty), & icoordsnew,ndim,ndimV,timei,xmin,xmax,gammai, & xplot(1:ntoti),yplot(1:ntoti),icolourme(1:ntoti),iamtype,npartoftype,iusetype, & pmassmin,pmassmax,ntoti,imarktype(1), & units(iplotx),units(iploty),irescale,iaxisy) endif ! !--enter interactive mode !--the following line sets the number of steps on page to nstepsonpage ! in the case where we reach the last timestep before nstepsonpage is reached ! (makes interactive replotting behave better) if (lastplot) istepsonpage = nstepsperpage if (interactive) then if (nacross*ndown.eq.1 .and. (nstepsperpage.eq.1 .or. nsteps.eq.1)) then iadvance = nfreq call interactive_part(ntoti,iplotx,iploty,0,irenderpart,0,0,0, & xplot(1:ntoti),yplot(1:ntoti),zplot(1:ntoti), & hh(1:ntoti),icolourme(1:ntoti),iamtype,iusetype,npartoftype, & xmin,xmax,ymin,ymax,rendermin,rendermax,renderminadapt,rendermaxadapt,& contmin,contmax,contminadapt,contmaxadapt,vecmax, & angletempx,angletempy,angletempz,ndim,xorigin(1:ndim), & dumxsec,dummy,dummy,dummy,dummy,.false.,dummy,.false.,irerender, & itrackpart,icolours,iColourBarStyle,labelrender,iadvance,ipos,iendatstep,iframe,nframesloop,interactivereplot) if (iadvance.eq.-666 .or. interactivereplot) exit over_frames ! this should be unnecessary elseif ((ipanel.eq.nacross*ndown .and. istepsonpage.eq.nstepsperpage) .or. lastplot) then ! !--timestep control only if multiple plots on page ! iadvance = nfreq ! call interactive_step(iadvance,ipos,iendatstep,xmin,xmax,ymin,ymax) nplots = ipanel irerender = .true. call interactive_multi(iadvance,ipos,ifirststeponpage,iendatstep,iframe,nframefirstonpage, & nframesloop,ipanel,iplotxtemp(1:nplots),iplotytemp(1:nplots),irendertemp(1:nplots),& icontourtemp(1:nplots),ivecplottemp(1:nplots),.false.,xminmulti(:),xmaxmulti(:),& vptxmin(1:nplots),vptxmax(1:nplots),vptymin(1:nplots),vptymax(1:nplots),barwmulti(1:nplots), & xminadapt(:),xmaxadapt(:),nacross,ndim,xorigin(1:ndim),icolours,iColourBarStyle,interactivereplot) if (iadvance.eq.-666 .or. interactivereplot) exit over_frames endif endif elseif (iploty.le.numplot) then! ie iploty = extra !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! additional plots (not plots of particle data - e.g where some additional ! information is read from a file and plotted on the same page as the ! particle plots, or where some additional plot is calculated ! from the particle data, such as errors etc) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (debugmode) print*,'DEBUG: starting extra plot...' !-------------------------------------------------------------- ! plot surface density, Toomre Q parameter ! or Probability Distribution Function ! => these all involve a new "y column" ! but use a particle property as the x axis !-------------------------------------------------------------- if (iploty.eq.isurfdens .or. iploty.eq.itoomre .or. iploty.eq.ipdf) then just = 0 if (iploty.eq.itoomre) then itemp = 2 label(iploty) = 'Q\dToomre\u' labely = trim(label(iploty)) elseif (iploty.eq.isurfdens) then itemp = 1 label(iploty) = '\gS '//get_unitlabel_coldens(iRescale,labelzintegration,unitslabel(irho)) labely = trim(label(iploty)) elseif (iploty.eq.ipdf) then label(iploty) = 'PDF ('//trim(label(iplotx))//')' labely = trim(label(iploty)) endif yplot(:) = 0. if (itrans(iploty).ne.0) then labely = trim(transform_label(label(iploty),itrans(iploty))) endif if ((.not.interactivereplot) .or. irerender) then !--call routines which actually calculate disc properties from the particles if (iploty.eq.isurfdens .or. iploty.eq.itoomre) then if (ispsound.gt.0 .and. ispsound.le.ndataplots) then icol = ispsound ! sound speed is present in data elseif (iutherm.gt.0 .and. iutherm.le.ndataplots) then icol = iutherm ! use thermal energy if spsound not present else icol = 0 endif ! work out the unit of mass, r needed for computing Toomre Q unit_mass = 1.d0 unit_r = 1.d0 unit_u = 1.d0 unit_dz = 1.d0 if (iRescale) then if (ix(1) > 0) unit_r = units(ix(1)) if (icol > 0) unit_u = units(icol) unit_dz = unitzintegration endif if (ipmass.gt.0 .and. ipmass.le.ndataplots) then if (iRescale) unit_mass = units(ipmass) if (icol.gt.0) then call disccalc(itemp,ntoti,xplot(1:ntoti),ntoti,dat(1:ntoti,ipmass),unit_mass,unit_r,unit_dz, & xmin,xmax,yminadapti,ymaxadapti,itrans(iplotx),itrans(iploty), & icolourme(1:ntoti),iamtype,iusetype,npartoftype,gammai,unit_u,dat(1:ntoti,icol),icol.eq.ispsound) else call disccalc(itemp,ntoti,xplot(1:ntoti),ntoti,dat(1:ntoti,ipmass),unit_mass,unit_r,unit_dz, & xmin,xmax,yminadapti,ymaxadapti,itrans(iplotx),itrans(iploty), & icolourme(1:ntoti),iamtype,iusetype,npartoftype,gammai) endif else if (iRescale .and. irho > 0) unit_mass = units(irho)*unitzintegration**3 if (icol.gt.0) then call disccalc(itemp,ntoti,xplot(1:ntoti),1,masstype(1),unit_mass,unit_r,unit_dz, & xmin,xmax,yminadapti,ymaxadapti,itrans(iplotx),itrans(iploty), & icolourme(1:ntoti),iamtype,iusetype,npartoftype,gammai,unit_u,dat(1:ntoti,icol),icol.eq.ispsound) else call disccalc(itemp,ntoti,xplot(1:ntoti),1,masstype(1),unit_mass,unit_r,unit_dz, & xmin,xmax,yminadapti,ymaxadapti,itrans(iplotx),itrans(iploty),& icolourme(1:ntoti),iamtype,iusetype,npartoftype,gammai) endif endif elseif (iploty.eq.ipdf) then if (npdfbins.gt.0) then ngrid = npdfbins else ! automatic number of bins determination ngrid = int(0.75*ntoti**(1./3.))+1 endif call set_grid1D(xmin,1.,ngrid) !--call routine which calculates pdf on the particles !--compute PDF on raw (un-transformed) data xplot(1:ntoti) = dat(1:ntoti,iplotx) if (irho.gt.0 .and. irho.le.ndataplots .and. ipmass.gt.0 .and. ipmass.le.ndataplots) then call pdf_calc(ntoti,xplot(1:ntoti),xmin,xmax,ngrid,xgrid,datpix1D, & yminadapti,ymaxadapti,(npdfbins.gt.0),volweightedpdf, & ierr,icolourme(1:ntoti),dat(1:ntoti,irho),dat(1:ntoti,ipmass)) else call pdf_calc(ntoti,xplot(1:ntoti),xmin,xmax,ngrid,xgrid,datpix1D, & yminadapti,ymaxadapti,(npdfbins.gt.0),volweightedpdf, & ierr,icolourme(1:ntoti)) endif ! !--write PDF to file ! if (ierr.eq.0) then call pdf_write(ngrid,xgrid,datpix1D,label(iplotx), & volweightedpdf,rootname(ifileopen),tagline) endif ! !--apply transformations to PDF data ! if (itrans(iplotx).gt.0) then !--reapply the x transform call transform(xplot,itrans(iplotx)) endif if (itrans(iploty).gt.0) then call transform(datpix1D,itrans(iploty)) call transform_limits(yminadapti,ymaxadapti,itrans(iploty)) endif endif endif if (iadapt .and. .not.interactivereplot) then print "(1x,a)",'adapting '//trim(labely)//' limits' ymin = yminadapti ymax = ymaxadapti endif call page_setup call plot_qci(icolourprev) ! query line style and colour call plot_qls(linestyleprev) ! set appropriate colour and style if multiple steps per page if (nstepsperpage.gt.1) then call plot_sci(linecolourthisstep) call plot_sls(linestylethisstep) endif if (iploty.eq.itoomre .or. iploty.eq.isurfdens) then call discplot() elseif (iploty.eq.ipdf) then ! !--plot PDF as line segment, with blanking at zero ! call plotline(size(xgrid),xgrid,datpix1D,blank=0.) endif !--restore line size and colour call plot_sci(icolourprev) call plot_sls(linestyleprev) if (plotlib_is_pgplot) call redraw_axes(iaxis,just,yscalealt,itransy) call legends_and_title ! !--plot exact solution (after redrawn axis for residual plots) ! if (iexact.ne.0 .and.nyplot.le.nacross*ndown .and. & ipanelselect(iPlotExactOnlyOnPanel,ipanel,irow,icolumn)) then iaxisy = iaxis if (tile_plots .and. icolumn.ne.1) iaxisy = -1 call exact_solution(iexact,iplotx,iploty,itrans(iplotx),itrans(iploty), & icoordsnew,ndim,ndimV,timei,xmin,xmax,gammai, & xplot(1:ntoti),yplot(1:ntoti),icolourme(1:ntoti),iamtype,npartoftype,iusetype, & pmassmin,pmassmax,ntoti,imarktype(1), & units(iplotx),units(iploty),irescale,iaxisy) endif if (lastplot) istepsonpage = nstepsperpage if (interactive .and. ((ipanel.eq.nacross*ndown .and. istepsonpage.eq.nstepsperpage) .or. lastplot)) then iadvance = nfreq nplots = ipanel irerender = .true. call interactive_multi(iadvance,ipos,ifirststeponpage,iendatstep,iframe,nframefirstonpage, & nframesloop,ipanel,iplotxtemp(1:nplots),iplotytemp(1:nplots),irendertemp(1:nplots),& icontourtemp(1:nplots),ivecplottemp(1:nplots),.false.,xminmulti(:),xmaxmulti(:),& vptxmin(1:nplots),vptxmax(1:nplots),vptymin(1:nplots),vptymax(1:nplots),barwmulti(1:nplots), & xminadapt(:),xmaxadapt(:),nacross,ndim,xorigin(1:ndim),icolours,iColourBarStyle,interactivereplot) if (iadvance.eq.-666 .or. interactivereplot) exit over_frames endif cycle over_plots !-------------------------------------------------------------- ! plot Toy star A-C plane solution !-------------------------------------------------------------- elseif (iexact.eq.4 .and. iploty.eq.iacplane) then ! !--A vs C for exact toystar solution ! if (ndim.eq.1) then call exact_toystar_acplane(atstar,ctstar,sigma,gammai) elseif (ndim.eq.2) then call exact_toystar_acplane2D(atstar,ctstar,sigma,gammai) endif !--increment page counter as setpage is not called iplots = iplots + 1 ipanel = ipanel + 1 if (ipanel.gt.nacross*ndown) ipanel = 1 !-------------------------------------------------------------- ! power spectrum plots (uses x and data as yet unspecified) !-------------------------------------------------------------- elseif (iploty.eq.ipowerspec) then labelx = 'frequency' labely = 'power' ! !--3D: use FFT routines ! if (ndim.eq.3) then if (.not.ihavesetweights) then call set_weights(weight,dat,iamtype,iusetype) endif call powerspec3D_sph(dat(1:ninterp,ix(1)),dat(1:ninterp,ix(2)),dat(1:ninterp,ix(3)), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,ipowerspecy),icolourme(1:ninterp), & ninterp,nfreqspec,lim(ipowerspecx,1),lim(ipowerspecx,2),xplot(1:nfreqspec), & yplot(1:nfreqspec),inormalise) xmin = max(minval(xplot(1:nfreqspec)),1.0) xmax = maxval(xplot(1:nfreqspec)) nfreqpts = nfreqspec else ! !--1D: use slow FT routines or Lomb periodogram ! if (.not.interactivereplot) then xmin = freqmin ! freq min xmax = freqmax ! freq max endif if (.not.interactivereplot .and. itrans(iploty).gt.0) then call transform_limits(xmin,xmax,itrans(iploty)) endif ! !--setup frequency grid (evenly spaced in transformed grid) ! nfreqpts = nfreqspec if (nfreqpts.ge.size(xplot)) then nfreqpts = size(xplot) print*,' WARNING: nfreqpts > array size, restricting to ',nfreqpts else print "(a,i6)",' number of frequency points = ',nfreqpts endif dxfreq = (xmax - xmin)/real(nfreqpts) do i=1,nfreqpts xplot(i) = xmin + (i-1)*dxfreq enddo ! !--transform back to frequency space ! if (itrans(iploty).gt.0) & call transform_inverse(xplot(1:nfreqpts),itrans(iploty)) if (.not.idisordered) then! interpolate first !!--allocate memory for 1D grid (size = 2*npart) ngrid = 2*npartoftype(1) !!--set up 1D grid xmingrid = lim(ipowerspecx,1) xmaxgrid = lim(ipowerspecx,2) dxgrid = (xmaxgrid-xmingrid)/ngrid call set_grid1D(xmingrid,dxgrid,ngrid) ninterp = ntoti !!--interpolate to 1D grid if (.not.ihavesetweights) then call set_weights(weight,dat,iamtype,iusetype) endif call interpolate1D(dat(1:ninterp,ipowerspecx),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,ipowerspecy),icolourme(1:ninterp), & ninterp,xmingrid,datpix1D,ngrid,dxgrid,inormalise) !!--plot interpolated 1D data to check it !!print*,minval(datpix1D),maxval(datpix1D) !call pgswin(xmin,xmax,minval(datpix1D),maxval(datpix1D),0,1) !call pgbox('BCNST',0.0,0,'1BVCNST',0.0,0) !call pglabel('x',label(ipowerspecy),'1D interpolation') !call pgline(ngrid,xgrid,datpix1D) !read* !call pgpage! change page !!--call power spectrum calculation on the even grid call powerspectrum(ngrid,xgrid,datpix1D,nfreqpts,xplot(1:nfreqpts), & yplot(1:nfreqpts),idisordered) if (allocated(datpix1D)) deallocate(datpix1D) if (allocated(xgrid)) deallocate(xgrid) else !!--or else call power spectrum calculation on the particles themselves call powerspectrum(ntoti,dat(1:ntoti,ipowerspecx), & dat(1:ntoti,ipowerspecy),nfreqpts, & xplot(1:nfreqpts),yplot(1:nfreqpts),idisordered) endif endif if (.not.interactivereplot) then ymin = minval(yplot(1:nfreqspec)) ymax = maxval(yplot(1:nfreqspec)) endif !!--uncomment next few lines to plot wavelengths instead !labelx = 'wavelength' !zplot(1:nfreqspec) = 1./xplot(1:nfreqspec) !xplot(1:nfreqspec) = zplot(1:nfreqspec) !if (.not.interactivereplot) then ! xmin = minval(xplot(1:nfreqspec)) ! xmax = maxval(xplot(1:nfreqspec)) !endif if (itrans(iploty).ne.0) then call transform(xplot(1:nfreqpts),itrans(iploty)) labelx = transform_label(labelx,itrans(iploty)) call transform(yplot(1:nfreqpts),itrans(iploty)) labely = transform_label(labely,itrans(iploty)) if (.not.interactivereplot) then call transform_limits(xmin,xmax,itrans(iploty)) call transform_limits(ymin,ymax,itrans(iploty)) endif endif just = 0 call page_setup call plot_qci(icolourprev) ! query line style and colour call plot_qls(linestyleprev) if (nstepsperpage.gt.1) then call plot_sci(linecolourthisstep) ! set appropriate colour and style if multiple steps per page call plot_sls(linestylethisstep) endif call plot_line(nfreqpts,xplot(1:nfreqpts),yplot(1:nfreqpts)) print*,' maximum power at '//trim(labelx)//' = ',xplot(maxloc(yplot(1:nfreqpts))) call plot_sci(icolourprev) call plot_sls(linestyleprev) ! !--redraw axes over what has been plotted ! if (plotlib_is_pgplot) call redraw_axes(iaxis,just,yscalealt,itransy) ! !--annotate with time / marker legend and title ! call legends_and_title elseif (iploty.eq.icolpixmap) then !-------------------------------------------------------------- ! plot the contents of a pixel map read from a file !-------------------------------------------------------------- ! !--irender should already be set, associating the pixmap ! with a column from the SPH data. Then we can use the ! limit settings from the SPH data. Otherwise just ! treat it like a separate column. ! if (irender.eq.0) irender = icolpixmap !--datpix is allocated inside the readpixmap routine if (allocated(datpix)) deallocate(datpix) if (irender.eq.icolpixmap) then labelrender = '|B_\phi|/|B_p|' else labelrender = label(irender) endif call readpixmap(datpix,npixx,npixy,rootname(ifileopen),& shortlabel(labelrender,unitslabel(irender)),istep,x_sec,ierr) if (.not.interactivereplot) then if (ndim.ge.1) then xmin = lim(ix(1),1) xmax = lim(ix(1),2) else xmin = 0. xmax = 1. endif if (ndim.ge.2) then ymin = lim(ix(3),1) ymax = lim(ix(3),2) else ymin = 0. ymax = 1. endif endif if (ndim.ge.1) iplotx = ix(1) if (ndim.ge.2) then iploty = ix(3) labely = label(ix(3)) endif pixwidth = (xmax-xmin)/real(npixx) if (itrans(irender).ne.0 .and. allocated(datpix)) then call transform(datpix,itrans(irender),errval=error_in_log) endif labelrender = transform_label(labelrender,itrans(irender)) !--find (adaptive) limits of rendered array if (allocated(datpix)) then renderminadapt = minval(datpix,mask=abs(datpix-error_in_log).gt.tiny(datpix)) rendermaxadapt = maxval(datpix) endif !--limits for rendered quantity if (.not.interactivereplot) then if (iadapt) then rendermin = renderminadapt rendermax = rendermaxadapt else !!--use fixed limits and apply transformations rendermin = lim(irender,1) rendermax = lim(irender,2) call transform_limits(rendermin,rendermax,itrans(irender)) endif endif just = 1 iPlotColourBar = .true. call page_setup if (ierr.eq.0 .and. allocated(datpix)) then !!--call subroutine to actually render the image call render_pix(datpix,rendermin,rendermax,trim(labelrender), & npixx,npixy,xmin,ymin,pixwidth,pixwidth, & icolours,iplotcont,0,0,.false.,.false.) endif ! !--redraw axes over what has been plotted ! if ((allocated(datpix) .and. ierr.eq.0) .or. plotlib_is_pgplot) then call redraw_axes(iaxis,just,yscalealt,itransy) endif ! !--annotate with time / marker legend and title ! call legends_and_title irender = 0 iploty = icolpixmap iplotx = 0 else !-------------------------------------------------------------- ! plot the contents of an extra two-column ascii file !-------------------------------------------------------------- call exact_fromfile('gwaves1.dat',xplot,yplot,1,2,nfreqpts,ierr) just = 0 labelx = 't [ms]' labely = 'h' if (.not.interactivereplot) then xmin = minval(xplot(1:nfreqpts)) xmax = maxval(xplot(1:nfreqpts)) ymin = minval(yplot(1:nfreqpts)) ymax = maxval(yplot(1:nfreqpts)) !--adjust y axes ymin = (ymin + ymax)/2. - 0.55*(ymax-ymin) ymax = (ymin + ymax)/2. + 0.55*(ymax-ymin) endif call page_setup !--plot extra point corresponding to current time ipt = 0 do i=1,nfreqpts-1 if (xplot(i).le.timei .and. xplot(i+1).gt.timei) ipt = i enddo if (ipt.ne.0) then call plot_pt1(xplot(ipt),yplot(ipt),4) call plot_line(ipt,xplot(1:ipt),yplot(1:ipt)) endif if (plotlib_is_pgplot) call redraw_axes(iaxis,just,yscalealt,itransy) call legends_and_title endif !--the following line sets the number of steps on page to nstepsonpage ! in the case where we reach the last timestep before nstepsonpage is reached ! (makes interactive replotting behave better) if (lastplot) istepsonpage = nstepsperpage if (interactive .and.((ipanel.eq.nacross*ndown .and. istepsonpage.eq.nstepsperpage) & .or. lastplot)) then iadvance = nfreq call interactive_step(iadvance,ipos,iendatstep,xmin,xmax,ymin,ymax,interactivereplot) irerender = .true. if (iadvance.eq.-666) exit over_frames endif !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! if plot not in correct range !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else print*,' error in plotting : iplotx = ',iplotx,' iploty =',iploty, 'numplot =',numplot call plot_page! just skip to next plot endif ! ploty = whatever enddo over_plots ! over plots per timestep (nyplot) enddo over_frames ! over nframes for animation sequences ! !--frame changing for multiple steps on the page (ie. page split into panels) ! iframesave = 0 if (iadvance.ne.-666 .and..not.interactivereplot) then if (nacross*ndown.gt.1 .and. insidesequence(iseqpos)) then !--for the last panel on the page, reset the sequence for next time if (ipanel.eq.nacross*ndown) then if (iframe.lt.nframes) then ipos = ipos - nacross*ndown endif iframesave = iframe else iframesave = iframe - 1 endif endif endif !--free all dynamically allocated memory if (.not.interactivereplot) then if (allocated(datpix1D)) deallocate(datpix1D) if (allocated(datpix)) deallocate(datpix) if (allocated(brightness)) deallocate(brightness) if (allocated(datpix3D)) deallocate(datpix3D) if (allocated(xgrid)) deallocate(xgrid) if (allocated(vecplot)) deallocate(vecplot) if (allocated(datpixcont)) deallocate(datpixcont) if (allocated(datpixcont3D)) deallocate(datpixcont3D) endif !--free temporary arrays if (allocated(xplot)) deallocate(xplot) if (allocated(yplot)) deallocate(yplot) if (allocated(zplot)) deallocate(zplot) if (allocated(hh)) deallocate(hh) if (allocated(weight)) deallocate(weight) return contains !---------------------------------------------- ! interfaces to the page setup routines ! this is called just before a plot is ! actually plotted !---------------------------------------------- subroutine page_setup(dummy) use colourbar, only:get_colourbarmargins use pagesetup, only:setpage2 use settings_page, only:nstepsperpage,iUseBackgroundColourForAxes, & vposlegend,iPlotLegend,usecolumnorder,interactive,& xminpagemargin,xmaxpagemargin,yminpagemargin,ymaxpagemargin use settings_limits, only:adjustlimitstodevice use plotlib, only:plot_qvp,plot_sci,plot_page,plotlib_is_pgplot,plot_set_opacity,plot_qcur use limits, only:fix_equal_limits implicit none integer :: iplotsave,ipanelsave,ipanelpos,npanels_remaining real :: barwidth, TitleOffset,xminmargin,xmaxmargin,yminmargin,ymaxmargin real :: xminpix,xmaxpix,yminpix,ymaxpix,dxpix logical :: ipanelchange,dum,iprint_axes,lastrow logical, intent(in), optional :: dummy character(len=7) :: string !-------------------------------------------- ! whether or not this is a dummy call or not !-------------------------------------------- if (present(dummy)) then dum = dummy if (debugmode) print*,'DEBUG: entering page setup (dummy)' else dum = .false. if (debugmode) print*,'DEBUG: entering page setup' endif !--------------------- ! increment counters !--------------------- iplotsave = iplots ipanelsave = ipanel iplots = iplots + 1 ipanelchange = .true. if (nstepsperpage.eq.0 .and. iplots.gt.1) ipanelchange = .false. ! this is an option to never change panels if (iplots.gt.1 .and. nyplots.eq.1 .and. nacross*ndown.gt.1.and..not.ipagechange) ipanelchange = .false. if (ipanelchange) ipanel = ipanel + 1 if (ipanel.gt.nacross*ndown) ipanel = 1 ipanel = max(ipanel,1) ! catch panel=0 if panel is not changing !--set counter for where we are in row, col if (.not.usecolumnorder) then irow = ipanel - ((ipanel-1)/ndown)*ndown icolumn = (ipanel-1)/ndown + 1 ipanelpos = (irow-1)*nacross + icolumn else icolumn = ipanel - ((ipanel-1)/nacross)*nacross irow = (ipanel-1)/nacross + 1 ipanelpos = ipanel endif !--if we are in interactive mode, use the currently buffered plot limits if (interactivereplot .and. (nacross*ndown.gt.1 .or. (nstepsperpage.gt.1 .and. nsteps.gt.1))) then xmin = xminmulti(iplotx) xmax = xmaxmulti(iplotx) ymin = xminmulti(iploty) ymax = xmaxmulti(iploty) if (ivectorplot.gt.0 .and. ivectorplot.le.numplot) then vecmax = xmaxmulti(ivectorplot) endif endif !nsteps_remaining = (nsteps - istep)/nstepsperpage !nplots_remaining = (nyplots - nyplot)/nstepsperpage npanels_remaining = (nsteps - istep)/nstepsperpage !nplots_remaining*nsteps_remaining ! npanels_remaining = (nsteps - istep)*nyplots/nstepsperpage lastrow = (npanels_remaining < nacross) lastplot = ((ipos.eq.iendatstep .or. istep.eq.nsteps) & .and. nyplot.eq.nyplots .and. k.eq.nxsec) !-------------------------------------------------------------- ! output some muff to the screen !-------------------------------------------------------------- if (((interactive .and. ((ipanel.eq.nacross*ndown .and. istepsonpage.eq.nstepsperpage) .or. lastplot)) & .or. (iadapt .and. (istepsonpage.eq.nstepsperpage .or. lastplot))) .and. .not.dum) then if (plot_qcur()) then print*,trim(labelx),' min, max = ',xmin,xmax print*,trim(labely),' min, max = ',ymin,ymax if (irender.gt.0 .and. .not.(ndim.eq.2 .and. x_sec)) then print*,trim(labelrender),' min, max = ',rendermin,rendermax if (gotcontours) then print*,trim(labelcont),' min, max = ',contmin,contmax endif endif endif endif !-------------------------------------------------------------- ! set up pgplot page !-------------------------------------------------------------- !--use foreground colour if (.not.dum) call plot_sci(1) !--page margins: zero if no box is drawn ! xminmargin = 0.0 ! xmaxmargin = 0.0 ! yminmargin = 0.0 ! ymaxmargin = 0.0 xminmargin = xminpagemargin xmaxmargin = xmaxpagemargin yminmargin = yminpagemargin ymaxmargin = ymaxpagemargin !--leave space for colour bar if necessary (at end of row only on tiled plots) if ((tile_plots .and. iAllowspaceforcolourbar).or.(.not.tile_plots.and.iPlotColourBar)) then call get_colourbarmargins(iColourBarStyle,xminmargin,xmaxmargin,yminmargin,ymaxmargin,barwidth) else barwidth = 0. endif !--work out whether or not to leave space above plots for titles/legends TitleOffset = -tiny(Titleoffset) if (iPlotTitles .and. nstepsperpage.eq.1 .and. vpostitle.gt.0.) TitleOffset = vpostitle if (iPlotLegend .and. nstepsperpage.eq.1 .and. vposlegend.lt.0.) TitleOffset = max(Titleoffset,-vposlegend) inewpage = ipanel.eq.1 .and. ipanelchange .and. ipagechange if (inewpage .and. .not.dum) then call plot_page !--store ipos and nyplot positions for first on page ! as starting point for interactive replotting nyplotfirstonpage = nyplot ifirststeponpage = ipos nframefirstonpage = iframe endif ! !--do not allow limits to be the same ! if (abs(xmax-xmin).lt.tiny(xmax)) then if (.not.dum) print "(a)",' WARNING: '//trim(labelx)//'min='//trim(labelx)//'max ' call fix_equal_limits(xmin,xmax) endif if (abs(ymax-ymin).lt.tiny(ymax)) then if (.not.dum) print "(a)",' WARNING: '//trim(labely)//'min='//trim(labely)//'max ' call fix_equal_limits(ymin,ymax) endif if (irender.gt.0 .and. abs(rendermax-rendermin).lt.tiny(rendermax) .or.rendermax.ne.rendermax) then if (.not.dum) print "(a)",' WARNING: '//trim(labelrender)//'min='//trim(labelrender)//'max ' call fix_equal_limits(rendermin,rendermax) endif if (debugmode) print*,'DEBUG: calling setpage...',nstepsperpage if (nstepsperpage.gt.0 .or. inewpage) then if (dum) then !--fake the page setup, then return if (.not.(interactivereplot .and. .not.irerender)) then call setpage2(ipanelpos,nacross,ndown,xmin,xmax,ymin,ymax, & trim(labelx),trim(labely),'NOPGBOX',just,iaxistemp, & xminmargin,xmaxmargin,yminmargin,ymaxmargin, & 0.0,TitleOffset,isamexaxis,tile_plots,adjustlimitstodevice, & lastrow,lastplot,yscalealt,labelyalt,itransy) call plot_qvp(3,xminpix,xmaxpix,yminpix,ymaxpix) if (debugmode) print*,'DEBUG: viewport xpix=',xminpix,'->',xmaxpix,' ypix=',yminpix,'->',ymaxpix npixx = max(nint(xmaxpix-xminpix),1) npixy = max(nint(ymaxpix-yminpix),1) if (debugmode) print*,'DEBUG: dx = ',xmax-xmin,' dy = ',ymax-ymin if (debugmode) print*,'DEBUG: dxpix = ',xmaxpix-xminpix,' dypix = ',ymaxpix-yminpix if (vectordevice .and. npixx.gt.1024) then npixx = 1024/nacross dxpix = (xmax-xmin)/npixx npixy = int(0.999*(ymax-ymin)/real(dxpix)) + 1 print "(a,i4,a,i4,a)",' auto-selecting resolution of ',npixx,' x ',npixy,' for vector device' print "(a)",' => set the number of pixels manually if you want more (or less) than this.' else if (npix.eq.0 .and. debugmode) & print "(a,i4,a,i4)",' auto-selecting device resolution = ',npixx,' x ',npixy ! !--warn about PGPLOT limitations ! if (plotlib_is_pgplot) then if ((xmaxpix-xminpix).gt.1024. .or. (ymaxpix-yminpix).gt.1024) then print "(/,75('*'))" print "(a)",'!! WARNING: PGPLOT will truncate image to 1024 pixels on pixel devices.' print "(a)",'!! To fix this, change line 18 of file grimg2.f in the PGPLOT source code:' print "(a)",'!! REAL BUFFER(1026)' print "(a)",'!! changing 1026 to something much bigger, then recompile PGPLOT.' print "(75('*'),/)" endif endif endif endif !--restore saved attributes iplots = iplotsave ipanel = ipanelsave if (debugmode) print*,'DEBUG: finished dummy page setup' return else !--if we are not changing page, do not reprint the axes iprint_axes = ipagechange .or. inewpage .or. & ((iplots.le.nacross*ndown) .and. (nyplot.le.nacross*ndown .and. istepsonpage.eq.1)) if (iprint_axes) then if (debugmode) print*,'DEBUG: printing axes ',ipagechange,inewpage,iplots,nyplot,istepsonpage string = ' ' else if (debugmode) print*,'DEBUG: not printing axes ',ipagechange,inewpage,iplots,nyplot,istepsonpage string = 'NOPGBOX' endif call setpage2(ipanelpos,nacross,ndown,xmin,xmax,ymin,ymax, & trim(labelx),trim(labely),string,just,iaxistemp, & xminmargin,xmaxmargin,yminmargin,ymaxmargin, & 0.0,TitleOffset,isamexaxis,tile_plots,adjustlimitstodevice, & lastrow,lastplot,yscalealt,labelyalt,itransy) endif endif if (debugmode) print*,'DEBUG: setpage ok, querying and saving viewport...' !--query and save viewport co-ordinates set up for this panel call plot_qvp(0,vptxmin(ipanel),vptxmax(ipanel),vptymin(ipanel),vptymax(ipanel)) !-------------------------------------------------------------- ! store current page setup for interactive mode on multiplots !-------------------------------------------------------------- if (tile_plots) then barwmulti(ipanel) = 0. else barwmulti(ipanel) = barwidth endif iplotxtemp(ipanel) = iplotx iplotytemp(ipanel) = iploty irendertemp(ipanel) = irender icontourtemp(ipanel) = icontourplot ivecplottemp(ipanel) = ivectorplot xminmulti(iplotx) = xmin xmaxmulti(iplotx) = xmax xminmulti(iploty) = ymin xmaxmulti(iploty) = ymax if (irender.gt.0 .and. irender.le.size(xmaxmulti)) then xminmulti(irender) = rendermin xmaxmulti(irender) = rendermax if (icontourplot.gt.0 .and. gotcontours .and. icontourplot.le.size(xmaxmulti)) then xminmulti(icontourplot) = contmin xmaxmulti(icontourplot) = contmax endif endif if (ivectorplot.gt.0 .and. ivectorplot.le.numplot) then xmaxmulti(ivectorplot) = vecmax endif ! ! store adaptive plot limits for a) in interactive mode ! on multiple plots per page ! !--adaptive plot limits are allowed to change even during ! interactive replotting !if (.not.interactivereplot) then if (inewpage) then xminadapt = huge(xminadapt) xmaxadapt = -huge(xmaxadapt) endif xminadapt(iplotx) = min(xminadapt(iplotx),xminadapti) xmaxadapt(iplotx) = max(xmaxadapt(iplotx),xmaxadapti) xminadapt(iploty) = min(xminadapt(iploty),yminadapti) xmaxadapt(iploty) = max(xmaxadapt(iploty),ymaxadapti) if (irender.gt.0) then xminadapt(irender) = min(xminadapt(irender),renderminadapt) xmaxadapt(irender) = max(xmaxadapt(irender),rendermaxadapt) if (icontourplot.gt.0) then xminadapt(icontourplot) = min(xminadapt(icontourplot),contminadapt) xmaxadapt(icontourplot) = max(xmaxadapt(icontourplot),contmaxadapt) endif endif !endif !--change to background colour index for overlaid text and axes if (iUseBackGroundColourForAxes) then call plot_sci(0) call plot_set_opacity(1.) ! ensure background colour is opaque endif if (debugmode) print*,'DEBUG: finished page setup' return end subroutine page_setup !------------------------------------------------------ ! draws legend(s), titles etc ! (must be called after rendering otherwise rendering ! will overwrite plot area) !------------------------------------------------------ subroutine legends_and_title use colourbar, only:plotcolourbar use legends, only:legend,legend_markers,legend_scale,ipanelselect use titles, only:pagetitles,steplegend,lensteplegend use filenames, only:nstepsinfile,nfiles,rootname use settings_page, only:iPlotLegend,iPlotStepLegend, & hposlegend,vposlegend,fjustlegend,legendtext,iPlotLegendOnlyOnPanel, & iPlotScale,iscalepanel,dxscale,hposscale,vposscale,scaletext,& alphalegend,iUseBackGroundColourForAxes use shapes, only:nshapes,plot_shapes use pagesetup, only:xlabeloffset use plotlib, only:plot_qci,plot_sci,plot_annotate,plot_set_opacity use labels, only:is_coord use asciiutils, only:add_escape_chars implicit none integer :: icoloursave character(len=lensteplegend) :: steplegendtext real :: xlabeloffsettemp integer :: ititle logical :: usebox !--save colour index call plot_qci(icoloursave) !--use foreground colour by default for legends call plot_sci(1) !-------------------------------------------------------------- ! plot colour bar for rendered plots (use currently set colour) ! do this here so it always appears OVERLAID on the renderings !-------------------------------------------------------------- if (irender.gt.0) then !--only plot colour bar at the end of first row on tiled plots if (tile_plots .and..not.(ipanel.eq.nacross*ndown .or. lastplot .or. & (OneColourBarPerRow.and.icolumn.eq.nacross))) iPlotColourBar = .false. if (iPlotColourBar) then xlabeloffsettemp = xlabeloffset + 1.0 if (iaxistemp.lt.0) xlabeloffsettemp = 0. !--for tiled plots only on last plot in first row, ! and use full viewport size in the y direction if (tile_plots .and. .not.OneColourBarPerRow) then if (double_rendering .and. gotcontours) then call plotcolourbar(iColourBarStyle,icolours,contmin,contmax, & trim(labelcont),.false.,xlabeloffsettemp, & minval(vptxmin(1:ipanel)),maxval(vptxmax(1:ipanel)), & minval(vptymin(1:ipanel)),maxval(vptymax(1:ipanel))) else call plotcolourbar(iColourBarStyle,icolours,rendermin,rendermax, & trim(labelrender),.false.,xlabeloffsettemp, & minval(vptxmin(1:ipanel)),maxval(vptxmax(1:ipanel)), & minval(vptymin(1:ipanel)),maxval(vptymax(1:ipanel))) endif elseif (.not.tile_plots .or. (OneColourBarPerRow .and. icolumn.eq.nacross)) then !!--plot colour bar if (double_rendering .and. gotcontours) then !--for double rendering, plot the colour bar in the 2nd quantity call plotcolourbar(iColourBarStyle,icolours,contmin,contmax, & trim(labelcont),.false.,xlabeloffsettemp) else call plotcolourbar(iColourBarStyle,icolours,rendermin,rendermax, & trim(labelrender),.false.,xlabeloffsettemp) endif endif endif endif !--plot time on plot if (iPlotLegend .and. nyplot.eq.1 & .and. ipanelselect(iPlotLegendOnlyOnPanel,ipanel,irow,icolumn) & .and. timei.gt.-0.5*huge(timei)) then ! but not if time has not been read from dump !--change to background colour index for legend text if overlaid if (iUseBackGroundColourForAxes .and. vposlegend.gt.0.) then call plot_sci(0) call plot_set_opacity(alphalegend) endif usebox = (ivectorplot.gt.0) if (istepsonpage.eq.1) then call legend(legendtext,timei,labeltimeunits,hposlegend,vposlegend,fjustlegend,usebox) endif endif !--line/marker style/colour legend for multiple timesteps on same page if (iPlotStepLegend .and. istepsonpage.gt.0 & .and.((nyplot.eq.1 .and. iPlotLegendOnlyOnPanel.eq.0) & .or. ipanelselect(iPlotLegendOnlyOnPanel,ipanel,irow,icolumn))) then !--change to background colour index for overlaid text and axes if (iUseBackGroundColourForAxes .and. vposlegend.gt.0.) call plot_sci(0) ! !--use filenames in legend if none set ! if (nstepsperpage.ge.1 .and. nsteplegendlines.ge.nstepsperpage*nacross*ndown) then steplegendtext = steplegend(istepsonpage + (ipanel-1)*nstepsperpage) elseif (istepsonpage.le.nsteplegendlines) then steplegendtext = steplegend(istepsonpage) elseif (all(nstepsinfile(1:nfiles).le.1)) then steplegendtext = add_escape_chars(rootname(istep)) else write(steplegendtext,"(a,i4)") 'step ',istep endif if (debugmode) print "(a,i2,a)",& ' DEBUG: plotting step legend (step ',istepsonpage,': "'//trim(steplegendtext)//'")' if (iploty.gt.ndataplots) then call legend_markers(istepsonpage,linecolourthisstep,imarktype(1),linestylethisstep, & .false.,.true.,trim(steplegendtext),hposlegend,vposlegend,1.0) else call legend_markers(istepsonpage,linecolourthisstep,imarktype(1),linestylethisstep, & iusetype(1),iplotline,trim(steplegendtext),hposlegend,vposlegend,1.0) endif endif !--use foreground colour by default for title call plot_sci(1) !--print title if appropriate if (iPlotTitles .and. istepsonpage.eq.1 .and. ipanel.le.ntitles) then if (ntitles.gt.nacross*ndown) then ititle = (ipos - 1)/nstepsperpage + 1 if (ititle.gt.ntitles) ititle = ipanel else ititle = ipanel endif if (len_trim(pagetitles(ititle)).gt.0) then !--change to background colour index if title is overlaid if (iUseBackGroundColourForAxes .and. vpostitle.lt.0.) then call plot_sci(0) call plot_set_opacity(alphalegend) endif call plot_annotate('T',vpostitle,hpostitle,fjusttitle,trim(pagetitles(ititle))) endif endif !--use foreground colour by default for scale call plot_sci(1) !--scale on co-ordinate plots if (iPlotScale .and. (iscalepanel.eq.0 .or. ipanel.eq.iscalepanel) & .and. is_coord(iplotx,ndim) .and. is_coord(iploty,ndim)) then !--change to background colour index if title is overlaid if (iUseBackGroundColourForAxes .and. vposscale.gt.0.) then call plot_sci(0) call plot_set_opacity(alphalegend) endif call legend_scale(dxscale,hposscale,vposscale,scaletext) endif !--plot shapes if (nshapes.gt.0 .and. istepsonpage.eq.1) & call plot_shapes(ipanel,irow,icolumn,itrans(iplotx),itrans(iploty),timei) !--restore colour index call plot_sci(icoloursave) call plot_set_opacity(1.0) return end subroutine legends_and_title !-------------------------------------------- ! sets up a one dimensional grid of pixels ! and allocates memory for datpix1D !-------------------------------------------- subroutine set_grid1D(xmin1D,dxgrid1D,ngridpts) implicit none integer, intent(in) :: ngridpts real, intent(in) :: xmin1D, dxgrid1D integer :: igrid if (allocated(datpix1D)) deallocate(datpix1D) if (allocated(xgrid)) deallocate(xgrid) allocate (datpix1D(ngridpts)) allocate (xgrid(ngridpts)) do igrid = 1,ngridpts xgrid(igrid) = xmin1D + (igrid-0.5)*dxgrid1D enddo end subroutine set_grid1D !------------------------------------------------------------------- ! interface for setting limits when using particle tracking limits !------------------------------------------------------------------- subroutine settrackinglimits(itrackpart,iplot,xploti,xmini,xmaxi) use labels, only:is_coord use settings_limits, only:xminoffset_track,xmaxoffset_track implicit none integer, intent(in) :: itrackpart,iplot real, dimension(:), intent(in) :: xploti real, intent(inout) :: xmini,xmaxi !--particle tracking limits only apply to co-ordinate axes if (is_coord(iplot,ndim)) then xmini = xploti(itrackpart) - xminoffset_track(iplot) xmaxi = xploti(itrackpart) + xmaxoffset_track(iplot) endif return end subroutine settrackinglimits !------------------------------------------------------------------- ! interface for setting interpolation weights ! (to make calls above neater) !------------------------------------------------------------------- subroutine set_weights(weighti,dati,iamtypei,usetype) use settings_render, only:idensityweightedinterpolation use interpolation, only:set_interpolation_weights use settings_units, only:unit_interp use settings_xsecrot, only:rendersinks,use3Dopacityrendering implicit none real, dimension(:), intent(out) :: weighti real, dimension(:,:), intent(in) :: dati integer(kind=int1), dimension(:), intent(in) :: iamtypei logical, dimension(:), intent(in) :: usetype ihavesetweights = .true. inormalise = inormalise_interpolations call set_interpolation_weights(weighti,dati,iamtypei,usetype,& ninterp,npartoftype,masstype,ntypes,ndataplots,irho,ipmass,ih,ndim,& iRescale,idensityweightedinterpolation,inormalise,units,unit_interp,required,& (use3Dopacityrendering .and. rendersinks)) return end subroutine set_weights !------------------------------------------------------------------- ! interface to vector plotting routines ! so that pixel arrays are allocated appropriately !------------------------------------------------------------------- subroutine vector_plot(ivecx,ivecy,numpixx,numpixy,pixwidthvec,pixwidthvecy,vmax,label,got_h) use settings_vecplot, only:UseBackgndColorVecplot,iplotstreamlines,iplotarrowheads, & iplotsynchrotron,rcrit,zcrit,synchrotronspecindex,uthermcutoff, & ihidearrowswherenoparts,minpartforarrow,iVecplotLegend,iVecLegendOnPanel use interpolations2D, only:interpolate2D_vec use projections3D, only:interpolate3D_proj_vec,interp3D_proj_vec_synctron use interpolate_vec, only:mask_vectors,interpolate_vec_average use render, only:render_vec use fieldlines, only:streamlines,vecplot3D_proj use labels, only:iutherm,is_coord use plotlib, only:plot_qci,plot_qlw,plot_sci,plot_slw use system_utils, only:lenvironment use legends, only:ipanelselect implicit none integer, intent(in) :: ivecx,ivecy,numpixx,numpixy real, intent(in) :: pixwidthvec,pixwidthvecy real, intent(inout) :: vmax character(len=*), intent(in) :: label logical, intent(in) :: got_h real, dimension(numpixx,numpixy) :: vecpixx, vecpixy real, dimension(max(npixx,numpixx),max(npixy,numpixy)) :: datpixvec integer :: i,j,icoloursav,linewidthprev,ivecz real :: vmag real :: blankval,datmax logical :: usevecplot,use3Dstreamlines,plotlegend !--query colour index and line width call plot_qci(icoloursav) call plot_qlw(linewidthprev) !print*,'plotting vector field ',trim(label) if ((is_coord(ivecx,ndim) .or. ivecx.lt.0 .or.(ivecx.gt.ndataplots)) .or. & (is_coord(ivecy,ndim) .or. ivecy.lt.0 .or.(ivecy.gt.ndataplots))) then print*,'error finding location of vector plot in array' else use3Dstreamlines = (ndim.eq.3) .and. .not.x_sec !lenvironment('SPLASH_3DSTREAMLINES') !--plot arrows in either background or foreground colour if (UseBackgndColorVecplot) then call plot_sci(0) else call plot_sci(1) endif usevecplot = .false. if (irotate) then if (allocated(vecplot)) usevecplot = .true. if (debugmode) print*,'DEBUG: using vecplot' ! this is to indicate (to me) that extra memory is in use endif ! !--interpolate using appropriate routine for number of dimensions ! select case(ndim) case(3) if (x_sec) then ! take vector plot in cross section if (got_h) then if (usevecplot) then ! using rotation call interpolate3D_xsec_vec(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp), & vecplot(1,1:ninterp),vecplot(2,1:ninterp), & icolourme(1:ninterp),ninterp,xmin,ymin,zslicepos, & vecpixx,vecpixy,numpixx,numpixy,pixwidthvec,pixwidthvecy,inormalise) else call interpolate3D_xsec_vec(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp), & dat(1:ninterp,ivecx),dat(1:ninterp,ivecy), & icolourme(1:ninterp),ninterp,xmin,ymin,zslicepos, & vecpixx,vecpixy,numpixx,numpixy,pixwidthvec,pixwidthvecy,inormalise) endif else ! don't have smoothing length, use averaging if (usevecplot) then call interpolate_vec_average(xplot(1:ninterp),yplot(1:ninterp), & vecplot(1,1:ninterp),vecplot(2,1:ninterp),icolourme(1:ninterp), & xmin,ymin,pixwidthvec,pixwidthvecy,vecpixx,vecpixy, & ninterp,numpixx,numpixy,zplot(1:ninterp),zslicemin,zslicemax) else call interpolate_vec_average(xplot(1:ninterp),yplot(1:ninterp), & dat(1:ninterp,ivecx),dat(1:ninterp,ivecy),icolourme(1:ninterp), & xmin,ymin,pixwidthvec,pixwidthvecy,vecpixx,vecpixy, & ninterp,numpixx,numpixy,zplot(1:ninterp),zslicemin,zslicemax) endif endif else if (iplotsynchrotron .and. .not.iplotstreamlines .and. .not.iplotarrowheads) then !--get synchrotron polarisation vectors if (iutherm.gt.0 .and. iutherm.le.numplot .and. uthermcutoff.gt.0.) then if (usevecplot) then call interp3D_proj_vec_synctron(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),vecplot(1,1:ninterp),vecplot(2,1:ninterp), & icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,datpixvec(1:numpixx,1:numpixy),numpixx,numpixy,pixwidthvec, & rcrit,zcrit,synchrotronspecindex,pixwidthvec,.false., & dat(1:ninterp,iutherm),uthermcutoff) else call interp3D_proj_vec_synctron(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,ivecx),dat(1:ninterp,ivecy), & icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,datpixvec(1:numpixx,1:numpixy),numpixx,numpixy,pixwidthvec, & rcrit,zcrit,synchrotronspecindex,pixwidthvec,.false., & dat(1:ninterp,iutherm),uthermcutoff) endif else if (usevecplot) then call interp3D_proj_vec_synctron(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),vecplot(1,1:ninterp),vecplot(2,1:ninterp), & icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,datpixvec(1:numpixx,1:numpixy),numpixx,numpixy,pixwidthvec, & rcrit,zcrit,synchrotronspecindex,pixwidthvec,.false.) elseif (.not.iplotstreamlines) then call interp3D_proj_vec_synctron(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,ivecx),dat(1:ninterp,ivecy), & icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,datpixvec(1:numpixx,1:numpixy),numpixx,numpixy,pixwidthvec, & rcrit,zcrit,synchrotronspecindex,pixwidthvec,.false.) endif endif elseif (.not.(iplotstreamlines .and. use3Dstreamlines)) then if (got_h) then if (usevecplot) then if (.not.allocated(vecplot)) stop 'internal error: vecplot not allocated' call interpolate3D_proj_vec(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),vecplot(1,1:ninterp),vecplot(2,1:ninterp), & icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,numpixx,numpixy,pixwidthvec,pixwidthvecy,& .false.,zobservertemp,dzscreentemp) else call interpolate3D_proj_vec(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,ivecx),dat(1:ninterp,ivecy), & icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,numpixx,numpixy,pixwidthvec,pixwidthvecy, & .false.,zobservertemp,dzscreentemp) endif else ! don't have smoothing length, use averaging if (usevecplot) then call interpolate_vec_average(xplot(1:ninterp),yplot(1:ninterp), & vecplot(1,1:ninterp),vecplot(2,1:ninterp),icolourme(1:ninterp), & xmin,ymin,pixwidthvec,pixwidthvecy,vecpixx,vecpixy, & ninterp,numpixx,numpixy) else call interpolate_vec_average(xplot(1:ninterp),yplot(1:ninterp), & dat(1:ninterp,ivecx),dat(1:ninterp,ivecy),icolourme(1:ninterp), & xmin,ymin,pixwidthvec,pixwidthvecy,vecpixx,vecpixy, & ninterp,numpixx,numpixy) endif endif endif !--adjust the units of the z-integrated quantity !if (iRescale .and. units(ih).gt.0.) then ! vecpixx = vecpixx*(unitzintegration/units(ih)) ! vecpixy = vecpixy*(unitzintegration/units(ih)) !endif endif case(2) ! !--two dimensions ! if (got_h) then if (usevecplot) then call interpolate2D_vec(xplot(1:ninterp),yplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp),vecplot(1,1:ninterp), & vecplot(2,1:ninterp),icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,numpixx,numpixy,pixwidthvec,pixwidthvecy,inormalise,& isperiodicx,isperiodicy) else call interpolate2D_vec(xplot(1:ninterp),yplot(1:ninterp), & hh(1:ninterp),weight(1:ninterp),dat(1:ninterp,ivecx), & dat(1:ninterp,ivecy),icolourme(1:ninterp),ninterp,xmin,ymin, & vecpixx,vecpixy,numpixx,numpixy,pixwidthvec,pixwidthvecy,inormalise,& isperiodicx,isperiodicy) endif else ! don't have smoothing length, use averaging if (usevecplot) then call interpolate_vec_average(xplot(1:ninterp),yplot(1:ninterp), & vecplot(1,1:ninterp),vecplot(2,1:ninterp),icolourme(1:ninterp), & xmin,ymin,pixwidthvec,pixwidthvecy,vecpixx,vecpixy, & ninterp,numpixx,numpixy) else call interpolate_vec_average(xplot(1:ninterp),yplot(1:ninterp), & dat(1:ninterp,ivecx),dat(1:ninterp,ivecy),icolourme(1:ninterp), & xmin,ymin,pixwidthvec,pixwidthvecy,vecpixx,vecpixy, & ninterp,numpixx,numpixy) endif endif case default print "(a,i1,a)",'ERROR: Cannot do vector plotting in ',ndim,' dimensions' return end select ! !--plot it, either as streamlines or arrows ! if (iplotstreamlines) then if (ndim.eq.3) then !--normalise the 3D vector field do j=1,numpixy do i=1,numpixx vmag = sqrt(vecpixx(i,j)**2 + vecpixy(i,j)**2) if (vmag.gt.tiny(vmag)) then vecpixx(i,j) = vecpixx(i,j)/vmag vecpixy(i,j) = vecpixy(i,j)/vmag endif enddo enddo endif if (ndim.eq.3 .and. use3Dstreamlines .and. .not.x_sec) then if (usevecplot) then if (.not.allocated(vecplot)) stop 'vecplot not allocated' call vecplot3D_proj(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp), & vecplot(1,1:ninterp),vecplot(2,1:ninterp),vecplot(3,1:ninterp),vmax, & weight(1:ninterp),icolourme(1:ninterp),ninterp,pixwidthvec,zobservertemp,dzscreentemp) else ivecz = ivecx + (iplotz - ix(1)) call vecplot3D_proj(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp), & dat(1:ninterp,ivecx),dat(1:ninterp,ivecy),dat(1:ninterp,ivecz),vmax, & weight(1:ninterp),icolourme(1:ninterp),ninterp,pixwidthvec,zobservertemp,dzscreentemp) endif else call streamlines(vecpixx,vecpixy,datpixvec(1:numpixx,1:numpixy),numpixx,numpixy,pixwidthvec) if (ihidearrowswherenoparts) then datmax = maxval(datpixvec(1:numpixx,1:numpixy)) blankval = 2.*datmax call mask_vectors(xplot(1:ninterp),yplot(1:ninterp),icolourme(1:ninterp),ninterp, & xmin,xmax,ymin,ymax,datpixvec(1:numpixx,1:numpixy), & datpixvec(1:numpixx,1:numpixy),numpixx,numpixy,minpartforarrow,blankval) !--use blanking for values of zero call render_pix(datpixvec(1:numpixx,1:numpixy), & minval(datpixvec(1:numpixx,1:numpixy)), & datmax, & 'crap',numpixx,numpixy,xmin,ymin,pixwidthvec,pixwidthvecy, & 0,.true.,0,ncontours,.false.,ilabelcont,blank=blankval) else call render_pix(datpixvec(1:numpixx,1:numpixy), & minval(datpixvec(1:numpixx,1:numpixy)), & maxval(datpixvec(1:numpixx,1:numpixy)), & 'crap',numpixx,numpixy,xmin,ymin,pixwidthvec,pixwidthvecy, & 0,.true.,0,ncontours,.false.,ilabelcont) endif endif else if (ihidearrowswherenoparts) then call mask_vectors(xplot(1:ninterp),yplot(1:ninterp),icolourme(1:ninterp),ninterp, & xmin,xmax,ymin,ymax,vecpixx,vecpixy,numpixx,numpixy,minpartforarrow,0.) endif plotlegend = iVecplotLegend .and. ipanelselect(iVecLegendOnPanel,ipanel,irow,icolumn) call render_vec(vecpixx,vecpixy,vmax, & numpixx,numpixy,xmin,ymin,pixwidthvec,pixwidthvecy,trim(label),' ',plotlegend) if (iplotsynchrotron .and. .not. iplotarrowheads) then !--get synchrotron polarisation intensity using more pixels if (iutherm.gt.0 .and. iutherm.le.numplot .and. uthermcutoff.gt.0.) then call interp3D_proj_vec_synctron(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,ivecx),dat(1:ninterp,ivecy), & icolourme(1:ninterp),ninterp,xmin,ymin, & datpixvec(1:npixx,1:npixy),datpixvec(1:npixx,1:npixy), & ! these are just dummy arguments datpixvec(1:npixx,1:npixy),npixx,npixy,pixwidth, & rcrit,zcrit,synchrotronspecindex,pixwidthvec,.true., & dat(1:ninterp,iutherm),uthermcutoff) else call interp3D_proj_vec_synctron(xplot(1:ninterp), & yplot(1:ninterp),zplot(1:ninterp),hh(1:ninterp), & weight(1:ninterp),dat(1:ninterp,ivecx),dat(1:ninterp,ivecy), & icolourme(1:ninterp),ninterp,xmin,ymin, & datpixvec(1:npixx,1:npixy),datpixvec(1:npixx,1:npixy), & ! these are just dummy arguments datpixvec(1:npixx,1:npixy),npixx,npixy,pixwidth, & rcrit,zcrit,synchrotronspecindex,pixwidthvec,.true.) endif !--adjust the units of the z-integrated quantity !if (iRescale .and. units(ih).gt.0. .and..not.inormalise) then ! datpix = datpix*(unitzintegration/units(ih)) !endif !--plot contours of synchrotron intensity call render_pix(datpixvec(1:npixx,1:npixy),minval(datpixvec(1:npixx,1:npixy)), & maxval(datpixvec(1:npixx,1:npixy)),'crap', & npixx,npixy,xmin,ymin,pixwidth,pixwidthy,0,.true.,0,ncontours,.false.,ilabelcont) endif endif endif !--restore colour index and line width call plot_sci(icoloursav) call plot_slw(linewidthprev) end subroutine vector_plot end subroutine plotstep !---------------------------------------------------- ! adapt the (particle plot) limits to include all ! particles which are to be plotted on the page !---------------------------------------------------- subroutine adapt_limits(iplot,xploti,xmini,xmaxi,xminadaptive,xmaxadaptive,labeli,& iamtype,ntoti,npartoftype,iusetype,ipagechange) use params, only:int1,maxparttypes use labels, only:is_coord use limits, only:assert_sensible_limits use settings_limits, only:scalemax,iadapt,iadaptcoords use settings_data, only:debugmode,ndim use settings_part, only:iplotline implicit none integer, intent(in) :: iplot real, dimension(:), intent(in) :: xploti real, intent(inout) :: xmini,xmaxi,xminadaptive,xmaxadaptive character(len=*), intent(in) :: labeli integer(kind=int1), dimension(:), intent(in) :: iamtype integer, intent(in) :: ntoti integer, dimension(:), intent(in) :: npartoftype logical, dimension(:), intent(in) :: iusetype logical, intent(in) :: ipagechange integer :: index1,index2,itype,i logical :: mixedtypes !--calculate adaptive limits for this quantity xminadaptive = huge(xminadaptive) xmaxadaptive = -huge(xmaxadaptive) mixedtypes = size(iamtype).gt.1 if (mixedtypes) then do i=1,ntoti itype = iamtype(i) if (iusetype(itype) .or. (iplotline.and.itype.eq.1)) then xminadaptive = min(xminadaptive,xploti(i)) xmaxadaptive = max(xmaxadaptive,xploti(i))*scalemax endif enddo else index1 = 1 do itype=1,maxparttypes index2 = index1 + npartoftype(itype) - 1 if (iusetype(itype).and.npartoftype(itype).gt.0 & .or. (iplotline.and.itype.eq.1)) then xminadaptive = min(xminadaptive,minval(xploti(index1:index2))) xmaxadaptive = max(xmaxadaptive,maxval(xploti(index1:index2))*scalemax) endif index1 = index2 + 1 enddo endif !--avoid infs and NaNs call assert_sensible_limits(xminadaptive,xmaxadaptive) if (debugmode) print*,'DEBUG: ',iplot,': '//trim(labeli)// & 'min,max adaptive = ',xminadaptive,xmaxadaptive !--set these as limits if adaptive limits are on if (.not.interactivereplot) then if (((is_coord(iplot,ndim) .and. iadaptcoords) & .or.(.not.is_coord(iplot,ndim) .and. iadapt)) .and. ipagechange) then print "(1x,a)",'adapting '//trim(labeli)//' limits' xmini = xminadaptive xmaxi = xmaxadaptive endif endif end subroutine adapt_limits !------------------------------------------------------------------- ! interface to log, inverse transformations: ! also adjusts label (depending on ! whether log axes are also set or not). ! (independent) !------------------------------------------------------------------- subroutine applytrans(xploti,xmini,xmaxi,labelxi,itransxi,chaxis,iplotxi,iaxis,intreplot) use transforms, only:transform,transform_label,transform_limits use settings_data, only:numplot implicit none integer, intent(in) :: itransxi,iplotxi,iaxis real, dimension(:), intent(inout) :: xploti real, intent(inout) :: xmini,xmaxi character(len=*), intent(inout) :: labelxi character(len=1), intent(in) :: chaxis logical, intent(in) :: intreplot integer :: itranstemp,lstr character(len=20) :: string if (itransxi.ne.0) then if (iplotxi.gt.0 .and. iplotxi.le.numplot) call transform(xploti(:),itransxi) if ((chaxis.eq.'x' .and. (iaxis.eq.10 .or. iaxis.eq.30)).or. & (chaxis.eq.'y' .and. (iaxis.eq.20 .or. iaxis.eq.30))) then ! logarithmic axes write(string,*) itransxi string = adjustl(string) itranstemp = 0 lstr = len_trim(string) if (string(lstr:lstr).eq.'1') then if (lstr.gt.1) read(string(1:lstr-1),*) itranstemp labelxi = transform_label(labelxi,itranstemp) else labelxi = transform_label(labelxi,itransxi) endif else labelxi = transform_label(labelxi,itransxi) endif if (.not.intreplot) call transform_limits(xmini,xmaxi,itransxi) endif end subroutine applytrans !------------------------------------------------------------------- ! interface for adding rotation and perspective ! (completely independent) !------------------------------------------------------------------- subroutine rotationandperspective(anglexi,angleyi,anglezi,dzscreen,zobs,xploti,yploti,zploti, & ntot,iplotx,iploty,iplotz,dat,ivecstart,vecploti,itrackpart) use labels, only:ix use settings_data, only:ndim,xorigin,debugmode use settings_xsecrot, only:use3Dperspective use rotation, only:rotate2D,rotate3D use plotlib, only:plot_qcur implicit none real, intent(in) :: anglexi,angleyi,anglezi,dzscreen,zobs real, dimension(:), intent(inout) :: xploti,yploti,zploti real, dimension(:,:), intent(in) :: dat real, dimension(:,:), intent(out) :: vecploti integer, intent(in) :: ntot,iplotx,iploty,iplotz,ivecstart,itrackpart integer :: j,iposx,iposy,iposz,i real :: angleradx,anglerady,angleradz real, dimension(ndim) :: xcoords,veci ! !--convert angles to radians ! angleradz = anglezi*pi/180. anglerady = angleyi*pi/180. angleradx = anglexi*pi/180. if (plot_qcur()) then ! only print for interactive devices if (ndim.eq.3) then print "(1x,a,2(f6.2,1x),f6.2,a)",'rotation: (z, y, x) = (',anglezi,angleyi,anglexi,')' else print "(1x,a,f6.2)",'rotating particles about z by ',anglezi endif if (ndim.eq.3 .and. use3Dperspective) then print*,' observer height = ',zobs,', screen at ',zobs-dzscreen elseif (ndim.eq.3) then if (abs(zobs).gt.tiny(zobs) .or. abs(dzscreen).gt.tiny(dzscreen)) then print "(a)",' INTERNAL ERROR: no 3D perspective but observer set' endif endif if (itrackpart.gt.0 .and. itrackpart.le.ntot) then print*,'rotating about tracked particle ',itrackpart,' x,y,z = ',dat(itrackpart,ix(1:ndim)) elseif (any(abs(xorigin).ge.tiny(xorigin))) then print*,'rotating about x,y,z = ',xorigin(1:ndim) endif endif if (debugmode .and. ivecstart.gt.0) print "(1x,a)",'(also rotating vector components)' ! !--set location of x,y and z ! such that: ! ix(iposx) = iplotx ! ix(iposy) = iploty ! ix(iposz) = iplotz ! iposx = 1 ! this is "just in case" iposy = 2 iposz = 3 do i=1,ndim if (ix(i).eq.iplotx) then iposx = i elseif (ix(i).eq.iploty) then iposy = i elseif (ix(i).eq.iplotz) then iposz = i else print "(a)",' WARNING: internal error in ix setting for rotation: ix = ',ix(:) endif enddo if (debugmode) print*,'DEBUG: in rotation, iplotz = ',iplotz,' iposz = ',iposz, xorigin(:) !$omp parallel default(none) & !$omp shared(dat,xorigin,ndim,angleradx,anglerady,angleradz,zobs,dzscreen) & !$omp shared(xploti,yploti,zploti,iposx,iposy,iposz,iplotz,ntot,ix,itrackpart) & !$omp shared(vecploti,ivecstart) & !$omp private(j,xcoords,veci) !$omp do do j=1,ntot if (itrackpart.gt.0 .and. itrackpart.le.ntot) then xcoords(1:ndim) = dat(j,ix(1:ndim)) - dat(itrackpart,ix(1:ndim)) else xcoords(1:ndim) = dat(j,ix(1:ndim)) - xorigin(1:ndim) endif if (ndim.eq.2) then call rotate2D(xcoords(:),angleradz) elseif (ndim.eq.3) then call rotate3D(xcoords(1:ndim),angleradx,anglerady,angleradz,zobs,dzscreen) endif if (itrackpart.gt.0 .and. itrackpart.le.ntot) then xploti(j) = xcoords(iposx) + dat(itrackpart,ix(iposx)) yploti(j) = xcoords(iposy) + dat(itrackpart,ix(iposy)) if (iplotz.gt.0) then zploti(j) = xcoords(iposz) + dat(itrackpart,ix(iposz)) endif else xploti(j) = xcoords(iposx) + xorigin(iposx) yploti(j) = xcoords(iposy) + xorigin(iposy) if (iplotz.gt.0) then zploti(j) = xcoords(iposz) + xorigin(iposz) endif endif ! !--rotate vector components ! if (ivecstart.gt.0) then veci(1:ndim) = dat(j,ivecstart:ivecstart+ndim-1) if (ndim.eq.2) then call rotate2D(veci(:),angleradz) elseif (ndim.eq.3) then call rotate3D(veci(1:ndim),angleradx,anglerady,angleradz,zobs,dzscreen) endif vecploti(1,j) = veci(iposx) vecploti(2,j) = veci(iposy) if (ndim.ge.3) vecploti(3,j) = veci(iposz) endif enddo !$omp end do !$omp end parallel return end subroutine rotationandperspective !------------------------------------------------------------------- ! interface for plotting rotated axes !------------------------------------------------------------------- subroutine rotatedaxes(irotateaxes,iplotx,iploty,anglexi,angleyi,anglezi,dzscreen,zobs) use labels, only:ix use rotation, only:rotate_axes3D,rotate_axes2D use settings_data, only:ndim,xorigin use settings_xsecrot, only:xminrotaxes,xmaxrotaxes,use3Dperspective implicit none integer, intent(in) :: irotateaxes,iplotx,iploty real, intent(in) :: anglexi,angleyi,anglezi real, intent(inout) :: dzscreen,zobs real :: angleradx,anglerady,angleradz ! !--convert angles to radians ! angleradz = anglezi*pi/180. anglerady = angleyi*pi/180. angleradx = anglexi*pi/180. if (ndim.eq.3) then if (.not.use3Dperspective .and. dzscreen.gt.tiny(zobs)) then print "(a)",' INTERNAL ERROR: no 3D perspective but observer set' zobs = 0. dzscreen = 0. endif call rotate_axes3D(irotateaxes,iplotx-ix(1)+1,iploty-ix(1)+1, & xminrotaxes(1:ndim),xmaxrotaxes(1:ndim),xorigin(1:ndim), & angleradx,anglerady,angleradz,zobs,dzscreen) elseif (ndim.eq.2) then call rotate_axes2D(irotateaxes,xminrotaxes(1:ndim), & xmaxrotaxes(1:ndim),xorigin(1:ndim),angleradz) endif return end subroutine rotatedaxes end module timestep_plotting splash/src/particleplot.f90000644 000766 000000 00000062256 13261626263 016610 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module particleplots implicit none public :: particleplot,plot_errorbarsx,plot_errorbarsy public :: plot_kernel_gr contains ! ! Drives raw particle plots ! Handles different particle types, particle cross-sections, particle labelling ! fast-plotting added 12.10.06 (excludes particles in crowded fields) ! ! Arguments: ! ! subroutine particleplot(x,y,z,h,ntot,iplotx,iploty,icolourpart,iamtype,noftype,iplot_type, & use_zrange,zmin,zmax,labelz,xmin,xmax,ymin,ymax, & fast,datpix,nx,ny,dval,brightness) use params, only:int1 use labels, only:labeltype, maxparttypes,is_coord use settings_data, only:ndim,icoords,ntypes use settings_part, only:imarktype,ncircpart,icoordsnew,icircpart,itypeorder, & ilabelpart,iplotline,linestylethisstep,linecolourthisstep use interpolations2D, only:interpolate_part,interpolate_part1 use transforms, only:transform use part_utils, only:igettype use sort, only:indexx use plotlib, only:plot_qci,plot_bbuf,plot_ebuf,plot_sci,plot_sfs,plot_circ, & plot_pt,plot_numb,plot_text,plot_pt1,plot_qls,plot_sls, & plot_line,plot_qlw,plot_slw,plot_errb,plotlib_maxlinestyle implicit none integer, intent(in) :: ntot,iplotx, iploty integer(kind=int1), intent(in) :: iamtype(:) integer, intent(in) :: icolourpart(:) integer, intent(in) :: noftype(maxparttypes) real, intent(in) :: x(:), y(:), z(:), h(:) real, intent(in) :: zmin,zmax,xmin,xmax,ymin,ymax logical, intent(in) :: use_zrange,fast logical, intent(in) :: iplot_type(maxparttypes) character(len=*), intent(in) :: labelz integer, intent(in), optional :: nx,ny real, intent(inout), optional :: datpix(:,:),brightness(:,:) real, intent(in), optional :: dval integer :: j,n,itype,linewidth,icolourindex,nplotted,oldlinestyle,ierr integer :: lenstring,index1,index2,ntotplot,icolourstart,nlooptypes,ilooptype integer :: nplottedtype(maxparttypes) character(len=20) :: string integer, parameter :: ncellx = 500, ncelly = 500 ! for crowded field reduction integer(kind=int1) :: nincell(ncellx,ncelly,maxparttypes) integer :: icellx,icelly,maxz real :: dx1,dy1,dxpix logical :: mixedtypes real, allocatable :: xerrb(:), yerrb(:), herr(:) !--query current character height and colour call plot_qci(icolourstart) ! !--check for errors in input ! ntotplot = sum(noftype(1:ntypes)) if (ntot.lt.ntotplot) then print "(a)",' ERROR: number of particles input < number of each type ' print*,ntot,noftype(1:ntypes) return elseif (ntot.ne.ntotplot) then print "(a)",' WARNING: particleplot: total not equal to sum of types on input' print*,' ntotal = ',ntot,' sum of types = ',ntotplot endif maxz = size(z) if (maxz > ntot) maxz = ntot if (use_zrange .and. maxz.lt.ntot) then print "(a)",' WARNING: particleplot: slice plot but z array too small - excluding particles > z array size' endif dxpix = 0. if (present(datpix)) then if (.not.(present(nx).and.present(ny).and.present(dval))) then print "(a)",' INTERNAL ERROR in call to particleplot: optional args not present' return else dxpix = (xmax - xmin)/real(nx) endif endif ! !--loop over all particle types ! index1 = 1 nplottedtype = 0 nlooptypes = ntypes mixedtypes = size(iamtype).gt.1 if (mixedtypes .or. use_zrange) nlooptypes = 1 dx1 = (ncellx - 1)/(xmax-xmin + tiny(xmin)) dy1 = (ncelly - 1)/(ymax-ymin + tiny(ymin)) nincell = 0 over_types: do ilooptype=1,nlooptypes call plot_bbuf !--buffer plot output until each particle type finished if (mixedtypes .or. use_zrange) then index1 = 1 index2 = ntot itype = 0 else itype = itypeorder(ilooptype) if (itype.eq.1) then index1 = 1 else index1 = sum(noftype(1:itype-1))+1 endif index2 = index1 + noftype(itype) - 1 if (.not.iplot_type(itype)) then call plot_ebuf cycle over_types endif endif if (index2.gt.ntot) then index2 = ntot print "(a)",' WARNING: incomplete data' endif if (index2.lt.index1) then call plot_ebuf cycle over_types endif if (use_zrange) then ! !--if particle cross section, plot particles only in a defined (z) coordinate range ! nplotted = 0 overj: do j=1,ntot if (mixedtypes) then itype = min(max(int(iamtype(j)),1),maxparttypes) else itype = igettype(j,noftype) endif if (.not. iplot_type(itype)) cycle overj if (j.le.maxz) then if (z(j) > zmin .and. z(j) < zmax) then if (icolourpart(j).ge.0) then nplotted = nplotted + 1 nplottedtype(itype) = nplottedtype(itype) + 1 if (fast .and. noftype(itype) > 100) then if (in_cell(icellx,icelly,x(j),y(j),xmin,ymin,dx1,dy1,ncellx,ncelly)) then if (nincell(icellx,icelly,itype).eq.0) then nincell(icellx,icelly,itype) = nincell(icellx,icelly,itype) + 1_int1 ! this +1 of type int*1 call plot_sci(icolourpart(j)) call plot_particle(imarktype(itype),x(j),y(j),h(j)) endif endif else call plot_sci(icolourpart(j)) call plot_particle(imarktype(itype),x(j),y(j),h(j)) endif if (present(datpix)) then if (present(brightness)) then call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval,brightness) else call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval) endif endif endif !--plot circle of interaction if gas particle if (itype.eq.1 .and. ncircpart.gt.0 .and. ANY(icircpart(1:ncircpart).eq.j)) then call plot_circ(x(j),y(j),2*h(j)) endif !!--plot particle label if (ilabelpart) then call plot_numb(j,0,1,string,lenstring) call plot_text(x(j),y(j),string(1:lenstring)) endif endif endif enddo overj do itype=1,ntypes if (iplot_type(itype) .and. nplottedtype(itype).gt.0) then if (zmin < -0.1*huge(zmin)) then print*,'plotted ',nplottedtype(itype),' of ',noftype(itype), & trim(labeltype(itype))//' particles with ', trim(labelz),' < ',zmax else print*,'plotted ',nplottedtype(itype),' of ',noftype(itype), & trim(labeltype(itype))//' particles in range ', trim(labelz),' = ',zmin,' -> ',zmax endif endif enddo else ! !--otherwise plot all particles of this type using appropriate marker and colour ! call plot_qci(icolourindex) ! !--all particles in range have same colour and type ! if (.not.mixedtypes .and. all(icolourpart(index1:index2).eq.icolourpart(index1)) & .and. icolourpart(index1).ge.0) then call plot_sci(icolourpart(index1)) if (fast .and. (index2-index1).gt.100) then !--fast-plotting only allows one particle per "grid cell" - avoids crowded fields write(*,"(a,i8,1x,a)") ' fast-plotting ',index2-index1+1,trim(labeltype(itype))//' particles' nincell = 0 do j=index1,index2 if (in_cell(icellx,icelly,x(j),y(j),xmin,ymin,dx1,dy1,ncellx,ncelly)) then if (nincell(icellx,icelly,itype).eq.0) then nincell(icellx,icelly,itype) = nincell(icellx,icelly,itype) + 1_int1 ! this +1 of type int*1 call plot_particle(imarktype(itype),x(j),y(j),h(j)) if (present(datpix)) then if (present(brightness)) then call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval,brightness) else call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval) endif endif endif endif enddo else !--plot all particles of this type print "(a,i8,1x,a)",' plotting ',index2-index1+1,trim(labeltype(itype))//' particles' select case(imarktype(itype)) case(32:35) do j=1,noftype(itype) call plot_particle(imarktype(itype),x(j),y(j),h(j)) enddo call plot_sfs(1) case default call plot_pt(noftype(itype),x(index1:index2),y(index1:index2),imarktype(itype)) end select if (present(datpix)) then if (present(brightness)) then call interpolate_part(x(index1:index2),y(index1:index2),h(index1:index2), & noftype(itype),xmin,ymin,datpix,nx,ny,dxpix,dval,brightness) else call interpolate_part(x(index1:index2),y(index1:index2),h(index1:index2), & noftype(itype),xmin,ymin,datpix,nx,ny,dxpix,dval) endif endif endif else ! !--mixed colours and/or mixed types ! nplotted = 0 nplottedtype = 0 overj2: do j=index1,index2 if (icolourpart(j).ge.0) then if (mixedtypes) then itype = int(iamtype(j)) if (.not.iplot_type(itype)) cycle overj2 nplottedtype(itype) = nplottedtype(itype) + 1 endif nplotted = nplotted + 1 if (fast .and. noftype(itype) > 100) then if (in_cell(icellx,icelly,x(j),y(j),xmin,ymin,dx1,dy1,ncellx,ncelly)) then !--exclude particles if there are more than 2 particles per cell ! (two here because particles can have different colours) if (nincell(icellx,icelly,itype).le.0) then nincell(icellx,icelly,itype) = nincell(icellx,icelly,itype) + 1_int1 ! this +1 of type int*1 call plot_sci(icolourpart(j)) call plot_particle(imarktype(itype),x(j),y(j),h(j)) if (present(datpix)) then if (present(brightness)) then call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval,brightness) else call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval) endif endif endif endif else call plot_sci(icolourpart(j)) call plot_particle(imarktype(itype),x(j),y(j),h(j)) if (present(datpix)) then if (present(brightness)) then call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval,brightness) else call interpolate_part1(x(j),y(j),h(j),xmin,ymin,datpix,nx,ny,dxpix,dval) endif endif endif endif enddo overj2 if (mixedtypes) then do itype=1,ntypes if (iplot_type(itype)) then if (fast .and. noftype(itype).gt.100) then print*,' fast-plotted ',nplottedtype(itype),' of ',noftype(itype),trim(labeltype(itype))//' particles' elseif (noftype(itype).gt.0) then print*,' plotted ',nplottedtype(itype),' of ',noftype(itype),trim(labeltype(itype))//' particles' endif endif enddo else if (fast .and. noftype(itype).gt.100) then print*,' fast-plotted ',nplotted,' of ',index2-index1+1,trim(labeltype(itype))//' particles' else print*,' plotted ',nplotted,' of ',index2-index1+1,trim(labeltype(itype))//' particles' endif endif endif call plot_sci(icolourindex) if (ilabelpart) then !!--plot particle labels print*,'plotting particle labels ',index1,':',index2 do j=index1,index2 call plot_numb(j,0,1,string,lenstring) call plot_text(x(j),y(j),string(1:lenstring)) enddo endif endif index1 = index2 + 1 call plot_ebuf !--flush PGPLOT buffer at end of each type enddo over_types ! !--plot lines joining particles if relevant ! call plot_qci(icolourindex) call plot_sci(linecolourthisstep) ! i.e., don't plot a line for cross section plots (would plot all particles) ! but do if there is 3D perspective --> in which case zmin = -huge(x) if (iplotline .and. .not.(use_zrange .and. abs(zmax-zmin).lt.0.5*huge(0.))) then call plot_qls(oldlinestyle) call plot_sls(linestylethisstep) call plot_line(noftype(1),x(1:noftype(1)),y(1:noftype(1))) if (noftype(2).gt.0 .and. iplot_type(2)) then call plot_sls(mod(linestylethisstep+1,plotlib_maxlinestyle) + 1) call plot_line(noftype(2),x(noftype(1)+1:sum(noftype(1:2))),y(noftype(1)+1:sum(noftype(1:2)))) endif call plot_sls(oldlinestyle)! reset endif call plot_sci(icolourindex) ! !--plot circles of interaction (ie a circle of radius 2h) ! around all or selected particles. For plots with only one coordinate axis, ! these are plotted as error bars in the coordinate direction. ! !--this bit is also used for error bar plotting on x or y axis. ! if (ncircpart.gt.0) then ! !--set fill area style and line width ! call plot_qlw(linewidth) call plot_slw(2) call plot_qci(icolourindex) call plot_sci(2) call plot_sfs(2) if (ncircpart.gt.0) then if (is_coord(iplotx,ndim) .and. is_coord(iploty,ndim) .and. ncircpart.gt.0) then print*,'plotting ',ncircpart,' circles of interaction' do n = 1,ncircpart if (icircpart(n).gt.ntot) then print*,'error: particle index > number of particles' else if (icoordsnew.ne.icoords) then call plot_kernel_gr(icoordsnew,icoords,iplotx,iploty,& x(icircpart(n)),y(icircpart(n)),z(icircpart(n)),2*h(icircpart(n))) else call plot_circ(x(icircpart(n)),y(icircpart(n)),2*h(icircpart(n))) endif endif enddo else if (.not.allocated(herr)) then allocate(xerrb(ncircpart),yerrb(ncircpart),herr(ncircpart),stat=ierr) if (ierr /= 0) & stop ' Error allocating memory in particleplot for circles of interaction' endif !!--only on specified particles do n=1,ncircpart if (icircpart(n).gt.ntot) then print*,'error: particle index > number of particles' xerrb(n) = 0. yerrb(n) = 0. herr(n) = 0. else xerrb(n) = x(icircpart(n)) yerrb(n) = y(icircpart(n)) herr(n) = 2.*h(icircpart(n)) endif enddo if (is_coord(iplotx,ndim)) then print*,'plotting ',ncircpart,' error bars x axis ' call plot_errb(5,ncircpart,xerrb(1:ncircpart),yerrb(1:ncircpart),herr(1:ncircpart),1.0) elseif (is_coord(iploty,ndim)) then print*,'plotting ',ncircpart,' error bars y axis' call plot_errb(6,ncircpart,xerrb(1:ncircpart),yerrb(1:ncircpart),herr(1:ncircpart),1.0) endif if (allocated(herr)) deallocate(herr) if (allocated(xerrb)) deallocate(xerrb) if (allocated(yerrb)) deallocate(yerrb) endif endif call plot_slw(linewidth) call plot_sci(icolourindex) endif ! !--reset colour ! call plot_sci(icolourstart) return end subroutine particleplot !-------------------------------------------------------------------------------- ! ! subroutine implementing scalable markers ! default case is just an interface to usual particle plotting routine ! !-------------------------------------------------------------------------------- subroutine plot_particle(imarktype,x,y,h) use plotlib, only:plot_circ,plot_sfs,plot_sci,plot_pt1 use settings_part, only:hfacmarkers implicit none integer, intent(in) :: imarktype real, intent(in) :: x,y,h integer :: imarker real :: size select case(imarktype) case(32:35) imarker = imarktype - 31 size = hfacmarkers*h if (imarker.le.2) then call plot_sfs(imarker) call plot_circ(x,y,size) call plot_sfs(1) elseif (imarker.eq.3) then call plot_sfs(1) call plot_circ(x,y,size) call plot_sfs(2) call plot_sci(0) call plot_circ(x,y,size) call plot_sfs(1) elseif (imarker.eq.4) then call plot_sfs(1) call plot_circ(x,y,size) call plot_sfs(2) call plot_sci(1) call plot_circ(x,y,size) call plot_sfs(1) else call plot_circ(x,y,size) endif case default call plot_pt1(x,y,imarktype) end select end subroutine plot_particle !------------------------------------------------------------ ! ! function used to determine which cell a particle lies in ! returns TRUE if within allowed limits, FALSE if not ! !------------------------------------------------------------ logical function in_cell(ix,iy,x,y,xmin,ymin,dx1,dy1,nx,ny) integer, intent(out) :: ix,iy real, intent(in) :: x,y,xmin,ymin,dx1,dy1 integer, intent(in) :: nx,ny ix = int((x - xmin)*dx1) + 1 iy = int((y - ymin)*dy1) + 1 !--exclude particles if there are more than 2 particles per cell ! (two here because particles can have different colours) in_cell = (ix.gt.0 .and. ix.le.nx .and. iy.gt.0 .and. iy.le.ny) end function in_cell !-------------------------------------------------------------------------------- ! ! subroutine to plot the circle of interaction for a given particle ! in general coordinate systems (e.g. cylindrical coordinates) ! ! input: igeom : coordinate system (0,1=cartesian, 2=cylindrical, 3=spherical) ! x,y : particle location in cartesian space ! h : size of smoothing sphere ! (assumed isotropic in coordinate space) ! ! PGPLOT page must already be set up - this just draws the "circle" ! !-------------------------------------------------------------------------------- subroutine plot_kernel_gr(igeom,igeomold,iplotx,iploty,x,y,z,h) use geometry, only:coord_transform,maxcoordsys,labelcoordsys use plotlib, only:plot_line use labels, only:ix implicit none integer, intent(in) :: igeom,igeomold,iplotx,iploty real, intent(in) :: x,y,z,h integer, parameter :: npts = 100 ! big enough to give a smooth circle real, parameter :: pi = 3.1415926536 integer :: i,iplotz real, dimension(3) :: xtemp,xplot_coords real, dimension(3,npts) :: xpts real :: angle, dangle, xi, yi, zi if (igeom.gt.1 .and. igeom.le.maxcoordsys) then print 10,labelcoordsys(igeom) else print 10,labelcoordsys(1) endif 10 format('coordinate system = ',a) iplotz = 0 do i=1,3 if (iplotx.ne.ix(i) .and. iploty.ne.ix(i)) iplotz = ix(i) enddo if (iplotz==0) return xplot_coords = (/x,y,z/) ! not actual x,y,z xtemp(iplotx-ix(1)+1) = xplot_coords(1) xtemp(iploty-ix(1)+1) = xplot_coords(2) xtemp(iplotz-ix(1)+1) = xplot_coords(3) !--e.g. from cylindricals TO cartesians call coord_transform(xtemp,3,igeom,xpts(:,1),3,igeomold) xi = xpts(1,1) yi = xpts(2,1) zi = xpts(3,1) ! !--step around a circle in co-ordinate space of radius h and store the ! location of the points in cartesian space in the 2D array xpts ! dangle = 2.*pi/REAL(npts-1) do i=1,npts angle = (i-1)*dangle xtemp(1) = xi + h*COS(angle) xtemp(2) = yi + h*SIN(angle) xtemp(3) = zi ! !--translate back to actual coordinate system plotted ! call coord_transform(xtemp,3,igeomold,xpts(:,i),3,igeom) enddo ! !--now plot the circle using pgline ! call plot_line(npts,xpts(iplotx-ix(1)+1,:),xpts(iploty-ix(1)+1,:)) return end subroutine plot_kernel_gr !-------------------------------------------------------------------------------- ! ! Plot y-axis error bars, handling the case where the axes are transformed ! ! input x,y are in transformed space (i.e., already logged) ! input err is not transformed, (i.e., not logged) ! !-------------------------------------------------------------------------------- subroutine plot_errorbarsy(npts,x,y,err,itrans) use plotlib, only:plot_bbuf,plot_ebuf,plot_err1,plot_errb use transforms, only:transform,transform_inverse,islogged use settings_part, only:ErrorBarType use settings_data, only:iverbose implicit none integer, intent(in) :: npts,itrans real, intent(in), dimension(:) :: x,y,err real :: yval,errval real, dimension(2) :: val real, dimension(npts) :: errp,errm integer :: i if (iverbose >= 1) then if (npts < 10000) then print "(a,i4,a)",' plotting ',npts,' error bars y axis' else print "(a,i10,a)",' plotting ',npts,' error bars y axis' endif endif if (itrans.ne.0) then if (islogged(itrans)) then errval = 0. !-300. else errval = 0. endif !call plot_bbuf do i=1,npts yval = y(i) call transform_inverse(yval,itrans) val(1) = yval + err(i) val(2) = yval - err(i) call transform(val,itrans,errval=errval) errp(i) = val(1) - y(i) errm(i) = y(i) - val(2) val(1) = val(1) - y(i) val(2) = y(i) - val(2) if (ErrorBarType /= 1) then call plot_err1(2,x(i),y(i),val(1),1.0) call plot_err1(4,x(i),y(i),val(2),1.0) endif enddo if (ErrorBarType==1) then call plot_errb(7,npts,x,y,errp,1.0) call plot_errb(8,npts,x,y,errm,1.0) endif !call plot_ebuf else if (ErrorBarType==1) then call plot_errb(9,npts,x,y,err,1.0) else call plot_errb(6,npts,x,y,err,1.0) endif endif end subroutine plot_errorbarsy !-------------------------------------------------------------------------------- ! ! Plot x-axis error bars, handling the case where the axes are transformed ! ! input x,y are in transformed space (i.e., already logged) ! input err is not transformed, (i.e., not logged) ! !-------------------------------------------------------------------------------- subroutine plot_errorbarsx(npts,x,y,err,itrans) use transforms, only:transform,transform_inverse,islogged use plotlib, only:plot_bbuf,plot_ebuf,plot_err1,plot_errb implicit none integer, intent(in) :: npts,itrans real, intent(in), dimension(:) :: x,y,err real :: xval,errval real, dimension(2) :: val integer :: i print*,'plotting ',npts,' error bars x axis ' if (itrans.ne.0) then if (islogged(itrans)) then errval = -300. else errval = 0. endif call plot_bbuf do i=1,npts xval = x(i) call transform_inverse(xval,itrans) val(1) = xval + err(i) val(2) = xval - err(i) call transform(val,itrans,errval=errval) val(1) = val(1) - x(i) val(2) = x(i) - val(2) call plot_err1(1,x(i),y(i),val(1),1.0) call plot_err1(3,x(i),y(i),val(2),1.0) enddo call plot_ebuf else call plot_errb(5,npts,x,y,err,1.0) endif end subroutine plot_errorbarsx end module particleplots splash/src/interpolate2D.f90000644 000766 000000 00000063211 13261626263 016612 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! ! Module containing all of the routines required for 2D interpolation ! !---------------------------------------------------------------------- module interpolations2D use kernels, only:radkernel2,radkernel,cnormk2D,wfunc implicit none public :: interpolate2D, interpolate2D_xsec, interpolate2D_vec public :: interpolate_part, interpolate_part1 public :: interpolate2D_pixels private contains !-------------------------------------------------------------------------- ! subroutine to interpolate from particle data to even grid of pixels ! ! The data is smoothed using the SPH summation interpolant, ! that is, we compute the smoothed array according to ! ! datsmooth(pixel) = sum_j w_j dat_j W(r-r_j, h_j) ! ! where _j is the quantity at the neighbouring particle j and ! W is the smoothing kernel, for which we use the usual cubic spline. ! For an SPH interpolation the weight for each particle should be ! the dimensionless quantity ! ! w_j = m_j / (rho_j * h_j**ndim) ! ! Other weights can be used (e.g. constants), but in this case the ! normalisation option should also be set. ! ! Input: particle coordinates : x,y (npart) ! smoothing lengths : hh (npart) ! interpolation weights : weight (npart) ! scalar data to smooth : dat (npart) ! ! number of pixels in x,y : npixx,npixy ! pixel width : pixwidth ! option to normalise interpolation : normalise (.true. or .false.) ! ! Output: smoothed data : datsmooth (npixx,npixy) ! ! Written by Daniel Price 2003-2012 !-------------------------------------------------------------------------- subroutine interpolate2D(x,y,hh,weight,dat,itype,npart, & xmin,ymin,datsmooth,npixx,npixy,pixwidthx,pixwidthy,& normalise,periodicx,periodicy) implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy real, intent(out), dimension(npixx,npixy) :: datsmooth logical, intent(in) :: normalise,periodicx,periodicy real, dimension(npixx,npixy) :: datnorm integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax integer :: ipixi,jpixi real :: hi,hi1,radkern,q2,wab,const real :: term,termnorm,dx,dy,xpix,ypix datsmooth = 0. datnorm = 0. if (normalise) then print "(1x,a)",'interpolating from particles to 2D grid (normalised)...' else print "(1x,a)",'interpolating from particles to 2D grid (non-normalised)...' endif if (pixwidthx.le.0. .or. pixwidthy.le.0.) then print "(1x,a)",'interpolate2D: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate2D: warning: ignoring some or all particles with h < 0' endif const = cnormk2D ! normalisation constant ! !--loop over particles ! over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--skip particles with zero weights ! termnorm = const*weight(i) if (termnorm.le.0.) cycle over_parts ! !--skip particles with wrong h's ! hi = hh(i) if (hi.le.tiny(hi)) cycle over_parts ! !--set kernel related quantities ! hi1 = 1./hi radkern = radkernel*hi ! radius of the smoothing kernel term = termnorm*dat(i) ! !--for each particle work out which pixels it contributes to ! ipixmin = int((x(i) - radkern - xmin)/pixwidthx) jpixmin = int((y(i) - radkern - ymin)/pixwidthy) ipixmax = int((x(i) + radkern - xmin)/pixwidthx) + 1 jpixmax = int((y(i) + radkern - ymin)/pixwidthy) + 1 if (.not.periodicx) then if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (ipixmax.gt.npixx) ipixmax = npixx ! to pixels in the image endif if (.not.periodicy) then if (jpixmin.lt.1) jpixmin = 1 if (jpixmax.gt.npixy) jpixmax = npixy endif ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax jpixi = jpix if (periodicy) then if (jpixi.lt.1) jpixi = mod(jpixi,npixy) + npixy if (jpixi.gt.npixy) jpixi = mod(jpixi-1,npixy) + 1 endif ypix = ymin + (jpix-0.5)*pixwidthy dy = ypix - y(i) do ipix = ipixmin,ipixmax ipixi = ipix if (periodicx) then if (ipixi.lt.1) ipixi = mod(ipixi,npixx) + npixx if (ipixi.gt.npixx) ipixi = mod(ipixi-1,npixx) + 1 endif xpix = xmin + (ipix-0.5)*pixwidthx dx = xpix - x(i) q2 = (dx*dx + dy*dy)*hi1*hi1 ! !--SPH kernel ! wab = wfunc(q2) ! !--calculate data value at this pixel using the summation interpolant ! datsmooth(ipixi,jpixi) = datsmooth(ipixi,jpixi) + term*wab if (normalise) datnorm(ipixi,jpixi) = datnorm(ipixi,jpixi) + termnorm*wab enddo enddo enddo over_parts ! !--normalise dat array ! if (normalise) then where (datnorm > 0.) datsmooth = datsmooth/datnorm end where endif return end subroutine interpolate2D !-------------------------------------------------------------------------- ! ! ** this version does vector quantities ! ! Input: particle coordinates : x,y (npart) ! smoothing lengths : hh (npart) ! interpolation weights : weight (npart) ! vector data to smooth : vecx (npart) ! vecy (npart) ! ! Output: smoothed vector field : vecsmoothx (npixx,npixy) ! : vecsmoothy (npixx,npixy) ! ! Daniel Price, University of Exeter, March 2005 !-------------------------------------------------------------------------- subroutine interpolate2D_vec(x,y,hh,weight,vecx,vecy,itype,npart, & xmin,ymin,vecsmoothx,vecsmoothy,npixx,npixy,pixwidthx,pixwidthy,& normalise,periodicx,periodicy) implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,hh,weight,vecx,vecy integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy real, intent(out), dimension(npixx,npixy) :: vecsmoothx,vecsmoothy logical, intent(in) :: normalise,periodicx,periodicy real, dimension(npixx,npixy) :: datnorm integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax integer :: ipixi,jpixi real :: hi,hi1,radkern,q2,wab,const real :: termnorm,termx,termy,dx,dy,xpix,ypix vecsmoothx = 0. vecsmoothy = 0. datnorm = 0. if (normalise) then print "(1x,a)",'interpolating vector field from particles to 2D grid (normalised)...' else print "(1x,a)",'interpolating vector field from particles to 2D grid (non-normalised)...' endif if (pixwidthx.le.0. .or. pixwidthy.le.0.) then print*,'interpolate2D_vec: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate2D_vec: warning: ignoring some or all particles with h < 0' endif const = cnormk2D ! normalisation constant ! !--loop over particles ! over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--skip particles with zero weights ! termnorm = const*weight(i) if (termnorm.le.0.) cycle over_parts ! !--skip particles with wrong h's ! hi = hh(i) if (hi.le.tiny(hi)) cycle over_parts ! !--set kernel related quantities ! hi1 = 1./hi radkern = radkernel*hi ! radius of the smoothing kernel termx = termnorm*vecx(i) termy = termnorm*vecy(i) ! !--for each particle work out which pixels it contributes to ! ipixmin = int((x(i) - radkern - xmin)/pixwidthx) jpixmin = int((y(i) - radkern - ymin)/pixwidthy) ipixmax = int((x(i) + radkern - xmin)/pixwidthx) + 1 jpixmax = int((y(i) + radkern - ymin)/pixwidthy) + 1 if (.not.periodicx) then if (ipixmin.lt.1) ipixmin = 1 if (ipixmax.gt.npixx) ipixmax = npixx endif if (.not.periodicy) then if (jpixmin.lt.1) jpixmin = 1 if (jpixmax.gt.npixy) jpixmax = npixy endif ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax jpixi = jpix if (periodicy) then if (jpixi.lt.1) jpixi = mod(jpixi,npixy) + npixy if (jpixi.gt.npixy) jpixi = mod(jpixi-1,npixy) + 1 endif ypix = ymin + (jpix-0.5)*pixwidthy dy = ypix - y(i) do ipix = ipixmin,ipixmax ipixi = ipix if (periodicx) then if (ipixi.lt.1) ipixi = mod(ipixi,npixx) + npixx if (ipixi.gt.npixx) ipixi = mod(ipixi-1,npixx) + 1 endif xpix = xmin + (ipix-0.5)*pixwidthx dx = xpix - x(i) q2 = (dx*dx + dy*dy)*hi1*hi1 ! !--SPH kernel ! wab = wfunc(q2) ! !--calculate data value at this pixel using the summation interpolant ! vecsmoothx(ipixi,jpixi) = vecsmoothx(ipixi,jpixi) + termx*wab vecsmoothy(ipixi,jpixi) = vecsmoothy(ipixi,jpixi) + termy*wab if (normalise) datnorm(ipixi,jpixi) = datnorm(ipixi,jpixi) + termnorm*wab enddo enddo enddo over_parts ! !--normalise dat arrays ! if (normalise) then where (datnorm > 0.) vecsmoothx = vecsmoothx/datnorm vecsmoothy = vecsmoothy/datnorm end where endif return end subroutine interpolate2D_vec !-------------------------------------------------------------------------- ! subroutine to interpolate from particle data to even grid of pixels ! ! this version takes any 1D cross section through a 2D data set ! the 1D line is specified by two points, (x1,y1) and (x2,y2) ! (ie. this is for arbitrary oblique cross sections) ! ! NB: A similar version could be used for 2D oblique cross sections ! of 3D data. In this case we would need to find the intersection ! between the smoothing sphere and the cross section plane. However ! in 3D it is simpler just to rotate the particles first and then take ! a straight cross section. ! ! Input: particle coordinates : x,y (npart) ! smoothing lengths : hh (npart) ! interpolation weights : weight (npart) ! scalar data to smooth : dat (npart) ! ! Output: smoothed data : datsmooth (npixx) ! ! Daniel Price, Institute of Astronomy, Cambridge, Feb 2004 !-------------------------------------------------------------------------- subroutine interpolate2D_xsec(x,y,hh,weight,dat,itype,npart,& x1,y1,x2,y2,datsmooth,npixx,normalise) implicit none integer, intent(in) :: npart,npixx real, intent(in), dimension(npart) :: x,y,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: x1,y1,x2,y2 real, intent(out), dimension(npixx) :: datsmooth logical, intent(in) :: normalise real, dimension(npixx) :: datnorm integer :: i,ipix,ipixmin,ipixmax real :: hi,hi1,radkern,q2,wab,const real :: term,termnorm,dx,dy,xpix,ypix,pixwidth,xpixwidth,xlength real :: gradient,yintercept,aa,bb,cc,determinant,det real :: xstart,xend,ystart,yend,rstart,rend real :: tol logical :: xsame, ysame, debug debug = .false. ! !--check for errors in input ! tol = 1.e-3 ysame = (abs(y2 - y1).lt.tol) xsame = (abs(x2 - x1).lt.tol) if (xsame.and.ysame) then print*,'error: interpolate2D_xsec: zero length cross section' return endif if (npixx.eq.0) then print*,'error: interpolate2D_xsec: npix = 0 ' return endif print*,'oblique 1D cross section through 2D data: npix =',npixx ! !--work out the equation of the line y = mx + c from the two points input ! gradient = 0. if (.not.xsame) gradient = (y2-y1)/(x2-x1) yintercept = y2 - gradient*x2 print*,'cross section line: y = ',gradient,'x + ',yintercept ! !--work out length of line and divide into pixels ! xlength = sqrt((x2-x1)**2 + (y2-y1)**2) pixwidth = xlength/real(npixx) xpixwidth = (x2 - x1)/real(npixx) if (debug) then print*,'length of line = ',xlength print*,'pixel width = ',pixwidth, ' in x direction = ',xpixwidth endif ! !--now interpolate to the line of pixels ! datsmooth = 0. datnorm = 0. const = cnormk2D ! normalisation constant ! !--loop over particles ! over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--skip particles with zero weights ! termnorm = const*weight(i) if (termnorm.le.0.) cycle over_parts ! !--skip particles with wrong h's ! hi = hh(i) if (hi.le.tiny(hi)) cycle over_parts ! !--set kernel related quantities ! hi1 = 1./hi radkern = radkernel*hi ! radius of the smoothing kernel term = termnorm*dat(i) ! !--for each particle work out which pixels it contributes to ! to do this we need to work out the two points at which the line ! intersects the particles smoothing circle ! given by the equation (x-xi)^2 + (y-yi)^2 = (2h)^2. ! The x co-ordinates of these points are the solutions to a ! quadratic with co-efficients: aa = 1. + gradient**2 bb = 2.*gradient*(yintercept - y(i)) - 2.*x(i) cc = x(i)**2 + y(i)**2 - 2.*yintercept*y(i) + yintercept**2 & - radkern**2 ! !--work out whether there are any real solutions and find them ! determinant = bb**2 - 4.*aa*cc if (determinant < 0) then !!print*,' particle ',i,': does not contribute ',x(i),y(i) else det = sqrt(determinant) xstart = (-bb - det)/(2.*aa) xend = (-bb + det)/(2.*aa) if (xstart.lt.x1) xstart = x1 if (xstart.gt.x2) xstart = x2 if (xend.gt.x2) xend = x2 if (xend.lt.x1) xend = x1 ystart = gradient*xstart + yintercept yend = gradient*xend + yintercept ! !--work out position in terms of distance (no. of pixels) along the line ! rstart = sqrt((xstart-x1)**2 + (ystart-y1)**2) rend = sqrt((xend-x1)**2 + (yend-y1)**2) ipixmin = int(rstart/pixwidth) ipixmax = int(rend/pixwidth) + 1 if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (ipixmax.lt.1) ipixmax = 1 if (ipixmax.gt.npixx) ipixmax = npixx if (ipixmin.gt.npixx) ipixmax = npixx ! !--loop over pixels, adding the contribution from this particle ! !if (debug) print*,' particle ',i,': ',ipixmin,ipixmax,xstart,x(i),xend do ipix = ipixmin,ipixmax xpix = x1 + (ipix-0.5)*xpixwidth ypix = gradient*xpix + yintercept dy = ypix - y(i) dx = xpix - x(i) q2 = (dx*dx + dy*dy)*hi1*hi1 ! !--SPH kernel ! wab = wfunc(q2) ! !--calculate data value at this pixel using the summation interpolant ! datsmooth(ipix) = datsmooth(ipix) + term*wab if (normalise) datnorm(ipix) = datnorm(ipix) + termnorm*wab enddo endif enddo over_parts ! !--normalise dat array ! if (normalise) then where (datnorm > 0.) datsmooth = datsmooth/datnorm end where endif return end subroutine interpolate2D_xsec !-------------------------------------------------------------------------- ! subroutine to render particles onto a pixel array ! at the maximum or minimum colour ! ! Written by Daniel Price 21/7/2008 !-------------------------------------------------------------------------- subroutine interpolate_part(x,y,hh,npart,xmin,ymin,datsmooth,npixx,npixy,pixwidth,datval,brightness) implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,hh real, intent(in) :: xmin,ymin,pixwidth,datval real, intent(inout), dimension(npixx,npixy) :: datsmooth real, intent(inout), dimension(npixx,npixy), optional :: brightness integer :: i if (pixwidth.le.0.) then print "(1x,a)",'interpolate_part: error: pixel width <= 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate_part: warning: ignoring some or all particles with h < 0' endif ! !--loop over particles ! if (present(brightness)) then do i=1,npart call interpolate_part1(x(i),y(i),hh(i),xmin,ymin,datsmooth,npixx,npixy,pixwidth,datval) enddo else do i=1,npart call interpolate_part1(x(i),y(i),hh(i),xmin,ymin,datsmooth,npixx,npixy,pixwidth,datval,brightness) enddo endif return end subroutine interpolate_part !-------------------------------------------------------------------------- ! subroutine to render a single particle onto a pixel array ! ! Written by Daniel Price 21/7/2008 !-------------------------------------------------------------------------- subroutine interpolate_part1(xi,yi,hi,xmin,ymin,datsmooth,npixx,npixy,pixwidth,datval,brightness) implicit none real, intent(in) :: xi,yi,hi,xmin,ymin,pixwidth,datval integer, intent(in) :: npixx,npixy real, intent(inout), dimension(npixx,npixy) :: datsmooth real, intent(inout), dimension(npixx,npixy), optional :: brightness integer :: ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax real :: radkern,radkern2,rab2 real :: dx,dy2,xpix,ypix ! !--skip particles with wrong h's ! if (hi.le.tiny(hi)) return ! !--set kernel related quantities ! radkern = max(hi,2.*pixwidth) radkern2 = radkern*radkern ! radius of the smoothing kernel ! !--for each particle work out which pixels it contributes to ! ipixmin = int((xi - radkern - xmin)/pixwidth) jpixmin = int((yi - radkern - ymin)/pixwidth) ipixmax = int((xi + radkern - xmin)/pixwidth) + 1 jpixmax = int((yi + radkern - ymin)/pixwidth) + 1 if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (jpixmin.lt.1) jpixmin = 1 ! to pixels in the image if (ipixmax.gt.npixx) ipixmax = npixx if (jpixmax.gt.npixy) jpixmax = npixy ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax ypix = ymin + (jpix-0.5)*pixwidth dy2 = (ypix - yi)**2 do ipix = ipixmin,ipixmax xpix = xmin + (ipix-0.5)*pixwidth dx = xpix - xi rab2 = dx**2 + dy2 ! !--set data value at this pixel to maximum ! if (rab2.lt.radkern2) then datsmooth(ipix,jpix) = datval if (present(brightness)) then brightness(ipix,jpix) = 1.0 endif endif enddo enddo return end subroutine interpolate_part1 !-------------------------------------------------------------------------- ! subroutine to interpolate from arbitrary data to even grid of pixels ! ! The data is smoothed using the SPH summation interpolant, ! that is, we compute the smoothed array according to ! ! datsmooth(pixel) = sum_j dat_j W(r-r_j, \Delta) / sum_j W(r-r_j, \Delta) ! ! where _j is the quantity at the neighbouring particle j and ! W is the smoothing kernel. The interpolation is normalised. ! ! Input: data points : x,y (npart) ! third scalar to use as weight : dat (npart) (optional) ! ! number of pixels in x,y : npixx,npixy ! pixel width : pixwidth ! option to normalise interpolation : normalise (.true. or .false.) ! ! Output: smoothed data : datsmooth (npixx,npixy) ! ! Written by Daniel Price 2017 !-------------------------------------------------------------------------- subroutine interpolate2D_pixels(x,y,itype,npart, & xmin,ymin,xmax,ymax,datsmooth,npixx,npixy,& normalise,adaptive,dat,datpix2) use timing, only:wall_time,print_time implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,xmax,ymax real, intent(out), dimension(npixx,npixy) :: datsmooth logical, intent(in) :: normalise,adaptive real, intent(in), dimension(npart), optional :: dat real, dimension(npixx,npixy), intent(out), optional :: datpix2 real, dimension(npixx,npixy) :: datnorm,datold real, dimension(npixx) :: dx2i,qq2,wabi integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax,its,itsmax real :: hi,hi1,radkernx,radkerny,q2,wab,const real :: term,termnorm,dx,dy,xpix,ypix,ddx,ddy real :: xi,yi,pixwidthx,pixwidthy,dy2 real :: t1,t2 if (adaptive) then print "(1x,a)",'interpolating from particles to 2D pixels (adaptive)...' else print "(1x,a)",'interpolating from particles to 2D pixels...' endif if (adaptive) then itsmax = 3 else itsmax = 1 endif call wall_time(t1) iterations: do its=1,itsmax datsmooth = 0. datnorm = 0. const = cnormk2D ! normalisation constant ! !--loop over particles ! ddx = npixx/(xmax - xmin) ddy = npixy/(ymax - ymin) pixwidthx = 1. !/npixx pixwidthy = 1. !/npixy if (pixwidthx.le.0. .or. pixwidthy.le.0.) then print "(1x,a)",'interpolate2D: error: pixel width <= 0' return endif !$omp parallel do default(none) & !$omp shared(npart,itype,x,y,xmin,ymin,ddx,ddy,its) & !$omp shared(datold,datsmooth,datnorm,npixx,npixy,const,radkernel,radkernel2,dat) & !$omp shared(pixwidthx,pixwidthy,normalise) & !$omp private(i,xi,yi,ipix,jpix,hi,hi1) & !$omp private(radkernx,radkerny,ipixmin,ipixmax,jpixmin,jpixmax) & !$omp private(dx2i,xpix,ypix,dy,dy2,q2,wab,term,termnorm,qq2,wabi) over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--scale particle positions into viewport coordinates ! xi = (x(i) - xmin)*ddx yi = (y(i) - ymin)*ddy hi = 1.0*pixwidthx ! in units of pixel spacing ipix = int(xi) jpix = int(yi) if (its > 1 .and. ipix.ge.1 .and. ipix.le.npixx.and. jpix.ge.1 .and. jpix.le.npixy) then hi = min(1.5/sqrt(datold(ipix,jpix)),20.) !) !,1.) endif hi1 = 1./hi termnorm = const*hi1*hi1 ! !--set kernel related quantities ! radkernx = radkernel*hi ! radius of the smoothing kernel radkerny = radkernel*hi ! radius of the smoothing kernel if (present(dat)) then term = termnorm*dat(i) else term = termnorm endif ! !--for each particle work out which pixels it contributes to ! ipixmin = int((xi - radkernx)) jpixmin = int((yi - radkerny)) ipixmax = int((xi + radkernx)) + 1 jpixmax = int((yi + radkerny)) + 1 if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (ipixmax.gt.npixx) ipixmax = npixx ! to pixels in the image if (jpixmin.lt.1) jpixmin = 1 if (jpixmax.gt.npixy) jpixmax = npixy ! !--precalculate an array of dx2 for this particle (optimisation) ! do ipix=ipixmin,ipixmax xpix = (ipix-0.5)*pixwidthx dx2i(ipix) = ((xpix - xi)**2)*hi1*hi1 enddo ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax ypix = (jpix-0.5)*pixwidthy dy = ypix - yi dy2 = dy*dy*hi1*hi1 do ipix = ipixmin,ipixmax q2 = dx2i(ipix) + dy2 ! !--SPH kernel ! if (q2 < radkernel2) then wab = wkernel(q2) ! !--calculate data value at this pixel using the summation interpolant ! !$omp atomic datsmooth(ipix,jpix) = datsmooth(ipix,jpix) + term*wab if (normalise) then !$omp atomic datnorm(ipix,jpix) = datnorm(ipix,jpix) + termnorm*wab endif endif enddo enddo enddo over_parts !$omp end parallel do if (present(dat)) then datold = datnorm else datold = datsmooth endif ! !--normalise dat array ! if (normalise) then where (datnorm > 0.) datsmooth = datsmooth/datnorm end where endif enddo iterations if (present(datpix2)) datpix2 = datnorm call wall_time(t2) if (t2-t1 > 1.) call print_time(t2-t1) return end subroutine interpolate2D_pixels !------------------------------------------------------------ ! interface to kernel routine to avoid problems with openMP !----------------------------------------------------------- real function wkernel(q2) use kernels, only:wfunc implicit none real, intent(in) :: q2 wkernel = wfunc(q2) end function wkernel end module interpolations2D splash/src/plotlib_pgplot.f90000644 000766 000000 00000047026 13261626263 017136 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !--------------------------------------------------------------------------- ! The plotlib module in SPLASH provides a consistent API so that SPLASH ! can be compiled against different graphics libraries as the backend ! ! This version provides an interface to Tim Pearson's PGPLOT, ! which was the original backend used in SPLASH v1.x. Thus, ! the functions mostly translate directly to PGPLOT equivalents, ! and basically this is a Fortran 90 interface for PGPLOT. ! ! Interface written by James Wetter and Daniel Price (2010) !--------------------------------------------------------------------------- module plotlib implicit none logical, parameter :: plotlib_is_pgplot = .true. logical, parameter :: plotlib_supports_alpha = .false. integer, parameter :: plotlib_maxlinestyle = 5 integer, parameter :: plotlib_maxfillstyle = 5 integer, parameter :: plotlib_maxlinecolour = 16 integer, parameter :: plotlib_maxpalette = 7 integer, parameter :: plotlib_extend_pad = 1 ! not implemented in PGPLOT integer, parameter :: plotlib_extend_repeat = 2 ! not implemented in PGPLOT integer, parameter :: plotlib_extend_reflect = 3 ! not implemented in PGPLOT integer, parameter :: plotlib_extend_none = 0 ! not implemented in PGPLOT public character(len=1),parameter :: plot_left_click = 'A' character(len=1),parameter :: plot_right_click = 'X' character(len=1),parameter :: plot_middle_click = 'D' character(len=1),parameter :: plot_shift_click = achar(15) character(len=1),parameter :: plot_scroll_up = achar(21) character(len=1),parameter :: plot_scroll_down = achar(4) character(len=1),parameter :: plot_scroll_left = achar(12) character(len=1),parameter :: plot_scroll_right = achar(18) interface plot_qci subroutine PGQCI (CI) integer,intent(out) :: CI end subroutine PGQCI end interface interface plot_qlw subroutine PGQLW (LW) integer,intent(out) :: LW end subroutine PGQLW end interface interface plot_qcr subroutine PGQCR (CI, CR, CG, CB) integer,intent(in) :: CI real,intent(out) :: CR, CG, CB end subroutine PGQCR end interface interface plot_qvp subroutine PGQVP (UNITS, X1, X2, Y1, Y2) integer,intent(in) :: UNITS real,intent(out) :: X1, X2, Y1, Y2 end subroutine PGQVP end interface interface plot_qwin subroutine PGQWIN (X1, X2, Y1, Y2) real,intent(out) :: X1, X2, Y1, Y2 end subroutine PGQWIN end interface interface plot_ebuf subroutine PGEBUF end subroutine PGEBUF end interface interface plot_bbuf subroutine PGBBUF end subroutine PGBBUF end interface interface plot_qcs subroutine PGQCS(UNITS, XCH, YCH) integer,intent(in) :: UNITS real,intent(out) :: XCH, YCH end subroutine PGQCS end interface interface plot_annotate subroutine PGMTXT (SIDE, DISP, COORD, FJUST, TEXT) character(len=*),intent(in) :: SIDE, TEXT real,intent(in) :: DISP, COORD, FJUST end subroutine PGMTXT end interface interface plot_sch subroutine PGSCH (SIZE) real,intent(in) :: SIZE end subroutine PGSCH end interface interface plot_sci subroutine PGSCI (CI) integer,intent(in) :: CI end subroutine PGSCI end interface interface plot_slw subroutine PGSLW (LW) integer,intent(in) :: lw end subroutine PGSLW module procedure plot_slw_float end interface interface plot_page subroutine pgpage end subroutine pgpage end interface interface plot_close subroutine PGEND end subroutine PGEND end interface interface plot_svp subroutine pgsvp(XLEFT, XRIGHT, YBOT, YTOP) real,intent(in) :: XLEFT, XRIGHT, YBOT, YTOP end subroutine pgsvp end interface interface plot_swin subroutine pgswin(X1, X2, Y1, Y2) real,intent(in) :: X1, X2, Y1, Y2 end subroutine pgswin end interface interface plot_wnad subroutine pgwnad(X1, X2, Y1, Y2) real,intent(in) :: X1, X2, Y1, Y2 end subroutine pgwnad end interface interface plot_qvsz subroutine PGQVSZ (UNITS, X1, X2, Y1, Y2) integer,intent(in) :: UNITS real,intent(out) :: X1, X2, Y1, Y2 end subroutine PGQVSZ end interface interface plot_line subroutine pgline(npts,xline,yline) integer, intent(in) :: npts real, intent(in), dimension(npts) :: xline,yline end subroutine pgline end interface interface plot_box subroutine pgbox(XOPT, XTICK, NXSUB, YOPT, YTICK, NYSUB) character*(*),intent(in) :: XOPT, YOPT real,intent(in) :: XTICK, YTICK integer,intent(in) :: NXSUB, NYSUB end subroutine pgbox end interface interface plot_scr subroutine PGSCR (CI, CR, CG, CB) integer, intent(in) :: CI real, intent(in) :: CR, CG, CB end subroutine pgscr module procedure pgscra end interface interface plot_bins subroutine PGBIN (NBIN, X, DATA, CENTER) integer,intent(in) :: NBIN real,intent(in) :: X(*), DATA(*) LOGICAL,intent(in) :: CENTER end subroutine PGBIN end interface interface plot_imag module procedure plot_imag_transparent end interface interface plot_qcol subroutine pgqcol(icolmin,icolmax) integer,intent(out) :: icolmin,icolmax end subroutine pgqcol end interface interface plot_qcir subroutine pgqcir(icolmin,icolmax) integer,intent(out) :: icolmin,icolmax end subroutine pgqcir end interface interface plot_scir subroutine pgscir(icilo, icihi) integer,intent(in) :: icilo,icihi end subroutine pgscir end interface interface plot_ctab subroutine pgctab(l,r,g,b,nc,contra,bright) integer,intent(in) :: nc real,intent(in) :: l(nc),r(nc),g(nc),b(nc),contra,bright end subroutine pgctab end interface interface plot_qls subroutine pgqls(ls) integer,intent(out) :: ls end subroutine pgqls end interface interface plot_qfs subroutine pgqfs(fs) integer,intent(out) :: fs end subroutine pgqfs end interface interface plot_sls subroutine pgsls(ls) integer,intent(in) :: ls end subroutine pgsls end interface interface plot_sfs subroutine pgsfs(fs) integer,intent(in) :: fs end subroutine pgsfs end interface interface plot_rect subroutine pgrect(x1,x2,y1,y2) real,intent(in) :: x1,x2,y1,y2 end subroutine pgrect module procedure plot_rect_rounded end interface interface plot_arro subroutine pgarro(x1,y1,x2,y2) real,intent(in) :: x1,y1,x2,y2 end subroutine pgarro end interface interface plot_circ subroutine pgcirc(xcent,ycent,radius) real,intent(in) :: xcent,ycent,radius end subroutine pgcirc end interface interface plot_lcur subroutine pglcur (maxpt, npt, x, y) implicit none integer, intent(in) :: maxpt integer, intent(inout) :: npt real, intent(inout) :: x(*), y(*) end subroutine pglcur module procedure plot_clcur end interface plot_lcur interface plot_olin subroutine pgolin (maxpt, npt, x, y, symbol) implicit none integer, intent(in) :: maxpt integer, intent(inout) :: npt real, intent(inout) :: x(*), y(*) integer, intent(in) :: symbol end subroutine pgolin end interface plot_olin interface plot_ncur subroutine pgncur(maxpt, npt, x, y, symbol) implicit none integer, intent(in) :: maxpt integer, intent(inout) :: npt real, intent(inout) :: x(*), y(*) integer, intent(in) :: symbol end subroutine pgncur end interface plot_ncur interface plot_qtxt subroutine pgqtxt(x,y,angle,fjust,text,xbox,ybox) real,intent(in) :: x, y, angle, fjust character(len=*),intent(in) :: text real,intent(out),dimension(4) :: xbox,ybox end subroutine pgqtxt end interface interface plot_ptxt subroutine pgptxt(x,y,angle,fjust,text) real,intent(in) :: x,y,angle,fjust character(len=*),intent(in) :: TEXT end subroutine pgptxt end interface interface plot_stbg subroutine pgstbg(bg) integer,intent(in) :: bg end subroutine pgstbg end interface interface plot_curs ! integer function pgcurs(x,y,ch) ! real,intent(inout) :: x,y ! character*(*),intent(out) :: ch ! end function pgcurs module procedure pgcurs_sub end interface interface plot_pt subroutine pgpt(n,xpts,ypts,symbol) integer,intent(in) :: n real,intent(in) :: xpts(*),ypts(*) integer,intent(in) :: symbol end subroutine pgpt end interface interface plot_funx subroutine pgfunx(fx,n,ymin,ymax,pgflags) real,external :: fx integer,intent(in) :: n,pgflags real,intent(in) :: ymin,ymax end subroutine pgfunx end interface interface plot_label subroutine pglabel(xlbl,ylbl,toplbl) character(len=*),intent(in) :: xlbl,ylbl,toplbl end subroutine pglabel end interface interface plot_scrn subroutine pgscrn(ci,name,ier) integer,intent(in) :: ci character(len=*),intent(in) :: name integer,intent(out) :: ier end subroutine pgscrn end interface interface plot_poly subroutine pgpoly(n,xpts,ypts) integer,intent(in) :: n real,intent(in) :: xpts(*),ypts(*) end subroutine pgpoly end interface interface plot_qinf subroutine pgqinf(item,value,length) character(len=*),intent(in) :: item character(len=*),intent(out) :: value integer,intent(out) :: length end subroutine pgqinf end interface interface plot_band module procedure pgband_sub end interface interface plot_pt1 subroutine pgpt1(xpt,ypt,symbol) real,intent(in) :: xpt,ypt integer,intent(in) :: symbol end subroutine pgpt1 end interface interface plot_numb subroutine pgnumb(m,pp,form,string,nc) integer,intent(in) :: m,pp,form character(len=*),intent(out) :: string integer,intent(out) :: nc end subroutine pgnumb end interface interface plot_qch subroutine pgqch(ch) real,intent(out) :: ch end subroutine pgqch end interface interface plot_text subroutine pgtext(x,y,text) real,intent(in) :: x,y character(len=*),intent(in) :: text end subroutine pgtext end interface interface plot_err1 subroutine pgerr1(dir,x,y,e,t) integer,intent(in) :: dir real,intent(in) :: x,y,e real,intent(in) :: t end subroutine pgerr1 end interface interface plot_errb subroutine pgerrb(dir,n,x,y,e,t) integer,intent(in) :: dir,n real,intent(in) :: x(n),y(n),e(n) real,intent(in) :: t end subroutine pgerrb end interface interface plot_conb subroutine pgconb(a,idim,jdim,i1,i2,j1,j2,c,nc,tr,blank) integer,intent(in) :: idim,jdim,i1,i2,j1,j2,nc real,intent(in) :: a(idim,jdim),c(*),tr(6),blank end subroutine pgconb end interface interface plot_cons subroutine pgcons(a,idim,jdim,i1,i2,j1,j2,c,nc,tr) integer,intent(in) :: idim,jdim,i1,i2,j1,j2,nc real,intent(in) :: a(idim,jdim),c(*),tr(6) end subroutine pgcons end interface interface plot_conl subroutine pgconl(a,idim,jdim,i1,i2,j1,j2,c,tr,label,intval,mininit) integer,intent(in) :: idim,jdim,i1,i2,j1,j2,intval,mininit real,intent(in) :: a(idim,jdim),c,tr(6) character(len=*),intent(in) :: label end subroutine pgconl end interface interface plot_sah subroutine pgsah(fs, angle, cutback) integer, intent(in) :: fs real, intent(in) :: angle, cutback end subroutine pgsah end interface interface plot_vect subroutine pgvect(a,b,idim,jdim,i1,i2,j1,j2,c,nc,tr,blank) integer,intent(in) :: idim,jdim,i1,i2,j1,j2,nc real,intent(in) :: a(idim,jdim),b(idim,jdim),tr(6),blank,c end subroutine pgvect end interface interface plot_pixl subroutine pgpixl(ia,idim,jdim,i1,i2,j1,j2,x1,x2,y1,y2) integer,intent(in) :: idim,jdim,i1,i2,j1,j2 integer,intent(in) :: ia(idim,jdim) real,intent(in) :: x1,x2,y1,y2 end subroutine pgpixl end interface interface plot_env subroutine pgenv(xmin,xmax,ymin,ymax,just,axis) real,intent(in) :: xmin,xmax,ymin,ymax integer,intent(in) :: just,axis end subroutine pgenv end interface contains !--------------------------------------------- ! initialise the plotting library !--------------------------------------------- subroutine plot_init(devicein, ierr, papersizex, aspectratio, paperunits) implicit none character*(*), intent(in) :: devicein integer, intent(out) :: ierr real, intent(in), optional :: papersizex,aspectratio integer, intent(in), optional :: paperunits integer :: pgopen real :: aspect if (devicein(1:1).eq.'?') then call pgbegin(0,'?',1,1) ierr = 1 else ierr = pgopen(devicein) endif !--check if there is an error ! (be careful here: from PGPLOT zero or -ve indicates an error) if (ierr.le.0) then if (ierr.eq.0) ierr = -1 !--make sure we return an error return else ierr = 0 endif !-- Turn off promting call pgask(.false.) !-- set paper size if given if (present(papersizex)) then if (present(aspectratio)) then aspect = aspectratio else aspect = sqrt(2.) endif if (present(paperunits)) then !--make sure that the units are in inches for PGPLOT if (paperunits.ne.1) return endif call plot_pap(papersizex,aspect) endif end subroutine plot_init subroutine plot_slc(lc) implicit none integer,intent(in) :: lc !--line cap has no effect in PGPLOT end subroutine plot_slc subroutine plot_pap(width,aspect,paperunits) real,intent(in) :: width,aspect integer, intent(in), optional :: paperunits if (present(paperunits)) then if (paperunits.ne.1) print "(a)",' WARNING: units not valid for PGPLOT' endif call pgpap(papersizex,aspect) end subroutine plot_pap subroutine plot_qlc(lc) implicit none integer,intent(out) :: lc lc = 0 end subroutine plot_qlc subroutine plot_set_opacity(alpha) implicit none real, intent(in) :: alpha !--opacity has no effect in PGPLOT end subroutine plot_set_opacity !--interface to set transparent colour ! (not implemented in PGPLOT) subroutine pgscra (CI, CR, CG, CB, CA) integer, intent(in) :: CI real, intent(in) :: CR, CG, CB, CA !--just throw away the alpha value call PGSCR(CI,CR,CG,CB) end subroutine pgscra !--floating point line widths ! (not implemented in PGPLOT) subroutine plot_slw_float (LW) real,intent(in) :: lw call PGSLW(nint(lw)) end subroutine plot_slw_float subroutine plot_rgb_from_table(frac,r,g,b) implicit none real, intent(in) :: frac real, intent(out) :: r,g,b !--rgb from table not implemented in PGPLOT end subroutine plot_rgb_from_table subroutine plot_set_palette(lp) implicit none integer, intent(in) :: lp !--set line palette not implemented in PGPLOT end subroutine plot_set_palette logical function plot_qcur() implicit none character(len=10) :: string integer :: nc call pgqinf('CURSOR',string,nc) if(string(1:nc).eq.'YES') then plot_qcur = .true. else plot_qcur = .false. end if end function plot_qcur ! !--inverts the return value of pgcurs ! function pgcurs_sub(x,y,ch) real,intent(inout) :: x,y character*(*),intent(out) :: ch integer :: pgcurs_sub,ierr integer, external :: pgcurs ierr = pgcurs(x,y,ch) if (ierr.eq.0) then pgcurs_sub = 1 else pgcurs_sub = 0 endif end function pgcurs_sub !--transparent rendering does not work in PGPLOT, but ! we give it an interface anyway subroutine plot_imag_transparent(a, idim, jdim, i1, i2, j1, j2, a1, a2, tr, iextend) implicit none integer,intent(in) :: IDIM, JDIM, I1, I2, J1, J2 real,intent(in) :: A(IDIM,JDIM), A1, A2, TR(6) integer, intent(in), optional :: iextend call pgimag(a, idim, jdim, i1, i2, j1, j2, a1, a2, tr) end subroutine plot_imag_transparent subroutine plot_imag_alpha(dat, alpha, idim, jdim, i1, i2, j1, j2, a1, a2, tr, iextend) integer,intent(in) :: IDIM, JDIM, I1, I2, J1, J2 real,intent(in) :: dat(IDIM,JDIM), alpha(IDIM,JDIM), A1, A2, TR(6) real :: affine(6) integer, intent(in), optional :: iextend call pgimag(dat, idim, jdim, i1, i2, j1, j2, a1, a2, tr) end subroutine plot_imag_alpha !--giza version of plot_gray takes additional arguments subroutine plot_gray(a, idim, jdim, i1, i2, j1, j2, a1, a2, tr, iextend) implicit none integer,intent(in) :: IDIM, JDIM, I1, I2, J1, J2 real,intent(in) :: A(IDIM,JDIM), A1, A2, TR(6) integer, intent(in), optional :: iextend call pggray(a, idim, jdim, i1, i2, j1, j2, a1, a2, tr) end subroutine plot_gray !--version of lcur that returns last character pressed subroutine plot_clcur(maxpt, npt, x, y, ch) implicit none integer, intent(in) :: maxpt integer, intent(inout) :: npt real, intent(inout) :: x(*), y(*) character*(*),intent(out) :: ch call pglcur (maxpt, npt, x, y) ch = 'A' end subroutine plot_clcur !--rounded rectangle plotting ! (not implemented -- just calls pgrect) subroutine plot_rect_rounded(x1,x2,y1,y2,r) implicit none real,intent(in) :: x1,x2,y1,y2,r call pgrect(x1,x2,y1,y2) end subroutine plot_rect_rounded ! !--inverts the return value of pgband ! function pgband_sub(mode, posn, xref, yref, x, y, ch) integer,intent(in) :: mode, posn real,intent(in) :: xref, yref real,intent(inout) :: x, y character*(*),intent(out) :: ch integer :: ierr,pgband_sub integer,external :: pgband ierr = pgband(mode,posn,xref,yref,x,y,ch) if(ierr.eq.1) then pgband_sub = 0 else pgband_sub = 1 endif end function pgband_sub ! !--this subroutine can be called after PGSVP to ! make sure that the viewport lies exactly on ! pixel boundaries. ! ! Queries PGPLOT routines directly so no need ! for input/output ! subroutine plot_set_exactpixelboundaries() implicit none real :: xminpix,xmaxpix,yminpix,ymaxpix real :: vptxmin,vptxmax,vptymin,vptymax real :: dv real, parameter :: tol = 1.e-6 ! ! setting axes adjusts the viewport, so query to get adjusted settings ! call pgqvp(0,vptxmin,vptxmax,vptymin,vptymax) !print*,'got ',vptxmin,vptxmax,vptymin,vptymax ! ! adjust viewport on pixel devices so that ! boundaries lie exactly on pixel boundaries ! ! query viewport size in pixels call pgqvp(3,xminpix,xmaxpix,yminpix,ymaxpix) !print*,' in pixels = ',xminpix,xmaxpix,yminpix,ymaxpix ! work out how many viewport coords/pixel dv = (vptymax - vptymin)/(ymaxpix-yminpix) ! adjust viewport min/max to lie on pixel boundaries vptymin = max((nint(yminpix)-tol)*dv,0.) vptymax = min((nint(ymaxpix)-tol)*dv,1.0-epsilon(1.0)) ! be careful of round-off errors ! same for x dv = (vptxmax - vptxmin)/(xmaxpix-xminpix) vptxmin = max((nint(xminpix)-tol)*dv,0.) vptxmax = min((nint(xmaxpix)-tol)*dv,1.0-epsilon(1.0)) ! be careful of round-off errors ! adjust viewport !print*,'adjusting ',vptxmin,vptxmax,vptymin,vptymax call pgsvp(vptxmin,vptxmax,vptymin,vptymax) !call pgqvp(3,xminpix,xmaxpix,yminpix,ymaxpix) !print*,' in pixels = ',xminpix,xmaxpix,yminpix,ymaxpix return end subroutine plot_set_exactpixelboundaries end module plotlib splash/src/read_data_UCLA.f90000644 000766 000000 00000015070 13261626263 016606 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR SKY KING / MARK MORRIS' (UCLA) ASCII DATA FORMAT ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,time,gamma,maxpart,maxcol,maxstep use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation, only:alloc implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,ierr,nerr,iunit,ncolstep,ncolenv integer :: nprint,npart_max,nstep_max,icol integer :: igatherb,ntot,ninit,ninit1,nstar logical :: iexist real :: tread,pmass,hbav1,dttot,xmacc,xlxacc,xlyacc,xlzacc real, dimension(3) :: xptmass,yptmass,vxptmass,vyptmass character(len=len(rootname)+4) :: dumpfile nstepsread = 0 nstep_max = 0 npart_max = maxpart iunit = 15 ! logical unit number for input dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions (0 means no particle coords) ! ndim = 3 ndimV = 3 ncolstep = 10 ! create one column for particle mass nstar = 3 j = indexstart nstepsread = 0 print "(a)",' reading Sky King/Mark Morris (UCLA) ascii data format ' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the file and read the number of particles ! open(unit=iunit,iostat=ierr,file=dumpfile,status='old',form='formatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' return endif ! !--read header lines, try to use it to set time ! read(iunit,*,iostat=ierr) nprint,tread,pmass,xptmass(1),yptmass(1),xptmass(2),yptmass(2),& vxptmass(1),vyptmass(1),vxptmass(2),vyptmass(2), & xptmass(3),yptmass(3),vxptmass(3),vyptmass(3) if (ierr /= 0) print "(a)",' WARNING: error(s) reading first header line' print "(a,i10,a,1pe10.3)",' npart = ',nprint, ' time = ',tread read(iunit,*,iostat=ierr) igatherb,ntot,ninit,ninit1,hbav1,dttot,xmacc,xlxacc,xlyacc,xlzacc if (ierr /= 0) print "(a)",' WARNING: error(s) reading second header line' print "(a)",' header info: ' print "(2(a11,i10))",' igatherb: ',igatherb,' ntot: ',ntot print "(2(a11,i10))",' ninit: ',ninit,' ninit1: ',ninit1 print "(3(a11,1pe10.4))",' hbav1: ',hbav1,' dttot: ',dttot,' xmacc: ',xmacc print "(3(a11,1pe10.4))",' accelx: ',xlxacc,' accely: ',xlyacc,' accelz: ',xlzacc ! !--(re)allocate memory ! nstep_max = max(nstep_max,indexstart,1) if (.not.allocated(dat) .or. (nprint.gt.maxpart) .or. (ncolstep+ncalc).gt.maxcol) then npart_max = max(npart_max,INT(1.1*(nprint)),maxpart) call alloc(npart_max,nstep_max,max(ncolstep+ncalc,maxcol)) endif ! !--set the necessary parameters ! ncolumns = ncolstep nstepsread = nstepsread + 1 npartoftype(:,j) = 0 npartoftype(1,j) = nprint npartoftype(2,j) = nstar time(j) = tread ! !--now read the timestep data in the dumpfile ! nerr = 0 do i=1,nprint read(iunit,*,iostat=ierr) (dat(i,icol,j),icol = 1,9) if (ierr.ne.0) nerr = nerr + 1 enddo if (nerr > 0) print *,' ERRORS reading particle data on ',nerr,' lines' close(iunit) !--set particle mass from column dat(1:nprint,10,j) = pmass !--copy star particle properties into main data array do i=nprint+1,nprint+nstar dat(i,1,j) = xptmass(i-nprint) dat(i,2,j) = yptmass(i-nprint) dat(i,3,j) = 0. dat(i,4,j) = vxptmass(i-nprint) dat(i,5,j) = vyptmass(i-nprint) dat(i,6,j) = 0. enddo return end subroutine read_data !!------------------------------------------------------------------- !! set labels for each column of data !! !! read these from a file called 'columns' in the current directory !! then take sensible guesses as to which quantities are which !! from the column labels !! !!------------------------------------------------------------------- subroutine set_labels use labels, only:label,labeltype,ix,irho,ipmass,ih,ipr,ivx,iamvec,labelvec use params use settings_data, only:ncolumns,ntypes,ndim,ndimV,UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i,ierr,ndimVtemp do i=1,ndim ix(i) = i enddo ivx = 4 irho = 7 ipr = 8 ih = 9 ipmass = 10 label(irho) = 'density' label(ipr) = 'pressure' label(ih) = 'smoothing length' label(ipmass) = 'particle mass' if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 2 labeltype(1) = 'gas' labeltype(2) = 'star' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/exact.f90000644 000766 000000 00000203232 13261626263 015201 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ! This module handles all of the settings relating to the exact solution ! plotting and calls the appropriate routines to change these settings and ! plot the actual solutions. ! ! The only thing to do with exact solutions that is not entirely handled ! by this module is the toy star AC plane solution (because ! it is called under different circumstances to the other solutions). ! module exact implicit none ! !--maximum number of solutions in any one plot ! integer, parameter :: maxexact=10 ! !--options used to plot the exact solution line ! integer :: maxexactpts, iExactLineColour, iExactLineStyle,iPlotExactOnlyOnPanel integer :: iNormaliseErrors logical :: iApplyTransExactFile,iCalculateExactErrors,iPlotResiduals logical :: iApplyUnitsExactFile real :: fracinsetResiduals,residualmax ! !--declare all of the parameters required for the various exact solutions ! !--toy star integer :: iACplane ! label position of toy star AC plane plot integer :: norder,morder ! for toy star real, public :: atstar,ctstar,sigma real :: htstar,alphatstar,betatstar,ctstar1,ctstar2 real :: sigma0,totmass !--sound wave integer :: iwaveploty,iwaveplotx ! linear wave real :: ampl,lambda,period,xzero !--sedov blast wave real :: rhosedov,esedov !--polytrope real :: polyk !--mhd shock solutions integer :: ishk real :: xshock !--density profiles integer :: iprofile,icolpoten,icolfgrav real, dimension(2) :: Msphere,rsoft !--from file integer :: iexactplotx(maxexact), iexactploty(maxexact) !--shock tube real :: rho_L, rho_R, pr_L, pr_R, v_L, v_R !--rho vs h real :: hfact !--read from file integer :: ixcolfile(maxexact),iycolfile(maxexact),nfiles character(len=120) :: filename_exact(maxexact) !--equilibrium torus real :: Mstar,Rtorus,distortion !--ring spreading real :: Mring,Rring,viscnu !--dusty waves real :: cs,Kdrag,rhozero,rdust_to_gas !--arbitrary function integer :: nfunc character(len=120), dimension(maxexact) :: funcstring !--Roche potential real :: mprim,msec real :: xprim(3),xsec(3) logical :: use_sink_data integer, parameter :: iexact_rochelobe = 15 !--C-shock real :: machs,macha !--planet-disc interaction real :: HonR,rplanet real :: spiral_params(7,maxexact) integer :: ispiral,narms !--bondi flow logical :: relativistic, geodesic_flow,is_wind real :: const1,const2 !--gamma, for manual setting real :: gamma_exact logical :: use_gamma_exact ! !--sort these into a namelist for input/output ! namelist /exactopts/ iexactplotx,iexactploty,filename_exact,maxexactpts, & iExactLineColour,iExactLineStyle,iApplyTransExactFile,iCalculateExactErrors, & iPlotResiduals,fracinsetResiduals,residualmax,iPlotExactOnlyOnPanel,& iApplyUnitsExactFile,iNormaliseErrors namelist /exactparams/ ampl,lambda,period,iwaveploty,iwaveplotx,xzero, & htstar,atstar,ctstar,alphatstar,betatstar,ctstar1,ctstar2, & polyk,sigma0,norder,morder,rhosedov,esedov, & rho_L, rho_R, pr_L, pr_R, v_L, v_R,ishk,hfact, & iprofile,Msphere,rsoft,icolpoten,icolfgrav,Mstar,Rtorus,distortion, & Mring,Rring,viscnu,nfunc,funcstring,cs,Kdrag,rhozero,rdust_to_gas, & mprim,msec,ixcolfile,iycolfile,xshock,totmass,machs,macha,& use_sink_data,xprim,xsec,nfiles,gamma_exact,use_gamma_exact,& HonR,rplanet,relativistic,geodesic_flow,is_wind,const1,const2,ispiral,narms,spiral_params public :: defaults_set_exact,submenu_exact,options_exact,read_exactparams public :: exact_solution public :: exactopts,exactparams contains !---------------------------------------------------------------------- ! sets default values of the exact solution parameters !---------------------------------------------------------------------- subroutine defaults_set_exact lambda = 1.0 ! sound wave exact solution : wavelength ampl = 0.005 ! sound wave exact solution : amplitude period = 1.0 iwaveploty = 7 iwaveplotx = 1 xzero = 0. htstar = 1. ! toy star crap atstar = 1. ctstar = 1. alphatstar = 0. betatstar = 0. ctstar1 = 0. ctstar2 = 0. totmass = 1. norder = -1 morder = 0 sigma0 = 0. rhosedov = 1.0 ! sedov blast wave esedov = 1.0 ! blast wave energy polyk = 1.0 ! polytropic k ! shock tube (default is sod problem) rho_L = 1.0 rho_R = 0.125 pr_L = 1.0 pr_R = 0.1 v_L = 0.0 v_R = 0.0 ishk = 1 xshock = 0. hfact = 1.2 ! read from file nfiles = 1 filename_exact = ' ' ixcolfile = 1 iycolfile = 2 iexactplotx = 0 iexactploty = 0 ! density profile parameters iprofile = 1 rsoft(1) = 1.0 rsoft(2) = 0.1 Msphere(1) = 1.0 Msphere(2) = 0.0 icolpoten = 0 icolfgrav = 0 ! equilibrium torus Mstar = 1.0 Rtorus = 1.0 distortion = 1.1 ! ring spreading Mring = 1.0 Rring = 1.0 viscnu = 1.e-3 ! dusty waves Kdrag = 1.0 cs = 1.0 rhozero = 1.0 rdust_to_gas = 0.0 ! Roche lobes use_sink_data = .true. mprim = 1. msec = 1. xprim = 0. xsec = 0. xsec(1) = 1. ! C-shock machs = 50. ! sonic Mach number macha = 5. ! Alfvenic Mach number ! planet-disc interaction HonR = 0.05 rplanet = 1. ispiral = 1 narms = 1 spiral_params = 0. spiral_params(2,:) = 360. ! Bondi relativistic = .true. geodesic_flow = .false. is_wind = .true. const1 = 8. const2 = 1. ! gamma, if not read from file gamma_exact = 5./3. use_gamma_exact = .false. ! arbitrary function nfunc = 1 funcstring = ' ' maxexactpts = 1001 ! points in exact solution plot iExactLineColour = 1 ! foreground iExactLineStyle = 1 ! solid iApplyTransExactFile = .true. ! false if exact from file is already logged iApplyUnitsExactFile = .false. iCalculateExactErrors = .true. iNormaliseErrors = 1 iPlotResiduals = .false. fracinsetResiduals = 0.15 residualmax = 0.0 iPlotExactOnlyOnPanel = 0 return end subroutine defaults_set_exact !---------------------------------------------------------------------- ! sets which exact solution to calculate + parameters for this !---------------------------------------------------------------------- subroutine submenu_exact(iexact) use settings_data, only:ndim use prompting, only:prompt use filenames, only:rootname,ifileopen,fileprefix use exactfunction, only:check_function use mhdshock, only:nmhdshocksolns,mhdprob use planetdisc, only:maxspirals,labelspiral use asciiutils, only:get_ncolumns,get_nrows,string_replace integer, intent(inout) :: iexact integer :: ierr,itry,i,ncols,nheaderlines,nadjust,nrows logical :: ians,iexist,ltmp,prompt_for_gamma character(len=len(filename_exact)) :: filename_tmp character(len=4) :: str print 10 10 format(' 0) none ',/, & ' 1) ANY function f(x,t)',/, & ' 2) read from file ',/, & ' 3) shock tube ',/, & ' 4) sedov blast wave ',/, & ' 5) polytrope ',/, & ' 6) toy star ',/, & ' 7) gresho vortex ',/, & ' 8) mhd shock tubes (tabulated) ',/, & ' 9) h vs rho ',/, & '10) Plummer/Hernquist spheres ',/, & '11) torus ',/, & '12) ring spreading ',/, & '13) special relativistic shock tube', /, & '14) dusty waves', /, & '15) Roche lobes/potential ',/, & '16) C-shock ',/, & '17) Spiral arms (planet-disc interaction)',/, & '18) Bondi flow') call prompt('enter exact solution to plot',iexact,0,18) print "(a,i2)",'plotting exact solution number ',iexact ! !--enter parameters for various exact solutions ! prompt_for_gamma = .false. select case(iexact) case(1) call prompt('enter number of functions to plot ',nfunc,1,maxexact) print "(/,a,6(/,11x,a))",' Examples: sin(2*pi*x - 0.1*t)','sqrt(0.5*x)','x^2', & 'exp(-2*x**2 + 0.1*t)','log10(x/2)','exp(y),y=sin(pi*x)','cos(z/y),z=acos(y),y=x^2' overfunc: do i=1,nfunc ierr = 1 itry = 0 do while(ierr /= 0 .and. itry.lt.10) if (nfunc > 1) print "(/,a,i2,/,11('-'),/)",'Function ',i call prompt('enter function f(x,t) to plot ',funcstring(i),noblank=.true.) call check_function(funcstring(i),ierr) if (ierr /= 0 .and. len(funcstring(i)).eq.len_trim(funcstring(i))) then print "(a,i3,a)",& ' (errors are probably because string is too long, max length = ',& len(funcstring(i)),')' endif itry = itry + 1 enddo if (itry >= 10) then print "(a)",' *** too many tries, aborting ***' ierr = i-1 exit overfunc endif print* call prompt('enter y axis of exact solution (0=all plots)',iexactploty(i),0) if (iexactploty(i) > 0) then call prompt('enter x axis of exact solution ',iexactplotx(i),1) endif enddo overfunc if (ierr /= 0) nfunc = ierr case(2) call prompt('enter number of files to read per plot ',nfiles,1,maxexact) nadjust = -1 over_files: do i=1,nfiles iexist = .false. do while(.not.iexist) print "(/,a)",'Use %f to represent current dump file, e.g. %f.exact looks for dump_000.exact' write(str,"(i4)") i call prompt('enter filename #'//trim(adjustl(str)),filename_exact(i)) !--substitute %f for filename filename_tmp = filename_exact(i) call string_replace(filename_tmp,'%f',trim(rootname(ifileopen))) !--check the first file for errors inquire(file=filename_tmp,exist=iexist) if (iexist) then open(unit=33,file=filename_tmp,status='old',iostat=ierr) if (ierr.eq.0) then call get_ncolumns(33,ncols,nheaderlines) call get_nrows(33,nheaderlines,nrows) if (nrows < 100000) then print "(a,i5,a)",' got ',nrows,' lines in file' else print "(a,i10,a)",' got ',nrows,' lines in file' endif if (nrows > maxexactpts) maxexactpts = nrows if (ncols.gt.2) then print "(a,i2,a)",' File '//trim(filename_tmp)//' contains ',ncols,' columns of data' call prompt('Enter column containing y data ',iycolfile(i),1,ncols) call prompt('Enter column containing x data ',ixcolfile(i),1,ncols) elseif (ncols.eq.2) then print "(a,i2,a)",' OK: got ',ncols,' columns from '//trim(filename_tmp) else iexist = .false. call prompt('Error: file contains < 2 readable columns: try again?',ians) if (.not.ians) then nadjust = i-1 exit over_files endif endif close(33) else iexist = .false. call prompt('Error opening '//trim(filename_tmp)//': try again?',ians) if (.not.ians) then nadjust = i-1 exit over_files endif endif else ians = .true. call prompt('file does not exist: try again? ',ians) if (.not.ians) then nadjust = i-1 exit over_files endif endif enddo call prompt('enter y axis of exact solution ',iexactploty(i),1) call prompt('enter x axis of exact solution ',iexactplotx(i),1) enddo over_files if (nadjust >= 0) then nfiles = nadjust if (nfiles == 1) then print "(/,a)",'Using 1 file only' elseif (nfiles > 0) then write(str,"(i4)") nfiles print "(/,a)",'Using '//trim(adjustl(str))//' files' endif endif if (nfiles > 0) then ! only ask if filename was read OK ltmp = .not.iApplyTransExactFile call prompt(' are exact solutions already logged?',ltmp) iApplyTransExactFile = .not.ltmp ltmp = .not.iApplyUnitsExactFile call prompt(' are exact solutions in physical units?',ltmp) iApplyUnitsExactFile = .not.ltmp endif case(3,13) ! !--read shock parameters from the .shk file ! call read_exactparams(iexact,trim(rootname(1)),ierr) if (ierr.ne.0) then call prompt('enter initial x position of shock',xshock) call prompt('enter density to left of shock ',rho_L,0.0) call prompt('enter density to right of shock ',rho_R,0.0) call prompt('enter pressure to left of shock ',pr_L,0.0) call prompt('enter pressure to right of shock ',pr_R,0.0) if (iexact.eq.13) then call prompt('enter velocity to left of shock ',v_L,max=1.0) call prompt('enter velocity to right of shock ',v_R,max=1.0) else call prompt('enter velocity to left of shock ',v_L) call prompt('enter velocity to right of shock ',v_R) call prompt('enter dust-to-gas ratio ',rdust_to_gas,0.) endif endif prompt_for_gamma = .true. case(4) call prompt('enter density of ambient medium ',rhosedov,0.0) call prompt('enter blast wave energy E ',esedov,0.0) prompt_for_gamma = .true. case(5) call prompt('enter polytropic k ',polyk) call prompt('enter total mass ',totmass) prompt_for_gamma = .true. case(6) print "(a)",' toy star: ' call read_exactparams(iexact,trim(rootname(1)),ierr) call prompt('enter polytropic k ',polyk) call prompt('enter total mass ',totmass) call prompt('enter central density rho_0 (rho = rho_0 - cr^2)',htstar) call prompt('enter parameter c (rho = rho_0 - cr^2)',ctstar,0.0) sigma = 0. call prompt('enter parameter sigma (By = sigma*rho)',sigma0) sigma = sigma0 ians = .false. call prompt('linear oscillations?',ians) if (ians) then call prompt('enter order of radial mode',norder,0) if (ndim.ge.2) call prompt('enter order of angular mode',morder,0) call prompt('enter velocity amplitude a (v = a*r) ',atstar) else print "(a)",'using exact non-linear solution:' ians = .true. if (norder.lt.0 .and. morder.lt.0) ians = .false. if (ndim.ge.2) call prompt('axisymmetric?',ians) if (ians .or. ndim.eq.1) then norder = -1 morder = 0 call prompt('enter v_r amplitude ',alphatstar) if (ndim.ge.2) call prompt('enter v_phi amplitude ',betatstar) else norder = -1 morder = -1 call prompt('enter vxx amplitude ',alphatstar) call prompt('enter vyy amplitude ',betatstar) call prompt('enter vxy amplitude ',ctstar1) call prompt('enter vyx amplitude ',ctstar2) endif endif prompt_for_gamma = .true. !case(7) ! call prompt('enter y-plot to place sine wave on',iwaveploty,1) ! call prompt('enter x-plot to place sine wave on',iwaveplotx,1) ! call prompt('enter starting x position',xzero) ! call prompt('enter wavelength lambda ',lambda,0.0) ! call prompt('enter amplitude ',ampl,0.0) ! call prompt('enter period ',period) case(8) print "(a)",' MHD shock tube tables: ' if (ishk.le.0) ishk = 1 do i=1,nmhdshocksolns print "(i2,') ',a)",i,trim(mhdprob(i)) enddo call prompt('enter solution to plot ',ishk,1,7) call prompt('enter initial x position of shock ',xshock) case(9) call prompt('enter hfact [h = hfact*(m/rho)**1/ndim]',hfact,0.) case(10) print 20 20 format(' 1) Plummer sphere [ rho = 3M r_s**2 /(4 pi (r**2 + r_s**2)**5/2) ]',/, & ' 2) Hernquist model [ rho = M r_s /(2 pi r (r_s + r)**3 ]') call prompt('enter density profile to plot',iprofile,1,2) call prompt('enter total mass of sphere M',Msphere(1),0.) call prompt('enter scale length length r_s,',rsoft(1),0.) ians = .false. if (icolpoten.gt.0) ians = .true. call prompt('Are the gravitational potential and/or force dumped?',ians) if (ians) then call prompt('enter column containing grav. potential',icolpoten,0) call prompt('enter column containing grav. force',icolfgrav,0) endif call prompt('enter mass of 2nd component',Msphere(2),0.) call prompt('enter scale length r_s for 2nd component,',rsoft(2),0.) case(11) call prompt('enter mass of central object',Mstar,0.) call prompt('enter radius of torus centre',Rtorus,0.) call prompt('enter distortion parameter ',distortion,1.,2.) if (abs(polyk-1.0).lt.tiny(polyk)) polyk = 0.0764 call prompt('enter K in P= K*rho^gamma',polyk,0.) prompt_for_gamma = .true. case(12) call prompt('enter mass of ring',Mring,0.) call prompt('enter radius of ring centre R0',Rring,0.) call prompt('enter viscosity parameter nu',viscnu,0.) case(14) call prompt('enter starting x position',xzero) call prompt('enter wavelength lambda ',lambda,0.) call prompt('enter amplitude of perturbation',ampl,0.) call prompt('enter sound speed in gas ',cs,0.) call prompt('enter initial gas density ',rhozero,0.) call prompt('enter dust-to-gas ratio ',rdust_to_gas,0.) call prompt('enter drag coefficient K ',Kdrag,0.) case(15) call prompt('use data from sink particles?',use_sink_data) if (.not.use_sink_data) then call prompt('enter mass of primary star ',mprim,0.) call prompt('enter mass of secondary star ',msec,0.,mprim) call prompt('enter x position of primary',xprim(1)) call prompt('enter y position of primary',xprim(2)) call prompt('enter x position of secondary',xsec(1)) call prompt('enter y position of secondary',xsec(2)) endif case(16) call prompt('enter sonic Mach number',machs,0.) call prompt('enter Alfvenic Mach number ',macha,0.) case(17) do i=1,maxspirals print "(1x,i1,')',1x,a)",i,trim(labelspiral(i)) enddo call prompt('Which spiral arm solution to plot?',ispiral,0,maxspirals) select case(ispiral) case(2) call prompt('enter number of spiral arms to plot',narms,1,maxexact) print "(3(/,a),/)",' Spiral data can be read from '//trim(fileprefix)//'.spirals file', & ' with one line per arm and columns corresponding to:', & ' PAmin, PAmax, a0, a1, a2, a3, a4' do i=1,narms print "(a,i1,a)",' -- arm ',i,' --' call prompt('enter starting position angle (deg)',spiral_params(1,i)) call prompt('enter finishing position angle (deg)',spiral_params(2,i)) call prompt('enter a0 in r = \sum a_i phi^i',spiral_params(3,i)) call prompt('enter a1 in r = \sum a_i phi^i',spiral_params(4,i)) call prompt('enter a2 in r = \sum a_i phi^i',spiral_params(5,i)) call prompt('enter a3 in r = \sum a_i phi^i',spiral_params(6,i)) call prompt('enter a4 in r = \sum a_i phi^i',spiral_params(7,i)) enddo case default call prompt('enter disc aspect ratio (H/R)',HonR,0.,1.) !call prompt('enter planet orbital radius ',rplanet,0.) end select case(18) prompt_for_gamma = .true. call prompt('is it a wind (instead of accretion)?',is_wind) call prompt('enter mass of central object',Mstar,0.) call prompt('do you want a relativistic solution?',relativistic) if(.not.relativistic) then prompt_for_gamma = .false. call prompt('enter Parker/Bondi critical radius (rcrit)',const1) call prompt('enter density at critical radius (rhocrit)',const2) elseif(relativistic) then call prompt('is it the geodesic flow?',geodesic_flow) if (geodesic_flow) then const1 = 1. const2 = 1.e-9 call prompt('enter constant den0',const1,min=0.) call prompt('enter constant en0 ',const2,min=0.) else call prompt('enter the critical radius in units of the central mass M',const1,min=2.) const1 = const1*Mstar const2 = 1. call prompt('enter adiabat (entropy normalisation)',const2,min=0.) endif endif end select if (prompt_for_gamma) then call prompt('set adiabatic gamma manually? (no=read from dumps)',use_gamma_exact) if (use_gamma_exact) then call prompt('enter gamma',gamma_exact) endif endif return end subroutine submenu_exact !--------------------------------------------------- ! sets options relating to exact solution plotting !--------------------------------------------------- subroutine options_exact use prompting, only:prompt use plotlib, only:plotlib_maxlinestyle,plotlib_maxlinecolour implicit none call prompt('enter number of exact solution points ',maxexactpts,10,1000000) call prompt('enter line colour ',iExactLineColour,1,plotlib_maxlinecolour) call prompt('enter line style ',iExactLineStyle,1,plotlib_maxlinestyle) call prompt('calculate error norms?',iCalculateExactErrors) if (iCalculateExactErrors) then print "(/,' 0 : not normalised L1 = 1/N \sum |y - y_exact|',/,"// & "' 1 : normalised L1 = 1/N 1/max(y_exact) \sum |y - y_exact|')" call prompt('enter choice of error norm',iNormaliseErrors) endif if (iCalculateExactErrors) then call prompt('plot residuals (as inset in main plot)?',iPlotResiduals) if (iPlotResiduals) then call prompt('enter fraction of plot to use for inset', & fracinsetResiduals,0.1,0.9) call prompt('enter max residual (0 for adaptive)',residualmax,0.) endif endif print "(/,' 0 : plot exact solution (where available) on every panel ',/,"// & "' -1 : plot exact solution on first row only ',/,"// & "' -2 : plot exact solution on first column only ',/,"// & "' n : plot exact solution on nth panel only ')" call prompt('Enter selection ',iPlotExactOnlyOnPanel,-2) return end subroutine options_exact !----------------------------------------------------------------------- ! read exact solution parameters from files ! (in ndspmhd these files are used in the input to the code) ! ! called after main data read and if exact solution chosen from menu !----------------------------------------------------------------------- subroutine read_exactparams(iexact,rootname,ierr) use settings_data, only:ndim,iverbose use prompting, only:prompt use exactfunction, only:check_function use filenames, only:fileprefix use asciiutils, only:read_asciifile,get_line_containing integer, intent(in) :: iexact character(len=*), intent(in) :: rootname integer, intent(out) :: ierr integer :: idash,nf,i,j,idrag,idum,linenum,k,ieq,ierrs(6),narmsread character(len=len_trim(rootname)+8) :: filename character(len=120) :: line character(len=30) :: var logical :: iexist idash = index(rootname,'_',back=.true.) if (idash.eq.0) idash = len_trim(rootname)+1 select case(iexact) case(1) ! !--read functions from file ! filename=trim(rootname)//'.func' call read_asciifile(trim(filename),nf,funcstring,ierr) if (ierr.eq.-1) then if (iverbose > 0) write(*,"(a)",advance='no') ' no file '//trim(filename)//'; ' filename = trim(fileprefix)//'.func' call read_asciifile(trim(filename),nf,funcstring,ierr) if (ierr.eq.-1) then if (iverbose > 0) print "(a)",' no file '//trim(filename) return endif endif if (nf.gt.0) then i = 0 do while(i.lt.nf) i = i + 1 call check_function(funcstring(i),ierr,verbose=.false.) if (ierr /= 0) then print "(a)",' error parsing function '//trim(funcstring(i))//', skipping...' do j=i+1,nf funcstring(j-1) = funcstring(j) enddo funcstring(nf) = ' ' nf = nf - 1 i = i - 1 endif enddo nfunc = nf if (iverbose > 0) print "(a,i2,a)",' read ',nfunc,' functions from '//trim(filename) else print "(a)",' *** NO FUNCTIONS READ: none will be plotted ***' ierr = 2 endif case(3,13) ! !--shock tube parameters from .shk file ! filename = trim(rootname(1:idash-1))//'.shk' inquire(file=filename,exist=iexist) if (iexist) then open(unit=19,file=filename,status='old',iostat=ierr) if (ierr==0) then read(19,*,iostat=ierrs(1)) rho_L, rho_R read(19,*,iostat=ierrs(2)) pr_L, pr_R read(19,*,iostat=ierrs(3)) v_L, v_R if (any(ierrs(1:3)/=0)) then print*,'error reading ',filename ierr = 1 endif endif close(unit=19) else print*,'no file ',filename ! ! look for .setup file for Phantom ! filename= trim(rootname(1:idash-1))//'.setup' inquire(file=filename,exist=iexist) open(unit=19,file=filename,status='old',iostat=ierr) ierrs(:) = 0 do while(ierr==0) read(19,"(a)",iostat=ierr) line ieq = index(line,'=') if (ierr==0 .and. ieq > 1) then var = trim(adjustl(line(1:ieq-1))) select case(trim(var)) case('densleft') read(line(ieq+1:),*,iostat=ierrs(1)) rho_L case('densright') read(line(ieq+1:),*,iostat=ierrs(1)) rho_R case('prleft') read(line(ieq+1:),*,iostat=ierrs(1)) pr_L case('prright') read(line(ieq+1:),*,iostat=ierrs(1)) pr_R case('vxleft') read(line(ieq+1:),*,iostat=ierrs(1)) v_L case('vxright') read(line(ieq+1:),*,iostat=ierrs(1)) v_R case('dtg') read(line(ieq+1:),*,iostat=ierrs(1)) rdust_to_gas end select endif enddo ierr = 0 if (.not.iexist .or. any(ierrs(1:6)/=0)) ierr = 1 endif if (iexist) then print*,'>> read ',filename print*,' rhoL, rho_R = ',rho_L,rho_R print*,' pr_L, pr_R = ',pr_L, pr_R print*,' v_L, v_R = ',v_L, v_R endif case(6) ! !--read toy star file for toy star solution ! select case(ndim) case(1) filename = trim(rootname(1:idash-1))//'.tstar' open(unit=20,ERR=8801,FILE=filename,STATUS='old') read(20,*,ERR=8888) Htstar,Ctstar,Atstar read(20,*,ERR=8888) sigma0 read(20,*,ERR=8888) norder close(UNIT=20) print*,' >> read ',filename print*,' H,C,A,sigma,n = ',Htstar,Ctstar,Atstar,sigma0,norder return 8801 continue print*,'no file ',filename ierr = 1 return 8888 print*,'error reading ',filename close(UNIT=20) ierr = 2 return case(2) filename = trim(rootname(1:idash-1))//'.tstar2D' open(unit=20,ERR=9901,FILE=filename,STATUS='old') read(20,*,ERR=9902) Htstar,Ctstar,Atstar read(20,*,ERR=9902) alphatstar,betatstar,ctstar1,ctstar2 read(20,*,ERR=9902) norder,morder close(UNIT=20) print*,' >> read ',filename print*,' j,m = ',norder,morder print*,' rho_0 = ',Htstar,' - ',Ctstar,' r^2' if (norder.ge.0 .and. morder.ge.0) then print*,' v = ',Atstar,' r' else print*,' vx = ',alphatstar,'x +',ctstar1,'y' print*,' vy = ',ctstar2,'x +',betatstar,'y' endif return 9901 continue print*,'no file ',filename ierr = 1 return 9902 print*,'error reading ',filename close(UNIT=20) ierr = 2 return end select case(8) ! !--attempt to guess which MHD shock tube has been done from filename ! !read(rootname(5:5),*,iostat=ios) ishk !if (ios.ne.0) ishk = 1 ! !--prompt for shock type if not set ! if (ishk.le.0) then ! prompt ishk = 1 call prompt('enter shock solution to plot',ishk,1,7) endif return case(14) ! !--dustywave parameters from ndspmhd or Phantom input file ! filename = trim(rootname(1:idash-1))//'.in' var = 'Kdrag' !ndspmhd linenum = get_line_containing(filename,trim(var)) if (linenum==0) then var = 'K_code' !phantom linenum = get_line_containing(filename,trim(var)) endif open(unit=19,file=filename,status='old',iostat=ierr) if (ierr.eq.0) then do i=1,linenum-1 read(19,*,iostat=ierr) enddo read(19,"(a)",iostat=ierr) line if (ierr.eq.0) then k = index(line,trim(var)) if (k > 0) read(line(k+len_trim(var)+2:),*,iostat=ierr) Kdrag if (ierr.eq.0) then print*,'>> read '//trim(var)//' = ',Kdrag,' from '//trim(filename) else read(line,*,iostat=ierr) idrag, idum, idum, Kdrag if (ierr.eq.0) then print*,'>> read '//trim(var)//' = ',Kdrag,' from old-style '//trim(filename) else print*,'>> error reading Kdrag from '//trim(filename) endif endif else print*,'>> error reading Kdrag from '//trim(filename) endif endif close(unit=19) return case(17) ! !--spiral arm parameters from .spirals file ! filename=trim(rootname)//'.spirals' call read_asciifile(trim(filename),narmsread,spiral_params,ierr) if (ierr.eq.-1) then if (iverbose > 0) write(*,"(a)",advance='no') ' no file '//trim(filename)//'; ' filename = trim(fileprefix)//'.spirals' call read_asciifile(trim(filename),narmsread,spiral_params,ierr) if (ierr.eq.-1) then if (iverbose > 0) print "(a)",' no file '//trim(filename) return else if (iverbose > 0) print*,trim(filename)//' read ',narmsread,' arms, err = ',ierr if (narmsread >= 0) narms = narmsread endif else if (iverbose > 0) print*,trim(filename)//' read ',narmsread,' arms, err = ',ierr narms = max(narmsread,0) endif end select return end subroutine read_exactparams !----------------------------------------------------------------------- ! this subroutine drives the exact solution plotting using the ! parameters which have been set ! ! acts as an interface between the main plotting loop and the ! exact solution calculation subroutines ! ! The exact solution is returned from the calculation via the arrays ! xexact and yexact. This means that the appropriate transformations ! can be applied (e.g. if the graph is logarithmic) and also ensures ! that the line style and colour settings are applied properly. ! ! Note that we attempt to space the solution evenly in the transformed ! space (ie. in the current plot window), but this can be overwritten ! in the subroutines (for example if an uneven sampling is desired or ! the plotting is via some similarity variable as in the Sedov solution). ! In these cases the resulting arrays are then transformed, possibly leading ! to poor sampling in some regions (e.g. an evenly spaced array will become ! highly uneven in logarithmic space). ! ! Note that any subroutine could in principle do its own plotting, ! provided that it returns ierr > 0 which means that the generic line ! is not plotted. Obviously transformations could not be applied in ! this case. ! !----------------------------------------------------------------------- subroutine exact_solution(iexact,iplotx,iploty,itransx,itransy,igeom, & ndim,ndimV,time,xmin,xmax,gamma,xplot,yplot,& itag,iamtype,noftype,iplot_type, & pmassmin,pmassmax,npart,imarker,unitsx,unitsy,irescale,iaxisy) use params, only:int1,maxparttypes use labels, only:ix,irad,iBfirst,ivx,irho,ike,iutherm,ih,ipr,iJfirst,& irhorestframe,is_coord,ideltav,idustfrac use filenames, only:ifileopen,rootname use asciiutils, only:string_replace use prompting, only:prompt use exactfromfile, only:exact_fromfile use mhdshock, only:exact_mhdshock use polytrope, only:exact_polytrope use rhoh, only:exact_rhoh use sedov, only:exact_sedov use shock, only:exact_shock use shock_sr, only:exact_shock_sr use torus, only:exact_torus use toystar1D, only:exact_toystar1D !, exact_toystar_ACplane use toystar2D, only:exact_toystar2D use wave, only:exact_wave use densityprofiles, only:exact_densityprofiles use exactfunction, only:exact_function use ringspread, only:exact_ringspread use dustywaves, only:exact_dustywave use rochelobe, only:exact_rochelobe use gresho, only:exact_gresho use Cshock, only:exact_Cshock use planetdisc, only:exact_planetdisc use bondi, only:exact_bondi use transforms, only:transform,transform_inverse use plotlib, only:plot_qci,plot_qls,plot_sci,plot_sls,plot_line,plotlib_maxlinestyle integer, intent(in) :: iexact,iplotx,iploty,itransx,itransy,igeom integer, intent(in) :: ndim,ndimV,npart,imarker,iaxisy real, intent(in) :: time,xmin,xmax,gamma,unitsx,unitsy real, intent(in) :: pmassmin,pmassmax real, intent(in) :: xplot(npart),yplot(npart) integer, intent(in) :: itag(npart) integer(int1), intent(in) :: iamtype(:) integer, intent(in) :: noftype(maxparttypes) logical, intent(in) :: iplot_type(maxparttypes) logical, intent(in) :: irescale real, parameter :: zero = 1.e-10 integer :: i,ierr,iexactpts,iCurrentColour,iCurrentLineStyle,LineStyle real, allocatable :: xexact(:),yexact(:),xtemp(:) real :: dx,timei,gammai character(len=len(filename_exact)) :: filename_tmp ! !--change line style and colour settings, but save old ones ! call plot_qci(iCurrentColour) call plot_qls(iCurrentLineStyle) call plot_sci(iExactLineColour) call plot_sls(iExactLineStyle) ! !--allocate memory ! allocate(xexact(maxexactpts),yexact(maxexactpts),xtemp(maxexactpts),stat=ierr) if (ierr /= 0) then print "(a)",'*** ERROR allocating memory for exact solution plotting, skipping ***' if (allocated(xexact)) deallocate(xexact) if (allocated(yexact)) deallocate(yexact) if (allocated(xtemp)) deallocate(xtemp) return endif ! !--set x axis (can be overwritten) ! Need to space x in transformed space (e.g. in log space) ! but send the values of x in *real* space to the calculation routines ! then need to plot x in transformed space ! ! Best solution is to set x grid initially, and inverse transform to get x values. ! These values can then be overwritten, if required in the exact subroutines ! We then re-transform the x array to plot it, which means that if spacing is ! overwritten the resulting array can still be transformed into log space ! but spacing will not be even ! !--note that xmin and xmax will already have been transformed prior to input ! as these were the limits used for plotting the particles ! dx = (xmax - xmin)/real(maxexactpts) do i=1,maxexactpts xexact(i) = xmin + (i-1)*dx enddo xtemp = xexact if (itransx.gt.0) call transform_inverse(xexact,itransx) iexactpts = maxexactpts ! !--exact solution plots must return a zero or negative value of ierr to be plotted ! (-ve ierr indicates a partial solution) ! ierr = 666 ! !--use time=0 if time has not been read from dump file (indicated by t < 0) ! if (time > 0) then timei = time else timei = 0. endif if (use_gamma_exact .and. gamma_exact >= 1.) then gammai = gamma_exact elseif (gamma >= 1.) then gammai = gamma else gammai = 5./3. endif select case(iexact) case(1) ! arbitrary function parsing do i=1,nfunc if ((iplotx.eq.iexactplotx(i) .and. iploty.eq.iexactploty(i)) .or. iexactploty(i).eq.0) then call exact_function(funcstring(i),xexact,yexact,timei,ierr) !--plot each solution separately and calculate errors call plot_exact_solution(itransx,itransy,iexactpts,npart,xexact,yexact,xplot,yplot, & itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy) ierr = 1 ! indicate that we have already plotted the solution endif enddo case(2) ! exact solution read from file do i=1,nfiles if (iplotx.eq.iexactplotx(i) .and. iploty.eq.iexactploty(i)) then !--substitute %f for filename filename_tmp = filename_exact(i) call string_replace(filename_tmp,'%f',trim(rootname(ifileopen))) !--read exact solution from file call exact_fromfile(filename_tmp,xexact,yexact,ixcolfile(i),iycolfile(i),iexactpts,ierr) !--plot this untransformed (as may already be in log space) if (ierr <= 0) then if (iApplyTransExactFile) then !--change into physical units if appropriate if (iRescale .and. iApplyUnitsExactFile) then xexact(1:iexactpts) = xexact(1:iexactpts)*unitsx yexact(1:iexactpts) = yexact(1:iexactpts)*unitsy endif endif !--change line style between files LineStyle = mod(iExactLineStyle+i-1,plotlib_maxlinestyle) !--plot each solution separately and calculate errors call plot_exact_solution(itransx,itransy,iexactpts,npart,xexact,yexact,xplot,yplot,& itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy,ls=LineStyle) ierr = 1 ! indicate that we have already plotted the solution endif endif enddo case(3)! shock tube if (iplotx.eq.ix(1) .and. igeom.le.1) then if (iploty.eq.irho) then call exact_shock(1,timei,gammai,xshock,rho_L,rho_R,pr_L,pr_R,v_L,v_R, & rdust_to_gas,xexact,yexact,ierr) elseif (iploty.eq.ipr) then call exact_shock(2,timei,gammai,xshock,rho_L,rho_R,pr_L,pr_R,v_L,v_R, & rdust_to_gas,xexact,yexact,ierr) elseif (iploty.eq.ivx) then call exact_shock(3,timei,gammai,xshock,rho_L,rho_R,pr_L,pr_R,v_L,v_R, & rdust_to_gas,xexact,yexact,ierr) elseif (iploty.eq.iutherm) then call exact_shock(4,timei,gammai,xshock,rho_L,rho_R,pr_L,pr_R,v_L,v_R, & rdust_to_gas,xexact,yexact,ierr) elseif (iploty.eq.ideltav) then call exact_shock(5,timei,gammai,xshock,rho_L,rho_R,pr_L,pr_R,v_L,v_R, & rdust_to_gas,xexact,yexact,ierr) elseif (iploty.eq.idustfrac) then call exact_shock(6,timei,gammai,xshock,rho_L,rho_R,pr_L,pr_R,v_L,v_R, & rdust_to_gas,xexact,yexact,ierr) endif endif case(4)! sedov blast wave ! this subroutine does change xexact if (iplotx.eq.irad .or. (igeom.eq.3 .and. iplotx.eq.ix(1))) then if (iploty.eq.irho) then call exact_sedov(1,timei,gammai,rhosedov,esedov,xmax,xexact,yexact,ierr) elseif (iploty.eq.ipr) then call exact_sedov(2,timei,gammai,rhosedov,esedov,xmax,xexact,yexact,ierr) elseif (iploty.eq.iutherm) then call exact_sedov(3,timei,gammai,rhosedov,esedov,xmax,xexact,yexact,ierr) elseif (iploty.eq.ike) then call exact_sedov(4,timei,gammai,rhosedov,esedov,xmax,xexact,yexact,ierr) elseif (iploty.eq.ivx .and. igeom.eq.3) then call exact_sedov(5,timei,gammai,rhosedov,esedov,xmax,xexact,yexact,ierr) endif elseif (igeom.le.1 .and. is_coord(iplotx,ndim) .and. is_coord(iploty,ndim)) then call exact_sedov(0,timei,gammai,rhosedov,esedov,xmax,xexact,yexact,ierr) endif case(5)! polytrope if (iploty.eq.irho .and. (iplotx.eq.irad .or.(igeom.eq.3 .and. iplotx.eq.ix(1)))) then call exact_polytrope(gammai,polyk,totmass,xexact,yexact,iexactpts,ierr) endif case(6)! toy star if (iBfirst.ne.0) then sigma = sigma0 else sigma = 0. endif if (ndim.eq.1) then ! !--1D toy star solutions ! if (iplotx.eq.ix(1) .or. iplotx.eq.irad) then! if x axis is x or r if (iploty.eq.irho) then call exact_toystar1D(1,timei,gammai,htstar,atstar,ctstar,sigma,norder, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.ipr) then call exact_toystar1D(2,timei,gammai,htstar,atstar,ctstar,sigma,norder, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.iutherm) then call exact_toystar1D(3,timei,gammai,htstar,atstar,ctstar,sigma,norder, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.ivx) then call exact_toystar1D(4,timei,gammai,htstar,atstar,ctstar,sigma,norder, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.iBfirst+1) then call exact_toystar1D(5,timei,gammai,htstar,atstar,ctstar,sigma,norder, & xexact,yexact,iexactpts,ierr) endif elseif (iplotx.eq.irho) then if (iploty.eq.iBfirst+1) then call exact_toystar1D(6,timei,gammai,htstar,atstar,ctstar,sigma,norder, & xexact,yexact,iexactpts,ierr) endif endif if (iploty.eq.iacplane) then! plot point on a-c plane call exact_toystar1D(7,timei,gammai,htstar,atstar,ctstar,sigma,norder, & xexact,yexact,iexactpts,ierr) endif else ! !--2D toy star solutions ! these routines change xexact ! if (igeom.eq.1 .and.((iplotx.eq.ix(1) .and. iploty.eq.ivx) & .or. (iplotx.eq.ix(2) .and. iploty.eq.ivx+1))) then call exact_toystar2D(4,timei,gammai,polyk,totmass, & atstar,htstar,ctstar,norder,morder, & alphatstar,betatstar,ctstar1,ctstar2,xexact,yexact,ierr) endif if (iplotx.eq.irad .or. (igeom.eq.2 .and. iplotx.eq.ix(1))) then if (iploty.eq.irho) then call exact_toystar2D(1,timei,gammai,polyk,totmass, & atstar,htstar,ctstar,norder,morder, & alphatstar,betatstar,ctstar1,ctstar2,xexact,yexact,ierr) elseif (iploty.eq.ipr) then call exact_toystar2D(2,timei,gammai,polyk,totmass, & atstar,htstar,ctstar,norder,morder, & alphatstar,betatstar,ctstar1,ctstar2,xexact,yexact,ierr) elseif (iploty.eq.iutherm) then call exact_toystar2D(3,timei,gammai,polyk,totmass, & atstar,htstar,ctstar,norder,morder, & alphatstar,betatstar,ctstar1,ctstar2,xexact,yexact,ierr) elseif (igeom.eq.2 .and. iploty.eq.ivx) then call exact_toystar2D(4,timei,gammai,polyk,totmass, & atstar,htstar,ctstar,norder,morder, & alphatstar,betatstar,ctstar1,ctstar2,xexact,yexact,ierr) elseif (iploty.eq.ike) then call exact_toystar2D(5,timei,gammai,polyk,totmass, & atstar,htstar,ctstar,norder,morder, & alphatstar,betatstar,ctstar1,ctstar2,xexact,yexact,ierr) endif elseif (is_coord(iplotx,ndim) .and. is_coord(iploty,ndim) .and. igeom.eq.1) then call exact_toystar2D(0,timei,gammai,polyk,totmass, & atstar,htstar,ctstar,norder,morder, & alphatstar,betatstar,ctstar1,ctstar2,xexact,yexact,ierr) endif endif case(7)! Gresho vortex !if ((iploty.eq.iwaveploty).and.(iplotx.eq.iwaveplotx)) then ! ymean = SUM(yplot(1:npart))/REAL(npart) ! call exact_wave(timei,ampl,period,lambda,xzero,ymean,xexact,yexact,ierr) !endif if (igeom.eq.2 .and. ndim.ge.2) then if (iploty.eq.ivx+1) then call exact_gresho(1,xexact,yexact,ierr) elseif (iploty.eq.ipr) then call exact_gresho(2,xexact,yexact,ierr) endif endif case(8) ! mhd shock tubes ! this subroutine modifies xexact if (iplotx.eq.ix(1) .and. igeom.le.1) then if (iploty.eq.irho) then call exact_mhdshock(1,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.ipr) then call exact_mhdshock(2,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.ivx) then call exact_mhdshock(3,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.ivx+1 .and. ndimV.gt.1) then call exact_mhdshock(4,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.ivx+ndimV-1 .and. ndimV.gt.2) then call exact_mhdshock(5,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.iBfirst+1 .and. ndimV.gt.1) then call exact_mhdshock(6,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.iBfirst+ndimV-1 .and. ndimV.gt.2) then call exact_mhdshock(7,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.iutherm) then call exact_mhdshock(8,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) elseif (iploty.eq.iBfirst) then call exact_mhdshock(9,ishk,timei,gammai,xmin,xmax,xshock, & xexact,yexact,iexactpts,ierr) endif endif case(9) !--h = (1/rho)^(1/ndim) if (((iploty.eq.ih).and.(iplotx.eq.irho)) .or. & ((iplotx.eq.ih).and.(iploty.eq.irho))) then if (iplotx.eq.ih) then call exact_rhoh(2,ndim,hfact,pmassmin,xexact,yexact,ierr) else call exact_rhoh(1,ndim,hfact,pmassmin,xexact,yexact,ierr) endif !--if variable particle masses, plot one for each pmass value if (abs(pmassmin-pmassmax).gt.zero .and. pmassmin.gt.zero) then !--plot first line if (ierr.le.0) then xtemp = xexact ! must not transform xexact as this is done again below if (itransx.gt.0) call transform(xtemp,itransx) if (itransy.gt.0) call transform(yexact,itransy) call plot_line(iexactpts,xtemp(1:iexactpts),yexact(1:iexactpts)) endif !--leave this one to be plotted below if (iplotx.eq.ih) then call exact_rhoh(2,ndim,hfact,pmassmax,xexact,yexact,ierr) else call exact_rhoh(1,ndim,hfact,pmassmax,xexact,yexact,ierr) endif endif endif case(10) ! density profiles if (iplotx.eq.irad .or.(igeom.eq.3 .and. iplotx.eq.ix(1))) then if (iploty.eq.irho) then call exact_densityprofiles(1,iprofile,Msphere,rsoft,xexact,yexact,ierr) elseif (iploty.eq.icolpoten) then call exact_densityprofiles(2,iprofile,Msphere,rsoft,xexact,yexact,ierr) elseif (iploty.eq.icolfgrav) then call exact_densityprofiles(3,iprofile,Msphere,rsoft,xexact,yexact,ierr) endif endif case(11) ! torus if (iplotx.eq.irad .or.(igeom.eq.3 .and. iplotx.eq.ix(1))) then if (iploty.eq.irho) then call exact_torus(1,1,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) elseif (iploty.eq.ipr) then call exact_torus(2,1,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) elseif (iploty.eq.iutherm) then call exact_torus(3,1,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) endif !--pr vs z at r=Rtorus elseif (igeom.eq.2 .and. iplotx.eq.ix(3) .and.iploty.eq.ipr) then call exact_torus(4,1,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) endif !--solutions for tokamak torus if (igeom.eq.4 .and. iplotx.eq.ix(1)) then if (iploty.eq.irho) then call exact_torus(1,2,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) elseif (iploty.eq.ipr) then call exact_torus(2,2,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) elseif (iploty.eq.iutherm) then call exact_torus(3,2,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) elseif (iploty.eq.iBfirst+1 .and. iBfirst.gt.0) then call exact_torus(4,2,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) elseif (iploty.eq.iJfirst+2 .and. iJfirst.gt.0) then call exact_torus(5,2,Mstar,Rtorus,polyk,distortion,gammai,xexact,yexact,ierr) endif endif case(12) if (iplotx.eq.irad .or.((igeom.eq.3 .or. igeom.eq.2) .and. iplotx.eq.ix(1))) then if (iploty.eq.irho) then call exact_ringspread(1,timei,Mring,Rring,viscnu,xexact,yexact,ierr) endif endif case(13) ! special relativistic shock tube if (iplotx.eq.ix(1) .and. igeom.le.1) then if (iploty.eq.irhorestframe) then call exact_shock_sr(1,timei,gammai,rho_L,rho_R,pr_L,pr_R,v_L,v_R,xexact,yexact,ierr) elseif (iploty.eq.ipr) then call exact_shock_sr(2,timei,gammai,rho_L,rho_R,pr_L,pr_R,v_L,v_R,xexact,yexact,ierr) elseif (iploty.eq.ivx) then call exact_shock_sr(3,timei,gammai,rho_L,rho_R,pr_L,pr_R,v_L,v_R,xexact,yexact,ierr) elseif (iploty.eq.iutherm) then call exact_shock_sr(4,timei,gammai,rho_L,rho_R,pr_L,pr_R,v_L,v_R,xexact,yexact,ierr) elseif (iploty.eq.irho) then call exact_shock_sr(5,timei,gammai,rho_L,rho_R,pr_L,pr_R,v_L,v_R,xexact,yexact,ierr) endif endif case(14) ! dusty wave exact solution if (iplotx.eq.ix(1) .and. igeom.le.1) then if (iploty.eq.ivx) then !--plot gas solution and calculate errors call exact_dustywave(1,timei,ampl,cs,Kdrag,lambda,xzero,rhozero,rhozero*rdust_to_gas,xexact,yexact,ierr) call plot_exact_solution(itransx,itransy,iexactpts,npart,xexact,yexact,xplot,yplot, & itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy,ls=1,matchtype=1) !--plot dust solution if (Kdrag > 0.) then call exact_dustywave(2,timei,ampl,cs,Kdrag,lambda,xzero,rhozero,rhozero*rdust_to_gas,xexact,yexact,ierr) call plot_exact_solution(itransx,itransy,iexactpts,npart,xexact,yexact,xplot,yplot, & itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy,ls=2,matchtype=2) endif ierr = 1 elseif (iploty.eq.irho) then !--plot gas solution and calculate errors call exact_dustywave(3,timei,ampl,cs,Kdrag,lambda,xzero,rhozero,rhozero*rdust_to_gas,xexact,yexact,ierr) call plot_exact_solution(itransx,itransy,iexactpts,npart,xexact,yexact,xplot,yplot,& itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy,ls=1,matchtype=1) !--plot dust solution if (Kdrag > 0.) then call exact_dustywave(4,timei,ampl,cs,Kdrag,lambda,xzero,rhozero,rhozero*rdust_to_gas,xexact,yexact,ierr) call plot_exact_solution(itransx,itransy,iexactpts,npart,xexact,yexact,xplot,yplot,& itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy,ls=2,matchtype=2) endif ierr = 1 endif endif case(15) ! Roche potential if (igeom.eq.1 .and. ndim.ge.2 .and. iplotx.eq.ix(1) .and. iploty.eq.ix(2)) then call exact_rochelobe(xprim(1),xprim(2),xsec(1),xsec(2),mprim,msec,xexact,yexact,ierr) endif case(16) ! C-shock if (ndim.ge.1 .and. iplotx.eq.ix(1) .and. igeom.le.1) then if (iploty.eq.irho) then call exact_Cshock(1,timei,gammai,machs,macha,xmin,xmax,xexact,yexact,ierr) elseif (iploty.eq.iBfirst+1 .and. iBfirst.gt.0) then call exact_Cshock(2,timei,gammai,machs,macha,xmin,xmax,xexact,yexact,ierr) elseif (iploty.eq.ivx) then call exact_Cshock(3,timei,gammai,machs,macha,xmin,xmax,xexact,yexact,ierr) elseif (iploty.eq.ivx+1 .and. ndimV > 1) then call exact_Cshock(4,timei,gammai,machs,macha,xmin,xmax,xexact,yexact,ierr) elseif (iploty.eq.iBfirst .and. iBfirst.gt.0) then call exact_Cshock(5,timei,gammai,machs,macha,xmin,xmax,xexact,yexact,ierr) endif endif case(17) ! planet-disc interaction if (ndim.ge.2 .and. iplotx.eq.ix(1) .and. iploty.eq.ix(2)) then call exact_planetdisc(igeom,ispiral,timei,HonR,rplanet,narms,spiral_params,xexact,yexact,ierr) endif case(18) if (iplotx.eq.irad .or. (igeom.eq.3 .and. iplotx.eq.ix(1))) then if (iploty.eq.ivx .and. igeom==3) then call exact_bondi(1,timei,gammai,const1,const2,Mstar,relativistic,geodesic_flow,is_wind,xexact,yexact,ierr) elseif (iploty.eq.iutherm .and. igeom==3) then call exact_bondi(2,timei,gammai,const1,const2,Mstar,relativistic,geodesic_flow,is_wind,xexact,yexact,ierr) elseif (iploty.eq.irho .and. igeom==3) then call exact_bondi(3,timei,gammai,const1,const2,Mstar,relativistic,geodesic_flow,is_wind,xexact,yexact,ierr) endif endif end select !---------------------------------------------------------- ! plot this as a line on the current graph !---------------------------------------------------------- if (ierr <= 0) call plot_exact_solution(itransx,itransy,iexactpts,npart,xexact,yexact,xplot,yplot, & itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy) ! !--reset line and colour settings ! call plot_sci(iCurrentColour) call plot_sls(iCurrentLineStyle) ! !--deallocate memory ! if (allocated(xexact)) deallocate(xexact) if (allocated(yexact)) deallocate(yexact) if (allocated(xtemp)) deallocate(xtemp) return end subroutine exact_solution !------------------------------------------------------------------ ! Wrapper routine to plot the exact solution line on current graph ! and calculate errors with respect to the data !------------------------------------------------------------------ subroutine plot_exact_solution(itransx,itransy,iexactpts,np,xexact,yexact,xplot,yplot,& itag,iamtype,noftype,iplot_type,xmin,xmax,imarker,iaxisy,ls,matchtype,err) use transforms, only:transform,transform_inverse use plotlib, only:plot_line,plot_sls,plot_sci,plot_qci use params, only:int1,maxparttypes integer, intent(in) :: itransx,itransy,iexactpts,np,imarker,iaxisy real, intent(inout) :: xexact(:),yexact(:) real, intent(in) :: xplot(:),yplot(:),xmin,xmax integer, intent(in) :: itag(:) integer(int1), intent(in) :: iamtype(:) integer, intent(in) :: noftype(maxparttypes) logical, intent(in) :: iplot_type(maxparttypes) integer, intent(in), optional :: ls, matchtype logical, intent(in), optional :: err real :: residuals(np),ypart(np) real :: errL1,errL2,errLinf integer :: iused,ierr,iCurrentColour,imatchtype logical :: plot_err character(len=12) :: str1,str2 call plot_qci(iCurrentColour) call plot_sci(iExactLineColour) if (itransx > 0) call transform(xexact(1:iexactpts),itransx) if (itransy > 0) call transform(yexact(1:iexactpts),itransy) if (present(ls)) call plot_sls(ls) call plot_line(iexactpts,xexact(1:iexactpts),yexact(1:iexactpts)) ! !--calculate errors ! if (present(err)) then plot_err = err else plot_err = .true. endif if (present(matchtype)) then imatchtype = matchtype else imatchtype = 0 endif if (iCalculateExactErrors .and. plot_err) then !--untransform y axis again for error calculation if (itransy > 0) call transform_inverse(yexact(1:iexactpts),itransy) !--untransform particle y axis also ypart(1:np) = yplot(1:np) if (itransy > 0) call transform_inverse(ypart(1:np),itransy) !--calculate errors call calculate_errors(xexact(1:iexactpts),yexact(1:iexactpts),xplot(1:np),ypart,& itag,iamtype,noftype,iplot_type,xmin,xmax,residuals, & errL1,errL2,errLinf,iused,imatchtype) if (iused.ne.np) then write(str1,"(i12)",iostat=ierr) iused write(str2,"(i12)",iostat=ierr) np print "(3(a,es12.5,1x),'(used ',a,'/',a,' parts)')",' L1 err = ',errL1,'L2 err = ',errL2, & 'L(inf) err = ',errLinf,trim(adjustl(str1)),trim(adjustl(str2)) else print "(3(a,es12.5,1x))",' L1 err = ',errL1,'L2 err = ',errL2, & 'L(inf) err = ',errLinf endif if (iPlotResiduals) then call plot_sci(1) call plot_residuals(xplot,residuals,imarker,iaxisy) endif endif call plot_sci(iCurrentColour) end subroutine plot_exact_solution !-------------------------------- ! Calculate various error norms !-------------------------------- subroutine calculate_errors(xexact,yexact,xpts,ypts,itag,iamtype,noftype,iplot_type,& xmin,xmax,residual,errL1,errL2,errLinf,iused,imatchtype) use part_utils, only:igettype use params, only:int1,maxparttypes real, intent(in) :: xexact(:),yexact(:),xpts(:),ypts(:),xmin,xmax integer, intent(in) :: itag(:) integer(int1), intent(in) :: iamtype(:) integer, intent(in) :: noftype(maxparttypes) logical, intent(in) :: iplot_type(maxparttypes) real, intent(out) :: residual(size(xpts)) real, intent(out) :: errL1,errL2,errLinf integer, intent(out) :: iused integer, intent(in) :: imatchtype integer :: i,j,npart,nerr,nused,itype real :: xi,dy,dx,yexacti,err1,ymax logical :: mixedtypes errL1 = 0. errL2 = 0. errLinf = 0. residual = 0. npart = size(xpts) iused = 0 ymax = 0. nerr = 0 nused = 0 mixedtypes = size(iamtype).gt.1 do i=1,npart xi = xpts(i) yexacti = 0. if (xi >= xmin .and. xi <= xmax .and. itag(i) > 0) then if (mixedtypes) then itype = min(max(int(iamtype(i)),1),maxparttypes) else itype = igettype(i,noftype) endif if (iplot_type(itype) .and. ((imatchtype==0).or.(itype==imatchtype))) then ! !--find nearest point in exact solution table ! do j=1,size(xexact)-1 if (xexact(j).le.xi .and. xexact(j+1).gt.xi) then if (abs(residual(i)).gt.tiny(residual)) nerr = nerr + 1 !--linear interpolation from tabulated exact solution dy = yexact(j+1) - yexact(j) dx = xexact(j+1) - xexact(j) if (dx.gt.0.) then yexacti = yexact(j) + dy/dx*(xi - xexact(j)) residual(i) = ypts(i) - yexacti elseif (dy.ge.0.) then yexacti = yexact(j) residual(i) = ypts(i) - yexacti else nerr = nerr + 1 residual(i) = 0. endif iused = iused + 1 ymax = max(ymax,abs(yexacti)) endif enddo err1 = abs(residual(i)) errL1 = errL1 + err1 errL2 = errL2 + err1**2 errLinf = max(errLinf,err1) if (yexacti.gt.tiny(yexacti)) residual(i) = residual(i)/abs(yexacti) nused = nused + 1 endif endif enddo ! !--normalise errors (use maximum y value) ! if (nused > 0) then if (iNormaliseErrors==1 .and. ymax > tiny(ymax)) then errL1 = errL1/(nused*ymax) errL2 = sqrt(errL2/(nused*ymax**2)) errLinf = errLinf/ymax else errL1 = errL1/nused errL2 = sqrt(errL2/(nused)) endif else print "(a)",' error normalising errors' errL1 = 0. errL2 = 0. errLinf = 0. endif if (nerr.gt.0) print*,'WARNING: ',nerr,' errors in residual calculation' return end subroutine calculate_errors !------------------------------------ ! Plot residual errors as inset plot !------------------------------------ subroutine plot_residuals(xpts,residuals,imarker,iaxisy) use plotlib, only:plot_qvp,plot_qwin,plot_svp,plot_qci,plot_qfs, & plot_qcs,plot_sci,plot_sfs,plot_svp,plot_box, & plot_pt,plot_swin,plot_rect real, intent(in) :: xpts(:),residuals(:) integer, intent(in) :: imarker,iaxisy real :: vptxminold,vptxmaxold,vptyminold,vptymaxold real :: vptxmin,vptxmax,vptymin,vptymax real :: xminold,xmaxold,yminold,ymaxold,ymin,ymax real :: xch,ych integer :: ioldcolour,ioldfill !--query old viewport and window size call plot_qvp(0,vptxminold,vptxmaxold,vptyminold,vptymaxold) call plot_qwin(xminold,xmaxold,yminold,ymaxold) !--use specified bottom % of viewport vptxmin = vptxminold vptxmax = vptxmaxold vptymin = vptyminold vptymax = vptyminold + FracinsetResiduals*(vptymaxold - vptyminold) call plot_svp(vptxmin,vptxmax,vptymin,vptymax) !--set window if (residualmax.lt.tiny(residualmax)) then ymax = maxval(abs(residuals)) print*,'max residual = ',ymax else ymax = residualmax endif ymin = -ymax !--erase space for residual plot call plot_qci(ioldcolour) call plot_qfs(ioldfill) call plot_qcs(0,xch,ych) call plot_sci(0) call plot_sfs(1) if (iaxisy.lt.0) then call plot_svp(vptxmin,vptxmax,vptymin,vptymax) else call plot_svp(vptxmin - 3.*xch,vptxmax,vptymin,vptymax) endif call plot_swin(xminold,xmaxold,ymin,ymax) call plot_rect(xminold,xmaxold,ymin,ymax) !--restore fill style call plot_sfs(ioldfill) call plot_sci(1) !--set window and draw axes call plot_svp(vptxmin,vptxmax,vptymin,vptymax) call plot_swin(xminold,xmaxold,ymin,ymax) if (iaxisy.lt.0) then call plot_box('ABCST',0.0,0,'BCST',0.0,0) else call plot_box('ABCST',0.0,0,'BVNCST',0.0,0) endif !--plot residuals call plot_sci(ioldcolour) call plot_pt(size(xpts),xpts,residuals,imarker) !--restore old viewport, window and colour index call plot_svp(vptxminold,vptxmaxold,vptyminold,vptymaxold) call plot_swin(xminold,xmaxold,yminold,ymaxold) end subroutine plot_residuals end module exact splash/src/read_data_h5part.f90000644 000766 000000 00000056023 13261626263 017270 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR DATA FORMATS WRITTEN WITH THE H5PART LIBRARY ! ! SOME CHOICES FOR THIS FORMAT CAN BE SET USING THE FOLLOWING ! ENVIRONMENT VARIABLES: ! ! H5SPLASH_NDIM=2 : number of spatial dimensions (overrides value inferred from data) ! H5SPLASH_HFAC=1.2 : factor to use in h= hfac*(m/rho)**(1/ndim) if h not present in data ! H5SPLASH_HSML=1.0 : value for global smoothing length if h not present in data ! H5SPLASH_TYPEID='MatID' : name of dataset containing the particle types ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- !--local module to store header information so we can later set the labels module h5partdataread use params use labels, only:lenlabel implicit none character(len=lenlabel), dimension(maxplot) :: datasetnames logical :: warn_labels = .true. end module h5partdataread subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:dat,iamtype,npartoftype,time,gamma,maxpart,maxcol,maxstep use params use settings_data, only:ndim,ndimV,ncolumns,ncalc,debugmode,ntypes,iverbose use mem_allocation, only:alloc use iso_c_binding, only:c_double,c_int64_t use asciiutils, only:lcase use system_utils, only:renvironment use system_commands, only:get_environment use labels, only:ih,ipmass,irho,ix use h5part use h5partattrib, only:h5pt_readstepattrib,h5pt_getnstepattribs,h5pt_getstepattribinfo, & h5pt_readstepattrib_r8, h5pt_readstepattrib_string use h5partdataread implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: i,j,ncolstep,nsteps,ncolsfile,icol,itypeidcol integer(kind=c_int64_t) :: nattrib,iattrib,nelem,idatasettype,k,icolsfile,ierr integer :: nprint,npart_max,nstep_max,maxcolsfile,itype integer, dimension(maxplot) :: iorder integer, dimension(maxparttypes) :: itypemap integer, dimension(:),allocatable :: itypefile logical :: iexist,typeiddefault integer(kind=c_int64_t) :: ifile,istep character(len=len(rootname)+5) :: dumpfile character(len=64) :: attribname character(len=lenlabel) :: datasetname,type_datasetname real(kind=doub_prec),dimension(1) :: dtime real :: hsmooth,hfac,dndim character(len=64) :: xstring nstepsread = 0 nstep_max = 0 npart_max = maxpart dumpfile = trim(rootname) if (iverbose.ge.1) print "(1x,a)",'reading h5part format' print "(26('>'),1x,a,1x,26('<'))",trim(dumpfile) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions (0 means no particle coords) ! ndim = 0 ndimV = 0 j = indexstart nstepsread = 0 ! !--open the file and read the number of particles ! if (debugmode) print*,'DEBUG: opening '//trim(dumpfile) !ierr = h5pt_set_verbosity_level(6_8) ifile = h5pt_openr(trim(dumpfile)) if (ifile.le.0) then print "(a)",'*** ERROR opening '//trim(dumpfile)//' ***' return endif if (debugmode) print*,'DEBUG: file opened ok' ! !--get number of steps and particles in the file ! nsteps = int(h5pt_getnsteps(ifile)) if (debugmode) print*,'DEBUG: nsteps = ',nsteps ! !--read environment variable giving the name of the dataset ! containing the particle type ID ! give default value if this is not set ! call get_environment('H5SPLASH_TYPEID',type_datasetname) if (len_trim(type_datasetname).le.0) then typeiddefault = .true. type_datasetname = 'MatID' else typeiddefault = .false. endif ! !--read "header" information from all steps in file: ! get maximum number of particles for all steps in file ! and maximum number of columns (datasets) in file ! npart_max = 0 ncolstep = 0 maxcolsfile = 0 itypeidcol = 0 do istep=1,nsteps if (debugmode) print "(a,i2)",'DEBUG: setting step ',istep ierr = h5pt_setstep(ifile,istep) if (ierr.eq.0) then npart_max = max(npart_max,int(h5pt_getnpoints(ifile))) ncolsfile = int(h5pt_getndatasets(ifile)) icol = 0 if ((istep.eq.nsteps .and. ncolsfile.gt.0)) then do icolsfile=0,ncolsfile-1 !ierr = h5pt_getdatasetname(ifile,icol,datasetnames(icol+1)) if (debugmode) print*,'DEBUG: getting datasetinfo' ierr = h5pt_getdatasetinfo(ifile,icolsfile,datasetname,idatasettype,nelem) if (ierr.ne.0) then print "(a,i3)",' ERROR reading dataset name for column ',icolsfile+1 else ! !--read only columns which contain real or double precision data ! select case(idatasettype) case(H5PART_FLOAT32,H5PART_FLOAT64) icol = icol + 1 datasetnames(icol) = trim(adjustl(datasetname)) !print*,' data set info = ',icol,trim(datasetnames(icol)),idatasettype,nelem case(H5PART_INT32,H5PART_INT64) ! !--try to recognise the dataset giving the particle types ! if (trim(type_datasetname)==trim(datasetname) .or. trim(datasetname)=='Phase') then type_datasetname = trim(datasetname) if (itypeidcol.le.0) itypeidcol = int(icolsfile) + 1 if (iverbose.ge.1) print "(a)",' getting particle types from data set '//trim(type_datasetname) else if (iverbose.ge.1) print "(a)",' skipping data set '//trim(datasetname)// & ' of type '//h5part_type(int(idatasettype)) endif case default if (iverbose.ge.1) print "(a)",' skipping data set '//trim(datasetname)//& ' of type '//h5part_type(int(idatasettype)) end select endif enddo elseif (ncolsfile.le.0) then print*,'ERROR: number of datasets in step ',istep,' = ',ncolsfile endif ncolstep = max(ncolstep,icol) maxcolsfile = max(ncolsfile,maxcolsfile) else print "(a,i3)",' ERROR, could not choose step ',istep return endif enddo nprint = npart_max ! !--warn if no particle type data has been read ! if (itypeidcol.le.0) then if (typeiddefault) then if (iverbose.ge.1) print "(a)",' Particle type dataset not found in file: Use H5SPLASH_TYPEID to give dataset name' else print "(a)",' WARNING: Particle type dataset '//trim(type_datasetname)//' (from H5SPLASH_TYPEID) not found in file ' endif endif ! !--call the set_labels routine to get the initial location of coords, smoothing length etc. given dataset labels ! warn_labels = .false. call set_labels() warn_labels = .true. ! !--set default ordering of columns ! do i=1,size(iorder) iorder(i) = i enddo ! !--if coordinates are not in the first 3 columns, shift data so that they are ! if (ndim.gt.0 .and. ix(1).ne.1) then do i=1,ndim iorder(ix(i)) = i enddo !--preserve the order of things after the coordinates icol = ndim do i=ix(1)+1,ncolstep if (.not.any(ix(1:ndim).eq.i)) then icol = icol + 1 iorder(i) = icol endif enddo !--shuffle things before the coordinates to the end do i=1,ix(1)-1 if (.not.any(ix(1:ndim).eq.i)) then icol = icol + 1 iorder(i) = icol endif enddo endif if (debugmode) print*,'DEBUG: iorder = ',iorder ! !--if smoothing length has not been set, look for an environment variable ! giving the smoothing length value ! hsmooth = -1. if (ih.eq.0) then hsmooth = renvironment('H5SPLASH_HSML',errval=-1.) if (hsmooth.ge.0.) then ncolstep = ncolstep + 1 elseif (ipmass.gt.0. .and. irho.gt.0 .and. ndim.gt.0) then hfac = renvironment('H5SPLASH_HFAC',errval=-1.) if (hfac.gt.0.) then if (iverbose.ge.1) print "(/,a,f6.2,a,/)",' Setting smoothing length using h = ',hfac,& '*(m/rho)**(1/ndim) (from H5SPLASH_HFAC setting)' else hfac = 1.2 if (iverbose.ge.1) then print "(/,a)",' WARNING: Smoothing length not found in data: using h = hfac*(m/rho)**(1/ndim)' print "(a)", ' (hfac = 1.2 by default, set H5SPLASH_HFAC to change this)' print "(a,/)",' (set constant h with H5SPLASH_HSML to give a global value)' endif endif ncolstep = ncolstep + 1 else if (iverbose.ge.1) print "(/,a,/)",' WARNING: Smoothing length not found in data: Set H5SPLASH_HSML to give a global value' endif endif ncolumns = ncolstep ! !--allocate memory for all data in the file ! nstep_max = max(nsteps,indexstart,1,maxstep) npart_max = max(maxpart,npart_max) if (.not.allocated(dat) .or. (nprint.gt.maxpart) .or. (ncolstep+ncalc).gt.maxcol) then if (itypeidcol.gt.0) then call alloc(npart_max,nstep_max,ncolstep+ncalc,mixedtypes=.true.) else call alloc(npart_max,nstep_max,ncolstep+ncalc) endif endif ! !--now read the timestep data in the dumpfile (for all steps) ! istep = 0 do j=indexstart,indexstart+nsteps-1 istep = istep + 1 print "(a,i4,a,i10)",' step ',istep,': ntotal = ',nprint ierr = h5pt_setstep(ifile,istep) nprint = int(h5pt_getnpoints(ifile)) ! use int() to avoid compiler warning about type conversion ! !--get the time from the step attributes ! nattrib = h5pt_getnstepattribs(ifile) if (nattrib.gt.0) then do iattrib=0,nattrib-1 ! yes, it's written in C ierr = h5pt_getstepattribinfo(ifile,iattrib,attribname,nelem) !print*,' step attribute '//trim(attribname),' nelem = ',nelem if (ierr.eq.0) then ! !--match anything that looks vaguely like the time ! if (nelem.eq.1 .and. (index(lcase(attribname),'time').ne.0 & .or. index(lcase(attribname),'t ').ne.0)) then ierr = h5pt_readstepattrib_string(ifile,attribname,xstring) if (ierr.eq.0) then read(xstring,'(f10.0)') time(j) print "(12x,a,es10.3,a)",'time = ',time(j),' (from '//trim(attribname)//')' else print "(a,i2,a)",' ERROR could not read time from step ',istep,' (from '//trim(attribname)//')' endif ! !--match gamma if possible ! elseif (nelem.eq.1 .and. (index(lcase(attribname),'gamma').ne.0 & .or. index(lcase(attribname),'gam ').ne.0)) then ierr = h5pt_readstepattrib_string(ifile,attribname,xstring) if (ierr.eq.0) then gamma(j) = real(dtime(1)) print "(12x,a,es10.3,a)",'gamma = ',gamma(j),' (from '//trim(attribname)//')' else print "(a,i2,a)",' ERROR could not read gamma from step ',istep,' (from '//trim(attribname)//')' endif else print "(a)",' unknown attribute '//trim(attribname) endif else print "(a,i3,a,i2)",' ERROR reading attribute info for step ',istep,', attribute #',iattrib endif enddo endif ! !--now read the data for this step ! icol = 0 do k=0,maxcolsfile-1 ierr = h5pt_getdatasetinfo(ifile,k,datasetname,idatasettype,nelem) select case(idatasettype) case(H5PART_FLOAT32,H5PART_FLOAT64) icol = icol + 1 datasetnames(iorder(icol)) = trim(datasetname) if (debugmode) print "(a,i3,a,i3)",'DEBUG: reading data set ',icol,& ': '//trim(datasetnames(iorder(icol)))//' into column ',iorder(icol) ierr = h5pt_readdata(ifile,datasetnames(iorder(icol)),dat(:,iorder(icol),j)) if (ierr.ne.0) print "(a)",' ERROR reading dataset '//trim(datasetnames(iorder(icol))) case default if (debugmode) print "(a)",' skipping data set '//trim(datasetname)//' of type '//lcase(h5part_type(int(idatasettype))) end select enddo ! !--read the particle types for this step from the typeid dataset (specified from the type_datasetname setting) ! npartoftype(:,j) = 0 if (itypeidcol.gt.0 .and. size(iamtype(:,1)).gt.1) then if (debugmode) print "(a)",'DEBUG: reading particle types from '//trim(type_datasetname) ! !--allocate temporary memory ! if (allocated(itypefile)) deallocate(itypefile) allocate(itypefile(nprint),stat=ierr) if (ierr.ne.0) stop 'ERROR allocating temporary memory for particle types' ! !--read type array from file ! ierr = h5pt_readdata(ifile,trim(type_datasetname),itypefile(:)) if (ierr.ne.0) then print "(a)",' ERROR reading dataset '//trim(type_datasetname) else ! !--work out the number of unique particle types ! and map these into SPLASH particle types (1->maxtypes) ! if (j.eq.1 .and. istep.eq.1) then ntypes = 1 itypemap(1) = minval(itypefile) endif do i=1,nprint !--increase the number of particle types if a particle of new type is found if (.not.any(itypemap(1:ntypes).eq.itypefile(i))) then ntypes = ntypes + 1 if (ntypes.le.size(itypemap)) then itypemap(ntypes) = itypefile(i) npartoftype(ntypes,j) = npartoftype(ntypes,j) + 1 iamtype(i,j) = ntypes endif else do itype=1,ntypes if (itypefile(i).eq.itypemap(itype)) then npartoftype(itype,j) = npartoftype(itype,j) + 1 iamtype(i,j) = itype endif enddo endif enddo if (nprint.lt.1e6) then print "(12x,a,10(i5,1x))",'npart (by type) = ',npartoftype(1:ntypes,j) else print "(12x,a,10(i10,1x))",'npart (by type) = ',npartoftype(1:ntypes,j) endif ! !--warn if the number of types exceeds the current limit ! if (ntypes.gt.maxparttypes) & print "(/,2(a,i2),a/)", & ' WARNING: too many particle types in dataset '//trim(type_datasetname)// & ' (got ',ntypes,': maximum is currently ',maxparttypes,')' endif ! !--clean up ! if (allocated(itypefile)) deallocate(itypefile) else !--only one particle type ntypes = 1 npartoftype(1,j) = nprint endif ! !--reset the labels now that the columns have been read in the correct order ! if (j.eq.indexstart) then ! set labels based on the first step read from the file warn_labels = .false. call set_labels() warn_labels = .true. endif ! !--if smoothing length set via environment variable, fill the extra column with the smoothing length value ! if (ih.eq.0) then if (hsmooth.ge.0.) then datasetnames(ncolstep) = 'h' ih = ncolstep dat(:,ih,j) = hsmooth elseif (ipmass.gt.0 .and. irho.gt.0 .and. ndim.gt.0) then ih = ncolstep datasetnames(ncolstep) = 'h' dndim = 1./ndim where (dat(:,irho,j).gt.tiny(0.)) dat(:,ih,j) = hfac*(dat(:,ipmass,j)/dat(:,irho,j))**dndim elsewhere dat(:,ih,j) = 0. end where endif endif ! read(iunit,*,iostat=ierr) (dat(i,icol,j),icol = 1,ncolstep) nstepsread = nstepsread + 1 enddo ierr = h5pt_close(ifile) return end subroutine read_data !!------------------------------------------------------------------- !! set labels for each column of data !! !! read these from a file called 'columns' in the current directory !! then take sensible guesses as to which quantities are which !! from the column labels !! !!------------------------------------------------------------------- subroutine set_labels() use asciiutils, only:lcase use labels, only:label,ix,irho,ipmass,ih,iutherm, & ipr,ivx,iBfirst,iamvec,labelvec,lenlabel !,labeltype !use params, only:maxparttypes use settings_data, only:ndim,ndimV,UseTypeInRenderings,iverbose use geometry, only:labelcoord use system_utils, only:ienvironment use h5partdataread implicit none integer :: i,ndimset,ndim_max character(len=lenlabel) :: labeli ndim = 0 ndimV = 0 ndimset = ienvironment('H5SPLASH_NDIM',errval=-1) ndim_max = 3 if (ndimset.ge.0) ndim_max = ndimset irho = 0 ih = 0 ipmass = 0 do i=1,size(datasetnames) if (len_trim(datasetnames(i)).gt.0) then label(i) = trim(datasetnames(i)) else label(i) = ' ' endif !--now try to recognise the column based on the dataset name ! compare all strings in lower case, trimmed and with no preceding spaces ! labeli = trim(adjustl(lcase(label(i)))) if (index(labeli,'coords').ne.0 .and. index(labeli,'_').ne.0 .or. labeli(1:1).eq.'x') then if (ndim.lt.ndim_max) then ndim = ndim + 1 ix(ndim) = i label(ix(ndim)) = labelcoord(ndim,1) endif elseif (index(labeli,'vel_').ne.0 .and. (ivx.eq.0 .or. i.le.ivx+ndim)) then if (ndimV.lt.3) ndimV = ndimV + 1 if (index(labeli,'_0').ne.0) ivx = i elseif (index(labeli,'dens').ne.0 .and. irho.eq.0) then irho = i elseif (index(labeli,'mass').ne.0 .and. ipmass.eq.0) then ipmass = i elseif (ih.eq.0 .and. (index(labeli,'smoothing').ne.0 .or. labeli(1:1).eq.'h')) then ih = i elseif (labeli(1:1).eq.'u') then iutherm = i !--identify vector quantities based on _0, _1, _2 labelling elseif (index(labeli,'_0').ne.0) then !print*,'labelling ',labeli(1:index(labeli,'_0')-1),' as vector, column ',i iamvec(i) = i labelvec(i) = labeli(1:index(labeli,'_0')-1) elseif (index(labeli,'_1').ne.0 .and. i.gt.1 .and. ndim.ge.2) then if (iamvec(i-1).gt.0) then iamvec(i) = i-1 labelvec(i) = labelvec(i-1) endif elseif (index(labeli,'_2').ne.0 .and. i.gt.2 .and. ndim.ge.3) then if (iamvec(i-2).gt.0) then iamvec(i) = i-2 labelvec(i) = labelvec(i-2) endif endif enddo if (ndim.lt.1) ndimV = 0 if (ndimV.gt.ndim) ndimV = ndim if (warn_labels .and. iverbose.ge.1) then if (ndimset.gt.0) then if (ndim.ne.ndimset) then print "(2(a,i1))",' WARNING: ndim = ',ndimset, & ' from H5SPLASH_NDIM setting but coords not found in data: using ndim = ',ndim else print "(a,i1,a)",' Assuming number of dimensions = ',ndim,' from H5SPLASH_NDIM setting' endif else if (ndim.gt.0) print "(a,i1,a)",' Assuming number of dimensions = ',ndim,' (set H5SPLASH_NDIM to override)' endif if (ndimV.gt.0) print "(a,i1)",' Assuming vectors have dimension = ',ndimV if (irho.gt.0) print "(a,i2)",' Assuming density in column ',irho if (ipmass.gt.0) print "(a,i2)",' Assuming particle mass in column ',ipmass if (ih.gt.0) print "(a,i2)",' Assuming smoothing length in column ',ih if (iutherm.gt.0) print "(a,i2)",' Assuming thermal energy in column ',iutherm if (ipr.gt.0) print "(a,i2)",' Assuming pressure in column ',ipr if (ivx.gt.0) then if (ndimV.gt.1) then print "(a,i2,a,i2)",' Assuming velocity in columns ',ivx,' to ',ivx+ndimV-1 else print "(a,i2)",' Assuming velocity in column ',ivx endif endif if (ndim.eq.0 .or. irho.eq.0 .or. ipmass.eq.0 .or. ih.eq.0) then print "(4(/,a))",' NOTE: Rendering capabilities cannot be enabled', & ' until positions of density, smoothing length and particle', & ' mass are known (for the h5part read this means labelling ', & ' the dataset appropriately)' endif ! !--assign vectors (don't do this on the first call otherwise it will remain assigned to the wrong columns) ! if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' endif if (iBfirst.gt.0) then iamvec(iBfirst:iBfirst+ndimV-1) = ivx labelvec(iBfirst:iBfirst+ndimV-1) = 'B' endif ! !--set labels for vector quantities ! do i=1,size(datasetnames) if (iamvec(i).ne.0) then label(i) = trim(labelvec(iamvec(i)))//'_'//trim(labelcoord(i-iamvec(i)+1,1)) endif enddo endif ! !--set labels for each particle type ! (for h5part this is done in the read_data routine) ! !ntypes = 1 !!maxparttypes ! labeltype(1) = 'gas' ! labeltype(2) = 'gas' ! labeltype(3) = 'gas' ! labeltype(4) = 'gas' UseTypeInRenderings(:) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/timing.f90000644 000766 000000 00000010475 13261626263 015371 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------- ! ! This module contains utilities for code timings ! !---------------------------------------------------------------- module timing implicit none integer, private :: istarttime(6) real, private :: starttime data starttime/-1./ public :: wall_time,print_time private contains !-------------------------------------------------------------------- !+ ! sets initial time !+ !-------------------------------------------------------------------- subroutine initialise_timing implicit none integer :: iday,imonth,iyear,ihour,imin,isec,imsec,ivalues(8) character(len=8) :: date character(len=5) :: zone character(len=10) :: time call date_and_time(date,time,zone,ivalues) iyear = ivalues(1) imonth = ivalues(2) iday = ivalues(3) ihour = ivalues(5) imin = ivalues(6) isec = ivalues(7) imsec = ivalues(8) istarttime(1) = iyear istarttime(2) = imonth istarttime(3) = iday istarttime(4) = ihour istarttime(5) = imin istarttime(6) = isec !istarttime(7) = imsec starttime = iday*86400. + ihour*3600. + imin*60. + isec + imsec*0.001 return end subroutine initialise_timing !-------------------------------------------------------------------- !+ ! Get time used since begining !+ !-------------------------------------------------------------------- subroutine wall_time(t) implicit none real, intent(out) :: t integer :: i,iday,imonth,ihour,imin,isec,imsec,ivalues(8) character(len=8) :: date character(len=5) :: zone character(len=10) :: time !--do self-initialisation the first time it is called if (starttime.lt.0.) call initialise_timing call date_and_time(date,time,zone,ivalues) iday = ivalues(3) ihour = ivalues(5) imin = ivalues(6) isec = ivalues(7) imsec = ivalues(8) if (ivalues(2).lt.istarttime(2)) then ivalues(2) = ivalues(2) + 12 endif do i = istarttime(2), ivalues(2) - 1 imonth = mod(i,12) if (imonth.eq.4 .or. imonth.eq.6 .or. imonth.eq.9 .or. imonth.eq.11) then iday = iday + 30 elseif (imonth.eq.2) then iday = iday + 28 else iday = iday + 31 endif end do t = iday*86400. + ihour*3600. + imin*60. + isec + imsec*0.001 - starttime return end subroutine wall_time !-------------------------------------------------------------------- ! ! print a time, nicely formatted into hours, mins, seconds ! !-------------------------------------------------------------------- subroutine print_time(time,string,iunit) implicit none real, intent(in) :: time character(len=*), intent(in), optional :: string integer, intent(in), optional :: iunit character(len=64) :: newstring integer :: nhr,nmin,lunit real :: trem trem = time nhr = int(trem/3600.) if (nhr.gt.0) trem = trem - nhr*3600. nmin = int(trem/60.) if (nmin.gt.0) trem = trem - nmin*60. if (present(string)) then newstring = trim(string(1:min(len(newstring),len_trim(string)))) else newstring = 'completed in' endif if (present(iunit)) then lunit = iunit else lunit = 6 endif if (nhr.gt.0) then write(lunit,"(1x,a,1x,i3,a,i2,a,f6.2,a)") & trim(newstring),nhr,' hr, ',nmin,' min, ',trem,' s' elseif (nmin.gt.0) then write(lunit,"(1x,a,1x,i2,a,f6.2,a)") & trim(newstring),nmin,' min, ',trem,' s' else write(lunit,"(1x,a,1x,f6.2,a)") trim(newstring),trem,' s' endif return end subroutine print_time end module timing splash/src/read_data_silo_utils.c000644 000766 000000 00000015362 13261626263 020100 0ustar00dpricewheel000000 000000 /* * This subroutine performs the calls to the SILO library for the * SILO data read * * We have to do it this way as the SILO read interface for Fortran * is incomplete * */ #include #include #include #include static int debug = 0; void set_blocklabel(int *icol, char *name); void read_silo_data_fromc(int *icol, int *npartoftypei, double temparr[*npartoftypei],int *itype); void read_silo_header(const char *filename, int *npart, int *ncol, int *ndim, int *ndimV, double *time, int *ierr) { *npart = 0; *ierr = 0; *ncol = 0; *time = 0.; *npart = 0; DBfile *silofile = DBOpen(filename, DB_UNKNOWN, DB_READ); if (!silofile) { *ierr = 1; return; } if (!DBVersionGEFileVersion(silofile)) { const char *siloversion = DBFileVersion(silofile); printf(" WARNING! File was created with newer version (v%s) of silo library\n",siloversion); } if (!DBInqFileHasObjects(silofile)) { printf(" ERROR: silo file %s does not appear to contain any objects\n",filename); *ierr = 2; return; } DBtoc *toc = DBGetToc(silofile); if (debug) { printf(" DEBUG: File contains:\n %i curves\n %i multimesh\n %i nmultimeshadj\n %i multivar\n", \ toc->ncurve,toc->nmultimesh,toc->nmultimeshadj,toc->nmultivar); printf(" %i multimat\n %i multimatspecies\n %i csgmesh\n %i csgvar\n", \ toc->nmultimat,toc->nmultimatspecies,toc->ncsgmesh,toc->ncsgvar); printf(" %i defvars\n %i qmesh\n %i qvar\n %i ucdmesh\n %i ucdvar\n", \ toc->ndefvars,toc->nqmesh,toc->nqvar,toc->nucdmesh,toc->nucdvar); printf(" %i ptmesh\n %i ptvar\n %i mat\n %i matspecies\n %i var\n %i obj\n", \ toc->nptmesh,toc->nptvar,toc->nmat,toc->nmatspecies,toc->nvar,toc->nobj); printf(" %i dir\n %i array\n %i mrgtree\n %i groupelmap\n %i mrgvar\n", \ toc->ndir,toc->narray,toc->nmrgtree,toc->ngroupelmap,toc->nmrgvar); } int nptmesh = toc->nptmesh; if (nptmesh <= 0) { printf(" ERROR: silo file %s does not appear to contain any point meshes\n",filename); *ierr = 3; return; } int i; for (i=0;i 0) { printf(" WARNING: IGNORNING ptmesh #%i (%s)\n",i+1,toc->ptmesh_names[i]); } } /* open the first point mesh and get info */ DBpointmesh *my_ptmesh = DBGetPointmesh(silofile,toc->ptmesh_names[0]); if (!my_ptmesh) { printf(" ERROR reading point mesh %s\n",toc->ptmesh_names[0]); *ierr = 4; return; } else { printf(" Reading point mesh %s\n",toc->ptmesh_names[0]); *time = my_ptmesh->dtime; *npart = my_ptmesh->nels; *ndim = my_ptmesh->ndims; *ndimV = *ndim; if (debug) { printf(" Got labels = %s %s %s \n",\ my_ptmesh->labels[0],my_ptmesh->labels[1],my_ptmesh->labels[2]); printf(" Got title = %s \n",my_ptmesh->title); printf(" Got units = %s %s %s \n",\ my_ptmesh->units[0],my_ptmesh->units[1],my_ptmesh->units[2]); printf(" max_extents = %f %f %f\n",\ my_ptmesh->max_extents[0],my_ptmesh->max_extents[1],my_ptmesh->max_extents[2]); } DBFreePointmesh(my_ptmesh); } /* Read the other point variables */ int nptvar = toc->nptvar; if (nptvar <= 0) { printf(" WARNING: silo file %s does not appear to contain any point variables\n",filename); } *ncol = *ndim + nptvar; if (*ncol <= 0) { *ierr = 4; printf(" ERROR: ncol <= 0 from silo header\n"); return; } DBClose(silofile); } void read_silo_data(char *filename, int maxtypes, int npartoftype[maxtypes], int ncol, int isrequired[ncol], int *ierr) { DBfile *silofile = DBOpen(filename, DB_UNKNOWN, DB_READ); if (!silofile) { *ierr = 1; return; } DBtoc *toc = DBGetToc(silofile); DBpointmesh *my_ptmesh = DBGetPointmesh(silofile,toc->ptmesh_names[0]); if (!my_ptmesh) { printf(" ERROR reading point mesh %s\n",toc->ptmesh_names[0]); DBClose(silofile); *ierr = 4; return; } else { if (debug) printf(" DEBUG: Reading data from point mesh %s\n",toc->ptmesh_names[0]); int ndim = my_ptmesh->ndims; /*int nels = my_ptmesh->nels;*/ int i; int np = npartoftype[0]; int idim; int particle_type = 1; /* read in float */ float *x = 0; x = malloc(np*sizeof(float)); /* must send double to splash */ double *tmp_dbl = 0; tmp_dbl = malloc(np*sizeof(double)); /* read each coordinate in turn */ int icol = 0; for (idim=0;idimcoords[idim]; for (i=0;inptvar; for (i=0;iptvar_names[i]); DBmeshvar *my_ptvar = DBGetPointvar(silofile,toc->ptvar_names[i]); if (debug) { printf(" Associated with mesh %s\n",my_ptvar->meshname); printf(" label = %s\n",my_ptvar->label); printf(" Nels,nvals,nspace,ndims = %i %i %i %i\n",\ my_ptvar->nels,my_ptvar->nvals,my_ptvar->nspace,my_ptvar->ndims); } icol = icol + 1; x = my_ptvar->vals[0]; for (i=0;iname); /*DBFreeMeshvar(my_ptvar);*/ } free(x); free(tmp_dbl); DBFreePointmesh(my_ptmesh); } DBClose(silofile); } splash/src/exact_sedov.f90000644 000766 000000 00000016576 13261626263 016416 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !--------------------------------------------------------------------------- ! compute exact solution for Sedov-type point-like energy injection ! the solution is correct for 3D, I have attempted to fix it in 1D & 2D but ! not working yet. !--------------------------------------------------------------------------- module sedov implicit none public :: exact_sedov private :: etau,rhou,pru,dudlneta,eta0 contains subroutine exact_sedov(iplot,time,gam,rhozero,energy,rmax,rplot,yplot,ierr) implicit none integer, intent(in) :: iplot integer, intent(out) :: ierr real, intent(in) :: time, gam, rhozero, energy, rmax real, dimension(:), intent(inout) :: rplot real, dimension(size(rplot)), intent(out) :: yplot integer, parameter :: ndim = 3 integer :: npts real, parameter :: pi = 3.1415926536 real :: rshock, dr real :: rhoshock, ushock, prshock real :: eta_0, power real :: ubar,ubarzero,dubar real :: phi,dphi integer :: i,ishock ierr = 0 print*,' Plotting 3D Sedov similarity solution at t = ',time print*,' rhozero = ',rhozero,' energy = ',energy, ' rmax = ',rmax if (abs(time).lt.1.e-10) then print*,'nothing at t=0, returning' ierr = 1 return endif npts = size(rplot) eta_0 = eta0(gam,ndim) print*,' eta0 = ',eta_0, ' gamma = ',gam power = 1./(ndim+2) dr = rmax/float(npts-1) ! !--calculate radius and velocity of shock from dimensional analysis ! rshock = eta_0*(energy*time**2./rhozero)**power !! ushock = 2.*power*eta_0*((energy*time**2./rhozero)**(power-1.))*energy*time/rhozero ushock = 2.*power*rshock/time print*,' rshock = ',rshock, ' ushock = ',ushock ! !--on x-y plots plot a circle at radius rshock ! if (iplot.eq.0) then dphi = 2.*pi/real(npts-1) phi = 0. do i=1,npts phi = (i-1)*dphi rplot(i) = rshock*cos(phi) yplot(i) = rshock*sin(phi) enddo return endif ! !--jump conditions to find states behind shock ! rhoshock = rhozero*(gam+1.)/(gam-1.) prshock = 2./(gam+1.)*rhoshock*ushock**2 rplot(1) = 0.0 select case(iplot) case(2) yplot(1) = 0.0 ! shouldn't be zero for pressure case default yplot(1) = 0.0 end select ishock = INT((rshock - rplot(1))/dr) if (ishock.gt.0) then ishock = min(ishock,npts) ! !--the solution for rho is given as a function of ubar (dimensionless velocity) ! ubar varies from (gamma+1)/2*gamma at eta = 0 to 1 at eta = 1 ! (eta is the dimensionless radius eta = r/rshock, so eta=1 is the shock position) ! ubarzero = 0.5*(gam+1.)/gam dubar = (1.0 - ubarzero)/REAL(ishock-1) ! need to check the denominator ! !--solution behind shock front (similarity solution) ! (I really want to start from ubar = ubarzero, but am having problems) ! do i=2,ishock ubar = ubarzero + (i-1)*dubar ! again need to check this rplot(i) = etau(ubar,gam,ndim)*rshock select case(iplot) case(1) ! rho yplot(i) = rhoshock*rhou(ubar,gam) case(2) ! pr yplot(i) = prshock*(rplot(i)/rshock)**2*pru(ubar,gam) case(3) ! utherm yplot(i) = prshock*(rplot(i)/rshock)**2*pru(ubar,gam)/ & ((gam-1.)*rhoshock*rhou(ubar,gam)) case(4) ! 1/2 v^2 yplot(i) = 0.5*(4./(5.*(gam+1.))*rplot(i)/time*ubar)**2 case(5) yplot(i) = (4./(5.*(gam+1.))*rplot(i)/time*ubar) end select !print*,'u,r, rho = ',ubar,rplot(i),rhoplot(i) enddo endif ! !--solution ahead of shock front ! ishock = max(ishock,1) if (ishock.lt.npts) then do i=ishock,npts rplot(i) = rshock + (i-ishock)*dr select case(iplot) case(1) ! rho yplot(i) = rhozero case default ! pr,utherm,v yplot(i) = 0. end select enddo endif return end subroutine exact_sedov ! !--eta (dimensionless radius) as a function of u_bar (dimensionless velocity) ! real function etau(u,gamma,ndim) implicit none integer, intent(in) :: ndim real, intent(in) :: u,gamma real :: gam1,term1,term2,power1,power2 gam1 = gamma-1. power1 = (-12.+7.*gamma-13.*gamma**2)/(5.*(-1.+gamma+6.*gamma**2)) power2 = gam1/(1.+2.*gamma) term1 = ((5.+5.*gamma+2.*u-6.*gamma*u)/(7.-gamma))**power1 term2 = ((2.*gamma*u-gamma-1.)/gam1)**power2 etau = u**(-2./(ndim+2.))*term1*term2 end function etau ! !--rhobar (dimensionless density) as a function of u_bar (dimensionless velocity) ! real function rhou(u,gamma) implicit none real, intent(in) :: u,gamma real :: gam1,power1,power2,power3,term1,term2,term3 gam1 = gamma-1. power1 = (2./(gamma-2.)) power2 = -(12.-7.*gamma+13.*gamma**2)/(2.-3.*gamma-11.*gamma**2+6.*gamma**3) power3 = 3./(1.+2.*gamma) term1 = ((1.+ gamma - 2.*u)/gam1)**power1 term2 = ((5.+5.*gamma+2.*u-6.*gamma*u)/(7.-gamma))**power2 term3 = ((2.*gamma*u - gamma -1.)/gam1)**power3 rhou = term1*term2*term3 end function rhou ! !--prbar (dimensionless pressure) as a function of u_bar (dimensionless velocity) ! real function pru(u,gamma) implicit none real, intent(in) :: u,gamma pru = (gamma+1. - 2.*u)/(2.*gamma*u - gamma - 1.)*u**2*rhou(u,gamma) end function pru ! !--du /dln eta - required for the integral to compute eta0 ! real function dudlneta(u,gamma) implicit none real, intent(in) :: u,gamma real :: term1,term2 term1 = u*(5.+5.*gamma + 2.*u - 6.*gamma*u)*(-1.-gamma+2.*gamma*u) term2 = 2.*(1.+gamma)*(1.+gamma - 2.*u - 2.*gamma*u + 2.*gamma*u**2) dudlneta = term1/term2 end function dudlneta ! !--eta_0 as a function of gamma ! real function eta0(gamma,ndim) implicit none integer, parameter :: ipts = 50000 real, parameter :: pi = 3.1415926536 integer, intent(in) :: ndim real, intent(in) :: gamma integer :: i real :: u0, u, du real :: sum, term, weight, factor ! if (abs(gamma-5./3.).lt.1.e-3) then ! eta0 = 1.1517 ! else ! print*,'warning: don''t know eta0: integral not implemented' ! eta0 = 1.0 ! endif u0 = 0.5*(gamma+1.)/gamma du = (1. - u0)/REAL(ipts) ! !--integrate using Simpson's 1/3 rule ! sum = 0. do i=1,ipts weight = 1.0 if (mod(i,2).eq.0) then weight = 4./3. else weight = 2./3. endif if ((i.eq.1).or.(i.eq.ipts)) weight = 1./3. u = u0 + i*du term = (pru(u,gamma) + rhou(u,gamma)*u**2)*(etau(u,gamma,ndim)**(ndim+2))/dudlneta(u,gamma) sum = sum + weight*du*term enddo if (ndim.eq.3) then factor = 4.*pi elseif (ndim.eq.2) then factor = 2.*pi else factor = 1. endif eta0 = (factor*8.*sum/(25.*(gamma**2 - 1.)))**(-1./REAL(ndim+2)) end function eta0 end module sedov splash/src/exact_Cshock.f90000644 000766 000000 00000013212 13261626263 016470 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! Plot exact solution for a magnetohydrodynamic oblique C-shock ! (ie. one dimensional MHD shock problem with ambipolar diffusion) ! ! Solution from Mac-Low, Norman, Konigl & Wardle (1995), ApJ 442, 726 ! http://adsabs.harvard.edu/abs/1995ApJ...442..726M ! ! ---------------------------------------------------------------------- module Cshock implicit none public :: exact_Cshock contains subroutine exact_Cshock(iplot,time,gamma,machs,macha,xmin,xmax,xpts,ypts,ierr) integer, intent(in) :: iplot integer, intent(out) :: ierr real, intent(in) :: time,gamma,machs,macha,xmin,xmax real, dimension(:), intent(inout) :: xpts real, dimension(size(xpts)), intent(out) :: ypts real, dimension(size(xpts)) :: D real, parameter :: pi = 3.1415926536 real :: theta,xshock,ambi_gamma,ambi_rhoi,vx0,vy0,Bx,By,rhon_pre real :: cs,rhon0,Bfield0,b0,shockl,vs,va,K1,K2,By_post,P_post,rhon_post,By0,Pr0 real :: sintheta,costheta,vx2,vx,vy,rhon,dvx,vxin integer :: npts,i logical printout printout = .false. npts = size(xpts) theta = pi/4. costheta = cos(theta) sintheta = sin(theta) D(npts) = 1. + 1.e-6 ! upstream cs = 0.1 rhon0 = 1. Bfield0 = 1. ! this gives Bx0 = By0 = 1/sqrt(2) as in Choi et al. (2009) ambi_gamma = 1. ambi_rhoi = 1.e-5 b0 = sintheta shockl = Bfield0/(ambi_gamma*ambi_rhoi*sqrt(rhon0)) va = Bfield0/sqrt(rhon0) xshock = 6./8.*va*time if ( printout ) open(unit = 625,file="Cshock_splash.dat") print "(4(a,g8.2))",& ' Plotting exact C-shock at t = ',time,' M = ',machs,' M_A = ',macha,' theta = ',theta print "(4(a,es10.3))",' shock length L = ',shockL,' shock is at x = ',xshock call integrate(xmin,xmax,xshock,xpts,macha,machs,theta,shockl,D,npts) ! ! compute velocity jump across shock: See Mac-Low et al. (1995). This is the difference ! in the velocity across the shock front since we assume that the ! post-shock gas is at rest ! ! !--post-shock, assume vx = 0 By_post = Bfield0*get_b(b0,macha,machs,D(1)) rhon_post = D(1)*rhon0 P_post = rhon_post*cs**2 ! K1 = P_post + 0.5*By_post*By_post ! print*,' K1 is ',K1 !--pre-shock vx0 = -5.0 vxin = -4.45 dvx = vxin-vx0 vy0 = 0. Bx = Bfield0*costheta By0 = Bfield0*get_b(b0,macha,machs,D(npts)) rhon_pre = D(npts)*rhon0 Pr0 = rhon_pre*cs**2 K1 = Pr0 + 0.5*By0*By0 + rhon_pre*vx0**2 K2 = rhon_pre*vx0*vy0 - Bx*By0 vx2 = (K1 - 0.5*By_post**2 - P_post)/rhon_post if (vx2 > 0.) then vx = -sqrt(vx2) print "(1x,a,g10.3)",'vx post-shock = ',vx else vx = 0. print*,'error, post-shock vx is imaginary' endif vs = vx0 - vx !print*,'vs = ',vs ! !--determine which parameter to plot ! do i=1,npts rhon = D(i)*rhon0 By = Bfield0*get_b(b0,macha,machs,D(i)) vx2 = (K1 - 0.5*By**2 - rhon*cs**2)/rhon if (vx2 > epsilon(vx2)) then vx = -sqrt(vx2) vy = (K2 + Bx*By)/(rhon*vx) else vx = 0. vy = 0. endif vx = vx + dvx select case(iplot) case(1) ypts(i) = rhon ! rho (neutrals) case(2) ypts(i) = By case(3) ! vx (neutrals) ypts(i) = vx case(4) ! vy (neutrals) ypts(i) = vy case(5) ! Bx ypts(i) = Bx case default ypts(i) = 0. end select if ( printout ) write(625,*) i,xpts(i),rhon,Bx,By,vx,vy enddo ierr = 0 if ( printout ) close(625) return end subroutine exact_Cshock real function rhs(D,macha,machs,theta,shockl) real, intent(in) :: D,macha,machs,theta,shockl real :: term,sintheta,costheta2,b0,b term = (1./D**2 - 1./machs**2)*shockl sintheta = sin(theta) costheta2 = cos(theta)**2 b0 = sintheta b = get_b(b0,macha,machs,D) rhs = b/macha*(b - D*((b - b0)/macha**2*costheta2 + sintheta))/(b**2 + costheta2)/term end function rhs real function get_b(b0,macha,machs,D) real, intent(in) :: b0,macha,machs,D get_b = sqrt(b0**2 + 2.*macha**2*(D-1.)*(1./D - 1./machs**2)) end function get_b subroutine integrate(xmin,xmax,xshock,xpts,macha,machs,theta,shockl,D,npts) integer, intent(in) :: npts real, intent(in) :: xmin,xmax,xshock,macha,machs,theta,shockl real, dimension(npts), intent(inout) :: xpts real, dimension(npts), intent(inout) :: D real :: Dhalf,dx,xminshock integer :: i ! ! set up grid to have good resolution around the shock ! and just two points extending to xmin and xmax ! xminshock = min(xshock - 100.*shockl,xmin) dx = (xshock - xminshock)/real(npts-1) xpts(npts) = xmax xpts(1) = xmin do i=2,npts-1 xpts(i) = xmin + (i-1)*dx enddo do i=npts-1,1,-1 if (xpts(i) > xshock) then D(i) = D(i+1) else !--mid-point rule Dhalf = D(i+1) - 0.5*dx*rhs(D(i+1),macha,machs,theta,-shockl) D(i) = D(i+1) - dx*rhs(Dhalf,macha,machs,theta,-shockl) endif enddo end subroutine integrate end module Cshock splash/src/options_limits.f90000644 000766 000000 00000027125 13261626263 017156 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing settings and options related to the plot limits ! includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module settings_limits implicit none logical :: iadapt, iadaptcoords, adjustlimitstodevice real :: scalemax real, dimension(3) :: xminoffset_track, xmaxoffset_track contains !--------------------------------------------- ! set default values for these options !--------------------------------------------- subroutine defaults_set_limits use multiplot, only:itrans implicit none iadapt = .true. ! adaptive plot limits iadaptcoords = .false. adjustlimitstodevice = .false. scalemax = 1.0 ! for rescaling adaptive limits itrans(:) = 0 ! no transformations (log10 etc) xminoffset_track = 0.5 ! offset of limits from tracked particle xmaxoffset_track = 0.5 ! return end subroutine defaults_set_limits !---------------------------------------------------------------------- ! submenu with options relating to plot limits !---------------------------------------------------------------------- subroutine submenu_limits(ichoose) use filenames, only:nsteps,nstepsinfile,ifileopen use settings_data, only:ndataplots,numplot,ndim,ivegotdata,iCalcQuantities, & DataIsBuffered,itracktype,itrackoffset,ntypes use calcquantities, only:calc_quantities !use settings_page, only:nstepsperpage use multiplot, only:itrans use prompting, only:prompt,print_logical use limits, only:lim,set_limits,range,rangeset,anyrangeset,print_rangeinfo use labels, only:label,ix,irad,is_coord,labeltype use transforms, only:ntrans,transform_label implicit none integer, intent(in) :: ichoose integer :: iaction,ipick,i,index,ierr integer :: itracktypeprev,itrackoffsetprev ! real :: diff, mid, zoom character(len=120) :: transprompt character(len=12) :: string,string2 character(len=20) :: pstring,pstring2 ! zoom = 1.0 iaction = ichoose if (iadapt) then string = 'ADAPT' else string = 'FIXED' endif if (iadaptcoords) then string2 = 'ADAPT' else string2 = 'FIXED' endif write(pstring,"(i12)") itrackoffset pstring = adjustl(pstring) if (itracktype.gt.0) then write(pstring2,"(i12)") itracktype pstring=trim(adjustl(pstring2))//':'//trim(pstring) else pstring=trim(pstring) endif print "(a)",'------------------ limits options ---------------------' 10 format( & ' 0) exit ',/, & ' 1) use adaptive/fixed limits ( ',a,', ',a,' ) ',/, & ' 2) set limits manually ',/, & ' 3) xy limits/radius relative to particle ( ',a,' )',/, & ' 4) auto-adjust limits to match device aspect ratio ( ',a,' )',/, & ' 5) apply log/other transformations to columns ',/, & ' 6) reset limits for all columns ',/, & ' 7) use subset of data restricted by parameter range ( ',a,')') if (iaction.le.0 .or. iaction.gt.7) then print 10,trim(string),trim(string2),trim(pstring),& print_logical(adjustlimitstodevice),print_logical(anyrangeset()) call prompt('enter option ',iaction,0,7) endif ! !--limits ! select case(iaction) !------------------------------------------------------------------------ case(1) !+ With limits set to adaptive, plot limits are minimum !+ and maximum of quantities at current timestep. !+ However, the co-ordinate limits are not adapted !+ in the case of rendered plots. With fixed limits, the !+ plot limits retain their default values for all timesteps. call prompt('Use adaptive plot limits?',iadapt) call prompt('Use adaptive plot limits on coordinate axes?',iadaptcoords) call prompt('Adjust limits to aspect ratio of device?',adjustlimitstodevice) print "(a)",'adaptive plot limits = '//print_logical(iadapt)// & ' on coords = '//print_logical(iadaptcoords) !if (nstepsperpage.gt.1 .and. (iadapt .or. iadaptcoords)) then ! print*,'WARNING: adaptive limits and multiple steps per page don''t mix' !endif !------------------------------------------------------------------------ case(2) !+ Manually sets the plot limits for each column of data ipick = 1 do while (ipick.gt.0) ipick = 0 !write(*,*) call prompt('Enter column number to set limits (0=quit)',ipick,0,numplot) if (ipick.gt.0) then call prompt(trim(label(ipick))//' min ',lim(ipick,1)) call prompt(trim(label(ipick))//' max ',lim(ipick,2)) print*,'>> '//trim(label(ipick))//' limits set (min,max) = ',lim(ipick,1),lim(ipick,2) if (is_coord(ipick,ndim)) then iadaptcoords = .false. elseif (ipick.le.numplot) then iadapt = .false. endif endif enddo return !------------------------------------------------------------------------ case(3) !+ Co-ordinate limits are centred on the selected !+ particle for all timesteps, with offsets as input by the user. !+ This effectively gives the `Lagrangian' perspective. itrackoffsetprev = itrackoffset itracktypeprev = itracktype print "(a,/,a,/)",'To track particle 4923, enter 4923', & 'To track the 43rd particle of type 3, enter 3:43' call prompt('Enter particle to track: ',pstring,noblank=.true.) call get_itrackpart(pstring,itracktype,itrackoffset,ierr) do while (ierr.ne.0 .or. itracktype.lt.0 .or. itracktype.gt.ntypes .or. itrackoffset.lt.0) if (itracktype.lt.0 .or. itracktype.gt.ntypes) print "(a)",'invalid particle type' if (itrackoffset.lt.0) print "(a)",'invalid particle index' if (ierr.ne.0) print "(a)",'syntax error in string' pstring = '0' call prompt('Enter particle to track: ',pstring,noblank=.true.) call get_itrackpart(pstring,itracktype,itrackoffset,ierr) enddo if (itracktype.gt.0) then write(string,"(i12)") itrackoffset string = adjustl(string) print "(a)",'=> tracking '//trim(labeltype(itracktype))//' particle #'//trim(string) elseif (itrackoffset.gt.0) then write(string,"(i12)") itrackoffset string = adjustl(string) print "(a)",'=> tracking particle '//trim(string) else print "(a)",'=> particle tracking limits OFF' endif if (itrackoffset.gt.0) then do i=1,ndim call prompt('Enter offset for '//trim(label(ix(i)))//'min:', & xminoffset_track(i)) call prompt('Enter offset for '//trim(label(ix(i)))//'max :', & xmaxoffset_track(i)) enddo if ((itrackoffset.ne.itrackoffsetprev .or. itracktype.ne.itracktypeprev) & .and. iCalcQuantities .and. irad.gt.0 .and. irad.le.numplot) then !--radius calculation is relative to tracked particle print "(a)",' recalculating radius relative to tracked particle ' if (DataIsBuffered) then call calc_quantities(1,nsteps) else call calc_quantities(1,nstepsinfile(ifileopen)) endif endif endif !------------------------------------------------------------------------ case(4) !+ Adjust plot limits to match device aspect ratio call prompt('Adjust limits to aspect ratio of device?',adjustlimitstodevice) !!+ Zooms in/out (alternatively do this in interactive mode) ! ! if (.not.iadapt) then ! call prompt('Enter zoom factor for fixed limits',zoom,0.0) ! do i=1,numplot ! diff = lim(i,2)- lim(i,1) ! mid = 0.5*(lim(i,1) + lim(i,2)) ! lim(i,1) = mid - 0.5*zoom*diff ! lim(i,2) = mid + 0.5*zoom*diff ! enddo ! else ! call prompt('Enter scale factor (adaptive limits)',scalemax,0.0) ! endif !------------------------------------------------------------------------ case(5) !+ Applies log, inverse and other transformations to data columns index = 1 do i=1,ntrans write(transprompt(index:),"(1x,i1,'=',a,',')") i,trim(transform_label('x',i)) index = len_trim(transprompt) + 1 enddo ipick = 1 do while (ipick.gt.0 .and. ipick.le.numplot) ipick = 0 call prompt('Enter column to apply transform (0=quit,-1=all) ',ipick) if (ipick.le.numplot .and. ipick.ne.0) then print "(a)", trim(transprompt) if (ipick.lt.0) then ipick = 0 call prompt('Which transform (or multiple e.g. 321)?',ipick,0) itrans(:) = ipick ipick = -99 else call prompt('Which transform (or multiple e.g. 321)?',itrans(ipick),0) endif endif enddo return !------------------------------------------------------------------------ case(6) !+ Resets plot limits using all data currently in memory !+ Note that these limits will only apply when fixed limits are used if (ivegotdata) then if (DataIsBuffered) then call set_limits(1,nsteps,1,ndataplots) else call set_limits(1,nstepsinfile(ifileopen),1,ndataplots) endif else print*,'no data with which to set limits!!' endif !------------------------------------------------------------------------ case(7) !+ Plot subset of data by restricting parameter range ipick = 1 do while (ipick.gt.0) ipick = 0 call print_rangeinfo() call prompt('Enter column to use to restrict data set (-1=none/unset all,0=quit)',ipick,-1,ndataplots) if (ipick.gt.0) then print*,'current plot limits for '//trim(label(ipick))//': (min,max) = ',lim(ipick,1),lim(ipick,2) call prompt(trim(label(ipick))//' min value ',range(ipick,1)) call prompt(trim(label(ipick))//' max value ',range(ipick,2),range(ipick,1)) if (.not.rangeset(ipick)) then print*,'>> min=max: no restriction set' endif elseif (ipick.eq.-1) then print "(a)",'>> removing all range restrictions on data set' range(:,:) = 0. endif write(*,*) enddo return end select return end subroutine submenu_limits subroutine get_itrackpart(string,itracktype,itrackpart,ierr) implicit none character(len=*), intent(in) :: string integer, intent(out) :: itracktype,itrackpart,ierr integer :: ic ic = index(string,':') if (ic.gt.0) then read(string(1:ic-1),*,iostat=ierr) itracktype read(string(ic+1:),*,iostat=ierr) itrackpart if (itrackpart.eq.0) itracktype = 0 else itracktype = 0 read(string,*,iostat=ierr) itrackpart endif end subroutine get_itrackpart end module settings_limits splash/src/write_sphdata.f90000644 000766 000000 00000024303 13261626263 016733 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! module containing output routines for writing SPH data ! as read by SPLASH to output file in various formats ! ! (c) D. Price 22/01/08 !----------------------------------------------------------------- module write_sphdata public :: issphformat,write_sphdump private contains !----------------------------------------------------------------- ! utility to check if a format selection is valid !----------------------------------------------------------------- logical function issphformat(string) implicit none character(len=*), intent(in) :: string issphformat = .false. select case(trim(string)) case('ascii','ASCII') issphformat = .true. case('binary','BINARY') issphformat = .true. case('rsph','RSPH') issphformat = .true. case('phantom','PHANTOM') issphformat = .true. case('gadget','GADGET') issphformat = .true. end select if (.not.issphformat) then print "(a)",' convert mode ("splash to X dumpfiles"): ' print "(a,/)",' splash to ascii : convert SPH data to ascii file dumpfile.ascii' print "(a)", ' to binary : convert SPH data to simple unformatted binary dumpfile.binary ' print "(a)", ' write(1) time,npart,ncolumns' print "(a)", ' do i=1,npart' print "(a)", ' write(1) dat(1:ncolumns),itype' print "(a)", ' enddo' print "(a)", ' to phantom : convert SPH data to binary dump file for PHANTOM' print "(a)", ' to gadget : convert SPH data to default GADGET snapshot file format' endif return end function issphformat subroutine write_sphdump(time,gamma,dat,npart,ntypes,npartoftype,masstype,itype,ncolumns,filename,outformat) use labels, only:labeltype,label,unitslabel,irho,ipmass,ix use settings_units, only:units use settings_data, only:ndim,icoords,icoordsnew,xorigin use params, only:int1,maxplot,doub_prec use write_data_phantom, only:write_sphdata_phantom use write_data_gadget, only:write_sphdata_gadget use filenames, only:tagline use geomutils, only:change_coords implicit none integer, intent(in) :: npart,ntypes,ncolumns integer, intent(in), dimension(:) :: npartoftype integer(kind=int1), intent(in), dimension(:) :: itype real, intent(in) :: time,gamma real, intent(in), dimension(npart,ncolumns) :: dat real, intent(in), dimension(:) :: masstype character(len=*), intent(in) :: filename,outformat integer, parameter :: iunit = 83 integer, parameter :: maxline = 1000 integer :: ierr,i,idim,i1,i2 character(len=40) :: fmtstring,fmtstring2,fmtstringlab,outfile real(kind=doub_prec), dimension(maxplot) :: vals real, dimension(3) :: x0,v0 logical :: change_coordsys select case(trim(outformat)) case ('ascii','ASCII') print "(/,5('-'),'>',a,i2,a,1x,'<',5('-'),/)",' WRITING TO FILE '//trim(filename)//'.ascii WITH ',ncolumns,' COLUMNS' !--format the header lines to go in the ascii file if (kind(1.)==8) then write(fmtstring,"(i10,a)") ncolumns,'(es23.15,1x)' else write(fmtstring,"(i10,a)") ncolumns,'(es15.7,1x)' endif fmtstring2 = '('//trim(adjustl(fmtstring))//',i1)' fmtstring = '('//trim(adjustl(fmtstring))//')' write(fmtstringlab,"(i10,a)") ncolumns,'(a15,1x),a' fmtstringlab = '(''#'',1x,'//trim(adjustl(fmtstringlab))//')' open(unit=iunit,file=trim(filename)//'.ascii',status='replace',form='formatted',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR OPENING FILE FOR WRITING' return endif write(iunit,"(a)",err=100) '# '//trim(filename)//'.ascii, created by '//trim(tagline) write(iunit,"('#')",err=100) write(iunit,"('#',1x,'time:',13x,'time unit (',a,')')",err=100) trim(unitslabel(0)) write(iunit,"('#',2(1x,1pe15.7))",err=100) time,units(0) write(iunit,"('#')",err=100) write(iunit,"('#',1x,'npart:',6(1x,a12))",err=100) (trim(labeltype(i)),i=1,ntypes) write(iunit,"('#',7x,6(1x,i12))",err=100) npartoftype(1:ntypes) write(iunit,"('# units:')",err=100) write(iunit,"('#'"//fmtstring(2:),err=100) units(1:ncolumns) write(iunit,fmtstringlab,err=100) unitslabel(1:ncolumns) write(iunit,"('#')",err=100) ! !--write body ! change_coordsys = (icoordsnew.ne.icoords .and. ndim.gt.0 .and. all(ix(1:ndim).gt.0)) x0 = xorigin(:) ! note that it is not currently possible to do splash to ascii v0 = 0. ! with coords set relative to a tracked particle, so just use xorigin if (size(itype).gt.1) then write(iunit,fmtstringlab,iostat=ierr) label(1:ncolumns),'itype' do i=1,npart vals(1:ncolumns) = dat(i,1:ncolumns) if (change_coordsys) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) write(iunit,fmtstring2,err=100) vals(1:ncolumns),itype(i) enddo else write(iunit,fmtstringlab,iostat=ierr) label(1:ncolumns) if (change_coordsys) then do i=1,npart vals(1:ncolumns) = dat(i,1:ncolumns) call change_coords(vals,ncolumns,ndim,icoords,icoordsnew,x0,v0) write(iunit,fmtstring,err=100) vals(1:ncolumns) enddo else do i=1,npart write(iunit,fmtstring,err=100) dat(i,1:ncolumns) enddo endif endif close(iunit) return 100 continue close(iunit) print*,'ERROR WRITING ASCII FILE' return case ('binary','BINARY') ! !--This is the most basic binary (ie. unformatted) file format I could think of, ! as an alternative to ascii for large files. ! print "(/,5('-'),'>',a,i2,a,1x,'<',5('-'),/)",' WRITING TO FILE '//trim(filename)//'.binary WITH ',ncolumns,' COLUMNS' open(unit=iunit,file=trim(filename)//'.binary',status='replace',form='unformatted',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR OPENING FILE FOR WRITING' return endif write(iunit,iostat=ierr) time,npart,ncolumns if (ierr /= 0) then print "(a)",' ERROR WRITING HEADER LINE TO BINARY FILE ' endif ! !--write body ! if (size(itype).gt.1) then do i=1,npart write(iunit,err=200) dat(i,1:ncolumns),int(itype(i)) enddo else do i=1,npart write(iunit,err=200) dat(i,1:ncolumns) enddo endif close(iunit) return 200 continue close(iunit) print*,'ERROR WRITING BINARY FILE' return case ('rsph','RSPH') ! !--Files for Steinar Borve's RSPH format ! if (ndim.lt.2) then print "(a)",' ERROR: cannot write RSPH format for < 2D' return endif outfile = 'rsph2D_pos.dat' print "(a)",' writing to '//trim(outfile) open(unit=iunit,file=outfile,status='replace',form='formatted',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR OPENING '//trim(outfile)//' FOR WRITING' return endif write(iunit,"(i1)") ndim write(iunit,"(a)") 'position' write(iunit,"(i4)") maxline write(iunit,*) (minval(dat(1:npart,ix(i))),i=1,ndim) write(iunit,*) (maxval(dat(1:npart,ix(i))),i=1,ndim) write(iunit,*) time write(iunit,*) npart do idim=1,2 i1 = 1 i2 = 0 do while (i2 < npart) i2 = min(i2 + maxline,npart) write(iunit,*) dat(i1:i2,ix(idim)) i1 = i2 + 1 enddo enddo close(unit=iunit) outfile = 'rsph2D_rho.dat' print "(a)",' writing to '//trim(outfile) open(unit=iunit,file=outfile,status='replace',form='formatted',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR OPENING FILE FOR WRITING' return endif write(iunit,"(i1)") ndim write(iunit,"(a)") 'density' write(iunit,"(i4)") maxline write(iunit,*) (minval(dat(1:npart,ix(i))),i=1,ndim) write(iunit,*) (maxval(dat(1:npart,ix(i))),i=1,ndim) i1 = 1 i2 = 0 do while (i2 < npart) i2 = min(i2 + maxline,npart) write(iunit,*) dat(i1:i2,irho) i1 = i2 + 1 enddo close(unit=iunit) outfile = 'rsph2D_siz.dat' print "(a)",' writing to '//trim(outfile) open(unit=iunit,file=outfile,status='replace',form='formatted',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR OPENING FILE FOR WRITING' return endif write(iunit,"(i1)") ndim write(iunit,"(a)") 'size' write(iunit,"(i4)") maxline write(iunit,*) (minval(dat(1:npart,ix(i))),i=1,ndim) write(iunit,*) (maxval(dat(1:npart,ix(i))),i=1,ndim) i1 = 1 i2 = 0 do while (i2 < npart) i2 = min(i2 + maxline,npart) write(iunit,*) ((dat(i,ipmass)/dat(i,irho))**(1./ndim),i=i1,i2) i1 = i2 + 1 enddo close(unit=iunit) case('phantom','PHANTOM') call write_sphdata_phantom(time,gamma,dat,npart,1,npartoftype(1:1),& masstype,ncolumns,filename) case('gadget','GADGET') call write_sphdata_gadget(time,dat,itype,npart,ntypes,npartoftype,& masstype,ncolumns,filename) case default print "(a)",' ERROR: unknown output format '''//trim(outformat)//''' in write_sphdump' return end select end subroutine write_sphdump end module write_sphdata splash/src/read_data_spyros.f90000644 000766 000000 00000015200 13261626263 017414 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR READING UNFORMATTED OUTPUT FROM THE VINE CODE ! (ie. STRAIGHT FROM THE DATA DUMP) ! ! *** CONVERTS TO SINGLE PRECISION *** ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer :: j,ierr,ntoti,irec integer :: npart_max,nstep_max,ncolstep logical :: iexist character(len=len(rootname)+10) :: dumpfile !--we are assuming dump is double precision real(doub_prec) :: variable1,variable2 real(doub_prec), dimension(:), allocatable :: dattemp nstepsread = 0 npart_max = maxpart dumpfile = trim(rootname) ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 ! !--allocate memory initially ! nstep_max = max(indexstart,1) j = indexstart nstepsread = 0 print "(1x,a)",'reading Spyros Kitsonias'' format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='unformatted',& access='direct',recl=4000000) if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' else ! !--read timestep header (integers only) ! read(15,iostat=ierr) variable1,variable2 if (ierr < 0) then print "(a)",'*** END OF FILE IN TIMESTEP HEADER ***' return elseif (ierr /= 0) then print "(a)",'*** ERROR READING TIMESTEP HEADER ***' return endif ! !--get number of particles from header and allocate memory ! ntoti = int(variable1) ncolstep = int(variable2) ncolumns = ncolstep if (.not.allocated(dat) .or. ntoti.gt.npart_max) then npart_max = max(npart_max,INT(1.1*ntoti)) call alloc(npart_max,nstep_max,ncolstep+ncalc) endif ! !--rewind file ! rewind(15) endif npart_max = max(npart_max,ntoti) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,j+2*nstepsread,maxcol) endif ! !--allocate a temporary array for double precision variables ! if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(npart_max),stat=ierr) dattemp = 0. if (ierr /= 0) print*,'not enough memory in read_data' ! !--now read the timestep data in the dumpfile ! print "(a,i5,a,f8.3,a,i8)",'| step ',j,': t = ',time(j),' ntotal = ',ntoti nstepsread = nstepsread + 1 ! !--set particle numbers ! npartoftype(:,j) = 0 npartoftype(1,j) = ntoti ! !--read all records ! overcolumns: do irec=1,ncolstep read(15,rec=irec,iostat=ierr) variable1,variable2,dattemp(1:ntoti) if (ierr < 0) then print "(a)",'*** END OF FILE IN READ DATA (CHECK PRECISION) ***' exit overcolumns elseif (ierr /= 0) then print "(a)",'*** ERROR READING DATA ***' exit overcolumns endif ! !--time and gamma ! if (irec.eq.2) time(j) = real(variable1) if (irec.eq.5) gamma(j) = real(variable2) ! !--convert to single precision ! dat(1:ntoti,irec,j) = real(dattemp(1:ntoti)) enddo overcolumns ! !--clean up ! if (allocated(dattemp)) deallocate(dattemp) ! !--close file ! close(15) if (nstepsread .gt. 0) then print*,'>> end of dump file: ntotal = ',sum(npartoftype(:,j)) endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ih = 10 ! smoothing length iutherm = 9 ! thermal energy ipmass = 8 ! particle mass irho = 7 ! location of rho in data array label(ix(1:ndim)) = labelcoord(1:ndim,1) label(irho) = 'density' label(iutherm) = 'temperature' label(ih) = 'h' label(ipmass) = 'particle mass' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 1 labeltype(1) = 'gas' UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_amuse_hdf5_utils.c000644 000766 000000 00000032303 13261626263 021144 0ustar00dpricewheel000000 000000 /* * This subroutine performs the calls to the HDF5 library for the * GADGET data read * * Easier to do it this way and link with c than to try to link against * the Fortran interface (in the latter case the modules must * have been compiled with the *exact* compiler used to compile splash * which is a real pain). * */ #include #include #include #include static int debug = 0; int checkfordataset(hid_t file_id, char *datasetname); int read_amuse_hdf5_dataset(hid_t group_id, char *datasetname, int itype, int maxtypes, int npartoftype[maxtypes], int ncol, int isrequired[ncol], int *j); int get_rank(hid_t dataspace_id); int get_rank_by_name(hid_t group_id, char *name); void set_blocklabel(int *icol, char *name); void read_amuse_hdf5_data_fromc(int *icol, int *npartoftypei, double temparr[*npartoftypei],int *itype); void read_amuse_hdf5_header(char *filename, int *npart, int *ncol, int *ndim, int *ndimV, double *time, int *ierr) { hid_t file_id; hid_t group_id, group_id1, group_id2; hid_t attrib_id; herr_t status; herr_t HDF5_error = -1; *ierr = 0; *ndim = 0; *ndimV = 0; if (debug) printf("DEBUG: opening %s \n",filename); file_id = H5Fopen(filename,H5F_ACC_RDONLY,H5P_DEFAULT); if (file_id == HDF5_error) { printf("ERROR opening %s \n",filename); *ierr = 1; return; } char *maingroup = "particles"; /* * Open the "particles" dataset and read the number of particles attribute * */ if (!checkfordataset(file_id,maingroup)) { printf(" ERROR: \"%s\" dataset not found in AMUSE HDF5 file\n",maingroup); *ierr = 2; return; } #if H5_VERSION_GE(1,8,0) group_id1 = H5Gopen2(file_id,maingroup,H5P_DEFAULT); #else group_id1 = H5Gopen(file_id,maingroup); #endif if (group_id1 == HDF5_error) { printf("ERROR opening %s data set \n",maingroup); *ierr = 2; return; } #if H5_VERSION_GE(1,8,0) group_id = H5Gopen2(group_id1,"0000000001",H5P_DEFAULT); #else group_id = H5Gopen(group_id1,"0000000001"); #endif if (group_id == HDF5_error) { printf("ERROR opening 00000000001 data set \n"); *ierr = 2; return; } int nattrib; int i; char name[256]; nattrib = H5Aget_num_attrs(group_id); if (debug) printf("number of attributes found = %i\n",nattrib); /* * Read through all of the attributes in the header, so we * can still spit out the values even if they are not used by SPLASH */ for(i=0; i < nattrib; i++) { attrib_id = H5Aopen_idx(group_id,i); ssize_t attr_status; attr_status = H5Aget_name(attrib_id, 256, name); hid_t type_id; type_id = H5Aget_type(attrib_id); /*type_class = H5Tget_native_type(type_id,H5T_DIR_ASCEND);*/ if (strcmp(name,"time")==0) { status = H5Aread(attrib_id,H5T_NATIVE_DOUBLE,time); } else if (strcmp(name,"number_of_particles")==0) { status = H5Aread(attrib_id,H5T_NATIVE_INT,npart); } else { if (debug) printf("DEBUG: unknown attribute %s \n",name); } if (status==HDF5_error) { printf(" ERROR reading attribute %s \n",name); } status = H5Aclose(attrib_id); } /* * Now we need to get the number of data columns in the file * (from the number of datasets in the "attributes" group) */ #if H5_VERSION_GE(1,8,0) group_id2 = H5Gopen2(group_id,"attributes",H5P_DEFAULT); #else group_id2 = H5Gopen(group_id,"attributes"); #endif if (group_id2 == HDF5_error) { printf("ERROR opening %s data set \n","attributes"); *ierr = 2; return; } hsize_t ndatasets; status = H5Gget_num_objs(group_id2, &ndatasets); if (debug) printf("DEBUG: number of datasets = %i \n",(int)ndatasets); *ncol = (int)ndatasets; int idim; /* check that coordinates are present in file */ idim = get_rank_by_name(group_id2,"x"); if (idim <= 0) { printf("ERROR: x positions not found\n"); *ierr = 3; } idim = get_rank_by_name(group_id2,"y"); if (idim <= 0) { printf("ERROR: y positions not found\n"); *ierr = 3; } idim = get_rank_by_name(group_id2,"z"); if (idim <= 0) { printf("z positions not found, assuming file is 2D \n"); *ndim = 2; *ndimV = 2; } else { *ndim = 3; *ndimV = 3; } /* finish, close all open datasets and close file */ status = H5Gclose(group_id2); if (status == HDF5_error) { printf("ERROR closing attributes data set \n"); *ierr = 3; return; } status = H5Gclose(group_id); if (status == HDF5_error) { printf("ERROR closing %s data set \n",maingroup); *ierr = 3; return; } status = H5Gclose(group_id1); if (status == HDF5_error) { printf("ERROR closing 0001 data set \n"); *ierr = 3; return; } status = H5Fclose( file_id ); if (status == HDF5_error) { printf("ERROR closing file \n"); *ierr = 7; } if (debug) printf("DEBUG: finished header read \n"); } void read_amuse_hdf5_data(char *filename, int maxtypes, int npartoftype[maxtypes], int ncol, int isrequired[ncol], int *ierr) { hid_t file_id; hid_t group_id, group_id1, group_id2; herr_t status; herr_t HDF5_error = -1; char groupname[12]; char datasetname[256]; int i; if (debug) printf("DEBUG: re-opening %s \n",filename); file_id = H5Fopen(filename,H5F_ACC_RDONLY,H5P_DEFAULT); if (file_id == HDF5_error) { printf("ERROR re-opening %s \n",filename); *ierr = 1; return; } /* open main particles group */ char *maingroup = "particles"; #if H5_VERSION_GE(1,8,0) group_id1 = H5Gopen2(file_id,maingroup,H5P_DEFAULT); #else group_id1 = H5Gopen(file_id,maingroup); #endif if (debug) printf("DEBUG: maxtypes = %i\n",maxtypes); /* read dataset for each particle type present in dump file */ int itype,iobjtype; for (itype=0;itype 0) { /* If npartoftype[N] > 0 in header, look for dataset of the form 000000000N */ sprintf(groupname,"00000000%02i",itype+1); if (debug) printf("DEBUG: opening group %s\n",groupname); #if H5_VERSION_GE(1,8,0) group_id = H5Gopen2(group_id1,groupname,H5P_DEFAULT); #else group_id = H5Gopen(group_id1,groupname); #endif if (group_id == HDF5_error) { printf("ERROR opening %s group \n",groupname); *ierr = 2; } else { #if H5_VERSION_GE(1,8,0) group_id2 = H5Gopen2(group_id,"attributes",H5P_DEFAULT); #else group_id2 = H5Gopen(group_id,"attributes"); #endif if (group_id2 == HDF5_error) { printf("ERROR opening attributes group \n"); *ierr = 2; } else { hsize_t ndatasets; status = H5Gget_num_objs(group_id2, &ndatasets); if (debug) printf("DEBUG: number of datasets = %i \n",(int)ndatasets); int j = 0; /* always read particle positions first */ *ierr = read_amuse_hdf5_dataset(group_id2,"x",itype,maxtypes,npartoftype,ncol,isrequired,&j); j = 1; *ierr = read_amuse_hdf5_dataset(group_id2,"y",itype,maxtypes,npartoftype,ncol,isrequired,&j); j = 2; *ierr = read_amuse_hdf5_dataset(group_id2,"z",itype,maxtypes,npartoftype,ncol,isrequired,&j); /* read remaining datasets in the order they appear in the file */ for(i=0; i < (int)ndatasets; i++) { status = H5Gget_objname_by_idx(group_id2, i, datasetname, 256); iobjtype = H5Gget_objtype_by_idx(group_id2, i); if (strcmp(datasetname,"x")&& strcmp(datasetname,"y")&& strcmp(datasetname,"z")&& (iobjtype == H5G_DATASET)) { *ierr = read_amuse_hdf5_dataset(group_id2,datasetname,itype,maxtypes,npartoftype,ncol,isrequired,&j); } } /* close "attributes" group */ H5Gclose(group_id2); } H5Gclose(group_id); } } } H5Gclose(group_id1); status = H5Fclose( file_id ); if (status == HDF5_error) { printf("ERROR closing file \n"); *ierr = 7; } } int read_amuse_hdf5_dataset(hid_t group_id, char *datasetname, int itype, int maxtypes, int npartoftype[maxtypes], int ncol, int isrequired[ncol], int *j) { hid_t dataset_id, dataspace_id, memspace_id; herr_t status; herr_t HDF5_error = -1; int ierr = 0; char name[256]; if (!checkfordataset(group_id,datasetname)) { ierr = 1; return ierr; } #if H5_VERSION_GE(1,8,0) dataset_id = H5Dopen2(group_id,datasetname,H5P_DEFAULT); #else dataset_id = H5Dopen(group_id,datasetname); #endif dataspace_id = H5Dget_space(dataset_id); int rank = get_rank(dataspace_id); int k, flag; /* do nothing if none of the columns are required */ flag = 0; for (k=0;k1) { rank = dims[1]; } else { rank = 1; } return rank; } /* * utility function to get dimensionality of a dataset */ int get_rank_by_name(hid_t group_id, char *name) { if (!checkfordataset(group_id,name)) { return 0; } herr_t HDF5_error = -1; #if H5_VERSION_GE(1,8,0) hid_t dataset_id = H5Dopen2(group_id,name,H5P_DEFAULT); #else hid_t dataset_id = H5Dopen(group_id,name); #endif if (dataset_id == HDF5_error) { printf("ERROR opening %s data set \n",name); return 0; } hid_t dataspace_id = H5Dget_space(dataset_id); int rank = get_rank(dataspace_id); H5Dclose(dataset_id); return rank; } splash/src/kernels.f90000644 000766 000000 00000015713 13261626263 015545 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------- ! ! Module containing kernel functions used for interpolation ! !------------------------------------------------------------- module kernels implicit none integer, parameter, public :: nkernels = 6 character(len=24), dimension(0:nkernels), parameter, public :: kernelname = & (/'default [cubic] ', & 'M4 cubic spline (2h) ', & 'M5 quartic (2.5h)', & 'M6 quintic spline (3h) ', & 'Wendland C2 (2h) ', & 'Wendland C4 (2h) ', & 'Wendland C6 (2h) '/) real, parameter :: pi = 3.14159236 integer, public :: ikernel = 0 real, public :: radkernel = 2. real, public :: radkernel2 = 4. real, public :: cnormk1D = 2./3. real, public :: cnormk2D = 10./(7.*pi) real, public :: cnormk3D = 1./pi procedure(k_func), pointer, public :: wfunc abstract interface pure function k_func(q) real, intent(in) :: q real :: k_func end function k_func end interface public :: select_kernel, select_kernel_by_name private contains !----------------------------------------------- ! ! Kernel selection routine, sets radkernel, ! cnormk values and pointer to kernel function ! !-------------------------------------- subroutine select_kernel(j) integer, intent(in) :: j if (j.ge.1 .and. j.le.nkernels) then !--print only if NOT using the default kernel print "(a,/)",' Using '//trim(kernelname(j))//' kernel' endif select case(j) case(6) ! Wendland 3D C6 ikernel = 6 radkernel = 2. cnormk1D = 15./16. cnormk2D = 39./(14.*pi) cnormk3D = 1365./(512.*pi) wfunc => w_wendlandc6 case(5) ! Wendland 3D C4 ikernel = 5 radkernel = 2. cnormk1D = 27./32. cnormk2D = 9./(4.*pi) cnormk3D = 495./(256.*pi) wfunc => w_wendlandc4 case(4) ! Wendland 3D C2 ikernel = 4 radkernel = 2. cnormk1D = 0.75 cnormk2D = 7./(4.*pi) cnormk3D = 21./(16.*pi) wfunc => w_wendlandc2 case(3) ! M6 quintic, 3h ikernel = 3 radkernel = 3.0 cnormk1D = 1./120. cnormk2D = 7./(478*pi) cnormk3D = 1./(120.*pi) wfunc => w_quintic case(2) ! M5 quartic, 2.5h ikernel = 2 radkernel = 2.5 cnormk1D = 1./24. cnormk2D = 96./(1199.*pi) cnormk3D = 1./(20.*pi) wfunc => w_quartic case default !-- cubic spline kernel if (j.eq.1) then ikernel = 1 ! deliberately chose cubic spline else ikernel = 0 ! just whatever is the default endif radkernel = 2.0 cnormk1D = 2./3. cnormk2D = 10./(7.*pi) cnormk3D = 1./pi wfunc => w_cubic end select radkernel2 = radkernel*radkernel end subroutine select_kernel !-------------------------------------- ! ! Kernel selection based on string ! !-------------------------------------- subroutine select_kernel_by_name(string) use asciiutils, only:lcase character(len=*), intent(in) :: string integer :: i,jkern jkern = 0 ! !--check if string exactly matches a kernel name ! do i=1,nkernels if (trim(adjustl(lcase(string)))==trim(adjustl(lcase(kernelname(i))))) then jkern = i endif enddo ! !--if no match to a kernel name, look for other possible strings ! if (ikernel.eq.0) then select case(trim(adjustl(lcase(string)))) case('wendlandc6','wendland c6','6th order wendland','wendland 3d c6','w6','wendland6') jkern = 6 case('wendlandc4','wendland c4','4th order wendland','wendland 3d c4','w4','wendland4') jkern = 5 case('wendlandc2','wendland c2','2nd order wendland','wendland 3d c2','w2','wendland2') jkern = 4 case('quintic','quintic spline','m6','quintic b-spline') jkern = 3 case('quartic','quartic spline','m5','quartic b-spline') jkern = 2 case('cubic','cubic spline','m4','cubic b-spline') jkern = 1 end select endif call select_kernel(jkern) end subroutine select_kernel_by_name !--------------------------------------- ! ! Functional forms of various kernels ! !-------------------------------------- pure real function w_cubic(q2) implicit none real, intent(in) :: q2 real :: q if (q2.lt.1.0) then q = sqrt(q2) w_cubic = 1.-1.5*q2 + 0.75*q2*q elseif (q2.lt.4.0) then q = sqrt(q2) w_cubic = 0.25*(2.-q)**3 else w_cubic = 0. endif end function w_cubic pure real function w_quartic(q2) implicit none real, intent(in) :: q2 real :: q q = sqrt(q2) if (q.lt.0.5) then w_quartic = (2.5-q)**4 - 5.*(1.5-q)**4 + 10.*(0.5-q)**4 elseif (q.lt.1.5) then w_quartic = (2.5-q)**4 - 5.*(1.5-q)**4 elseif (q.lt.2.5) then w_quartic = (2.5-q)**4 else w_quartic = 0. endif end function w_quartic pure real function w_quintic(q2) implicit none real, intent(in) :: q2 real :: q,q4 if (q2.lt.1.0) then q = sqrt(q2) q4 = q2*q2 w_quintic = 66.-60.*q2 + 30.*q4 - 10.*q4*q elseif ((q2.ge.1.0).and.(q2.lt.4.0)) then q = sqrt(q2) w_quintic = (3.-q)**5 - 6.*(2.-q)**5 elseif ((q2.ge.4.0).and.(q2.lt.9.0)) then q = sqrt(q2) w_quintic = (3.-q)**5 else w_quintic = 0.0 endif end function w_quintic pure real function w_quartic2h(q2) implicit none real, intent(in) :: q2 real :: q q = sqrt(q2) if (q.lt.0.4) then w_quartic2h = (2.-q)**4 - 5.*(1.2-q)**4 + 10.*(0.4-q)**4 elseif (q.lt.1.2) then w_quartic2h = (2.-q)**4 - 5.*(1.2-q)**4 elseif (q.lt.2.) then w_quartic2h = (2.-q)**4 else w_quartic2h = 0. endif end function w_quartic2h pure real function w_wendlandc2(q2) implicit none real, intent(in) :: q2 real :: q if (q2.lt.4.) then q = sqrt(q2) w_wendlandc2 = (1. - 0.5*q)**4*(2.*q + 1.) else w_wendlandc2 = 0. endif end function w_wendlandc2 pure real function w_wendlandc4(q2) implicit none real, intent(in) :: q2 real :: q if (q2.lt.4.) then q = sqrt(q2) w_wendlandc4 = (1. - 0.5*q)**6*(35./12.*q2 + 3.*q + 1.) else w_wendlandc4 = 0. endif end function w_wendlandc4 pure real function w_wendlandc6(q2) implicit none real, intent(in) :: q2 real :: q if (q2.lt.4.) then q = sqrt(q2) w_wendlandc6 = (1. - 0.5*q)**8*(4.*q2*q + 25./4.*q2 + 4.*q + 1.) else w_wendlandc6 = 0. endif end function w_wendlandc6 end module kernels splash/src/exact_shock_sr.f90000644 000766 000000 00000043645 13261626263 017106 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! compute exact solution for the one dimensional Riemann problem ! in Special Relativity ! ! input parameters are initial left and right states of ! density, pressure and velocity ! ! Computes shock profile at time t ! ! Calls the exact solution routine provided by Marti & Mueller ! ! Daniel Price, Institute of Astronomy, Cambridge, 2004 ! University of Exeter 2004-2008 ! Monash University 2008- ! ! daniel.price@monash.edu !----------------------------------------------------------------------- module shock_sr implicit none public :: exact_shock_sr contains subroutine exact_shock_sr(iplot,time,gamma,rho_L,rho_R,p_L,p_R,v_L,v_R,xplot,yplot,ierr) implicit none integer, intent(in) :: iplot integer, intent(out) :: ierr real, intent(in) :: time,gamma real, intent(in) :: rho_L,rho_R,p_L,p_R,v_L,v_R real, dimension(:), intent(inout) :: xplot real, dimension(size(xplot)), intent(out) :: yplot double precision, dimension(size(xplot)) :: rad,dens,pr,vel,uu double precision :: rhol,rhor,pl,prr,vl,vr,gam,t print*,'Plotting Special Relativistic Riemann solution at t = ',time,' gamma = ',gamma ! ! check for errors in input ! ierr = 0 if (rho_L.le.0. .or. rho_R.le.0.) then print*,'error: rho <= 0 on input : ',rho_L,rho_R ierr = 1 return elseif (p_L .le.0. .or. p_R .le.0.) then print*,'error: pr <= 0 on input ',p_L, p_R ierr = 2 return endif rhol = rho_L rhor = rho_R pl = p_L prr = p_R vl = v_L vr = v_R gam = gamma t = time rad(:) = xplot(:) call riemann(size(xplot),rad,dens,pr,vel,uu,rhol,rhor,pl,prr,vl,vr,gam,t,0.0d0) !------------------------------------ ! determine which solution to plot !------------------------------------ select case(iplot) case(1) yplot = real(dens) case(2) yplot = real(pr) case(3) yplot = real(vel) case(4) if (gamma.gt.1.0001) then yplot = real(pr/((gamma-1.)*dens)) else yplot = real(pr/dens) endif case(5) ! rho* yplot = real(dens/dsqrt(1.d0-vel**2)) end select return end subroutine exact_shock_sr ! ------------ !n name: r i e m a n n ! ------------ !p purpose: !p this program computes the solution of a 1d !p relativistic riemann problem (for constant-gamma ideal gases) with !p initial data ul if x<0.5 and ur if x>0.5 !p in the whole spatial domain [0, 1] ! !c comments: !c see marti and mueller, jfm, 1994 !c !c written by: jose-maria marti !c departamento de astronomia y astrofisica !c universidad de valencia !c 46100 burjassot (valencia), spain !c jose-maria.marti@uv.es !c and !c ewald mueller !c max-planck-institut fuer astrophysik !c karl-schwarzschild-str. 1 !c 85741 garching, germany !c emueller@mpa-garching.mpg.de !c !c Modifications by D. Price (daniel.price@sci.monash.edu.au): !c 07/2007: used as subroutine instead of program !c 09/2008: reformatted in free-form and included in exact_shock_sr module !c subroutine riemann(mn,rad,rhoa,pa,vela,ua,rholin,rhorin,plin,prin, & vlin,vrin,gamin,tin,x0) implicit none ! ------- ! common blocks ! ------- double precision rhol, pl, ul, hl, csl, vell, wl, & rhor, pr, ur, hr, csr, velr, wr common /states/ rhol, pl, ul, hl, csl, vell, wl, & rhor, pr, ur, hr, csr, velr, wr double precision rhols, uls, hls, csls, vells, vshockl common /ls/ rhols, uls, hls, csls, vells, vshockl double precision rhors, urs, hrs, csrs, velrs, vshockr common /rs/ rhors, urs, hrs, csrs, velrs, vshockr double precision gamma common /adind/ gamma ! --------- ! internal variables ! --------- integer, intent(in) :: mn integer n, i, iloop ! parameter (mn = 400) double precision tol, pmin, pmax, dvel1, dvel2, check !, xmin double precision, intent(in) :: rholin,rhorin,plin,prin,vlin,vrin,gamin,tin double precision ps, vels double precision, intent(out) :: rhoa(mn), pa(mn), vela(mn), ua(mn) double precision, intent(in) :: x0 double precision xi double precision, intent(in) :: rad(mn) double precision x1, x2, x3, x4, x5, t ! ------- ! initial states ! ------- ! write(*,*) ' adiabatic index of the gas: ' ! read (*,*) gamma gamma = gamin ! write(*,*) ' time for the solution: ' ! read (*,*) t t = tin ! xmin = x0 - 0.5d0 ! ----- ! left state ! ----- ! write(*,*) ' -- left state -- ' ! write(*,*) ' pressure : ' ! read (*,*) pl ! write(*,*) ' density : ' ! read (*,*) rhol ! write(*,*) ' flow velocity: ' ! read (*,*) vell pl = plin rhol = rholin vell = vlin pr = prin rhor = rhorin velr = vrin ! ------ ! right state ! ------ ! write(*,*) ' -- right state -- ' ! write(*,*) ' pressure : ' ! read (*,*) pr ! write(*,*) ' density : ' ! read (*,*) rhor ! write(*,*) ' flow velocity: ' ! read (*,*) velr ! ------------------------------ ! specific internal energy, specific enthalpy, sound speed and ! flow lorentz factors in the initial states ! ------------------------------ ul = pl/(gamma-1.d0)/rhol ur = pr/(gamma-1.d0)/rhor hl = 1.d0+ul+pl/rhol hr = 1.d0+ur+pr/rhor csl = dsqrt(gamma*pl/rhol/hl) csr = dsqrt(gamma*pr/rhor/hr) wl = 1.d0/dsqrt(1.d0-vell**2) wr = 1.d0/dsqrt(1.d0-velr**2) ! -------- ! number of points ! -------- n = mn ! ------------- ! tolerance for the solution ! ------------- tol = 0.d0 ! iloop = 0 pmin = (pl + pr)/2.d0 pmax = pmin 5 iloop = iloop + 1 pmin = 0.5d0*max(pmin,0.d0) pmax = 2.d0*pmax call getdvel(pmin, dvel1) call getdvel(pmax, dvel2) check = dvel1*dvel2 if (check.gt.0.d0) goto 5 ! --------------------------- ! pressure and flow velocity in the intermediate states ! --------------------------- call getp(pmin, pmax, tol, ps) vels = 0.5d0*(vells + velrs) ! --------------- ! solution on the numerical mesh ! --------------- ! ----------- ! positions of the waves ! ----------- if (pl.ge.ps) then x1 = x0 + (vell - csl )/(1.d0 - vell*csl )*t x2 = x0 + (vels - csls)/(1.d0 - vels*csls)*t else x1 = x0 + vshockl*t x2 = x1 end if x3 = x0 + vels*t if (pr.ge.ps) then x4 = x0 + (vels + csrs)/(1.d0 + vels*csrs)*t x5 = x0 + (velr + csr )/(1.d0 + velr*csr )*t else x4 = x0 + vshockr*t x5 = x4 end if ! ---------- ! solution on the mesh ! ---------- !do i=1,n ! rad(i) = xmin + dfloat(i-1)/dfloat(n-1) !enddo do i=1,n if (rad(i).le.x1) then pa(i) = pl rhoa(i) = rhol vela(i) = vell ua(i) = ul else if (rad(i).le.x2) then xi = (rad(i) - x0)/t call raref(xi, rhol, csl, vell, 'l', & rhoa(i), pa(i), ua(i), vela(i)) else if (rad(i).le.x3) then pa(i) = ps rhoa(i) = rhols vela(i) = vels ua(i) = uls else if (rad(i).le.x4) then pa(i) = ps rhoa(i) = rhors vela(i) = vels ua(i) = urs else if (rad(i).le.x5) then xi = (rad(i) - x0)/t call raref(xi, rhor, csr, velr, 'r', & rhoa(i), pa(i), ua(i), vela(i)) else pa(i) = pr rhoa(i) = rhor vela(i) = velr ua(i) = ur end if enddo ! open (3,file='solution.dat',form='formatted',status='new') ! write(3,150) n, t ! 150 format(i5,1x,f10.5) ! do 60 i=1,n ! write(3,200) rad(i),pa(i),rhoa(i),vela(i),ua(i) ! 60 continue ! 200 format(5(e15.8,1x)) ! close(3) return end subroutine riemann ! ---------- !n name: g e t d v e l ! ---------- !p purpose: !p compute the difference in flow speed between left and right intermediate !p states for given left and right states and pressure ! !c comments !c none subroutine getdvel( p, dvel ) implicit none ! ----- ! arguments ! ----- doubleprecision, intent(in) :: p doubleprecision, intent(out) :: dvel ! ------- ! common blocks ! ------- double precision rhols,uls,hls,csls,vells,vshockl common /ls/ rhols,uls,hls,csls,vells,vshockl double precision rhors,urs,hrs,csrs,velrs,vshockr common /rs/ rhors,urs,hrs,csrs,velrs,vshockr double precision rhol, pl, ul, hl, csl, vell, wl, & rhor, pr, ur, hr, csr, velr, wr common /states/ rhol, pl, ul, hl, csl, vell, wl, & rhor, pr, ur, hr, csr, velr, wr double precision gamma common /adind/ gamma ! ----- ! left wave ! ----- call getvel(p, rhol, pl, hl, csl, vell, wl, 'l', & rhols, uls, hls, csls, vells, vshockl ) ! ----- ! right wave ! ----- call getvel(p, rhor, pr, hr, csr, velr, wr, 'r', & rhors, urs, hrs, csrs, velrs, vshockr ) dvel = vells - velrs return end subroutine getdvel ! ------- !n name: g e t p ! ------- !p purpose: !p find the pressure in the intermediate state of a riemann problem in !p relativistic hydrodynamics ! !c comments: !c this routine uses a combination of interval bisection and inverse !c quadratic interpolation to find the root in a specified interval. !c it is assumed that dvel(pmin) and dvel(pmax) have opposite signs without !c a check. !c adapted from "computer methods for mathematical computation", !c by g. e. forsythe, m. a. malcolm, and c. b. moler, !c prentice-hall, englewood cliffs n.j. ! subroutine getp( pmin, pmax, tol, ps ) implicit none ! ----- ! arguments ! ----- doubleprecision, intent(in) :: pmin, pmax, tol doubleprecision, intent(out) :: ps ! ------- ! common blocks ! ------- doubleprecision gamma common /adind/ gamma doubleprecision rhol, pl, ul, hl, csl, vell, wl, & rhor, pr, ur, hr, csr, velr, wr common /states/ rhol, pl, ul, hl, csl, vell, wl, & rhor, pr, ur, hr, csr, velr, wr ! --------- ! internal variables ! --------- doubleprecision a, b, c, d, e, eps, fa, fb, fc, tol1, xm, p, q, r, s ! ------------- ! compute machine precision ! ------------- eps = 1.d0 10 eps = eps/2.d0 tol1 = 1.d0 + eps if( tol1 .gt. 1.d0 ) go to 10 ! ------- ! initialization ! ------- a = pmin b = pmax call getdvel(a,fa) call getdvel(b,fb) ! ----- ! begin step ! ----- 20 c = a fc = fa d = b - a e = d 30 if( dabs(fc) .ge. dabs(fb) )go to 40 a = b b = c c = a fa = fb fb = fc fc = fa ! -------- ! convergence test ! -------- 40 tol1 = 2.d0*eps*dabs(b) + 0.5d0*tol xm = 0.5d0*(c - b) if( dabs(xm) .le. tol1 ) go to 90 if( fb .eq. 0.d0 ) go to 90 ! ------------ ! is bisection necessary? ! ------------ if( dabs(e) .lt. tol1 ) go to 70 if( dabs(fa) .le. dabs(fb) ) go to 70 ! ------------------ ! is quadratic interpolation possible? ! ------------------ if( a .ne. c ) go to 50 ! ---------- ! linear interpolation ! ---------- s = fb/fa p = 2.d0*xm*s q = 1.d0 - s go to 60 ! ---------------- ! inverse quadratic interpolation ! ---------------- 50 q = fa/fc r = fb/fc s = fb/fa p = s*(2.d0*xm*q*(q - r) - (b - a)*(r - 1.d0)) q = (q - 1.d0)*(r - 1.d0)*(s - 1.d0) ! ------ ! adjust signs ! ------ 60 if( p .gt. 0.d0 ) q = -q p = dabs(p) ! -------------- ! is interpolation acceptable? ! -------------- if( (2.d0*p) .ge. (3.d0*xm*q-dabs(tol1*q)) ) go to 70 if( p .ge. dabs(0.5d0*e*q) ) go to 70 e = d d = p/q go to 80 ! ----- ! bisection ! ----- 70 d = xm e = d ! ------- ! complete step ! ------- 80 a = b fa = fb if( dabs(d) .gt. tol1 ) b = b+d if( dabs(d) .le. tol1 ) b = b+dsign(tol1,xm) call getdvel(b,fb) if( (fb*(fc/dabs(fc))) .gt. 0.d0) go to 20 go to 30 ! -- ! done ! -- 90 ps = b return end subroutine getp ! --------- !n name: g e t v e l ! --------- !p purpose: !p compute the flow velocity behind a rarefaction or shock in terms of the !p post-wave pressure for a given state ahead the wave in a relativistic !p flow ! !c comments: !c this routine closely follows the expressions in Marti and Mueller, !c J. fluid mech., (1994) subroutine getvel( p, rhoa, pa, ha, csa, vela, wa, s, & rho, u, h, cs, vel, vshock ) implicit none ! ----- ! arguments ! ----- double precision, intent(in) :: p, rhoa, pa, ha, csa, vela, wa character(len=1), intent(in) :: s double precision, intent(out) :: rho, u, h, cs, vel, vshock ! ------- ! common blocks ! ------- double precision gamma common /adind/ gamma ! --------- ! internal variables ! --------- double precision a, b, c, sign double precision j, wshock double precision k, sqgl1 ! --------------- ! left or right propagating wave ! --------------- sign = 0.d0 if (s.eq.'l') sign = -1.d0 if (s.eq.'r') sign = 1.d0 ! if (p.gt.pa) then ! --- ! shock ! --- a = 1.d0+(gamma-1.d0)*(pa-p)/gamma/p b = 1.d0-a c = ha*(pa-p)/rhoa-ha**2 ! ---------------- ! check for unphysical enthalpies ! ---------------- if (c.gt.(b**2/4.d0/a)) then print*,'getvel: unphysical specific enthalpy in intermediate state' return endif ! ----------------------------- ! specific enthalpy in the post-wave state ! (from the equation of state and the taub adiabat, ! eq.(74), mm94) ! ----------------------------- h = (-b+dsqrt(b**2-4.d0*a*c))/2.d0/a ! --------------- ! density in the post-wave state ! (from eq.(73), mm94) ! --------------- rho = gamma*p/(gamma-1.d0)/(h-1.d0) ! ------------------------ ! specific internal energy in the post-wave state ! (from the equation of state) ! ------------------------ u = p/(gamma-1.d0)/rho ! -------------------------- ! mass flux across the wave ! (from the rankine-hugoniot relations, eq.(71), mm94) ! -------------------------- j = sign*dsqrt((p-pa)/(ha/rhoa-h/rho)) ! ---------- ! shock velocity ! (from eq.(86), mm94 ! ---------- a = j**2+(rhoa*wa)**2 b = -vela*rhoa**2*wa**2 vshock = (-b+sign*j**2*dsqrt(1.d0+rhoa**2/j**2))/a wshock = 1.d0/dsqrt(1.d0-vshock**2) ! ------------------- ! flow velocity in the post-shock state ! (from eq.(67), mm94) ! ------------------- a = wshock*(p-pa)/j+ha*wa*vela b = ha*wa+(p-pa)*(wshock*vela/j+1.d0/rhoa/wa) vel = a/b ! --------------------- ! local sound speed in the post-shock state ! (from the equation of state) ! --------------------- cs = dsqrt(gamma*p/rho/h) else ! ------ ! rarefaction ! ------ ! --------------------------- ! politropic constant of the gas across the rarefaction ! --------------------------- k = pa/rhoa**gamma ! --------------- ! density behind the rarefaction ! --------------- rho = (p/k)**(1.d0/gamma) ! ------------------------ ! specific internal energy behind the rarefaction ! (from the equation of state) ! ------------------------ u = p/(gamma-1.d0)/rho ! -------------------- ! local sound speed behind the rarefaction ! (from the equation of state) ! -------------------- cs = dsqrt(gamma*p/(rho+gamma*p/(gamma-1.d0))) ! ------------------ ! flow velocity behind the rarefaction ! ------------------ sqgl1 = dsqrt(gamma-1.d0) a = (1.d0+vela)/(1.d0-vela)*((sqgl1+csa)/(sqgl1-csa)* & (sqgl1-cs )/(sqgl1+cs ))**(-sign*2.d0/sqgl1) vel = (a-1.d0)/(a+1.d0) end if end subroutine getvel ! -------- !n name: r a r e f ! -------- !p purpose: !p compute the flow state in a rarefaction for given pre-wave state ! !c comments: !c this routine closely follows the expressions in marti and mueller, !c j. fluid mech., (1994) subroutine raref( xi, rhoa, csa, vela, s, rho, p, u, vel ) implicit none ! ----- ! arguments ! ----- double precision, intent(in) :: xi double precision, intent(in) :: rhoa, csa, vela character, intent(in) :: s double precision, intent(out) :: rho, p, u, vel ! ------- ! common blocks ! ------- double precision gamma common /adind/ gamma ! --------- ! internal variables ! --------- double precision b, c, d, k, l, v, ocs2, fcs2, dfdcs2, cs2, sign ! --------------- ! left or right propagating wave ! --------------- sign = 0.d0 if (s.eq.'l') sign = 1.d0 if (s.eq.'r') sign = -1.d0 b = dsqrt(gamma - 1.d0) c = (b + csa)/(b - csa) d = -sign*b/2.d0 k = (1.d0 + xi)/(1.d0 - xi) l = c*k**d v = ((1.d0 - vela)/(1.d0 + vela))**d ocs2 = csa 25 fcs2 = l*v*(1.d0 + sign*ocs2)**d*(ocs2 - b) + (1.d0 - sign*ocs2)**d*(ocs2 + b) dfdcs2 = l*v*(1.d0 + sign*ocs2)**d* & (1.d0 + sign*d*(ocs2 - b)/(1.d0 + sign*ocs2)) + & (1.d0 - sign*ocs2)**d* & (1.d0 - sign*d*(ocs2 + b)/(1.d0 - sign*ocs2)) cs2 = ocs2 - fcs2/dfdcs2 if (abs(cs2 - ocs2)/ocs2.gt.5.e-7)then ocs2 = cs2 goto 25 end if vel = (xi + sign*cs2)/(1.d0 + sign*xi*cs2) rho = rhoa*((cs2**2*(gamma - 1.d0 - csa**2))/ & (csa**2*(gamma - 1.d0 - cs2**2)))**(1.d0/(gamma - 1.d0)) p = cs2**2*(gamma - 1.d0)*rho/(gamma - 1.d0 - cs2**2)/gamma u = p/(gamma - 1.d0)/rho return end subroutine raref end module shock_sr splash/src/write_pixmap.f90000644 000766 000000 00000037143 13261626263 016613 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! module containing output routines for writing pixel map ! to output file in various formats ! ! (c) D. Price 21/09/07 ! Added read routines June 2009 !----------------------------------------------------------------- module write_pixmap use filenames, only:fileprefix,tagline implicit none logical, public :: iwritepixmap = .false. logical, public :: ireadpixmap = .false. character(len=5), public :: pixmapformat = ' ' character(len=5), public :: readpixformat = ' ' public :: isoutputformat,writepixmap,write_pixmap_ppm public :: isinputformat,readpixmap private contains !----------------------------------------------------------------- ! utility to check if an output format selection is valid !----------------------------------------------------------------- logical function isoutputformat(string) implicit none character(len=*), intent(in) :: string isoutputformat = .false. select case(trim(string)) case('ascii','ppm') isoutputformat = .true. end select if (.not.isoutputformat) then print "(a)",' possible formats for -o option: ' print "(a)",' -o ppm : dump pixel map to ppm file' print "(a)",' -o ascii : dump pixel map to ascii file' print "(a)",' use -p to change the prefix for the filenames' endif return end function isoutputformat !----------------------------------------------------------------- ! utility to check if an input format selection is valid !----------------------------------------------------------------- logical function isinputformat(string) implicit none character(len=*), intent(in) :: string isinputformat = .false. select case(trim(string)) case('ascii','ftn','ftn512','chf') isinputformat = .true. end select if (.not.isinputformat) then print "(a)",' possible formats for -readpix option: ' print "(a)",' -readpix ascii : read pixel maps from ascii file' print "(a)",' -readpix ftn : read pixel maps from unformatted fortran file "read(1) dat(:,:)"' print "(a)",' -readpix ftn512 : read pixel maps from unformatted fortran file "read(1) dat(1:512,1:512)"' endif return end function isinputformat !----------------------------------------------------------------- ! wrapper routine for all output formats !----------------------------------------------------------------- subroutine writepixmap(datpix,npixx,npixy,xmin,ymin,dx,datmin,datmax,label,labu,istep,xsec,dumpfile) implicit none integer, intent(in) :: npixx,npixy real, intent(in), dimension(npixx,npixy) :: datpix real, intent(in) :: xmin,ymin,dx,datmin,datmax logical, intent(in) :: xsec character(len=*), intent(in) :: label,labu,dumpfile integer, intent(in) :: istep select case(trim(pixmapformat)) case('ascii') call write_pixmap_ascii(datpix,npixx,npixy,xmin,ymin,dx,datmin,datmax,label,labu,istep,xsec,dumpfile) case('ppm') call write_pixmap_ppm(datpix,npixx,npixy,xmin,ymin,dx,datmin,datmax,label,istep) ! case('fits') ! call write_pixmap_fits(datpix,npixx,npixy,xmin,ymin,dx,datmin,datmax,label) case default print "(a)",' ERROR: invalid output format for pixel map ' end select end subroutine writepixmap !----------------------------------------------------------------- ! output pixmap as an ascii file !----------------------------------------------------------------- subroutine write_pixmap_ascii(datpix,npixx,npixy,xmin,ymin,dx,datmin,datmax,label,labu,istep,xsec,dumpfile) use labels, only:shortlabel implicit none integer, intent(in) :: npixx,npixy,istep real, intent(in), dimension(npixx,npixy) :: datpix real, intent(in) :: xmin,ymin,dx,datmin,datmax logical, intent(in) :: xsec character(len=*), intent(in) :: label,labu,dumpfile character(len=10) :: stringx,stringy character(len=30) :: fmtstring character(len=len(dumpfile)+10) :: filename integer :: ierr,j integer, parameter :: iunit = 166 ! !--write ascii file ! !write(filename,"(a,i5.5,a)") trim(fileprefix)//'_',istep,'.dat' call get_pixmap_filename(filename,dumpfile,shortlabel(label,labu),'.pix',xsec) open(unit=iunit,file=filename,status='replace',form='formatted',iostat=ierr) if (ierr /=0) then print*,'error opening '//trim(filename) return endif write(*,"(a)",ADVANCE='NO') '> writing pixel map to file '//trim(filename)//' ...' write(stringx,"(i10)") npixx write(stringy,"(i10)") npixy write(iunit,"(a)",err=66) '# '//trim(adjustl(filename))//' created by '//trim(tagline) write(iunit,"(a)",err=66) '# Contains 2D pixel array '//trim(adjustl(stringx))//' x '//trim(adjustl(stringy))//' written as ' write(iunit,"(a)",err=66) '# do j=1,'//trim(adjustl(stringy)) write(iunit,"(a)",err=66) '# write(*,*) dat(1:'//trim(adjustl(stringx))//',j)' write(iunit,"(a)",err=66) '# enddo' write(iunit,"(a,1pe14.6,a,1pe14.6)",err=66) '# '//trim(label)//': min = ',datmin,' max = ',datmax write(iunit,"(a,1pe14.6,a,1pe14.6)",err=66) '# x axis: min = ',xmin,' max = ',xmin+(npixx-1)*dx write(iunit,"(a,1pe14.6,a,1pe14.6)",err=66) '# y axis: min = ',ymin,' max = ',ymin+(npixy-1)*dx write(iunit,"(a)",err=66) '# '//trim(adjustl(stringx))//' '//trim(adjustl(stringy)) write(fmtstring,"(a,i6,a)",iostat=ierr) '(',npixx,'(1pe14.6))' if (ierr /= 0) then do j=1,npixy write(iunit,*,err=66) datpix(1:npixx,j) enddo else do j=1,npixy write(iunit,fmtstring,err=66) datpix(1:npixx,j) enddo endif close(iunit) print "(a)",'OK' return 66 continue print "(a)",' ERROR during write ' close(iunit) return end subroutine write_pixmap_ascii !----------------------------------------------------------------- ! output pixmap as a raw .ppm file !----------------------------------------------------------------- subroutine write_pixmap_ppm(datpix,npixx,npixy,xmin,ymin,dx,datmin,datmax,label,istep,brightness) use colours, only:rgbtable,ncolours implicit none integer, intent(in) :: npixx,npixy real, intent(in), dimension(npixx,npixy) :: datpix real, intent(in), dimension(npixx,npixy), optional :: brightness real, intent(in) :: xmin,ymin,dx,datmin,datmax character(len=*), intent(in) :: label integer, intent(in) :: istep character(len=120) :: filename real, dimension(3) :: rgbi,drgb real :: dati,ddatrange,datfraci,ftable integer :: ipix,jpix,ir,ib,ig,ierr,maxcolour,indexi integer, parameter :: iunit = 167 ! !--check for errors ! if (abs(datmax-datmin).gt.tiny(datmin)) then ddatrange = 1./abs(datmax-datmin) else print "(a)",'error: datmin=datmax : pointless writing ppm file' return endif ! !--write PPM-- ! write(filename,"(a,i5.5,a)") trim(fileprefix)//'_',istep,'.ppm' open(unit=iunit,file=filename,status='replace',form='formatted',iostat=ierr) if (ierr /=0) then print*,'error opening ppm file' return endif write(*,"(a)",ADVANCE='NO') '> writing pixel map to file '//trim(filename)//' ...' ! !--PPM header ! maxcolour = 255 write(iunit,"(a)",err=66) 'P3' write(iunit,"(a)",err=66) '# '//trim(adjustl(filename))//' created by '//trim(tagline) write(iunit,"(a,1pe14.6,a,1pe14.6)",err=66) '# '//trim(label)//': min = ',datmin,' max = ',datmax write(iunit,"(a,1pe14.6,a,1pe14.6)",err=66) '# x axis: min = ',xmin,' max = ',xmin+(npixx-1)*dx write(iunit,"(a,1pe14.6,a,1pe14.6)",err=66) '# y axis: min = ',ymin,' max = ',ymin+(npixy-1)*dx write(iunit,"(i4,1x,i4)",err=66) npixx, npixy write(iunit,"(i3)",err=66) maxcolour !--pixel information do jpix = npixy,1,-1 do ipix = 1,npixx dati = datpix(ipix,jpix) datfraci = (dati - datmin)*ddatrange datfraci = max(datfraci,0.) datfraci = min(datfraci,1.) !--define colour for current particle ftable = datfraci*ncolours indexi = int(ftable) + 1 indexi = min(indexi,ncolours) if (indexi.lt.ncolours) then !--do linear interpolation from colour table drgb(:) = rgbtable(:,indexi+1) - rgbtable(:,indexi) rgbi(:) = rgbtable(:,indexi) + (ftable - int(ftable))*drgb(:) else rgbi(:) = rgbtable(:,indexi) endif if (present(brightness)) then rgbi(:) = rgbi(:)*min(brightness(ipix,jpix),1.0) endif ir = max(min(int(rgbi(1)*maxcolour),maxcolour),0) ig = max(min(int(rgbi(2)*maxcolour),maxcolour),0) ib = max(min(int(rgbi(3)*maxcolour),maxcolour),0) write(iunit,"(i3,1x,i3,1x,i3,2x)",err=66) ir,ig,ib enddo enddo close(unit=iunit) print "(a)",'OK' return 66 continue print "(a)",' ERROR during write ' close(iunit) return end subroutine write_pixmap_ppm !----------------------------------------------------------------- ! read in pixels from file !----------------------------------------------------------------- subroutine readpixmap(datpix,npixx,npixy,dumpfile,label,istep,xsec,ierr) use asciiutils, only:nheaderlines implicit none real, intent(out), dimension(:,:), allocatable :: datpix integer, intent(out) :: npixx,npixy,ierr character(len=*), intent(in) :: dumpfile character(len=*), intent(in) :: label integer, intent(in) :: istep logical, intent(in) :: xsec integer :: i,j,nheader,nerr integer, parameter :: iunit = 168 character(len=128) :: filename character(len=2) :: char logical :: iexist ierr = 0 select case(trim(adjustl(readpixformat))) case('ascii') ! splash pixmap output files call check_for_pixmap_files(filename,dumpfile,label,'.pix',istep,xsec,iexist) if (.not.iexist) then print "('*',a,'*',/,72('*'))",' Create a file with one of these names (or a soft link) and try again ' ierr = 1 return endif open(unit=iunit,file=filename,status='old',form='formatted',iostat=ierr) if (ierr /=0) then print*,'error opening '//trim(filename) return else npixx = 0 npixy = 0 nheader = nheaderlines(iunit) rewind(iunit) do i=1,nheader-1 read(iunit,*,iostat=ierr) enddo read(iunit,*,iostat=ierr) char,npixx,npixy if (ierr /= 0 .or. npixx.le.0 .or. npixy.le.0) then print*,'ERROR reading size of pixel map, got nx = ',npixx,' ny = ',npixy,& ', skipped ',nheader,' header lines' else print "(a,i5,a,i5,a)",' reading',npixx,' x',npixy,' pixel map from '//trim(filename) endif allocate(datpix(npixx,npixy),stat=ierr) if (ierr /= 0) then print "(a)",' ERROR allocating memory for pixel map' close(iunit) return endif nerr = 0 do j=1,npixy read(iunit,*,iostat=ierr) datpix(1:npixx,j) if (ierr /= 0) nerr = nerr + 1 enddo if (nerr /= 0) print "(a,i3,a,i3)",' WARNING: ',nerr,' errors reading pixel map from '//trim(filename)//' on unit ',iunit close(iunit) endif case('ftn','ftn512','chf') ! Christoph Federrath files npixx = 512 npixy = 512 ! !--cycle through possible filenames ! call check_for_pixmap_files(filename,dumpfile,label,'.pix',istep,xsec,iexist) if (.not.iexist) then print "('*',a,'*',/,72('*'))",' Create a file with one of these names (or a soft link) and try again ' ierr = 1 return endif open(unit=iunit,file=filename,status='old',form='unformatted',iostat=ierr) if (ierr /= 0) then print "(a)",' ERROR: cannot open '//trim(filename) ierr = 2 return else print "(a)",' reading pixel map from '//trim(filename) allocate(datpix(npixx,npixy),stat=ierr) read(iunit,iostat=ierr) datpix if (ierr /= 0) print "(a,i3)",' WARNING: ERRORS reading pixel map from '//trim(filename)//' on unit ',iunit close(iunit) endif case default if (len_trim(readpixformat).le.0) then print "(a)",' ERROR: pixel format not set prior to read_pixmap call' else print "(a)",' ERROR: unknown pixmap format '//trim(adjustl(readpixformat)) endif ierr = 1 end select end subroutine readpixmap !----------------------------------------------------------------- ! look for pixel map files matching a variety of naming schemes !----------------------------------------------------------------- subroutine get_pixmap_filename(filename,dumpfile,label,ext,xsec) use asciiutils, only:basename,safename character(len=*), intent(out) :: filename character(len=*), intent(in) :: dumpfile,label,ext logical, intent(in) :: xsec if (xsec) then filename = trim(basename(dumpfile))//'_'//trim(safename(label))//'_slice'//trim(ext) else filename = trim(basename(dumpfile))//'_'//trim(safename(label))//'_proj'//trim(ext) endif end subroutine get_pixmap_filename !----------------------------------------------------------------- ! look for pixel map files matching a variety of naming schemes !----------------------------------------------------------------- subroutine check_for_pixmap_files(filename,dumpfile,label,ext,istep,xsec,iexist) use asciiutils, only:safename,basename implicit none character(len=*), intent(out) :: filename character(len=*), intent(in) :: dumpfile,label,ext integer, intent(in) :: istep logical, intent(in) :: xsec logical, intent(out) :: iexist character(len=len(dumpfile)) :: dumpfilei integer :: i,maxnames logical :: printinfo dumpfilei = dumpfile iexist = .false. i = 0 maxnames = 5 printinfo = .false. do while (.not.iexist .and. i.lt.maxnames) i = i + 1 select case(i) case(1) if (xsec) then filename = trim(dumpfilei)//'_'//trim(safename(label))//'_slice'//trim(ext) else filename = trim(dumpfilei)//'_'//trim(safename(label))//'_proj'//trim(ext) endif case(2) filename = trim(dumpfilei)//'_'//trim(safename(label))//trim(ext) case(3) if (xsec) then filename = trim(dumpfilei)//'_slice'//trim(ext) else filename = trim(dumpfilei)//'_proj'//trim(ext) endif case(4) filename = trim(dumpfilei)//trim(ext) case(5) write(filename,"(a,i5.5,a)") trim(fileprefix)//'_',istep,trim(ext) end select ! !--query to see if file exists ! if (printinfo) then print "('*',a,'*')",' no file '//filename(1:60)//'' else inquire(file=filename,exist=iexist) endif if (.not.iexist) then ! !--try the same files again but in the current directory ! instead of the directory in which the dump files are located ! if (i.eq.maxnames) then if (len_trim(dumpfilei).ne.len_trim(basename(dumpfile))) then i = 0 dumpfilei = basename(dumpfile) elseif (.not.printinfo) then print "(72('*'),/,'*',a,12x,'*')", & ' ERROR: could not find any '//trim(ext)//' files with matching names:' dumpfilei = dumpfile printinfo = .true. i = 0 endif endif endif enddo end subroutine check_for_pixmap_files end module write_pixmap splash/src/read_data_flash_hdf5_utils.c000644 000766 000000 00000024267 13261626263 021141 0ustar00dpricewheel000000 000000 /* * This subroutine performs the calls to the HDF5 library for the FLASH * tracer particles data read * * Easier to do it this way and link with c than to try to link against * the Fortran interface (in the latter case the modules must * have been compiled with the *exact* compiler used to compile splash * which is a real pain). * */ #include #include #include #include void read_flash_hdf5_header(char *filename, float *time, int *npart, int *ncol, int *ierr) { hid_t file_id; hid_t dataset_id; hid_t dataspace_id; herr_t status; herr_t HDF5_error = -1; //printf(" opening %s \n",filename); file_id = H5Fopen(filename,H5F_ACC_RDONLY,H5P_DEFAULT); if (file_id == HDF5_error) { printf("ERROR opening %s \n",filename); *ierr = 1; return; } // READ NPART AND NCOL from file dataset_id = H5Dopen(file_id,"tracer particles"); if (dataset_id == HDF5_error) { printf("ERROR opening tracer particle data set \n"); *ierr = 2; return; } dataspace_id = H5Dget_space(dataset_id); // get dimensional information from dataspace hsize_t HDFxdims[4], HDFmaxdims[4]; int rank = H5Sget_simple_extent_dims(dataspace_id, HDFxdims, HDFmaxdims); if (rank > 4) { printf("RANK of dataset exceeds array bounds \n"); *ierr = 3; return; } // from the dimensional info, calculate the size of the buffer. *npart = HDFxdims[0]; *ncol = HDFxdims[1]; //printf(" number of particles %i \n",HDFxdims[0]); //printf(" number of columns = %i \n",HDFxdims[1]); status = H5Sclose(dataspace_id); if (status == HDF5_error) { printf("ERROR closing dataspace \n"); *ierr = 4; } status = H5Dclose(dataset_id); if (status == HDF5_error) { printf("ERROR closing dataset \n"); *ierr = 4; } /* * read the time from the file - this is * contained in a pointlessly complicated * compound structure that we have to replicate here * */ dataset_id = H5Dopen(file_id,"real scalars"); if (dataset_id == HDF5_error) { printf("ERROR opening real scalars data set for time \n"); *ierr = 5; return; } dataspace_id = H5Dget_space(dataset_id); rank = H5Sget_simple_extent_dims(dataspace_id, HDFxdims, HDFmaxdims); if (rank > 4) { printf("RANK of dataset exceeds array bounds \n"); *ierr = 3; return; } int lenheader = HDFxdims[0]; //printf(" header contains %i items \n",lenheader); typedef struct h_t { char name[80]; double value; } h_t; h_t header[lenheader]; hid_t strtype = H5Tcopy(H5T_C_S1); status = H5Tset_size (strtype, 80); hid_t compound_id = H5Tcreate(H5T_COMPOUND, sizeof(h_t)); H5Tinsert(compound_id,"name",HOFFSET(h_t, name), strtype); H5Tinsert(compound_id,"value",HOFFSET(h_t, value), H5T_NATIVE_DOUBLE); status = H5Dread(dataset_id,compound_id,H5S_ALL,H5S_ALL,H5P_DEFAULT,header); if (status == HDF5_error) { printf("ERROR reading header \n"); *ierr = 6; } *time = header[0].value; status = H5Sclose(dataspace_id); if (status == HDF5_error) { printf("ERROR closing dataspace \n"); *ierr = 4; } status = H5Dclose(dataset_id); if (status == HDF5_error) { printf("ERROR closing dataset \n"); *ierr = 4; } // Additionally check if an SPH_density data set is present if (checkfordataset(file_id,"SPH_density")==1) { printf(" file contains SPH-calculated densities \n"); *ncol += 1; } else { printf(" file does not contain SPH_density data set \n"); } status = H5Fclose( file_id ); if (status == HDF5_error) { printf("ERROR closing file \n"); *ierr = 7; } } void read_flash_hdf5_data(char *filename, int *npart, int *ncol, int *isrequired, int *ierr) { hid_t file_id; hid_t dataset_id, SPHdataset_id; hid_t dataspace_id, SPHdataspace_id; hid_t memspace_id; herr_t status; herr_t HDF5_error = -1; //printf(" re-opening %s \n",filename); file_id = H5Fopen(filename,H5F_ACC_RDONLY,H5P_DEFAULT); if (file_id == HDF5_error) { printf("ERROR re-opening %s \n",filename); *ierr = 1; return; } // re-open tracer particle dataspace dataset_id = H5Dopen(file_id,"tracer particles"); if (dataset_id == HDF5_error) { printf("ERROR opening tracer particle data set \n"); *ierr = 2; return; } dataspace_id = H5Dget_space(dataset_id); // Additionally check if an SPH_density data set is present int ncolloop; int gotSPHdata = checkfordataset(file_id,"SPH_density"); if (gotSPHdata==0) { //printf(" file does not contain SPH_density data set \n"); ncolloop = *ncol; } else { //printf(" file contains SPH-calculated densities \n"); ncolloop = *ncol - 1; } // make a temporary space to put each column as we read it hsize_t nparth[1]; nparth[0] = *npart; memspace_id = H5Screate_simple(1,nparth,NULL); // dynamically allocate a temporary double array to store one column double* temp = 0; temp = malloc(*npart*sizeof(double)); // read particle information into one column hsize_t offset[2], count[2]; int i; count[0] = *npart; count[1] = 1; /* * read particle IDs first so we can sort into ID order */ offset[0] = 0; offset[1] = 5; // ID in column 5: should check this but this is for the future status = H5Sselect_hyperslab(dataspace_id, H5S_SELECT_SET, offset, NULL, count, NULL); if (status == HDF5_error) { printf("ERROR creating hyperslab \n"); *ierr = 4; } if (!H5Sselect_valid(dataspace_id)) { printf("ERROR selecting hyperslab \n"); *ierr = 5; } // read ID H5Dread(dataset_id,H5T_NATIVE_DOUBLE,memspace_id,dataspace_id,H5P_DEFAULT,temp); int* tempid = 0; tempid = malloc(*npart*sizeof(int)); // convert temp (double) into integer array for (i=0;i<*npart;i++) { tempid[i] = (int)temp[i]; } /* * start loop from 1 because first array is a useless "tag" array that * we don't need to read. * */ for (i=1;i> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- !--short module storing units information module oilonwaterread use params implicit none real(doub_prec) :: udisti,umassi,utimei end module oilonwaterread subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation use labels, only:ix,ivx,ih,irho,ipmass use oilonwaterread, only:udisti,umassi,utimei implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname integer, parameter :: maxptmass = 1000 real, parameter :: pi=3.141592653589 integer :: i,j,k,ifile,ierr,ipart integer :: npart_max,nstep_max,ncolstep,npart,nunknown logical :: iexist,doubleprec character(len=len(rootname)+10) :: dumpfile integer :: nprint, n1, n2, nptmass, nstepsalloc integer :: npartoil, npartwater integer, dimension(:), allocatable :: isteps, iphase !--use these lines if dump is double precision real(doub_prec), dimension(:,:), allocatable :: dattemp real(doub_prec) :: timei, gammai real(doub_prec) :: rhozero, RK2 real(doub_prec) :: escap,tkin,tgrav,tterm real(doub_prec) :: dtmax !--use these lines for single precision real :: timesi, gammasi real :: rhozeros, RK2s real :: escaps,tkins,tgravs,tterms real :: dtmaxs nstepsread = 0 nstep_max = 0 npart_max = maxpart ifile = 1 dumpfile = trim(rootname) ! !--check if data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 3 ndimV = 3 ncolstep = 18 ! number of columns in file ncolumns = ncolstep ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,1) j = indexstart nstepsread = 0 doubleprec = .false. print "(1x,a)",'reading oil-on-water code format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the (unformatted) binary file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='unformatted') if (ierr /= 0) then print "(a)",'*** ERROR OPENING '//trim(dumpfile)//' ***' else ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,end=55,iostat=ierr) udisti,umassi,utimei,nprint,n1,n2,timei,gammai,rhozero,RK2 print*,'npart = ',nprint if (.not.allocated(dat) .or. nprint.gt.npart_max) then npart_max = max(npart_max,INT(1.1*nprint)) call alloc(npart_max,nstep_max,ncolstep+ncalc,mixedtypes=.true.) endif doubleprec = .true. !--try single precision if non-sensible values for time, gamma etc. if (ierr.ne.0 .or. timei.lt.0. .or. timei.gt.1e30 & .or. gammai.lt.1. .or. gammai.gt.10. & .or. rhozero.lt.0. .or. RK2.lt.0. .or. RK2.gt.1.e10) then doubleprec = .false. endif rewind(15) endif call set_labels if (ierr /= 0) then print "(a)",'*** ERROR READING TIMESTEP HEADER ***' else ! !--loop over the timesteps in this file ! over_steps_in_file: do npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then if (nstepsread.ge.2) then nstepsalloc = 2*nstepsread else nstepsalloc = j endif call alloc(maxpart,nstepsalloc,maxcol,mixedtypes=.true.) endif ! !--allocate integer arrays required for data read ! if (allocated(isteps)) deallocate(isteps) allocate(isteps(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' if (allocated(iphase)) deallocate(iphase) allocate(iphase(npart_max),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' ! !--now read the timestep data in the dumpfile ! write(*,"(a,i5,a)",advance="no") '| step ',j,': ' if (doubleprec) then print "(a)",'double precision dump' ! !--allocate a temporary array for (double precision) variables ! if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(npart_max,ncolstep),stat=ierr) if (ierr /= 0) print*,'not enough memory in read_data' read(15,end=55,iostat=ierr) udisti, umassi, utimei, & nprint, n1, n2, timei, gammai, rhozero, RK2, & (dattemp(i,ih), i=1, nprint),escap, tkin, tgrav, tterm, & ((dattemp(i,k), i=1, nprint),k=1,ih-1), & ((dattemp(i,k), i=1, nprint),k=ih+1,11), & dtmax, & (isteps(i), i=1,nprint), & ((dattemp(i,k), i=1, nprint),k=12,18), & nptmass, & ((dattemp(i,k), i=nprint+1,nprint+nptmass),k=1,6), & (dattemp(i,ipmass), i=nprint+1,nprint+nptmass), & (dattemp(i,ih), i=nprint+1,nprint+nptmass), & (isteps(i), i=nprint+1,nprint+nptmass), & (iphase(i), i=1,nprint) else print "(a)",'single precision dump' read(15,end=55,iostat=ierr) udisti, umassi, utimei, & nprint, n1, n2, timesi, gammasi, rhozeros, RK2s, & (dat(i,ih,j), i=1, nprint),escaps, tkins, tgravs, tterms, & ((dat(i,k,j), i=1, nprint),k=1,ih-1), & ((dat(i,k,j), i=1, nprint),k=ih+1,11), & dtmaxs, & (isteps(i),i=1, nprint), & ((dat(i,k,j), i=1, nprint),k=12,18), & nptmass, & ((dat(i,k,j), i=nprint+1,nprint+nptmass),k=1,6), & (dat(i,ipmass,j), i=nprint+1,nprint+nptmass), & (dat(i,ih,j), i=nprint+1,nprint+nptmass), & (isteps(i), i=nprint+1,nprint+nptmass), & (iphase(i), i=1,nprint) endif ! !--extract time and gamma ! if (doubleprec) then gamma(j) = real(gammai) time(j) = real(timei) else gamma(j) = gammasi time(j) = timesi endif print*,' time = ',time(j),' gamma = ',gamma(j) npart = nprint+nptmass ! !--convert to single precision if necessary ! if (doubleprec) then dat(1:npart,1:ncolstep,j) = real(dattemp(1:npart,1:ncolstep)) endif ! !--set particle types using iphase ! ipart = 0 npartoil = 0 npartwater = 0 nunknown = 0 do i=1,nprint select case(iphase(i)) case(0) npartoil = npartoil + 1 iamtype(i,j) = 1 case(1) npartwater = npartwater + 1 iamtype(i,j) = 2 case default nunknown = nunknown + 1 iamtype(i,j) = 4 end select enddo ! !--set particle type for point masses ! if (nptmass.gt.0) then iamtype(nprint+1:nprint+nptmass,j) = 3 endif if (nunknown.gt.0) print *,nunknown,' particles of unknown type (probably dead)' if (allocated(dattemp)) deallocate(dattemp) if (allocated(isteps)) deallocate(isteps) if (allocated(iphase)) deallocate(iphase) npartoftype(1,j) = npartoil npartoftype(2,j) = npartwater npartoftype(3,j) = nptmass npartoftype(4,j) = nunknown print "(1x,3(a,i10))",' n(oil) = ',npartoil,' n(water) = ',npartwater,' nptmass = ',nptmass if (ierr /= 0) then print "(a)",'*** INCOMPLETE DATA ***' nstepsread = nstepsread + 1 exit over_steps_in_file else nstepsread = nstepsread + 1 endif j = j + 1 enddo over_steps_in_file endif 55 continue ! !--reached end of file ! close(15) if (j-1 .gt. 0) then print*,'>> end of dump file: nsteps =',j-1 endif return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use physcon, only:solarrcgs,solarmcgs use settings_data use geometry, only:labelcoord use oilonwaterread, only:udisti,umassi,utimei use settings_units, only:units,unitzintegration implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ih = 7 ! smoothing length iutherm = 8 ! thermal energy ipmass = 9 ! particle mass irho = 10 ! location of rho in data array if (ncolumns.gt.10) then label(11) = 'dgrav' label(12) = 'torque t' label(13) = 'torque g' label(14) = 'torque p' label(15) = 'torque v' label(16) = 'torque c' label(17) = 'hecomp' label(18) = 'potential energy' endif label(ix(1:ndim)) = labelcoord(1:ndim,1) do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo label(irho) = 'density' label(iutherm) = 'u' label(ih) = 'h' label(ipmass) = 'particle mass' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo ! !--set transformation factors between code units/real units ! units(1:3) = udisti/solarrcgs unitslabel(1:3) = ' [R\d\(2281)\u]' units(4:6) = udisti/utimei unitslabel(4:6) = ' [cm/s]' units(ih) = units(1) unitslabel(ih) = unitslabel(1) units(iutherm) = (udisti/utimei)**2 unitslabel(iutherm) = ' [erg/g]' units(ipmass) = umassi/solarmcgs unitslabel(ipmass) = ' [M\d\(2281)\u]' units(irho) = umassi/udisti**3 unitslabel(irho) = ' [g/cm\u3\d]' !--unit for z integration - leave this as cm to get g/cm^2 in column density unitzintegration = udisti labelzintegration = ' [cm]' !--time units for legend units(0) = utimei/3.1536e7 unitslabel(0) = ' yrs' ! !--set labels for each particle type ! ntypes = 4 labeltype(1) = 'gas (oil)' labeltype(2) = 'gas (water)' labeltype(3) = 'point mass' labeltype(4) = 'unknown' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. UseTypeInRenderings(4) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/colourparts.f90000644 000766 000000 00000003157 13261626263 016456 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- module colourparts implicit none contains subroutine colour_particles(dat,datmin,datmax,icolour,npart) use plotlib, only: plot_qcir implicit none integer, intent(in) :: npart real, dimension(npart), intent(in) :: dat real, intent(in) :: datmin, datmax integer, dimension(npart), intent(out) :: icolour integer :: i,icolourmin,icolourmax,icolourtemp real :: dx call plot_qcir(icolourmin,icolourmax) dx = (datmax - datmin)/real(icolourmax - icolourmin) do i=1,npart icolourtemp = int((dat(i) - datmin)/dx) + icolourmin if (icolourtemp.gt.icolourmax) icolourtemp = icolourmax if (icolourtemp.lt.icolourmin) icolourtemp = icolourmin icolour(i) = icolourtemp enddo return end subroutine colour_particles end module colourparts splash/src/transform.f90000644 000766 000000 00000047553 13261626263 016124 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------ ! ! module containing subroutines for applying transformations to data ! prior to plotting. ! ! the transformations are: ! ! 1) log10(x) ! 2) |x| ! 3) 1/x ! 4) sqrt(x) ! 5) x^2 ! 6) ln(x) ! ! * combinations of transformations are done when the ! input number is > 10 (e.g. 321 means 1/x, then abs, then log10) ! ! subroutines contained within this module are the following: ! ! transform : applies transformation to a one dimensional array ! transform2 : applies the transformation to a two dimensional array ! transform_limits : transforms the plot limits appropriately ! transform_label : changes the plot label appropriately ! ! Written for use in supersphplot/splash by ! Daniel Price, Institute of Astronomy, Cambridge, UK 2002-2004 ! University of Exeter, UK 2004- ! dprice@astro.ex.ac.uk ! !------------------------------------------------------------------------ module transforms integer, parameter, public :: ntrans = 6 ! this is the number of different transformations real, parameter, private :: zerolog = 1.e-12 ! this is minimum set if xmin = 0 and log public :: transform,transform_inverse,trans public :: transform_limits,transform_limits_inverse,transform_label public :: convert_to_ln_fac public :: islogged private interface transform module procedure transform,transform_limits,transform2,transforma end interface transform !--function interface interface trans module procedure transformarray end interface trans interface transform_inverse module procedure transform_inverse,transform_limits_inverse,transforma_inverse end interface transform_inverse contains !------------------------------------------------------------------------ ! ! subroutine returns log, 1/x of a given array ! ! * can specify up to 9 individual operations to perform ! * combinations of transformations are done when the ! input number is > 10 (e.g. 321 means 1/x, then abs, then log10) ! !------------------------------------------------------------------------ subroutine transform(array,itrans,errval) implicit none integer, intent(in) :: itrans real, dimension(:), intent(inout) :: array real, intent(in), optional :: errval real, dimension(size(array)) :: arraytemp real :: errvali character(len=20) :: string integer :: i ! !--errval is the value to be set for errors ! (default is zero if not present) ! if (present(errval)) then errvali = errval else errvali = 0. endif ! !--extract the digits from the input number ! if (itrans.gt.0) then write(string,*) itrans ! !--do a transformation for each digit ! arraytemp = array do i=1,len_trim(string) ! !--perform transformation appropriate to this digit ! select case(string(i:i)) case('1') where (arraytemp > 0. .and. arraytemp.ne.errvali) arraytemp = log10(arraytemp) elsewhere arraytemp = errvali end where case('2') where (arraytemp.ne.errvali) arraytemp = abs(arraytemp) end where case('3') where (arraytemp .ne. 0. .and. arraytemp.ne.errvali) arraytemp = 1./arraytemp elsewhere arraytemp = errvali end where case('4') where (arraytemp .gt. 0. .and. arraytemp.ne.errvali) arraytemp = sqrt(arraytemp) elsewhere arraytemp = errvali end where case('5') where (arraytemp.ne.errvali) arraytemp = arraytemp**2 end where case('6') where (arraytemp > 0. .and. arraytemp.ne.errvali) arraytemp = log(arraytemp) elsewhere arraytemp = errvali end where end select enddo array = arraytemp endif end subroutine transform !------------------------------------------------------------------------ ! ! interface to above for a single real number ! !------------------------------------------------------------------------ subroutine transforma(aa,itrans,errval) implicit none integer, intent(in) :: itrans real, intent(inout) :: aa real, intent(in), optional :: errval real, dimension(1) :: array array(1) = aa if (present(errval)) then call transform(array,itrans,errval=errval) else call transform(array,itrans) endif aa = array(1) return end subroutine transforma !------------------------------------------------------------------------ ! ! function interface: returns array valued function ! !------------------------------------------------------------------------ function transformarray(array,itrans,errval) implicit none integer, intent(in) :: itrans real, intent(in), dimension(:) :: array real, dimension(size(array)) :: transformarray real, intent(in), optional :: errval transformarray = array if (present(errval)) then call transform(transformarray,itrans,errval=errval) else call transform(transformarray,itrans) endif return end function transformarray !------------------------------------------------------------------------ ! ! inverse transform ! !------------------------------------------------------------------------ subroutine transform_inverse(array,itrans,errval) implicit none integer, intent(in) :: itrans real, dimension(:), intent(inout) :: array real, intent(in), optional :: errval real, dimension(size(array)) :: arraytemp real :: errvali character(len=20) :: string integer :: i ! !--errval is the value to be set for errors ! (default is zero if not present) ! if (present(errval)) then errvali = errval else errvali = 0. endif ! !--extract the digits from the input number ! if (itrans.gt.0) then write(string,*) itrans ! !--do a transformation for each digit ! arraytemp = array do i=len_trim(string),1,-1 ! !--perform transformation appropriate to this digit ! select case(string(i:i)) case('1') where (arraytemp.ne.errvali) arraytemp = 10**arraytemp end where case('3') where (arraytemp .ne. 0. .and. arraytemp.ne.errvali) arraytemp = 1./arraytemp elsewhere arraytemp = errvali end where case('4') where (arraytemp.ne.errvali) arraytemp = arraytemp**2 end where case('5') where (arraytemp .gt. 0. .and. arraytemp.ne.errvali) arraytemp = sqrt(arraytemp) elsewhere arraytemp = errvali end where case('6') where (arraytemp.ne.errvali) arraytemp = exp(arraytemp) end where end select enddo array = arraytemp endif end subroutine transform_inverse !------------------------------------------------------------------------ ! ! interface to above for a single real number ! !------------------------------------------------------------------------ subroutine transforma_inverse(aa,itrans,errval) implicit none integer, intent(in) :: itrans real, intent(inout) :: aa real, intent(in), optional :: errval real, dimension(1) :: array array(1) = aa if (present(errval)) then call transform_inverse(array,itrans,errval=errval) else call transform_inverse(array,itrans) endif aa = array(1) return end subroutine transforma_inverse !------------------------------------------------------------------------ ! ! same as transform but for a two dimensional array ! applies the transformation to the same array as was input ! !------------------------------------------------------------------------ subroutine transform2(array,itrans,errval) implicit none integer, intent(in) :: itrans real, dimension(:,:), intent(inout) :: array real, intent(in), optional :: errval real, dimension(size(array(:,1)),size(array(1,:))) :: arraytemp real :: errvali character(len=20) :: string integer :: i ! !--errval is the value to be set for errors ! (default is zero if not present) ! if (present(errval)) then errvali = errval else errvali = 0. endif ! !--extract the digits from the input number ! if (itrans.gt.0) then write(string,*) itrans ! !--do a transformation for each digit ! arraytemp = array do i=1,len_trim(string) ! !--perform transformation appropriate to this digit ! select case(string(i:i)) case('1') where (arraytemp > 0. .and. arraytemp.ne.errvali) arraytemp = log10(arraytemp) elsewhere arraytemp = errvali end where case('2') where (arraytemp.ne.errvali) arraytemp = abs(arraytemp) end where case('3') where (arraytemp .ne. 0. .and. arraytemp.ne.errvali) arraytemp = 1./arraytemp elsewhere arraytemp = errvali end where case('4') where (arraytemp .gt. 0. .and. arraytemp.ne.errvali) arraytemp = sqrt(arraytemp) elsewhere arraytemp = errvali end where case('5') where (arraytemp.ne.errvali) arraytemp = arraytemp**2 end where case('6') where (arraytemp > 0. .and. arraytemp.ne.errvali) arraytemp = log(arraytemp) elsewhere arraytemp = errvali end where end select enddo array = arraytemp endif end subroutine transform2 !------------------------------------------------------------------------ ! ! same as transform but for the plot limits ! (min can become max and vice versa) ! !------------------------------------------------------------------------ subroutine transform_limits(xmin,xmax,itrans) implicit none integer, intent(in) :: itrans real, intent(inout) :: xmin,xmax real :: xmintemp,xmaxtemp character(len=20) :: string integer :: i ! !--extract the digits from the input number ! if (itrans.gt.0) then write(string,*) itrans ! !--do a transformation for each digit ! xmintemp = xmin xmaxtemp = xmax do i=1,len_trim(string) ! !--perform transformation appropriate to this digit ! select case(string(i:i)) case('1') if (xmintemp > 0) then xmintemp = log10(xmintemp) elseif (xmintemp.eq.0) then print*,' log10(xmin = 0): min set to ',zerolog xmintemp = log10(zerolog) endif if (xmaxtemp > 0) then xmaxtemp = log10(xmaxtemp) elseif (xmaxtemp.eq.0) then print*,' log10(xmax = 0): max set to ',zerolog xmaxtemp = log10(zerolog) endif case('2') if ((xmintemp.lt.0. .and. xmaxtemp.gt.0.) & .or.(xmaxtemp.lt.0. .and. xmintemp.gt.0.)) then ! !--minimum is zero if limits have opposite signs ! xmaxtemp = max(abs(xmintemp),abs(xmaxtemp)) xmintemp = 0. else ! !--or just take magnitude ! xmintemp = abs(xmintemp) xmaxtemp = abs(xmaxtemp) endif case('3') if (xmintemp .ne. 0) then xmintemp = 1./xmintemp else xmintemp = 0. endif if (xmaxtemp .ne. 0) then xmaxtemp = 1./xmaxtemp else xmaxtemp = 0. endif case('4') if (xmintemp .ge. 0) then xmintemp = sqrt(xmintemp) else xmintemp = 0. endif if (xmaxtemp .ge. 0) then xmaxtemp = sqrt(xmaxtemp) else xmaxtemp = 0. endif case('5') xmintemp = xmintemp**2 xmaxtemp = xmaxtemp**2 case('6') if (xmintemp > 0) then xmintemp = log(xmintemp) elseif (xmintemp.eq.0) then print*,' ln(xmin = 0): min set to ',zerolog xmintemp = log(zerolog) endif if (xmaxtemp > 0) then xmaxtemp = log(xmaxtemp) elseif (xmaxtemp.eq.0) then print*,' ln(xmax = 0): max set to ',zerolog xmaxtemp = log(zerolog) endif end select enddo xmin = min(xmintemp,xmaxtemp) xmax = max(xmintemp,xmaxtemp) endif end subroutine transform_limits !------------------------------------------------------------------------ ! ! inverse transform for the plot limits ! (so that we can change the transformed limits and set the ! untransformed limits accordingly) ! !------------------------------------------------------------------------ subroutine transform_limits_inverse(xmin,xmax,itrans) implicit none integer, intent(in) :: itrans real, intent(inout) :: xmin,xmax real :: xmintemp,xmaxtemp,xtemp character(len=20) :: string integer :: i ! !--extract the digits from the input number ! if (itrans.gt.0) then write(string,*) itrans ! !--do a transformation for each digit ! xmintemp = xmin xmaxtemp = xmax do i=len_trim(string),1,-1 ! do digits in reverse ! !--perform transformation appropriate to this digit ! select case(string(i:i)) case('1') xmintemp = 10**xmintemp xmaxtemp = 10**xmaxtemp case('2') ! !--if minimum is zero give limits opposite signs ! (but same magnitude), otherwise do nothing ! if (xmintemp.eq.0.) then xtemp = max(abs(xmintemp),abs(xmaxtemp)) xmintemp = -xtemp xmaxtemp = xtemp endif case('3') if (xmintemp .ne. 0) then xmintemp = 1./xmintemp else xmintemp = 0. endif if (xmaxtemp .ne. 0) then xmaxtemp = 1./xmaxtemp else xmaxtemp = 0. endif case('4') xmintemp = xmintemp**2 xmaxtemp = xmaxtemp**2 case('5') if (xmintemp.gt.0) then xmintemp = sqrt(xmintemp) else xmintemp = 0. endif if (xmaxtemp.gt.0) then xmaxtemp = sqrt(xmaxtemp) else xmaxtemp = 0. endif case('6') xmintemp = exp(xmintemp) xmaxtemp = exp(xmaxtemp) end select enddo xmin = min(xmintemp,xmaxtemp) xmax = max(xmintemp,xmaxtemp) endif end subroutine transform_limits_inverse !------------------------------------------------------------------------ ! ! function to adjust the label of a plot if log, 1/x etc ! ! Note: *cannot* put print or write statements into this function ! as it is used in the middle of write or print statements ! this means that finding the digits is a bit trickier ! !------------------------------------------------------------------------ function transform_label(label,itrans) implicit none integer, intent(in) :: itrans character(len=*), intent(in) :: label integer :: itransmulti,i,ndigits integer, dimension(5) :: digit character(len=len(label)+20) :: transform_label character(len=len(label)+20) :: temp_label ! !--extract the digits from the input number ! if (itrans.gt.0) then call get_digits(itrans,digit,ndigits) temp_label = label ! !--do a transformation for each digit ! do i=1,ndigits itransmulti = digit(i) ! !--perform transformation appropriate to this digit ! select case(itransmulti) case(1) temp_label = 'log '//trim(temp_label) case(2) temp_label = '|'//trim(temp_label)//'|' case(3) temp_label = '1/'//trim(temp_label) case(4) temp_label = 'sqrt('//trim(temp_label)//')' case(5) temp_label = trim(temp_label)//'\u2\d' case(6) temp_label = 'ln '//trim(temp_label) case default temp_label = trim(temp_label) end select enddo transform_label = temp_label else transform_label = label endif end function transform_label !------------------------------------------------------------------------ ! get_digits: for an integer i returns number of digits it contains ! and a list of these *without* using write statements ! ! i : integer to split into digits ! nmax : dimensions of digits array ! digits(nmax) : array of digits ! ndigits : number of digits in i !------------------------------------------------------------------------ subroutine get_digits(i,digits,ndigits) implicit none integer, intent(in) :: i integer, intent(out) :: ndigits integer, intent(out), dimension(:) :: digits integer :: j,isubtract,idigit ndigits = 0 isubtract = 0 do j=size(digits),0,-1 if (i.ge.10**j) then ndigits = ndigits + 1 idigit = (i - isubtract)/10**j digits(ndigits) = idigit isubtract = isubtract + digits(ndigits)*10**j endif enddo end subroutine get_digits !------------------------------------------------------------------------ ! function returning the correction factor by which to multiply ! to convert the log in the transform to a natural log !------------------------------------------------------------------------ real function convert_to_ln_fac(itrans) implicit none integer, intent(in) :: itrans character(len=20) :: string integer :: i real :: xtemp ! !--default conversion factor is unity ! xtemp = 1.0 ! !--extract the digits from the input number ! if (itrans.gt.0) then write(string,*) itrans do i=len_trim(string),1,-1 ! do digits in reverse ! !--perform transformation appropriate to this digit ! select case(string(i:i)) ! !--correction factor is ln(10.) but can be ! 1./ln(10.), sqrt(1./ln(10.), etc. depending ! on other transformations ! case('1') xtemp = log(10.)*xtemp case('2') xtemp = abs(xtemp) case('3') xtemp = 1./xtemp case('4') xtemp = sqrt(xtemp) case('5') xtemp = xtemp**2 !case('6') ! xtemp = xtemp end select enddo endif convert_to_ln_fac = xtemp end function convert_to_ln_fac !--------------------------------------- ! query function to return whether the ! transformation involves a log or not !--------------------------------------- logical function islogged(itrans) implicit none integer, intent(in) :: itrans character(len=20) :: string write(string,"(i8)") itrans islogged = (index(string,'1').ne.0 .or. index(string,'6').ne.0) end function islogged end module transforms splash/src/interpolate3D_proj_geom.F90000644 000766 000000 00000047107 13261626263 020622 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2018 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! ! Module containing routines required for 3D projections ! in different coordinate systems ! !---------------------------------------------------------------------- module projections3Dgeom use projections3D, only:setup_integratedkernel,wfromtable,coltable use kernels, only:radkernel,radkernel2 use geometry, only:igeom_cartesian,coord_transform,coord_is_length, & coord_transform_limits,igeom_cylindrical,& get_coord_limits,coord_is_periodic implicit none public :: interpolate3D_proj_geom, interpolate3D_xsec_geom ! public :: interpolate3D_proj_geom_vec #ifdef _OPENMP character(len=5), parameter :: str = 'cpu s' #else character(len=1), parameter :: str = 's' #endif contains !-------------------------------------------------------------------------- ! subroutine to interpolate from particle data to even grid of pixels ! ! The data is smoothed using the SPH summation interpolant, ! that is, we compute the smoothed array according to ! ! datsmooth(pixel) = sum_b weight_b dat_b W(r-r_b, h_b) ! ! where _b is the quantity at the neighbouring particle b and ! W is the smoothing kernel, for which we use the usual cubic spline ! ! ** This version is for 3D projections in alternative coordinate ! ** systems, e.g. \Int rho d\phi ! ! The (dimensionless) weight for each particle should be ! ! weight = pmass/(rho*h^3) ! ! the interface is written in this form to avoid floating exceptions ! on physically scaled data. ! ! Input: particle coordinates : x,y,z (npart) ! smoothing lengths : hh (npart) ! weight for each particle : weight (npart) ! scalar data to smooth : dat (npart) ! ! Output: smoothed data : datsmooth (npixx,npixy) ! ! Written by Daniel Price July 2011 !-------------------------------------------------------------------------- subroutine interpolate3D_proj_geom(x,y,z,hh,weight,dat,itype,npart, & xmin,ymin,datsmooth,npixx,npixy,pixwidthx,pixwidthy,normalise,igeom,& iplotx,iploty,iplotz,ix,xorigin) use timing, only:wall_time,print_time implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,z,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy real, intent(out), dimension(npixx,npixy) :: datsmooth logical, intent(inout) :: normalise integer, intent(in) :: igeom,iplotx,iploty,iplotz integer, dimension(3), intent(in) :: ix real, dimension(3), intent(in) :: xorigin real, dimension(npixx,npixy) :: datnorm real, parameter :: pi = 3.1415926536 integer :: ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax,ip,jp integer :: ixcoord,iycoord,izcoord,ierr,ncpus integer :: iprintinterval, iprintnext, itmin #ifdef _OPENMP integer :: omp_get_num_threads,i #else integer(kind=selected_int_kind(10)) :: iprogress,i ! up to 10 digits #endif real, dimension(3) :: xcoord, xpix real, dimension(3), save :: xci, xi !$omp threadprivate(xci,xi) real :: hi,hi1,hi21,radkern,wab,q2,xminpix,yminpix real :: term,termnorm,dx,dx2,dy,dy2,dz real :: xmax,ymax,hmin,horigi real :: t_start,t_end,t_used,tsec logical :: iprintprogress,islengthx,islengthy,islengthz character(len=64) :: string datsmooth = 0. term = 0. string = 'projecting' if (normalise) then string = trim(string)//' (normalised, non-cartesian)' datnorm = 0. else string = trim(string)//' (non-cartesian)' endif ncpus = 0 !$omp parallel !$omp master !$ ncpus = omp_get_num_threads() !$omp end master !$omp end parallel if (ncpus > 0) then write (*,"(1x,a,': ',i4,' x ',i4,' on ',i3,' cpus')") trim(string),npixx,npixy,ncpus else write (*,"(1x,a,': ',i4,' x ',i4)") trim(string),npixx,npixy endif if (pixwidthx.le.0. .or. pixwidthy.le.0) then print "(1x,a)",'interpolate3D_proj_geom: error: pixel width <= 0' return endif if (any(hh(1:npart).le.0.)) then print*,'interpolate3D_proj_geom: warning: ignoring some or all particles with h <= 0' endif ! !--get information about the coordinates ! call get_coord_info(iplotx,iploty,iplotz,ix(1),igeom,ixcoord,iycoord,izcoord,& islengthx,islengthy,islengthz,ierr) if (ierr /= 0) return ! !--if z coordinate is not a length, use normalised interpolation ! (e.g. azimuthally averaged density) ! if (.not.islengthz) normalise = .true. ! !--check column density table has actually been setup ! if (abs(coltable(1)).le.1.e-5) then call setup_integratedkernel endif ! !--print a progress report if it is going to take a long time ! (a "long time" is, however, somewhat system dependent) ! iprintprogress = (npart .ge. 100000) .or. (npixx*npixy .gt.100000) ! !--loop over particles ! iprintinterval = 25 if (npart.ge.1e6) iprintinterval = 10 iprintnext = iprintinterval ! !--get starting CPU time ! call wall_time(t_start) xminpix = xmin - 0.5*pixwidthx yminpix = ymin - 0.5*pixwidthy xmax = xmin + npixx*pixwidthx ymax = ymin + npixy*pixwidthy ! !--use a minimum smoothing length on the grid to make ! sure that particles contribute to at least one pixel ! hmin = 0. if (islengthx) hmin = 0.5*pixwidthx if (islengthy) hmin = max(hmin,0.5*pixwidthy) !$omp parallel default(none) & !$omp shared(hh,z,x,y,weight,dat,itype,datsmooth,npart,hmin) & !$omp shared(xmin,ymin,xmax,ymax,xminpix,yminpix,pixwidthx,pixwidthy) & !$omp shared(npixx,npixy,ixcoord,iycoord,izcoord,islengthx,islengthy,islengthz,igeom) & !$omp shared(datnorm,normalise,radkernel,radkernel2,xorigin) & !$omp private(hi,horigi,radkern) & !$omp private(hi1,hi21,term,termnorm) & !$omp private(q2,dx,dx2,dy,dy2,dz,wab,xcoord,xpix) & !$omp private(i,ipix,jpix,ip,jp,ipixmin,ipixmax,jpixmin,jpixmax,ierr) !$omp do schedule (guided, 2) over_particles: do i=1,npart ! !--report on progress ! #ifndef _OPENMP if (iprintprogress) then iprogress = 100*i/npart if (iprogress.ge.iprintnext) then write(*,"('(',i3,'% -',i12,' particles done)')") iprogress,i iprintnext = iprintnext + iprintinterval endif endif #endif ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_particles ! !--set h related quantities ! horigi = hh(i) if (horigi.le.0.) cycle over_particles hi = max(horigi,hmin) radkern = radkernel*hi ! radius of the smoothing kernel ! !--get limits of contribution from particle in cartesian space ! xci(1) = x(i) + xorigin(1) ! xci = position in cartesians xci(2) = y(i) + xorigin(2) xci(3) = z(i) + xorigin(3) call get_pixel_limits(xci,xi,radkern,ipixmin,ipixmax,jpixmin,jpixmax,igeom,& npixx,npixy,pixwidthx,pixwidthy,xmin,ymin,ixcoord,iycoord,ierr) if (ierr /= 0) cycle over_particles ! !--set kernel related quantities ! hi1 = 1./hi hi21 = hi1*hi1 ! h gives the z length scale (NB: no perspective) if (islengthz) then termnorm = weight(i)*horigi elseif (igeom.eq.igeom_cylindrical) then termnorm = weight(i)*atan(radkern/xi(ixcoord))/pi else termnorm = weight(i) endif term = termnorm*dat(i) ! !--loop over pixels, adding the contribution from this particle ! if (islengthz) then xcoord(izcoord) = xi(izcoord) ! assume all pixels at same r as particlefor theta-phi else xcoord(izcoord) = 0. ! use phi=0 so get x = r cos(phi) = r endif do jpix = jpixmin,jpixmax jp = jpix if (jp < 1) jp = jp + npixy if (jp > npixy) jp = jp - npixy xcoord(iycoord) = yminpix + jp*pixwidthy do ipix = ipixmin,ipixmax ip = ipix if (ip < 1) ip = ip + npixx if (ip > npixx) ip = ip - npixx xcoord(ixcoord) = xminpix + ip*pixwidthx !--now transform to get location of pixel in cartesians call coord_transform(xcoord,3,igeom,xpix,3,igeom_cartesian) !--find distances using cartesians and perform interpolation dy = xpix(iycoord) - xci(iycoord) dx = xpix(ixcoord) - xci(ixcoord) dz = xpix(izcoord) - xci(izcoord) ! z dir important if surface not flat (e.g. r slice) dx2 = dx*dx dy2 = dy*dy q2 = (dx2 + dy2 +dz*dz)*hi21 ! !--SPH kernel - integral through cubic spline ! interpolate from a pre-calculated table ! if (q2.lt.radkernel2) then wab = wfromtable(q2) ! !--calculate data value at this pixel using the summation interpolant ! !$omp atomic datsmooth(ip,jp) = datsmooth(ip,jp) + term*wab if (normalise) then !$omp atomic datnorm(ip,jp) = datnorm(ip,jp) + termnorm*wab endif endif !endif enddo enddo enddo over_particles !$omp end do !$omp end parallel ! !--normalise dat array ! if (normalise) then !--normalise everywhere (required if not using SPH weighting) where (datnorm > tiny(datnorm)) datsmooth = datsmooth/datnorm end where endif ! !--get/print timings ! call wall_time(t_end) t_used = t_end - t_start if (t_used > 5.) call print_time(t_used) end subroutine interpolate3D_proj_geom !-------------------------------------------------------------------------- ! ! ** In this version 3D data is interpolated to a single 2D cross section ! ! ** Note that the cross section is always taken in the z co-ordinate ! ** so should submit the appropriate arrays as x, y and z. ! ! Input: particle coordinates : x1,x2,x3 (npart) ! particle masses : pmass (npart) ! density on particles : rho (npart) - must be computed separately ! smoothing lengths : hh (npart) - could be computed from density ! scalar data to smooth : dat (npart) ! cross section location: zslice ! ! Output: smoothed data : datsmooth (npixx,npixy) ! ! Daniel Price, Monash University 2nd May 2017 !-------------------------------------------------------------------------- subroutine interpolate3D_xsec_geom(x,y,z,hh,weight,dat,itype,npart,& xmin,ymin,zslice,datsmooth,npixx,npixy,pixwidthx,pixwidthy,normalise,igeom, & iplotx,iploty,iplotz,ix,xorigin) use kernels, only:cnormk3D,wfunc implicit none integer, intent(in) :: npart,npixx,npixy real, intent(in), dimension(npart) :: x,y,z,hh,weight,dat integer, intent(in), dimension(npart) :: itype real, intent(in) :: xmin,ymin,pixwidthx,pixwidthy,zslice real, intent(out), dimension(npixx,npixy) :: datsmooth logical, intent(in) :: normalise integer, intent(in) :: igeom,iplotx,iploty,iplotz integer, dimension(3), intent(in) :: ix real, dimension(3), intent(in) :: xorigin real, dimension(npixx,npixy) :: datnorm integer :: i,ipix,jpix,ipixmin,ipixmax,jpixmin,jpixmax,ip,jp integer :: ixcoord,iycoord,izcoord,ierr real :: hi,hi1,radkern,q2,wab,const,hi21 real :: termnorm,term,dx,dx2,dy,dy2,dz,dz2 real :: xmax,ymax,xminpix,yminpix real, dimension(3) :: xcoord,xpix,xci,xi logical :: islengthx,islengthy,islengthz datsmooth = 0. datnorm = 0. if (normalise) then print*,'taking fast cross section (normalised, non-cartesian)...',zslice else print*,'taking fast cross section (non-cartesian)...',zslice endif if (pixwidthx.le.0. .or. pixwidthy.le.0.) then print*,'interpolate3D_xsec: error: pixel width <= 0' return elseif (npart.le.0) then print*,'interpolate3D_xsec: error: npart = 0' return endif if (any(hh(1:npart).le.tiny(hh))) then print*,'interpolate3D_xsec_geom: WARNING: ignoring some or all particles with h < 0' endif const = cnormk3D ! !--get information about the coordinates ! call get_coord_info(iplotx,iploty,iplotz,ix(1),igeom,ixcoord,iycoord,izcoord,& islengthx,islengthy,islengthz,ierr) if (ierr /= 0) return ! !--if z coordinate is not a length, quit ! if (.not.islengthz) then print*,'interpolate3D_xsec_geom: ERROR xsec not implemented when z is an angle' return endif xminpix = xmin - 0.5*pixwidthx yminpix = ymin - 0.5*pixwidthy xmax = xmin + npixx*pixwidthx ymax = ymin + npixy*pixwidthy ! !--loop over particles ! over_parts: do i=1,npart ! !--skip particles with itype < 0 ! if (itype(i).lt.0) cycle over_parts ! !--set h related quantities ! hi = hh(i) if (hi.le.0.) cycle over_parts !horigi = hh(i) !if (horigi.le.0.) cycle over_parts !hi = max(horigi,hmin) radkern = radkernel*hi ! radius of the smoothing kernel ! !--set kernel related quantities ! hi1 = 1./hi hi21 = hi1*hi1 ! !--for each particle, work out distance from the cross section slice. ! dz = zslice - z(i) dz2 = dz**2 xcoord(izcoord) = 1. ! !--if this is < 2h then add the particle's contribution to the pixels ! otherwise skip all this and start on the next particle ! if (dz2 .lt. radkernel2) then ! !--get limits of contribution from particle in cartesian space ! xci(1) = x(i) + xorigin(1) xci(2) = y(i) + xorigin(2) xci(3) = z(i) + xorigin(3) call get_pixel_limits(xci,xi,radkern,ipixmin,ipixmax,jpixmin,jpixmax,igeom,& npixx,npixy,pixwidthx,pixwidthy,xmin,ymin,ixcoord,iycoord,ierr) if (ierr /= 0) cycle over_parts termnorm = const*weight(i) term = termnorm*dat(i) !/rescalefac ! !--loop over pixels, adding the contribution from this particle ! do jpix = jpixmin,jpixmax jp = jpix if (jp < 1) jp = jp + npixy if (jp > npixy) jp = jp - npixy xcoord(iycoord) = yminpix + jp*pixwidthy do ipix = ipixmin,ipixmax ip = ipix if (ip < 1) ip = ip + npixx if (ip > npixx) ip = ip - npixx xcoord(ixcoord) = xminpix + ip*pixwidthx !--now transform to get location of pixel in cartesians call coord_transform(xcoord,3,igeom,xpix,3,igeom_cartesian) !--find distances using cartesians and perform interpolation dy = xpix(iycoord) - xci(iycoord) dx = xpix(ixcoord) - xci(ixcoord) dx2 = dx*dx dy2 = dy*dy q2 = (dx*dx + dy*dy + dz2)*hi1*hi1 ! !--SPH kernel - integral through cubic spline ! interpolate from a pre-calculated table ! if (q2.lt.radkernel2) then wab = wfunc(q2) ! !--calculate data value at this pixel using the summation interpolant ! datsmooth(ip,jp) = datsmooth(ip,jp) + term*wab if (normalise) then datnorm(ip,jp) = datnorm(ip,jp) + termnorm*wab endif endif enddo enddo endif ! if particle within 2h of slice enddo over_parts ! over particles ! !--normalise dat array ! if (normalise) then !--normalise everywhere (required if not using SPH weighting) where (datnorm > tiny(datnorm)) datsmooth = datsmooth/datnorm end where endif !datsmooth = datsmooth*rescalefac return end subroutine interpolate3D_xsec_geom !-------------------------------------------------------------------------- ! ! utility routine for use in above routines ! !-------------------------------------------------------------------------- subroutine get_coord_info(iplotx,iploty,iplotz,ix1,igeom,ixcoord,iycoord,izcoord,& islengthx,islengthy,islengthz,ierr) integer, intent(in) :: iplotx,iploty,iplotz,ix1,igeom integer, intent(out) :: ixcoord,iycoord,izcoord,ierr logical, intent(out) :: islengthx,islengthy,islengthz ierr = 0 ! !--get plotted coordinates in range 1->ndim ! ixcoord = iplotx - ix1 + 1 iycoord = iploty - ix1 + 1 izcoord = iplotz - ix1 + 1 if (ixcoord.le.0 .or. ixcoord.gt.3) then print*,' ERROR finding x coordinate offset, cannot render' ierr = 1 endif if (iycoord.le.0 .or. iycoord.gt.3) then print*,' ERROR finding y coordinate offset, cannot render' ierr = 2 endif if (izcoord.le.0 .or. izcoord.gt.3) then print*,' ERROR finding y coordinate offset, cannot render' ierr = 3 endif ! !--check if coordinate is a length (i.e., not an angle) ! islengthx = coord_is_length(ixcoord,igeom) islengthy = coord_is_length(iycoord,igeom) islengthz = coord_is_length(izcoord,igeom) end subroutine get_coord_info !-------------------------------------------------------------------------- ! ! utility routine for use in above routines ! IN: ! xci - coordinates of particle in transformed space (e.g. r, phi, z) ! radkern - radius of kernel (e.g. 2h) ! OUT: ! xi - cartesian coordinates of particle ! ipixmin,ipixmax,jpixmin,jpixmax - pixel limits ! !-------------------------------------------------------------------------- subroutine get_pixel_limits(xci,xi,radkern,ipixmin,ipixmax,jpixmin,jpixmax,igeom,& npixx,npixy,pixwidthx,pixwidthy,xmin,ymin,ixcoord,iycoord,ierr) real, intent(in) :: xci(3),radkern,pixwidthx,pixwidthy,xmin,ymin real, intent(out) :: xi(3) integer, intent(out) :: ipixmin,ipixmax,jpixmin,jpixmax,ierr integer, intent(in) :: igeom,npixx,npixy,ixcoord,iycoord real :: xpixmin(3),xpixmax(3) ierr = 0 ! !--get limits of rendering in new coordinate system ! call get_coord_limits(radkern,xci,xi,xpixmin,xpixmax,igeom) ! !--now work out contributions to pixels in the the transformed space ! ipixmax = int((xpixmax(ixcoord) - xmin)/pixwidthx)+1 if (ipixmax.lt.1) ierr = 1 jpixmax = int((xpixmax(iycoord) - ymin)/pixwidthy)+1 if (jpixmax.lt.1) ierr = 2 ipixmin = int((xpixmin(ixcoord) - xmin)/pixwidthx) if (ipixmin.gt.npixx) ierr = 3 jpixmin = int((xpixmin(iycoord) - ymin)/pixwidthy) if (jpixmin.gt.npixy) ierr = 4 if (.not.coord_is_periodic(ixcoord,igeom)) then if (ipixmin.lt.1) ipixmin = 1 ! make sure they only contribute if (ipixmax.gt.npixx) ipixmax = npixx ! to pixels in the image endif if (.not.coord_is_periodic(iycoord,igeom)) then if (jpixmin.lt.1) jpixmin = 1 ! (note that this optimises if (jpixmax.gt.npixy) jpixmax = npixy ! much better than using min/max) endif end subroutine get_pixel_limits end module projections3Dgeom splash/src/allocate.f90000644 000766 000000 00000022041 13261626263 015656 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module mem_allocation implicit none contains !---------------------------------------------------------------------------- ! ! memory allocation/reallocation for main data arrays ! ! the global parameters maxpart, maxstep and maxcol are set to ! the dimensions allocated ! !---------------------------------------------------------------------------- subroutine alloc(npartin,nstep,ncolumnsin,mixedtypes) use particle_data implicit none integer, intent(in) :: npartin,nstep,ncolumnsin logical, intent(in), optional :: mixedtypes integer :: maxpartold,maxstepold,maxcolold integer :: ierr,ncolumns logical :: reallocate,reallocate_part,reallocate_step,reallocate_itype integer, dimension(:), allocatable :: icolourmetemp integer(kind=int1), dimension(:,:), allocatable :: iamtypetemp integer, dimension(:,:), allocatable :: npartoftypetemp real, dimension(:,:), allocatable :: masstypetemp real, dimension(:), allocatable :: timetemp, gammatemp real, dimension(:,:,:), allocatable :: dattemp ! !--check for errors in input ! if (npartin.le.0) then print*,'allocate: error in input, npartin = ',npartin return endif if (nstep.le.0) then print*,'allocate: error in input, nstep = ',nstep return endif if (ncolumnsin.lt.0) then print*,'allocate: error in input, ncolumns = ',ncolumnsin return elseif (ncolumnsin.eq.0) then print*,'WARNING: allocate: ncolumns = 0 in input' endif !--do nothing if array sizes are the same if (npartin.eq.maxpart .and. ncolumnsin.eq.maxcol .and. nstep.eq.maxstep) then return endif ! !--save array sizes ! if (npartin.lt.maxpart) print "(a)",' WARNING: # particles < previous in allocate' if (nstep.lt.maxstep) print "(a)",' WARNING: # steps < previous in allocate' !--at the moment ncolumns cannot be decreased (due to calc_quantities) if (ncolumnsin.lt.maxcol) then ncolumns = maxcol !!print "(a)",' WARNING: # columns < previous in allocate' else ncolumns = ncolumnsin endif maxpartold = min(maxpart,npartin) maxstepold = min(maxstep,nstep) maxcolold = min(maxcol,ncolumns) reallocate = .false. reallocate_part = .false. reallocate_step = .false. reallocate_itype = .false. ! !--if re-allocating, copy arrays to temporary versions ! ierr = 0 if (allocated(dat)) then reallocate = .true. if (maxpart.ne.npartin) reallocate_part = .true. if (maxstep.ne.nstep) reallocate_step = .true. print 10,'> reallocating memory:',npartin,nstep,ncolumns 10 format (a,' parts = ',i10,' steps = ',i6,' cols = ',i4) allocate(dattemp(maxpartold,maxcolold,maxstepold), stat=ierr) if (ierr /= 0) stop 'error allocating memory (dattemp)' if (reallocate_part) then allocate(icolourmetemp(maxpartold),stat=ierr) if (ierr /= 0) stop 'error allocating memory (icolourmetemp)' icolourmetemp(1:maxpartold) = icolourme(1:maxpartold) deallocate(icolourme) endif dattemp = dat deallocate(dat) if (allocated(iamtype)) then ! should always be true !--if iamtype has meaningful contents and reallocation is necessary reallocate_itype = (reallocate_part .or. reallocate_step) .and. (size(iamtype(:,1)).eq.maxpartold) if (reallocate_itype) then allocate(iamtypetemp(maxpartold,maxstepold), stat=ierr) if (ierr /= 0) stop 'error allocating memory (iamtypetemp)' iamtypetemp(1:maxpartold,1:maxstepold) = iamtype(1:maxpartold,1:maxstepold) deallocate(iamtype) elseif (present(mixedtypes)) then !--if iamtype has size 1 or 0 but should be allocated here, ! deallocate so we can give it correct size if (mixedtypes .and. size(iamtype(:,1)).lt.maxpart) deallocate(iamtype) endif endif if (reallocate_step) then allocate(npartoftypetemp(maxparttypes,maxstep),stat=ierr) if (ierr /= 0) stop 'error allocating memory (npartoftypetemp)' npartoftypetemp = npartoftype deallocate(npartoftype) allocate(masstypetemp(maxparttypes,maxstep),stat=ierr) if (ierr /= 0) stop 'error allocating memory (npartoftypetemp)' masstypetemp = masstype deallocate(masstype) allocate(timetemp(maxstep),gammatemp(maxstep),stat=ierr) if (ierr /= 0) stop 'error allocating memory (timetemp,gammatemp)' timetemp = time gammatemp = gamma deallocate(time,gamma) endif else print 10,'> allocating memory:',npartin,nstep,ncolumns maxpart = npartin maxstep = nstep maxcol = ncolumns endif maxpart = npartin maxstep = nstep maxcol = ncolumns ! !--main data array ! allocate(dat(maxpart,maxcol,maxstep), stat=ierr) if (ierr /= 0) then print*,' parts = ',maxpart,' columns = ',maxcol,' steps = ',maxstep stop 'error allocating memory for dat array' endif if (reallocate) then dat(1:maxpartold,1:maxcolold,1:maxstepold) = dattemp(1:maxpartold,1:maxcolold,1:maxstepold) deallocate(dattemp) else dat = 0. endif ! !--type array if necessary ! if (present(mixedtypes)) then if (mixedtypes .and. .not.allocated(iamtype)) then allocate(iamtype(maxpart,maxstep), stat=ierr) if (ierr /= 0) stop 'error allocating memory for type array' iamtype = 1 !--copy contents if reallocating if (reallocate_itype) then iamtype(1:maxpartold,1:maxstepold) = iamtypetemp(1:maxpartold,1:maxstepold) deallocate(iamtypetemp) endif elseif (.not.mixedtypes) then !--if called with mixedtypes explictly false, deallocate itype array if (allocated(iamtype)) deallocate(iamtype) endif elseif (reallocate_itype) then !--if called without mixedtypes, preserve contents of itype array allocate(iamtype(maxpart,maxstep), stat=ierr) if (ierr /= 0) stop 'error allocating memory for type array' iamtype = 1 iamtype(1:maxpartold,1:maxstepold) = iamtypetemp(1:maxpartold,1:maxstepold) deallocate(iamtypetemp) endif !--make sure iamtype is always allocated for safety, just with size=1 if not used if (.not.allocated(iamtype)) then allocate(iamtype(1,maxstep),stat=ierr) if (ierr /= 0) stop 'error allocating memory for type array (1)' endif ! !--particle arrays ! if (.not.allocated(icolourme) .or. reallocate_part) then allocate(icolourme(maxpart),stat=ierr) if (ierr /= 0) stop 'error allocating memory for icolourme array' icolourme = 1 if (reallocate_part) then icolourme(1:maxpartold) = icolourmetemp(1:maxpartold) deallocate(icolourmetemp) endif endif ! !--other arrays ! if (.not.allocated(npartoftype)) then allocate(npartoftype(maxparttypes,maxstep),stat=ierr) if (ierr /= 0) stop 'error allocating memory for header arrays' allocate(masstype(maxparttypes,maxstep),stat=ierr) if (ierr /= 0) stop 'error allocating memory for header arrays' allocate(time(maxstep),gamma(maxstep),stat=ierr) if (ierr /= 0) stop 'error allocating memory for header arrays' npartoftype = 0 masstype = 0. time = time_not_read_val ! initialise like this so we know if has not been read gamma = 0. if (reallocate_step) then npartoftype(:,1:maxstepold) = npartoftypetemp(:,1:maxstepold) masstype(:,1:maxstepold) = masstypetemp(:,1:maxstepold) time(1:maxstepold) = timetemp(1:maxstepold) gamma(1:maxstepold) = gammatemp(1:maxstepold) deallocate(npartoftypetemp,masstypetemp) deallocate(timetemp,gammatemp) endif endif return end subroutine alloc !----------------------------------------- ! ! deallocation of remaining memory ! (for tidiness - not strictly necessary) ! !----------------------------------------- subroutine deallocate_all use particle_data, only:dat,icolourme,iamtype,npartoftype,masstype,time,gamma implicit none if (allocated(dat)) deallocate(dat) if (allocated(icolourme)) deallocate(icolourme) if (allocated(iamtype)) deallocate(iamtype) if (allocated(npartoftype)) deallocate(npartoftype) if (allocated(masstype)) deallocate(masstype) if (allocated(time)) deallocate(time) if (allocated(gamma)) deallocate(gamma) return end subroutine deallocate_all end module mem_allocation splash/src/interpolation.f90000644 000766 000000 00000024212 13261626263 016763 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! Module containing utility routines for SPH kernel interpolation !----------------------------------------------------------------- module interpolation implicit none public :: set_interpolation_weights real, parameter, public :: weight_sink = -1. private contains !------------------------------------------------------------------- ! Set interpolation weights for the particles. The weights are ! calculated using: ! ! w = m/(rho*h**ndim), ! ! where we need to handle a few special scenarios: ! ! 1) Firstly, the weight should be calculated in a consistent ! set of units. Safest way is to use the data as originally ! read from the dump file, before any unit scaling was applied. ! ! 2) Particle weights are set to zero for particle types not ! used in the rendering. ! ! 3) If particle mass not read, it is still possible to perform ! interpolations, but not using the SPH weights. These ! interpolations *must* therefore be normalised. !------------------------------------------------------------------- subroutine set_interpolation_weights(weighti,dati,iamtypei,usetype, & ninterp,npartoftype,masstype,ntypes,ndataplots,irho,ipmass,ih,ndim, & iRescale,idensityweighted,inormalise,units,unit_interp,required, & rendersinks) use params, only:doub_prec,int1,maxplot use labels, only:get_sink_type implicit none real, dimension(:), intent(out) :: weighti real, dimension(:,:), intent(in) :: dati integer(kind=int1), dimension(:), intent(in) :: iamtypei logical, dimension(:), intent(in) :: usetype logical, dimension(0:maxplot), intent(in) :: required integer, intent(in) :: ih,irho,ipmass,ndim integer, intent(in) :: ninterp,ntypes,ndataplots integer, dimension(:), intent(in) :: npartoftype real, dimension(:), intent(in) :: masstype logical, intent(in) :: iRescale,idensityweighted,rendersinks logical, intent(inout) :: inormalise real, dimension(0:maxplot), intent(in) :: units real(doub_prec), intent(in) :: unit_interp integer :: i2,i1,itype,ipart,isinktype real(doub_prec) :: dunitspmass,dunitsrho,dunitsh ! !-- unit_interp is a multiplication factor that can ! be used to scale the "weight" in case the ! units read from the read_data routine ! are inconsistent (this is the case for the SEREN read) ! dunitspmass = 1.d0 dunitsrho = 1.d0 dunitsh = 1.d0 if (iRescale) then if (ipmass.gt.0) dunitspmass = 1.d0/units(ipmass) if (ih.gt.0) dunitsh = 1.d0/units(ih) if (irho.gt.0) dunitsrho = 1.d0/units(irho) endif dunitspmass = dunitspmass * unit_interp isinktype = get_sink_type(ntypes) if (ipmass.gt.0 .and. ipmass.le.ndataplots .and. & irho.gt.0 .and. irho.le.ndataplots .and. & ih .gt. 0 .and. ih.le.ndataplots .and. & required(ipmass) .and. required(irho) .and. required(ih)) then if (size(iamtypei) > 1) then ! !--particles with mixed types ! !$omp parallel do default(none) & !$omp shared(ninterp,iamtypei,weighti,dati,rendersinks,isinktype) & !$omp shared(usetype,idensityweighted,dunitsrho) & !$omp shared(ipmass,ih,irho,dunitspmass,dunitsh,ndim) & !$omp private(ipart,itype) do ipart=1,ninterp itype = iamtypei(ipart) if (.not.usetype(itype)) then if (rendersinks .and. itype.eq.isinktype) then weighti(ipart) = weight_sink else weighti(ipart) = 0. endif elseif (idensityweighted) then if (dati(ipart,ih) > tiny(dati)) then weighti(ipart) = (dati(ipart,ipmass)*dunitspmass)/ & ((dati(ipart,ih)*dunitsh)**ndim) else weighti(ipart) = 0. endif else if (dati(ipart,irho) > tiny(dati) .and. dati(ipart,ih) > tiny(dati)) then weighti(ipart) = (dati(ipart,ipmass)*dunitspmass)/ & ((dati(ipart,irho)*dunitsrho)*(dati(ipart,ih)*dunitsh)**ndim) else weighti(ipart) = 0. endif endif enddo !$omp end parallel do else ! !--particles ordered by type ! i2 = 0 over_types: do itype=1,ntypes i1 = i2 + 1 i2 = i2 + npartoftype(itype) i2 = min(i2,ninterp) if (i1 > i2) cycle over_types !--set weights to zero for particle types not used in the rendering if (.not.usetype(itype)) then if (rendersinks .and. itype.eq.isinktype) then weighti(i1:i2) = weight_sink else weighti(i1:i2) = 0. endif elseif (idensityweighted) then !--for density weighted interpolation use m/h**ndim where(dati(i1:i2,ih) > tiny(dati)) weighti(i1:i2) = (dati(i1:i2,ipmass)*dunitspmass)/ & ((dati(i1:i2,ih)*dunitsh)**ndim) elsewhere weighti(i1:i2) = 0. endwhere else !--usual interpolation use m/(rho h**ndim) where(dati(i1:i2,irho) > tiny(dati) .and. dati(i1:i2,ih) > tiny(dati)) weighti(i1:i2) = (dati(i1:i2,ipmass)*dunitspmass)/ & ((dati(i1:i2,irho)*dunitsrho)*(dati(i1:i2,ih)*dunitsh)**ndim) elsewhere weighti(i1:i2) = 0. endwhere endif enddo over_types endif if (idensityweighted) then print "(a)",' USING DENSITY WEIGHTED INTERPOLATION ' inormalise = .true. endif elseif (any(masstype(1:ntypes).gt.0.) .and. & irho.gt.0 .and. irho.le.ndataplots .and. & ih .gt. 0 .and. ih.le.ndataplots .and. & required(irho) .and. required(ih)) then if (size(iamtypei) > 1) then ! !--particles with mixed types ! !$omp parallel do default(none) & !$omp shared(ninterp,iamtypei,weighti,dati,rendersinks,isinktype) & !$omp shared(usetype,idensityweighted,dunitsrho,masstype) & !$omp shared(ih,irho,dunitspmass,dunitsh,ndim) & !$omp private(ipart,itype) do ipart=1,ninterp itype = iamtypei(ipart) if (.not.usetype(itype)) then if (rendersinks .and. itype.eq.isinktype) then weighti(ipart) = weight_sink else weighti(ipart) = 0. endif elseif (idensityweighted) then if (dati(ipart,ih) > tiny(dati)) then weighti(ipart) = (masstype(itype)*dunitspmass)/ & ((dati(ipart,ih)*dunitsh)**ndim) else weighti(ipart) = 0. endif else if (dati(ipart,irho) > tiny(dati) .and. dati(ipart,ih) > tiny(dati)) then weighti(ipart) = (masstype(itype)*dunitspmass)/ & ((dati(ipart,irho)*dunitsrho)*(dati(ipart,ih)*dunitsh)**ndim) else weighti(ipart) = 0. endif endif enddo !$omp end parallel do else ! !--particles ordered by type ! i2 = 0 over_types2: do itype=1,ntypes i1 = i2 + 1 i2 = i2 + npartoftype(itype) i2 = min(i2,ninterp) if (i1 > i2) cycle over_types2 !--set weights to zero for particle types not used in the rendering if (.not.usetype(itype)) then if (rendersinks .and. itype.eq.isinktype) then weighti(i1:i2) = weight_sink else weighti(i1:i2) = 0. endif else where(dati(i1:i2,irho) > tiny(dati) .and. dati(i1:i2,ih) > tiny(dati)) weighti(i1:i2) = masstype(itype)/ & ((dati(i1:i2,irho)*dunitsrho)*(dati(i1:i2,ih)*dunitsh)**ndim) elsewhere weighti(i1:i2) = 0. endwhere endif enddo over_types2 endif if (idensityweighted) then print "(a)",' USING DENSITY WEIGHTED INTERPOLATION ' inormalise = .true. endif else if (required(ih) .and. required(irho) .and. ih.gt.0 .and. irho.gt.0) then print "(a)",' WARNING: particle mass not set: using normalised interpolations' endif weighti(1:ninterp) = 1.0 !--if particle mass has not been set, then must use normalised interpolations inormalise = .true. if (size(iamtypei) > 1) then ! !--particles with mixed types ! !$omp parallel do default(none) & !$omp shared(ninterp,iamtypei,weighti,usetype) & !$omp private(ipart,itype) do ipart=1,ninterp itype = iamtypei(ipart) if (.not.usetype(itype)) weighti(ipart) = 0. enddo !$omp end parallel do endif endif end subroutine set_interpolation_weights end module interpolation splash/src/fieldlines.f90000644 000766 000000 00000035166 13261626263 016224 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ! module for field line / flux tube plotting in 2 and 3 dimensions ! module fieldlines implicit none public :: streamlines,vecplot3D_proj private :: trace2D,interpolate_pt private contains !--------------------------------------------------------------------------- ! ! This subroutine integrates a 2D vector field to give the stream function ! Plotting the contours of this function gives the field/stream lines ! ! The solution is given by ! ! A(x,y) = \int v_x(x,y) dy - \int v_y(x,y) dx ! ! which we compute, knowing v_x and v_y on a fixed grid of square pixels, ! by a simple trapezoidal integration for each component. ! ! For SPH, this means we first interpolate the vector field to ! get v_x and v_y on the two-dimensional grid. This then also works for ! cross sections / projections of 3D vector fields by first interpolating ! to the 2D grid. ! ! ! Inputs: vecpixx(npixx,npixy) : x component of vector field on fixed grid ! vecpixy(npixx,npixy) : y component of vector field on fixed grid ! xmin, ymin : xmin and ymin of grid ! pixwidth : grid cell size (pixels are square) ! npixx,npixy : number of grid cells (pixels) in x,y ! ! Output: datpix(npixx,npixy) : stream function on fixed grid ! ! written by Daniel Price dprice@astro.ex.ac.uk ! ! Oct 2007: uses Simpson's rule instead of trapezoidal ! !--------------------------------------------------------------------------- subroutine streamlines(vecpixx,vecpixy,datpix,npixx,npixy,pixwidth) implicit none integer, intent(in) :: npixx,npixy real, intent(in), dimension(npixx,npixy) :: vecpixx,vecpixy real, intent(in) :: pixwidth real, intent(out), dimension(npixx,npixy) :: datpix real, dimension(npixx,npixy) :: datpix2 double precision :: fyj,fyjhalf,term,termi,termj,fxprevi,fyjprev double precision, dimension(npixx) :: fx,fxhalf integer :: i,j ! !--check for errors in input ! if (pixwidth.le.0.) then print "(1x,a)",'streamlines: error: pixel width <= 0' datpix = 0. return endif fyj = 0. fyjhalf = 0. ! !--perform the integration forwards ! do j=1,npixy do i=1,npixx term = 0. if (i.eq.1) then fyj = 0. fyjhalf = 0. else fyjprev = fyj !--trapezoidal rule in x termj = 0.5*pixwidth*(vecpixy(i-1,j)+vecpixy(i,j)) fyj = fyj - termj if (mod(i-1,2).eq.0) then ! 3, 5, 7, 9 ... ! !--for odd points, use trapezoidal solution at half grid points ! to get Simpson's rule ! fyjhalf = fyjhalf - pixwidth*(vecpixy(i-2,j)+vecpixy(i,j)) term = term + 4./3.*fyj - 1./3.*fyjhalf else ! !--for even points, use Simpson's rule up to last odd point ! then finish with a trapezoidal integration over last two points ! term = term + 4./3.*fyjprev - 1./3.*fyjhalf - termj endif endif ! !--same as above but for integration in y ! if (j.eq.1) then fx(i) = 0. fxhalf(i) = 0. fxprevi = 0. else fxprevi = fx(i) termi = 0.5*pixwidth*(vecpixx(i,j-1)+vecpixx(i,j)) fx(i) = fx(i) + termi if (mod(j-1,2).eq.0) then fxhalf(i) = fxhalf(i) + pixwidth*(vecpixx(i,j-2)+vecpixx(i,j)) term = term + 4./3.*fx(i) - 1./3.*fxhalf(i) else term = term + 4./3.*fxprevi - 1./3.*fxhalf(i) + termi endif endif datpix(i,j) = real(term) enddo enddo ! !--perform the integration backwards ! datpix2 = 0. do j=npixy,1,-1 do i=npixx,1,-1 term = 0. if (i.eq.npixx) then fyj = 0. fyjhalf = 0. else fyjprev = fyj !--trapezoidal rule in x termj = 0.5*pixwidth*(vecpixy(i+1,j)+vecpixy(i,j)) fyj = fyj + termj if (mod(npixx-i,2).eq.0) then ! 3, 5, 7, 9 ... ! !--for odd points, use trapezoidal solution at half grid points ! to get Simpson's rule ! fyjhalf = fyjhalf + pixwidth*(vecpixy(i+2,j)+vecpixy(i,j)) term = term + 4./3.*fyj - 1./3.*fyjhalf else ! !--for even points, use Simpson's rule up to last odd point ! then finish with a trapezoidal integration over last two points ! term = term + 4./3.*fyjprev - 1./3.*fyjhalf + termj endif endif ! !--same as above but for integration in y ! if (j.eq.npixy) then fx(i) = 0. fxhalf(i) = 0. fxprevi = 0. else fxprevi = fx(i) termi = 0.5*pixwidth*(vecpixx(i,j+1)+vecpixx(i,j)) fx(i) = fx(i) - termi if (mod(npixy-j,2).eq.0) then fxhalf(i) = fxhalf(i) - pixwidth*(vecpixx(i,j+2)+vecpixx(i,j)) term = term + 4./3.*fx(i) - 1./3.*fxhalf(i) else term = term + 4./3.*fxprevi - 1./3.*fxhalf(i) - termi endif endif datpix2(i,j) = real(term) enddo enddo ! !--average the two ! datpix = 0.5*(datpix + datpix2) return end subroutine streamlines !--------------------------------------------------------------------------------- ! ! THE REST OF THIS MODULE IS EITHER OLD, *VERY* EXPERIMENTAL AND/OR UNDOCUMENTED ! !--------------------------------------------------------------------------------- ! ! we want to trace the curve through a 2D vector field ! subroutine fieldlines2D(npart,x,y,vecx,vecy,h,pmass,rho,xmin,xmax,ymin,ymax) implicit none integer, intent(in) :: npart real, intent(in), dimension(npart) :: x,y,vecx,vecy,h,pmass,rho real, intent(in) :: xmin,xmax,ymin,ymax integer :: i,nlines real :: dx,dy,xstart,ystart,ymaxline nlines = 10 dx = (xmax-xmin)/nlines dy = (ymax-ymin)/nlines ystart = ymin ymaxline = ymin do while (ymaxline.lt.ymax) do i=1,nlines xstart = xmin + (i-1)*dx + 0.5*dx print*,' tracing field line ',i,' x, y = ',xstart,ystart call trace2D(xstart,ystart,xmin,xmax,ymin,ymax,ymaxline, & x,y,vecx,vecy,h,pmass,rho,npart) enddo ystart = ymaxline + 0.5*dy enddo end subroutine fieldlines2D subroutine trace2D(xstart,ystart,xmin,xmax,ymin,ymax,ymaxline, & x,y,vecx,vecy,h,pmass,rho,npart) use plotlib, only:plot_line implicit none integer, intent(in) :: npart real, intent(in) :: xstart,ystart,xmin,xmax,ymin,ymax real, intent(inout) :: ymaxline real, dimension(npart), intent(in) :: x,y,vecx,vecy,h,pmass,rho integer :: ipt,npix real, dimension(2) :: xline, yline real :: runit,runit1,dx,dy,vx,vy,pixwidth,sign xline(1) = xstart yline(1) = ystart npix = 100 pixwidth = (xmax - xmin)/npix ipt = 0 sign = 1.0 do while ((xline(1).ge.xmin .and. xline(1).le.xmax) .and. & (yline(1).ge.ymin .and. yline(1).le.ymax) .and. ipt.lt.10*npix) ipt = ipt + 1 ! !--get dx and dy from interpolation of vector field from particles ! call interpolate_pt(xline(1),yline(1),vx,vy, & x,y,vecx,vecy,h,pmass,rho,npart) ! !--get unit vector in direction of vector ! runit = sqrt(vx**2 + vy**2) if (runit.gt.0) then runit1 = 1./runit dx = vx*runit1*pixwidth dy = vy*runit1*pixwidth else dx = 0. dy = 0. endif if (ipt.eq.1 .and. dy.lt.0.) sign = -1.0 xline(2) = xline(1) + sign*dx yline(2) = yline(1) + sign*dy !!print*,'x, y = ',xline(2),yline(2) ymaxline = max(ymaxline,yline(2)) ! !--plot line segment ! call plot_line(2,xline,yline) xline(1) = xline(2) yline(1) = yline(2) enddo if (ipt.ge.10*npix) print*,'WARNING: infinite field line' end subroutine trace2D ! !--interpolate from particles to single point ! would be nice to know neighbours ! subroutine interpolate_pt(xpt,ypt,vxpt,vypt,x,y,vecx,vecy,h,pmass,rho,npart) use kernels, only:radkernel2,wfunc,cnormk2D implicit none integer, intent(in) :: npart real, dimension(npart), intent(in) :: x,y,vecx,vecy,h,pmass,rho real, intent(in) :: xpt, ypt real, intent(out) :: vxpt, vypt real :: rho1i,term,const,dx,dy,hi1,q2,wab integer :: i vxpt = 0. vypt = 0. const = cnormk2D do i=1,npart dx = xpt - x(i) dy = ypt - y(i) hi1 = 1./h(i) q2 = (dx*dx + dy*dy)*hi1*hi1 ! !--if particles are within range, calculate contribution to this pt ! if (q2.lt.radkernel2) then if (rho(i) > 0.) then rho1i = 1./rho(i) else rho1i = 0. endif term = const*pmass(i)*rho1i wab = wfunc(q2) vxpt = vxpt + term*vecx(i)*wab vypt = vypt + term*vecy(i)*wab endif enddo end subroutine interpolate_pt !-------------------------------------------------------------------------- ! Visualisation of a 3D vector field in projection ! by means of "iron filings" drawn on particles ! ! We draw a line on each particle in the direction of the vector field ! This line is illuminated by reflections from a lighting source, and ! is drawn with an opacity proportional to the field strength, such that ! strong field regions are highlighted. ! ! For details of the lighting algorithm, see e.g. ! Stalling, Zoeckler and Hege, 1997, IEEE Trans. Viz. Comp. Graphics, 3, 118-128 ! ! Added by D. Price, Dec 2011 !-------------------------------------------------------------------------- subroutine vecplot3D_proj(x,y,z,vx,vy,vz,vecmax,weight,itype,n,dx,zobs,dscreen) use plotlib, only:plot_line,plot_bbuf,plot_ebuf,plot_slw,plot_sci,plot_set_opacity use plotlib, only:plot_qcr,plot_scr,plot_qlw,plot_arro,plot_sah use sort, only:indexx implicit none integer, intent(in) :: n real, dimension(n), intent(in) :: x,y,z,vx,vy,vz,weight integer, dimension(n), intent(in) :: itype real, intent(inout) :: vecmax real, intent(in) :: dx,zobs,dscreen integer, dimension(n) :: iorder integer :: i,ipart,np real, dimension(2) :: xpts,ypts real :: vxi,vyi,vzi,dvmag,zfrac,vmax,vmag,frac,ri,gi,bi,term,lw real :: toti,fambient,diffuse,specular,fdiff,fspec,ldotn,vdotr,ldott,vdott integer :: pdiff,nspec,lwold real, dimension(3) :: vunit,lighting,viewangle logical :: white_bg,use3Dperspective ! !--get the max adaptively if it is not already set ! if (vecmax.le.0. .or. vecmax.gt.0.5*huge(vecmax)) then vmax = 0. do i=1,n if (itype(i).ge.0 .and. weight(i).gt.0.) then vmax = max(vx(i)**2 + vy(i)**2 + vz(i)**2,vmax) endif enddo vmax = sqrt(vmax) vecmax = vmax else vmax = vecmax endif use3Dperspective = abs(dscreen).gt.tiny(dscreen) ! !--work out whether or not we have a white or black ! background colour ! !call plot_sah(1,20.,1.0) call plot_qcr(0,ri,gi,bi) white_bg = (ri + gi + bi > 1.5) ! !--specify the parameters in the lighting algorithm ! these should differ depending on whether we are drawing ! on a white or black background ! if (white_bg) then fambient = 0. fdiff = 0.1 fspec = 0.5 else fambient = 0.3 fdiff = 0.7 fspec = 0.8 endif pdiff = 4 nspec = 12 ! !--specify the viewing and lighting angles ! viewangle = (/0.,0.,1./) !lighting = (/0.3,0.3,1./) lighting = (/0.,0.,1./) !--make sure these are normalised !lighting = lighting/sqrt(dot_product(lighting,lighting)) print*,'plotting 3D field structure: min,max = ',1.e-3*vmax,vmax ! !--first sort the particles in z so that we do the opacity in the correct order ! call indexx(n,z,iorder) call plot_bbuf call plot_qcr(1,ri,gi,bi) np = 0 zfrac = 1. call plot_qlw(lwold) lw = 2.*lwold over_particles: do ipart=1,n i = iorder(ipart) if (itype(i).ge.0 .and. weight(i).gt.0.) then if (use3Dperspective) then if (z(i).gt.zobs) cycle over_particles zfrac = abs(dscreen/(z(i)-zobs)) endif !if (mod(ipart,10)/=0) cycle over_particles ! lw = min(zfrac,2.5) vxi = vx(i) vyi = vy(i) vzi = vz(i) ! !--we draw lines on each particle with an ! opacity proportional to the field strength ! vmag = sqrt(vxi**2 + vyi**2 + vzi**2) dvmag = 1./vmag vunit = abs((/vxi,vyi,vzi/)*dvmag) frac = min(vmag/vmax,1.0) if (frac.ge.1.e-3) then !--specify the length of line to draw term = 1.5*dx*dvmag*zfrac !term = term*(vmag/vmax)**0.2 xpts(1) = x(i) - vxi*term xpts(2) = x(i) + vxi*term ypts(1) = y(i) - vyi*term ypts(2) = y(i) + vyi*term !--draw "halo" in background colour with ! twice the thickness, same opacity call plot_slw(2.*lw) call plot_sci(0) call plot_set_opacity(frac) call plot_line(2,xpts,ypts) !--Phong lighting ldott = dot_product(lighting,vunit) ldotn = sqrt(1. - ldott**2) diffuse = fdiff*(ldotn)**pdiff vdott = dot_product(viewangle,vunit) vdotr = ldotn*sqrt(1. - vdott**2) - ldott*vdott specular = fspec*(vdotr)**nspec toti = (fambient + diffuse + specular) !--draw line with intensity proportional ! to the amount of lighting !call plot_scr(1,toti,toti,toti,max(frac,0.15)) call plot_scr(1,toti,toti,toti,max(frac,0.05)) call plot_sci(1) call plot_slw(lw) call plot_line(2,xpts,ypts) !call plot_arro(xpts(1),ypts(1),xpts(2),ypts(2)) np = np + 1 endif endif enddo over_particles !--reset opacity for both foreground and background colour indices call plot_sci(0) call plot_set_opacity(1.0) call plot_sci(1) call plot_scr(1,ri,gi,bi) call plot_set_opacity(1.0) call plot_ebuf call plot_slw(lwold) print*,' plotted ',np,' of ',n,' particles' end subroutine vecplot3D_proj end module fieldlines splash/src/options_particleplots.f90000644 000766 000000 00000042261 13261626263 020540 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing settings and options relating to particle plots ! includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module settings_part use params use settings_data, only:icoordsnew,iexact implicit none integer, dimension(maxparttypes) :: imarktype,idefaultcolourtype,itypeorder integer, dimension(100) :: icircpart integer, dimension(maxplot) :: ilocerrbars logical, dimension(maxparttypes) :: iplotpartoftype,PlotOnRenderings,UseTypeInContours integer :: ncircpart,ismooth_particle_plots integer :: linestyle, linecolour,linestylethisstep,linecolourthisstep,ErrorBarType logical :: iplotline,ilabelpart,ifastparticleplot,iploterrbars real :: hfacmarkers namelist /plotopts/ iplotline,linestyle,linecolour, & imarktype,iplotpartoftype,PlotOnRenderings, & iexact,icoordsnew,ifastparticleplot,idefaultcolourtype,& itypeorder,UseTypeInContours,iploterrbars,ilocerrbars,hfacmarkers,& ErrorBarType,ismooth_particle_plots contains !--------------------------------------------- ! set default values for these options !--------------------------------------------- subroutine defaults_set_part use settings_data, only:icoords implicit none integer :: i ncircpart = 0 iplotline = .false. ! plot line joining the particles linestyle = 1 ! line style for above linecolour = 1 linestylethisstep = 1 linecolourthisstep = 1 iexact = 0 ! exact solution to plot ilabelpart = .false. ! plot particle numbers icoordsnew = icoords icircpart(:) = 0 iplotpartoftype(1) = .true. ! whether or not to plot particles of certain types iplotpartoftype(2:maxparttypes) = .false. PlotOnRenderings = .false. imarktype = 1 ! marker type for all particles imarktype(2) = 4 ! marker type for ghost/dark matter particles imarktype(3) = 17 ! marker type for sink particles imarktype(5) = 3 ! marker type for star particles (gadget) idefaultcolourtype = -1 ! default colour for each particle type ifastparticleplot = .true. ! allow crowded-field elimination on particle plots do i=1,maxparttypes itypeorder(i) = i enddo UseTypeInContours(:) = iplotpartoftype(:) iploterrbars = .false. ! plot error bars for a particular column ilocerrbars(:) = 0 ! location of data for error bars in dat array hfacmarkers = 1.0 ErrorBarType = 0 ismooth_particle_plots = 0 return end subroutine defaults_set_part !--------------------------------------------- ! changed default values for these options !--------------------------------------------- subroutine defaults_set_part_ev implicit none iplotline = .true. ! plot line joining the particles iplotpartoftype(1:maxparttypes) = .false. ! whether or not to plot particles of certain types UseTypeInContours(:) = iplotpartoftype(:) return end subroutine defaults_set_part_ev !---------------------------------------------------------------------- ! submenu with options relating to particle plots !---------------------------------------------------------------------- subroutine submenu_particleplots(ichoose) use exact, only:options_exact,submenu_exact use labels, only:labeltype,ih,label,idustfrac,idustfracsum, & ideltavsum use limits, only:lim use settings_data, only:icoords,ntypes,ndim,ndimV,UseTypeInRenderings, & ndataplots,ndusttypes,idustfrac_plot,ideltav_plot,ncalc use settings_render, only:iplotcont_nomulti use particle_data, only:npartoftype,iamtype use prompting, only:prompt,print_logical use geometry, only:maxcoordsys,labelcoordsys,coord_transform_limits use multiplot, only:itrans use plotlib, only:plotlib_maxlinestyle,plotlib_maxlinecolour use calcquantities, only:calc_quantities use settings_data, only:DataIsBuffered,numplot use filenames, only:nsteps,nstepsinfile,ifileopen use geomutils, only:set_coordlabels use calcquantities, only:setup_calculated_quantities use asciiutils, only:enumerate implicit none integer, intent(in) :: ichoose integer :: i,iaction,n,itype,icoordsprev,ierr,icol integer :: idustfrac_prev character(len=2) :: charntypes character(len=20) :: substring1,substring2,substring3 character(len=1000) :: fmtstring character(len=120) :: contline character(len=3) :: idustfracsum_string logical :: ians iaction = ichoose !--we require some tricks with the format string to print only the actual number of ! particle types rather than the whole array ! if (ntypes.gt.100) print*,'WARNING: Internal error: ntypes too large for formatting in particle plot menu' if (ntypes.le.0) then substring1 = "no types specified" substring2 = "not applicable" elseif (ntypes.eq.1) then substring1 = "a" substring2 = "i2" else write(charntypes,"(i2)") ntypes-1 substring1 = charntypes//"(a,',',1x),a" substring2 = charntypes//"(i2,',',1x),i2" endif substring3 = enumerate(ismooth_particle_plots+1,(/'OFF ','FIXED','ADAPT'/),default=1) if (iplotcont_nomulti) then contline = "' use in contour plots: ( ',"//trim(substring1)//",' )',/," else contline = ' ' endif fmtstring="("// & "' 0) exit ',/,"// & "' 1) turn on/off particles by type ( ',"//trim(substring1)//",' )',/,"//trim(contline)// & "' 2) change graph markers for each type ( ',"//trim(substring2)//",' )',/,"// & "' 3) set colour for each particle type ( ',"//trim(substring2)//",' )',/,"// & "' 4) smooth particle plots ( ',a,' )',/,"// & "' 5) plot line joining particles ( ',a,' ) ',/,"// & "' 6) plot error bars/smoothing circles ( ',a,' ) ',/,"// & "' 7) change coordinate systems ( ',i2,' ) ',/,"// & "' 8) plot exact solution ( ',i2,' ) ',/,"// & "' 9) exact solution plot options ')" print "(a)",'------------- particle plot options -------------------' if (iaction.le.0 .or. iaction.gt.9) then if (iplotcont_nomulti) then print fmtstring,(trim(print_logical(iplotpartoftype(i))),i=1,ntypes), & (trim(print_logical(UseTypeInContours(i),mask=UseTypeInRenderings(i))),i=1,ntypes), & imarktype(1:ntypes),idefaultcolourtype(1:ntypes),trim(substring3), & print_logical(iplotline),print_logical(ncircpart.gt.0 .or.iploterrbars),icoordsnew,iexact else print fmtstring,(trim(print_logical(iplotpartoftype(i))),i=1,ntypes), & imarktype(1:ntypes),idefaultcolourtype(1:ntypes),trim(substring3), & print_logical(iplotline),print_logical(ncircpart.gt.0 .or.iploterrbars),icoordsnew,iexact endif call prompt('enter option',iaction,0,9) endif ! select case(iaction) !------------------------------------------------------------------------ case(1) ! plot particles by type? do itype=1,ntypes if (UseTypeinRenderings(itype) .and. ndim.gt.1) then call prompt('Plot '//trim(labeltype(itype))//' particles / use in renderings?',iplotpartoftype(itype)) if (iplotcont_nomulti) then call prompt('Use '//trim(labeltype(itype))//' particles in contour plots?',UseTypeInContours(itype)) endif else call prompt('Plot '//trim(labeltype(itype))//' particles?',iplotpartoftype(itype)) UseTypeInContours(itype) = .false. endif if (iplotpartoftype(itype) .and. itype.gt.1) then if (.not.UseTypeInRenderings(itype)) then call prompt('>> Plot '//trim(labeltype(itype))//' particles on top of rendered plots?',PlotOnRenderings(itype)) else PlotonRenderings(itype) = .false. endif elseif (.not.iplotpartoftype(itype)) then PlotonRenderings(itype) = .false. endif if (trim(labeltype(itype))=='dust'.and. iplotpartoftype(itype) .and. ndusttypes>1) then !--if idustfrac_plot hasn't been defined... if (idustfrac_plot == 0 ) then idustfrac_plot = idustfracsum endif !--save to compare after user input idustfrac_prev = idustfrac_plot write(idustfracsum_string,'(I3)') idustfracsum call prompt('Which dust phase would you like to render? (' & //trim(adjustl(idustfracsum_string))//'=summed)', & idustfrac_plot,idustfracsum,idustfracsum+ndusttypes) !--update which set of deltav's will be used ideltav_plot = ideltavsum + ndimV*(idustfrac_plot - idustfracsum) if (idustfrac_prev /= idustfrac_plot) then !--Modify calculated data for fake dust particles if necessary if (ncalc /= 0) then print*,'...recalibrating calculated quantities...' call setup_calculated_quantities(ncalc,quiet=.true.) print*,'...done!' endif endif endif enddo return !------------------------------------------------------------------------ case(2) print "(/,' Marker options (for all from -8->31, see plot library userguide):',11(/,i2,') ',a))", & 0,'square',1,'.',2,'+',3,'*',4,'o',5,'x',17,'bold circle',-8,'large bold circle', & 32,'solid circle, size proportional to h', & 33,'open circle, size proportional to h', & 34,'outlined solid circle, size prop. to h' !print*,'(0 Square) (1 .) (2 +) (3 *) (4 o) (5 x) (17 bold circle) (-8 bigger bold circle)' do itype=1,ntypes call prompt(' Enter marker to use for '//trim(labeltype(itype)) & //' particles:',imarktype(itype),-8,35) enddo if (any(imarktype(1:ntypes).ge.32)) then print* call prompt(' Enter proportionality factor for scalable markers (radius = fac*h)',hfacmarkers) endif return !------------------------------------------------------------------------ case(3) print "(2(a,/),/,4(a,/))", & ' Warning: setting a colour for a particle type overrides', & ' (at each new timestep) colours set interactively ', & ' -1 = retain interactively set colours between timesteps', & ' 0 = background ',& ' 1 = foreground ',& ' 2->10 = various colours (see default colour indices for plot library)' do itype=1,ntypes call prompt(' Enter default colour for '//trim(labeltype(itype)) & //' particles:',idefaultcolourtype(itype),-1,14) enddo return !------------------------------------------------------------------------ case(4) !print "(3(/,a))",' 0) no smoothing, raw particle plots',& ! ' 1) render particle plots with fixed h', & ! ' 2) render particle plots with adaptive h (slower)' call prompt('smooth particle plots? (0=none 1=fixed 2=adaptive)',ismooth_particle_plots,0,2) if (ismooth_particle_plots.eq.0) then if (size(iamtype(:,1)).gt.1) then print "(3(/,a),/)", & ' WARNING: changing type plotting order currently has no effect ', & ' when particle types are mixed in the dump file', & ' (for sphNG read disable this using -lowmem on the command line)' endif if (ntypes > 1) then ians = .false. call prompt('set plot order of particle types manually?',ians) if (ians) then print "(9(i1,'=',a,', '))",(i,trim(labeltype(i)),i=1,ntypes) call prompt('enter first particle type to plot',itypeorder(1),1,ntypes) do i=2,ntypes ierr = 1 do while (ierr /= 0) itype = itypeorder(i) call prompt('enter next particle type to plot',itype,1,ntypes) if (any(itypeorder(1:i-1).eq.itype)) then print "(a)",' error: cannot be same as previous type' ierr = 1 else itypeorder(i) = itype ierr = 0 endif enddo enddo endif endif print "(/,a,/,a,/)",' Fast particle plotting excludes particles in crowded regions', & ' Turn this option off to always plot every particle' call prompt('Allow fast particle plotting?',ifastparticleplot) endif return !------------------------------------------------------------------------ case(5) call prompt('plot line joining particles?',iplotline) if (iplotline) then call prompt('Enter line style to use ',linestyle,1,plotlib_maxlinestyle) call prompt('Enter colour for line ',linecolour,0,plotlib_maxlinecolour) endif return !------------------------------------------------------------------------ case(6) if (ndim.le.1 .or. ih.le.0) then icol = 0 do icol=1,ndataplots if (ilocerrbars(icol).gt.0) print "(a,i2,a,i2,a)", & 'column ',ilocerrbars(icol),' contains errors for column ',icol,':'//label(icol) enddo if (any(ilocerrbars(1:ndataplots).gt.0)) then call prompt('turn on plotting of error bars? ',iploterrbars) if (.not.iploterrbars) return else iploterrbars = .false. endif icol = 1 do while(icol.ne.0) icol = 0 call prompt('Enter column to set location of error bars for (0=none)',icol,0,ndataplots) if (icol.gt.0) then call prompt('Enter location of error data for this column in the data',ilocerrbars(icol),0) if (ilocerrbars(icol) <= 0 .or. ilocerrbars(icol) > ndataplots) then print "(a,i2)",' WARNING: currently no data in column ',ilocerrbars(icol) else iploterrbars = .true. endif if (all(ilocerrbars(1:ndataplots).le.0)) iploterrbars = .false. else if (all(ilocerrbars(1:ndataplots).le.0)) iploterrbars = .false. endif enddo print "(2(/,a))",' 0) Default style |--|',& ' 1) Semi-transparent shaded region' call prompt('Select error bar style',ErrorBarType,0,1) else print*,'Circles of interaction can also be set interactively' call prompt('Enter number of circles to draw',ncircpart,0,size(icircpart)) if (ncircpart.gt.0) then do n=1,ncircpart if (icircpart(n).eq.0) then if (n.gt.1) then icircpart(n) = icircpart(n-1)+1 else icircpart(n) = 1 endif endif call prompt('Enter particle number to plot circle around', & icircpart(n),1,maxval(npartoftype(1,:))) enddo endif endif return !------------------------------------------------------------------------ case(7) print 20,icoords do i=1,maxcoordsys print 30,i,labelcoordsys(i) enddo 20 format(' 0) reset (=',i2,')') 30 format(1x,i1,')',1x,a) icoordsprev = icoordsnew call prompt(' Enter coordinate system to plot in:', & icoordsnew,0,maxcoordsys) if (icoordsnew.eq.0) icoordsnew = icoords if (icoordsnew.ne.icoordsprev) then itrans(1:ndim) = 0 call coord_transform_limits(lim(1:ndim,1),lim(1:ndim,2), & icoordsprev,icoordsnew,ndim) call set_coordlabels(numplot) if (DataIsBuffered) then call calc_quantities(1,nsteps) else call calc_quantities(1,nstepsinfile(ifileopen)) endif endif return !------------------------------------------------------------------------ case(8) call submenu_exact(iexact) return !------------------------------------------------------------------------ case(9) call options_exact return !------------------------------------------------------------------------ case default return end select return end subroutine submenu_particleplots end module settings_part splash/src/rotate.f90000644 000766 000000 00000017446 13261626263 015405 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ! This module contains all the routines for rotating the particles ! and plotting the rotated axes ! module rotation implicit none ! !--2D rotation (about z axis) ! contains subroutine rotate2D(xcoords,anglez) implicit none real, intent(inout) :: xcoords(2) real, intent(in) :: anglez real :: x, y, r, phi x = xcoords(1) y = xcoords(2) ! !--rotate about z ! r = sqrt(x**2 + y**2) phi = ATAN2(y,x) phi = phi - anglez x = r*COS(phi) y = r*SIN(phi) xcoords(1) = x xcoords(2) = y return end subroutine rotate2D ! !--3D rotation (about x, y and z axes) ! This is done in the order z-y-x ! subroutine rotate3D(xcoords,anglex,angley,anglez,zobs,dz1) implicit none real, intent(inout) :: xcoords(3) real, intent(in) :: anglex, angley, anglez, zobs, dz1 real :: x, y, z, r, phi, zfrac x = xcoords(1) y = xcoords(2) z = xcoords(3) ! !--rotate about z ! if (abs(anglez).gt.tiny(anglez)) then r = sqrt(x**2 + y**2) phi = ATAN2(y,x) phi = phi - anglez x = r*COS(phi) y = r*SIN(phi) endif ! !--rotate about y ! if (abs(angley).gt.tiny(angley)) then r = sqrt(z**2 + x**2) phi = ATAN2(z,x) phi = phi - angley z = r*SIN(phi) x = r*COS(phi) endif ! !--rotate about x ! if (abs(anglex).gt.tiny(anglex)) then r = sqrt(y**2 + z**2) phi = ATAN2(z,y) phi = phi - anglex y = r*COS(phi) z = r*SIN(phi) endif ! !--change perspective according to z depth ! (for straight rotation == parallel projections use dz1= 0 on input) ! zobs is the z position of the observer. ! if (abs(dz1).gt.tiny(dz1)) then zfrac = abs(dz1/(z-zobs)) else zfrac = 1.0 endif xcoords(1) = x*zfrac xcoords(2) = y*zfrac xcoords(3) = z return end subroutine rotate3D ! !--plots rotated plot axes ! subroutine rotate_axes2D(ioption,xmin,xmax,xorigin,anglez) use plotlib, only:plot_arro,plot_sfs,plot_poly implicit none integer, intent(in) :: ioption real, intent(in), dimension(2) :: xmin,xmax,xorigin real, intent(in) :: anglez integer :: i,idim real, dimension(2) :: xpttemp real, dimension(2,4) :: xpt ! ! plot various options for the 3D axes ! select case(ioption) case(1) !--rotated axes print*,'plotting rotated (2D) axes...' do idim=1,2 !--plot to max of each axis xpt(:,2) = 0. xpt(idim,2) = xmax(idim) do i=1,2 xpttemp(:) = xpt(:,i) - xorigin(:) call rotate2D(xpttemp(:),anglez) xpt(:,i) = xpttemp(:) + xorigin(:) enddo !--plot each axis as an arrow call plot_arro(xpt(1,1),xpt(2,1),xpt(1,2),xpt(2,2)) enddo case default print*,'plotting rotated (2D) box...' !--front face (pts 1->4) xpt(:,1) = xmin(:) ! xmin, ymin xpt(1,2) = xmin(1) ! xmin xpt(2,2) = xmax(2) ! ymax xpt(1,3) = xmax(1) ! xmax xpt(2,3) = xmax(2) ! ymax xpt(1,4) = xmax(1) ! xmax xpt(2,4) = xmin(2) ! ymin ! !--now rotate each of these coordinates ! do i=1,4 xpttemp(:) = xpt(:,i) - xorigin(:) call rotate2D(xpttemp(:),anglez) xpt(:,i) = xpttemp(:) + xorigin(:) enddo ! !--now plot box appropriately using points ! call plot_sfs(2) call plot_poly(4,xpt(1,1:4),xpt(2,1:4)) end select return end subroutine rotate_axes2D subroutine rotate_axes3D(ioption,iplotx,iploty,xmin,xmax,xorigin, & anglex,angley,anglez,zobs,dz1) use plotlib, only:plot_poly,plot_sfs,plot_arro,plot_line implicit none integer, intent(in) :: ioption,iplotx,iploty real, intent(in), dimension(3) :: xmin,xmax,xorigin real, intent(in) :: anglex, angley, anglez, zobs, dz1 integer :: i,idim,iline integer, parameter :: nlines = 10 real, dimension(3,8) :: xpt real, dimension(3) :: xpttemp real, dimension(2) :: xline,yline real :: dx ! ! plot various options for the 3D axes ! select case(ioption) case(1) !--rotated axes print*,'plotting rotated 3D axes...' xpt = 0. !--origin xpt(1:3,1) = 0. do idim=1,3 !--plot to max of each axis xpt(:,2) = 0. xpt(idim,2) = xmax(idim) do i=1,2 xpttemp(:) = xpt(:,i) - xorigin(:) call rotate3D(xpttemp(:),anglex,angley,anglez,zobs,dz1) xpt(:,i) = xpttemp(:) + xorigin(:) enddo !--plot each axis as an arrow call plot_arro(xpt(iplotx,1),xpt(iploty,1),xpt(iplotx,2),xpt(iploty,2)) !! call pgline(2,xpt(iplotx,1:2),xpt(iploty,1:2)) enddo case(2) !--rotated box print*,'plotting rotated 3D box...',iplotx,iploty !--front face (pts 1->4) xpt(:,1) = xmin(:) ! xmin, ymin xpt(1,2) = xmin(1) ! xmin xpt(2,2) = xmax(2) ! ymax xpt(1,3) = xmax(1) ! xmax xpt(2,3) = xmax(2) ! ymax xpt(1,4) = xmax(1) ! xmax xpt(2,4) = xmin(2) ! ymin xpt(3,1:4) = xmin(3) ! zmin !--back face (pts 5->8) do i=1,4 xpt(1:2,i+4) = xpt(1:2,i) enddo xpt(3,5:8) = xmax(3) ! !--now rotate each of these coordinates ! do i=1,8 xpttemp(:) = xpt(:,i) - xorigin(:) call rotate3D(xpttemp(:),anglex,angley,anglez,zobs,dz1) xpt(:,i) = xpttemp(:) + xorigin(:) enddo ! !--now draw lines appropriately through points ! call plot_sfs(2) !--front face call plot_poly(4,xpt(iplotx,1:4),xpt(iploty,1:4)) !--back face call plot_poly(4,xpt(iplotx,5:8),xpt(iploty,5:8)) !--connecting lines ( 1->5, 2->6, 3->7, 4->8 ) do i=1,4 xline(1) = xpt(iplotx,i) yline(1) = xpt(iploty,i) xline(2) = xpt(iplotx,i+4) yline(2) = xpt(iploty,i+4) call plot_line(2,xline,yline) enddo case(3) !--gridded x-y plane print*,'plotting rotated x-y plane...' !--lines of constant x dx = (xmax(1) - xmin(1))/real(nlines-1) do iline=1,nlines !--all pts at z = 0 xpt(3,:) = 0. !--start from xmin, plot line from ymin to ymax xpt(1,1:2) = xmin(1) + (iline-1)*dx xpt(2,1) = xmin(2) xpt(2,2) = xmax(2) do i=1,2 xpttemp(:) = xpt(:,i) - xorigin(:) call rotate3D(xpttemp(:),anglex,angley,anglez,zobs,dz1) xpt(:,i) = xpttemp(:) + xorigin(:) enddo call plot_line(2,xpt(iplotx,1:2),xpt(iploty,1:2)) enddo !--lines of constant y dx = (xmax(2) - xmin(2))/real(nlines-1) do iline=1,nlines !--all pts at z = 0 xpt(3,:) = 0. !--start from ymin, plot line from xmin to xmax xpt(2,1:2) = xmin(2) + (iline-1)*dx xpt(1,1) = xmin(1) xpt(1,2) = xmax(1) do i=1,2 xpttemp(:) = xpt(:,i) - xorigin(:) call rotate3D(xpttemp(:),anglex,angley,anglez,zobs,dz1) xpt(:,i) = xpttemp(:) + xorigin(:) enddo call plot_line(2,xpt(iplotx,1:2),xpt(iploty,1:2)) enddo case default !--do nothing end select return end subroutine rotate_axes3D end module rotation splash/src/read_data_silo.f90000644 000766 000000 00000026607 13261626263 017040 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR SILO FILES ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! ! Columns with the 'required' flag set to false are not read !------------------------------------------------------------------------- ! ! The module below contains interface routines to c functions ! that perform the actual calls to the SILO libs ! !------------------------------------------------------------------------- module siloread use params, only:maxplot,doub_prec use labels, only:lenlabel use, intrinsic :: iso_c_binding, only:c_int,c_double,c_char implicit none character(len=lenlabel), dimension(maxplot) :: blocklabel logical :: havewarned = .false. integer, parameter :: maxtypes = 6 interface subroutine read_silo_header(filename,npart,ncol,ndim,ndimV,time,ierr) bind(c) import character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(out) :: npart,ncol,ndim,ndimV,ierr real(kind=c_double), intent(out) :: time end subroutine read_silo_header subroutine read_silo_data(filename,maxtypes,npartoftypei,& ncol,isrequired,ierr) bind(c) import implicit none character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(in), value :: maxtypes integer(kind=c_int), dimension(6), intent(in) :: npartoftypei integer(kind=c_int), intent(in), value :: ncol integer(kind=c_int), intent(out) :: ierr integer(kind=c_int), dimension(ncol), intent(in) :: isrequired end subroutine read_silo_data end interface end module siloread !------------------------------------------------------------------------- ! ! The routine that reads the data into splash's internal arrays ! !------------------------------------------------------------------------- subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol,maxstep use params, only:doub_prec,maxparttypes,maxplot use settings_data, only:ndim,ndimV,ncolumns,ncalc,iformat,required,ipartialread, & ntypes,debugmode,iverbose use settings_page, only:legendtext use mem_allocation, only:alloc use labels, only:ih,irho,ipmass,labeltype use system_utils, only:renvironment,lenvironment,ienvironment,envlist use asciiutils, only:cstring use siloread, only:blocklabel,havewarned,read_silo_header, & read_silo_data,maxtypes implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile,densfile,hfile character(len=20) :: string integer :: i,j,itype,ierr integer :: index1,index2,nhfac integer :: ncolstep,npart_max,nstep_max,ntoti,ntotall,idot integer, parameter :: iunit = 11 logical :: iexist,reallocate,usez,debug,goterrors real(doub_prec) :: timetemp,ztemp real :: hfact,hfactmean,pmassi real, parameter :: pi = 3.1415926536 integer, dimension(maxplot) :: isrequired nstepsread = 0 goterrors = .false. if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! print "(1x,a)",'reading SILO format' inquire(file=datfile,exist=iexist) if (.not.iexist) then ! !--append .silo on the end if not already present ! datfile=trim(rootname)//'.silo' inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(rootname)//': file not found ***' return endif endif ! !--set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 ! !--read data from snapshots ! i = istepstart write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open file and read header information ! if (debug) print*,'DEBUG: reading header...' call read_silo_header(cstring(datfile),ntoti,ncolstep,ndim,ndimV,timetemp,ierr) if (ierr /= 0) then print "(a)", '*** ERROR READING HEADER ***' return endif ncolumns = ncolstep if (iverbose >= 1) print "(a,1x,i10,a,es10.3)",' ndim: ',ndim,' time: ',timetemp if (iverbose >= 1) print "(2(a,1x,i10))",' npart: ',ntoti,' ncolumns: ',ncolstep ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntotall) else ! if first time, save on memory npart_max = int(ntoti) endif endif if (i.ge.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol)) endif ! !--copy header data into allocated arrays ! ntypes = 1 npartoftype(1,i) = ntoti time(i) = real(timetemp) masstype(:,i) = 0. ! all masses read from file ! !--read particle data ! got_particles: if (ntoti > 0) then isrequired(:) = 0 where (required(1:ncolumns)) isrequired(1:ncolumns) = 1 call read_silo_data(cstring(datfile),ntypes,npartoftype(:,i),ncolumns,isrequired,ierr) nstepsread = 1 endif got_particles ! !--now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. ! !--set flag to indicate that only part of this file has been read ! if (.not.all(required(1:ncolstep))) ipartialread = .true. ! !--call set labels to identify location of smoothing length ! call set_labels ! !--cover the special case where no particles have been read ! if (ntoti.le.0) then npartoftype(1,i) = 1 dat(:,:,i) = 0. endif if (nstepsread.gt.0) then print "(a,i10,a)",' >> read ',sum(npartoftype(:,istepstart+nstepsread-1)),' particles' endif return end subroutine read_data subroutine read_silo_data_fromc(icol,npartoftypei,temparr,itype) bind(c) use, intrinsic :: iso_c_binding, only:c_int,c_double use particle_data, only:dat,iamtype use settings_data, only:debugmode use labels, only:label implicit none integer(kind=c_int), intent(in) :: icol,npartoftypei,itype real(kind=c_double), intent(in) :: temparr(npartoftypei) integer(kind=c_int) :: i,icolput integer :: nmax,nerr,idi logical :: useids icolput = icol if (debugmode) print "(a,i2,a,i2,a,i8)",'DEBUG: reading column ',icol,' type ',itype,' -> '//trim(label(icolput)) ! check column is within array limits if (icolput.gt.size(dat(1,:,1)) .or. icolput.eq.0) then print "(a,i2,a)",' ERROR: column = ',icolput,' out of range in receive_data_fromc' return endif ! ensure no array overflows nmax = min(npartoftypei,size(dat(:,1,1))) ! copy data into main splash array dat(1:nmax,icolput,1) = real(temparr(1:nmax)) ! set particle type if (size(iamtype(:,1)).gt.1) then do i=1,nmax iamtype(i,1) = itype + 1 enddo endif return end subroutine read_silo_data_fromc !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass, & ih,irho,ipr,iutherm,iBfirst,idivB,iax use params use settings_data, only:ndim,ndimV,ncolumns,ntypes,UseTypeInRenderings,iformat use geometry, only:labelcoord use system_utils, only:envlist,ienvironment use siloread, only:blocklabel use asciiutils, only:lcase implicit none integer :: i,j,icol,irank if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif ix = 0 iutherm = 0 do icol=1,size(blocklabel) select case(trim(lcase(blocklabel(icol)))) case('x') ix(1) = icol case('y') ix(2) = icol case('z') ix(3) = icol case('vx') ivx = icol case('ax') iax = icol case('h') ih = icol case('mass') ipmass = icol case('density') irho = icol end select label(icol) = trim(blocklabel(icol)) enddo ! set labels of the quantities read in if (ix(1).gt.0) label(ix(1:ndim)) = labelcoord(1:ndim,1) !if (irho.gt.0) label(irho) = 'density' !if (iutherm.gt.0) label(iutherm) = 'u' !if (ipmass.gt.0) label(ipmass) = 'particle mass' !if (ih.gt.0) label(ih) = 'h' ! set labels for vector quantities if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'_'//labelcoord(i,1) enddo endif if (iax.gt.0) then iamvec(iax:iax+ndimV-1) = iax labelvec(iax:iax+ndimV-1) = 'a' do i=1,ndimV label(iax+i-1) = trim(labelvec(iax))//'_'//labelcoord(i,1) enddo endif ! set labels for each particle type labeltype(1) = 'gas' UseTypeInRenderings(:) = .false. UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels subroutine set_blocklabel(icol,name) bind(c) use, intrinsic :: iso_c_binding, only:c_int, c_char use siloread, only:blocklabel use asciiutils, only:fstring implicit none integer(kind=c_int), intent(in) :: icol character(kind=c_char), intent(in) :: name(256) blocklabel(icol) = trim(fstring(name)) !print*,icol,' name = ',trim(blocklabel(icol)) end subroutine set_blocklabel splash/src/write_data_phantom.f90000644 000766 000000 00000022350 13261626263 017746 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------------- ! Module implementing "splash to phantom" operation, writing ! a binary dump file suitable for input to the PHANTOM code !----------------------------------------------------------------- module write_data_phantom implicit none character(len=10), parameter, public :: formatname='phantom' public :: write_sphdata_phantom private contains subroutine write_sphdata_phantom(time,gamma,dat,ntotal,ntypes,npartoftype, & masstype,ncolumns,filename) use labels, only:labeltype,ih,ivx,iBfirst,ipmass,ix,iutherm use settings_units, only:units use settings_data, only:ndim,UseTypeInRenderings use params, only:int8,doub_prec,sing_prec implicit none integer, intent(in) :: ntotal,ntypes,ncolumns integer, intent(in) :: npartoftype(:) real, intent(in) :: time,gamma real, intent(in) :: dat(ntotal,ncolumns) real, intent(in) :: masstype(:) character(len=*), intent(in) :: filename integer, parameter :: i_int = 1, & i_int1 = 2, & i_int2 = 3, & i_int4 = 4, & i_int8 = 5, & i_real = 6, & i_real4 = 7, & i_real8 = 8 integer, parameter :: idump = 83 character(len=len(filename)+10) :: outfile integer, parameter :: intval1=690706,intval2=780806 integer, parameter :: idimhead = 22 integer(kind=int8) :: nparttot,npartoftypetot(5),number8 integer :: nums(8) integer :: narraylengths,nblocks,nblockarrays integer :: i,j,ierr,i1,index1,number,npart real :: rheader(idimhead) real(doub_prec) :: udist,umass,utime,umagfd real :: r1,hfact logical :: mhd ! !--define output file name ! outfile=trim(filename)//'.tmp' narraylengths = 2 nblocks = 1 ! not parallel dump hfact = 1.2 ! must be specified in phantom dumps ! !--check if we have enough data to write a PHANTOM dump ! if (ndim < 3) then print "(a)",' ERROR: ndim < 3 but must be 3 for PHANTOM data -- cannot write PHANTOM dump, skipping...' return endif if (any(ix(:) <= 0)) then print "(a)",' ERROR: position labels not set -- cannot write PHANTOM dump, skipping...' return endif if (ivx <= 0) then print "(a)",' ERROR: velocity not found in data -- cannot write PHANTOM dump, skipping...' return endif if (ih <= 0) then print "(a)",' ERROR: smoothing length not found in data -- cannot write PHANTOM dump, skipping...' return endif mhd = .false. if (iBfirst > 0) then mhd = .true. narraylengths = 4 endif !--fill rheader and check that we have equal mass particles rheader(:) = 0. rheader(1) = time rheader(3) = gamma rheader(6) = hfact if (ipmass > 0) then index1 = 1 do i=1,ntypes rheader(14+i) = dat(index1,ipmass) if (npartoftype(i) > 0) then if (any(dat(index1:index1+npartoftype(i)-1,ipmass).ne.dat(index1,ipmass))) then print*,' ERROR: unequal mass particles detected but PHANTOM only accepts equal mass, skipping...' return endif index1 = index1 + npartoftype(i) endif enddo else do i=1,ntypes rheader(14+i) = masstype(i) enddo endif write(*,"(/,/,'--------> TIME = ',f10.4,"// & "': full dump written to file ',a,' on unit ',i2,' <--------',/)") & time,trim(outfile),idump open(unit=idump,file=outfile,status='new',form='unformatted',iostat=ierr) if (ierr /= 0) then write(*,*) 'error: can''t create new dumpfile '//trim(outfile) return endif ! !--write full dump Phantom/sphNG file ! i1 = intval1 r1 = real(intval2) write (idump, err=100) intval1,r1,intval2,i1,intval1 write (idump, err=100) fileident('F','Phantom',mhd=mhd) npart = npartoftype(1) npartoftypetot(:) = 0 do i=2,ntypes if (all(UseTypeInRenderings(1:i))) then npart = npart + npartoftype(i) if (npartoftype(i) > 0) print "(a)",' WARNING: assuming '// & trim(labeltype(i))//' particles are same as gas particles' if (rheader(15) <= 0.) then rheader(15) = masstype(i) rheader(15+i) = 0. elseif (abs(masstype(i)-rheader(15)) < tiny(masstype)) then print*,' WARNING! WARNING! mass of '//trim(labeltype(i))// & ' particles differs from '//trim(labeltype(1))//' particles' print*,' Assuming all particles have '//trim(labeltype(1))//' particle mass' endif endif enddo npartoftypetot(1) = npart nparttot = npart ! !--single values ! !--default int number = 7 write (idump, err=100) number write (idump, err=100) int(nparttot),(int(npartoftypetot(i)),i=1,5),nblocks !--int*1, int*2, int*4 number = 0 do i = 1, 3 write (idump, err=100) number end do !--int*8 number = 1 + ntypes write (idump, err=100) number write (idump, err=100) nparttot,npartoftypetot(1:ntypes) !--default real write (idump, err=100) idimhead write (idump, err=100) rheader(1:idimhead) !--real*4 number = 0 write (idump, err=100) number !--real*8 udist = units(ix(1)) utime = units(0) if (ipmass > 0) then umass = units(ipmass) else print "(a)",' WARNING: units for mass unknown, written as 1.0' umass = 1.0d0 endif if (iBfirst > 0) then umagfd = units(iBfirst) number = 4 write (idump, err=100) number write (idump, err=100) udist, umass, utime, umagfd else number = 3 write (idump, err=100) number write (idump, err=100) udist, umass, utime endif nblockarrays = narraylengths*nblocks write (idump, err=100) nblockarrays ! !--array length 1 header ! number8 = npart nums(:) = 0 if (iutherm.gt.0) then nums(i_real) = 7 else nums(i_real) = 6 endif nums(i_real4) = 1 write (idump, err=100) number8, (nums(i), i=1,8) ! !--array length 2 header ! number8 = 0 nums(:) = 0 write (idump, err=100) number8, (nums(i), i=1,8) ! !--array length 3 header ! if (narraylengths >= 3) then number8 = 0 nums(1:8) = 0 write (idump, err=100) number8, (nums(i), i=1,8) endif ! !--array length 4 header ! if (narraylengths >= 4) then if (mhd) then number8 = npart else number8 = 0 endif nums(:) = 0 if (mhd) nums(i_real4) = 3 write (idump, err=100) number8, (nums(i), i=1,8) endif ! !--array length 1 arrays ! !--default int !--int*1 !--int*2 !--int*4 !--int*8 !--default real do j = 1, 3 write (idump, err=100) (dat(i,ix(j)), i=1, npart) end do do j = 1, 3 write (idump, err=100) (dat(i,ivx+j-1), i=1, npart) end do if (iutherm.gt.0) then write (idump, err=100) (dat(i,iutherm), i=1, npart) endif !--real*4 ! dump smoothing length as a real*4 to save space write (idump, err=100) (real(dat(i,ih),kind=sing_prec), i=1, npart) if (mhd) then do j=1,3 write(idump,err=100) (real(dat(i,iBfirst+j-1),kind=sing_prec),i=1, npart) enddo endif close(unit=idump) return 100 continue write(*,*) 'error whilst writing dumpfile '//trim(outfile) close(unit=idump) end subroutine write_sphdata_phantom !-------------------------------------------------------------------- !+ ! contruct header string based on compile-time options ! these are for information only (ie. not important for restarting) !+ !-------------------------------------------------------------------- character(len=100) function fileident(firstchar,codestring,mhd) implicit none character(len=1), intent(in) :: firstchar character(len=*), intent(in), optional :: codestring logical, intent(in), optional :: mhd character(len=10) :: datestring, timestring, string logical :: gotmhd ! !--print date and time stamp in file header ! call date_and_time(datestring,timestring) datestring = datestring(7:8)//'/'//datestring(5:6)//'/'//datestring(1:4) timestring = timestring(1:2)//':'//timestring(3:4)//':'//timestring(5:) string = ' ' if (present(codestring)) then fileident = firstchar//':'//trim(codestring) else fileident = firstchar//':Phantom' endif gotmhd = .false. if (present(mhd)) gotmhd = mhd if (gotmhd) then fileident = trim(fileident)//' (mhd'//trim(string)//') : '//trim(datestring)//' '//trim(timestring) else fileident = trim(fileident)//' (hydro'//trim(string)//'): ' & //trim(datestring)//' '//trim(timestring) endif end function fileident end module write_data_phantom splash/src/interactive.f90000644 000766 000000 00000324755 13261626263 016430 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- module interactive_routines use colourbar, only:barisvertical,incolourbar,incolourbarlabel,adjustcolourbar implicit none public :: interactive_part,interactive_step,interactive_multi private :: mvlegend,mvtitle,save_limits,save_rotation private :: get_vptxy real, private :: xcursor = 0.5 real, private :: ycursor = 0.5 private contains ! !--interactive tools on particle plots ! allows user to change settings interactively ! ! Arguments: ! ! INPUT: ! npart : number of particles plotted ! iplotx : quantity plotted as x axis ! iploty : quantity plotted as y axis ! iplotz : quantity to use in selecting particles ! irender : quantity rendered ! xcoords(npart) : x coordinates of particles ! ycoords(npart) : y coordinates of particles ! zcoords(npart) : z coordinates (or third quantity) of particles ! hi(npart) : smoothing lengths of particles ! zmin, zmax : range of z within which to plot particles ! istep : current step position ! ilaststep : position of last timestep ! ! CHANGEABLE: ! icolourpart(npart) : flag indicating colour of particles ! xmin, xmax, ymin, ymax : current plot limits ! rendermin, rendermax : current rendering limits ! vecmax : maximum vector limits ! ! OUTPUT: ! iadvance : integer telling the loop how to advance the timestep ! irerender : if set, redo rendering. Anything which requires rendering ! to be recalculated must set this ! subroutine interactive_part(npart,iplotx,iploty,iplotz,irender,icontour,ivecx,ivecy, & xcoords,ycoords,zcoords,hi,icolourpart,iamtype,usetype,npartoftype,xmin,xmax,ymin,ymax, & rendermin,rendermax,renderminadapt,rendermaxadapt,contmin,contmax,& contminadapt,contmaxadapt,vecmax, & anglex,angley,anglez,ndim,xorigin,x_sec,zslicepos,dzslice, & zobserver,dscreen,use3Dopacity,taupartdepth,double_rendering,irerender,itrackpart,icolourscheme, & iColourBarStyle,labelrender,iadvance,istep,ilaststep,iframe,nframes,interactivereplot) use settings_xsecrot, only:setsequenceend use shapes, only:inshape,edit_shape,edit_textbox,delete_shape,nshapes,add_textshape use multiplot, only:itrans use labels, only:is_coord,ix use limits, only:assert_sensible_limits use settings_render, only:projlabelformat,iapplyprojformat use settings_data, only:ndataplots,ntypes,icoords,icoordsnew use plotlib, only:plot_qwin,plot_curs,plot_sfs,plot_circ,plot_line,plot_pt1, & plot_rect,plot_band,plot_sfs,plot_qcur,plot_left_click,plot_right_click,& plot_scroll_left,plot_scroll_right,plotlib_is_pgplot,& plot_shift_click,plot_lcur,plot_poly use params, only:int1,maxparttypes use part_utils, only:igettype use particleplots, only:plot_kernel_gr implicit none integer, intent(in) :: npart,icontour,ndim,iplotz,ivecx,ivecy,istep,ilaststep,iframe,nframes integer, intent(inout) :: irender,iColourBarStyle integer, intent(inout) :: iplotx,iploty,itrackpart,icolourscheme integer, intent(out) :: iadvance integer, dimension(npart), intent(inout) :: icolourpart real, dimension(npart), intent(in) :: xcoords,ycoords,zcoords,hi integer(kind=int1), dimension(:), intent(in) :: iamtype logical, dimension(maxparttypes), intent(in) :: usetype integer, dimension(maxparttypes), intent(in) :: npartoftype real, intent(inout) :: xmin,xmax,ymin,ymax,rendermin,rendermax,vecmax,contmin,contmax,taupartdepth real, intent(inout) :: anglex,angley,anglez,zslicepos,dzslice,zobserver,dscreen real, intent(in) :: renderminadapt,rendermaxadapt,contminadapt,contmaxadapt real, intent(in), dimension(ndim) :: xorigin character(len=*), intent(inout) :: labelrender logical, intent(inout) :: x_sec logical, intent(out) :: irerender,interactivereplot logical, intent(in) :: use3Dopacity, double_rendering real, parameter :: pi=3.141592653589 integer, parameter :: maxpts = 64 integer :: i,iclosest,ierr,ixsec,ishape,itype,npts integer :: nmarked,ncircpart,itrackparttemp,iadvancenew integer, dimension(1000) :: icircpart real :: xpt,ypt real :: xpt2,ypt2,xcen,ycen,xminwin,xmaxwin,yminwin,ymaxwin real :: xptmin,xptmax,yptmin,yptmax,zptmin,zptmax,rptmax2 real :: rmin,rr,gradient,yint,dx,dy,dr,anglerad real :: xlength,ylength,renderlength,renderpt,zoomfac real :: dxlength,dylength,xmaxin,ymaxin,contlength real, dimension(4) :: xline,yline real, dimension(maxpts) :: xpts,ypts character(len=1) :: char,char2 logical :: iexit, rotation, verticalbar, iamincolourbar, mixedtypes, use3Dperspective logical :: iadvanceset, leftclick, iselectpoly, iselectcircle logical, save :: print_help = .true. logical, save :: in_movie_mode = .false. if (plot_qcur()) then if (.not.print_help) print*,'entering interactive mode...press h in plot window for help' !print*, plot_left_click else !print*,'cannot enter interactive mode: device has no cursor' return endif mixedtypes = size(iamtype).ge.npart use3Dperspective = abs(dscreen).gt.tiny(0.) iadvanceset = .false. char = 'A' xline = 0. yline = 0. ! !--convert saved cursor position (saved in viewport coords) ! back to coordinates ! call plot_qwin(xminwin,xmaxwin,yminwin,ymaxwin) call get_posxy(xcursor,ycursor,xpt,ypt,xminwin,xmaxwin,yminwin,ymaxwin) ! xpt = 0. ! ypt = 0. xpt2 = 0. ypt2 = 0. xmaxin = xmax ymaxin = ymax zoomfac = 1.0 ncircpart = 0 itrackparttemp = itrackpart iexit = .false. rotation = .false. irerender = .false. interactivereplot = .false. if (is_coord(iplotx,ndim) .and. is_coord(iploty,ndim) .and. ndim.ge.2) rotation = .true. verticalbar = barisvertical(iColourBarStyle) if (iplotz.gt.0 .and. x_sec) then zptmin = zslicepos - 0.5*dzslice zptmax = zslicepos + 0.5*dzslice else !--if not using z range, make it encompass all the particles zptmin = -huge(zptmin) zptmax = huge(zptmax) endif interactiveloop: do while (.not.iexit) if (print_help) then char = 'h' ierr = 0 print_help = .false. else ierr = plot_curs(xpt,ypt,char) endif ! !--exit if the device is not interactive ! if (ierr.eq.1) return !print*,'x,y = ',xpt,ypt,' function = ',char,iachar(char) ! !--find closest particle ! rmin = 1.e6 iclosest = 0 xlength = xmax - xmin ylength = ymax - ymin if (xlength.gt.tiny(xlength)) then dxlength = 1./xlength else dxlength = 0. endif if (ylength.gt.tiny(ylength)) then dylength = 1./ylength else dylength = 0. endif over_npart: do i=1,npart if (ntypes.gt.1) then if (mixedtypes) then itype = int(iamtype(i)) else itype = igettype(i,npartoftype) endif if (.not.usetype(itype)) cycle over_npart endif !--use distance normalised on screen rr = ((xcoords(i)-xpt)*dxlength)**2 + ((ycoords(i)-ypt)*dylength)**2 if (rr.lt.rmin) then iclosest = i rmin = rr endif enddo over_npart !--query the position of the colour bar iamincolourbar = incolourbar(iColourBarStyle,4,xpt,ypt,xmin,xmax,ymin,ymax) select case(char) ! !--particle plot stuff ! case('p') if (iclosest.gt.0 .and. iclosest.le.npart) then print*,' closest particle = ',iclosest,'x = ',xcoords(iclosest),' y =',ycoords(iclosest) call plot_number(iclosest,xcoords(iclosest),ycoords(iclosest)) else print*,'error: could not determine closest particle' endif case('c') if (iclosest.gt.0 .and. iclosest.le.npart) then print*,'plotting circle of interaction on particle ',iclosest, & ' h = ',hi(iclosest) !--save settings for these ncircpart = ncircpart + 1 if (ncircpart.gt.size(icircpart)) then print*,'WARNING: ncircles > array limits, cannot save' ncircpart = size(icircpart) else icircpart(ncircpart) = iclosest endif call plot_sfs(2) if (icoordsnew.ne.icoords) then call plot_kernel_gr(icoordsnew,icoords,iplotx,iploty,& xcoords(iclosest),ycoords(iclosest),zcoords(iclosest),2.*hi(iclosest)) else call plot_circ(xcoords(iclosest),ycoords(iclosest),2.*hi(iclosest)) endif else print*,'error: could not determine closest particle' endif case('t') !--track closest particle (must save to activate) if (itrackpart.ne.0 .and. itrackparttemp.eq.itrackpart) then itrackpart = 0 itrackparttemp = 0 print*,' particle tracking limits OFF' else if (iclosest.gt.0 .and. iclosest.le.npart) then itrackparttemp = iclosest call plot_number(iclosest,xcoords(iclosest),ycoords(iclosest)) print*,' limits set to track particle ',itrackparttemp,' x, y = ', & xcoords(iclosest),ycoords(iclosest) print*,' save settings to activate ' else print*,'error: could not determine closest particle' endif endif case('g') ! draw a line between two points xline(2) = xpt yline(2) = ypt !--mark first point call plot_pt1(xpt,ypt,4) !--select second point print*,' select another point (using left click or g) to plot line ' ierr = plot_band(1,1,xline(2),yline(2),xline(3),yline(3),char2) !--draw line if left click or g select case(char2) case(plot_left_click,'g') print* !--mark second point call plot_pt1(xline(3),yline(3),4) xlength = xline(3)-xline(2) if (abs(xlength).lt.tiny(xlength)) then xline(1) = xline(2) xline(4) = xline(2) yline(1) = ymin yline(4) = ymax print*,' error: gradient = infinite' elseif (xline(2).lt.xline(3)) then xline(1) = xmin xline(4) = xmax else xline(1) = xmax xline(4) = xmin endif ylength = yline(3)-yline(2) dr = sqrt(xlength**2 + ylength**2) print*,' (x1,y1) = (',xline(2),',',yline(2),')' print*,' (x2,y2) = (',xline(3),',',yline(3),')' print*,' dr = ',dr,' dx = ',xlength,' dy = ',ylength if (abs(xlength).gt.tiny(xlength)) then gradient = ylength/xlength yint = yline(3) - gradient*xline(3) print*,' gradient = ',gradient,' y intercept = ',yint yline(1) = gradient*xline(1) + yint yline(4) = gradient*xline(4) + yint endif !--plot line joining the two points call plot_line(4,xline,yline) case default print*,' action cancelled' end select ! !--help ! case('h') print "(/,a)",' -------------- interactive mode commands -----------------------------' print*,' SPACE BAR (or n) : skip to next timestep/file' print*,' 0,1,2,3..9 and click : go forward/back n timesteps (back=r.click)' print*,' left click (or A) : zoom/select' print*,' right click (or X or b) : previous timestep' print*,' shift+left click : IRREGULAR particle selection' print*,' left click on colour bar : change rendering limits' print*,' +/- : zoom IN/OUT (_ for out by 20%) ' print*,' a : (a)dapt plot limits (inside box, over axes or colour bar)' print*,' l : (l)og / unlog axis (over x/y axis or colour bar)' print*,' o/C : re-centre plot on (o)rigin/(C)ursor position' print*,' backspace: delete annotation (over axes, legend, title or colour bar)' print*,' r : (r)eplot current plot' print*,' R : (R)eset/remove all range restrictions' print*,' p/c : label closest (p)article/plot (c)ircle of interaction' print*,' t : t)rack closest particle/turn tracking off (coord plots only)' print*,' g : plot a line and find its g)radient' print*,' ctrl-t : add text annotation at current position' print*,' ctrl-m : toggle Hollywood mode' print*,' G/T/H : move le(G)end, (T)itle or (H) vector legend to current position' print*,' m/M/i : change colour map (m=next,M=previous,i=invert) (rendered plots only)' if (irender.gt.0) print*,' f/F : f)lip to next/previous column in rendering (rendered plots only)' print*,' v/V/w : decrease/increase/adapt arrow size on vector plots (Z for x10)' if (ndim.ge.3) then print*,' k/K : decrease/increase opacity on opacity-rendered plots (Z for x10)' endif print*,' e/E : use current frame/settings as end point to animation sequence' if (ndim.gt.1) then print*,' , . < > : rotate about z axis by +(-) 15,30 degrees (coord plots only)' if (ndim.ge.3) then print*,' [ ] { } : rotate about x axis by +/- 15,30 degrees (coord plots only)' print*,' / \ ? | : rotate about y axis by +/- 15,30 degrees (coord plots only)' print*,' x : take cross section (coord plots only)' if (iplotz.gt.0) then print*,' u/U/d/D : move cross section/perspective pos. up/down (towards/away from observer)' endif endif endif print*,' s : (s)ave current settings for all steps' print*,' q/Q/esc : (q)uit plotting' print*,' z/Z(oom) : timestepping, zoom and limits-changing multiplied by 10' print*,'----------------------------------------------------------------------' case('s','S') itrackpart = itrackparttemp if (itrackpart.eq.0) then call save_limits(iplotx,xmin,xmax) call save_limits(iploty,ymin,ymax) call save_itrackpart_recalcradius(itrackpart) ! set saved value to zero else print*,'tracking particle ',itrackpart,'x,y = ',xcoords(itrackpart),ycoords(itrackpart) if (is_coord(iplotx,ndim)) call save_limits_track(iplotx,xmin,xmax,xcoords(itrackpart)) if (is_coord(iploty,ndim)) call save_limits_track(iploty,ymin,ymax,ycoords(itrackpart)) call save_itrackpart_recalcradius(itrackpart) endif if (irender.gt.0) call save_limits(irender,rendermin,rendermax) if (icontour.gt.0) then if (icontour.eq.irender & .and. abs(rendermin-contmin).le.tiny(0.) & .and. abs(rendermax-contmax).le.tiny(0.)) then call reset_limits2(icontour) elseif (icontour.eq.irender .and. .not.double_rendering) then call save_limits(icontour,contmin,contmax,setlim2=.true.) else call save_limits(icontour,contmin,contmax) endif endif if (ivecx.gt.0 .and. ivecy.gt.0) then call save_limits(ivecx,-vecmax,vecmax) call save_limits(ivecy,-vecmax,vecmax) endif if (ndim.eq.3 .and. iplotz.gt.0 .and. irender.gt.0 .and. use3Dopacity) then call save_opacity(taupartdepth) endif if (ncircpart.gt.0) call save_circles(ncircpart,icircpart) if (rotation) call save_rotation(ndim,anglex,angley,anglez) if (iplotz.gt.0) then if (x_sec) then call save_xsecpos(zslicepos,x_sec) else call save_perspective(zobserver,dscreen) endif endif print*,'> interactively set limits saved <' ! !--actions on left click ! case(plot_left_click,plot_right_click,plot_shift_click) ! left click ! !--change colour bar limits ! leftclick = (char == plot_left_click) ishape = inshape(xpt,ypt,itrans(iplotx),itrans(iploty)) if (ishape.gt.0) then call edit_shape(ishape,xpt,ypt,itrans(iplotx),itrans(iploty)) iadvance = 0 interactivereplot = .true. iexit = .true. elseif (iamincolourbar .and. irender.gt.0 .and. leftclick) then if (incolourbarlabel(iColourBarStyle,4,xpt,ypt,xmin,xmax,ymin,ymax)) then if (verticalbar) then call edit_textbox(xpt,ypt,90.,labelrender) projlabelformat = trim(labelrender) iapplyprojformat = irender else call edit_textbox(xpt,ypt,0.,labelrender) projlabelformat = trim(labelrender) iapplyprojformat = irender endif iadvance = 0 interactivereplot = .true. iexit = .true. else print*,'click to set rendering limits' if (verticalbar) then ierr = plot_band(3,1,xpt,ypt,xpt2,ypt2,char2) else ierr = plot_band(4,1,xpt,ypt,xpt2,ypt2,char2) endif if (char2 == plot_left_click) then if (double_rendering) then call adjustcolourbar(iColourBarStyle,xpt,ypt,xpt2,ypt2,& xmin,xmax,ymin,ymax,contmin,contmax) print*,'setting doublerender min = ',contmin print*,'setting doublerender max = ',contmax else call adjustcolourbar(iColourBarStyle,xpt,ypt,xpt2,ypt2,& xmin,xmax,ymin,ymax,rendermin,rendermax) print*,'setting render min = ',rendermin print*,'setting render max = ',rendermax endif iadvance = 0 interactivereplot = .true. iexit = .true. endif endif ! !--zoom or mark particles ! else print*,'select area: ' !--Note: circle selection is not implemented in PGPLOT iselectpoly = (char==plot_shift_click .or.((.not.leftclick).and.plotlib_is_pgplot)) iselectcircle = (char==plot_right_click .and. .not.plotlib_is_pgplot) if (iselectpoly) then if (plotlib_is_pgplot) then print*,'left click/a)dd points; middle click/d)elete points; X/x)finish' else print*,'left click/a)dd points; middle click/d)elete points; q)uit/abort' if (irender.le.0) print*,'1-9 = close polygon and mark particles with colours 1-9' print*,'0 = close polygon and hide selected particles' print*,'p = close polygon and plot selected particles only' print*,'c = close polygon and plot circles of interaction on selected parts' endif else print*,'left click : zoom' if (irender.le.0) print*,'1-9 = mark selected particles with colours 1-9' print*,'0 = hide selected particles' print*,'p = plot selected particles only' print*,'c = plot circles of interaction on selected parts' endif if (leftclick .or. iselectcircle) then print*,'x = use particles within x parameter range only' print*,'y = use particles within y parameter range only' print*,'r = use particles within x and y parameter range only' print*,'R = remove all range restrictions' endif npts = 1 xpts(1) = xpt ! to avoid problems with uninitialised variables ypts(1) = ypt if (iselectpoly) then call plot_lcur(maxpts,npts,xpts,ypts,char2) if (plotlib_is_pgplot) then if (irender.le.0) print*,'1-9 = close polygon and mark particles with colours 1-9' print*,'0 = close polygon and hide selected particles' print*,'p = close polygon and plot selected particles only' print*,'c = close polygon and plot circles of interaction on selected parts' ierr = plot_band(0,1,xpt,ypt,xpt2,ypt2,char2) endif xptmin = minval(xpts(1:npts)) xptmax = maxval(xpts(1:npts)) yptmin = minval(ypts(1:npts)) yptmax = maxval(ypts(1:npts)) elseif (iselectcircle) then ierr = plot_band(8,1,xpt,ypt,xpt2,ypt2,char2) rptmax2 = (xpt2-xpt)**2 + (ypt2-ypt)**2 rr = sqrt(rptmax2) xptmin = xpt - rr xptmax = xpt + rr yptmin = ypt - rr yptmax = ypt + rr else ! left click: rectangle selection ierr = plot_band(2,1,xpt,ypt,xpt2,ypt2,char2) xptmin = min(xpt,xpt2) xptmax = max(xpt,xpt2) yptmin = min(ypt,ypt2) yptmax = max(ypt,ypt2) endif if (.not.(iselectcircle.or.iselectpoly) .or. char2==plot_left_click) then ! rectangle selection print*,'xrange = ',xptmin,'->',xptmax print*,'yrange = ',yptmin,'->',yptmax if (iplotz.ne.0 .and. x_sec) then print*,'(zrange = ',zptmin,'->',zptmax,')' endif endif select case (char2) case(plot_left_click) ! zoom if another left click call plot_sfs(2) !--draw the selected shape in case zooming is slow if (iselectpoly) then call plot_poly(npts,xpts,ypts) elseif (iselectcircle) then call plot_circ(xpt,ypt,sqrt(rptmax2)) else call plot_rect(xpt,xpt2,ypt,ypt2) endif !--zoom is identical for rectangle, poly and circle selection xmin = xptmin xmax = xptmax ymin = yptmin ymax = yptmax iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. case('0','1','2','3','4','5','6','7','8','9') ! mark particles nmarked = 0 if (irender.le.0 .or. char2.eq.'0' .or. char2.eq.'1') then do i=1,npart if (inslice(zcoords(i),zptmin,zptmax) .and. & (leftclick .and. inrectangle(xcoords(i),ycoords(i),xptmin,xptmax,yptmin,yptmax) & .or.(iselectcircle .and. incircle(xcoords(i)-xpt,ycoords(i)-ypt,rptmax2)) & .or.(iselectpoly .and. inpoly(xcoords(i),ycoords(i),xpts,ypts,npts)))) then read(char2,*,iostat=ierr) icolourpart(i) if (ierr /=0) then print*,'*** error marking particle' icolourpart(i) = 1 !--translate 0 to icolourpart = -1 for non-plotted particles elseif (icolourpart(i).eq.0) then icolourpart(i) = -1 endif nmarked = nmarked + 1 endif enddo print*,'marked ',nmarked,' particles in selected region' endif iadvance = 0 if (nmarked.gt.0) irerender = .true. interactivereplot = .true. iexit = .true. case('p') ! plot selected particles only nmarked = 0 do i=1,npart if (inslice(zcoords(i),zptmin,zptmax) .and. & (leftclick .and. inrectangle(xcoords(i),ycoords(i),xptmin,xptmax,yptmin,yptmax) & .or.(iselectcircle .and. incircle(xcoords(i)-xpt,ycoords(i)-ypt,rptmax2)) & .or.(iselectpoly .and. inpoly(xcoords(i),ycoords(i),xpts,ypts,npts)))) then nmarked = nmarked + 1 if (icolourpart(i).le.0) icolourpart(i) = 1 else icolourpart(i) = -1 endif enddo print*,'plotting selected ',nmarked,' particles only' iadvance = 0 irerender = .true. interactivereplot = .true. iexit = .true. case('c') ! set circles of interaction in marked region ncircpart = 0 do i=1,npart if (inslice(zcoords(i),zptmin,zptmax) .and. & (leftclick .and. inrectangle(xcoords(i),ycoords(i),xptmin,xptmax,yptmin,yptmax) & .or.(iselectcircle .and. incircle(xcoords(i)-xpt,ycoords(i)-ypt,rptmax2)) & .or.(iselectpoly .and. inpoly(xcoords(i),ycoords(i),xpts,ypts,npts)))) then if (ncircpart.lt.size(icircpart)) then ncircpart = ncircpart + 1 icircpart(ncircpart) = i call plot_sfs(2) call plot_circ(xcoords(i),ycoords(i),2.*hi(i)) endif endif enddo print*,'set ',ncircpart,' circles of interaction in selected region' if (ncircpart.eq.size(icircpart)) print*,' (first ',size(icircpart),' only)' case('x') call restrict_range(iplotx,xptmin,xptmax) iadvance = 0 irerender = .true. interactivereplot = .true. iexit = .true. case('y') call restrict_range(iploty,yptmin,yptmax) iadvance = 0 irerender = .true. interactivereplot = .true. iexit = .true. case('r') call restrict_range(iplotx,xptmin,xptmax) call restrict_range(iploty,yptmin,yptmax) iadvance = 0 irerender = .true. interactivereplot = .true. iexit = .true. case('R') call reset_ranges iadvance = 0 irerender = .true. interactivereplot = .true. iexit = .true. case default print*,' action cancelled' end select endif ! !--zooming ! case('-','_','+','o','C') ! zoom in/out xlength = xmax - xmin ylength = ymax - ymin xcen = 0.5*(xmax + xmin) ycen = 0.5*(ymax + ymin) renderlength = rendermax - rendermin contlength = contmax - contmin select case(char) case('-') xlength = 1.1*zoomfac*xlength ylength = 1.1*zoomfac*ylength renderlength = 1.1*zoomfac*renderlength contlength = 1.1*zoomfac*contlength case('_') xlength = 1.2*zoomfac*xlength ylength = 1.2*zoomfac*ylength renderlength = 1.2*zoomfac*renderlength contlength = 1.2*zoomfac*contlength case('+') xlength = xlength/(1.1*zoomfac) ylength = ylength/(1.1*zoomfac) renderlength = renderlength/(1.1*zoomfac) contlength = contlength/(1.1*zoomfac) case('o') if (itrackpart.gt.0) then print*,'centreing limits on tracked particle ',itrackpart,'x,y = ',xcoords(itrackpart),ycoords(itrackpart) xcen = xcoords(itrackpart) ycen = ycoords(itrackpart) else if (is_coord(iplotx,ndim)) then xcen = xorigin(iplotx) else xcen = 0. endif if (is_coord(iploty,ndim)) then ycen = xorigin(iploty) else ycen = 0. endif print*,' centreing plot on origin x,y = ',xcen,ycen endif case('C') xcen = xpt ycen = ypt end select if (iamincolourbar .and. irender.gt.0) then !--rendering zoom does not allow pan - renderpt is always centre of axis if (double_rendering) then renderpt = 0.5*(contmin + contmax) contmin = renderpt - 0.5*contlength contmax = renderpt + 0.5*contlength call assert_sensible_limits(contmin,contmax) print*,'zooming on colour bar: min, max = ',contmin,contmax else renderpt = 0.5*(rendermin + rendermax) rendermin = renderpt - 0.5*renderlength rendermax = renderpt + 0.5*renderlength call assert_sensible_limits(rendermin,rendermax) print*,'zooming on colour bar: min, max = ',rendermin,rendermax endif iadvance = 0 interactivereplot = .true. iexit = .true. else if (xpt.ge.xmin .and. xpt.le.xmax .and. ypt.le.ymaxin) then xmin = xcen - 0.5*xlength xmax = xcen + 0.5*xlength call assert_sensible_limits(xmin,xmax) print*,'zooming on x axis: min, max = ',xmin,xmax iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif if (ypt.ge.ymin .and. ypt.le.ymax .and. xpt.le.xmaxin) then ymin = ycen - 0.5*ylength ymax = ycen + 0.5*ylength call assert_sensible_limits(ymin,ymax) print*,'zooming on y axis: min, max = ',ymin,ymax iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif endif if (char.eq.'o') then xpt = xcen ypt = ycen endif case('a') ! reset plot limits if (iamincolourbar .and. irender.gt.0) then if (double_rendering) then contmin = contminadapt contmax = contmaxadapt call assert_sensible_limits(contmin,contmax) else rendermin = renderminadapt rendermax = rendermaxadapt call assert_sensible_limits(rendermin,rendermax) endif iadvance = 0 ! that it should change the render limits interactivereplot = .true. iexit = .true. else xmaxin = xmax ymaxin = ymax if (xpt.ge.xmin .and. xpt.le.xmax .and. ypt.le.ymaxin) then call adapt_limits_interactive('x',npart,xcoords,xmin,xmax,icolourpart,iamtype,usetype) iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif if (ypt.ge.ymin .and. ypt.le.ymax .and. xpt.le.xmaxin) then call adapt_limits_interactive('y',npart,ycoords,ymin,ymax,icolourpart,iamtype,usetype) iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif endif ! !--zoom in/out on vector plots (arrow size) ! case('v') if (ivecx.gt.0 .and. ivecy.gt.0) then print*,'decreasing vector arrow size' vecmax = 1.2*zoomfac*vecmax iadvance = 0 interactivereplot = .true. iexit = .true. endif case('V') if (ivecx.gt.0 .and. ivecy.gt.0) then print*,'increasing vector arrow size' vecmax = vecmax/(1.2*zoomfac) iadvance = 0 interactivereplot = .true. iexit = .true. endif case('w','W') if (ivecx.gt.0 .and. ivecy.gt.0) then print*,'adapting vector arrow size' vecmax = -1.0 iadvance = 0 interactivereplot = .true. iexit = .true. endif ! !--change opacity on 3D opacity rendered plots ! case('k') if (ndim.eq.3 .and. iplotz.gt.0 .and. irender.ne.0 .and. use3Dopacity) then print*,'decreasing opacity by factor of ',1.5*zoomfac !--opacity goes like 1/taupartdepth taupartdepth = 1.5*zoomfac*taupartdepth iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('K') if (ndim.eq.3 .and. iplotz.gt.0 .and. irender.ne.0 .and. use3Dopacity) then print*,'increasing opacity by factor of ',1.5*zoomfac !--opacity goes like 1/taupartdepth taupartdepth = taupartdepth/(1.5*zoomfac) iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif ! !--set/unset log axes ! case('l') ! !--change colour bar, y and x itrans between log / not logged ! if (iamincolourbar .and. irender.gt.0) then if (double_rendering) then !call change_itrans(irender,rendermin,rendermax) call change_itrans(icontour,contmin,contmax) else if (icontour.eq.irender) then call change_itrans2(irender,rendermin,rendermax,contmin,contmax) else call change_itrans(irender,rendermin,rendermax) endif endif iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. elseif (xpt.lt.xmin) then if (is_coord(iploty,ndim) .and. irender.gt.0) then print "(a)",'error: cannot log coordinate axes with rendering' else call change_itrans(iploty,ymin,ymax) iadvance = 0 interactivereplot = .true. iexit = .true. endif elseif (ypt.lt.ymin) then if (is_coord(iplotx,ndim) .and. irender.gt.0) then print "(a)",'error: cannot log coordinate axes with rendering' else call change_itrans(iplotx,xmin,xmax) iadvance = 0 interactivereplot = .true. iexit = .true. endif endif ! !--reset all range restrictions ! case('R') call reset_ranges iadvance = 0 interactivereplot = .true. iexit = .true. ! !--save as end point of animation sequence ! case('e','E') call setsequenceend(istep,iplotx,iploty,irender,rotation, & anglex,angley,anglez,zobserver,use3Dopacity,taupartdepth, & x_sec,zslicepos,xmin,xmax,ymin,ymax,rendermin,rendermax) ! !--rotation ! case(',') if (rotation) then print*,'changing z rotation angle by -15 degrees...' anglez = anglez - 15. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('<') if (rotation) then print*,'changing z rotation angle by -30 degrees...' anglez = anglez - 30. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('.') if (rotation) then print*,'changing z rotation angle by 15 degrees...' anglez = anglez + 15. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('>') if (rotation) then print*,'changing z rotation angle by 30 degrees...' anglez = anglez + 30. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('/') if (rotation .and. ndim.ge.2) then print*,'changing y rotation angle by -15 degrees...' angley = angley - 15. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('?') if (rotation .and. ndim.ge.2) then print*,'changing y rotation angle by -30 degrees...' angley = angley - 30. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('\') if (rotation .and. ndim.ge.2) then print*,'changing y rotation angle by 15 degrees...' angley = angley + 15. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('|') if (rotation .and. ndim.ge.2) then print*,'changing y rotation angle by 30 degrees...' angley = angley + 30. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('[') if (rotation .and. ndim.ge.3) then print*,'changing x rotation angle by -15 degrees...' anglex = anglex - 15. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('{') if (rotation .and. ndim.ge.3) then print*,'changing x rotation angle by -30 degrees...' anglex = anglex - 30. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case(']') if (rotation .and. ndim.ge.3) then print*,'changing x rotation angle by 15 degrees...' anglex = anglex + 15. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('}') if (rotation .and. ndim.ge.3) then print*,'changing x rotation angle by 30 degrees...' anglex = anglex + 30. iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif ! !--set cross section position ! case('x') if (rotation .and. ndim.ge.3) then xline(1) = xpt yline(1) = ypt !--work out which is the third dimension do i=1,3 if (i.ne.iplotx .and. i.ne.iploty) ixsec = i enddo print*,' select cross section position (using left click or x)' ierr = plot_band(1,1,xline(1),yline(1),xline(2),yline(2),char2) !--work out cross section if left click or x again select case(char2) case(plot_left_click,'x') !--plot the cross section line call plot_line(2,xline(1:2),yline(1:2)) !--work out angle with the x axis ! and offset of line from origin dx = xline(2) - xline(1) dy = yline(2) - yline(1) anglerad = ATAN2(dy,dx) select case(ixsec) case(1) anglex = 180.*anglerad/pi + anglex print*,'setting angle x = ',anglex case(2) angley = 180.*anglerad/pi + angley print*,'setting angle y = ',angley case(3) anglez = 180.*anglerad/pi + anglez print*,'setting angle z = ',anglez end select iploty = ixsec !--work out offset of cross section line ! y intercept yint = yline(2) - (dy/dx)*xline(2) zslicepos = yint/COS(anglerad) !--if we are in column density mode, change back to cross-section mode x_sec = .true. print*,'iploty = ',ixsec, ' xsecpos = ',zslicepos iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. case default print*,' action cancelled' end select endif ! !--cross sections ! case('u') ! move cross section up by dxsec if (iplotz.gt.0 .and. ndim.eq.3) then if (x_sec) then print*,'shifting cross section position up by ',dzslice zslicepos = zslicepos + dzslice iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. elseif (use3Dperspective) then if (abs(zobserver).lt.tiny(0.)) then print*,'resetting z position' zobserver = 1. else print*,'shifting perspective position up by 20%' zobserver = 1.2*zobserver endif iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif endif case('U') ! move cross section up by 2*dxsec if (iplotz.gt.0 .and. ndim.eq.3) then if (x_sec) then print*,'shifting cross section position up by ',2.*dzslice zslicepos = zslicepos + 2.*dzslice iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. elseif (use3Dperspective) then if (abs(zobserver).lt.tiny(0.)) then print*,'resetting z position' zobserver = 1. else print*,'shifting perspective position up by factor of 2' zobserver = 2.*zobserver endif iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif endif case('d') ! move cross section down by dxsec if (iplotz.gt.0 .and. ndim.eq.3) then if (x_sec) then print*,'shifting cross section position down by ',dzslice zslicepos = zslicepos - dzslice elseif (use3Dperspective) then if (abs(zobserver).lt.tiny(0.)) then print*,'resetting z position' zobserver = 1. else print*,'shifting perspective position down by 20%' zobserver = zobserver/1.2 endif endif iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif case('D') ! move cross section down by 2*dxsec if (iplotz.gt.0 .and. ndim.eq.3) then if (x_sec) then print*,'shifting cross section position down by ',2.*dzslice zslicepos = zslicepos - 2.*dzslice elseif (use3Dperspective) then if (abs(zobserver).lt.tiny(0.)) then print*,'resetting z position' zobserver = 1. else print*,'shifting perspective position down by factor of 2' zobserver = zobserver/2. endif endif iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. endif ! !--general plot stuff ! case('G') ! move legend here print*,'setting legend position to current location...' call mvlegend(xpt,ypt,xmin,xmax,ymax) iadvance = 0 interactivereplot = .true. iexit = .true. case('T') ! move title here print*,'setting title position to current location...' call mvtitle(xpt,ypt,xmin,xmax,ymax) iadvance = 0 interactivereplot = .true. iexit = .true. case('H') ! move vector legend here if (ivecx.gt.0 .and. ivecy.gt.0) then print*,'setting vector plot legend to current location...' call mvlegendvec(xpt,ypt,xmin,xmax,ymax) endif iadvance = 0 interactivereplot = .true. iexit = .true. case('f') ! change rendered quantity (next) if (irender.ne.0 .and. ndim.gt.0) then irender = irender + 1 if (irender.gt.ndataplots) irender = 1 if (is_coord(irender,ndim)) irender = ix(ndim) + 1 !if (irender.eq.ndataplots+1) irender = 0 iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. else print*,'ERROR: f has no effect if not rendering' endif case('F') ! change rendered quantity (previous) if (irender.ne.0 .and. ndim.gt.0) then irender = irender - 1 if (is_coord(irender,ndim)) irender = ix(1) - 1 if (irender.lt.1) irender = ndataplots !if (irender.eq.ndim) irender = 0 iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. else print*,'ERROR: F has no effect if not rendering' endif case(achar(13)) if (in_movie_mode) then call unset_movie_mode() in_movie_mode = .false. else call set_movie_mode() in_movie_mode = .true. endif iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. case('m') ! change colour map (next scheme) call change_colourmap(icolourscheme,1) iadvance = 0 interactivereplot = .true. !irerender = .true. iexit = .true. case('M') ! change colour map (previous scheme) call change_colourmap(icolourscheme,-1) iadvance = 0 interactivereplot = .true. iexit = .true. case('i') ! invert colour map icolourscheme = -icolourscheme call change_colourmap(icolourscheme,0) iadvance = 0 interactivereplot = .true. iexit = .true. case(achar(20)) ! add text shape call add_textshape(xpt,ypt,itrans(iplotx),itrans(iploty),0,ierr) if (ierr.eq.0) then iadvance = 0 interactivereplot = .true. iexit = .true. endif case(achar(8)) ! delete plot annotation / colour bar (backspace) ishape = inshape(xpt,ypt,itrans(iplotx),itrans(iploty)) if (ishape.gt.0) then call delete_shape(ishape,nshapes) iadvance = 0 interactivereplot = .true. iexit = .true. elseif (iamincolourbar .and. irender.gt.0) then iColourBarStyle = 0 iadvance = 0 interactivereplot = .true. iexit = .true. elseif (xpt.lt.xmin .or. xpt.gt.xmax .or. ypt.lt.ymin .or. ypt.gt.ymax) then call deleteaxes() iadvance = 0 interactivereplot = .true. iexit = .true. else print*,' nothing to delete at x,y =',xpt,',',ypt endif ! !--timestepping ! case('q','Q',achar(27),achar(3)) iadvance = -666 print*,'quitting...' iexit = .true. case('b','B',plot_scroll_left) ! right click -> go back iadvance = -abs(iadvance) iexit = .true. case('r') ! replot iadvance = 0 interactivereplot = .true. irerender = .true. iexit = .true. case(' ','n','N',plot_scroll_right) ! space iadvance = abs(iadvance) iexit = .true. case('0','1','2','3','4','5','6','7','8','9') read(char,*,iostat=ierr) iadvancenew if (ierr /=0) then print*,'*** internal error setting timestep jump' iadvancenew = 1 endif if ((iadvance.gt.1 .or. iadvanceset) .and. iadvance.le.9999) then iadvance = 10*iadvance + iadvancenew if (iadvance.gt.9999) iadvance = 1 elseif (iadvancenew.eq.0) then iadvance = 10 else iadvance = iadvancenew endif iadvance = int(zoomfac*iadvance) print*,' setting timestep jump = ',iadvance iadvanceset = .true. case(')') iadvance = int(zoomfac*10) print*,' setting timestep jump = ',iadvance ! !--multiply everything by a factor of 10 ! case('z','Z') zoomfac = 10.*zoomfac if (zoomfac.gt.1000000.) then zoomfac = 1.0 endif print*,' LIMITS/TIMESTEPPING CHANGES NOW x ',zoomfac ! !--unknown ! case default if (iachar(char).ge.iachar('a')) then print*,' x, y = ',xpt,ypt,'; unknown option "',trim(char), '" ',iachar(char) endif print*,' x, y = ',xpt,ypt,'; GOT ',iachar(char) end select ! !--save cursor position relative to the viewport ! call plot_qwin(xminwin,xmaxwin,yminwin,ymaxwin) call get_vptxy(xpt,ypt,xcursor,ycursor) if (rotation) then if (anglez.ge.360.) anglez = anglez - 360. if (anglez.lt.0.) anglez = anglez + 360. if (ndim.gt.2) then if (angley.ge.360.) angley = angley - 360. if (angley.lt.0.) angley = angley + 360. if (anglex.ge.360.) anglex = anglex - 360. if (anglex.lt.0.) anglex = anglex + 360. endif endif ! !--do not let timestep go outside of bounds ! if we are at the first/last step, just print message and do nothing ! if iadvance trips over the bounds, jump to last/first step ! if (iadvance.ne.-666 .and. iexit) then if (istep + iadvance .gt. ilaststep .and. iframe.eq.nframes) then print "(1x,a)",'reached last timestep' if (ilaststep-istep .gt.0) then iadvance= ilaststep - istep else iexit = .false. endif elseif (istep + iadvance .lt. 1 .and. iframe.eq.1) then print "(1x,a)",'reached first timestep: can''t go back' if (1-istep .lt.0) then iadvance= 1 - istep else iexit = .false. endif endif endif enddo interactiveloop return end subroutine interactive_part ! ! cut down version of interactive mode -> controls timestepping only ! used in powerspectrum / extra plots ! THIS IS NOW LARGELY OBSOLETE (superseded by interactive_multi) ! AND WILL BE REMOVED IN FUTURE VERSIONS ! subroutine interactive_step(iadvance,istep,ilaststep,xmin,xmax,ymin,ymax,interactivereplot) use plotlib, only:plot_qcur,plot_curs,plot_band,plot_rect,plot_left_click implicit none integer, intent(inout) :: iadvance integer, intent(in) :: istep,ilaststep real, intent(inout) :: xmin,xmax,ymin,ymax logical, intent(out) :: interactivereplot integer :: ierr real :: xpt,ypt,xpt2,ypt2 real :: xlength, ylength, zoomfac character(len=1) :: char,char2 logical :: iexit if (plot_qcur()) then print*,'entering interactive mode...press h in plot window for help' else !print*,'cannot enter interactive mode: device has no cursor' return endif char = 'A' xpt = 0. ypt = 0. zoomfac = 1.0 iexit = .false. interactivereplot = .false. do while (.not.iexit) ierr = plot_curs(xpt,ypt,char) ! !--exit if the device is not interactive ! if (ierr.eq.1) return print*,'x, y = ',xpt,ypt,' function = ',char select case(char) case('h') print*,'-------------- interactive mode commands --------------' print*,' select area and zoom : left click (or A)' print*,' zoom in by 10% : +' print*,' zoom out by 10(20)% : - (_)' print*,' (r)eplot current plot : r' print*,' next timestep/plot : space, n' print*,' previous timestep : right click (or X), b' print*,' jump forward (back) by n timesteps : 0,1,2,3..9 then left (right) click' print*,' G : move legend to current position' print*,' T : move title to current position' print*,' (h)elp : h' print*,' (q)uit plotting : q, Q, esc' print*,'-------------------------------------------------------' case(plot_left_click) ! left click ! !--draw rectangle from the point and reset the limits ! print*,'select area: ' print*,'left click : zoom' ierr = plot_band(2,1,xpt,ypt,xpt2,ypt2,char2) print*,xpt,ypt,xpt2,ypt2,char2 select case (char2) case(plot_left_click) ! zoom if another left click call plot_rect(xpt,xpt2,ypt,ypt2) xmin = min(xpt,xpt2) xmax = max(xpt,xpt2) ymin = min(ypt,ypt2) ymax = max(ypt,ypt2) iadvance = 0 interactivereplot = .true. iexit = .true. case default print*,' action cancelled' end select ! !--zooming ! case('-','_','+','o') ! zoom out by 10 or 20% xlength = xmax - xmin ylength = ymax - ymin select case(char) case('-') xlength = 1.1*zoomfac*xlength ylength = 1.1*zoomfac*ylength case('_') xlength = 1.2*zoomfac*xlength ylength = 1.2*zoomfac*ylength case('+') xlength = 0.9/zoomfac*xlength ylength = 0.9/zoomfac*ylength case('o') !--reset cursor to origin xpt = 0. ypt = 0. end select if (xpt.ge.xmin .and. xpt.le.xmax .and. ypt.le.ymax) then print*,'zooming on x axis' xmin = xpt - 0.5*xlength xmax = xpt + 0.5*xlength iadvance = 0 interactivereplot = .true. iexit = .true. endif if (ypt.ge.ymin .and. ypt.le.ymax .and. xpt.le.xmax) then print*,'zooming on y axis' ymin = ypt - 0.5*ylength ymax = ypt + 0.5*ylength iadvance = 0 interactivereplot = .true. iexit = .true. endif ! !--general plot stuff ! case('G') ! move legend here print*,'setting legend position to current location...' call mvlegend(xpt,ypt,xmin,xmax,ymax) case('T') ! move title here print*,'setting title position to current location...' call mvtitle(xpt,ypt,xmin,xmax,ymax) ! !--timestepping ! case('q','Q',achar(27),achar(3)) iadvance = -666 print*,'quitting...' iexit = .true. case('X','b','B') ! right click -> go back iadvance = -abs(iadvance) iexit = .true. case('r','R') ! replot iadvance = 0 interactivereplot = .true. iexit = .true. case(' ','n','N') ! space iadvance = abs(iadvance) iexit = .true. case('0','1','2','3','4','5','6','7','8','9') read(char,*,iostat=ierr) iadvance if (ierr /=0) then print*,'*** internal error setting timestep jump' iadvance = 1 endif iadvance = int(zoomfac*iadvance) print*,' setting timestep jump = ',iadvance case(')') iadvance = int(zoomfac*10) print*,' setting timestep jump = ',iadvance ! !--multiply everything by a factor of 10 ! case('z','Z') zoomfac = 10.*zoomfac if (zoomfac.gt.1000000.) then zoomfac = 1.0 endif print*,' LIMITS/TIMESTEPPING CHANGES NOW x ',zoomfac ! !--unknown ! case default print*,' x, y = ',xpt,ypt,'; unknown option "',trim(char), '" ',iachar(char) end select ! !--do not let timestep go outside of bounds ! if we are at the first/last step, just print message and do nothing ! if iadvance trips over the bounds, jump to last/first step ! if (iadvance.ne.-666 .and. iexit) then if (istep + iadvance .gt. ilaststep) then print "(1x,a)",'reached last timestep' if (ilaststep-istep .gt.0) then iadvance= ilaststep - istep else iexit = .false. endif elseif (istep + iadvance .lt. 1) then print "(1x,a)",'reached first timestep: can''t go back' if (1-istep .lt.0) then iadvance= 1 - istep else iexit = .false. endif endif endif enddo return end subroutine interactive_step ! ! interactive mode for multiple plots per page - requires determination of which plot/panel ! a mouse-click refers to from stored settings for the viewport and limits for each plot. ! (this could be made into the only subroutine required) ! subroutine interactive_multi(iadvance,istep,ifirststeponpage,ilaststep,iframe,ifirstframeonpage,nframes, & lastpanel,iplotxarr,iplotyarr,irenderarr,icontourarr,ivecarr,& use_double_rendering,xmin,xmax,vptxmin,vptxmax,vptymin,vptymax, & barwmulti,xminadapt,xmaxadapt,nacross,ndim,xorigin,icolourscheme, & iColourBarStyle,interactivereplot) use labels, only:is_coord,iamvec use limits, only:assert_sensible_limits use multiplot, only:itrans use shapes, only:add_textshape,inshape,edit_shape,delete_shape,nshapes use plotlib, only:plot_qcur,plot_band,plot_qwin,plot_pt1,plot_curs,plot_line,plot_left_click implicit none integer, intent(inout) :: iadvance integer, intent(inout) :: istep,iframe,lastpanel,iColourBarStyle integer, intent(in) :: ifirststeponpage,ilaststep,nacross,ndim,ifirstframeonpage,nframes integer, intent(inout) :: icolourscheme integer, intent(in), dimension(:) :: iplotxarr,iplotyarr,irenderarr,icontourarr,ivecarr real, dimension(:), intent(in) :: vptxmin,vptxmax,vptymin,vptymax,barwmulti real, dimension(:), intent(inout) :: xmin,xmax,xminadapt,xmaxadapt real, intent(in), dimension(ndim) :: xorigin logical, intent(in) :: use_double_rendering logical, intent(out) :: interactivereplot integer :: ierr,ipanel,ipanel2,istepin,istepnew,i,istepjump,istepsonpage,ishape integer :: istepjumpnew,ivecx,ivecy real :: xpt,ypt,xpt2,ypt2,xpti,ypti,renderpt,xptmin,xptmax,yptmin,yptmax real :: xlength,ylength,renderlength,contlength,zoomfac real :: vptxi,vptyi,vptx2i,vpty2i,vptxceni,vptyceni real :: xmini,xmaxi,ymini,ymaxi,xcen,ycen,gradient,dr,yint,xmaxin real, dimension(4) :: xline,yline character(len=1) :: char,char2 logical :: iexit,iamincolourbar,verticalbar,double_render,istepjumpset logical, save :: print_help = .true. if (plot_qcur()) then if (.not.print_help) print*,'entering interactive mode...press h in plot window for help' else !print*,'cannot enter interactive mode: device has no cursor' return endif char = 'A' zoomfac = 1.0 xpt2 = 0. ypt2 = 0. verticalbar = barisvertical(iColourBarStyle) ! !--convert saved cursor position (saved in viewport coords) ! back to world coordinates: ! !--query window limits in world coords call plot_qwin(xmini,xmaxi,ymini,ymaxi) !--determine which plot the cursor falls on !print*,' saved xcursor,ycursor = ',xcursor,ycursor,vptxmin,vptxmax,vptymin,vptymax ipanel = getpanel(xcursor,ycursor) !print*,' saved panel = ',ipanel !--set the window to correspond to the panel we last left the cursor in call set_panel(ipanel) !--set the position in x,y relative to this panel call getxy(xcursor,ycursor,xpt,ypt,ipanel) !print*,' saved x,y = ',xpt,ypt call get_vptxy(xpt,ypt,vptxi,vptyi) !print*,'saved vptx,y = ',vptxi,vptyi,ipanel iexit = .false. interactivereplot = .false. istepin = istep istepnew = ifirststeponpage - iadvance istepsonpage = abs(istep - ifirststeponpage)/iadvance + 1 istepjump = 1 istepjumpset = .false. ! print*,'istep = ',istepnew ! print*,'steps on page = ',istepsonpage interactive_loop: do while (.not.iexit) if (print_help) then print_help = .false. char = 'h' ierr = 0 else ierr = plot_curs(xpt,ypt,char) endif ! !--exit if the device is not interactive ! if (ierr.eq.1) return !print*,'x, y = ',xpt,ypt,' function = ',char ! !--determine which plot the cursor falls on ! call get_vptxy(xpt,ypt,vptxi,vptyi) ipanel = getpanel(vptxi,vptyi) !print*,'xpt,ypt = ',xpt,ypt,vptxi,vptyi,ipanel !--translate vpt co-ords to x,y in current panel call getxy(vptxi,vptyi,xpti,ypti,ipanel) !print*,'vptx,y = ',xpti,ypti,vptxi,vptyi,ipanel !--query the position of the colour bar if (ipanel.gt.0) then if (barwmulti(ipanel).gt.tiny(barwmulti)) then iamincolourbar = incolourbar(iColourBarStyle,4,xpti,ypti,xmin(iplotxarr(ipanel)), & xmax(iplotxarr(ipanel)),xmin(iplotyarr(ipanel)),xmax(iplotyarr(ipanel))) else !--for colour bars on tiled plots, use viewport coords iamincolourbar = incolourbar(iColourBarStyle,0,vptxi,vptyi,& minval(vptxmin),maxval(vptxmax),minval(vptymin),maxval(vptymax)) endif else iamincolourbar = .false. endif !--work out if this plot is double rendered or not double_render = (icontourarr(ipanel).gt.0 .and. use_double_rendering) select case(char) case('h') print "(/,a)",' ------- interactive mode commands (multiple plots per page) --------' print*,' SPACE BAR (or n) : skip to next timestep/file' print*,' 0,1,2,3..9 and click : go forward/back n timesteps (back=r.click)' print*,' left click (or A) : zoom/select' print*,' right click (or X or b) : previous timestep' print*,' left click on colour bar : change rendering limits' print*,' +/- : zoom IN/OUT (_ for out by 20%) ' print*,' a : (a)dapt plot limits (inside box, over axes/colour bar)' print*,' l : (l)og / unlog axis (over x/y axis or colour bar)' print*,' o/C : re-centre plot on (o)rigin/(C)ursor position' print*,' backspace: delete annotation (over axes/legend/title/colour bar)' print*,' r : (r)eplot current plot' print*,' R : (R)eset/remove all range restrictions' print*,' g : plot a line and find its g)radient' print*,' ctrl-t : add text at current position' print*,' G/T/H : move le(G)end, (T)itle or (H) vector legend to current position' print*,' m/M/i : change colour map (m=next,M=previous,i=invert) (rendered plots only)' print*,' v/V/w : decrease/increase/adapt arrow size on vector plots (Z for x10)' print*,' s : (s)ave current settings for all steps' print*,' q/Q/esc : (q)uit plotting' print*,' z/Z(oom) : timestepping, zoom and limits-changing multiplied by 10' print*,'--------------------------------------------------------------------' case('g') ! draw a line between two points xline(2) = xpti yline(2) = ypti call set_panel(ipanel) !--mark first point call plot_pt1(xpti,ypti,4) !--select second point print*,' select another point (using left click or g) to plot line ' ierr = plot_band(1,1,xline(2),yline(2),xline(3),yline(3),char2) !--draw line if left click or g select case(char2) case(plot_left_click,'g') print* !--mark second point call plot_pt1(xline(3),yline(3),4) xlength = xline(3)-xline(2) if (abs(xlength).lt.tiny(xlength)) then xline(1) = xline(2) xline(4) = xline(2) yline(1) = xmin(iplotyarr(ipanel)) yline(4) = xmax(iplotyarr(ipanel)) print*,' error: gradient = infinite' elseif (xline(2).lt.xline(3)) then xline(1) = xmin(iplotxarr(ipanel)) xline(4) = xmax(iplotxarr(ipanel)) else xline(1) = xmax(iplotxarr(ipanel)) xline(4) = xmin(iplotxarr(ipanel)) endif ylength = yline(3)-yline(2) dr = sqrt(xlength**2 + ylength**2) print*,' (x1,y1) = (',xline(2),',',yline(2),')' print*,' (x2,y2) = (',xline(3),',',yline(3),')' print*,' dr = ',dr,' dx = ',xlength,' dy = ',ylength if (abs(xlength).gt.tiny(xlength)) then gradient = ylength/xlength yint = yline(3) - gradient*xline(3) print*,' gradient = ',gradient,' y intercept = ',yint yline(1) = gradient*xline(1) + yint yline(4) = gradient*xline(4) + yint endif !--plot line joining the two points call plot_line(4,xline,yline) call reset_panel case default print*,' action cancelled' end select case('s','S') do i=1,size(vptxmin) call save_limits(iplotxarr(i),xmin(iplotxarr(i)),xmax(iplotxarr(i))) call save_limits(iplotyarr(i),xmin(iplotyarr(i)),xmax(iplotyarr(i))) if (irenderarr(i).gt.0) call save_limits(irenderarr(i),xmin(irenderarr(i)),xmax(irenderarr(i))) if (icontourarr(i).gt.0) call save_limits(icontourarr(i),xmin(icontourarr(i)),xmax(icontourarr(i))) if (ivecarr(i).gt.0) then ivecx = iamvec(ivecarr(i)) + iplotxarr(i) - 1 ivecy = iamvec(ivecarr(i)) + iplotyarr(i) - 1 if (ivecx.gt.0) call save_limits(ivecx,-xmax(ivecx),xmax(ivecx)) if (ivecy.gt.0) call save_limits(ivecy,-xmax(ivecy),xmax(ivecy)) endif enddo print*,'> interactively set limits saved <' case(plot_left_click) ! left click ! !--draw rectangle from the point and reset the limits ! print*,'select area: ' print*,'left click : zoom' print*,'x = use particles within x parameter range only' print*,'y = use particles within y parameter range only' print*,'r = use particles within x and y parameter range only' print*,'R = remove all range restrictions' ! !--change colour bar limits ! if (ipanel.gt.0 .and. iamincolourbar .and. irenderarr(ipanel).gt.0) then print*,'click to set rendering limits' if (verticalbar) then ierr = plot_band(3,1,xpt,ypt,xpt2,ypt2,char2) else ierr = plot_band(4,1,xpt,ypt,xpt2,ypt2,char2) endif if (char2 == plot_left_click) then call get_vptxy(xpt2,ypt2,vptx2i,vpty2i) !--use centre point of first click and current click to ! better determine panel vptxceni = 0.5*(vptxi + vptx2i) vptyceni = 0.5*(vptyi + vpty2i) ipanel2 = getpanel(vptxceni,vptyceni) if (ipanel2.gt.0 .and. ipanel2.ne.ipanel) then print*,'panel = ',ipanel2,' was ',ipanel ipanel = ipanel2 endif call getxy(vptx2i,vpty2i,xpt2,ypt2,ipanel) !--reset first point according to current panel call getxy(vptxi,vptyi,xpti,ypti,ipanel) double_render = (icontourarr(ipanel).gt.0 .and. use_double_rendering) if (barwmulti(ipanel).gt.tiny(barwmulti)) then if (double_render) then call adjustcolourbar(iColourBarStyle,vptxi,vptyi,vptx2i,vpty2i,& vptxmin(ipanel),vptxmax(ipanel),vptymin(ipanel),vptymax(ipanel),& xmin(icontourarr(ipanel)),xmax(icontourarr(ipanel))) else call adjustcolourbar(iColourBarStyle,vptxi,vptyi,vptx2i,vpty2i,& vptxmin(ipanel),vptxmax(ipanel),vptymin(ipanel),vptymax(ipanel),& xmin(irenderarr(ipanel)),xmax(irenderarr(ipanel))) endif else !--for global colour bars (ie. on tiled plots) use viewport co-ordinates to set render limits if (double_render) then call adjustcolourbar(iColourBarStyle,vptxi,vptyi,vptx2i,vpty2i,& minval(vptxmin),maxval(vptxmax),minval(vptymin),maxval(vptymax),& xmin(icontourarr(ipanel)),xmax(icontourarr(ipanel))) else call adjustcolourbar(iColourBarStyle,vptxi,vptyi,vptx2i,vpty2i,& minval(vptxmin),maxval(vptxmax),minval(vptymin),maxval(vptymax),& xmin(irenderarr(ipanel)),xmax(irenderarr(ipanel))) endif endif if (double_render) then print*,'setting double-render min, max = ',xmin(icontourarr(ipanel)),xmax(icontourarr(ipanel)) else print*,'setting render min, max = ',xmin(irenderarr(ipanel)),xmax(irenderarr(ipanel)) endif istep = istepnew interactivereplot = .true. iexit = .true. endif else ierr = plot_band(2,1,xpt,ypt,xpt2,ypt2,char2) !call pgrect(xpt,xpt2,ypt,ypt2) call get_vptxy(xpt2,ypt2,vptx2i,vpty2i) !--use centre point of first click and current click to ! better determine panel vptxceni = 0.5*(vptxi + vptx2i) vptyceni = 0.5*(vptyi + vpty2i) ipanel2 = getpanel(vptxceni,vptyceni) if (ipanel2.gt.0 .and. ipanel2.ne.ipanel) then ipanel = ipanel2 print*,'panel = ',ipanel endif if (ipanel.le.0) cycle interactive_loop call getxy(vptx2i,vpty2i,xpt2,ypt2,ipanel) !--reset first point according to current panel call getxy(vptxi,vptyi,xpti,ypti,ipanel) xptmin = min(xpti,xpt2) xptmax = max(xpti,xpt2) yptmin = min(ypti,ypt2) yptmax = max(ypti,ypt2) select case (char2) case(plot_left_click) ! zoom if another left click xmin(iplotxarr(ipanel)) = xptmin xmax(iplotxarr(ipanel)) = xptmax xmin(iplotyarr(ipanel)) = yptmin xmax(iplotyarr(ipanel)) = yptmax print*,'setting limits: xmin = ',xmin(iplotxarr(ipanel)),' xmax = ',xmax(iplotxarr(ipanel)) istep = istepnew interactivereplot = .true. iexit = .true. case('x') call restrict_range(iplotxarr(ipanel),xptmin,xptmax) istep = istepnew interactivereplot = .true. iexit = .true. case('y') call restrict_range(iplotyarr(ipanel),yptmin,yptmax) istep = istepnew interactivereplot = .true. iexit = .true. case('r') call restrict_range(iplotxarr(ipanel),xptmin,xptmax) call restrict_range(iplotyarr(ipanel),yptmin,yptmax) istep = istepnew interactivereplot = .true. iexit = .true. case('R') call reset_ranges istep = istepnew interactivereplot = .true. iexit = .true. case default print*,' action cancelled' end select endif ! !--zooming ! case('-','_','+','o','C') ! zoom in/out if (ipanel.le.0) cycle interactive_loop xlength = xmax(iplotxarr(ipanel)) - xmin(iplotxarr(ipanel)) ylength = xmax(iplotyarr(ipanel)) - xmin(iplotyarr(ipanel)) xcen = 0.5*(xmax(iplotxarr(ipanel)) + xmin(iplotxarr(ipanel))) ycen = 0.5*(xmax(iplotyarr(ipanel)) + xmin(iplotyarr(ipanel))) if (irenderarr(ipanel).gt.0) then renderlength = xmax(irenderarr(ipanel)) - xmin(irenderarr(ipanel)) else renderlength = 0. endif if (icontourarr(ipanel).gt.0) then contlength = xmax(icontourarr(ipanel)) - xmin(icontourarr(ipanel)) else contlength = 0. endif select case(char) case('-') xlength = 1.1*zoomfac*xlength ylength = 1.1*zoomfac*ylength renderlength = 1.1*zoomfac*renderlength contlength = 1.1*zoomfac*contlength case('_') xlength = 1.2*zoomfac*xlength ylength = 1.2*zoomfac*ylength renderlength = 1.2*zoomfac*renderlength contlength = 1.2*zoomfac*contlength case('+') xlength = xlength/(1.1*zoomfac) ylength = ylength/(1.1*zoomfac) renderlength = renderlength/(1.1*zoomfac) contlength = contlength/(1.1*zoomfac) case('o') if (is_coord(iplotxarr(ipanel),ndim)) then xcen = xorigin(iplotxarr(ipanel)) else xcen = 0. endif if (is_coord(iplotyarr(ipanel),ndim)) then ycen = xorigin(iplotyarr(ipanel)) else ycen = 0. endif print*,' centreing plot on origin x,y = ',xcen,ycen case('C') xcen = xpti ycen = ypti end select xmaxin = xmax(iplotxarr(ipanel)) if (iamincolourbar .and. irenderarr(ipanel).gt.0) then if (double_render) then !--rendering zoom does not allow pan - renderpt is always centre of axis renderpt = 0.5*(xmin(icontourarr(ipanel)) + xmax(icontourarr(ipanel))) xmin(icontourarr(ipanel)) = renderpt - 0.5*contlength xmax(icontourarr(ipanel)) = renderpt + 0.5*contlength call assert_sensible_limits(xmin(icontourarr(ipanel)),xmax(icontourarr(ipanel))) print*,'zooming on colour bar: min, max = ',xmin(icontourarr(ipanel)),xmax(icontourarr(ipanel)) else !--rendering zoom does not allow pan - renderpt is always centre of axis renderpt = 0.5*(xmin(irenderarr(ipanel)) + xmax(irenderarr(ipanel))) xmin(irenderarr(ipanel)) = renderpt - 0.5*renderlength xmax(irenderarr(ipanel)) = renderpt + 0.5*renderlength call assert_sensible_limits(xmin(irenderarr(ipanel)),xmax(irenderarr(ipanel))) print*,'zooming on colour bar: min, max = ',xmin(irenderarr(ipanel)),xmax(irenderarr(ipanel)) endif istep = istepnew interactivereplot = .true. iexit = .true. else if (xpti.ge.xmin(iplotxarr(ipanel)) .and. xpti.le.xmax(iplotxarr(ipanel)) .and. ypti.le.xmax(iplotyarr(ipanel))) then xmin(iplotxarr(ipanel)) = xcen - 0.5*xlength xmax(iplotxarr(ipanel)) = xcen + 0.5*xlength call assert_sensible_limits(xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel))) print*,'zooming on x axis: min, max = ',xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel)) istep = istepnew interactivereplot = .true. iexit = .true. endif if (ypti.ge.xmin(iplotyarr(ipanel)) .and. ypti.le.xmax(iplotyarr(ipanel)) .and. xpti.le.xmaxin) then xmin(iplotyarr(ipanel)) = ycen - 0.5*ylength xmax(iplotyarr(ipanel)) = ycen + 0.5*ylength call assert_sensible_limits(xmin(iplotyarr(ipanel)),xmax(iplotyarr(ipanel))) print*,'zooming on y axis: min, max = ',xmin(iplotyarr(ipanel)),xmax(iplotyarr(ipanel)) istep = istepnew interactivereplot = .true. iexit = .true. endif endif case('a') ! adapt plot limits if (iamincolourbar .and. irenderarr(ipanel).gt.0) then if (double_render) then print*,'adapting double-render limits ',xminadapt(icontourarr(ipanel)),xmaxadapt(icontourarr(ipanel)) xmin(icontourarr(ipanel)) = xminadapt(icontourarr(ipanel)) xmax(icontourarr(ipanel)) = xmaxadapt(icontourarr(ipanel)) call assert_sensible_limits(xmin(icontourarr(ipanel)),xmax(icontourarr(ipanel))) else print*,'adapting render limits ',xminadapt(irenderarr(ipanel)),xmaxadapt(irenderarr(ipanel)) xmin(irenderarr(ipanel)) = xminadapt(irenderarr(ipanel)) xmax(irenderarr(ipanel)) = xmaxadapt(irenderarr(ipanel)) call assert_sensible_limits(xmin(irenderarr(ipanel)),xmax(irenderarr(ipanel))) endif istep = istepnew interactivereplot = .true. iexit = .true. else !--save xmax before we go changing it so can check the y axis xmaxin = xmax(iplotxarr(ipanel)) if (xpti.ge.xmin(iplotxarr(ipanel)) .and. xpti.le.xmax(iplotxarr(ipanel)) & .and. ypti.le.xmax(iplotyarr(ipanel))) then print*,'adapting x limits ',xminadapt(iplotxarr(ipanel)),xmaxadapt(iplotxarr(ipanel)) xmin(iplotxarr(ipanel)) = xminadapt(iplotxarr(ipanel)) xmax(iplotxarr(ipanel)) = xmaxadapt(iplotxarr(ipanel)) call assert_sensible_limits(xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel))) istep = istepnew interactivereplot = .true. iexit = .true. endif if (ypti.ge.xmin(iplotyarr(ipanel)) .and. ypti.le.xmax(iplotyarr(ipanel)) & .and. xpti.le.xmaxin) then print*,'adapting y limits ',xminadapt(iplotyarr(ipanel)),xmaxadapt(iplotyarr(ipanel)) xmin(iplotyarr(ipanel)) = xminadapt(iplotyarr(ipanel)) xmax(iplotyarr(ipanel)) = xmaxadapt(iplotyarr(ipanel)) call assert_sensible_limits(xmin(iplotyarr(ipanel)),xmax(iplotyarr(ipanel))) istep = istepnew interactivereplot = .true. iexit = .true. endif endif ! !--zoom in/out on vector plots (arrow size) ! case('v') if (ivecarr(ipanel).gt.0) then print*,'decreasing vector arrow size' xmax(ivecarr(ipanel)) = 1.2*zoomfac*xmax(ivecarr(ipanel)) istep = istepnew interactivereplot = .true. iexit = .true. endif case('V') if (ivecarr(ipanel).gt.0) then print*,'increasing vector arrow size' xmax(ivecarr(ipanel)) = xmax(ivecarr(ipanel))/(1.2*zoomfac) istep = istepnew interactivereplot = .true. iexit = .true. endif case('w','W') if (ivecarr(ipanel).gt.0) then print*,'adapting vector arrow size' xmax(ivecarr(ipanel)) = -1.0 istep = istepnew interactivereplot = .true. iexit = .true. endif ! !--set/unset log axes ! case('l') ! !--change colour bar, y and x itrans between log / not logged ! if (iamincolourbar .and. irenderarr(ipanel).gt.0) then if (double_render) then call change_itrans2(icontourarr(ipanel),xmin(icontourarr(ipanel)),xmax(icontourarr(ipanel)),& xminadapt(icontourarr(ipanel)),xmaxadapt(icontourarr(ipanel))) else call change_itrans2(irenderarr(ipanel),xmin(irenderarr(ipanel)),xmax(irenderarr(ipanel)),& xminadapt(irenderarr(ipanel)),xmaxadapt(irenderarr(ipanel))) endif istep = istepnew interactivereplot = .true. iexit = .true. elseif (xpti.lt.xmin(iplotxarr(ipanel))) then if (is_coord(iplotyarr(ipanel),ndim) .and. irenderarr(ipanel).gt.0) then print "(a)",'error: cannot log coordinate axes with rendering' else call change_itrans2(iplotyarr(ipanel),xmin(iplotyarr(ipanel)),xmax(iplotyarr(ipanel)),& xminadapt(iplotyarr(ipanel)),xmaxadapt(iplotyarr(ipanel))) istep = istepnew interactivereplot = .true. iexit = .true. endif elseif (ypti.lt.xmin(iplotyarr(ipanel))) then if (is_coord(iplotxarr(ipanel),ndim) .and. irenderarr(ipanel).gt.0) then print "(a)",'error: cannot log coordinate axes with rendering' else call change_itrans2(iplotxarr(ipanel),xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel)),& xminadapt(iplotxarr(ipanel)),xmaxadapt(iplotxarr(ipanel))) istep = istepnew interactivereplot = .true. iexit = .true. endif endif ! !--reset all range restrictions ! case('R') call reset_ranges interactivereplot = .true. istep = istepnew iexit = .true. ! !--general plot stuff ! case('G') ! move legend here print*,'setting legend position to current location...' if (ipanel.gt.0) then call mvlegend(xpti,ypti,xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel)),xmax(iplotyarr(ipanel)),ipanel) istep = istepnew interactivereplot = .true. iexit = .true. endif case('T') ! move title here if (ipanel.gt.0) then print*,'setting title position to current location...' call mvtitle(xpti,ypti,xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel)),xmax(iplotyarr(ipanel))) istep = istepnew interactivereplot = .true. iexit = .true. endif case('H') ! move vector legend here if (ipanel.gt.0) then if (ivecarr(ipanel).gt.0) then print*,'setting vector plot legend to current location...' call mvlegendvec(xpti,ypti,xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel)),xmax(iplotyarr(ipanel))) istep = istepnew interactivereplot = .true. iexit = .true. endif endif case('m') ! change colour map (next scheme) call change_colourmap(icolourscheme,1) istep = istepnew interactivereplot = .true. iexit = .true. case('M') ! change colour map (previous scheme) call change_colourmap(icolourscheme,-1) istep = istepnew interactivereplot = .true. iexit = .true. case('i') ! invert colour map icolourscheme = -icolourscheme call change_colourmap(icolourscheme,0) istep = istepnew interactivereplot = .true. iexit = .true. case(achar(20)) ! add text shape print*,' adding text in panel ',ipanel call add_textshape(xpti,ypti,itrans(iplotxarr(ipanel)),itrans(iplotyarr(ipanel)),ipanel,ierr) if (ierr.eq.0) then istep = istepnew interactivereplot = .true. iexit = .true. endif case(achar(8)) ! delete plot annotation / colour bar (backspace) ishape = inshape(xpti,ypti,itrans(iplotxarr(ipanel)),itrans(iplotxarr(ipanel))) if (ishape.gt.0) then call delete_shape(ishape,nshapes) istep = istepnew interactivereplot = .true. iexit = .true. elseif (iamincolourbar .and. irenderarr(ipanel).gt.0) then iColourBarStyle = 0 istep = istepnew interactivereplot = .true. iexit = .true. elseif (xpti.lt.xmin(iplotxarr(ipanel)) .or. xpti.gt.xmax(iplotxarr(ipanel)) & .or. ypti.lt.xmin(iplotyarr(ipanel)) .or. ypti.gt.xmax(iplotyarr(ipanel))) then call deleteaxes() istep = istepnew interactivereplot = .true. iexit = .true. else print*,' nothing to delete at x,y =',xpt,',',ypt endif ! !--timestepping ! case('q','Q',achar(27),achar(3)) iadvance = -666 print*,'quitting...' iexit = .true. case('X','b','B') ! right click -> go back ! iadvance = -abs(iadvance) istep = istepin - (istepjump)*istepsonpage - iadvance*istepsonpage lastpanel = 0 iexit = .true. case('r') ! replot interactivereplot = .true. istep = istepnew iexit = .true. case(' ','n','N') ! space !iadvance = abs(iadvance) istep = istepin + (istepjump-1)*istepsonpage lastpanel = 0 iexit = .true. case('0','1','2','3','4','5','6','7','8','9') read(char,*,iostat=ierr) istepjumpnew if (ierr /=0) then print*,'*** internal error setting timestep jump' istepjumpnew = 1 endif if ((istepjump.gt.1 .or. istepjumpset) .and. istepjump.le.9999) then istepjump = 10*istepjump + istepjumpnew if (istepjump.gt.9999) istepjump = 1 elseif (istepjumpnew.eq.0) then istepjump = 10 else istepjump = istepjumpnew endif istepjump = int(zoomfac*istepjump) print*,' setting timestep jump = ',istepjump istepjumpset = .true. case(')') istepjump = int(zoomfac*10) print*,' setting timestep jump = ',istepjump ! !--multiply everything by a factor of 10 ! case('z','Z') zoomfac = 10.*zoomfac if (zoomfac.gt.1000000.) then zoomfac = 1.0 endif print*,' LIMITS/TIMESTEPPING CHANGES NOW x ',zoomfac ! !--unknown ! case default print*,' x, y = ',xpti,ypti,'; unknown option "',trim(char),'" ',iachar(char) end select ! !--save cursor position relative to the viewport ! call get_vptxy(xpt,ypt,xcursor,ycursor) call reset_panel ! !--do not let timestep go outside of bounds ! if we are at the first/last step, just print message and do nothing ! if iadvance trips over the bounds, jump to last/first step ! if (iadvance.ne.-666 .and. iexit) then if (istep + iadvance .gt. ilaststep .and. iframe.eq.nframes) then print "(1x,a)",'reached last timestep' if (istepin.ne.ilaststep) then istep = ilaststep - istepsonpage*iadvance else istep = istepin iexit = .false. endif elseif (istep + iadvance .lt. 1 .and. ifirstframeonpage.eq.1) then print "(1x,a)",'reached first timestep: can''t go back' if (ifirststeponpage.ne.1) then istep = 1 - iadvance else istep = istepin iexit = .false. endif endif endif enddo interactive_loop return contains !-------- ! utility to return which panel we are in given a point on the viewport ! and the viewport limits for each panel. !-------- integer function getpanel(vptx,vpty) implicit none real, intent(in) :: vptx,vpty real :: vptxmini,vptxmaxi,vptymini,vptymaxi integer :: i,icol getpanel = 0 icol = 0 do i=1,size(vptxmin) icol = icol + 1 if (icol.gt.nacross) icol = 1 if (icol.gt.1) then ! if column>1 assign panel by being to the right of previous panel vptxmini = vptxmax(i-1)+barwmulti(i-1) else vptxmini = -0.1 ! allow for some error endif !--if last column extend xmax to right of page if (icol.eq.nacross) then vptxmaxi = 1.1 elseif (verticalbar) then ! otherwise use max of current panel + space containing the colour bar vptxmaxi = vptxmax(i) + barwmulti(i) else vptxmaxi = vptxmax(i) endif !--if first row extend ymax to top of page if (i.le.nacross) then vptymaxi = 1.1 else vptymaxi = vptymax(i) endif !--if last row then allow ymin to extend to bottom of page if (i.gt.(size(vptxmin)-nacross)) then vptymini = -0.1 ! if not last row assign panel by being above row below elseif (i+nacross.le.size(vptxmin)) then vptymini = vptymax(i+nacross) elseif (.not.verticalbar) then vptymini = vptymin(i) - barwmulti(i) else vptymini = vptymin(i) endif if (vptx.gt.vptxmini .and. vptx.lt.vptxmaxi .and. & vpty.gt.vptymini .and. vpty.lt.vptymaxi) then if (getpanel.ne.0) print*,'Warning: multiple matching panels found ',getpanel,i getpanel = i endif enddo if (getpanel.le.0 .or. getpanel.gt.size(vptxmin)) then !print*,' vptx,y = ',vptx,vpty,vptxmin(:),vptxmax(:) print*,'Error determining panel: assuming last ' getpanel = size(vptxmin) endif end function getpanel !-------- ! utility to return x,y coordinates in a given panel given viewport coords !-------- subroutine getxy(vptx,vpty,x,y,ipanel) implicit none real, intent(in) :: vptx,vpty real, intent(out) :: x,y integer, intent(in) :: ipanel if (ipanel.gt.0) then x = xmin(iplotxarr(ipanel)) + (vptx-vptxmin(ipanel))/(vptxmax(ipanel)-vptxmin(ipanel)) & *(xmax(iplotxarr(ipanel))-xmin(iplotxarr(ipanel))) y = xmin(iplotyarr(ipanel)) + (vpty-vptymin(ipanel))/(vptymax(ipanel)-vptymin(ipanel)) & *(xmax(iplotyarr(ipanel))-xmin(iplotyarr(ipanel))) else x = 0. y = 0. endif return end subroutine getxy !--- ! utility which translates between world co-ordinates (x,y) ! and viewport co-ordinates (relative to the whole viewport) !--- !subroutine get_vptxy(x,y,vptx,vpty,ipanel) ! implicit none ! real, intent(in) :: x,y ! real, intent(out) :: vptx,vpty ! integer, intent(in) :: ipanel ! ! if (ipanel.gt.0) then ! vptx = vptxmin(ipanel) + (x-xmin(iplotxarr(ipanel)))/& ! (xmax(iplotxarr(ipanel))-xmin(iplotxarr(ipanel)))*(vptxmax(ipanel)-vptxmini(ipanel)) ! vpty = vptymin(ipanel) + (y-xmin(iplotyarr(ipanel)))/&(xmax(iplotyarr(ipanel))-xmin(iplotyarr(ipanel)))*(vptymax(ipanel)-vptymini(ipanel)) ! else ! vptx = 0.5 ! vpty = 0.5 ! endif ! !end subroutine get_vptxy !-------- ! utility to reset the drawing surface so we can draw in a panel !-------- subroutine set_panel(ipanel) use plotlib, only:plot_svp,plot_swin implicit none integer, intent(in) :: ipanel if (ipanel.gt.0) then call plot_swin(xmin(iplotxarr(ipanel)),xmax(iplotxarr(ipanel)),xmin(iplotyarr(ipanel)),xmax(iplotyarr(ipanel))) !--really should save viewport setting here, but doesn't matter ! so long as interactive mode is the last thing called call plot_svp(vptxmin(ipanel),vptxmax(ipanel),vptymin(ipanel),vptymax(ipanel)) endif return end subroutine set_panel subroutine reset_panel use plotlib, only:plot_swin implicit none call plot_swin(xmini,xmaxi,ymini,ymaxi) end subroutine reset_panel end subroutine interactive_multi !-------------------------------------------------------------------- ! utilities to determine whether a point is in or out of a selection !-------------------------------------------------------------------- logical function inslice(x,xmin,xmax) implicit none real, intent(in) :: x,xmin,xmax inslice = (x.ge.xmin .and. x.le.xmax) end function inslice logical function inrectangle(x,y,xmin,xmax,ymin,ymax) implicit none real, intent(in) :: x,y,xmin,xmax,ymin,ymax inrectangle = (x.ge.xmin .and. x.le.xmax .and. y.ge.ymin .and. y.le.ymax) end function inrectangle logical function incircle(x,y,r2) implicit none real, intent(in) :: x,y,r2 incircle = ((x*x + y*y) <= r2) end function incircle ! ! Point in polygon ! See: http://en.wikipedia.org/wiki/Even-odd_rule ! logical function inpoly(x,y,xpts,ypts,npts) implicit none real, intent(in) :: x,y real, dimension(:), intent(in) :: xpts,ypts integer, intent(in) :: npts integer :: i,j inpoly = .false. j = npts do i=1,npts if (((ypts(i) > y) .neqv. (ypts(j) > y)) .and. & (x < (xpts(j) - xpts(i))*(y-ypts(i))/(ypts(j) - ypts(i)) + xpts(i))) then inpoly = .not. inpoly endif j = i enddo end function inpoly !------------------------------------------------------------ ! utility which adapts plot limits based only on the ! particles being plotted !------------------------------------------------------------ subroutine adapt_limits_interactive(labeli,np,xarr,xmin,xmax,icolourpart,iamtype,iusetype) use params, only:int1 use limits, only:assert_sensible_limits implicit none character(len=*), intent(in) :: labeli integer, intent(in) :: np real, dimension(np), intent(in) :: xarr real, intent(out) :: xmin,xmax integer(kind=int1), dimension(:) , intent(in) :: iamtype integer, dimension(np), intent(in) :: icolourpart logical, dimension(:), intent(in) :: iusetype integer :: itype,i logical :: mixedtypes xmin = huge(xmin) xmax = -huge(xmax) mixedtypes = size(iamtype).ge.np if (mixedtypes) then do i=1,np itype = int(iamtype(i)) if (iusetype(itype) .and. icolourpart(i).gt.0) then xmin = min(xmin,xarr(i)) xmax = max(xmax,xarr(i)) endif enddo else xmin = minval(xarr,mask=(icolourpart.ge.0)) xmax = maxval(xarr,mask=(icolourpart.ge.0)) endif call assert_sensible_limits(xmin,xmax) print "(1x,a)",' resetting '//trim(labeli)//' limits' end subroutine adapt_limits_interactive !------------------------------------------------------------ ! utility which translates between world co-ordinates (x,y) ! and viewport co-ordinates (relative to the whole viewport) !------------------------------------------------------------ subroutine get_vptxy(x,y,vptx,vpty) use plotlib, only:plot_qvp,plot_qwin implicit none real, intent(in) :: x,y real, intent(out) :: vptx,vpty real :: xmini,xmaxi,ymini,ymaxi real :: vptxmini,vptxmaxi,vptymini,vptymaxi call plot_qvp(0,vptxmini,vptxmaxi,vptymini,vptymaxi) call plot_qwin(xmini,xmaxi,ymini,ymaxi) vptx = vptxmini + (x-xmini)/(xmaxi-xmini)*(vptxmaxi-vptxmini) vpty = vptymini + (y-ymini)/(ymaxi-ymini)*(vptymaxi-vptymini) end subroutine get_vptxy !------------------------------------------------------------ ! utility to return x,y coordinates given viewport coords ! (only works for single-panelled plots) !------------------------------------------------------------ subroutine get_posxy(vptx,vpty,x,y,xmini,xmaxi,ymini,ymaxi) use plotlib, only:plot_qvp implicit none real, intent(in) :: vptx,vpty real, intent(out) :: x,y real, intent(in) :: xmini,xmaxi,ymini,ymaxi real :: vptxmini,vptxmaxi,vptymini,vptymaxi call plot_qvp(0,vptxmini,vptxmaxi,vptymini,vptymaxi) x = xmini + (vptx-vptxmini)/(vptxmaxi-vptxmini)*(xmaxi-xmini) y = ymini + (vpty-vptymini)/(vptymaxi-vptymini)*(ymaxi-ymini) return end subroutine get_posxy !----------------------------------------------------------- ! These subroutines interface to the actual plot settings !----------------------------------------------------------- ! !--plot a label showing the particle ID on the plot ! subroutine plot_number(i,xi,yi) use plotlib, only:plot_numb,plot_qch,plot_sch,plot_text implicit none integer, intent(in) :: i real, intent(in) :: xi,yi integer :: nc real :: charheight character(len=20) :: string !--convert number to text string call plot_numb(i,0,1,string,nc) !--query and store character height call plot_qch(charheight) !--change character height call plot_sch(2.0) !--plot text string call plot_text(xi,yi,string(1:nc)) !--reset character height call plot_sch(charheight) return end subroutine plot_number subroutine deleteaxes() use settings_page, only:iaxis,iPlotLegend,& !iPlotStepLegend, & iPlotTitles,iPlotScale use settings_vecplot, only:iVecplotLegend implicit none if (iaxis.eq.-2) then ! !-- would be better to do this properly by ! determining whether or not the cursor is over ! the legend, shape, title or whatever annotation the user ! wishes to be deleted. Instead at the moment we just ! delete the legends once the axes are already gone, and ! then in a somewhat arbitrary order. ! iVecplotLegend = .false. if (iPlotLegend) then iPlotLegend = .false. elseif (iPlotTitles) then iPlotTitles = .false. elseif (iPlotScale) then iPlotScale = .false. endif elseif (iaxis.le.2 .and. iaxis.gt.-2) then iaxis = iaxis - 1 elseif (iaxis.gt.2) then iaxis = -1 elseif (iaxis.lt.-2) then iaxis = -2 endif end subroutine deleteaxes ! !--move the legend to the current position ! subroutine mvlegend(xi,yi,xmin,xmax,ymax,ipanel) use settings_page, only:hposlegend,vposlegend,fjustlegend,iPlotLegend,iPlotLegendOnlyOnPanel use plotlib, only:plot_qcs implicit none real, intent(in) :: xi,yi,xmin,xmax,ymax integer, intent(in), optional :: ipanel real :: xch,ych iPlotLegend = .true. hposlegend = (xi - xmin)/(xmax-xmin) !--query character height in world coordinates call plot_qcs(4,xch,ych) vposlegend = (ymax - yi)/ych ! !--automatically change justification ! if (hposlegend < 0.25) then fjustlegend = 0.0 ! elseif (hposlegend > 0.75) then ! fjustlegend = 1.0 ! else ! fjustlegend = 0.5 ! endif if (present(ipanel)) then if (ipanel.gt.0 .and. iPlotLegendOnlyOnPanel.gt.0) iPlotLegendOnlyOnPanel = ipanel endif print*,'hpos = ',hposlegend,' vpos = ',vposlegend,' just = ',fjustlegend return end subroutine mvlegend ! !--move the vector legend to the current position ! subroutine mvlegendvec(xi,yi,xmin,xmax,ymax) use settings_vecplot, only:hposlegendvec,vposlegendvec,iVecplotLegend use plotlib, only:plot_qcs implicit none real, intent(in) :: xi,yi,xmin,xmax,ymax real :: xch,ych iVecplotLegend = .true. hposlegendvec = (xi - xmin)/(xmax-xmin) !--query character height in world coordinates call plot_qcs(4,xch,ych) vposlegendvec = (ymax - yi)/ych print*,'hpos = ',hposlegendvec,' vpos = ',vposlegendvec return end subroutine mvlegendvec ! !--move the title to the current position ! subroutine mvtitle(xi,yi,xmin,xmax,ymax) use settings_page, only:hpostitle,vpostitle,fjusttitle,iPlotTitles use plotlib, only:plot_qcs implicit none real, intent(in) :: xi,yi,xmin,xmax,ymax real :: xch,ych iPlotTitles = .true. hpostitle = (xi - xmin)/(xmax-xmin) !--query character height in world coordinates call plot_qcs(4,xch,ych) vpostitle = (yi - ymax)/ych !--automatically change justification if (hpostitle < 0.25) then fjusttitle = 0.0 elseif (hpostitle > 0.75) then fjusttitle = 1.0 else fjusttitle = 0.5 endif print*,'hpos = ',hpostitle,' vpos = ',vpostitle,' just = ',fjusttitle return end subroutine mvtitle ! !--saves current plot limits ! subroutine save_limits(iplot,xmin,xmax,setlim2) use limits, only:lim,lim2 use labels, only:is_coord use multiplot, only:itrans use settings_data, only:ndim use settings_limits, only:iadapt,iadaptcoords use transforms, only:transform_limits_inverse implicit none integer, intent(in) :: iplot real, intent(in) :: xmin,xmax logical, intent(in), optional :: setlim2 logical :: uselim2 real :: xmintemp,xmaxtemp uselim2 = .false. if (present(setlim2)) uselim2 = setlim2 if (itrans(iplot).ne.0) then xmintemp = xmin xmaxtemp = xmax call transform_limits_inverse(xmintemp,xmaxtemp,itrans(iplot)) if (uselim2) then lim2(iplot,1) = xmintemp lim2(iplot,2) = xmaxtemp else lim(iplot,1) = xmintemp lim(iplot,2) = xmaxtemp endif else if (uselim2) then lim2(iplot,1) = xmin lim2(iplot,2) = xmax else lim(iplot,1) = xmin lim(iplot,2) = xmax endif endif ! !--change appropriate plot limits to fixed (not adaptive) ! if (is_coord(iplot,ndim)) then iadaptcoords = .false. else iadapt = .false. endif return end subroutine save_limits ! !--implements parameter range restriction ! subroutine restrict_range(iplot,xmin,xmax) use limits, only:range use multiplot, only:itrans use transforms, only:transform_limits_inverse implicit none integer, intent(in) :: iplot real, intent(in) :: xmin,xmax real :: xmintemp,xmaxtemp if (itrans(iplot).ne.0) then xmintemp = xmin xmaxtemp = xmax call transform_limits_inverse(xmintemp,xmaxtemp,itrans(iplot)) range(iplot,1) = xmintemp range(iplot,2) = xmaxtemp else range(iplot,1) = xmin range(iplot,2) = xmax endif return end subroutine restrict_range ! !--interface to routine which removes all parameter range restrictions ! subroutine reset_ranges() use limits, only:reset_all_ranges implicit none call reset_all_ranges() return end subroutine reset_ranges ! !--interface to routine which resets second set of limits ! subroutine reset_limits2(icol) use limits, only:reset_lim2 implicit none integer, intent(in) :: icol call reset_lim2(icol) return end subroutine reset_limits2 ! !--saves current plot limits for particle tracking ! subroutine save_limits_track(iplot,xmin,xmax,xi) use multiplot, only:itrans use settings_data, only:ndim use settings_limits, only:xminoffset_track,xmaxoffset_track use transforms, only:transform_limits_inverse implicit none integer, intent(in) :: iplot real, intent(in) :: xmin,xmax,xi real :: xmintemp,xmaxtemp if (iplot.gt.ndim) then print*,'ERROR in save_limits_track: iplot>ndim' return elseif (itrans(iplot).ne.0) then xmintemp = xmin xmaxtemp = xmax call transform_limits_inverse(xmintemp,xmaxtemp,itrans(iplot)) xminoffset_track(iplot) = abs(xi - xmintemp) xminoffset_track(iplot) = abs(xmaxtemp - xi) else xminoffset_track(iplot) = abs(xi - xmin) xmaxoffset_track(iplot) = abs(xmax - xi) endif return end subroutine save_limits_track ! !--recalculates radius ! subroutine save_itrackpart_recalcradius(itrackpart) use filenames, only:nsteps,nstepsinfile,ifileopen use settings_data, only:ncalc,DataIsBuffered,iCalcQuantities, & itracktype,itrackoffset use calcquantities, only:calc_quantities,calc_quantities_use_x0 implicit none integer, intent(in) :: itrackpart itracktype = 0 ! cannot interactively track by type itrackoffset = itrackpart if (iCalcQuantities .and. itrackpart.gt.0) then if (ncalc.gt.0 .and. calc_quantities_use_x0()) then print "(a)",' Recalculating radius relative to tracked particle' if (DataIsBuffered) then call calc_quantities(1,nsteps) else call calc_quantities(1,nstepsinfile(ifileopen)) endif endif endif return end subroutine save_itrackpart_recalcradius ! !--toggles log/unlog ! note this only changes a pure log transform: will not change combinations ! subroutine change_itrans(iplot,xmin,xmax) use multiplot, only:itrans use settings_data, only:numplot use transforms, only:transform_limits,transform_limits_inverse implicit none integer, intent(in) :: iplot real, intent(inout) :: xmin, xmax if (iplot.le.numplot) then if (itrans(iplot).eq.1) then itrans(iplot) = 0 !!--untransform the plot limits call transform_limits_inverse(xmin,xmax,1) else itrans(iplot) = 1 !!--transform the plot limits call transform_limits(xmin,xmax,itrans(iplot)) endif endif end subroutine change_itrans subroutine change_itrans2(iplot,xmin,xmax,xmina,xmaxa) use multiplot, only:itrans use settings_data, only:numplot use transforms, only:transform_limits,transform_limits_inverse implicit none integer, intent(in) :: iplot real, intent(inout) :: xmin, xmax, xmina, xmaxa if (iplot.le.numplot) then if (itrans(iplot).eq.1) then itrans(iplot) = 0 !!--untransform the plot limits call transform_limits_inverse(xmin,xmax,1) call transform_limits_inverse(xmina,xmaxa,1) else itrans(iplot) = 1 !!--transform the plot limits call transform_limits(xmin,xmax,itrans(iplot)) call transform_limits(xmina,xmaxa,itrans(iplot)) endif endif end subroutine change_itrans2 ! !--saves rotation options ! subroutine save_rotation(ndim,anglexi,angleyi,anglezi) use settings_xsecrot, only:anglex,angley,anglez implicit none integer, intent(in) :: ndim real, intent(in) :: anglexi,angleyi,anglezi anglez = anglezi if (ndim.ge.3) then anglex = anglexi angley = angleyi endif return end subroutine save_rotation ! !--saves cross section position ! subroutine save_xsecpos(xsecpos,xsec) use settings_xsecrot, only:xsecpos_nomulti,xsec_nomulti implicit none real, intent(in) :: xsecpos logical, intent(in) :: xsec xsecpos_nomulti = xsecpos xsec_nomulti = xsec return end subroutine save_xsecpos ! !--saves 3D perspective ! subroutine save_perspective(zpos,dz) use settings_xsecrot, only:zobserver,dzscreenfromobserver implicit none real, intent(in) :: zpos,dz zobserver = zpos dzscreenfromobserver = dz return end subroutine save_perspective ! !--saves 3D opacity ! subroutine save_opacity(taupartdepthi) use settings_xsecrot, only:taupartdepth implicit none real, intent(in) :: taupartdepthi taupartdepth = taupartdepthi return end subroutine save_opacity ! !--saves circles of interaction ! subroutine save_circles(ncircpartset,icircpartset) use settings_part, only:ncircpart,icircpart implicit none integer, intent(in) :: ncircpartset integer, intent(in), dimension(:) :: icircpartset integer :: imax imax = min(size(icircpartset),size(icircpart),ncircpartset) ncircpart = imax icircpart(1:imax) = icircpartset(1:imax) print*,'saving ',imax,' circles of interaction only' end subroutine save_circles ! !--change colour map ! subroutine change_colourmap(imap,istep) use colours, only:colour_set,ncolourschemes implicit none integer, intent(inout) :: imap integer, intent(in) :: istep imap = imap + istep if (abs(imap).gt.ncolourschemes) imap = 1 if (abs(imap).lt.1) imap = ncolourschemes call colour_set(imap) end subroutine change_colourmap ! !--set movie mode ! subroutine set_movie_mode() use settings_page, only:iaxis,papersizex,aspectratio,ipapersize,ipapersizeunits,iPageColours use settings_limits, only:adjustlimitstodevice use settings_render, only:iColourBarStyle use pagecolours, only:set_pagecolours use plotlib, only:plotlib_is_pgplot,plot_pap use colourbar, only:set_floating_bar_style implicit none iaxis = -1 iPageColours = 2 if (.not.plotlib_is_pgplot) then ipapersize = 9 ipapersizeunits = 0 papersizex = 1280. aspectratio = 0.5625 call plot_pap(papersizex,aspectratio,ipapersizeunits) iColourBarStyle = 8 call set_floating_bar_style(iColourBarStyle,4) call set_pagecolours(iPageColours) adjustlimitstodevice = .true. endif end subroutine set_movie_mode ! !--unset movie mode ! subroutine unset_movie_mode() use settings_page, only:iaxis,papersizex,aspectratio,ipapersize,iPageColours use settings_limits, only:adjustlimitstodevice use settings_render, only:iColourBarStyle use pagecolours, only:set_pagecolours use plotlib, only:plotlib_is_pgplot,plot_pap implicit none iaxis = 0 iPageColours = 1 if (.not.plotlib_is_pgplot) then ipapersize = 0 papersizex = 0. aspectratio = 0. iColourBarStyle = 1 call plot_pap(papersizex,aspectratio,9) call set_pagecolours(iPageColours) adjustlimitstodevice = .true. endif end subroutine unset_movie_mode end module interactive_routines splash/src/system_utils.f90000644 000766 000000 00000012644 13261626263 016646 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ! this module contains certain useful utilities which ! depend on system commands (in the system_commands module) ! module system_utils use system_commands, only:get_environment implicit none public :: ienvironment,lenvironment,renvironment,lenvstring,ienvstring contains ! !--this routine returns an integer variable ! from an environment variable setting ! ! if the errval argument is present then this is the ! value assigned when an error has occurred ! (default is zero) ! integer function ienvironment(variable,errval) implicit none character(len=*), intent(in) :: variable character(len=30) :: string integer, intent(in), optional :: errval call get_environment(variable,string) if (present(errval)) then ienvironment = ienvstring(string,errval) else ienvironment = ienvstring(string) endif end function ienvironment ! !--this routine returns a floating point (real) variable ! from an environment variable setting ! ! if the errval argument is present then this is the ! value assigned when an error has occurred ! (default is zero) ! real function renvironment(variable,errval) implicit none character(len=*), intent(in) :: variable character(len=30) :: string real, intent(in), optional :: errval integer :: ierr call get_environment(variable,string) if (len_trim(string).gt.0) then read(string,*,iostat=ierr) renvironment else ierr = 1 endif if (ierr /= 0) then if (present(errval)) then renvironment = errval else renvironment = 0. endif endif end function renvironment ! !--this routine returns a logical variable ! from an environment variable setting ! logical function lenvironment(variable) implicit none character(len=*), intent(in) :: variable character(len=30) :: string call get_environment(variable,string) lenvironment = lenvstring(string) end function lenvironment ! !--utility routine to determine whether a string ! should be interpreted as true or false ! logical function lenvstring(string) implicit none character(len=*), intent(in) :: string if (string(1:1).eq.'y'.or.string(1:1).eq.'Y' & .or.string(1:1).eq.'t'.or.string(1:1).eq.'T' & .or.trim(string).eq.'on'.or.trim(string).eq.'ON' & .or.trim(string).eq.'1') then lenvstring = .true. else lenvstring = .false. endif end function lenvstring integer function ienvstring(string,errval) implicit none character(len=*), intent(in) :: string integer, intent(in), optional :: errval character(len=5) :: fmtstring integer :: ierr if (len_trim(string).gt.0) then !--use a formatted read - this is to avoid a compiler bug ! should in general be more robust anyway write(fmtstring,"(a,i2,a)",iostat=ierr) '(i',len_trim(string),')' read(string,fmtstring,iostat=ierr) ienvstring else ierr = 1 endif if (ierr /= 0) then if (present(errval)) then ienvstring = errval else ienvstring = 0 endif endif end function ienvstring ! !--this routine returns an arbitrary number of ! comma separated strings ! subroutine envlist(variable,nlist,list) implicit none character(len=*), intent(in) :: variable integer, intent(out) :: nlist character(len=*), dimension(:), intent(out), optional :: list character(len=120) :: string character(len=10) :: dummy integer :: i1,i2,ierr logical :: notlistfull !--set list to blank strings if argument is present if (present(list)) then list = ' ' endif !--get envlist from the environment call get_environment(variable,string) !--split the string on commas i1 = 1 i2 = index(string,',')-1 if (i2.eq.-1) i2 = len_trim(string) nlist = 0 ierr = 0 notlistfull = .true. !--for each comma separated string, add a list member do while(i2.ge.i1 .and. notlistfull .and. ierr.eq.0) nlist = nlist + 1 !print*,'i1,i2,stringfrag= ',i1,i2,trim(string(i1:)) if (present(list)) then read(string(i1:i2),"(a)",iostat=ierr) list(nlist) notlistfull = (nlist.lt.size(list)) else read(string(i1:i2),"(a)",iostat=ierr) dummy ! to get ierr at end of string notlistfull = .true. endif i1 = i2 + 2 i2 = index(string(i1:),',') if (i2.eq.0) then i2 = len_trim(string) else i2 = i2 + i1 - 2 endif enddo return end subroutine envlist end module system_utils splash/src/exact_toystar1D.f90000644 000766 000000 00000027076 13261626263 017165 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------ ! plot exact solution for toystar in one dimension ! ! linear solution uses Gegenbauer/Legendre polynomials ! ! non-linear solution solves ODEs, assumes linear velocity ! ! For details see Monaghan and Price (2004) MNRAS !------------------------------------------------------------ module toystar1D implicit none public :: exact_toystar1D, exact_toystar_ACplane private :: Pm, Gn contains subroutine exact_toystar1D(iplot,time,gamma,H0,A0,C0, & sigma,norder,xplot,yplot,npts,ierr) use plotlib, only:plot_pt1 implicit none integer, intent(in) :: iplot,norder,npts integer, intent(out) :: ierr real, intent(in) :: time,gamma,sigma real, intent(in) :: H0, C0, A0 ! parameters for toy star real, dimension(:), intent(inout) :: xplot real, dimension(size(xplot)), intent(out) :: yplot integer :: i,nsteps real :: const ! parameter for toy star real :: Hprev, Cprev, Aprev, Htemp, Ctemp, Atemp, H, C, A real :: radstar,dx,dt,tnow real :: rhoplot,deltarho real :: fprevC,fprevA,fprevH,ftempC,ftempA,ftempH real :: gamp1,gamm1,gam1,fact,constK,omega real :: fnorm logical :: linear linear = .false. if (norder.ge.0) linear = .true. gamp1 = gamma + 1. gamm1 = gamma - 1. gam1 = 1./gamm1 constK = 0.25 ierr = 0 if (linear) then !--------------------------------------------------------------------------- ! linear solution uses Gegenbauer & Legendre Polynomials ! (this is for the toy star oscillations) omega = sqrt(0.5*(norder+1.)*(norder+2.)) fnorm = 2.*(norder+1)*(norder+2)/real(2.*norder + 3.) print*,' Plotting toy star oscills: time, norder, omega = ', & time,norder,omega,H0,C0,A0 if (C0.le.0.) then print*,'*** C = 0 = illegal in input' ierr = 1 return else radstar = sqrt(H0/C0) endif xplot(1) = -radstar dx = (radstar-xplot(1))/float(npts-1) do i=2,npts xplot(i) = xplot(1)+dx*(i-1) ! print*,i,' x,y = ',xplot(i),yplot(i) rhoplot = (H0 - C0*xplot(i)**2) if (rhoplot.le.0.) rhoplot = 0. deltarho = Pm(xplot(i),norder+1)*sin(omega*time) rhoplot = rhoplot**gam1 + 2.*omega*A0*deltarho/fnorm select case(iplot) case(1) ! plot solution for density yplot(i) = rhoplot case(2) ! plot solution for pressure yplot(i) = constK*rhoplot**gamma case(3) ! plot solution for utherm yplot(i) = constK*(rhoplot**gamm1)/gamm1 case(4) ! plot solution for vx yplot(i) = A0*Gn(xplot(i),norder)*cos(omega*time) case(5) ! plot solution for By yplot(i) = sigma*rhoplot end select enddo if (iplot.eq.6) then ! plot By \propto rho dx = (H0**gam1)/float(npts-1) ! ie (rhomax - 0)/npts xplot(1) = 0. yplot(1) = sigma*xplot(1) do i=2,npts xplot(i) = xplot(1) + dx*(i-1) yplot(i) = sigma*(xplot(i)) enddo endif if (iplot.eq.7) then ! plot current point on A-C plane call plot_pt1(C0,A0*cos(omega*time),4) ierr = 2 ! do not plot again else ! plot normal exact solution line ierr = 0 endif !--------------------------------------------------------------------------- ! non-linear solution for the fundamental (n=1) mode ! else ! ! solve for H, C and A given initial conditions on v, rho and the time. ! nsteps = 1000*(int(time) + 1) Hprev = H0 Cprev = C0 Aprev = A0 ! PRINT*,' nsteps,H,C,A in = ',nsteps,H0,C0,A0 dt = time/nsteps fact = 2.*(constK + 0.5*sigma**2)*gamma*gam1 const = (A0**2 + 1. + 2.*fact*C0*gam1)*C0**(-2./gamp1) print*,' Plotting toy star: time, H0, C0, A0, k = ', & time,Hprev,Cprev,Aprev,const tnow = 0. do i = 1,nsteps tnow = tnow + dt ! integrate using improved Euler fprevC = -Cprev*Aprev*gamp1 fprevA = fact*Cprev -1.-Aprev**2 fprevH = -Aprev*Hprev*gamm1 ! predictor Ctemp = Cprev + dt*(-Cprev*Aprev*gamp1) Atemp = Aprev + dt*(fact*Cprev-1.-Aprev**2) Htemp = Hprev + dt*(-Aprev*Hprev*gamm1) ftempC = -Ctemp*Atemp*gamp1 ftempA = fact*Ctemp -1. -Aprev**2 ftempH = -Atemp*Htemp*gamm1 ! corrector C = Cprev + 0.5*dt*(fprevC + ftempC) A = Aprev + 0.5*dt*(fprevA + ftempA) H = Hprev + 0.5*dt*(fprevH + ftempH) Cprev = C Aprev = A Hprev = H ! print*,' time = ',tnow ! IF ((abs(C-C0).LT.5.e-3).AND. & ! (abs(A-A0).LT.5.e-3).AND.(tnow.GT.5e-3)) THEN ! PRINT*,'*** period, t = ',tnow,' err = ',abs(C-C0)+abs(A-A0) ! ENDIF enddo const = (A**2 + 1. + 2.*fact*C*gam1)*C**(-2./gamp1) print*,' C, A, H, k = ',C,A,H,const if (C.le.0.) then radstar = 0.5 print*,'*** C = 0 = illegal' ierr = 1 return else radstar = sqrt(H/C) endif xplot(1) = -radstar dx = (radstar-xplot(1))/float(npts-1) do i=2,npts xplot(i) = xplot(1)+dx*(i-1) ! print*,i,' x,y = ',xplot(i),yplot(i) rhoplot = (H - C*xplot(i)**2) if (rhoplot.le.0.) rhoplot = 0. rhoplot = rhoplot**gam1 select case(iplot) case(1) ! plot solution for density yplot(i) = rhoplot case(2) ! plot solution for pressure yplot(i) = constK*rhoplot**gamma case(3) ! plot solution for utherm yplot(i) = constK*(rhoplot**gamm1)/gamm1 case(4) ! plot solution for vx yplot(i) = A*xplot(i) case(5) ! plot solution for By yplot(i) = sigma*rhoplot end select enddo if (iplot.eq.6) then ! plot By \propto rho dx = (H**gam1)/float(npts-1) ! ie (rhomax - 0)/npts xplot(1) = 0. yplot(1) = sigma*xplot(1) do i=2,npts xplot(i) = xplot(1) + dx*(i-1) yplot(i) = sigma*(xplot(i)) enddo endif if (iplot.eq.7) then ! plot current point on A-C plane call plot_pt1(C,A,4) ierr = 2 ! do not plot again else ! plot normal exact solution line ierr = 0 endif ! !------------------------------------------------------------------------ ! endif return end subroutine exact_toystar1D ! !--function to evaluate the Gegenbauer polynomial of index n given x ! real function Gn(x,n) implicit none integer, intent(in) :: n real, intent(in) :: x integer :: i real :: Gnminus1,Gnminus2 real :: fnorm fnorm = 2.*(n+1)*(n+2)/real(2.*n + 3.) ! PRINT*,' fnorm = ',fnorm ! !--specify first two Gegenbauer polynomials ! Gnminus2 = 1. Gnminus1 = 3.*x Gn = 0. ! avoid compiler warning ! !--use recurrence relation to calculate the rest ! select case (n) case (0) Gn = Gnminus2 case (1) Gn = Gnminus1 case (2:) do i=2,n Gn = ((2*i+1)*x*Gnminus1 - (i+1)*Gnminus2)/real(i) Gnminus2 = Gnminus1 Gnminus1 = Gn enddo end select Gn = Gn/fnorm end function Gn ! !--function to calculate a Legendre Polynomial of order m ! real function Pm(x,m) implicit none integer, intent(in) :: m real, intent(in) :: x integer :: i real :: Pmminus1,Pmminus2 ! !--specify first two Legendre polynomials ! Pmminus2 = 1. Pmminus1 = x Pm = 0. ! avoid compiler warning select case(m) case (0) Pm = 1. case (1) Pm = x case (2:) ! use recurrence relation to calculate the rest do i=2,m Pm = ((2.*(i-1.)+1.)*x*Pmminus1 - (i-1.)*Pmminus2)/real(i) Pmminus2 = Pmminus1 Pmminus1 = Pm enddo end select end function Pm !---------------------------------------------------------------------- ! ! this subroutine plots the A-C relation in the 1D Toy star solution ! !---------------------------------------------------------------------- subroutine exact_toystar_ACplane(astart,cstart,sigma,gamma) use plotlib, only:plot_swin,plot_funx,plot_label,plot_box implicit none real, intent(in) :: astart,cstart,sigma,gamma real :: constk,gam1,gamm1,gamp1,fact real :: xstart,xend,xcentre,c,cnew,k real :: func,func2,funct,fderiv,ymin,ymax,extra external func,func2 common /kconst/ k,fact,gam1,gamp1 print*,' plotting a-c plane...' gamp1 = gamma + 1. gamm1 = gamma - 1. gam1 = 1./gamm1 constk = 0.25 ! print*,' k, kdash = ',constk,constk + 0.5*sigma**2 fact = 2.*(constk + 0.5*sigma**2)*gamma*gam1 k = (astart**2 + 1. + 2.*fact*cstart*gam1)*cstart**(-2./gamp1) ! print*,' k,fact = ',k,fact ! !--find limits of plot (ie. where a = 0) ! c = 1.e6 cnew = 0.25 do while (abs(c-cnew).gt.1.e-5) c = cnew funct = k*c**(2./gamp1) - 2.*fact*c*gam1 - 1. fderiv = 2.*k/gamp1*c**(-gamm1/gamp1) - 2.*fact*gam1 cnew = c - funct/fderiv if (cnew.lt.0.) print*,'eek c < 0' enddo xstart = cnew c = 1.e6 cnew = 6.37935 do while (abs(c-cnew).gt.1.e-5) c = cnew funct = k*c**(2./gamp1) - 2.*fact*c*gam1 - 1. fderiv = 2.*k/gamp1*c**(-gamm1/gamp1) - 2.*fact*gam1 cnew = c - funct/fderiv enddo xend = cnew ! print*,'plotting k = ',k,' cstart = ',cstart,' astart = ',astart ! print*,'min c = ',xstart,' max c = ',xend xstart = xstart + 0.000001 xend = xend - 0.000001 extra = 0.1*(xend-xstart) xcentre = 0.5*(xstart + xend) ymax = 1.5*func(xcentre) ymin = 1.5*func2(xcentre) call plot_swin(xstart-extra,xend+extra,ymin,ymax) call plot_box('bcnst',0.0,0,'1bvcnst',0.0,0) call plot_funx(func,10000,xstart,xend,1) call plot_funx(func2,10000,xstart,xend,1) call plot_label ('c','a',' ') return end subroutine exact_toystar_ACplane end module toystar1D !------------------------------------ ! ! these functions must be external ! !------------------------------------ real function func(x) real, intent(in) :: x real :: k,term,fact,gam1,gamp1 common /kconst/ k,fact,gam1,gamp1 ! print*,'k = ',k term = -1 -2.*fact*x*gam1 + k*x**(2./gamp1) if (term.le.0.) then ! print*,' warning: func < 0 ',term func = 0. else func = sqrt(term) endif end function func real function func2(x) implicit none real, intent(in) :: x real :: k,term,fact,gam1,gamp1 common /kconst/ k,fact,gam1,gamp1 ! print*,' k = ',k term = -1 -2.*fact*x*gam1 + k*x**(2./gamp1) if (term.le.0.) then func2 = 0. else func2 = -sqrt(term) endif end function func2 splash/src/read_data_mhutch.f90000644 000766 000000 00000043057 13261626263 017360 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2013 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! the data is stored in the global array dat ! ! THIS VERSION FOR SARAH MADDISON+MARK HUTCHISON'S DUSTY-SPH CODE ! -> Now automatically handles single/double precision ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(maxstep) : number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data, only:npartoftype,time,gamma,dat,maxpart,maxstep,maxcol,iamtype use params use filenames, only:nfiles use settings_data, only:ndim,ndimV,ncolumns,ncalc,iverbose,debugmode use mem_allocation, only:alloc use system_utils, only:lenvironment implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+4) :: datfile integer :: i,icol,ierr,iunit,ilen,j,ilast,k integer :: ihead1,ihead2,ihead3,numdata ! # of items on header lines integer :: ncol_max,ndim_max,npart_max,ndimV_max,nstep_max integer :: ncolstep,np,istep,nstepsinfile logical :: reallocate,finished integer :: norigin,ncr,istart,iout,nmlmax,index,ratio integer :: dimsw,fluidsw,itrace integer, dimension(:), allocatable :: head1 integer, dimension(:), allocatable :: head2 real(doub_prec), dimension(:), allocatable :: head3 real(doub_prec) :: timei,dt,sopt0 real(doub_prec) :: gammai real(doub_prec), dimension(:), allocatable :: dattemp integer, dimension(:), allocatable :: iam integer, dimension(:), allocatable :: iwas real :: dum iunit = 11 ! file unit number ndim_max = 1 ndimV_max = 1 nstepsread = 0 if (rootname(1:1).ne.' ') then datfile = trim(rootname) !print*,'rootname = ',rootname else print*,' **** no data read **** ' return endif if (iverbose.ge.1) print "(1x,a)",'reading Maddison/Hutchison format' write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! open(unit=iunit,iostat=ierr,file=datfile,status='old',form='unformatted') if (ierr /= 0) then print*,' *** Error opening '//trim(datfile)//' ***' return endif ! !--read first header line ! read(iunit,iostat=ierr,end=80) ihead1,ihead2,ihead3,ncolstep,np allocate(head1(ihead1)) allocate(head2(ihead2)) allocate(head3(ihead3)) read(iunit,iostat=ierr,end=80) head1(1:ihead1) read(iunit,iostat=ierr,end=80) head2(1:ihead2) read(iunit,iostat=ierr,end=80) head3(1:ihead3) ! Header one variables norigin = head1(1) ncr = head1(2) istart = head1(3) iout = head1(4) nmlmax = head1(5) index = head1(6) ratio = head1(7) itrace = head1(8) ! Header two variables ndim = abs(head2(1)) fluidsw = head2(2) !velsw = head2(3) !dragsw = head2(4) !Bkernsw = head2(5) !Dkernsw = head2(6) !freesw = head2(7) !stopgas = head2(8) !coolsw = head2(9) !interpsw = head2(10) !voidsw = head2(11) !phasesw = head2(12) !gravsw = head2(13) !photosw = head2(14) !eossw = head2(15) !masssw = head2(16) !usenumdens = head2(17) !varhsw = head2(18) !tracesw = head2(19) !timesw = head2(20) !growsw = head2(21) !shocksw = head2(22) !wavesw = head2(23) !plotsw = head2(24) !disc1dsw = head2(25) ! Header three variables dt = head3(1) timei = head3(2) sopt0 = head3(3) gammai = head3(4) deallocate(head1) deallocate(head2) deallocate(head3) ncolstep = ncolstep-2 ! minus 2 because iwas and iam read individually ndimV = 3 ! always have 3 velocity components written to file print "(a,i2,a,f8.4)",' ncolumns: ',ncolstep,' gamma: ',gammai ! !--check for basic errors in first line ! if (ierr /= 0 .or. ncr < 0 .or. istart < 0 & .or. iout < 0 .or. nmlmax < 0 .or. index < 0 .or. ratio < 0) then print "(a)",' *** Error reading header ***' print*,' norigin = ',norigin,' ncr = ',ncr,' istart =',istart,' iout = ',iout print*,' nmlmax = ',nmlmax,' index = ',index,' ratio =',ratio close(iunit) return endif ! !--Check for errors ! if (ierr /= 0 .or. np < 0 .or. np > 1.e9 .or. itrace > np) then print*,'n = ',np,' dt = ',dt,' time = ',timei,' i = ',itrace print*,'*** error reading timestep header ***' close(iunit) return endif ! !--check for errors in 3rd line ! if (ndim > 3 .or. ndimV > 3) then print*,'*** error in header: ndim or ndimV in file > 3' ndim = 3 ndimV = 3 close(iunit) return endif nstepsinfile = 1 ! nmlmax/iout nstep_max = 1 ! max(nstepsinfile,maxstep) nstepsread = 0 npart_max = maxpart ncol_max = ncolstep ! !--read first step ! over_steps: do i = indexstart,indexstart + nstepsinfile - 1 ! !--allocate memory for data arrays ! nstep_max = nstepsinfile !max(nstep_max,nfiles,maxstep,indexstart) npart_max = max(np,maxpart,norigin) if (.not.allocated(dat) .or. np > maxpart & .or. nstep_max > maxstep .or. ncol_max > maxcol) then call alloc(npart_max,nstep_max,ncolstep+ncalc,mixedtypes=.true.) endif ! !--now that memory is allocated, put header quantities -> splash quantities ! time(i) = timei gamma(i) = gammai npartoftype(1,i) = np if (iverbose.ge.1) then print "(a,i5,a,f8.4,a,i8,a,f8.4)",' step:',i,' time:',time(i),' npart:',np,' dt:',dt else print "(a,i5,a,f8.4,a,i8,a,i8)",' step:',i,' time:',time(i),' npart:',np endif if (ncolstep.ne.ncol_max) then print*,'*** Warning number of columns not equal for timesteps' ncolumns = ncolstep if (iverbose.ge.1) print*,'ncolumns = ',ncolumns,ncol_max if (ncolumns.gt.ncol_max) ncol_max = ncolumns endif ncolumns = ncolstep nstepsread = nstepsread + 1 ! !--read data for this timestep ! allocate(iwas(np)) read(iunit,iostat=ierr,end=80) iwas(1:np) if ( any(iwas.lt.1).or.any(iwas.gt.norigin) ) then do j = 1,np iwas(j) = j enddo endif npartoftype(:,i) = 0 allocate(iam(norigin)) iam(:) = 3 read(iunit,iostat=ierr,end=80) iam(iwas(:)) do j=1,norigin select case(iam(j)) case(1) ! GAS npartoftype(1,i) = npartoftype(1,i) + 1 iamtype(j,i) = 1_int1 case(0) ! DUST npartoftype(2,i) = npartoftype(2,i) + 1 iamtype(j,i) = 2_int1 case default ! DELETED PARTICLES npartoftype(3,i) = npartoftype(3,i) + 1 iamtype(j,i) = 3_int1 end select enddo deallocate(iam) if (kind(dat).ne.kind(dattemp)) then if (debugmode) print*,' converting kind from ',kind(dattemp),' to ',kind(dat) allocate(dattemp(norigin)) !dattemp(:) = 0D0 !--convert precision !do icol=1,ncolstep-1 ! all columns except h do icol=1,ncolstep ! all columns with h read(iunit,iostat=ierr,end=80) dattemp(iwas(:)) dat(1:norigin,icol,i) = real(dattemp) enddo deallocate(dattemp) else !--read directly into dat array if data types are the same !do icol=1,ncolstep-1 ! all columns except h do icol=1,ncolstep ! all columns with h read(iunit,iostat=ierr,end=80) dat(iwas(:),icol,i) enddo endif deallocate(iwas) enddo over_steps close(unit=11) ilast = indexstart+nstepsinfile - 1 ncolumns = ncol_max call set_labels if ( fluidsw.lt.0 .and. .not.lenvironment('NSPLASH_BARYCENTRIC') ) then call fake_twofluids endif if (npartoftype(2,ilast).gt.0) then print*,' ngas = ',npartoftype(1,ilast),' ndust = ',npartoftype(2,ilast) if (npartoftype(3,ilast) > 0) print*,' nunknown = ',npartoftype(3,ilast) endif if (debugmode) print*,'DEBUG> Read steps ',indexstart,'->',indexstart + nstepsread - 1, & ' last step ntot = ',sum(npartoftype(:,indexstart+nstepsread-1)) return 80 continue print*,' *** data file empty : no timesteps ***' return contains !-------------------------------------------- !-------------------------------------------- subroutine fake_twofluids use labels, only:idustfrac,irho,ix,ih,ipmass,ivx,ideltav implicit none integer :: ndust,jdust integer :: ntoti real :: rhodust,rhogas,rhotot,dustfraci,pmassgas,pmassdust,pmassj real, dimension(ndimV) :: veli,vgas,vdust,deltav integer :: itemp integer :: ifx,ify,ifz integer :: ir_grain integer :: ics integer :: idhdt integer :: idepsdt !do i=1,ndim ! ix(i) = i ! x,y,z positions !enddo !!--2D means x-z !if (ndim.eq.2) ix(2) = 3 ! x,z positions if 2D !ivx = 4 ! velocity (vector so it takes 3 rows) !irho = 7 ! density !ipmass = 8 ! mass !iutherm = 9 ! thermal energy !idendt = 10 ! time derivative of thermal energy !itemp = 11 ! temperature !ifx = 12 ! force in x direction !ify = 13 ! force in y direction !ifz = 14 ! force in z direction ir_grain = 15 ! grain size !ics = 16 ! sound speed !ih = 17 ! smoothing length !idhdt = 18 ! time derivative of h !idustfrac = 19 ! dust fraction !idepsdt = 20 ! time derivative of dust fraction if (idustfrac.gt.0 .and. irho.gt.0) then do i=indexstart,indexstart+nstepsread-1 ntoti = sum(npartoftype(:,i)) if (.not.allocated(dat) .or. (ntoti + npartoftype(1,i)).gt.maxpart) then call alloc(ntoti + npartoftype(1,i),maxstep,maxcol,mixedtypes=.true.) endif ndust = 0 !--zero the properties of newly created dust particles dat(ntoti+1:ntoti+npartoftype(1,i),:,i) = 0. do j=1,ntoti if (iamtype(j,i).eq.1) then ndust = ndust + 1 ! one dust particle for every gas particle rhotot = dat(j,irho,i) dustfraci = dat(j,idustfrac,i) rhogas = rhotot*(1. - dustfraci) rhodust = rhotot*dustfraci !--replace global properties with gas-only stuff dat(j,irho,i) = rhogas !--copy x, smoothing length onto dust particle jdust = ntoti + ndust !--fix dust fraction for viewing purposes !dat(jdust,idustfrac,i) = dustfraci !dat(j,idustfrac,i) = 1.-dustfraci !--fix grain size to be for dust only dat(jdust,ir_grain,i) = dat(j,ir_grain,i) dat(j,ir_grain,i) = 0. !--fill in dust properties if (ndim.gt.0) dat(jdust,ix(1:ndim),i) = dat(j,ix(1:ndim),i) if (ih.gt.0) dat(jdust,ih,i) = dat(j,ih,i) if (irho.gt.0) dat(jdust,irho,i) = rhodust iamtype(ntoti + ndust,i) = 2 !--particle masses if (ipmass.gt.0) then pmassj = dat(j,ipmass,i) pmassgas = pmassj*(1. - dustfraci) pmassdust = pmassj*dustfraci dat(j,ipmass,i) = pmassgas dat(jdust,ipmass,i) = pmassdust endif !--velocities if (ideltav.gt.0 .and. ivx.gt.0 .and. ndimV.gt.0) then veli(:) = dat(j,ivx:ivx+ndimV-1,i) deltav(:) = dat(j,ideltav:ideltav+ndimV-1,i) if ( rhodust.lt.1.e-30 ) then vgas(:) = veli(:) !vdust(:) = 0. vdust(:) = 1.e10 ! Dirty way to clean up axis else vgas(:) = veli(:) - rhodust/rhotot*deltav(:) vdust(:) = veli(:) + rhogas/rhotot*deltav(:) endif dat(j ,ivx:ivx+ndimV-1,i) = vgas(:) dat(jdust,ivx:ivx+ndimV-1,i) = vdust(:) endif endif enddo if (iverbose.ge.1) then print "(a,i10,a)",' Creating ',ndust,' fictional dust particles...' print "(a)",' (set NSPLASH_BARYCENTRIC=yes to plot barycentric values)' endif npartoftype(2,i) = npartoftype(2,i) + ndust enddo else print "(a)",' ERROR: could not locate dust-to-gas ratio and/or density' endif end subroutine fake_twofluids !-------------------------------------------- !-------------------------------------------- end subroutine read_data !------------------------------------------------------------ ! set labels for each column of data !------------------------------------------------------------ subroutine set_labels use labels, only:ix,ivx,ih,irho,iutherm,ipmass, & iamvec,labelvec,label,labeltype, & idustfrac,ideltav use params use settings_data, only:ndim,ndimV,iformat,ntypes, & UseTypeInRenderings use geometry, only:labelcoord implicit none integer :: i integer :: idendt,itemp integer :: ifx,ify,ifz integer :: ir_grain integer :: ics integer :: idhdt integer :: idepsdt integer :: iddeltavdt integer :: itcour integer :: itstop integer :: itdiff if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i ! x,y,z positions enddo !--2D means x-z if (ndim.eq.2) ix(2) = 3 ! x,z positions if 2D ivx = 4 ! velocity (vector so it takes 3 rows) irho = 7 ! density ipmass = 8 ! mass iutherm = 9 ! thermal energy idendt = 10 ! time derivative of thermal energy itemp = 11 ! temperature ifx = 12 ! force in x direction ify = 13 ! force in y direction ifz = 14 ! force in z direction ir_grain = 15 ! grain size ics = 16 ! sound speed ih = 17 ! smoothing length idhdt = 18 ! time derivative of h idustfrac = 19 ! dust fraction idepsdt = 20 ! time derivative of dust fraction ideltav = 21 iddeltavdt = 24 itcour = 27 itstop = 28 itdiff = 29 !!TESTING ! itemp = 10 ! temperature ! ifx = 11 ! force in x direction ! ify = 12 ! force in y direction ! ifz = 13 ! force in z direction ! ir_grain = 14 ! grain size ! ics = 15 ! sound speed ! ih = 16 ! smoothing length ! idhdt = 17 ! time derivative of h ! idustfrac = 18 ! dust fraction ! idepsdt = 19 ! time derivative of dust fraction ! ! ideltav = 20 !!TESTING iamvec(ifx:ifz) = ifx iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' labelvec(ifx:ifz) = 'f' label(1:3) = labelcoord(1:3,1) label(ivx:ivx+ndimV-1) = 'v_'//labelcoord(1:3,1) label(irho) = '\rho' label(ipmass) = 'm_{particle}' label(iutherm) = 'u' label(idendt) = 'du/dt' label(itemp) = 'Temp' label(ifx:ifz) = 'f_'//labelcoord(1:3,1) label(ir_grain) = 's_{grain}' label(ics) = 'cs' label(ih) = 'h' label(idhdt) = 'dh/dt' label(idustfrac) = '\epsilon' label(idepsdt) = 'd\epsilon/dt' label(ideltav) = '\Delta v_x' label(ideltav+1) = '\Delta v_y' label(ideltav+2) = '\Delta v_z' label(iddeltavdt) = 'd\Delta v_x/dt' label(iddeltavdt+1) = 'd\Delta v_y/dt' label(iddeltavdt+2) = 'd\Delta v_z/dt' label(itcour) = 't_{cour}' label(itstop) = 't_{stop}' label(itdiff) = 't_{diff}' ! !--set labels for each type of particles ! ntypes = 3 labeltype(1) = 'gas' labeltype(2) = 'dust' labeltype(3) = 'unknown' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/exact_function.f90000644 000766 000000 00000015713 13261626263 017113 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! Plots arbitrary analytic function y = f(x) ! Uses the function parser module !---------------------------------------------------------------------- module exactfunction implicit none contains subroutine exact_function(string,xplot,yplot,time,ierr) use fparser, only:initf,evalf,endf,EvalErrType,EvalErrMsg,rn implicit none character(len=*), intent(in) :: string real, intent(in), dimension(:) :: xplot real, intent(in) :: time real, intent(out), dimension(size(xplot)) :: yplot integer, intent(out) :: ierr integer :: i,j,nvars real(kind=rn), dimension(:), allocatable :: val print "(a)",' Plotting function f(x) = '//trim(string) if (len_trim(string).le.0) then print "(a)",' *** ERROR: blank function string in exact_function call' ierr = 1 return endif ierr = 0 ! !--work out how many subfunctions the string contains ! and allocate memory for the sub function values appropriately ! call get_nvars(string,nvars) allocate(val(nvars),stat=ierr) if (ierr /= 0) then print "(a)",' ERROR allocating memory for ',nvars,' sub-functions in exact_function' if (allocated(val)) deallocate(val) return endif call initf(nvars) call parse_subfunctions(string,nvars,.false.,ierr) if (EvalErrType.ne.0) then print "(a)",' *** ERROR parsing function: '//trim(EvalerrMsg())//' ***' ierr = EvalErrType else do i=1,size(xplot) val(1) = xplot(i) ! type conversion here val(2) = time ! type conversion here !--evaluate sub-functions in order of dependency do j=3,nvars val(j) = evalf(j,val(1:j-1)) enddo yplot(i) = real(evalf(1,val(1:nvars))) ! type conversion back if (EvalErrType /= 0) ierr = EvalErrType enddo if (ierr.ne.0) then print "(a)",' *** ERROR during function evaluation: '//trim(EvalerrMsg(ierr)) !--set exit error to zero so we plot the results anyway ierr = 0 endif endif call endf if (allocated(val)) deallocate(val) return end subroutine exact_function !---------------------------------------------------------------- ! check syntax in the function string - this subroutine ! mainly just an interface to checking routines in fparser !---------------------------------------------------------------- subroutine check_function(string,ierr,verbose) ! use fparser, only:checkf implicit none character(len=*), intent(in) :: string integer, intent(out) :: ierr logical, intent(in), optional :: verbose integer :: nvars call get_nvars(string,nvars) if (present(verbose)) then call parse_subfunctions(string,nvars,.true.,ierr,verbose=verbose) else call parse_subfunctions(string,nvars,.true.,ierr) endif ! ierr = checkf(string,(/'x'/)) end subroutine check_function !---------------------------------------------------------------- ! allow sub-function syntax (f(x) = y, y = 24*x) !---------------------------------------------------------------- subroutine parse_subfunctions(string,nvars,check,ierr,verbose) use fparser, only:checkf,parsef,EvalErrMsg,EvalErrType implicit none character(len=*), intent(in) :: string integer, intent(in) :: nvars logical, intent(in) :: check integer, intent(out) :: ierr logical, intent(in), optional :: verbose character(len=len(string)), dimension(nvars) :: var integer :: ieq,ivars,ivarsinit,lstr,j,icommaprev logical :: iverb iverb = .true. if (present(verbose)) iverb = verbose var(1) = 'x' var(2) = 't' ivarsinit = 2 ivars = ivarsinit lstr = len_trim(string) icommaprev = lstr+1 do j=lstr,1,-1 ! !--split the string according to commas ! if (string(j:j)==',') then !--sub functions must be of the form f(var) = val ieq = j + index(string(j+1:lstr),'=') if (ieq.eq.j) then print "(a)",'*** Error in sub-function syntax, missing equals sign in comma-separated function list' ierr = 4 return endif !--variable is what lies to left of equals sign ivars = ivars + 1 var(ivars) = string(j+1:ieq-1) if (len_trim(var(ivars)).le.0) then print "(a)",'*** Error in sub-function syntax, blank variable ' ierr = 3 return endif !--function is what lies to right of equals sign if (check) then if (iverb) then if (ivars.eq.ivarsinit+1) print "(a)",'Evaluating sub-functions in the order:' print "(a)",trim(var(ivars))//' = '//string(ieq+1:icommaprev-1) endif ierr = checkf(string(ieq+1:icommaprev-1),var(1:ivars-1)) if (ierr /= 0) return else call parsef(ivars,string(ieq+1:icommaprev-1),var(1:ivars-1)) if (EvalErrType.ne.0) then print "(a)",' *** ERROR parsing function: '//trim(EvalerrMsg())//' ***' ierr = EvalErrType return endif endif icommaprev = j endif enddo if (ivars.ne.nvars) then print "(a)",' Internal consistency error in parse_subfunctions:' print*,' nvars ',ivars,' not equal to that obtained in get_nvars, ',nvars endif ! !--finally, check/parse combined function ! if (check) then if (ivars.ge.ivarsinit .and. iverb) print "(1x,a)",'f('//trim(var(1))//') = '//string(1:icommaprev-1) ierr = checkf(string(1:icommaprev-1),var(1:ivars)) else call parsef(1,string(1:icommaprev-1),var(1:ivars)) if (EvalErrType.ne.0) then print "(a)",' *** ERROR parsing function: '//trim(EvalerrMsg())//' ***' ierr = EvalErrType endif endif end subroutine parse_subfunctions !---------------------------------------------------------------- ! query the number of sub-functions (number of commas) !---------------------------------------------------------------- subroutine get_nvars(string,nvars) implicit none character(len=*), intent(in) :: string integer, intent(out) :: nvars integer :: j nvars = 2 do j=1,len_trim(string) if (string(j:j)==',') nvars = nvars + 1 enddo end subroutine get_nvars end module exactfunction splash/src/exact_dustywaves.f90000644 000766 000000 00000675314 13261626263 017515 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! compute exact solution for a linear wave ! plots a sine function with a given amplitude, period and wavelength ! ---------------------------------------------------------------------- module dustywaves implicit none contains subroutine exact_dustywave(iplot,time,ampl,cs,Kdragin,lambda,x0,rhog0,rhod0,xplot,yplot,ierr) use cubic, only:cubicsolve_complex implicit none integer :: i real, parameter :: pi = 3.1415926536 integer, intent(in) :: iplot real, intent(in) :: time, ampl, cs, Kdragin, lambda, x0, rhog0, rhod0 real, intent(in) :: xplot(:) real, intent(out) :: yplot(size(xplot)) integer, intent(out) :: ierr real :: rhodeq,rhogeq,rhodsol,rhogsol,vdeq,vgeq,vgsol,vdsol real :: aa,bb,cc,w1r,w2r,w3r,w1i,w2i,w3i real :: k,xk,arg1,arg2,arg3,vgas,vdust,rhogas,rhodust real :: vd1r,vd1i,vd2r,vd2i,vd3r,vd3i real :: vg1r,vg1i,vg2r,vg2i,vg3r,vg3i real :: rhod1r,rhod1i,rhod2r,rhod2i,rhod3r,rhod3i real :: rhog1r,rhog1i,rhog2r,rhog2i,rhog3r,rhog3i real :: tgas1,tdust1,Kdrag complex :: xc(3) Kdrag = Kdragin if (mod(iplot,2).eq.1) then print*,'plotting two-fluid gas/dust linear wave solution ... ' print*,' lambda = ',lambda,' ampl = ',ampl,' cs = ',cs,' Kdrag = ',Kdrag endif ! ! check for errors ! ierr = 0 if (ampl.lt.0.) then print*,'error: amplitude < 0 on input' ierr = 1 return endif if (lambda <= 0.) then print*,'error: lambda <= 0 on input' ierr = 2 return endif if (cs <= 0) then print*,'error: sound speed <= 0 on input' ierr = 3 return endif if (rhog0 < 0) then print*,'error: gas density < 0 on input' ierr = 4 return endif if (rhod0 < 0) then print*,'error: dust density < 0 on input' ierr = 4 return endif if (Kdrag < 0) then print*,'error: drag coefficient < 0 on input' ierr = 5 return elseif (abs(Kdrag).lt.1.e-8) then print*,' WARNING: Kdrag = 0 on input; using tiny to avoid divergence ' Kdrag = 0. endif rhodeq = rhod0 ! initial dust density rhogeq = rhog0 ! initial gas density if (mod(iplot,2).eq.1) print*,' rho(dust),0 = ',rhod0,' rho(gas),0 = ',rhog0 select case(iplot) case(4) print*,'(dust density)' case(3) print*,'(gas density)' case(2) print*,'(dust velocity)' case default print*,'(gas velocity)' end select rhodsol = ampl*rhod0 ! amplitude of dust density perturbation rhogsol = ampl*rhog0 ! amplitude of gas density perturbation vdeq = 0. vgeq = 0. vgsol = ampl ! amplitude of gas velocity perturbation vdsol = ampl ! amplitude of dust velocity perturbation k = 2.*pi/lambda ! wavenumber vd1r = 0. vd1i = 0. vd2r = 0. vd2i = 0. vd3r = 0. vd3i = 0. rhod1r = 0. rhod1i = 0. rhod2r = 0. rhod2i = 0. rhod3r = 0. rhod3i = 0. ! !--solve cubic to get the 3 solutions for omega ! (these each have both real and imaginary components, ! labelled w1r, w1i etc.) ! tdust1 = Kdrag/rhodeq tgas1 = Kdrag/rhogeq aa = (tdust1 + tgas1) bb = k**2*cs**2 cc = bb*tdust1 call cubicsolve_complex(aa,bb,cc,xc) !--get solutions for (w = iy instead of y) xc = xc*cmplx(0.,1.) !print*,' roots are ',xc w1r = real(xc(1)) w2r = real(xc(2)) w3r = real(xc(3)) w1i = aimag(xc(1)) w2i = aimag(xc(2)) w3i = aimag(xc(3)) !------------------------------- ! G A S V E L O C I T I E S !------------------------------- vg3r =(k*Kdrag*vdsol*w3r**2*w2r + k*Kdrag*vdsol*w3r**2*w1r - k*Kdrag*vdsol*w3r*w2r*w1r - k*Kdrag*vdsol*w3r*w3i**2 +& k*Kdrag*vdsol*w2i*w1i*w3r - k*Kdrag*vdsol*w2r*w1i*w3i + k*Kdrag*vdsol*w2r*w3i**2 - k*Kdrag*vdsol*w2i*w3i*w1r +& k*Kdrag*vdsol*w3i**2*w1r - k*Kdrag*vgsol*w3r**2*w2r - k*Kdrag*vgsol*w3r**2*w1r + k*Kdrag*vgsol*w3r*w2r*w1r +& k*Kdrag*vgsol*w3r*w3i**2 - k*Kdrag*vgsol*w2i*w1i*w3r + k*Kdrag*vgsol*w2r*w1i*w3i - k*Kdrag*vgsol*w2r*w3i**2 +& k*Kdrag*vgsol*w2i*w3i*w1r - k*Kdrag*vgsol*w3i**2*w1r - rhogsol*w3r*w3i**2*w2r*w1i - rhogsol*w3r*w3i**2*w2i*w1r& + k*rhogeq*vgsol*w3r**3*w1i + k*rhogeq*vgsol*w3r**3*w2i - k*rhogeq*vgsol*w3r**2*w2r*w3i -& k*rhogeq*vgsol*w3r**2*w3i*w1r - k*rhogeq*vgsol*w3r*w2r**2*w1i - k*rhogeq*vgsol*w3r*w1i*w2i**2 +& k*rhogeq*vgsol*w3r*w3i**2*w2i - k*rhogeq*vgsol*w2r*w3i**3 - rhogsol*w3r**3*w1i*w2r - rhogsol*w3r**3*w2i*w1r +& rhogsol*w3r**2*w2r**2*w1i + rhogsol*w3r**2*w1i*w2i**2 + rhogsol*w3r**2*w2i*w1r**2 + rhogsol*w3r**2*w2i*w1i**2 -& rhogsol*w2r**2*w1i**2*w3i + rhogsol*w2r**2*w1i*w3i**2 - rhogsol*w3i*w1r**2*w2r**2 + rhogsol*w3i**3*w1r*w2r +& rhogsol*w3i**2*w2i*w1r**2 + rhogsol*w2i*w1i**2*w3i**2 - rhogsol*w1i**2*w2i**2*w3i - rhogsol*w2i**2*w1r**2*w3i +& rhogsol*w2i**2*w3i**2*w1i - rhogsol*w2i*w3i**3*w1i - rhogsol*k**2*cs**2*w3r**2*w2i -& rhogsol*k**2*cs**2*w3r**2*w1i + rhogsol*k**2*cs**2*w3r*w1i*w2r + rhogsol*k**2*cs**2*w3r*w2i*w1r -& rhogsol*k**2*cs**2*w3i*w2r*w1r + rhogsol*k**2*cs**2*w2i*w3i*w1i - rhogsol*k**2*cs**2*w3i**2*w1i -& rhogsol*k**2*cs**2*w3i**2*w2i + rhogsol*k**2*cs**2*w3r**2*w3i - k*rhogeq*vgsol*w3r*w2i*w1r**2 +& k*rhogeq*vgsol*w3r*w3i**2*w1i - k*rhogeq*vgsol*w3r*w2i*w1i**2 + k*rhogeq*vgsol*w3i*w1r*w2r**2 +& k*rhogeq*vgsol*w3i*w2r*w1r**2 + k*rhogeq*vgsol*w3i*w2r*w1i**2 + k*rhogeq*vgsol*w3i*w1r*w2i**2 -& k*rhogeq*vgsol*w3i**3*w1r - k*Kdrag*vdsol*w3r**3 + k*Kdrag*vgsol*w3r**3 + rhogsol*k**2*cs**2*w3i**3 +& rhogsol*w3r**2*w3i*w2r*w1r - rhogsol*w3r**2*w2i*w3i*w1i)/rhogeq/k/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 -& 2*w2i*w3i + w3r**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r) vg3i = - 1/rhogeq/k*( - w3r*w3i**2*cs**2*k**2*rhogsol - w3r**3*rhogsol*k**2*cs**2 + w3r**3*w2i*rhogsol*w1i +& w3r**2*w3i*w2i*rhogeq*k*vgsol - w3r**2*w2i*w3i*w1r*rhogsol - w2r*w3r**2*w3i*rhogsol*w1i -& w2r*w1i*w3i**3*rhogsol - w2r*w3r**3*w1r*rhogsol - w3r**2*w1i*k*Kdrag*vgsol + w3r**2*w2i*k*Kdrag*vdsol +& w3r*w3i**2*k*rhogeq*w1r*vgsol + w3r*w1i*w3i**2*rhogsol*w2i - w3r**2*w2i*k*Kdrag*vgsol +& w3r**2*w1i*k*Kdrag*vdsol - w3r**2*w1i**2*k*rhogeq*vgsol + w3r**3*k*rhogeq*w1r*vgsol +& w3r**2*w3i*w1i*rhogeq*k*vgsol - w3r**2*w1r**2*rhogeq*k*vgsol + w3r**2*w1r*cs**2*k**2*rhogsol +& w3r**2*w3i*k*Kdrag*vgsol - w3r**2*w3i*k*Kdrag*vdsol + w2r*w3r*rhogeq*k*vgsol*w3i**2 +& w2r*w3r**2*cs**2*k**2*rhogsol + w2r*w3r**3*vgsol*k*rhogeq + w2r*w3i**2*cs**2*k**2*rhogsol -& w3i**2*k*rhogeq*w1r**2*vgsol + w3i**2*cs**2*k**2*rhogsol*w1r - 2*w3r**2*k*w2i*rhogeq*w1i*vgsol -& w1i**2*rhogeq*k*vgsol*w3i**2 - 2*w3i**2*k*w2i*rhogeq*w1i*vgsol - w3r**2*w2i**2*rhogeq*k*vgsol +& w2i*w1i**2*w3i*rhogeq*k*vgsol - w2i*w3i*w1i*k*Kdrag*vdsol + w2i*w3i*w1i*k*Kdrag*vgsol -& w2i*w3i*w1r*cs**2*k**2*rhogsol + w2i*rhogeq*k*vgsol*w3i*w1r**2 + w2r*w3i*w1r*k*Kdrag*vdsol -& w2i*w3i**2*k*Kdrag*vgsol - w2r*w3i*w1r*k*Kdrag*vgsol + w2i*w3i**2*k*Kdrag*vdsol -& 2*w2r*w3i**2*k*rhogeq*w1r*vgsol + w2i*w3i**3*rhogeq*k*vgsol + w3r*w2i**2*w1r*rhogeq*k*vgsol +& w2r**2*w3i**2*w1r*rhogsol - w1i*w3i**2*k*Kdrag*vgsol + w1i*rhogeq*k*vgsol*w3i**3 + w1i*w3i**2*k*Kdrag*vdsol -& w2r*w3r*w3i**2*w1r*rhogsol - w3i**3*k*Kdrag*vdsol + w3i**3*k*Kdrag*vgsol - w2r*w1i*w3i*cs**2*k**2*rhogsol -& w1r*w2i*w3i**3*rhogsol + w3i*w2r**2*w1i*rhogeq*k*vgsol + w1i**2*w3i**2*w2r*rhogsol -& w3i**2*w2i**2*rhogeq*k*vgsol + w2i**2*w3i*w1i*rhogeq*k*vgsol + w3r*w2i*w1i*cs**2*k**2*rhogsol -& w3r*w1i**2*w2r**2*rhogsol - w3r*w2i**2*w1i**2*rhogsol + w3r**2*w1i**2*w2r*rhogsol + w3r**2*w2i**2*w1r*rhogsol -& w2r**2*w3i**2*k*rhogeq*vgsol + w3r**2*w2r*w1r**2*rhogsol - w3r**2*w2r**2*rhogeq*k*vgsol +& w3r**2*w2r**2*w1r*rhogsol - w3r*w1r**2*w2i**2*rhogsol - w3r*w2r**2*w1r**2*rhogsol - w3r*w2i*w1r*k*Kdrag*vdsol +& w3r*w2i*w1r*k*Kdrag*vgsol - w3r*w2r*w1r*cs**2*k**2*rhogsol - 2*w3r**2*w2r*k*rhogeq*w1r*vgsol +& w3r*w2r**2*k*rhogeq*w1r*vgsol + w3r*w2r*w1i*k*Kdrag*vgsol + w3r*w2r*w1i**2*k*rhogeq*vgsol -& w3r*w2r*w1i*k*Kdrag*vdsol + w3r*w2r*w1r**2*rhogeq*k*vgsol + w2i**2*w3i**2*w1r*rhogsol +& w2r*w3i**2*w1r**2*rhogsol)/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i + w3r**2)/(w1i**2 - 2*w3i*w1i +& w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r) vg2r = - (w2r**2*rhogsol*w2i*w3i*w1i + w2r**2*rhogsol*k**2*cs**2*w1i + k*Kdrag*vdsol*w3r*w2r*w1r +& k*Kdrag*vdsol*w2i*w1i*w3r - k*Kdrag*vdsol*w2r*w1i*w3i + k*Kdrag*vdsol*w2i*w3i*w1r - k*Kdrag*vgsol*w3r*w2r*w1r -& k*Kdrag*vgsol*w2i*w1i*w3r + k*Kdrag*vgsol*w2r*w1i*w3i - k*Kdrag*vgsol*w2i*w3i*w1r - rhogsol*w3r**2*w2r**2*w1i -& rhogsol*w3r**2*w1i*w2i**2 + rhogsol*w3r**2*w2i*w1r**2 + rhogsol*w3r**2*w2i*w1i**2 - rhogsol*w2r**2*w1i**2*w3i -& rhogsol*w2r**2*w1i*w3i**2 - rhogsol*w3i*w1r**2*w2r**2 + rhogsol*w3i**2*w2i*w1r**2 + rhogsol*w2i*w1i**2*w3i**2 -& rhogsol*w1i**2*w2i**2*w3i - rhogsol*w2i**2*w1r**2*w3i - rhogsol*w2i**2*w3i**2*w1i + w2r**3*rhogsol*w3i*w1r -& rhogsol*k**2*cs**2*w3r*w1i*w2r + rhogsol*k**2*cs**2*w3r*w2i*w1r - rhogsol*k**2*cs**2*w3i*w2r*w1r -& rhogsol*k**2*cs**2*w2i*w3i*w1i - k*rhogeq*vgsol*w3r*w2i*w1r**2 - k*rhogeq*vgsol*w3r*w2i*w1i**2 +& k*rhogeq*vgsol*w3i*w2r*w1r**2 + k*rhogeq*vgsol*w3i*w2r*w1i**2 + w2r**2*rhogsol*k**2*cs**2*w3i +& w2i**3*rhogsol*w3i*w1i - w2r**3*k*Kdrag*vgsol + w2r**3*k*Kdrag*vdsol - w2r**3*vgsol*k*rhogeq*w1i +& w2i*vgsol*k*rhogeq*w1r*w2r**2 + w2i**3*vgsol*k*rhogeq*w1r - w2i**2*w2r*vgsol*k*rhogeq*w1i -& w3r*w2i**3*rhogsol*w1r + w3r*w2r**3*rhogsol*w1i - w3r*w2r**2*rhogsol*w2i*w1r - w3r*w2i**2*k*Kdrag*vdsol +& w3r*w2i**2*rhogsol*w1i*w2r + w3r*w2i**2*k*Kdrag*vgsol - w3r**2*vgsol*k*rhogeq*w2i*w1r +& w3r**2*w2r*k*rhogeq*vgsol*w1i + w3r*w2r**2*k*Kdrag*vgsol - w3r*w2r**2*k*Kdrag*vdsol +& w3r*w2r**2*k*rhogeq*vgsol*w2i + w3r*w2i**3*k*rhogeq*vgsol - w2r**3*k*rhogeq*vgsol*w3i -& w2i**3*rhogsol*k**2*cs**2 - vgsol*k*rhogeq*w2i*w1r*w3i**2 + w2i**2*k*Kdrag*vgsol*w1r - w2i**2*k*Kdrag*vgsol*w2r& - w2i**2*k*Kdrag*vdsol*w1r + w2i**2*rhogsol*k**2*cs**2*w3i - w2i**2*k*rhogeq*vgsol*w2r*w3i +& w2i**2*rhogsol*k**2*cs**2*w1i + w2i**2*rhogsol*w3i*w2r*w1r - w2r**2*k*Kdrag*vdsol*w1r +& w2r**2*k*Kdrag*vgsol*w1r + w2r*vgsol*k*rhogeq*w1i*w3i**2 + w2i**2*k*Kdrag*vdsol*w2r -& w2r**2*rhogsol*k**2*cs**2*w2i)/rhogeq/k/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i + w3r**2)/(w2r**2 +& w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2) vg2i =(w1r**2*w2i**2*k*rhogeq*vgsol - w2r**2*w2i*k*Kdrag*vgsol + w2i**2*w1i**2*k*rhogeq*vgsol -& w1i*w2i**3*rhogeq*k*vgsol - w2i**3*w3i*rhogeq*k*vgsol - w3r**2*k*w2i*rhogeq*w1i*vgsol -& w3i**2*k*w2i*rhogeq*w1i*vgsol + w3r*w2i*w1i*w2r**2*rhogsol + w3r**2*w2i**2*rhogeq*k*vgsol -& w2i**2*w1r*cs**2*k**2*rhogsol - w2i*w1i**2*w3i*rhogeq*k*vgsol + w2i*w3i*w1i*k*Kdrag*vdsol -& w2i*w3i*w1i*k*Kdrag*vgsol - w2r**2*w3i*w2i*rhogeq*k*vgsol + w2i*w3i*w1r*cs**2*k**2*rhogsol -& w2i*rhogeq*k*vgsol*w3i*w1r**2 + w2r**3*cs**2*k**2*rhogsol - w2r**2*w1i*w2i*rhogeq*k*vgsol +& w2r**2*w2i*w3i*w1r*rhogsol + w2r*w3i*w1r*k*Kdrag*vdsol - w2i**2*w1i*k*Kdrag*vdsol - w2r*w3i*w1r*k*Kdrag*vgsol -& w2r*w3i**2*k*rhogeq*w1r*vgsol - w2r*w1r*w2i**2*rhogeq*k*vgsol + 2*w3r*w2i**2*w1r*rhogeq*k*vgsol +& w2r**2*w1i*k*Kdrag*vgsol - w2r**2*w3i**2*w1r*rhogsol + w3r*w2i**3*w1i*rhogsol + w2i**2*w1i*k*Kdrag*vgsol -& w2r*w1i*w3i*cs**2*k**2*rhogsol + 2*w3i*w2r**2*w1i*rhogeq*k*vgsol + w1i**2*w3i**2*w2r*rhogsol +& w3i**2*w2i**2*rhogeq*k*vgsol + 2*w2i**2*w3i*w1i*rhogeq*k*vgsol + w3r*w2i*w1i*cs**2*k**2*rhogsol -& w3r*w1i**2*w2r**2*rhogsol - w3r*w2i**2*w1i**2*rhogsol + w3r**2*w1i**2*w2r*rhogsol - w3r**2*w2i**2*w1r*rhogsol +& w2r**2*w2i*k*Kdrag*vdsol - w2r**3*k*rhogeq*w1r*vgsol + w2i**2*w3i*k*Kdrag*vgsol - w2i**2*w3i*k*Kdrag*vdsol +& w2r*w2i**2*cs**2*k**2*rhogsol - w2r*w2i**2*w3i*rhogsol*w1i + w2r**2*w3i**2*k*rhogeq*vgsol +& w2r**2*w1r**2*rhogeq*k*vgsol - w2r**2*w1r*cs**2*k**2*rhogsol + w3r**2*w2r*w1r**2*rhogsol +& w2r**2*w3i*k*Kdrag*vgsol - w2r**2*w3i*k*Kdrag*vdsol - w2r**2*w1i*k*Kdrag*vdsol + w2r**2*w1i**2*k*rhogeq*vgsol -& w3r*w2r**2*cs**2*k**2*rhogsol + w3r*w2r*w2i**2*w1r*rhogsol - w3r*w2i**2*cs**2*k**2*rhogsol -& w3r*w2r**3*rhogeq*k*vgsol + w3r**2*w2r**2*rhogeq*k*vgsol - w3r**2*w2r**2*w1r*rhogsol - w2r**3*w3i*rhogsol*w1i -& w3r*w1r**2*w2i**2*rhogsol - w3r*w2r**2*w1r**2*rhogsol - w3r*w2i*w1r*k*Kdrag*vdsol + w3r*w2i*w1r*k*Kdrag*vgsol +& w3r*w2r**3*w1r*rhogsol + w3r*w2r*w1r*cs**2*k**2*rhogsol - w3r**2*w2r*k*rhogeq*w1r*vgsol +& 2*w3r*w2r**2*k*rhogeq*w1r*vgsol - w3r*w2r*w2i**2*rhogeq*k*vgsol - w3r*w2r*w1i*k*Kdrag*vgsol -& w3r*w2r*w1i**2*k*rhogeq*vgsol + w3r*w2r*w1i*k*Kdrag*vdsol - w3r*w2r*w1r**2*rhogeq*k*vgsol +& w2i**3*w3i*w1r*rhogsol - w2i**3*k*Kdrag*vgsol - w2i**2*w3i**2*w1r*rhogsol + w2i**3*k*Kdrag*vdsol +& w2r*w3i**2*w1r**2*rhogsol)/rhogeq/k/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i + w3r**2)/(w2r**2 +& w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2) vg1r =( - rhogsol*w1i**3*w3i*w2i + rhogsol*k**2*cs**2*w1i**3 - rhogsol*w3i*w2r*w1r**3 - k*Kdrag*vgsol*w2r*w1r**2 +& k*Kdrag*vdsol*w2r*w1r**2 - k*Kdrag*vdsol*w1r*w1i**2 - k*Kdrag*vgsol*w2r*w1i**2 + k*Kdrag*vdsol*w2r*w1i**2 +& k*Kdrag*vgsol*w1r*w1i**2 - k*Kdrag*vdsol*w1r**3 + k*Kdrag*vgsol*w1r**3 - w3r*rhogsol*w2i*w1r**3 -& w3r*Kdrag*w1i**2*k*vgsol + w3r*k*Kdrag*vdsol*w1r**2 + w3r*k*Kdrag*vdsol*w1i**2 - w3r*Kdrag*w1r**2*k*vgsol -& k*Kdrag*vdsol*w3r*w2r*w1r - k*Kdrag*vdsol*w2i*w1i*w3r - k*Kdrag*vdsol*w2r*w1i*w3i + k*Kdrag*vdsol*w2i*w3i*w1r +& k*Kdrag*vgsol*w3r*w2r*w1r + k*Kdrag*vgsol*w2i*w1i*w3r + k*Kdrag*vgsol*w2r*w1i*w3i - k*Kdrag*vgsol*w2i*w3i*w1r +& k*rhogeq*vgsol*w3r*w2r**2*w1i + k*rhogeq*vgsol*w3r*w1i*w2i**2 - rhogsol*w3r**2*w2r**2*w1i -& rhogsol*w3r**2*w1i*w2i**2 + rhogsol*w3r**2*w2i*w1r**2 + rhogsol*w3r**2*w2i*w1i**2 + rhogsol*w2r**2*w1i**2*w3i -& rhogsol*w2r**2*w1i*w3i**2 + rhogsol*w3i*w1r**2*w2r**2 + rhogsol*w3i**2*w2i*w1r**2 + rhogsol*w2i*w1i**2*w3i**2 +& rhogsol*w1i**2*w2i**2*w3i + rhogsol*w2i**2*w1r**2*w3i - rhogsol*w2i**2*w3i**2*w1i -& rhogsol*k**2*cs**2*w3r*w1i*w2r + rhogsol*k**2*cs**2*w3r*w2i*w1r + rhogsol*k**2*cs**2*w3i*w2r*w1r +& rhogsol*k**2*cs**2*w2i*w3i*w1i - k*rhogeq*vgsol*w3i*w1r*w2r**2 - k*rhogeq*vgsol*w3i*w1r*w2i**2 -& w3r**2*vgsol*k*rhogeq*w2i*w1r + w3r**2*w2r*k*rhogeq*vgsol*w1i + w3r*rhogsol*w1i**3*w2r -& vgsol*k*rhogeq*w2i*w1r*w3i**2 - w3r*w1r**2*k*rhogeq*vgsol*w1i - w3r*w1i**3*k*rhogeq*vgsol -& w3r*w1r*rhogsol*w2i*w1i**2 + w3r*rhogsol*w1i*w2r*w1r**2 + w2r*vgsol*k*rhogeq*w1i*w3i**2 -& rhogsol*w3i*w2r*w1r*w1i**2 - rhogeq*w2r*w1i*k*vgsol*w1r**2 - rhogeq*w2r*w1i**3*k*vgsol +& k*rhogeq*vgsol*w2i*w1r**3 - rhogsol*k**2*cs**2*w2i*w1r**2 + k*rhogeq*vgsol*w3i*w1r*w1i**2 +& rhogsol*k**2*cs**2*w1i*w1r**2 - rhogsol*k**2*cs**2*w3i*w1i**2 + k*rhogeq*vgsol*w3i*w1r**3 -& rhogsol*k**2*cs**2*w1i**2*w2i - rhogsol*k**2*cs**2*w3i*w1r**2 - rhogsol*w3i*w1r**2*w2i*w1i +& w1r*k*rhogeq*vgsol*w2i*w1i**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r)/(w2r**2 + w1r**2 +& w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2)/rhogeq/k vg1i = - ( - w1r**2*w2i**2*k*rhogeq*vgsol - w2i**2*w1i**2*k*rhogeq*vgsol - w3r**2*w1i**2*k*rhogeq*vgsol -& w3r**2*w1r**2*rhogeq*k*vgsol - w3r*w2r*w1r**3*rhogsol - w3r*w1i**2*w2r*rhogsol*w1r -& w3i**2*k*rhogeq*w1r**2*vgsol + w3r**2*k*w2i*rhogeq*w1i*vgsol - w1i**2*rhogeq*k*vgsol*w3i**2 +& w3i**2*k*w2i*rhogeq*w1i*vgsol + w1i**3*k*rhogeq*vgsol*w3i - w3r*w2i*w1i*rhogsol*w1r**2 - w1i**3*k*Kdrag*vdsol -& 2*w2i*w1i**2*w3i*rhogeq*k*vgsol + w2i*w3i*w1r*rhogsol*w1i**2 - w1i**2*k*Kdrag*vgsol*w3i -& w1i*k*Kdrag*vdsol*w1r**2 + w1i**2*k*rhogeq*vgsol*w3r*w1r + w1i*k*Kdrag*vgsol*w1r**2 - w1r**3*cs**2*k**2*rhogsol& + cs**2*k**2*rhogsol*w3r*w1r**2 + w3i*k*Kdrag*vdsol*w1r**2 - w2i*w3i*w1i*k*Kdrag*vdsol +& w2i*w3i*w1i*k*Kdrag*vgsol + cs**2*k**2*rhogsol*w3r*w1i**2 + w3i*k*Kdrag*vdsol*w1i**2 +& w1i**3*w2i*rhogeq*k*vgsol + w2r*k*rhogeq*w1r*vgsol*w1i**2 - w2i*k*Kdrag*vgsol*w1i**2 + w2i*k*Kdrag*vdsol*w1i**2& + w1r**2*rhogeq*k*vgsol*w3i*w1i + w1r**3*rhogeq*k*vgsol*w3r + w2i*k*Kdrag*vdsol*w1r**2 +& w2i*w3i*w1r*cs**2*k**2*rhogsol - 2*w2i*rhogeq*k*vgsol*w3i*w1r**2 - w3i*k*Kdrag*vgsol*w1r**2 -& w2r*w3i*w1r*k*Kdrag*vdsol + w2r*w3i*w1r*k*Kdrag*vgsol + w2r*w3i**2*k*rhogeq*w1r*vgsol +& w3r*w2i**2*w1r*rhogeq*k*vgsol - w2r**2*w3i**2*w1r*rhogsol - w1i**3*w2r*rhogsol*w3i - w2r*w1r**2*rhogsol*w3i*w1i& - w2i*k*Kdrag*vgsol*w1r**2 - w2r*w1i*w3i*cs**2*k**2*rhogsol + w2i*w3i*w1r**3*rhogsol +& w3i*w2r**2*w1i*rhogeq*k*vgsol + w1i**2*w3i**2*w2r*rhogsol + w1i*w2i*rhogeq*k*vgsol*w1r**2 +& w2i**2*w3i*w1i*rhogeq*k*vgsol - w3r*w2i*w1i*cs**2*k**2*rhogsol + w3r*w1i**2*w2r**2*rhogsol +& w3r*w2i**2*w1i**2*rhogsol + w3r**2*w1i**2*w2r*rhogsol - w3r**2*w2i**2*w1r*rhogsol -& w2r**2*w1r**2*rhogeq*k*vgsol + w3r**2*w2r*w1r**2*rhogsol - w2r**2*w1i**2*k*rhogeq*vgsol -& w1r*cs**2*k**2*rhogsol*w1i**2 - w3r**2*w2r**2*w1r*rhogsol + w3r*w1r**2*w2i**2*rhogsol +& w3r*w2r**2*w1r**2*rhogsol - w3r*w2i*w1i**3*rhogsol + w1i**3*k*Kdrag*vgsol - w3r*w2i*w1r*k*Kdrag*vdsol +& w3r*w2i*w1r*k*Kdrag*vgsol - w3r*w2r*w1r*cs**2*k**2*rhogsol + w3r**2*w2r*k*rhogeq*w1r*vgsol +& w3r*w2r**2*k*rhogeq*w1r*vgsol - w3r*w2r*w1i*k*Kdrag*vgsol - 2*w3r*w2r*w1i**2*k*rhogeq*vgsol +& w3r*w2r*w1i*k*Kdrag*vdsol - 2*w3r*w2r*w1r**2*rhogeq*k*vgsol + w2r*k*rhogeq*w1r**3*vgsol +& w2r*w1r**2*cs**2*k**2*rhogsol - w2i**2*w3i**2*w1r*rhogsol + rhogsol*k**2*cs**2*w1i**2*w2r +& w2r*w3i**2*w1r**2*rhogsol)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r)/(w2r**2 + w1r**2 +& w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2)/rhogeq/k !------------------------------- ! D U S T V E L O C I T I E S !------------------------------- if (Kdrag.gt.0.) then vd3r = - (rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol + rhogeq**2*w3i**4*k*w1r*vgsol -& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r - w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r + w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r -& rhogeq**2*cs**2*k**3*w2i**2*w1r*vgsol + rhogeq*cs**4*k**4*w2r*w1r*rhogsol -& rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol - rhogeq*cs**2*k**3*w2r*w1i*Kdrag*vgsol -& rhogeq**2*cs**2*k**3*w2r*w1i**2*vgsol + rhogeq*cs**2*k**3*w2r*w1i*Kdrag*vdsol -& rhogeq**2*cs**2*k**3*w2r*w1r**2*vgsol - rhogeq*w3i**4*cs**2*k**2*rhogsol - rhogeq*cs**4*k**4*w2i*w1i*rhogsol +& rhogeq*cs**2*k**2*w1i**2*w2r**2*rhogsol + rhogeq**2*w3i**4*w2r*k*vgsol - rhogeq*w3i**3*k*Kdrag*vdsol*w1r +& 2*rhogeq*w3i**3*k*Kdrag*vgsol*w2r + rhogeq*w2i**2*w1i**2*cs**2*k**2*rhogsol +& rhogeq*w2i**2*w1r**2*cs**2*k**2*rhogsol - rhogeq*w3i**4*w2r*w1r*rhogsol + rhogeq*w3i**4*w1i*rhogsol*w2i +& w3i*cs**4*k**4*rhogeq*rhogsol*w2i + w3i*cs**4*k**4*rhogeq*rhogsol*w1i + w3i**2*Kdrag**2*k*vgsol*w1r -& w3i**3*Kdrag*rhogsol*k**2*cs**2 - w3i**3*Kdrag*rhogsol*w2r*w1r + w3i**3*Kdrag*rhogsol*w2i*w1i -& w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1i - w3i*cs**2*k**2*rhogeq*rhogsol*w1i*w2i**2 +& rhogeq*w3i**3*rhogsol*k**2*cs**2*w2i + rhogeq*w3i**3*rhogsol*k**2*cs**2*w1i - rhogeq*w3i**3*rhogsol*w2r**2*w1i& - rhogeq*w3i**3*rhogsol*w1i*w2i**2 - rhogeq*w3i**3*rhogsol*w2i*w1r**2 -& w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w1r**2 - w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w1i**2 -& rhogeq*w3i**3*rhogsol*w2i*w1i**2 - rhogeq*w3i**3*k*Kdrag*vdsol*w2r + 2*rhogeq*w3i**3*k*Kdrag*vgsol*w1r +& w3r*rhogeq**2*cs**2*k**3*w2i**2*vgsol + w3r*rhogeq*w2r*w3i**2*cs**2*k**2*rhogsol -& w3r*rhogeq**2*w3i**2*k*w1r**2*vgsol + w3r*rhogeq*w3i**2*cs**2*k**2*rhogsol*w1r +& 2*w3r*rhogeq*cs**2*k**3*w3i*Kdrag*vdsol - w3r*rhogeq*cs**4*k**4*w2r*rhogsol +& w3r*rhogeq**2*cs**2*k**3*w2r**2*vgsol + w3r**4*rhogeq**2*w2r*vgsol*k - w3r**4*rhogeq*w2r*w1r*rhogsol +& w3r**4*rhogeq**2*k*w1r*vgsol - w3r**4*rhogeq*rhogsol*k**2*cs**2 + w3r**4*rhogeq*w2i*rhogsol*w1i +& 2*w3r*rhogeq*w2i*w3i*w1i*k*Kdrag*vgsol + 2*w3r*rhogeq**2*w2i*k*vgsol*w3i*w1r**2 +& 2*w3r*rhogeq*w2r*w3i*w1r*k*Kdrag*vdsol - 2*w3r*rhogeq*w2i*w3i**2*k*Kdrag*vgsol - w3r*w3i**2*Kdrag**2*k*vgsol +& w3r*w3i**2*Kdrag**2*k*vdsol - w3r*rhogeq**2*w1i**2*k*vgsol*w3i**2 - 2*w3r*rhogeq**2*w3i**2*k*w2i*w1i*vgsol +& 2*w3r*rhogeq**2*w2i*w1i**2*w3i*k*vgsol + w3r*w3i**2*Kdrag*rhogsol*w1i*w2r + w3r*w3i**2*Kdrag*rhogsol*w2i*w1r -& w3r*Kdrag*rhogsol*k**2*cs**2*w1i*w2r - w3r*Kdrag*rhogsol*k**2*cs**2*w2i*w1r +& w3r*Kdrag*k*rhogeq*vgsol*w2i*w1r**2 + w3r*Kdrag*k*rhogeq*vgsol*w2i*w1i**2 + w3r*Kdrag**2*k*vdsol*w2r*w1r -& w3r*Kdrag**2*k*vdsol*w2i*w1i - w3r*Kdrag**2*k*vgsol*w2r*w1r + w3r*Kdrag**2*k*vgsol*w2i*w1i +& w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1i + w3r*Kdrag*k*rhogeq*vgsol*w1i*w2i**2 - w3r*rhogeq**2*w2r**2*w3i**2*k*vgsol& - 2*w3r*rhogeq*w2r*w3i*w1r*k*Kdrag*vgsol + w3r*rhogeq*w2i*w3i**2*k*Kdrag*vdsol -& 2*w3r*rhogeq**2*w2r*w3i**2*k*w1r*vgsol + w3r*rhogeq*w2r**2*w3i**2*w1r*rhogsol -& 2*w3r*rhogeq*w1i*w3i**2*k*Kdrag*vgsol + w3r*rhogeq*w1i*w3i**2*k*Kdrag*vdsol +& 2*w3r*rhogeq**2*w3i*w2r**2*w1i*k*vgsol + w3r*rhogeq*w1i**2*w3i**2*w2r*rhogsol -& w3r*rhogeq**2*w3i**2*w2i**2*k*vgsol - w3r*rhogeq*cs**2*k**2*w1i**2*w2r*rhogsol -& w3r*rhogeq*cs**2*k**2*w2r*w1r**2*rhogsol - w3r*rhogeq*cs**2*k**2*w2r**2*w1r*rhogsol +& w3r*rhogeq*w2i**2*w3i**2*w1r*rhogsol + w3r*rhogeq*w2r*w3i**2*w1r**2*rhogsol -& 2*w3r*rhogeq**2*cs**2*k**3*w3i*w2i*vgsol - w3r*rhogeq*w2i**2*w1r*cs**2*k**2*rhogsol +& 2*w3r*rhogeq**2*w2i**2*w3i*w1i*k*vgsol + w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol -& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol + w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol -& w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol + w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol -& 2*w3r*rhogeq**2*cs**2*k**3*w3i*w1i*vgsol + w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol -& w3r*rhogeq*cs**4*k**4*w1r*rhogsol - 2*w3r*rhogeq*cs**2*k**3*w3i*Kdrag*vgsol +& 2*w3r*rhogeq**2*cs**2*k**3*w2r*w1r*vgsol - 2*w3r*rhogeq*w2i*w3i*w1i*k*Kdrag*vdsol +& 2*w3r*rhogeq**2*cs**2*k**3*w2i*w1i*vgsol + w3r**2*w3i*rhogeq*rhogsol*k**2*cs**2*w1i -& w3r**2*w3i*rhogeq*rhogsol*w2r**2*w1i - w3r**2*w3i*rhogeq*rhogsol*w1i*w2i**2 -& w3r**2*w3i*rhogeq*rhogsol*w2i*w1r**2 - w3r**2*w3i*rhogeq*rhogsol*w2i*w1i**2 -& w3r**2*w3i*rhogeq*k*Kdrag*vdsol*w2r - w3r**2*Kdrag*rhogsol*w3i*w2r*w1r + w3r**2*Kdrag*rhogsol*w2i*w3i*w1i -& w3r**2*w3i*rhogeq*k*Kdrag*vdsol*w1r + w3r**2*w3i*rhogeq*rhogsol*k**2*cs**2*w2i -& w3r**2*Kdrag*rhogsol*k**2*cs**2*w3i + w3r**2*Kdrag*rhogsol*k**2*cs**2*w2i + w3r**2*Kdrag*rhogsol*k**2*cs**2*w1i& + 2*w3r**2*Kdrag*k*rhogeq*vgsol*w2r*w3i + 2*w3r**2*Kdrag*k*rhogeq*vgsol*w3i*w1r +& 2*w3r**2*rhogeq**2*w2r*k*vgsol*w3i**2 - w3r**2*rhogeq**2*cs**2*k**3*w2r*vgsol +& w3r**2*rhogeq*w2r*w1i*k*Kdrag*vgsol + w3r**2*rhogeq**2*w2r*w1i**2*k*vgsol - w3r**2*rhogeq*w2r*w1i*k*Kdrag*vdsol& + w3r**2*rhogeq**2*w2r*w1r**2*k*vgsol - w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol - w3r**2*Kdrag**2*k*vdsol*w2r -& w3r**2*rhogeq*w2i**2*w1i**2*rhogsol - w3r**2*rhogeq*w1r**2*w2i**2*rhogsol - w3r**2*rhogeq*w2r**2*w1r**2*rhogsol& + w3r**2*rhogeq*cs**4*k**4*rhogsol - w3r**2*rhogeq*w1i**2*w2r**2*rhogsol - w3r**2*Kdrag*rhogsol*w1i*w2i**2 -& w3r**2*Kdrag*rhogsol*w2i*w1r**2 - w3r**2*Kdrag*rhogsol*w2i*w1i**2 - w3r**2*Kdrag*rhogsol*w2r**2*w1i -& w3r**2*Kdrag**2*k*vdsol*w1r + w3r**2*Kdrag**2*k*vgsol*w2r + w3r**2*Kdrag**2*k*vgsol*w1r +& w3r**3*Kdrag*rhogsol*w2i*w1r + w3r**3*rhogeq*w2i*k*Kdrag*vdsol - w3r**3*rhogeq**2*w2i**2*k*vgsol +& w3r**2*rhogeq**2*w2i**2*w1r*k*vgsol - 2*w3r**2*rhogeq*w2r*w3i**2*w1r*rhogsol +& 2*w3r**2*rhogeq**2*w3i**2*k*w1r*vgsol + 2*w3r**2*rhogeq*w1i*w3i**2*rhogsol*w2i -& w3r**2*rhogeq*w2i*w1r*k*Kdrag*vdsol + w3r**2*rhogeq*w2i*w1r*k*Kdrag*vgsol - w3r**3*rhogeq**2*w1i**2*k*vgsol -& 2*w3r**3*rhogeq**2*w2r*k*w1r*vgsol + w3r**3*rhogeq*w2r**2*w1r*rhogsol + w3r**3*rhogeq*w2r*cs**2*k**2*rhogsol -& 2*w3r**3*rhogeq*w2i*k*Kdrag*vgsol - w3r**3*rhogeq**2*w1r**2*k*vgsol + w3r**3*rhogeq*w1r*cs**2*k**2*rhogsol -& w3r**3*rhogeq**2*w2r**2*k*vgsol + w3r**3*rhogeq*w1i*k*Kdrag*vdsol - 2*w3r**3*rhogeq**2*k*w2i*w1i*vgsol +& w3r**3*rhogeq*w2r*w1r**2*rhogsol + w3r**3*rhogeq*w1i**2*w2r*rhogsol + w3r**3*rhogeq*w2i**2*w1r*rhogsol -& 2*w3r**3*rhogeq*w1i*k*Kdrag*vgsol + w3r**3*Kdrag*rhogsol*w1i*w2r + w3r**2*rhogeq**2*w2r**2*k*w1r*vgsol -& 2*w3r**2*rhogeq*w3i**2*cs**2*k**2*rhogsol + w3r**3*Kdrag**2*k*vdsol + w3i**2*rhogeq**2*cs**2*k**3*w2r*vgsol -& w3r**3*Kdrag**2*k*vgsol + w3i**2*rhogeq*w2i*w1r*k*Kdrag*vdsol - w3i**2*Kdrag**2*k*vdsol*w2r +& w3i**2*Kdrag**2*k*vgsol*w2r - w3i**2*Kdrag*rhogsol*w2r**2*w1i - w3i**2*Kdrag*rhogsol*w1i*w2i**2 -& w3i**2*Kdrag*rhogsol*w2i*w1r**2 - w3i**2*Kdrag*rhogsol*w2i*w1i**2 + w3i**2*Kdrag*rhogsol*k**2*cs**2*w2i +& w3i**2*Kdrag*rhogsol*k**2*cs**2*w1i - Kdrag*k*rhogeq*vgsol*w3i*w1r*w2r**2 - Kdrag*k*rhogeq*vgsol*w3i*w2r*w1r**2& - Kdrag*k*rhogeq*vgsol*w3i*w2r*w1i**2 - Kdrag*k*rhogeq*vgsol*w3i*w1r*w2i**2 +& Kdrag*rhogsol*k**2*cs**2*w3i*w2r*w1r - Kdrag*rhogsol*k**2*cs**2*w2i*w3i*w1i + Kdrag*rhogsol*w2r**2*w1i**2*w3i +& Kdrag*rhogsol*w3i*w1r**2*w2r**2 + Kdrag*rhogsol*w1i**2*w2i**2*w3i + Kdrag*rhogsol*w2i**2*w1r**2*w3i +& Kdrag**2*k*vdsol*w2r*w1i*w3i + Kdrag**2*k*vdsol*w2i*w3i*w1r - Kdrag**2*k*vgsol*w2r*w1i*w3i -& Kdrag**2*k*vgsol*w2i*w3i*w1r - w3i**2*Kdrag**2*k*vdsol*w1r + rhogeq*cs**2*k**3*w2i*w1r*Kdrag*vdsol -& rhogeq*cs**2*k**3*w2i*w1r*Kdrag*vgsol - w3i**2*rhogeq**2*w2i**2*w1r*k*vgsol -& w3i**2*rhogeq**2*w2r**2*k*w1r*vgsol - w3i**2*rhogeq*w2i*w1r*k*Kdrag*vgsol - w3i**2*rhogeq*w2r*w1i*k*Kdrag*vgsol& - w3i**2*rhogeq**2*w2r*w1i**2*k*vgsol + w3i**2*rhogeq*w2r*w1i*k*Kdrag*vdsol -& w3i**2*rhogeq**2*w2r*w1r**2*k*vgsol + w3i**2*rhogeq**2*cs**2*k**3*w1r*vgsol +& w3i**2*rhogeq*w2i**2*w1i**2*rhogsol + w3i**2*rhogeq*w1r**2*w2i**2*rhogsol + w3i**2*rhogeq*w2r**2*w1r**2*rhogsol& - w3i**2*rhogeq*cs**4*k**4*rhogsol + w3i**2*rhogeq*w1i**2*w2r**2*rhogsol)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2& + w3i**2 - 2*w3r*w1r)/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i + w3r**2)/k/rhogeq/Kdrag vd3i = - (cs**2*k**3*rhogeq*w3i**2*Kdrag*vgsol - cs**2*k**3*rhogeq*w3i**2*Kdrag*vdsol -& 2*cs**4*k**4*rhogeq*w3r*w3i*rhogsol - 4*cs**2*k**2*rhogeq*w3r*w3i*w2r*w1r*rhogsol +& 4*cs**2*k**2*rhogeq*w3r*w3i*w2i*rhogsol*w1i - cs**2*k**3*rhogeq*w3r**2*Kdrag*vgsol +& cs**2*k**3*rhogeq*w3r**2*Kdrag*vdsol - 2*cs**2*k**3*rhogeq**2*w3i*w2r*w1r*vgsol +& 2*cs**2*k**3*rhogeq**2*w3r*w3i*w2r*vgsol + 2*cs**2*k**3*rhogeq**2*w3r*w3i*w1r*vgsol -& w3r**2*w1i*k*Kdrag**2*vgsol + w3r**2*w1i*k*Kdrag**2*vdsol + rhogeq*w3i*w3r**2*w1i*k*Kdrag*vdsol -& rhogeq**2*w3i*w3r**2*w1i**2*k*vgsol + 2*rhogeq**2*w3i**2*w3r**2*w1i*k*vgsol -& rhogeq**2*w3i*w3r**2*w1r**2*k*vgsol + rhogeq*w3i*w3r**2*w1r*cs**2*k**2*rhogsol +& 2*rhogeq*w3i**2*w3r**2*k*Kdrag*vgsol - 2*rhogeq*w3i**2*w3r**2*k*Kdrag*vdsol +& rhogeq*w3i*w2r*w3r**2*cs**2*k**2*rhogsol + rhogeq*w3i**3*w2r*cs**2*k**2*rhogsol -& rhogeq**2*w3i**3*k*w1r**2*vgsol + rhogeq*w3i**3*cs**2*k**2*rhogsol*w1r - 2*rhogeq**2*w3i*w3r**2*k*w2i*w1i*vgsol& - rhogeq**2*w3i**3*w1i**2*k*vgsol - 2*rhogeq**2*w3i**3*k*w2i*w1i*vgsol - rhogeq**2*w3i*w3r**2*w2i**2*k*vgsol +& rhogeq**2*w3i**2*w2i*w1i**2*k*vgsol - rhogeq*w3i**2*w2i*w1i*k*Kdrag*vdsol +& 2*rhogeq**2*w3i**2*w3r**2*w2i*k*vgsol - 2*rhogeq*w3i**2*w3r**2*w2i*w1r*rhogsol -& 2*rhogeq*w3i**2*w2r*w3r**2*rhogsol*w1i - rhogeq*w3i**4*w2r*w1i*rhogsol + rhogeq*w3i*w3r**2*w2i*k*Kdrag*vdsol +& w3r**2*w3i*k*Kdrag**2*vgsol - w3r**2*w3i*k*Kdrag**2*vdsol - w2i*w3i**2*k*Kdrag**2*vgsol +& w2i*w3i**2*k*Kdrag**2*vdsol - w1i*w3i**2*k*Kdrag**2*vgsol + w1i*w3i**2*k*Kdrag**2*vdsol +& rhogeq**2*w3i**2*w2i*k*vgsol*w1r**2 + rhogeq*w3i**2*w2r*w1r*k*Kdrag*vdsol + rhogeq*w3i**3*w2i*k*Kdrag*vdsol -& 2*rhogeq**2*w3i**3*w2r*k*w1r*vgsol + rhogeq**2*w3i**4*w2i*k*vgsol + 2*rhogeq**2*w3i*w3r*w2i**2*w1r*k*vgsol +& rhogeq*w3i**3*w1i**2*w2r*rhogsol - rhogeq**2*w3i**3*w2i**2*k*vgsol - 2*rhogeq*w3i*w3r*w1i**2*w2r**2*rhogsol -& 2*rhogeq*w3i*w3r*w2i**2*w1i**2*rhogsol + rhogeq*w3i*w3r**2*w1i**2*w2r*rhogsol +& rhogeq*w3i*w3r**2*w2i**2*w1r*rhogsol - rhogeq**2*w3i**3*w2r**2*k*vgsol + rhogeq*w3i*w3r**2*w2r*w1r**2*rhogsol -& 2*rhogeq**2*w3i*w3r**2*w2r*k*w1r*vgsol + rhogeq*w3i**3*w2r**2*w1r*rhogsol + rhogeq**2*w3i**4*w1i*k*vgsol +& rhogeq*w3i**3*w1i*k*Kdrag*vdsol - rhogeq*w3i**4*k*Kdrag*vdsol + rhogeq*w3i**4*k*Kdrag*vgsol -& rhogeq*w3i**4*w1r*w2i*rhogsol - Kdrag*w2r*w3r*w3i**2*w1r*rhogsol - Kdrag*w1r*w2i*w3i**3*rhogsol +& Kdrag*w2r*w3i**2*cs**2*k**2*rhogsol - Kdrag*w3i**2*k*rhogeq*w1r**2*vgsol + Kdrag*w3i**2*cs**2*k**2*rhogsol*w1r& - Kdrag*w1i**2*rhogeq*k*vgsol*w3i**2 + Kdrag*w3r*w1i*w3i**2*rhogsol*w2i - Kdrag*w3r**2*w1i**2*k*rhogeq*vgsol -& Kdrag*w3r**2*w1r**2*rhogeq*k*vgsol + Kdrag*w3r**2*w1r*cs**2*k**2*rhogsol + Kdrag*w2r*w3r**2*cs**2*k**2*rhogsol& - 2*rhogeq*w3i*w3r*w2r*w1i*k*Kdrag*vdsol + 2*rhogeq**2*w3i*w3r*w2r*w1r**2*k*vgsol +& rhogeq*w3i**3*w2i**2*w1r*rhogsol + rhogeq*w3i**3*w2r*w1r**2*rhogsol - Kdrag*w3r*w3i**2*cs**2*k**2*rhogsol -& Kdrag*w3r**3*rhogsol*k**2*cs**2 + Kdrag*w3r**3*w2i*rhogsol*w1i - Kdrag*w3r**2*w2i*w3i*w1r*rhogsol -& Kdrag*w2r*w3r**2*w3i*rhogsol*w1i - Kdrag*w2r*w1i*w3i**3*rhogsol + 2*rhogeq**2*w3i*w3r*w2r*w1i**2*k*vgsol -& Kdrag*w2r*w3r**3*w1r*rhogsol + rhogeq*w3r**3*k*Kdrag*vdsol*w2r - rhogeq**2*w3i*w3r**2*w2r**2*k*vgsol +& rhogeq*w3i*w3r**2*w2r**2*w1r*rhogsol - 2*rhogeq*w3i*w3r*w1r**2*w2i**2*rhogsol -& 2*rhogeq*w3i*w3r*w2r**2*w1r**2*rhogsol - 2*rhogeq*w3i*w3r*w2i*w1r*k*Kdrag*vdsol +& 2*rhogeq*w3i*w3r*w2i*w1r*k*Kdrag*vgsol + 2*rhogeq**2*w3i*w3r*w2r**2*k*w1r*vgsol +& 2*rhogeq*w3i*w3r*w2r*w1i*k*Kdrag*vgsol + rhogeq**2*w3r**4*k*vgsol*w2i - rhogeq*w3r**4*rhogsol*w1i*w2r -& rhogeq*w3r**4*rhogsol*w2i*w1r + rhogeq*w3r**3*rhogsol*w2r**2*w1i + rhogeq*w3r**3*rhogsol*w1i*w2i**2 +& rhogeq*w3r**3*rhogsol*w2i*w1r**2 + rhogeq*w3r**3*rhogsol*w2i*w1i**2 + rhogeq*w3r*rhogsol*w2r**2*w1i*w3i**2 -& rhogeq*w3r**4*k*Kdrag*vdsol - rhogeq*w3r**2*k*Kdrag*vdsol*w2r*w1r + rhogeq*w3r**2*k*Kdrag*vdsol*w2i*w1i +& rhogeq*w3r*k*Kdrag*vdsol*w2r*w3i**2 + rhogeq*w3r*k*Kdrag*vdsol*w3i**2*w1r -& rhogeq*w3r*rhogsol*k**2*cs**2*w3i**2*w1i - rhogeq*w3r*rhogsol*k**2*cs**2*w3i**2*w2i -& rhogeq**2*w3r**2*k*vgsol*w2i*w1r**2 - rhogeq**2*w3r**2*k*vgsol*w2i*w1i**2 + rhogeq**2*w3r**4*k*vgsol*w1i +& rhogeq*w3r**4*k*Kdrag*vgsol + rhogeq*w3r*rhogsol*w3i**2*w2i*w1r**2 + rhogeq*w3r*rhogsol*w2i*w1i**2*w3i**2 +& rhogeq*w3r*rhogsol*w2i**2*w3i**2*w1i - rhogeq*w3r**3*rhogsol*k**2*cs**2*w2i -& rhogeq*w3r**3*rhogsol*k**2*cs**2*w1i + rhogeq*w3r**3*k*Kdrag*vdsol*w1r + w3r**2*w2i*k*Kdrag**2*vdsol -& w3r**2*w2i*k*Kdrag**2*vgsol - w3i**3*k*Kdrag**2*vdsol + w3i**3*k*Kdrag**2*vgsol -& w3r*Kdrag*rhogsol*w2r**2*w1r**2 - w3r*Kdrag*rhogsol*w2r**2*w1i**2 + w3r*Kdrag*k*rhogeq*vgsol*w2r*w1i**2 +& w3r*Kdrag*k*rhogeq*vgsol*w2r*w1r**2 - w3r*rhogeq*cs**2*k**2*rhogsol*w2r**2*w1i +& w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r - w3r*Kdrag*cs**2*k**2*rhogsol*w2r*w1r -& w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r + w3r**2*Kdrag*rhogsol*w2r*w1r**2 +& 2*w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i - w3r**2*rhogeq**2*k*vgsol*w2r**2*w1i -& w3r**2*Kdrag*rhogeq*k*vgsol*w2r*w1r - w3r**2*Kdrag*rhogeq*k*vgsol*w2r**2 - w3r**2*Kdrag*rhogeq*k*vgsol*w2i**2 -& w3r**2*rhogeq**2*cs**2*k**3*vgsol*w1i + w3r*Kdrag**2*k*vgsol*w2r*w1i + w3i**2*rhogeq**2*k*vgsol*w2r**2*w1i +& rhogeq*cs**2*k**3*vdsol*Kdrag*w2r*w1r + w3i*Kdrag*k*rhogeq*vgsol*w2r**2*w1i +& w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1r + w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w1r**2 +& w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w1i**2 - 3*w3i**2*Kdrag*rhogeq*k*vgsol*w2r*w1r -& w3i**2*Kdrag*rhogeq*k*vgsol*w2i**2 - w3i*rhogeq**2*cs**2*k**3*vgsol*w2r**2 -& 2*w3i**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i - w3i**2*Kdrag*rhogeq*k*vgsol*w2r**2 -& rhogeq*cs**4*k**4*rhogsol*w2r*w1i + w3i*rhogeq*cs**4*k**4*rhogsol*w2r - w3i*Kdrag*cs**2*k**2*rhogsol*w2r*w1i -& cs**2*k**3*rhogeq*Kdrag*vgsol*w2r*w1r + w3i**2*cs**2*k**3*rhogeq**2*vgsol*w1i + w3i**2*Kdrag*rhogsol*w2r**2*w1r& + w3i*Kdrag**2*k*vdsol*w2r*w1r + w3i**2*Kdrag*rhogsol*w2r*w1r**2 + w3i**2*Kdrag*rhogsol*w2r*w1i**2 -& w3i*Kdrag**2*k*vgsol*w2r*w1r + w3r**2*Kdrag*rhogsol*w2r*w1i**2 + w3r**2*Kdrag*rhogsol*w2r**2*w1r +& rhogeq**2*cs**2*k**3*vgsol*w1i*w2r**2 + w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1r - w3r*Kdrag**2*k*vdsol*w2r*w1i -& w3r*rhogeq*cs**2*k**2*rhogsol*w2i*w1r**2 + w3r*cs**4*k**4*rhogeq*rhogsol*w1i +& w3r*Kdrag*cs**2*k**2*rhogsol*w1i*w2i - w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r - w3i*Kdrag**2*k*vdsol*w1i*w2i +& w3i*Kdrag**2*k*vgsol*w1i*w2i + w3r*Kdrag**2*k*vgsol*w2i*w1r - cs**2*k**3*rhogeq*Kdrag*vdsol*w1i*w2i -& w3i**2*Kdrag*rhogeq*k*vgsol*w1i*w2i + w3r*Kdrag*k*rhogeq*vgsol*w2i**2*w1r + w3i*rhogeq*k*Kdrag*vgsol*w2i**2*w1i& + w3i*rhogeq*k*Kdrag*vgsol*w2i*w1r**2 + w3i*rhogeq*k*Kdrag*vgsol*w1i**2*w2i -& w3i*rhogeq**2*cs**2*k**3*vgsol*w1r**2 - w3i*rhogeq**2*cs**2*k**3*vgsol*w1i**2 -& 3*w3r**2*Kdrag*k*rhogeq*vgsol*w1i*w2i + w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r -& w3i*Kdrag*cs**2*k**2*rhogsol*w2i*w1r - rhogeq*cs**4*k**4*rhogsol*w2i*w1r +& 2*w3r**2*rhogeq*cs**2*k**2*rhogsol*w2i*w1r - w3r**2*vgsol*rhogeq**2*k**3*cs**2*w2i +& w3i*rhogeq*cs**2*k**3*vdsol*Kdrag*w2i + w3i*rhogeq*cs**2*k**3*vdsol*Kdrag*w1i +& cs**2*k**3*rhogeq*Kdrag*vgsol*w1i*w2i - w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1i -& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i + w3i*rhogeq*cs**4*k**4*rhogsol*w1r +& w3i**2*vgsol*rhogeq**2*k**3*cs**2*w2i + w3i**2*vgsol*k*rhogeq**2*w2i**2*w1i -& 2*w3i**2*rhogeq*cs**2*k**2*rhogsol*w2i*w1r - w3r**2*vgsol*k*rhogeq**2*w2i**2*w1i -& w3i*rhogeq**2*cs**2*k**3*vgsol*w2i**2 - 2*w3i*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i -& w3r*Kdrag*rhogsol*w2i**2*w1r**2 + rhogeq**2*cs**2*k**3*vgsol*w1i**2*w2i + rhogeq**2*cs**2*k**3*vgsol*w2i*w1r**2& - w3r*Kdrag**2*k*vdsol*w2i*w1r + w3r*cs**4*k**4*rhogeq*rhogsol*w2i - w3r*rhogeq*cs**2*k**2*rhogsol*w1i**2*w2i -& w3r*rhogeq*cs**2*k**2*rhogsol*w2i**2*w1i - w3r*Kdrag*rhogsol*w2i**2*w1i**2 +& w3i*rhogeq*cs**2*k**2*rhogsol*w2i**2*w1r + w3i**2*Kdrag*rhogsol*w2i**2*w1r + w3r**2*Kdrag*rhogsol*w2i**2*w1r +& rhogeq**2*cs**2*k**3*vgsol*w1i*w2i**2)/Kdrag/rhogeq/k/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i +& w3r**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r) vd2r = - ( - rhogeq*w1i*rhogsol*k**2*cs**2*w3i**2*w2i + rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r**2 +& rhogeq*w1i**2*rhogsol*k**2*cs**2*w3i**2 - rhogeq*w1i*rhogsol*k**2*cs**2*w3r**2*w2i +& rhogeq**2*w1i**2*k*vgsol*w3r*w2r**2 - rhogeq**2*w1i**2*k*vgsol*w3r*w2i**2 - rhogeq**2*w2r**3*k*vgsol*w3i**2 +& rhogeq*w2r**3*w3i**2*w1r*rhogsol + 2*rhogeq**2*w1r*w2r**2*w2i**2*k*vgsol - rhogeq**2*w1r**2*w2r*k*vgsol*w2i**2& - 2*cs**2*k**3*rhogeq**2*w2r*vgsol*w2i*w1i + cs**2*k**2*rhogeq*w2r*w1r*rhogsol*w2i**2 +& cs**2*k**2*rhogeq*w2r**3*w1r*rhogsol - w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r +& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r + w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r -& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r + rhogeq**2*cs**2*k**3*w2i**2*w1r*vgsol -& rhogeq*cs**4*k**4*w2r*w1r*rhogsol - rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol +& rhogeq*cs**2*k**3*w2r*w1i*Kdrag*vgsol + rhogeq**2*cs**2*k**3*w2r*w1i**2*vgsol -& rhogeq*cs**2*k**3*w2r*w1i*Kdrag*vdsol + rhogeq**2*cs**2*k**3*w2r*w1r**2*vgsol +& rhogeq*cs**4*k**4*w2i*w1i*rhogsol + w3i*cs**4*k**4*rhogeq*rhogsol*w2i - w3i*cs**4*k**4*rhogeq*rhogsol*w1i -& w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w1r**2 - w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w1i**2 +& w3r*rhogeq**2*cs**2*k**3*w2i**2*vgsol - w3r*rhogeq*cs**4*k**4*w2r*rhogsol -& w3r*rhogeq**2*cs**2*k**3*w2r**2*vgsol - w3r*Kdrag*rhogsol*k**2*cs**2*w1i*w2r +& w3r*Kdrag*rhogsol*k**2*cs**2*w2i*w1r - w3r*Kdrag*k*rhogeq*vgsol*w2i*w1r**2 -& w3r*Kdrag*k*rhogeq*vgsol*w2i*w1i**2 + w3r*Kdrag**2*k*vdsol*w2r*w1r + w3r*Kdrag**2*k*vdsol*w2i*w1i -& w3r*Kdrag**2*k*vgsol*w2r*w1r - w3r*Kdrag**2*k*vgsol*w2i*w1i + w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1i -& w3r*Kdrag*k*rhogeq*vgsol*w1i*w2i**2 - w3r*rhogeq*cs**2*k**2*w1i**2*w2r*rhogsol -& w3r*rhogeq*cs**2*k**2*w2r*w1r**2*rhogsol - w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol -& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol + w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol +& w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol - w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol -& w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol + w3r*rhogeq*cs**4*k**4*w1r*rhogsol +& 2*w3r*rhogeq**2*cs**2*k**3*w2r*w1r*vgsol + w3r**2*rhogeq**2*cs**2*k**3*w2r*vgsol +& w3r**2*rhogeq*w2r*w1i*k*Kdrag*vgsol - w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol +& w3r**2*rhogeq*w2i**2*w1i**2*rhogsol + w3r**2*rhogeq*w1r**2*w2i**2*rhogsol - w3r**2*rhogeq*w2r**2*w1r**2*rhogsol& - w3r**2*rhogeq*w1i**2*w2r**2*rhogsol - w3r**2*Kdrag*rhogsol*w1i*w2i**2 + w3r**2*Kdrag*rhogsol*w2i*w1r**2 +& w3r**2*Kdrag*rhogsol*w2i*w1i**2 - w3r**2*Kdrag*rhogsol*w2r**2*w1i - w3r**2*rhogeq**2*w2i**2*w1r*k*vgsol -& w3r**2*rhogeq*w2i*w1r*k*Kdrag*vgsol + w3r**2*rhogeq**2*w2r**2*k*w1r*vgsol +& w3i**2*rhogeq**2*cs**2*k**3*w2r*vgsol - w3i**2*Kdrag*rhogsol*w2r**2*w1i - w3i**2*Kdrag*rhogsol*w1i*w2i**2 +& w3i**2*Kdrag*rhogsol*w2i*w1r**2 + w3i**2*Kdrag*rhogsol*w2i*w1i**2 + Kdrag*k*rhogeq*vgsol*w3i*w1r*w2r**2 +& Kdrag*k*rhogeq*vgsol*w3i*w2r*w1r**2 + Kdrag*k*rhogeq*vgsol*w3i*w2r*w1i**2 - Kdrag*k*rhogeq*vgsol*w3i*w1r*w2i**2& - Kdrag*rhogsol*k**2*cs**2*w3i*w2r*w1r - Kdrag*rhogsol*k**2*cs**2*w2i*w3i*w1i - Kdrag*rhogsol*w2r**2*w1i**2*w3i& - Kdrag*rhogsol*w3i*w1r**2*w2r**2 - Kdrag*rhogsol*w1i**2*w2i**2*w3i - Kdrag*rhogsol*w2i**2*w1r**2*w3i -& Kdrag**2*k*vdsol*w2r*w1i*w3i + Kdrag**2*k*vdsol*w2i*w3i*w1r + Kdrag**2*k*vgsol*w2r*w1i*w3i -& Kdrag**2*k*vgsol*w2i*w3i*w1r - rhogeq*cs**2*k**3*w2i*w1r*Kdrag*vdsol + rhogeq*cs**2*k**3*w2i*w1r*Kdrag*vgsol -& w3i**2*rhogeq**2*w2i**2*w1r*k*vgsol + w3i**2*rhogeq**2*w2r**2*k*w1r*vgsol - w3i**2*rhogeq*w2i*w1r*k*Kdrag*vgsol& + w3i**2*rhogeq*w2r*w1i*k*Kdrag*vgsol - w3i**2*rhogeq**2*cs**2*k**3*w1r*vgsol +& w3i**2*rhogeq*w2i**2*w1i**2*rhogsol + w3i**2*rhogeq*w1r**2*w2i**2*rhogsol - w3i**2*rhogeq*w2r**2*w1r**2*rhogsol& - w3i**2*rhogeq*w1i**2*w2r**2*rhogsol - 2*w3i*rhogeq*k*Kdrag*vdsol*w2r*w2i*w1i +& w3i*rhogeq*rhogsol*k**2*cs**2*w2i*w2r**2 + 2*rhogeq**2*w1i**2*k*vgsol*w2r*w3i*w2i -& rhogeq*w2i*w1r*k*Kdrag*vdsol*w2r**2 - 2*vgsol*Kdrag*rhogeq*k**3*cs**2*w2r*w2i -& 2*cs**2*k**2*rhogeq*rhogsol*w2i**2*w2r**2 + 2*vdsol*Kdrag*rhogeq*k**3*cs**2*w2r*w2i -& cs**2*k**2*rhogeq*rhogsol*w2r**4 - rhogeq**2*w2r*k*vgsol*w3i**2*w2i**2 - w3i*rhogeq*k*Kdrag*vdsol*w1r*w2r**2 +& w3i*rhogeq*k*Kdrag*vdsol*w2r*w2i**2 - w3i*rhogeq*rhogsol*w2i*w1r**2*w2r**2 +& 2*w3i*rhogeq*rhogsol*w2r**2*w1i*w2i**2 + rhogeq*cs**4*k**4*rhogsol*w2r**2 + w3i*rhogeq*k*Kdrag*vdsol*w2r**3 +& w3i*rhogeq*rhogsol*w2r**4*w1i - rhogeq*w1i*w3i**2*rhogsol*w2i*w2r**2 + rhogeq*w2r*w3i**2*w1r*rhogsol*w2i**2 +& 2*w3i*rhogeq**2*k*vgsol*w2i*w2r*w1r**2 - 2*w3i*cs**2*k**3*rhogeq**2*vgsol*w2r*w2i +& 2*w3i*cs**2*k**3*rhogeq**2*vgsol*w2r*w1i - rhogeq*w3i**2*cs**2*k**2*rhogsol*w2r*w1r +& 2*rhogeq**2*w2r*k*vgsol*w3i**2*w2i*w1i + 2*rhogeq*w2r*w1i*k*Kdrag*vgsol*w2i*w3i +& w3r*rhogeq*w2r**3*cs**2*k**2*rhogsol + w3r*rhogeq*w2r**3*w1r**2*rhogsol + w3r*rhogeq**2*w2r**4*k*vgsol +& w3r*rhogeq**2*k*vgsol*w2i**4 - w3r*vdsol*Kdrag*k*rhogeq*w2i**3 - w3i*rhogeq*rhogsol*w2i**3*w1r**2 +& w3i*rhogeq*rhogsol*w2i**4*w1i - w3r**2*rhogeq*rhogsol*w2i**3*w1i - w3i**2*rhogeq*rhogsol*w2i**3*w1i -& w3r*rhogeq**2*k*vgsol*w2i**2*w1r**2 + w3i*vdsol*Kdrag*k*rhogeq*w2i**2*w1r - rhogeq*rhogsol*k**2*cs**2*w2i**4 +& w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2 - rhogeq*k*Kdrag*vdsol*w2i**3*w1r +& w3i**2*cs**2*k**2*rhogeq*rhogsol*w1r**2 + w3i*cs**2*k**2*rhogeq*rhogsol*w2i**3 - w3r*rhogeq*rhogsol*w2i**4*w1r& - rhogeq**2*w1i**2*w2r**3*vgsol*k - rhogeq*w1i**2*w2i**3*rhogsol*w3i - cs**4*k**4*rhogeq*rhogsol*w2i**2 +& w2i**2*k*Kdrag**2*vdsol*w2r + w2r**2*k*Kdrag**2*vgsol*w1r - w2r**2*k*Kdrag**2*vdsol*w1r -& w2i**2*k*Kdrag**2*vgsol*w2r - w2i**2*k*Kdrag**2*vdsol*w1r + w2r**3*k*Kdrag**2*vdsol +& w3r*w2r**2*k*Kdrag**2*vgsol - w3r*w2r**2*k*Kdrag**2*vdsol + w2i**2*k*Kdrag**2*vgsol*w1r +& w3r*w2i**2*k*Kdrag**2*vgsol - w3r*w2i**2*k*Kdrag**2*vdsol - w2r**3*k*Kdrag**2*vgsol -& 2*Kdrag*w2r**3*k*rhogeq*vgsol*w3i + Kdrag*w2i**2*rhogsol*k**2*cs**2*w3i - 2*Kdrag*w2i**2*k*rhogeq*vgsol*w2r*w3i& - 2*Kdrag*w2r**3*vgsol*k*rhogeq*w1i + 2*Kdrag*w2i*vgsol*k*rhogeq*w1r*w2r**2 + 2*Kdrag*w2i**3*vgsol*k*rhogeq*w1r& - 2*Kdrag*w2i**2*w2r*vgsol*k*rhogeq*w1i + Kdrag*w2i**3*rhogsol*w3i*w1i - Kdrag*w3r*w2i**3*rhogsol*w1r +& Kdrag*w3r*w2r**3*rhogsol*w1i - Kdrag*w3r*w2r**2*rhogsol*w2i*w1r + Kdrag*w3r*w2i**2*rhogsol*w1i*w2r +& Kdrag*w2r**3*rhogsol*w3i*w1r + rhogeq*w1i*w2i**2*k*Kdrag*vdsol*w2r + rhogeq*w1i*w2r**2*rhogsol*k**2*cs**2*w2i +& Kdrag*w2r**2*rhogsol*w2i*w3i*w1i + Kdrag*w2r**2*rhogsol*k**2*cs**2*w1i + Kdrag*w2r**2*rhogsol*k**2*cs**2*w3i -& 2*rhogeq**2*w1i*w2i**2*k*vgsol*w2r*w3i - rhogeq*w1i*w3r*w2r**2*k*Kdrag*vdsol -& 2*rhogeq**2*w1i*w2r**3*k*vgsol*w3i + rhogeq*w1i*w2i**3*rhogsol*k**2*cs**2 + rhogeq*w1i*w3r*w2i**2*k*Kdrag*vdsol& + rhogeq*w1i**2*w3r*w2i**2*rhogsol*w2r + rhogeq*w1i*w2r**3*k*Kdrag*vdsol + rhogeq*w1i**2*w3r*w2r**3*rhogsol -& rhogeq*w1i**2*w2r**2*rhogsol*w2i*w3i - rhogeq**2*w1i**2*w2i**2*w2r*vgsol*k +& Kdrag*w2i**2*rhogsol*k**2*cs**2*w1i + Kdrag*w2i**2*rhogsol*w3i*w2r*w1r - Kdrag*w2r**2*rhogsol*k**2*cs**2*w2i -& Kdrag*w2i**3*rhogsol*k**2*cs**2 + 2*Kdrag*w3r*w2r**2*k*rhogeq*vgsol*w2i + 2*Kdrag*w3r*w2i**3*k*rhogeq*vgsol -& 2*w3r*w2r*Kdrag*k*rhogeq*vgsol*w2i*w1r + 2*w3r*rhogeq*w2i*k*Kdrag*vdsol*w2r*w1r -& 2*w3r*w2r*rhogeq**2*vgsol*k*w2i**2*w1r + w3r*rhogeq**2*w1r**2*k*vgsol*w2r**2 +& w3r*rhogeq*w2r*cs**2*k**2*rhogsol*w2i**2 - w3r*rhogeq*w2r**4*w1r*rhogsol + w3r*rhogeq*w2r*w1r**2*rhogsol*w2i**2& - 2*w3r*rhogeq**2*w2r**3*k*w1r*vgsol - 2*w3r*rhogeq*w2r**2*w1r*rhogsol*w2i**2 -& w3r*rhogeq*w2i*k*Kdrag*vdsol*w2r**2 + 2*w3r*rhogeq**2*w2i**2*k*vgsol*w2r**2 +& w3r**2*rhogeq*w2r*w1r*rhogsol*w2i**2 - w3r**2*rhogeq**2*w2r*vgsol*k*w2i**2 -& w3r**2*rhogeq*w2i*rhogsol*w1i*w2r**2 - w3r**2*rhogeq**2*w2r**3*vgsol*k + 2*w3r**2*rhogeq**2*w2r*vgsol*k*w2i*w1i& - w3r**2*rhogeq*rhogsol*k**2*cs**2*w2r*w1r + w3r**2*rhogeq*w2r**3*w1r*rhogsol + rhogeq**2*w1r*w2i**4*k*vgsol +& rhogeq**2*w1r*w2r**4*k*vgsol - rhogeq**2*w1r**2*w2r**3*k*vgsol)/k/rhogeq/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2& - 2*w2i*w3i + w3r**2)/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2)/Kdrag vd2i =1/k*(w3r*rhogeq*k*Kdrag*vdsol*w2r**2*w1r - w3r*rhogeq*k*Kdrag*vdsol*w2r*w2i**2 -& w3r*rhogeq*k*Kdrag*vdsol*w2r**3 - w3r*Kdrag*rhogsol*w2r**2*w1r**2 - w3r*Kdrag*rhogsol*w2r**2*w1i**2 +& w3r*rhogeq*rhogsol*w2r**4*w1i - w3r*Kdrag*k*rhogeq*vgsol*w2r*w1i**2 - w3r*Kdrag*k*rhogeq*vgsol*w2r*w1r**2 -& 2*w3r*rhogeq*cs**2*k**2*rhogsol*w2r**2*w1i - w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r -& 2*w3r*rhogeq**2*k*vgsol*w2r*w2i*w1i**2 - 2*w3r*rhogeq**2*k*vgsol*w2r*w2i*w1r**2 +& 2*w3r*rhogeq**2*k*vgsol*w2r**2*w2i*w1r - w3r*rhogeq*cs**2*k**2*rhogsol*w2i*w2r**2 +& w3r*Kdrag*cs**2*k**2*rhogsol*w2r*w1r - w3r*Kdrag*cs**2*k**2*rhogsol*w2r**2 + w3r*Kdrag*rhogsol*w2r**2*w1i*w2i -& 2*w3r*cs**2*k**3*rhogeq**2*vgsol*w2i*w2r + w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r +& 4*w3r*rhogeq*cs**2*k**2*rhogsol*w2r*w2i*w1r + w3r*Kdrag*rhogsol*w2r*w1r*w2i**2 +& 2*w3r*rhogeq*rhogsol*w2i**2*w2r**2*w1i + w3r**2*Kdrag*rhogsol*w2r*w1r**2 +& 2*w3r**2*rhogeq*rhogsol*w2r*w2i*w1i**2 - w3r*rhogeq*rhogsol*w1i**2*w2r**2*w2i -& w3r*rhogeq*rhogsol*w2i*w1r**2*w2r**2 - 2*w3r*w2r*Kdrag*rhogeq*k*vgsol*w2i*w1i +& w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i + w3r**2*rhogeq**2*k*vgsol*w2i*w2r**2 +& w3r**2*rhogeq**2*k*vgsol*w2r**2*w1i - w3r**2*Kdrag*rhogeq*k*vgsol*w2r*w1r + w3r**2*Kdrag*rhogeq*k*vgsol*w2r**2& - w3r**2*rhogeq*rhogsol*w2r*w2i**2*w1i - w3r**2*rhogeq*rhogsol*w2r**2*w2i*w1r +& w3r**2*Kdrag*rhogeq*k*vgsol*w2i**2 - w3r**2*rhogeq**2*cs**2*k**3*vgsol*w1i +& 2*w3r**2*rhogeq*rhogsol*w2r*w2i*w1r**2 - 2*w3r**2*rhogeq**2*k*vgsol*w2r*w2i*w1r - w3r*Kdrag**2*k*vgsol*w2r*w1i& + rhogeq*k*Kdrag*vdsol*w2r**4 - w3i*rhogeq*rhogsol*w2r**3*w1i**2 + Kdrag**2*k*vdsol*w2i*w2r**2 -& 2*vgsol*k*rhogeq**2*w2i**2*w2r**2*w1i + vgsol*k*rhogeq**2*w2i*w1r**2*w2r**2 -& 2*vgsol*rhogeq**2*k**3*cs**2*w2r*w2i*w1r - rhogeq*cs**2*k**2*rhogsol*w2r**2*w2i*w1r +& rhogeq*cs**2*k**2*rhogsol*w2r**3*w1i + rhogeq*cs**2*k**2*rhogsol*w2r*w2i**2*w1i +& w3i*rhogeq**2*k*vgsol*w2r**2*w1r**2 - w3i*rhogeq**2*k*vgsol*w2r**4 + w3i**2*rhogeq**2*k*vgsol*w2i*w2r**2 -& 2*w3i**2*rhogeq**2*k*vgsol*w2r*w2i*w1r + w3i**2*rhogeq**2*k*vgsol*w2r**2*w1i -& rhogeq*cs**2*k**3*vdsol*Kdrag*w2r**2 + rhogeq*cs**2*k**3*vdsol*Kdrag*w2r*w1r - Kdrag**2*k*vdsol*w2r**2*w1i +& 3*w3i*Kdrag*k*rhogeq*vgsol*w2r**2*w1i - 2*w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1r +& w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w2i**2 + w3i*cs**2*k**2*rhogeq*rhogsol*w2r**3 +& w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w1r**2 - 4*w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w2i*w1i +& w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w1i**2 + w3i*rhogeq**2*k*vgsol*w2r**2*w1i**2 -& 2*w3i*rhogeq**2*k*vgsol*w2i**2*w2r**2 - w3i**2*Kdrag*rhogeq*k*vgsol*w2r*w1r +& w3i**2*Kdrag*rhogeq*k*vgsol*w2i**2 + Kdrag**2*k*vgsol*w2r**2*w1i + w3i*rhogeq**2*cs**2*k**3*vgsol*w2r**2 +& Kdrag*k*rhogeq*vgsol*w2r**2*w1r**2 + Kdrag*k*rhogeq*vgsol*w2r**2*w1i**2 +& w3i**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i - Kdrag**2*k*vgsol*w2i*w2r**2 + 2*rhogeq*cs**4*k**4*rhogsol*w2i*w2r +& 2*w3i*rhogeq*rhogsol*w2i**2*w2r**2*w1r + w3i**2*Kdrag*rhogeq*k*vgsol*w2r**2 + w3i*rhogeq*rhogsol*w2r**4*w1r -& w3i*rhogeq*rhogsol*w2r**3*w1r**2 - rhogeq*cs**4*k**4*rhogsol*w2r*w1i - w3i*rhogeq*cs**4*k**4*rhogsol*w2r -& rhogeq*k*Kdrag*vdsol*w2r**2*w1i*w2i + Kdrag*cs**2*k**2*rhogsol*w2r**3 + 2*rhogeq*k*Kdrag*vdsol*w2i**2*w2r**2 -& rhogeq*k*Kdrag*vdsol*w2r**3*w1r + Kdrag*cs**2*k**2*rhogsol*w2r*w2i**2 - rhogeq*k*Kdrag*vdsol*w2r*w1r*w2i**2 -& Kdrag*cs**2*k**2*rhogsol*w2r**2*w1r + w3i*Kdrag*rhogsol*w2r**2*w2i*w1r - w3i*Kdrag*rhogsol*w2r*w2i**2*w1i -& w3i*Kdrag*rhogsol*w2r**3*w1i - w3i*Kdrag*cs**2*k**2*rhogsol*w2r*w1i - w3i**2*rhogeq*rhogsol*w2r**2*w2i*w1r -& w3i**2*rhogeq*rhogsol*w2r*w2i**2*w1i - w3i*rhogeq*rhogsol*w2r*w2i**2*w1r**2 +& 2*w3i**2*rhogeq*rhogsol*w2r*w2i*w1r**2 - w3i**2*rhogeq*rhogsol*w2r**3*w1i +& 2*w3i**2*rhogeq*rhogsol*w2r*w2i*w1i**2 - w3i*rhogeq*rhogsol*w2r*w2i**2*w1i**2 -& cs**2*k**3*rhogeq*Kdrag*vgsol*w2r*w1r + cs**2*k**3*rhogeq*Kdrag*vgsol*w2r**2 -& w3i**2*cs**2*k**3*rhogeq**2*vgsol*w1i - w3i**2*Kdrag*rhogsol*w2r**2*w1r + w3i*Kdrag**2*k*vdsol*w2r*w1r +& w3i**2*Kdrag*rhogsol*w2r*w1r**2 + w3i**2*Kdrag*rhogsol*w2r*w1i**2 - w3i*Kdrag**2*k*vdsol*w2r**2 +& w3i*Kdrag**2*k*vgsol*w2r**2 - w3i*Kdrag**2*k*vgsol*w2r*w1r + 2*w3i*rhogeq*k*Kdrag*vdsol*w2r*w2i*w1r -& w3i*rhogeq*k*Kdrag*vdsol*w2r**2*w1i - w3i*rhogeq*k*Kdrag*vdsol*w2i*w2r**2 + w3r**2*Kdrag*rhogsol*w2r*w1i**2 -& w3r**2*Kdrag*rhogsol*w2r**2*w1r + w3r*Kdrag*rhogsol*w2r**3*w1r - w3r**2*rhogeq*rhogsol*w2r**3*w1i +& Kdrag**2*k*vdsol*w2i**3 - Kdrag**2*k*vgsol*w2i**3 - Kdrag*rhogeq*k*vgsol*w2r**4 +& rhogeq**2*cs**2*k**3*vgsol*w1i*w2r**2 - 2*Kdrag*rhogeq*k*vgsol*w2i**2*w2r**2 -& 2*Kdrag*rhogeq*k*vgsol*w2r*w1r*w2i*w3i + 2*rhogeq**2*k*vgsol*w2r**2*w1i*w2i*w3i +& 2*w3r*rhogeq*k*Kdrag*vdsol*w2r*w2i*w1i + w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1r + w3r*Kdrag**2*k*vdsol*w2r*w1i -& rhogeq**2*k*vgsol*w2r**4*w1i - w3r*rhogeq*k*Kdrag*vdsol*w2i**2*w1r - w3r*rhogeq*cs**2*k**2*rhogsol*w2i*w1r**2 -& w3r*rhogeq*cs**2*k**2*rhogsol*w2i**3 + 2*w3r*vgsol*k**3*cs**2*rhogeq**2*w2i*w1r +& w3r*cs**4*k**4*rhogeq*rhogsol*w1i + w3r*Kdrag*cs**2*k**2*rhogsol*w1i*w2i -& w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r + w3i*Kdrag**2*k*vdsol*w1i*w2i - w3i*Kdrag**2*k*vdsol*w2i**2 +& rhogeq*k*Kdrag*vdsol*w2i**4 - w3i*Kdrag**2*k*vgsol*w1i*w2i + w3i*Kdrag**2*k*vgsol*w2i**2 +& w3r*Kdrag**2*k*vgsol*w2i*w1r + 2*w3r*rhogeq**2*k*vgsol*w2i**3*w1r - cs**2*k**3*rhogeq*Kdrag*vdsol*w1i*w2i -& w3i**2*Kdrag*rhogeq*k*vgsol*w1i*w2i - Kdrag*rhogeq*k*vgsol*w2i**4 + Kdrag*rhogeq*k*vgsol*w2i**2*w1r**2 +& Kdrag*rhogeq*k*vgsol*w2i**2*w1i**2 + 3*w3r*Kdrag*k*rhogeq*vgsol*w2i**2*w1r +& w3i*rhogeq*k*Kdrag*vgsol*w2i**2*w1i - w3i*rhogeq*k*Kdrag*vgsol*w2i*w1r**2 - w3i*rhogeq*k*Kdrag*vgsol*w1i**2*w2i& - w3i*rhogeq*k*Kdrag*vdsol*w2i**3 + w3i*rhogeq*k*Kdrag*vdsol*w2i**2*w1i - w3i*rhogeq**2*k*vgsol*w2i**2*w1i**2 +& 2*w3i*rhogeq**2*k*vgsol*w2i**3*w1i - w3i*rhogeq**2*k*vgsol*w2i**4 - w3i*rhogeq**2*cs**2*k**3*vgsol*w1r**2 -& w3i*rhogeq**2*cs**2*k**3*vgsol*w1i**2 - w3r**2*Kdrag*k*rhogeq*vgsol*w1i*w2i +& w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r - rhogeq*cs**2*k**2*rhogsol*w2i**3*w1r +& w3i*Kdrag*cs**2*k**2*rhogsol*w2i*w1r - rhogeq*cs**4*k**4*rhogsol*w2i*w1r -& w3r**2*rhogeq*cs**2*k**2*rhogsol*w2i*w1r + w3r**2*vgsol*rhogeq**2*k**3*cs**2*w2i -& w3i*rhogeq*cs**2*k**3*vdsol*Kdrag*w2i + w3i*rhogeq*cs**2*k**3*vdsol*Kdrag*w1i +& cs**2*k**3*rhogeq*Kdrag*vgsol*w1i*w2i - w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1i +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i + w3i*rhogeq*cs**4*k**4*rhogsol*w1r +& w3i**2*vgsol*rhogeq**2*k**3*cs**2*w2i - w3i**2*vgsol*k*rhogeq**2*w2i**2*w1i -& w3i**2*rhogeq*cs**2*k**2*rhogsol*w2i*w1r + w3i**2*vgsol*k*rhogeq**2*w2i**3 -& w3r**2*vgsol*k*rhogeq**2*w2i**2*w1i + w3r**2*vgsol*k*rhogeq**2*w2i**3 - w3i*rhogeq**2*cs**2*k**3*vgsol*w2i**2 +& 2*w3i*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i - cs**2*k**3*rhogeq*Kdrag*vgsol*w2i**2 +& cs**2*k**3*rhogeq*Kdrag*vdsol*w2i**2 - w3i*rhogeq**2*k*vgsol*w2i**2*w1r**2 - rhogeq*k*Kdrag*vdsol*w2i**3*w1i -& Kdrag*cs**2*k**2*rhogsol*w2i**2*w1r + w3r*Kdrag*rhogsol*w2i**3*w1i - w3r*Kdrag*rhogsol*w2i**2*w1r**2 +& w3i*Kdrag*rhogsol*w2i**3*w1r + rhogeq**2*cs**2*k**3*vgsol*w1i**2*w2i + rhogeq**2*cs**2*k**3*vgsol*w2i*w1r**2 -& w3r*Kdrag**2*k*vdsol*w2i*w1r + vgsol*k*rhogeq**2*w1i**2*w2r**2*w2i - w3r*cs**4*k**4*rhogeq*rhogsol*w2i -& w3r*rhogeq*cs**2*k**2*rhogsol*w1i**2*w2i + 2*w3r*rhogeq*cs**2*k**2*rhogsol*w2i**2*w1i -& w3r*Kdrag*rhogsol*w2i**2*w1i**2 + 2*w3i*rhogeq*cs**2*k**2*rhogsol*w2i**2*w1r - w3r**2*rhogeq*rhogsol*w2i**3*w1r& - w3i**2*rhogeq*rhogsol*w2i**3*w1r - w3i**2*Kdrag*rhogsol*w2i**2*w1r - w3r**2*Kdrag*rhogsol*w2i**2*w1r -& rhogeq**2*k*vgsol*w2i**4*w1i + rhogeq**2*k*vgsol*w2i**3*w1r**2 - w3r*rhogeq*rhogsol*w2i**3*w1i**2 +& w3r*rhogeq*rhogsol*w2i**4*w1i - w3r*rhogeq*rhogsol*w2i**3*w1r**2 + rhogeq**2*k*vgsol*w2i**3*w1i**2 -& Kdrag**2*k*vdsol*w2i**2*w1i + w3i*rhogeq*rhogsol*w2i**4*w1r + Kdrag**2*k*vgsol*w2i**2*w1i -& w3r*Kdrag*cs**2*k**2*rhogsol*w2i**2 - rhogeq**2*cs**2*k**3*vgsol*w1i*w2i**2)/(w2r**2 - 2*w3r*w2r + w2i**2 +& w3i**2 - 2*w2i*w3i + w3r**2)/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2)/rhogeq/Kdrag vd1r =(w3r*Kdrag*rhogsol*w1i**3*w2r - w3r*w1r**2*k*Kdrag**2*vgsol - w3r*w1r**3*rhogeq*rhogsol*w2i**2 -& w3r*rhogeq*w2r**2*w1r**3*rhogsol - w3r*w1i**2*k*Kdrag**2*vgsol + w3r*rhogeq*w1i**4*w2r*rhogsol +& w3r*Kdrag*rhogsol*w1i*w2r*w1r**2 - 2*w3r*rhogeq*w1i*k*Kdrag*vdsol*w2r*w1r +& 2*w3r*rhogeq*w2r*w1i*k*Kdrag*vgsol*w1r - w3r*rhogeq*w1i**2*w2r**2*rhogsol*w1r + w3r*w1i**2*k*Kdrag**2*vdsol +& w3r*rhogeq*w2i*w1r**2*k*Kdrag*vdsol + w3r*w1r**2*k*Kdrag**2*vdsol - w3r*Kdrag*rhogsol*w2i*w1r**3 +& w3r**2*w1i**2*rhogeq**2*k*w1r*vgsol + w3r**2*w1r**3*rhogeq**2*k*vgsol - 2*w3r**2*w2i*w1i*rhogeq**2*k*w1r*vgsol& - w3r**2*rhogeq*rhogsol*k**2*cs**2*w2r**2 - w3r**2*rhogeq*rhogsol*k**2*cs**2*w2i**2 -& w3r**2*rhogeq*w2r*w1r*rhogsol*w1i**2 + w3r**2*w2i*w1i*rhogeq*w1r**2*rhogsol +& 2*w3r*w2r*w1i**2*rhogeq**2*k*w1r*vgsol + 2*w3r*w2r*w1r**3*rhogeq**2*k*vgsol -& w3r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w1r - w3r*cs**2*k**2*rhogeq*rhogsol*w1r**3 -& 2*w3r*w1i**2*rhogeq**2*w1r**2*k*vgsol + w3r*w1i**3*rhogeq*k*Kdrag*vdsol - 2*w3r*w1i**3*Kdrag*k*rhogeq*vgsol -& w3r*w1i**2*rhogeq*w1r*rhogsol*w2i**2 + w3r*w1r**2*rhogeq*w1i*k*Kdrag*vdsol - w3r*w1r*Kdrag*rhogsol*w2i*w1i**2 -& w3r*w2i*w1i**2*rhogeq*k*Kdrag*vdsol - 2*w3r*w1r**2*Kdrag*k*rhogeq*vgsol*w1i +& 2*w3r*rhogeq*w1i**2*w2r*rhogsol*w1r**2 + rhogeq*w1i*rhogsol*k**2*cs**2*w3i**2*w2i +& rhogeq*w1i*rhogsol*k**2*cs**2*w3r**2*w2i + rhogeq**2*w1i**2*k*vgsol*w3r*w2r**2 +& rhogeq**2*w1i**2*k*vgsol*w3r*w2i**2 - w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r +& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r + w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r -& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r - rhogeq**2*cs**2*k**3*w2i**2*w1r*vgsol +& rhogeq*cs**4*k**4*w2r*w1r*rhogsol - rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol -& rhogeq*cs**2*k**3*w2r*w1i*Kdrag*vgsol - rhogeq**2*cs**2*k**3*w2r*w1i**2*vgsol +& rhogeq*cs**2*k**3*w2r*w1i*Kdrag*vdsol + rhogeq**2*cs**2*k**3*w2r*w1r**2*vgsol -& rhogeq*cs**4*k**4*w2i*w1i*rhogsol + w3i*cs**4*k**4*rhogeq*rhogsol*w2i - w3i*cs**4*k**4*rhogeq*rhogsol*w1i +& w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1i + w3i*cs**2*k**2*rhogeq*rhogsol*w1i*w2i**2 +& w3r*rhogeq**2*cs**2*k**3*w2i**2*vgsol - w3r*rhogeq*cs**4*k**4*w2r*rhogsol +& w3r*rhogeq**2*cs**2*k**3*w2r**2*vgsol - w3r*Kdrag*rhogsol*k**2*cs**2*w1i*w2r +& w3r*Kdrag*rhogsol*k**2*cs**2*w2i*w1r - w3r*Kdrag*k*rhogeq*vgsol*w2i*w1r**2 +& w3r*Kdrag*k*rhogeq*vgsol*w2i*w1i**2 - w3r*Kdrag**2*k*vdsol*w2r*w1r - w3r*Kdrag**2*k*vdsol*w2i*w1i +& w3r*Kdrag**2*k*vgsol*w2r*w1r + w3r*Kdrag**2*k*vgsol*w2i*w1i + w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1i +& w3r*Kdrag*k*rhogeq*vgsol*w1i*w2i**2 + w3r*rhogeq*cs**2*k**2*w2r**2*w1r*rhogsol +& w3r*rhogeq*w2i**2*w1r*cs**2*k**2*rhogsol - w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol -& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol + w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol +& w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol - w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol +& w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol + w3r*rhogeq*cs**4*k**4*w1r*rhogsol -& 2*w3r*rhogeq**2*cs**2*k**3*w2r*w1r*vgsol + w3r**2*rhogeq**2*cs**2*k**3*w2r*vgsol +& w3r**2*rhogeq*w2r*w1i*k*Kdrag*vgsol + w3r**2*rhogeq**2*w2r*w1i**2*k*vgsol - w3r**2*rhogeq**2*w2r*w1r**2*k*vgsol& - w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol - w3r**2*rhogeq*w2i**2*w1i**2*rhogsol +& w3r**2*rhogeq*w1r**2*w2i**2*rhogsol + w3r**2*rhogeq*w2r**2*w1r**2*rhogsol - w3r**2*rhogeq*w1i**2*w2r**2*rhogsol& - w3r**2*Kdrag*rhogsol*w1i*w2i**2 + w3r**2*Kdrag*rhogsol*w2i*w1r**2 + w3r**2*Kdrag*rhogsol*w2i*w1i**2 -& w3r**2*Kdrag*rhogsol*w2r**2*w1i - w3r**2*rhogeq*w2i*w1r*k*Kdrag*vgsol + w3i**2*rhogeq**2*cs**2*k**3*w2r*vgsol -& w3i**2*Kdrag*rhogsol*w2r**2*w1i - w3i**2*Kdrag*rhogsol*w1i*w2i**2 + w3i**2*Kdrag*rhogsol*w2i*w1r**2 +& w3i**2*Kdrag*rhogsol*w2i*w1i**2 - Kdrag*k*rhogeq*vgsol*w3i*w1r*w2r**2 - Kdrag*k*rhogeq*vgsol*w3i*w2r*w1r**2 +& Kdrag*k*rhogeq*vgsol*w3i*w2r*w1i**2 - Kdrag*k*rhogeq*vgsol*w3i*w1r*w2i**2 +& Kdrag*rhogsol*k**2*cs**2*w3i*w2r*w1r + Kdrag*rhogsol*k**2*cs**2*w2i*w3i*w1i + Kdrag*rhogsol*w2r**2*w1i**2*w3i +& Kdrag*rhogsol*w3i*w1r**2*w2r**2 + Kdrag*rhogsol*w1i**2*w2i**2*w3i + Kdrag*rhogsol*w2i**2*w1r**2*w3i -& Kdrag**2*k*vdsol*w2r*w1i*w3i + Kdrag**2*k*vdsol*w2i*w3i*w1r + Kdrag**2*k*vgsol*w2r*w1i*w3i -& Kdrag**2*k*vgsol*w2i*w3i*w1r + rhogeq*cs**2*k**3*w2i*w1r*Kdrag*vdsol - rhogeq*cs**2*k**3*w2i*w1r*Kdrag*vgsol -& w1r**3*vdsol*Kdrag*k*rhogeq*w2i - Kdrag*rhogsol*k**2*cs**2*w2i*w1r**2 + 2*Kdrag*k*rhogeq*vgsol*w2i*w1r**3 -& w3i**2*rhogeq*w2i*w1r*k*Kdrag*vgsol + w3i**2*rhogeq*w2r*w1i*k*Kdrag*vgsol + w3i**2*rhogeq**2*w2r*w1i**2*k*vgsol& - w3i**2*rhogeq**2*w2r*w1r**2*k*vgsol - w3i**2*rhogeq**2*cs**2*k**3*w1r*vgsol -& w3i**2*rhogeq*w2i**2*w1i**2*rhogsol + w3i**2*rhogeq*w1r**2*w2i**2*rhogsol + w3i**2*rhogeq*w2r**2*w1r**2*rhogsol& - w3i**2*rhogeq*w1i**2*w2r**2*rhogsol + rhogeq*w1i**3*w2r**2*rhogsol*w3i - 2*rhogeq*w2r*w1i**3*k*Kdrag*vgsol +& rhogeq*w2r**2*w1r**2*rhogsol*w3i*w1i + rhogeq**2*w2r**2*k*w1r*vgsol*w1i**2 + Kdrag**2*k*vdsol*w2r*w1r**2 -& Kdrag**2*k*vgsol*w2r*w1r**2 - rhogeq*cs**2*k**2*w2r*w1r**3*rhogsol + Kdrag**2*k*vgsol*w1r**3 -& Kdrag**2*k*vdsol*w1r**3 + rhogeq**2*w2r**2*k*w1r**3*vgsol - 2*rhogeq**2*w2r**2*k*w1r*vgsol*w3i*w1i +& rhogeq*w3i**2*cs**2*k**2*rhogsol*w2r*w1r - 2*rhogeq*w2r*w1i*k*Kdrag*vgsol*w1r**2 -& rhogeq*cs**2*k**2*w1i**2*w2r*rhogsol*w1r - w3r*rhogeq**2*k*vgsol*w2i**2*w1r**2 - rhogeq**2*w2r*w1r**4*k*vgsol -& rhogeq*w2r*w3i**2*w1r**3*rhogsol + w3i*rhogeq*k*Kdrag*vdsol*w2r*w1r**2 + rhogeq*w2r*w1i*k*Kdrag*vdsol*w1r**2 -& Kdrag*rhogsol*w3i*w2r*w1r**3 - 2*rhogeq**2*w2r*w1i**2*k*vgsol*w1r**2 - rhogeq*w3i**2*cs**2*k**2*rhogsol*w2r**2& - rhogeq*w3i**2*cs**2*k**2*rhogsol*w2i**2 - Kdrag*rhogsol*w3i*w2r*w1r*w1i**2 -& w3i*rhogeq*k*Kdrag*vdsol*w2r*w1i**2 + Kdrag**2*k*vdsol*w2r*w1i**2 - rhogeq*w2r*w3i**2*w1r*rhogsol*w1i**2 -& Kdrag**2*k*vgsol*w2r*w1i**2 + rhogeq*w2r*w1i**3*k*Kdrag*vdsol - rhogeq**2*w2r*w1i**4*k*vgsol +& cs**2*k**2*rhogeq*rhogsol*w1r**4 + w1r**2*w3i*rhogeq*rhogsol*w2i**2*w1i +& 2*rhogeq*w1i**2*rhogsol*k**2*cs**2*w1r**2 + rhogeq*w1i**4*rhogsol*k**2*cs**2 -& w2i*w1i**3*rhogeq*rhogsol*k**2*cs**2 - w1r**4*w3i*rhogeq*rhogsol*w2i - w1r**2*rhogeq*cs**4*k**4*rhogsol +& w2i*w1i**3*rhogeq*rhogsol*w3i**2 - w3i*cs**2*k**2*rhogeq*rhogsol*w1i*w1r**2 +& w2i*w1i*rhogeq*w1r**2*rhogsol*w3i**2 - w3i*cs**2*k**2*rhogeq*rhogsol*w1i**3 + w1i**3*w3i*rhogeq*rhogsol*w2i**2& - w2i*w1i*cs**2*k**2*rhogeq*rhogsol*w1r**2 + w1i**2*rhogeq**2*k*vgsol*w2i**2*w1r -& 2*w2i*w1i*rhogeq**2*k*w1r*vgsol*w3i**2 - 2*vgsol*k*rhogeq*Kdrag*w2i*w1r*w3i*w1i +& w1r**3*rhogeq**2*k*vgsol*w2i**2 - w1i**4*w3i*rhogeq*rhogsol*w2i + 2*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w1r +& rhogeq*cs**4*k**4*rhogsol*w1i**2 - 2*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w1r + Kdrag*rhogsol*k**2*cs**2*w1i**3 -& Kdrag**2*k*vdsol*w1r*w1i**2 + rhogeq**2*w3i**2*k*w1r**3*vgsol + Kdrag**2*k*vgsol*w1r*w1i**2 -& Kdrag*rhogsol*w1i**3*w3i*w2i + 2*w1r*Kdrag*k*rhogeq*vgsol*w2i*w1i**2 + 2*w2i*w1i*rhogeq**2*cs**2*k**3*w1r*vgsol& - Kdrag*rhogsol*w3i*w1r**2*w2i*w1i + rhogeq**2*w3i**2*k*w1r*vgsol*w1i**2 +& 2*w3i*rhogeq*k*Kdrag*vdsol*w1r*w2i*w1i - Kdrag*rhogsol*k**2*cs**2*w3i*w1r**2 - w3i*rhogeq*k*Kdrag*vdsol*w1r**3& + 2*rhogeq**2*cs**2*k**3*w1r*vgsol*w3i*w1i + 2*Kdrag*k*rhogeq*vgsol*w3i*w1r**3 -& Kdrag*rhogsol*k**2*cs**2*w1i**2*w2i - Kdrag*rhogsol*k**2*cs**2*w3i*w1i**2 + Kdrag*rhogsol*k**2*cs**2*w1i*w1r**2& - 2*rhogeq**2*k*vgsol*w2i**2*w1r*w3i*w1i - 2*w1r**2*w3i*rhogeq*rhogsol*w1i**2*w2i -& w1r*w1i**2*vdsol*Kdrag*k*rhogeq*w2i - 2*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i*w3i -& w3i*rhogeq*k*Kdrag*vdsol*w1r*w1i**2 + 2*Kdrag*k*rhogeq*vgsol*w3i*w1r*w1i**2 - w3r**2*rhogeq*w2r*w1r**3*rhogsol& - w3r*w1i**4*rhogeq**2*k*vgsol + w3r*rhogeq*w2r*w1r**4*rhogsol - w3r*rhogeq**2*w1r**4*k*vgsol +& 2*w1i**2*rhogeq**2*k*w1r*vgsol*w2i*w3i + w3r**2*w2i*w1i**3*rhogeq*rhogsol - w3r*rhogeq**2*w1r**2*k*vgsol*w2r**2& + 2*w1r**3*rhogeq**2*k*vgsol*w2i*w3i + w3r**2*rhogeq*rhogsol*k**2*cs**2*w2r*w1r)/(w1i**2 - 2*w3i*w1i + w3r**2 +& w1r**2 + w3i**2 - 2*w3r*w1r)/Kdrag/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2)/rhogeq/k vd1i = - (w3r**2*rhogeq*rhogsol*w1i**3*w2r + w3r*Kdrag*rhogsol*k**2*cs**2*w1i**2 +& w3r*rhogeq*rhogsol*k**2*cs**2*w1i**3 - 2*w3r*rhogeq*rhogsol*w2i*w1i**2*w1r**2 +& w3r*rhogeq*rhogsol*w1i*w2i**2*w1r**2 + w3r*rhogeq*rhogsol*w2r**2*w1i*w1r**2 -& 2*w3r*w2r*rhogeq**2*k*vgsol*w1i*w1r**2 - 2*w3r*w2r*rhogeq**2*cs**2*k**3*vgsol*w1i -& 2*w3r*w2r*rhogeq**2*k*vgsol*w1i**3 - w3r*Kdrag*rhogsol*w2r*w1i**2*w1r +& w3r*rhogeq*rhogsol*k**2*cs**2*w1i*w1r**2 + w3r*rhogeq*k*Kdrag*vdsol*w2r*w1i**2 +& w3r*Kdrag*rhogsol*k**2*cs**2*w1r**2 - w3r*Kdrag*w2i*rhogsol*w1i*w1r**2 - w3r*rhogeq*k*Kdrag*vdsol*w2r*w1r**2 +& w3r*rhogeq*k*Kdrag*vdsol*w1r**3 - 2*w3r*rhogeq*k*Kdrag*vdsol*w1r*w2i*w1i + w3r*rhogeq*k*Kdrag*vdsol*w1r*w1i**2& + 2*w3r*vgsol*k*rhogeq**2*w2i**2*w1i*w1r + 2*w3r*Kdrag*k*rhogeq*vgsol*w1i*w2i*w1r +& 2*w3r*rhogeq**2*cs**2*k**3*vgsol*w1i*w1r - 2*w3r**2*rhogeq*rhogsol*w1i*w2r**2*w1r -& w3r**2*rhogeq**2*k*vgsol*w1i*w1r**2 - 2*w3r**2*rhogeq*rhogsol*w2i**2*w1r*w1i +& 2*w3r**2*rhogeq**2*k*vgsol*w1i*w2r*w1r + w3r**2*rhogeq*rhogsol*w1i*w2r*w1r**2 +& rhogeq**2*w3i**2*w2i*w1i**2*k*vgsol - rhogeq**2*w3i**2*w2i*k*vgsol*w1r**2 - Kdrag*w3i**2*k*rhogeq*w1r**2*vgsol& - Kdrag*w1i**2*rhogeq*k*vgsol*w3i**2 - Kdrag*w3r**2*w1i**2*k*rhogeq*vgsol - Kdrag*w3r**2*w1r**2*rhogeq*k*vgsol& - rhogeq**2*w3r**2*k*vgsol*w2i*w1r**2 + rhogeq**2*w3r**2*k*vgsol*w2i*w1i**2 + w3r*Kdrag*rhogsol*w2r**2*w1r**2 +& w3r*Kdrag*rhogsol*w2r**2*w1i**2 - 3*w3r*Kdrag*k*rhogeq*vgsol*w2r*w1i**2 - w3r*Kdrag*k*rhogeq*vgsol*w2r*w1r**2 +& w3r*rhogeq*cs**2*k**2*rhogsol*w2r**2*w1i - w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r -& w3r*Kdrag*cs**2*k**2*rhogsol*w2r*w1r + w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r + w3r**2*Kdrag*rhogsol*w2r*w1r**2& + w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i + w3r**2*Kdrag*rhogeq*k*vgsol*w2r*w1r -& w3r**2*rhogeq**2*cs**2*k**3*vgsol*w1i - w3r*Kdrag**2*k*vgsol*w2r*w1i -& 4*w3r*cs**2*k**2*rhogeq*rhogsol*w2r*w1i*w1r + 2*w3r*rhogeq**2*k*vgsol*w2r**2*w1i*w1r -& Kdrag*rhogsol*w2r*w1i**3*w3i + cs**2*k**2*rhogeq*rhogsol*w2r*w1i**3 + 2*Kdrag*rhogeq*k*vgsol*w2r*w1r*w3i*w1i -& rhogeq**2*k*vgsol*w2r**2*w1i**3 - 2*rhogeq*k*Kdrag*vdsol*w1r**2*w1i**2 - rhogeq*k*Kdrag*vdsol*w1r**4 -& Kdrag*rhogsol*w2r*w1r**2*w3i*w1i + Kdrag*cs**2*k**2*rhogsol*w2r*w1r**2 +& cs**2*k**2*rhogeq*rhogsol*w2r*w1i*w1r**2 - rhogeq**2*k*vgsol*w2r**2*w1i*w1r**2 -& vgsol*k*rhogeq**2*w2i**2*w1i**3 - rhogeq*k*Kdrag*vdsol*w1i**4 - 2*rhogeq**2*k*vgsol*w1i**3*w2i*w3i -& cs**2*k**3*rhogeq*Kdrag*vgsol*w1r**2 + 4*rhogeq*cs**2*k**2*rhogsol*w2i*w1r*w3i*w1i -& rhogeq*cs**2*k**2*rhogsol*w2i*w1r*w1i**2 + w3r**2*rhogeq*rhogsol*w2i*w1r*w1i**2 -& rhogeq*cs**2*k**2*rhogsol*w2i*w1r**3 + cs**2*k**3*rhogeq*Kdrag*vdsol*w1r**2 -& 2*cs**4*k**4*rhogeq*rhogsol*w1i*w1r + w1i**3*k*Kdrag**2*vgsol - vgsol*k*rhogeq**2*w2i**2*w1i*w1r**2 -& Kdrag**2*k*vgsol*w2i*w1r**2 + rhogeq*k*Kdrag*vdsol*w2r*w1r**3 + rhogeq*k*Kdrag*vdsol*w2i*w1i*w1r**2 +& cs**2*k**3*rhogeq*Kdrag*vgsol*w1i**2 - cs**2*k**3*rhogeq*Kdrag*vdsol*w1i**2 + w1i*k*Kdrag**2*vgsol*w1r**2 +& rhogeq*w3i*w1i*k*Kdrag*vdsol*w1r**2 + 2*rhogeq**2*w3i*w1i**2*k*vgsol*w1r**2 -& rhogeq**2*w3i**2*w1i*k*vgsol*w1r**2 - rhogeq*w3i*w1r**3*cs**2*k**2*rhogsol +& rhogeq*w3i*w2i*k*Kdrag*vdsol*w1r**2 + rhogeq*w3i**2*w2r*rhogsol*w1i*w1r**2 + rhogeq*w3i**2*w2i*w1r**3*rhogsol +& rhogeq*k*Kdrag*vdsol*w2r*w1r*w1i**2 + rhogeq*w3i*w2i**2*w1r*rhogsol*w1i**2 -& Kdrag*w1r*cs**2*k**2*rhogsol*w1i**2 + Kdrag*w2i*w3i*w1r*rhogsol*w1i**2 + Kdrag*w2r*cs**2*k**2*rhogsol*w1i**2 +& rhogeq*w3i**2*w2i*w1r*rhogsol*w1i**2 + rhogeq*w3i*w1i**2*w2r**2*rhogsol*w1r +& 2*rhogeq**2*w3i**2*w1i*k*vgsol*w2r*w1r - 2*rhogeq*w3i*w1i*k*Kdrag*vdsol*w2r*w1r +& rhogeq*w3i*w1i**3*k*Kdrag*vdsol - rhogeq*w3i*w1i**2*k*Kdrag*vdsol*w2i -& rhogeq*w3i*w1r*cs**2*k**2*rhogsol*w1i**2 - 2*rhogeq*w3i**2*w2r**2*rhogsol*w1i*w1r +& rhogeq*w3i**2*w2r*rhogsol*w1i**3 - 2*rhogeq*w3i**2*w2i**2*w1r*rhogsol*w1i -& 2*rhogeq*w3i*w1i**2*w2r*rhogsol*w1r**2 + 2*Kdrag*w1i**2*k*rhogeq*vgsol*w1r**2 - w1i**3*k*Kdrag**2*vdsol +& Kdrag*w1r**4*rhogeq*k*vgsol + rhogeq*k*Kdrag*vdsol*w2i*w1i**3 + 2*rhogeq**2*k*vgsol*w2i*w1i**2*w1r**2 +& 2*rhogeq**2*cs**2*k**3*vgsol*w1i*w2r*w1r + Kdrag**2*k*vdsol*w2i*w1r**2 - 2*rhogeq**2*k*vgsol*w1i*w1r**2*w2i*w3i& + w3i*k*Kdrag**2*vdsol*w1r**2 + rhogeq*w3i*w2i**2*w1r**3*rhogsol - w3i*k*Kdrag**2*vgsol*w1r**2 +& rhogeq**2*w3i*w1r**4*k*vgsol - w1i*k*Kdrag**2*vdsol*w1r**2 - w3i*rhogeq**2*k*vgsol*w2r**2*w1r**2 -& rhogeq*cs**2*k**3*vdsol*Kdrag*w2r*w1r + w3i*Kdrag*k*rhogeq*vgsol*w2r**2*w1i -& w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1r + 2*w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w1r**2 -& 2*w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w1i**2 + w3i*rhogeq**2*k*vgsol*w2r**2*w1i**2 +& w3i**2*Kdrag*rhogeq*k*vgsol*w2r*w1r - w1i**2*k*Kdrag**2*vgsol*w2i + w3i*rhogeq**2*cs**2*k**3*vgsol*w2r**2 -& Kdrag*k*rhogeq*vgsol*w2r**2*w1r**2 - Kdrag*k*rhogeq*vgsol*w2r**2*w1i**2 +& w3i**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i + rhogeq*cs**4*k**4*rhogsol*w2r*w1i -& w3i*rhogeq*cs**4*k**4*rhogsol*w2r - w3i*Kdrag*cs**2*k**2*rhogsol*w2r*w1i + w1i**2*k*Kdrag**2*vdsol*w2i -& rhogeq**2*w3i**2*w1i**3*k*vgsol + rhogeq**2*w3i*w1i**4*k*vgsol - rhogeq*w3i*w2r*w1r**4*rhogsol -& Kdrag*w1r**3*cs**2*k**2*rhogsol + Kdrag*w2i*w3i*w1r**3*rhogsol + rhogeq*w3i*w2r**2*w1r**3*rhogsol -& w3i*k*Kdrag**2*vgsol*w1i**2 + w3i*k*Kdrag**2*vdsol*w1i**2 + rhogeq**2*k*vgsol*w2i*w1r**4 -& rhogeq*w3i*w1i**4*w2r*rhogsol + Kdrag*w1i**4*k*rhogeq*vgsol + cs**2*k**3*rhogeq*Kdrag*vgsol*w2r*w1r -& w3i**2*cs**2*k**3*rhogeq**2*vgsol*w1i - w3i**2*Kdrag*rhogsol*w2r**2*w1r - w3i*Kdrag**2*k*vdsol*w2r*w1r +& w3i**2*Kdrag*rhogsol*w2r*w1r**2 + w3i**2*Kdrag*rhogsol*w2r*w1i**2 + w3i*Kdrag**2*k*vgsol*w2r*w1r +& rhogeq**2*k*vgsol*w2i*w1i**4 - w3r*Kdrag*rhogsol*w2r*w1r**3 + w3r*rhogeq*rhogsol*w2r**2*w1i**3 -& w3r**2*rhogeq**2*k*vgsol*w1i**3 + w3r**2*rhogeq*rhogsol*w2i*w1r**3 + w3r**2*Kdrag*rhogsol*w2r*w1i**2 -& w3r**2*Kdrag*rhogsol*w2r**2*w1r - w3r*rhogeq*rhogsol*w2i*w1i**4 + w3r*rhogeq*rhogsol*w1i**3*w2i**2 -& w3r*Kdrag*w2i*rhogsol*w1i**3 - w3r*rhogeq*rhogsol*w2i*w1r**4 - rhogeq**2*cs**2*k**3*vgsol*w1i*w2r**2 +& w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1r + w3r*Kdrag**2*k*vdsol*w2r*w1i + 2*w3r*rhogeq*cs**2*k**2*rhogsol*w2i*w1r**2& + w3r*cs**4*k**4*rhogeq*rhogsol*w1i - w3r*Kdrag*cs**2*k**2*rhogsol*w1i*w2i -& w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r - w3i*Kdrag**2*k*vdsol*w1i*w2i + w3i*Kdrag**2*k*vgsol*w1i*w2i +& w3r*Kdrag**2*k*vgsol*w2i*w1r + cs**2*k**3*rhogeq*Kdrag*vdsol*w1i*w2i + w3i**2*Kdrag*rhogeq*k*vgsol*w1i*w2i -& Kdrag*rhogeq*k*vgsol*w2i**2*w1r**2 - Kdrag*rhogeq*k*vgsol*w2i**2*w1i**2 + w3r*Kdrag*k*rhogeq*vgsol*w2i**2*w1r +& w3i*rhogeq*k*Kdrag*vgsol*w2i**2*w1i - 3*w3i*rhogeq*k*Kdrag*vgsol*w2i*w1r**2 -& w3i*rhogeq*k*Kdrag*vgsol*w1i**2*w2i + w3i*rhogeq**2*k*vgsol*w2i**2*w1i**2 -& w3i*rhogeq**2*cs**2*k**3*vgsol*w1r**2 + w3i*rhogeq**2*cs**2*k**3*vgsol*w1i**2 +& w3r**2*Kdrag*k*rhogeq*vgsol*w1i*w2i + w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r +& w3i*Kdrag*cs**2*k**2*rhogsol*w2i*w1r + rhogeq*cs**4*k**4*rhogsol*w2i*w1r -& w3r**2*rhogeq*cs**2*k**2*rhogsol*w2i*w1r + w3r**2*vgsol*rhogeq**2*k**3*cs**2*w2i -& w3i*rhogeq*cs**2*k**3*vdsol*Kdrag*w2i + w3i*rhogeq*cs**2*k**3*vdsol*Kdrag*w1i -& cs**2*k**3*rhogeq*Kdrag*vgsol*w1i*w2i - w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1i +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i + w3i*rhogeq*cs**4*k**4*rhogsol*w1r +& w3i**2*vgsol*rhogeq**2*k**3*cs**2*w2i - w3i**2*rhogeq*cs**2*k**2*rhogsol*w2i*w1r +& w3i*rhogeq**2*cs**2*k**3*vgsol*w2i**2 - 2*w3i*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i -& w3i*rhogeq**2*k*vgsol*w2i**2*w1r**2 + w3r*Kdrag*rhogsol*w2i**2*w1r**2 + rhogeq**2*cs**2*k**3*vgsol*w1i**2*w2i -& rhogeq**2*cs**2*k**3*vgsol*w2i*w1r**2 - w3r*Kdrag**2*k*vdsol*w2i*w1r - w3r*cs**4*k**4*rhogeq*rhogsol*w2i -& 2*w3r*rhogeq*cs**2*k**2*rhogsol*w1i**2*w2i + w3r*rhogeq*cs**2*k**2*rhogsol*w2i**2*w1i +& w3r*Kdrag*rhogsol*w2i**2*w1i**2 - w3i*rhogeq*cs**2*k**2*rhogsol*w2i**2*w1r - w3i**2*Kdrag*rhogsol*w2i**2*w1r -& w3r**2*Kdrag*rhogsol*w2i**2*w1r - rhogeq**2*cs**2*k**3*vgsol*w1i*w2i**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2& + w3i**2 - 2*w3r*w1r)/Kdrag/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2)/rhogeq/k endif !------------------------------- ! G A S D E N S I T I E S !------------------------------- rhog3r =(w3r**2*k*rhogeq*vgsol*w1i + w3r**2*w2i*rhogeq*k*vgsol - w3r**2*w2r*w1i*rhogsol - w3i**2*w2i*rhogeq*k*vgsol +& w3i**2*w2r*w1i*rhogsol - w3i*w1i**2*w2r*rhogsol - w3i*w2i**2*w1r*rhogsol - w3i*w2r**2*w1r*rhogsol -& w3i*w2r*w1r**2*rhogsol + w3i**2*rhogsol*w2i*w1r - w3i*w1i*k*Kdrag*vdsol + k*Kdrag*vdsol*w2i*w1i -& k*Kdrag*vgsol*w2i*w1i + w3i*w1i**2*k*rhogeq*vgsol + w3i*w1r**2*rhogeq*k*vgsol - w3i*w1r*cs**2*k**2*rhogsol -& w3i*w2i*k*Kdrag*vdsol + w3i*w1i*k*Kdrag*vgsol + w3i*w2i*k*Kdrag*vgsol + w3i*w2r**2*rhogeq*k*vgsol -& w3i**2*k*Kdrag*vgsol - k*Kdrag*vdsol*w2r*w1r + w3i**2*k*Kdrag*vdsol - k*rhogeq*vgsol*w2r**2*w1i -& w3i*w2r*cs**2*k**2*rhogsol + w3i*w2i**2*rhogeq*k*vgsol + k*Kdrag*vgsol*w2r*w1r - k*rhogeq*vgsol*w2i*w1r**2 +& rhogsol*k**2*cs**2*w2i*w1r + rhogsol*k**2*cs**2*w1i*w2r - k*rhogeq*vgsol*w1i*w2i**2 - w3i**2*k*rhogeq*vgsol*w1i& - k*rhogeq*vgsol*w2i*w1i**2 + w3r*k*Kdrag*vdsol*w2r + 2*w3r*w3i*rhogsol*k**2*cs**2 - w3r*rhogsol*k**2*cs**2*w1i& + w3r*k*Kdrag*vdsol*w1r - w3r*rhogsol*k**2*cs**2*w2i + 2*w3r*w3i*w2r*w1r*rhogsol - 2*w3r*w3i*w2i*rhogsol*w1i -& w3r*k*Kdrag*vgsol*w2r - w3r*k*Kdrag*vgsol*w1r + w3r*rhogsol*w2i*w1i**2 + w3r*rhogsol*w2i*w1r**2 +& w3r*rhogsol*w2r**2*w1i + w3r*rhogsol*w1i*w2i**2 + w3r**2*k*Kdrag*vgsol - w3r**2*k*Kdrag*vdsol -& w3r**2*rhogsol*w2i*w1r + 2*w3i*w2r*k*rhogeq*w1r*vgsol + 2*w3i*k*w2i*rhogeq*w1i*vgsol -& 2*w3r*w3i*w2r*vgsol*k*rhogeq - 2*w3r*w3i*k*rhogeq*w1r*vgsol)/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i& + w3r**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r) rhog3i = - ( - rhogsol*w1i**2*w2r**2 - rhogsol*w1i**2*w2i**2 - rhogsol*w1r**2*w2r**2 - rhogsol*w1r**2*w2i**2 -& 2*w3r*rhogeq*k*vgsol*w2i*w1i + rhogsol*w2i**2*w3i*w1i + rhogsol*k**2*cs**2*w1r*w3r - rhogsol*k**2*cs**2*w3i*w1i& - vgsol*k*rhogeq*w3r*w2i**2 - vgsol*Kdrag*k*w3r*w1i - vgsol*Kdrag*k*w3r*w2i + 2*w3r*rhogeq*k*vgsol*w3i*w1i +& rhogsol*w1i**2*w2i*w3i + rhogsol*w1i*w2i*w3r**2 + rhogsol*w2i*w3i*w1r**2 - rhogsol*w1i*w2i*w3i**2 +& rhogsol*w2i**2*w3r*w1r - rhogsol*k**2*cs**2*w2i*w3i - vgsol*k*rhogeq*w3r*w1i**2 + w3r*rhogsol*k**2*cs**2*w2r -& 2*w3r*w2r*k*rhogeq*w1r*vgsol + w3r**2*w2r*vgsol*k*rhogeq - w3r*w2r**2*rhogeq*k*vgsol -& vgsol*k*rhogeq*w3r*w1r**2 + vgsol*k*rhogeq*w3r**2*w1r + vdsol*Kdrag*k*w3r*w1i - vdsol*Kdrag*k*w2i*w1r +& vdsol*Kdrag*k*w3r*w2i + vdsol*Kdrag*k*w3i*w1r + rhogsol*k**2*cs**2*w1i*w2i + vgsol*Kdrag*k*w2i*w1r -& vgsol*Kdrag*k*w3i*w1r - vgsol*k*rhogeq*w1r*w3i**2 + vgsol*k*rhogeq*w2i**2*w1r - rhogsol*k**2*cs**2*w2r*w1r -& w3r**2*w2r*w1r*rhogsol + w3r*w2r*rhogsol*w1i**2 + w3r*w2r*w1r**2*rhogsol + w3r*w2r**2*w1r*rhogsol -& 2*w3r*w1r*rhogsol*w2i*w3i + 2*vgsol*Kdrag*k*w3i*w3r - 2*vdsol*Kdrag*k*w3i*w3r - w3r**2*rhogsol*k**2*cs**2 +& 2*w3r*rhogeq*k*vgsol*w2i*w3i + rhogsol*k**2*cs**2*w3i**2 - 2*w2r*rhogsol*w3i*w1i*w3r +& vgsol*k*rhogeq*w2r*w1r**2 + vgsol*k*rhogeq*w1r*w2r**2 + vgsol*k*rhogeq*w2r*w1i**2 - vgsol*k*rhogeq*w2r*w3i**2 +& vgsol*Kdrag*k*w1i*w2r - vgsol*Kdrag*k*w2r*w3i + rhogsol*w2r**2*w3i*w1i + rhogsol*w2r*w1r*w3i**2 +& vdsol*Kdrag*k*w2r*w3i - vdsol*Kdrag*k*w1i*w2r)/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i +& w3r**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r) rhog2r =( - w3r**2*k*rhogeq*vgsol*w1i + w3r**2*w2i*rhogeq*k*vgsol + w3r**2*w2r*w1i*rhogsol +& w3i**2*w2i*rhogeq*k*vgsol + w3i**2*w2r*w1i*rhogsol + w3i*w1i**2*w2r*rhogsol + w3i*w2i**2*w1r*rhogsol -& w3i*w2r**2*w1r*rhogsol + w3i*w2r*w1r**2*rhogsol + 2*rhogsol*w3r*w2i*w1r*w2r + 2*cs**2*k**2*rhogsol*w2r*w2i -& k*Kdrag*vgsol*w2i**2 + k*Kdrag*vgsol*w2r**2 - 2*rhogsol*w2r*w2i*w3i*w1i + k*Kdrag*vdsol*w2i**2 -& k*Kdrag*vdsol*w2r**2 - 2*vgsol*rhogeq*k*w3r*w2r*w2i - 2*vgsol*rhogeq*k*w2i*w1r*w2r +& 2*vgsol*rhogeq*k*w3r*w2i*w1r - w3i**2*rhogsol*w2i*w1r + w3i*w1i*k*Kdrag*vdsol - k*Kdrag*vdsol*w2i*w1i +& k*Kdrag*vgsol*w2i*w1i - w3i*w1i**2*k*rhogeq*vgsol - w3i*w1r**2*rhogeq*k*vgsol + w3i*w1r*cs**2*k**2*rhogsol -& w3i*w2i*k*Kdrag*vdsol - w3i*w1i*k*Kdrag*vgsol + w3i*w2i*k*Kdrag*vgsol + w3i*w2r**2*rhogeq*k*vgsol +& k*Kdrag*vdsol*w2r*w1r + k*rhogeq*vgsol*w2r**2*w1i - w3i*w2r*cs**2*k**2*rhogsol - w3i*w2i**2*rhogeq*k*vgsol -& k*Kdrag*vgsol*w2r*w1r + k*rhogeq*vgsol*w2i*w1r**2 - rhogsol*k**2*cs**2*w2i*w1r - rhogsol*k**2*cs**2*w1i*w2r -& k*rhogeq*vgsol*w1i*w2i**2 - w3i**2*k*rhogeq*vgsol*w1i + k*rhogeq*vgsol*w2i*w1i**2 + w3r*k*Kdrag*vdsol*w2r +& w3r*rhogsol*k**2*cs**2*w1i - w3r*k*Kdrag*vdsol*w1r - w3r*rhogsol*k**2*cs**2*w2i - w3r*k*Kdrag*vgsol*w2r +& w3r*k*Kdrag*vgsol*w1r - w3r*rhogsol*w2i*w1i**2 - w3r*rhogsol*w2i*w1r**2 - w3r*rhogsol*w2r**2*w1i +& w3r*rhogsol*w1i*w2i**2 - w3r**2*rhogsol*w2i*w1r + 2*w3i*k*w2i*rhogeq*w1i*vgsol)/(w2r**2 - 2*w3r*w2r + w2i**2 +& w3i**2 - 2*w2i*w3i + w3r**2)/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2) rhog2i =( - 2*w2i*w2r*vgsol*k*rhogeq*w1i + 2*w3r*w2i*rhogsol*w1i*w2r - 2*w2i*k*rhogeq*vgsol*w2r*w3i +& rhogsol*w2i**2*w3i*w1i + rhogsol*k**2*cs**2*w1r*w3r - rhogsol*k**2*cs**2*w3i*w1i + vgsol*k*rhogeq*w3r*w2i**2 -& vgsol*Kdrag*k*w3r*w1i + vgsol*Kdrag*k*w3r*w2i - rhogsol*w1i**2*w2i*w3i - rhogsol*w1i*w2i*w3r**2 -& rhogsol*w2i*w3i*w1r**2 - rhogsol*w1i*w2i*w3i**2 - rhogsol*w2i**2*w3r*w1r + rhogsol*k**2*cs**2*w2i*w3i -& vgsol*k*rhogeq*w3r*w1i**2 - w3r*rhogsol*k**2*cs**2*w2r + 2*w3r*w2r*k*rhogeq*w1r*vgsol +& w3r**2*w2r*vgsol*k*rhogeq - w3r*w2r**2*rhogeq*k*vgsol - vgsol*k*rhogeq*w3r*w1r**2 - vgsol*k*rhogeq*w3r**2*w1r +& vdsol*Kdrag*k*w3r*w1i - vdsol*Kdrag*k*w2i*w1r - vdsol*Kdrag*k*w3r*w2i + vdsol*Kdrag*k*w3i*w1r +& rhogsol*k**2*cs**2*w1i*w2i + vgsol*Kdrag*k*w2i*w1r - vgsol*Kdrag*k*w3i*w1r - vgsol*k*rhogeq*w1r*w3i**2 +& vgsol*k*rhogeq*w2i**2*w1r - rhogsol*k**2*cs**2*w2r*w1r - w3r**2*w2r*w1r*rhogsol - w3r*w2r*rhogsol*w1i**2 -& w3r*w2r*w1r**2*rhogsol + w3r*w2r**2*w1r*rhogsol + rhogsol*w3r**2*w1i**2 + rhogsol*w3r**2*w1r**2 +& rhogsol*w1i**2*w3i**2 + vgsol*k*rhogeq*w2r*w1r**2 - vgsol*k*rhogeq*w1r*w2r**2 + vgsol*k*rhogeq*w2r*w1i**2 +& rhogsol*w3i**2*w1r**2 - w2i**2*rhogsol*k**2*cs**2 + vgsol*k*rhogeq*w2r*w3i**2 + vgsol*Kdrag*k*w1i*w2r +& vgsol*Kdrag*k*w2r*w3i + w2r**2*rhogsol*k**2*cs**2 + 2*w2i*k*Kdrag*vdsol*w2r + 2*w2i*rhogsol*w3i*w2r*w1r -& 2*w2i*k*Kdrag*vgsol*w2r + 2*w2r*w3i*k*rhogeq*w1i*vgsol - rhogsol*w2r**2*w3i*w1i - rhogsol*w2r*w1r*w3i**2 -& vdsol*Kdrag*k*w2r*w3i - vdsol*Kdrag*k*w1i*w2r)/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i +& w3r**2)/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2) rhog1r = - ( - w3r**2*k*rhogeq*vgsol*w1i - 2*rhogsol*k**2*cs**2*w1i*w1r + 2*k*rhogeq*vgsol*w1i*w2r*w1r +& 2*rhogsol*w2i*w1r*w3i*w1i + 2*w3r*k*rhogeq*vgsol*w1i*w1r - 2*w3r*w2r*k*rhogeq*vgsol*w1i -& 2*w3r*w2r*w1i*rhogsol*w1r + w3r**2*w2i*rhogeq*k*vgsol + w3r**2*w2r*w1i*rhogsol + w3i**2*w2i*rhogeq*k*vgsol +& w3i**2*w2r*w1i*rhogsol - w3i*w1i**2*w2r*rhogsol - w3i*w2i**2*w1r*rhogsol - w3i*w2r**2*w1r*rhogsol +& w3i*w2r*w1r**2*rhogsol - w3i**2*rhogsol*w2i*w1r + w3i*w1i*k*Kdrag*vdsol + k*Kdrag*vdsol*w2i*w1i -& k*Kdrag*vgsol*w2i*w1i + w3i*w1i**2*k*rhogeq*vgsol - w3i*w1r**2*rhogeq*k*vgsol + w3i*w1r*cs**2*k**2*rhogsol -& w3i*w2i*k*Kdrag*vdsol - w3i*w1i*k*Kdrag*vgsol + w3i*w2i*k*Kdrag*vgsol + w3i*w2r**2*rhogeq*k*vgsol -& k*Kdrag*vdsol*w2r*w1r - k*rhogeq*vgsol*w2r**2*w1i - w3i*w2r*cs**2*k**2*rhogsol + w3i*w2i**2*rhogeq*k*vgsol +& k*Kdrag*vgsol*w2r*w1r - k*rhogeq*vgsol*w2i*w1r**2 + rhogsol*k**2*cs**2*w2i*w1r + rhogsol*k**2*cs**2*w1i*w2r -& k*rhogeq*vgsol*w1i*w2i**2 - w3i**2*k*rhogeq*vgsol*w1i + k*rhogeq*vgsol*w2i*w1i**2 + w3r*k*Kdrag*vdsol*w2r +& w3r*rhogsol*k**2*cs**2*w1i - w3r*k*Kdrag*vdsol*w1r - w3r*rhogsol*k**2*cs**2*w2i - w3r*k*Kdrag*vgsol*w2r +& w3r*k*Kdrag*vgsol*w1r - Kdrag*w1r**2*k*vgsol - w3r*rhogsol*w2i*w1i**2 + w3r*rhogsol*w2i*w1r**2 +& Kdrag*w1i**2*k*vgsol + w3r*rhogsol*w2r**2*w1i + w3r*rhogsol*w1i*w2i**2 + k*Kdrag*vdsol*w1r**2 -& w3r**2*rhogsol*w2i*w1r - k*Kdrag*vdsol*w1i**2 - 2*w3i*k*w2i*rhogeq*w1i*vgsol)/(w1i**2 - 2*w3i*w1i + w3r**2 +& w1r**2 + w3i**2 - 2*w3r*w1r)/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2) rhog1i = - ( - 2*vdsol*Kdrag*k*w1i*w1r + rhogsol*w2i**2*w3i*w1i + rhogsol*k**2*cs**2*w1i**2 -& rhogsol*k**2*cs**2*w1r**2 + rhogsol*k**2*cs**2*w1r*w3r - rhogsol*k**2*cs**2*w3i*w1i + vgsol*k*rhogeq*w3r*w2i**2& + 2*vgsol*Kdrag*k*w1i*w1r - vgsol*Kdrag*k*w3r*w1i + vgsol*Kdrag*k*w3r*w2i - rhogsol*w1i**2*w2i*w3i +& rhogsol*w1i*w2i*w3r**2 - 2*rhogsol*w1i*w2i*w3r*w1r + rhogsol*w2i*w3i*w1r**2 + rhogsol*w1i*w2i*w3i**2 +& rhogsol*w2i**2*w3r*w1r + rhogsol*k**2*cs**2*w2i*w3i - vgsol*k*rhogeq*w3r*w1i**2 - w3r*rhogsol*k**2*cs**2*w2r -& 2*w3r*w2r*k*rhogeq*w1r*vgsol + w3r**2*w2r*vgsol*k*rhogeq + w3r*w2r**2*rhogeq*k*vgsol +& vgsol*k*rhogeq*w3r*w1r**2 + 2*vgsol*k*rhogeq*w1r*w2i*w1i - vgsol*k*rhogeq*w3r**2*w1r + vdsol*Kdrag*k*w3r*w1i +& vdsol*Kdrag*k*w2i*w1r - vdsol*Kdrag*k*w3r*w2i + vdsol*Kdrag*k*w3i*w1r - w3r**2*rhogsol*w2r**2 -& rhogsol*k**2*cs**2*w1i*w2i - vgsol*Kdrag*k*w2i*w1r - vgsol*Kdrag*k*w3i*w1r + 2*vgsol*k*rhogeq*w1i*w1r*w3i -& vgsol*k*rhogeq*w1r*w3i**2 - 2*vgsol*k*rhogeq*w2i*w1r*w3i - vgsol*k*rhogeq*w2i**2*w1r +& rhogsol*k**2*cs**2*w2r*w1r + w3r**2*w2r*w1r*rhogsol + w3r*w2r*rhogsol*w1i**2 - w3r*w2r*w1r**2*rhogsol +& w3r*w2r**2*w1r*rhogsol + vgsol*k*rhogeq*w2r*w1r**2 - vgsol*k*rhogeq*w1r*w2r**2 - vgsol*k*rhogeq*w2r*w1i**2 -& rhogsol*w2i**2*w3i**2 - rhogsol*w2i**2*w3r**2 + vgsol*k*rhogeq*w2r*w3i**2 - vgsol*Kdrag*k*w1i*w2r +& vgsol*Kdrag*k*w2r*w3i - 2*rhogsol*w2r*w1r*w3i*w1i + rhogsol*w2r**2*w3i*w1i + rhogsol*w2r*w1r*w3i**2 -& vdsol*Kdrag*k*w2r*w3i + vdsol*Kdrag*k*w1i*w2r - rhogsol*w2r**2*w3i**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 +& w3i**2 - 2*w3r*w1r)/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2) !------------------------------- ! D U S T D E N S I T I E S !------------------------------- if (Kdrag.gt.0.) then rhod3r = - rhodeq*( - w3r**3*rhogeq*w1i**2*w2r**2*rhogsol - w3r**2*Kdrag**2*k*vgsol*w2r*w1r +& w3r**2*Kdrag**2*k*vgsol*w2i*w1i - w3r**2*Kdrag**2*k*vdsol*w2i*w1i + w3r**2*Kdrag*k*rhogeq*vgsol*w2i*w1i**2 +& w3r**2*Kdrag**2*k*vdsol*w2r*w1r - w3r**2*Kdrag*rhogsol*k**2*cs**2*w2i*w1r +& w3r**2*Kdrag*k*rhogeq*vgsol*w2i*w1r**2 - w3r**2*Kdrag*rhogsol*k**2*cs**2*w1i*w2r +& w3r**2*rhogeq**2*w2i*w1i**2*w3i*k*vgsol - 4*w3r**2*rhogeq**2*w3i**2*k*w2i*w1i*vgsol -& 2*w3r**2*rhogeq*w2i*w3i**2*k*Kdrag*vgsol + w3r**2*rhogeq*w2r*w3i*w1r*k*Kdrag*vdsol -& 2*w3r**2*rhogeq**2*w1i**2*k*vgsol*w3i**2 - w3r**2*rhogeq*w2i*w3i*w1i*k*Kdrag*vgsol +& w3r**5*rhogeq*w2i*rhogsol*w1i + w3r**2*rhogeq**2*w2i*k*vgsol*w3i*w1r**2 - w3r**5*rhogeq*rhogsol*k**2*cs**2 -& w3r**5*rhogeq*w2r*w1r*rhogsol + w3r**5*rhogeq**2*k*w1r*vgsol + 3*w3r**2*rhogeq*cs**2*k**3*w3i*Kdrag*vdsol +& w3r**5*rhogeq**2*w2r*vgsol*k + 2*w3r**2*rhogeq*w3i**2*cs**2*k**2*rhogsol*w1r -& w3r**2*rhogeq*cs**4*k**4*w2r*rhogsol - 2*w3r**2*rhogeq**2*w3i**2*k*w1r**2*vgsol +& 2*w3r**2*rhogeq*w2r*w3i**2*cs**2*k**2*rhogsol - w3r*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i**2 -& w3r**3*Kdrag*rhogsol*w2i*w1i**2 - w3r**3*Kdrag*rhogsol*w2r**2*w1i - w3r**3*Kdrag*rhogsol*w1i*w2i**2 -& w3r**3*Kdrag*rhogsol*w2i*w1r**2 - w3r**3*rhogeq*w2r*w1i*k*Kdrag*vdsol + w3r**3*rhogeq**2*w2r*w1r**2*k*vgsol +& w3r**3*rhogeq**2*w2r*w1i**2*k*vgsol - w3r**3*rhogeq**2*cs**2*k**3*w1r*vgsol +& w3r**3*rhogeq*w2r*w1i*k*Kdrag*vgsol - w3r**3*Kdrag**2*k*vdsol*w2r + 2*w3r**3*Kdrag*k*rhogeq*vgsol*w3i*w1r +& 2*w3r**3*rhogeq**2*w2r*k*vgsol*w3i**2 + 2*w3r**3*Kdrag*k*rhogeq*vgsol*w2r*w3i +& w3r**3*Kdrag*rhogsol*k**2*cs**2*w1i - w3r**3*rhogeq**2*cs**2*k**3*w2r*vgsol -& 2*w3r**3*Kdrag*rhogsol*k**2*cs**2*w3i + w3r**3*Kdrag*rhogsol*k**2*cs**2*w2i +& 2*w3r**3*Kdrag*rhogsol*w2i*w3i*w1i - w3r**3*rhogeq*w2i**2*w1i**2*rhogsol - 2*w3r**3*Kdrag*rhogsol*w3i*w2r*w1r -& w3r**2*rhogeq*w2i*w3i*w1i*k*Kdrag*vdsol - w3r**3*rhogeq*w1r**2*w2i**2*rhogsol -& w3r**2*rhogeq*cs**4*k**4*w1r*rhogsol - 3*w3r**2*rhogeq**2*cs**2*k**3*w3i*w1i*vgsol -& 3*w3r**2*rhogeq*cs**2*k**3*w3i*Kdrag*vgsol - w3r**2*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol -& w3r**2*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol + w3r**2*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol +& w3r**2*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol - w3r**3*rhogeq*w2r**2*w1r**2*rhogsol +& w3r**2*rhogeq**2*w2i**2*w3i*w1i*k*vgsol + w3r**2*rhogeq**2*cs**2*k**3*w1i**2*vgsol +& w3r**3*rhogeq*cs**4*k**4*rhogsol - 3*w3r**2*rhogeq**2*cs**2*k**3*w3i*w2i*vgsol +& 2*w3r**2*rhogeq*w2r*w3i**2*w1r**2*rhogsol + w3r**2*rhogeq**2*cs**2*k**3*w1r**2*vgsol -& 2*w3r**2*rhogeq**2*w3i**2*w2i**2*k*vgsol + 2*w3r**2*rhogeq*w2i**2*w3i**2*w1r*rhogsol +& w3r**2*rhogeq**2*w3i*w2r**2*w1i*k*vgsol + 2*w3r**2*rhogeq*w1i*w3i**2*k*Kdrag*vdsol +& 2*w3r**2*rhogeq*w1i**2*w3i**2*w2r*rhogsol - 2*w3r**2*rhogeq*w1i*w3i**2*k*Kdrag*vgsol +& 2*w3r**2*rhogeq*w2r**2*w3i**2*w1r*rhogsol - 4*w3r**2*rhogeq**2*w2r*w3i**2*k*w1r*vgsol -& 3*w3r**2*rhogeq*w2r*w3i*w1r*k*Kdrag*vgsol - 2*w3r**2*rhogeq**2*w2r**2*w3i**2*k*vgsol +& 2*w3r**2*rhogeq*w2i*w3i**2*k*Kdrag*vdsol + w3r**3*rhogeq**2*w2r**2*k*w1r*vgsol -& 2*w3r**3*rhogeq*w3i**2*cs**2*k**2*rhogsol + w3r**4*rhogeq*w2i**2*w1r*rhogsol + w3r**4*Kdrag*rhogsol*w1i*w2r +& w3r**4*rhogeq*w2r*w1r**2*rhogsol + w3r**4*rhogeq*w1i**2*w2r*rhogsol - 2*w3r**4*rhogeq**2*k*w2i*w1i*vgsol -& 2*w3r**4*rhogeq*w1i*k*Kdrag*vgsol - w3r**4*rhogeq**2*w2r**2*k*vgsol + w3r**4*rhogeq*w1i*k*Kdrag*vdsol -& w3r**4*rhogeq**2*w1r**2*k*vgsol + w3r**4*rhogeq*w1r*cs**2*k**2*rhogsol + w3r**4*rhogeq*w2r*cs**2*k**2*rhogsol -& 2*w3r**4*rhogeq*w2i*k*Kdrag*vgsol - w3r**4*rhogeq**2*w1i**2*k*vgsol + w3r**4*rhogeq*w2r**2*w1r*rhogsol +& w3r**3*rhogeq*w2i*w1r*k*Kdrag*vgsol + w3r**4*Kdrag**2*k*vdsol + 2*w3r**3*rhogeq*w1i*w3i**2*rhogsol*w2i -& 2*w3r**4*rhogeq**2*w2r*k*w1r*vgsol + 2*w3r**3*rhogeq**2*w3i**2*k*w1r*vgsol -& w3r**3*rhogeq*w2i*w1r*k*Kdrag*vdsol + w3r**3*rhogeq**2*w2i**2*w1r*k*vgsol -& 2*w3r**3*rhogeq*w2r*w3i**2*w1r*rhogsol + w3r**4*rhogeq*w2i*k*Kdrag*vdsol - w3r**4*rhogeq**2*w2i**2*k*vgsol +& w3r**3*Kdrag**2*k*vgsol*w1r + w3r**4*Kdrag*rhogsol*w2i*w1r - w3r**4*Kdrag**2*k*vgsol -& w3r**3*Kdrag**2*k*vdsol*w1r + w3r**3*Kdrag**2*k*vgsol*w2r + w1i*w3i**3*k*Kdrag**2*vdsol +& 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r + w3r*rhogeq**2*w3i**4*w2r*k*vgsol +& 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r - 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r -& 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r + w3r*rhogeq**2*w3i**4*k*w1r*vgsol -& w3r*rhogeq*w3i**4*w2r*w1r*rhogsol + 2*w3r*w3i*cs**4*k**4*rhogeq*rhogsol*w2i + w3r*rhogeq*w3i**4*w1i*rhogsol*w2i& - w2i*w3i**3*k*Kdrag**2*vgsol + 2*w3r*rhogeq*w3i**3*k*Kdrag*vgsol*w2r + w2i*w3i**3*k*Kdrag**2*vdsol -& w1i*w3i**3*k*Kdrag**2*vgsol - w3r*rhogeq*w3i**4*cs**2*k**2*rhogsol -& 2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w1i*w2i**2 - 2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1i) !--break to avoid too many continuation lines rhod3r = rhod3r - rhodeq*( & 2*w3r*w3i**3*Kdrag*rhogsol*w2i*w1i - 2*w3r*w3i**3*Kdrag*rhogsol*w2r*w1r - 2*w3r*w3i**3*Kdrag*rhogsol*k**2*cs**2& + w3r*w3i**2*Kdrag**2*k*vgsol*w1r + 2*w3r*w3i*cs**4*k**4*rhogeq*rhogsol*w1i +& w3r*rhogeq*cs**2*k**2*w1i**2*w2r**2*rhogsol + w3r*rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol -& w3r*rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol + w3r**2*rhogeq**2*cs**2*k**3*w2r**2*vgsol +& w3r**2*rhogeq*w2r**2*w1i*k*Kdrag*vgsol - 2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w1i**2 -& 2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w1r**2 + w1r*w3i**2*cs**2*k**2*rhogeq*rhogsol*w2i**2 -& w1r*w3r**2*cs**2*k**2*rhogeq*rhogsol*w2i**2 - w2r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r**2 -& w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol + w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol -& w2r*w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol - w2r*w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol +& w2r*w3r*rhogeq*cs**4*k**4*w1r*rhogsol + 2*w2r*w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol +& rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r*w1r - w3r*w3i**2*rhogeq*w2i*w1r*k*Kdrag*vdsol +& 3*w3r*w3i**2*rhogeq**2*cs**2*k**3*w2r*vgsol + w3r*w3i**2*Kdrag*rhogsol*k**2*cs**2*w1i +& 2*w3r*rhogeq*w3i**3*k*Kdrag*vgsol*w1r + rhogeq*w3i**2*cs**2*k**2*rhogsol*w1r*w2r**2 -& w3r**2*rhogeq*rhogsol*k**2*cs**2*w2r**2*w1r - w2r*w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2 +& rhogeq*w2r*w3i**2*cs**2*k**2*rhogsol*w1r**2 - w3r*w3i**2*Kdrag*rhogsol*w2r**2*w1i -& w3r*w3i**2*Kdrag**2*k*vdsol*w2r + w3r*w3i**2*Kdrag**2*k*vgsol*w2r - rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r*w1r +& rhogeq**2*cs**2*k**3*w3i*w1i*vgsol*w2r**2 - w3r*w3i**2*Kdrag*rhogsol*w2i*w1r**2 +& w3r*w3i**2*Kdrag*rhogsol*k**2*cs**2*w2i - w3r*w3i**2*Kdrag*rhogsol*w1i*w2i**2 -& 2*w3i**2*cs**2*k**3*rhogeq**2*vgsol*w2r*w1r - w3i**2*cs**2*k**3*rhogeq**2*vgsol*w2r**2 -& w3r*w3i**2*Kdrag*rhogsol*w2i*w1i**2 - w3i*rhogsol*k**4*cs**4*rhogeq*w2r*w1i +& rhogeq*cs**2*k**2*w1i**2*w2r*rhogsol*w3i**2 + Kdrag*k*rhogeq*vgsol*w2r**2*w1i*w3i**2 -& w3r*w3i**2*Kdrag**2*k*vdsol*w1r - rhogeq*w3i**5*w2r*w1i*rhogsol + w3r*w3i**2*rhogeq*w2r*w1i*k*Kdrag*vgsol +& w3r*w3i**2*rhogeq**2*w2r*w1i**2*k*vgsol + w3r*w3i**2*rhogeq*w2i*w1r*k*Kdrag*vgsol -& w3r*w3i**2*rhogeq*w2i**2*w1i**2*rhogsol + w3r*w3i**2*rhogeq**2*w2r**2*k*w1r*vgsol -& w3r*w3i**2*rhogeq*w1r**2*w2i**2*rhogsol - w3r*w3i**2*rhogeq*w2r**2*w1r**2*rhogsol -& 3*w3r*w3i**2*rhogeq*cs**4*k**4*rhogsol + w3r*w3i**2*rhogeq**2*w2i**2*w1r*k*vgsol -& 4*cs**2*k**2*rhogeq*w3r*w3i**2*w2r*w1r*rhogsol - rhogeq**2*w3i**4*w1i**2*k*vgsol -& cs**2*k**3*rhogeq*w3i**3*Kdrag*vdsol - w3r*w3i**2*rhogeq*w1i**2*w2r**2*rhogsol +& cs**2*k**3*rhogeq*w3i**3*Kdrag*vgsol + 3*w3r*w3i**2*rhogeq**2*cs**2*k**3*w1r*vgsol -& w3r*w3i**2*rhogeq*w2r*w1i*k*Kdrag*vdsol + w3r*w3i**2*rhogeq**2*w2r*w1r**2*k*vgsol +& 4*cs**2*k**2*rhogeq*w3r*w3i**2*w2i*rhogsol*w1i - rhogeq**2*w3i**4*k*w1r**2*vgsol +& 2*rhogeq*w3i**3*w3r**2*k*Kdrag*vgsol + 2*rhogeq**2*w3i**3*w3r**2*w1i*k*vgsol +& rhogeq*w3i**4*cs**2*k**2*rhogsol*w1r - 2*rhogeq*w3i**3*w3r**2*k*Kdrag*vdsol +& rhogeq*w3i**4*w2r*cs**2*k**2*rhogsol - 2*rhogeq**2*w3i**4*k*w2i*w1i*vgsol - rhogeq*w3i**3*w2i*w1i*k*Kdrag*vdsol& + rhogeq**2*w3i**3*w2i*w1i**2*k*vgsol + Kdrag*w2i**2*vgsol*k*rhogeq*w1i*w3r**2 -& w3i**2*rhogeq**2*cs**2*k**3*vgsol*w2i**2 + 2*w3r**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i -& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i*w1i - rhogeq**2*w3i**4*w2r**2*k*vgsol +& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w1i + w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1r**2 +& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**2 + rhogeq*w3i**4*w1i**2*w2r*rhogsol - rhogeq**2*w3i**4*w2i**2*k*vgsol) !--break to avoid too many continuation lines rhod3r = rhod3r - rhodeq*( & rhogeq**2*w3i**5*w2i*k*vgsol + rhogeq*w3i**3*w2r*w1r*k*Kdrag*vdsol + rhogeq*w3i**4*w2i*k*Kdrag*vdsol +& rhogeq**2*w3i**3*w2i*k*vgsol*w1r**2 - 2*rhogeq**2*w3i**4*w2r*k*w1r*vgsol +& w3r*rhogeq*cs**2*k**2*w1r**2*rhogsol*w2i**2 + w3r*rhogeq*cs**2*k**2*w1i**2*rhogsol*w2i**2 -& 2*rhogeq*w3i**3*w3r**2*w2i*w1r*rhogsol - 2*rhogeq*w3i**3*w2r*w3r**2*rhogsol*w1i +& w3r**2*rhogeq**2*cs**2*k**3*vgsol*w2i**2 - w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w1r -& w3r*rhogeq*cs**4*k**4*rhogsol*w2i*w1i + 2*rhogeq**2*w3i**3*w3r**2*w2i*k*vgsol +& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w1r - w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w1r +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i*w1i + Kdrag*w3i**3*cs**2*k**2*rhogsol*w1r +& rhogeq*w3i**4*w2i**2*w1r*rhogsol - Kdrag*w3i**3*k*rhogeq*w1r**2*vgsol + Kdrag*w2r*w3i**3*cs**2*k**2*rhogsol -& rhogeq*w3i**5*w1r*w2i*rhogsol - Kdrag*w1r*w2i*w3i**4*rhogsol - rhogeq*w3i**5*k*Kdrag*vdsol +& rhogeq*w3i**5*k*Kdrag*vgsol - 2*w3i**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i + rhogeq**2*w3i**5*w1i*k*vgsol +& rhogeq*w3i**4*w2r**2*w1r*rhogsol + rhogeq*w3i**4*w1i*k*Kdrag*vdsol - Kdrag*w1i**2*rhogeq*k*vgsol*w3i**3 +& rhogeq*w3i**4*w2r*w1r**2*rhogsol - Kdrag*w2r*w1i*w3i**4*rhogsol + w3i**4*k*Kdrag**2*vgsol -& w3i**4*k*Kdrag**2*vdsol + w3i**3*rhogeq**2*k*vgsol*w2r**2*w1i - w3i**2*Kdrag**2*k*vgsol*w2r*w1r +& w3i**3*Kdrag*rhogsol*w2r*w1r**2 + w3i**3*Kdrag*rhogsol*w2r*w1i**2 + w3i**2*Kdrag**2*k*vdsol*w2r*w1r +& w3i**3*cs**2*k**3*rhogeq**2*vgsol*w1i - w3i**2*Kdrag*cs**2*k**2*rhogsol*w2r*w1i +& w3i**3*Kdrag*rhogsol*w2r**2*w1r + w3i**2*rhogeq*cs**4*k**4*rhogsol*w2r -& 2*w3i**3*cs**2*k**2*rhogeq*rhogsol*w2r*w1i - w3i**3*Kdrag*rhogeq*k*vgsol*w2r**2 -& 3*w3i**3*Kdrag*rhogeq*k*vgsol*w2r*w1r - w3i**3*Kdrag*rhogeq*k*vgsol*w2i**2 -& w3i**2*rhogeq**2*cs**2*k**3*vgsol*w1i**2 + w3i**2*rhogeq*k*Kdrag*vgsol*w1i**2*w2i -& w3i**2*rhogeq**2*cs**2*k**3*vgsol*w1r**2 + w3i**2*rhogeq*k*Kdrag*vgsol*w2i*w1r**2 -& w3i**3*Kdrag*rhogeq*k*vgsol*w1i*w2i - w3i**2*Kdrag**2*k*vdsol*w1i*w2i + w3i**2*Kdrag**2*k*vgsol*w1i*w2i -& w3i**2*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i - w3i**2*cs**2*k**3*rhogeq*Kdrag*vgsol*w1i +& w3i**3*Kdrag*rhogsol*w2i**2*w1r + w3i**2*rhogeq*cs**2*k**3*vdsol*Kdrag*w1i +& w3i**2*rhogeq*cs**2*k**3*vdsol*Kdrag*w2i - w3i**2*Kdrag*cs**2*k**2*rhogsol*w2i*w1r -& 2*w3i**3*rhogeq*cs**2*k**2*rhogsol*w2i*w1r + w3i**3*vgsol*k*rhogeq**2*w2i**2*w1i +& w3i**3*vgsol*rhogeq**2*k**3*cs**2*w2i + w3i**2*rhogeq*cs**4*k**4*rhogsol*w1r -& w3i*Kdrag*w3r**2*w1r**2*rhogeq*k*vgsol - w3i*Kdrag*w3r**2*w1i**2*k*rhogeq*vgsol -& w3i*w3r**2*w1i*k*Kdrag**2*vgsol + w3i*w3r**2*w1i*k*Kdrag**2*vdsol + w3i*Kdrag*w3r**2*w1r*cs**2*k**2*rhogsol +& w3i*Kdrag*w2r*w3r**2*cs**2*k**2*rhogsol + Kdrag*w2i**2*k*rhogeq*vgsol*w3i**2*w1i -& w3i*rhogeq*w3r**4*k*Kdrag*vdsol - w3i*rhogeq*w3r**4*rhogsol*w2i*w1r - w3i*rhogeq*w3r**4*rhogsol*w1i*w2r +& w3i*rhogeq**2*w3r**4*k*vgsol*w2i - w3i*w3r**2*Kdrag*rhogeq*k*vgsol*w2i**2 + w3i*w3r**2*Kdrag*rhogsol*w2r*w1r**2& - w3i*w3r**2*w2i*k*Kdrag**2*vgsol + w3i*w3r**2*w2i*k*Kdrag**2*vdsol + w3i*rhogeq*w3r**4*k*Kdrag*vgsol +& w3i*rhogeq**2*w3r**4*k*vgsol*w1i + w3i*w3r**2*Kdrag*rhogsol*w2r**2*w1r + w3i*w3r**2*Kdrag*rhogsol*w2r*w1i**2 +& w3i*w3r**2*Kdrag*rhogsol*w2i**2*w1r + 2*w3i*w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i -& w3i*w3r**2*Kdrag*rhogeq*k*vgsol*w2r**2 + 2*w3i*w3r**2*rhogeq*cs**2*k**2*rhogsol*w2i*w1r) rhod3r = rhod3r/(w3r**2 +& w3i**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r)/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 -& 2*w2i*w3i + w3r**2)/rhogeq/Kdrag rhod3i = - rhodeq*(w3r*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w1i + w2r*w3i*cs**2*k**3*rhogeq**2*vgsol*w1i**2 +& w2r*w3i*cs**2*k**3*rhogeq**2*vgsol*w1r**2 - w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1i +& rhogeq*w2r**2*w3i**2*cs**2*k**2*rhogsol*w1i - w3i**2*Kdrag*rhogsol*w2i**2*w1r**2 -& w3i**2*Kdrag*rhogsol*w2i**2*w1i**2 - w3r**2*Kdrag*rhogsol*w2i**2*w1i**2 +& rhogeq*w1i*rhogsol*k**2*cs**2*w3i**2*w2i**2 - w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1i**2 -& w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1r**2 - w2r*w3i*cs**4*k**4*rhogeq*rhogsol*w1r -& w2r*w3r*rhogeq*cs**4*k**4*rhogsol*w1i + w2r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1i +& rhogeq*w1i**2*rhogsol*k**2*cs**2*w3i**2*w2i - rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r**2*w2i +& w2r*w3r*rhogeq*cs**2*k**3*Kdrag*vdsol*w1r - w2r*w3r*rhogeq*cs**2*k**3*Kdrag*vgsol*w1r +& w3i*cs**4*k**4*rhogeq*rhogsol*w1i*w2i - w3i*cs**2*k**2*rhogeq*rhogsol*w2i**2*w1r**2 +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r*w2i - w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r*w2i -& rhogeq*w1i*rhogsol*k**2*cs**2*w3r**2*w2i**2 - w3i*cs**2*k**2*rhogeq*rhogsol*w2i**2*w1i**2 +& w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i + w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2i +& w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol*w2i - w3r*rhogeq*cs**4*k**4*w1r*rhogsol*w2i -& w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i + w3r**2*rhogeq*w2i**2*w1r*k*Kdrag*vgsol +& w3r**2*rhogeq*w1r*k*Kdrag*vgsol*w2r**2 - w3r**2*Kdrag*rhogsol*w1i**2*w2r**2 -& w3r**2*Kdrag*rhogsol*w2i**2*w1r**2 - w2r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1i -& w3i**2*Kdrag*rhogsol*w1r**2*w2r**2 - w3i**2*Kdrag*rhogsol*w1i**2*w2r**2 - w3r**2*Kdrag*rhogsol*w1r**2*w2r**2 +& w3i**2*rhogeq*w2i**2*w1r*k*Kdrag*vgsol + w3i**2*rhogeq*w1r*k*Kdrag*vgsol*w2r**2 -& w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2*w2i + w3i**2*cs**2*k**2*rhogeq*rhogsol*w1r**2*w2i +& rhogeq*w3r**4*rhogsol*w2r**2*w1i + rhogeq*w3r**4*rhogsol*w1i*w2i**2 + rhogeq*w3r**4*rhogsol*w2i*w1i**2 +& rhogeq*w3r**4*rhogsol*w2i*w1r**2 - w3r*rhogeq*w2r*w3i**2*w1r*k*Kdrag*vgsol - rhogeq**2*w3i**5*w2r*k*vgsol -& rhogeq**2*w3i**5*k*w1r*vgsol + rhogeq*w3i**5*w2r*w1r*rhogsol - w3i**2*cs**4*k**4*rhogeq*rhogsol*w2i +& 2*w3r**2*rhogeq*w3i**3*cs**2*k**2*rhogsol - 3*w3r*rhogeq*w2i*w3i**2*w1i*k*Kdrag*vgsol -& w3r*rhogeq*w2r*w3i**2*w1r*k*Kdrag*vdsol - 2*w3r**2*Kdrag*k*rhogeq*vgsol*w3i**2*w1r -& 2*w3r**2*Kdrag*k*rhogeq*vgsol*w2r*w3i**2 - 2*w3r**2*rhogeq**2*w2r*k*vgsol*w3i**3 +& 3*w3r*rhogeq**2*cs**2*k**3*w3i**2*w1i*vgsol + 3*w3r*rhogeq*cs**2*k**3*w3i**2*Kdrag*vgsol -& w3r*rhogeq**2*w2i**2*w3i**2*w1i*k*vgsol + 3*w3r*rhogeq**2*cs**2*k**3*w3i**2*w2i*vgsol -& w3r*rhogeq**2*w3i**2*w2r**2*w1i*k*vgsol + 2*w3r*rhogeq*w1i*w3i**3*k*Kdrag*vgsol -& w3r*rhogeq**2*w2i*w1i**2*w3i**2*k*vgsol + 2*w3r*rhogeq*w2i*w3i**3*k*Kdrag*vgsol -& w3r*rhogeq**2*w2i*k*vgsol*w3i**2*w1r**2 - 3*w3r*rhogeq*cs**2*k**3*w3i**2*Kdrag*vdsol -& w3i**3*rhogeq*w2i**2*w1i**2*rhogsol + w3i**2*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r -& w3i**3*rhogeq*w1r**2*w2i**2*rhogsol + w3i**3*rhogeq**2*w2r**2*k*w1r*vgsol + w3i**3*rhogeq**2*w2i**2*w1r*k*vgsol& + w3i**3*rhogeq*cs**4*k**4*rhogsol - rhogeq*w3i**5*w1i*rhogsol*w2i - w3i**4*Kdrag*rhogsol*w2i*w1i +& rhogeq*w3i**5*cs**2*k**2*rhogsol + w3i**4*Kdrag*rhogsol*w2r*w1r + w3i**2*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r +& w3i**4*Kdrag*rhogsol*k**2*cs**2 - w3i**2*cs**4*k**4*rhogeq*rhogsol*w1i - w3i**3*Kdrag**2*k*vgsol*w1r -& w3i**3*rhogeq**2*cs**2*k**3*w2r*vgsol + w3i**3*Kdrag*rhogsol*w2r**2*w1i - w3i**3*Kdrag*rhogsol*k**2*cs**2*w1i -& 2*rhogeq*w3i**4*k*Kdrag*vgsol*w1r - 2*w3r**2*rhogeq*w1i*w3i**3*rhogsol*w2i -& 2*w3r**2*rhogeq**2*w3i**3*k*w1r*vgsol + 2*w3r**2*rhogeq*w2r*w3i**3*w1r*rhogsol +& w3r*rhogeq*w2i*w3i**2*w1i*k*Kdrag*vdsol - w3i**3*rhogeq*w2i*w1r*k*Kdrag*vdsol -& w3i**3*Kdrag*rhogsol*k**2*cs**2*w2i - w3i**2*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r) !--break to avoid too many continuation lines rhod3i = rhod3i - rhodeq*( & w3i**3*Kdrag*rhogsol*w2i*w1i**2 - w3i**2*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r + w3i**3*Kdrag**2*k*vdsol*w1r +& w3i**3*rhogeq**2*w2r*w1i**2*k*vgsol - w3i**3*rhogeq*w2r**2*w1r**2*rhogsol - 2*rhogeq*w3i**4*k*Kdrag*vgsol*w2r -& Kdrag*w3r**4*rhogsol*k**2*cs**2 - w3i**3*Kdrag**2*k*vgsol*w2r - rhogeq*w3r**5*rhogsol*w2i*w1r +& w3i**3*Kdrag*rhogsol*w1i*w2i**2 + w3i**3*rhogeq*w2r*w1i*k*Kdrag*vgsol + Kdrag*w3r**4*w2i*rhogsol*w1i +& w3i**3*rhogeq*w2i*w1r*k*Kdrag*vgsol + rhogeq**2*w3i**4*w3r*w1i*k*vgsol - rhogeq*w3i**4*w3r*k*Kdrag*vdsol +& w3i**3*rhogeq**2*w2r*w1r**2*k*vgsol + w3i**3*Kdrag**2*k*vdsol*w2r + rhogeq*w3i**4*w3r*k*Kdrag*vgsol -& w3i**3*rhogeq**2*cs**2*k**3*w1r*vgsol - rhogeq*w3i**4*w2r*w3r*rhogsol*w1i - w3i**3*rhogeq*w1i**2*w2r**2*rhogsol& - rhogeq*w3i**4*w3r*w2i*w1r*rhogsol - w3i**3*rhogeq*w2r*w1i*k*Kdrag*vdsol - 2*w3r*w3i**3*k*Kdrag**2*vdsol +& 2*w3i**2*rhogeq**2*w3r**3*k*vgsol*w1i + 2*w3i**2*rhogeq*w3r**3*k*Kdrag*vgsol +& w3i**2*w3r*Kdrag*rhogsol*w2r*w1i**2 + w3i**2*w3r*Kdrag*rhogsol*w2r**2*w1r -& w3i**2*w3r*Kdrag*rhogeq*k*vgsol*w2r**2 + w3i**2*w3r*Kdrag*rhogsol*w2i**2*w1r -& 2*w3i**2*w3r*cs**2*k**2*rhogeq*rhogsol*w2r*w1i - 2*w3i**2*w3r*rhogeq*cs**2*k**2*rhogsol*w2i*w1r -& w3i*w3r**2*rhogeq*w1i**2*w2r**2*rhogsol + 2*w3i*w3r*rhogeq*cs**4*k**4*w2r*rhogsol -& w3i*w3r**4*rhogeq*w2i*rhogsol*w1i + w3i*w3r**4*rhogeq*rhogsol*k**2*cs**2 + w3i*w3r**4*rhogeq*w2r*w1r*rhogsol -& w3i*w3r**4*rhogeq**2*w2r*vgsol*k + rhogeq**2*w3i**4*w3r*w2i*k*vgsol - w3i*w3r**4*rhogeq**2*k*w1r*vgsol +& w3i*w3r**2*Kdrag*rhogsol*w2r**2*w1i + w3i*w3r**2*Kdrag*rhogsol*w2i*w1r**2 + w3i*w3r**2*Kdrag*rhogsol*w1i*w2i**2& + w3i*w3r**2*Kdrag**2*k*vdsol*w2r + w3i*w3r**2*rhogeq**2*w2r*w1r**2*k*vgsol +& w3i*w3r**2*rhogeq**2*w2r*w1i**2*k*vgsol + 3*w3i*w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol +& 2*w3i*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol - w3i*w3r**2*Kdrag*rhogsol*k**2*cs**2*w1i +& 3*w3i*w3r**2*rhogeq**2*cs**2*k**3*w2r*vgsol + 2*w3i*w3r*rhogeq*cs**4*k**4*w1r*rhogsol +& 2*w3i*w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol - w3i*w3r**2*Kdrag*rhogsol*k**2*cs**2*w2i -& w3i*w3r**2*rhogeq*w1r**2*w2i**2*rhogsol - w3i*w3r**2*rhogeq*w2i**2*w1i**2*rhogsol +& w3i**2*w3r*w1i*k*Kdrag**2*vdsol - w3i**2*Kdrag*w3r*w1r**2*rhogeq*k*vgsol -& w3i**2*Kdrag*w3r*w1i**2*k*rhogeq*vgsol + w3i**2*Kdrag*w3r*w1r*cs**2*k**2*rhogsol +& w3i**2*Kdrag*w2r*w3r*cs**2*k**2*rhogsol - w3i**2*w3r*w1i*k*Kdrag**2*vgsol -& 2*w3i**2*rhogeq*w3r**3*k*Kdrag*vdsol - 2*w3i**2*rhogeq*w3r**3*rhogsol*w2i*w1r -& 2*w3i**2*rhogeq*w3r**3*rhogsol*w1i*w2r + w3i**2*w3r*Kdrag*rhogsol*w2r*w1r**2 -& w3i**2*w3r*Kdrag*rhogeq*k*vgsol*w2i**2 - 2*w3i*w3r**3*Kdrag**2*k*vdsol - w3i**2*w3r*w2i*k*Kdrag**2*vgsol +& w3i**2*w3r*w2i*k*Kdrag**2*vdsol - w3i*w3r**2*Kdrag**2*k*vgsol*w1r + 2*w3i*w3r**3*rhogeq*w2i*k*Kdrag*vgsol -& 2*w3i*w3r**3*Kdrag*rhogsol*w2i*w1r + w3i*w3r**2*Kdrag**2*k*vdsol*w1r -& 2*w3i*w3r*rhogeq**2*cs**2*k**3*vgsol*w2i**2 - 2*w3i*w3r*rhogeq**2*cs**2*k**3*w2r**2*vgsol -& w3i*w3r**2*rhogeq*w2r**2*w1r**2*rhogsol - 2*w3i*w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol -& 2*w3i*w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol - 3*w3i*w3r**2*rhogeq*cs**4*k**4*rhogsol +& w3i*w3r**2*rhogeq**2*w2r**2*k*w1r*vgsol - w3i*w3r**2*rhogeq*w2r*w1i*k*Kdrag*vdsol -& 2*w3i*w3r**3*Kdrag*rhogsol*w1i*w2r + 2*w3i*w3r**3*rhogeq*w1i*k*Kdrag*vgsol) !--break to avoid too many continuation lines rhod3i = rhod3i - rhodeq*( & w3i*w3r**2*rhogeq*w2r*w1i*k*Kdrag*vgsol + w3i*w3r**2*Kdrag*rhogsol*w2i*w1i**2 +& rhogeq*rhogsol*w2r**2*w1i*w3i**4 - rhogeq*w3r**5*k*Kdrag*vdsol + rhogeq*w3r**3*k*Kdrag*vdsol*w2i*w1i -& rhogeq*w3r**3*k*Kdrag*vdsol*w2r*w1r + 2*rhogeq*w3r**2*rhogsol*w2r**2*w1i*w3i**2 +& w3i*w3r**2*rhogeq*w2i*w1r*k*Kdrag*vgsol + 2*rhogeq*w3r**2*rhogsol*w1i*w2i**2*w3i**2 +& 2*rhogeq*w3r**2*rhogsol*w2i*w1r**2*w3i**2 + 2*rhogeq*w3r**2*rhogsol*w2i*w1i**2*w3i**2 -& 2*Kdrag*w2r*w3r*w3i**3*rhogsol*w1i + rhogeq*w3r**4*k*Kdrag*vdsol*w2r + 2*rhogeq*w3r**2*k*Kdrag*vdsol*w2r*w3i**2& - Kdrag*w3r**3*w1r**2*rhogeq*k*vgsol - Kdrag*w3r**3*w1i**2*k*rhogeq*vgsol + Kdrag*w3r**3*w1r*cs**2*k**2*rhogsol& + Kdrag*w2r*w3r**3*cs**2*k**2*rhogsol + cs**2*k**3*rhogeq*w3r**3*Kdrag*vdsol +& w3i*w3r**2*rhogeq**2*w2i**2*w1r*k*vgsol - rhogeq**2*w3r**3*k*vgsol*w2i*w1r**2 -& rhogeq*rhogsol*k**2*cs**2*w3i**4*w2i + rhogeq**2*w3r**5*k*vgsol*w1i + rhogeq*w3r**5*k*Kdrag*vgsol -& w3i*w3r**2*rhogeq*w2i*w1r*k*Kdrag*vdsol - w3i*w3r**2*Kdrag**2*k*vgsol*w2r + rhogeq*rhogsol*w3i**4*w2i*w1r**2 +& 2*w3i*w3r**3*Kdrag**2*k*vgsol + w3i*rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol + rhogeq*rhogsol*w2i*w1i**2*w3i**4 +& rhogeq*k*Kdrag*vdsol*w2r*w3i**4 + 2*w3i*w1r*w3r*cs**2*k**2*rhogeq*rhogsol*w2i**2 + rhogeq**2*w3r**5*k*vgsol*w2i& + 2*rhogeq*w3r**2*k*Kdrag*vdsol*w3i**2*w1r - 2*w3i*w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol -& rhogeq*w3r**5*rhogsol*w1i*w2r + rhogeq*k*Kdrag*vdsol*w3i**4*w1r - 2*rhogeq*w3r**2*rhogsol*k**2*cs**2*w3i**2*w1i& - 2*w3i*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol - Kdrag*w2r*w3r**4*w1r*rhogsol + w3r**3*Kdrag*rhogsol*w2r**2*w1r& + Kdrag**2*k*vgsol*w2r*w1i*w3i**2 + w3r**2*Kdrag*k*rhogeq*vgsol*w2r*w1i**2 +& w3r**2*Kdrag*k*rhogeq*vgsol*w2r*w1r**2 + w3r**2*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r -& w3r**2*Kdrag*cs**2*k**2*rhogsol*w2r*w1r - w3r**2*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r +& rhogeq**2*cs**2*k**3*w1r*vgsol*w2i**2*w3i + 2*w3r**3*cs**2*k**2*rhogeq*rhogsol*w2r*w1i +& 2*w3i**2*rhogeq**2*w3r**3*k*vgsol*w2i + w3r**3*Kdrag*rhogsol*w2r*w1r**2 - w3r**3*Kdrag*rhogeq*k*vgsol*w2i**2 -& w3r**3*rhogeq**2*cs**2*k**3*vgsol*w1i + 2*w3i*w2r*w3r*cs**2*k**2*rhogeq*rhogsol*w1r**2 -& w3r**3*rhogeq**2*k*vgsol*w2r**2*w1i - w3r**3*Kdrag*rhogeq*k*vgsol*w2r**2 - rhogeq*w3r**4*rhogsol*k**2*cs**2*w2i& + rhogeq*rhogsol*w2i**2*w3i**4*w1i - rhogeq*w3r**4*rhogsol*k**2*cs**2*w1i -& 4*w3i*w3r*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i + w3r**3*w1i*k*Kdrag**2*vdsol - w3r**3*w2i*k*Kdrag**2*vgsol +& rhogeq*w3r**4*k*Kdrag*vdsol*w1r + w3r**3*w2i*k*Kdrag**2*vdsol - rhogeq*rhogsol*k**2*cs**2*w3i**4*w1i -& rhogeq**2*w3r**3*k*vgsol*w2i*w1i**2 - 2*rhogeq*w3r**2*rhogsol*k**2*cs**2*w3i**2*w2i -& w3r**3*w1i*k*Kdrag**2*vgsol - 4*w3i*w2r*w3r*rhogeq**2*cs**2*k**3*w1r*vgsol +& w3r**2*cs**4*k**4*rhogeq*rhogsol*w2i + 2*w3i*w3r*rhogeq*rhogsol*k**2*cs**2*w2r**2*w1r +& 2*w3r*w3i**3*k*Kdrag**2*vgsol - w3r**2*Kdrag**2*k*vdsol*w2i*w1r - Kdrag**2*k*vdsol*w2i*w1r*w3i**2 -& w3r**3*vgsol*k*rhogeq**2*w2i**2*w1i - w3r**3*Kdrag*rhogeq*k*vgsol*w2r*w1r +& 2*w3r**3*rhogeq*cs**2*k**2*rhogsol*w2i*w1r - 3*w3r**3*Kdrag*k*rhogeq*vgsol*w1i*w2i +& w3r**2*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r - w3r**2*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r +& Kdrag**2*k*vgsol*w2i*w1r*w3i**2 + rhogeq**2*cs**2*k**3*vgsol*w1i*w2r**2*w3r - w3r**2*Kdrag**2*k*vdsol*w2r*w1i -& Kdrag**2*k*vdsol*w2r*w1i*w3i**2 + w3r**2*cs**4*k**4*rhogeq*rhogsol*w1i +& w3r**2*Kdrag*cs**2*k**2*rhogsol*w1i*w2i + w3r**3*Kdrag*rhogsol*w2r*w1i**2 -& cs**2*k**3*rhogeq*w3r**3*Kdrag*vgsol + 2*w3i*w2r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r +& 4*cs**2*k**2*rhogeq*w3r**2*w3i*w2i*rhogsol*w1i - 4*cs**2*k**2*rhogeq*w3r**2*w3i*w2r*w1r*rhogsol -& 2*Kdrag*w1r*w2i*w3i**3*rhogsol*w3r + Kdrag*k*rhogeq*vgsol*w2r*w1r**2*w3i**2 + w3r**2*Kdrag**2*k*vgsol*w2r*w1i -& Kdrag*cs**2*k**2*rhogsol*w2r*w1r*w3i**2 + w3i**3*Kdrag*rhogsol*w2i*w1r**2 +& Kdrag*k*rhogeq*vgsol*w2r*w1i**2*w3i**2 + w3r**2*Kdrag**2*k*vgsol*w2i*w1r +& Kdrag*cs**2*k**2*rhogsol*w1i*w2i*w3i**2 + w3r**3*Kdrag*rhogsol*w2i**2*w1r -& w3r**3*vgsol*rhogeq**2*k**3*cs**2*w2i) rhod3i = rhod3i/(w3r**2 + w3i**2)/(w1i**2 - 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 -& 2*w3r*w1r)/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i + w3r**2)/rhogeq/Kdrag rhod2r = - rhodeq*(k*Kdrag**2*vdsol*w2i**3*w1i - k*Kdrag**2*vgsol*w2i**3*w1i -& 2*w3r*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i**2 + w3r*Kdrag*rhogsol*w2i**3*w1i**2 -& Kdrag*k*rhogeq*vgsol*w2i**3*w1i**2 + w3r*Kdrag*rhogsol*w2i**3*w1r**2 - Kdrag*k*rhogeq*vgsol*w2i**3*w1r**2 -& 2*w2i*w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i + w2i*w3r**2*rhogeq**2*k*vgsol*w2r**2*w1i -& w2i*w3r**2*Kdrag*rhogeq*k*vgsol*w2r**2 - w3r*rhogeq**2*cs**2*k**3*w2r**3*vgsol -& w3r*Kdrag*rhogsol*k**2*cs**2*w1i*w2r**2 + w3r*Kdrag**2*k*vdsol*w2r**2*w1r - w3r*Kdrag**2*k*vgsol*w2r**2*w1r +& w3r*Kdrag*k*rhogeq*vgsol*w2r**3*w1i - w3r*rhogeq*cs**2*k**2*w1i**2*w2r**2*rhogsol -& w3r*rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol + 2*w3r*rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol +& w3r**2*rhogeq**2*cs**2*k**3*w2r**2*vgsol + w3r**2*rhogeq*w2r**2*w1i*k*Kdrag*vgsol -& w3r**2*Kdrag*rhogsol*w2r**3*w1i + w3r**2*rhogeq**2*w2r**3*k*w1r*vgsol + w2i*w3r*rhogeq*k*Kdrag*vdsol*w2r**2*w1r& + w2i*w3r*Kdrag*rhogsol*w2r**2*w1r**2 + w2i*w3r*Kdrag*rhogsol*w2r**2*w1i**2 - w2i*w3r*rhogeq*rhogsol*w2r**4*w1i& + 2*w2i*w3r*rhogeq*cs**2*k**2*rhogsol*w2r**2*w1i + 2*w2i*w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r +& w2i*w3r*Kdrag*cs**2*k**2*rhogsol*w2r**2 - 2*w2i*w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r -& 4*w3r*rhogeq*cs**2*k**2*rhogsol*w2r*w2i**2*w1r - w3r*rhogeq*k*Kdrag*vdsol*w2r*w2i**2*w1i +& rhogeq**2*k*vgsol*w2i**5*w1i - w3r*rhogeq*rhogsol*w2i**5*w1i + 4*w3i*cs**2*k**2*rhogeq*rhogsol*w2r*w2i**2*w1i +& 2*w3r**2*rhogeq*rhogsol*w2r**2*w2i**2*w1r - w3r**2*rhogeq*rhogsol*w2r*w2i**2*w1r**2 +& w3r**2*rhogeq**2*k*vgsol*w2r*w2i**2*w1r + 2*vgsol*k*rhogeq**2*w2i**3*w2r**2*w1i +& 2*w3r*rhogeq*cs**2*k**2*rhogsol*w2i**2*w2r**2 + 3*w3r*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w2r -& 2*w3r*Kdrag*rhogsol*w2r*w1r*w2i**3 - 2*w3r*rhogeq*rhogsol*w2i**3*w2r**2*w1i -& w3r**2*rhogeq*rhogsol*w2r*w2i**2*w1i**2 + 2*w3r*rhogeq*rhogsol*w1i**2*w2r**2*w2i**2 +& 2*w3r*rhogeq*rhogsol*w2i**2*w1r**2*w2r**2 + w3r*w2r*Kdrag*rhogeq*k*vgsol*w2i**2*w1i -& 2*w3r**2*rhogeq**2*k*vgsol*w2i**2*w2r**2 + w3r*rhogeq**2*k*vgsol*w2r*w2i**2*w1i**2 +& w3r*rhogeq**2*k*vgsol*w2r*w2i**2*w1r**2 - 4*w3r*rhogeq**2*k*vgsol*w2r**2*w2i**2*w1r -& w3r*rhogeq*cs**4*k**4*w2r**2*rhogsol - w3r**2*rhogeq*w2r**3*w1r**2*rhogsol +& w1r*Kdrag*rhogsol*k**2*cs**2*w2i**3 + w2i*w3r**2*Kdrag*rhogsol*w2r**2*w1r - 2*w2i*w3r*Kdrag*rhogsol*w2r**3*w1r& - 3*w2i*w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1r + w2i*rhogeq**2*k*vgsol*w2r**4*w1i +& rhogeq**2*w1i**2*k*vgsol*w3r*w2r**3 + 2*w2i*w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1r +& w1r*w3i**2*cs**2*k**2*rhogeq*rhogsol*w2i**2 + w1r*w3r**2*cs**2*k**2*rhogeq*rhogsol*w2i**2 +& 2*Kdrag*w3r*w2r**3*k*rhogeq*vgsol*w2i + w3r*rhogeq**2*w1r**2*k*vgsol*w2r**3 - w3r*rhogeq*w2r**5*w1r*rhogsol -& rhogeq*w1i*w3r*w2r**3*k*Kdrag*vdsol + rhogeq*w1i**2*w3r*w2r**4*rhogsol + w3r*rhogeq*w2r**4*w1r**2*rhogsol +& w3r*rhogeq**2*w2r**5*k*vgsol + w3r*w2r**3*k*Kdrag**2*vgsol - w3r*w2r**3*k*Kdrag**2*vdsol +& Kdrag*w3r*w2r**4*rhogsol*w1i + w3r*rhogeq*w2r**4*cs**2*k**2*rhogsol +& w2r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r**2 - w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol +& w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol - w2r*w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol -& w2r*w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol + w2r*w3r*rhogeq*cs**4*k**4*w1r*rhogsol -& w2r*w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol - w2r*w3r**2*Kdrag*rhogsol*w1i*w2i**2 -& rhogeq*w2r*w3i*w1r*k*Kdrag*vdsol*w2i**2 + rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r*w1r -& rhogeq*w2i*w3i*w1i*k*Kdrag*vdsol*w2r**2 - rhogeq*w3i**2*cs**2*k**2*rhogsol*w1r*w2r**2 -& 2*w3r*rhogeq**2*w2r**4*k*w1r*vgsol - 2*w3r*rhogeq*w2r**3*w1r*rhogsol*w2i**2) !--break to avoid too many continuation lines rhod2r = rhod2r - rhodeq*( & 2*w3r*rhogeq**2*w2i**2*k*vgsol*w2r**3 - w3r**2*rhogeq**2*w2r**4*vgsol*k -& w3r**2*rhogeq*rhogsol*k**2*cs**2*w2r**2*w1r + w3r**2*rhogeq*w2r**4*w1r*rhogsol +& w2r*w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2 - w2r*w3r*rhogeq*rhogsol*w2i**4*w1r +& Kdrag*rhogsol*k**2*cs**2*w1i*w2r**3 - rhogeq**2*w2r**4*w3i**2*k*vgsol +& rhogeq*w2r*w3i**2*cs**2*k**2*rhogsol*w1r**2 + rhogeq*cs**4*k**4*w2r**3*rhogsol +& w2r*w3r*rhogeq**2*k*vgsol*w2i**4 + rhogeq*w2r**4*w3i**2*w1r*rhogsol + rhogeq**2*cs**2*k**3*w2r**2*vgsol*w1r**2& - 3*rhogeq*cs**4*k**4*w2r*rhogsol*w2i**2 - 2*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w2r*w1r +& 2*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w2r*w1r - rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r**2 -& 2*rhogeq*w2r*w3i**2*cs**2*k**2*rhogsol*w2i*w1i - rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r*w1r -& rhogeq**2*w1i**2*k*vgsol*w2r**4 + rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r**2 +& 2*rhogeq**2*cs**2*k**3*w3i*w1i*vgsol*w2r**2 - 3*rhogeq**2*cs**2*k**3*w2r**2*vgsol*w2i*w1i +& 2*rhogeq*cs**4*k**4*w2r*rhogsol*w2i*w1i - rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2r**2 -& 3*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w2r**2 - 2*rhogeq**2*w1i**2*k*vgsol*w2r**2*w2i**2 +& rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2r**2 + rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2r**2 +& 3*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w2r**2 - 4*rhogeq**2*w3i*w2r**2*w1i*k*vgsol*w2i**2 -& 2*rhogeq**2*w3i*w2r**4*w1i*k*vgsol + 2*rhogeq*w2r**2*w3i**2*w1r*rhogsol*w2i**2 +& 2*w2r*Kdrag*w3r*w2i**3*k*rhogeq*vgsol + rhogeq*cs**2*k**2*w2r**4*w1r*rhogsol +& rhogeq**2*w2r**3*w3i**2*k*w1r*vgsol - rhogeq**2*cs**2*k**3*w2r**3*w1r*vgsol -& rhogeq*cs**4*k**4*w1r*rhogsol*w2r**2 + w2r*w3r*w2i**2*k*Kdrag**2*vgsol - w2r*w3r*w2i**2*k*Kdrag**2*vdsol -& rhogeq*w2r*w3i**2*w1r**2*rhogsol*w2i**2 + 2*rhogeq*cs**2*k**2*w2r**2*w1r*rhogsol*w2i**2 +& rhogeq**2*w2i*k*vgsol*w3i*w1r**2*w2r**2 - 3*rhogeq**2*cs**2*k**3*w3i*w2i*vgsol*w2r**2 -& 2*Kdrag*k*rhogeq*vgsol*w2r**4*w1i - rhogeq*w2r**3*w3i**2*w1r**2*rhogsol + w3i*vdsol*Kdrag*k*rhogeq*w2r**4 -& vdsol*Kdrag*k*rhogeq*w2i*w2r**4 - 2*w3i*rhogeq*rhogsol*w2r**2*w2i**3*w1r + w3i*rhogeq*rhogsol*w2r*w2i**4*w1i -& w3i*rhogeq*rhogsol*w2i**5*w1r + w3i*rhogeq*rhogsol*w2r**5*w1i - w3i*rhogeq*rhogsol*w2i*w2r**4*w1r +& 2*w3i*rhogeq*rhogsol*w2i**2*w2r**3*w1i - w3i**2*cs**2*k**3*rhogeq**2*vgsol*w2r*w1r +& w3i**2*cs**2*k**3*rhogeq**2*vgsol*w2r**2 - 2*rhogsol*k**2*cs**2*Kdrag*w2i*w2r**3 +& w3i*rhogsol*k**2*cs**2*Kdrag*w2r**3 + w3i*rhogsol*k**2*cs**2*Kdrag*w2r*w2i**2 -& 2*rhogsol*k**2*cs**2*Kdrag*w2r*w2i**3 - w3i*vgsol*k*Kdrag**2*w2i*w2r**2 + vdsol*k*Kdrag**2*w2r**4 -& cs**2*k**2*rhogeq*rhogsol*w2r*w2i**4 + 2*w3i*rhogeq**2*k*vgsol*w2r**2*w2i**3 -& 2*cs**2*k**2*rhogeq*rhogsol*w2r**3*w2i**2 + w3i*rhogeq**2*k*vgsol*w2i*w2r**4 +& 2*w3i*Kdrag*rhogsol*w2r*w2i**3*w1i - cs**2*k**2*rhogeq*rhogsol*w2r**5 + w3i*rhogeq**2*k*vgsol*w2i**5 +& 2*rhogeq**2*k*vgsol*w2i**2*w2r**3*w1r + w3i*Kdrag*rhogsol*w2r**4*w1r + 2*w3i*Kdrag*rhogsol*w2i*w2r**3*w1i +& rhogeq*w2r**2*w3i*w1r**2*k*Kdrag*vgsol + w3i*vdsol*k*Kdrag**2*w2i*w2r**2 - vgsol*k*Kdrag**2*w2r**4 +& 2*vdsol*Kdrag*k*rhogeq*w2i**2*w2r**2*w1i - vdsol*Kdrag*k*rhogeq*w2i**5 + vdsol*Kdrag*k*rhogeq*w2r**4*w1i -& 2*vdsol*Kdrag*k*rhogeq*w2r**2*w2i**3 - w3i*rhogsol*k**4*cs**4*rhogeq*w2r*w1i +& 2*vgsol*k*rhogeq*Kdrag*w2i*w2r**3*w1r + 2*vgsol*k*rhogeq*Kdrag*w2r**2*w2i**3 +& 2*vgsol*k*rhogeq*Kdrag*w2r*w2i**3*w1r + vgsol*k*rhogeq*Kdrag*w2i*w2r**4 - 2*w3i*vgsol*k*rhogeq*Kdrag*w2r**4 +& vgsol*k*rhogeq*Kdrag*w2i**5 - Kdrag*w2r**3*rhogsol*w1i**2*w3i - Kdrag*w2r**2*k*rhogeq*vgsol*w2i*w3i**2 -& Kdrag*w2i**2*rhogsol*w3i*w2r*w1r**2 + Kdrag*w2r**2*rhogsol*k**2*cs**2*w2i*w1r -& Kdrag*w2i**2*rhogsol*w1i**2*w2r*w3i + w2i**2*k*Kdrag**2*vgsol*w2r*w1r - w2r**3*k*Kdrag**2*vdsol*w1r -& w2i**2*k*Kdrag**2*vdsol*w2r*w1r + rhogeq**2*w1i**2*w2r**2*k*vgsol*w2i*w3i +& rhogeq*cs**2*k**2*w1i**2*w2r*rhogsol*w3i**2 + Kdrag*k*rhogeq*vgsol*w2r**2*w1i*w3i**2 +& Kdrag*k*rhogeq*vgsol*w2r**2*w1i**2*w3i + w2r**2*k*Kdrag**2*vgsol*w3i*w1i - w2r**2*k*Kdrag**2*vdsol*w3i*w1i +& rhogeq**2*k*vgsol*w2r**5*w1r - 2*w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w2r*w1i**2 -& 2*w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w2r*w1r**2 - 2*rhogeq**2*k*vgsol*w2i**2*w2r**2*w1r**2) !--break to avoid too many continuation lines rhod2r = rhod2r - rhodeq*( & rhogeq**2*k*vgsol*w2r*w2i**4*w1r - Kdrag*w2r**2*k*rhogeq*vgsol*w2i*w3i*w1i -& Kdrag*w2r**2*k*rhogeq*vgsol*w2i*w1i**2 + Kdrag*w2r**3*k*rhogeq*vgsol*w3i*w1r -& Kdrag*w2i*vgsol*k*rhogeq*w1r**2*w2r**2 + Kdrag*w2i**2*k*rhogeq*vgsol*w2r*w3i*w1r + w2r**3*k*Kdrag**2*vgsol*w1r& - Kdrag*w2r**3*rhogsol*w1i*w3i**2 - Kdrag*w2r**2*rhogsol*k**2*cs**2*w3i*w1r +& rhogeq**2*w1i*w2r**2*k*vgsol*w2i*w3i**2 + Kdrag*w2r**2*rhogsol*w2i*w1r*w3i**2 -& rhogeq*w1i**2*w2r**3*rhogsol*w3i**2 - Kdrag*w2r**3*rhogsol*w3i*w1r**2 - rhogeq*w1i**2*w2i**2*rhogsol*w2r*w3i**2& - Kdrag*w2i**2*rhogsol*w1i*w2r*w3i**2 - w3r**2*rhogeq*w1i**2*w2r**3*rhogsol +& 2*rhogsol*k**4*cs**4*rhogeq*w2r*w2i*w3i + 3*cs**2*k**3*rhogeq**2*vgsol*w2r*w1r*w2i**2 +& 2*rhogeq*w2i**2*k*Kdrag*vdsol*w2r**2*w3i - rhogeq*w2r**3*w3i*w1r*k*Kdrag*vdsol +& Kdrag*rhogsol*k**2*cs**2*w1i*w2r*w2i**2 - 2*Kdrag*k*rhogeq*vgsol*w2r**2*w1i*w2i**2 -& 2*rhogeq**2*w2r**2*w3i**2*k*vgsol*w2i**2 - Kdrag**2*k*vgsol*w2i*w1i*w2r**2 + Kdrag**2*k*vdsol*w2i*w1i*w2r**2 +& w2r*rhogeq**2*vgsol*k*w2i**2*w1r*w3i**2 - 2*Kdrag*w2r**2*k*rhogeq*vgsol*w2i**2*w3i - k*Kdrag**2*vdsol*w2i**4 +& k*Kdrag**2*vgsol*w2i**4 + Kdrag*w2i**2*vgsol*k*rhogeq*w1i*w3r**2 - w3i**2*rhogeq**2*cs**2*k**3*vgsol*w2i**2 +& w2i**2*k*Kdrag**2*vdsol*w3r*w1r - w2i**2*k*Kdrag**2*vdsol*w3i*w1i + w2i**2*k*Kdrag**2*vgsol*w3i*w1i -& Kdrag*rhogsol*k**2*cs**2*w3i*w1r*w2i**2 - w2i**2*k*Kdrag**2*vgsol*w3r*w1r +& w3r**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i - w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i*w1i -& rhogeq**2*vgsol*k*w2i**4*w1r**2 - rhogeq**2*vgsol*k*w2i**4*w1i**2 - w3r**2*vgsol*k*rhogeq*Kdrag*w2i**3 -& w3r*Kdrag*rhogsol*w2i**4*w1i + cs**2*k**3*rhogeq*vgsol*Kdrag*w2i**3 + w3r**2*rhogeq**2*vgsol*k*w2i**3*w1i +& cs**2*k**3*rhogeq**2*vgsol*w2i**3*w1i + w3r*vdsol*Kdrag*k*rhogeq*w2i**3*w1r - w3i*vgsol*k*Kdrag**2*w2i**3 +& w3i*vdsol*k*Kdrag**2*w2i**3 - w3i**2*vgsol*k*rhogeq*Kdrag*w2i**3 - w3i*vgsol*k*rhogeq*Kdrag*w2i**3*w1i -& 3*w3r*vgsol*k*rhogeq*Kdrag*w2i**3*w1r - w3i*Kdrag*rhogsol*w2i**4*w1r + w3r*rhogsol*k**2*cs**2*Kdrag*w2i**3 +& w3r**2*rhogeq*rhogsol*w2i**4*w1r + w3i**2*rhogeq*rhogsol*w2i**4*w1r + w3i**2*Kdrag*rhogsol*w2i**3*w1r +& w3r**2*Kdrag*rhogsol*w2i**3*w1r - 2*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w1i +& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**3 + w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1r**2 +& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**2 - cs**2*k**3*rhogeq*vdsol*Kdrag*w2i**3 -& 2*w3r*rhogeq**2*k*vgsol*w2i**4*w1r - w3r**2*rhogeq**2*vgsol*k*w2i**4 + w3r*cs**2*k**2*rhogeq*rhogsol*w2i**4 -& w3i**2*rhogeq**2*vgsol*k*w2i**4 - w3i*vdsol*Kdrag*k*rhogeq*w2i**3*w1i + w3i*vdsol*Kdrag*k*rhogeq*w2i**4 +& rhogeq*rhogsol*k**2*cs**2*w2i**4*w1r + rhogeq*k*Kdrag*vdsol*w2i**4*w1i - 2*w3i*rhogeq**2*k*vgsol*w2i**4*w1i +& w3i*rhogeq**2*k*vgsol*w2i**3*w1i**2 + w3i*rhogeq**2*k*vgsol*w2i**3*w1r**2 -& 2*w3r*cs**2*k**2*rhogeq*rhogsol*w2i**3*w1i + w3i**2*rhogeq**2*vgsol*k*w2i**3*w1i +& w3r*rhogeq*cs**2*k**2*w1r**2*rhogsol*w2i**2 + w3r*rhogeq*cs**2*k**2*w1i**2*rhogsol*w2i**2 +& w3r*rhogeq*cs**4*k**4*rhogsol*w2i**2 - w3r*Kdrag*rhogsol*k**2*cs**2*w1i*w2i**2 -& w3r**2*rhogeq**2*cs**2*k**3*vgsol*w2i**2 - w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w1r -& w3r*rhogeq*cs**4*k**4*rhogsol*w2i*w1i + rhogeq*cs**4*k**4*w1r*rhogsol*w2i**2 -& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i**2 + w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i**2 -& rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i**2 + rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i**2 +& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w1r - rhogeq**2*cs**2*k**3*w1r**2*vgsol*w2i**2 -& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w1r - rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2i**2 +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i*w1i + Kdrag*w2i**2*k*rhogeq*vgsol*w3i*w1i**2 +& w3i**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i - 2*w3i*cs**2*k**2*rhogeq*rhogsol*w2i**3*w1r +& w3r*rhogeq*rhogsol*w2i**4*w1r**2 + w3r*rhogeq*rhogsol*w2i**4*w1i**2 - rhogeq**2*k*vgsol*w2r**4*w1r**2 +& Kdrag*w2i**2*k*rhogeq*vgsol*w3i**2*w1i + Kdrag*w2i**2*k*rhogeq*vgsol*w3i*w1r**2) rhod2r = rhod2r/(w2i**2 +& w2r**2)/rhogeq/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i + w3r**2)/(w2r**2 + w1r**2 + w2i**2 -& 2*w2i*w1i - 2*w2r*w1r + w1i**2)/Kdrag rhod2i =rhodeq*( - w3r**2*Kdrag*rhogsol*w1i*w2i**3 - w2r*w3i*vdsol*k*Kdrag**2*w2i**2 +& w2r*w3i**2*vgsol*k*rhogeq*Kdrag*w2i**2 + 3*w2r*w3i*vgsol*k*rhogeq*Kdrag*w2i**2*w1i +& w2r*w3r*vgsol*k*rhogeq*Kdrag*w2i**2*w1r + w2r*w3r**2*vgsol*k*rhogeq*Kdrag*w2i**2 -& 3*w2r*cs**2*k**3*rhogeq*vgsol*Kdrag*w2i**2 - w2r*w3r*rhogsol*k**2*cs**2*Kdrag*w2i**2 +& 2*w2r*w3i*Kdrag*rhogsol*w2i**3*w1r - w2r*w3i*cs**2*k**3*rhogeq**2*vgsol*w1i**2 +& 3*w2r*cs**2*k**3*rhogeq*vdsol*Kdrag*w2i**2 - w2r*w3r**2*Kdrag*rhogsol*w2i**2*w1r -& 3*w2r*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**2 - 2*Kdrag*k*rhogeq*vgsol*w2r**3*w1i*w2i +& 4*w2r*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i - w2r*w3i**2*Kdrag*rhogsol*w2i**2*w1r -& w2r*w3i*cs**2*k**3*rhogeq**2*vgsol*w1r**2 - w2r*w3i*vdsol*Kdrag*k*rhogeq*w2i**2*w1i +& w2r*w3i*vgsol*k*Kdrag**2*w2i**2 + Kdrag*vgsol*k*rhogeq*w1r**2*w2r**3 - Kdrag*w2i*k*rhogeq*vgsol*w2r**2*w3i*w1r& - Kdrag*w2r**3*rhogsol*w1r*w3i**2 - 3*cs**2*k**3*rhogeq**2*vgsol*w2r**2*w1r*w2i +& Kdrag*rhogsol*k**2*cs**2*w1i*w2r**2*w2i - w3i**2*Kdrag*rhogsol*w1i*w2i**3 +& 4*w2r*w3r*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i - w2r*w3r*Kdrag*rhogsol*w2i**2*w1i**2 -& Kdrag**2*k*vdsol*w1i*w2r**3 - w2r**2*rhogeq**2*vgsol*k*w2i*w1r*w3i**2 -& 2*w2r*w1r*w3r**2*cs**2*k**2*rhogeq*rhogsol*w2i - w2r*k*Kdrag**2*vdsol*w2i**2*w1i +& w2r*w3r*rhogeq*rhogsol*w2i**4*w1i - 2*w2r*w1r*w3i**2*cs**2*k**2*rhogeq*rhogsol*w2i +& w2r*Kdrag*k*rhogeq*vgsol*w2i**2*w1i**2 - w2r*rhogeq**2*k*vgsol*w2i**4*w1i - w2r*w3r*Kdrag*rhogsol*w2i**2*w1r**2& + w2r*k*Kdrag**2*vgsol*w2i**2*w1i - 2*Kdrag*w2r**3*k*rhogeq*vgsol*w2i*w3i + Kdrag**2*k*vgsol*w1i*w2r**3 +& rhogeq*w1i**2*w2i*rhogsol*w2r**2*w3i**2 - Kdrag*w2i*rhogsol*w1i*w2r**2*w3i**2 -& rhogsol*k**4*cs**4*rhogeq*w2r**2*w3i + rhogeq**2*w1i*w2r**3*k*vgsol*w3i**2 +& w3r**2*rhogeq**2*k*vgsol*w2r**3*w1i + w3r**2*Kdrag*rhogeq*k*vgsol*w2r**3 + w2r*w3i*rhogeq*rhogsol*w2i**4*w1r -& w2r*w3i*rhogeq**2*k*vgsol*w2i**4 + w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1i +& w2r*vdsol*Kdrag*k*rhogeq*w2i**4 - w2r*vgsol*k*rhogeq*Kdrag*w2i**4 + 2*w2r*w3i**2*rhogeq**2*cs**2*k**3*vgsol*w2i& + w2r*Kdrag*k*rhogeq*vgsol*w2i**2*w1r**2 - w2r*w1r*Kdrag*rhogsol*k**2*cs**2*w2i**2 -& 2*vgsol*k*rhogeq**2*w2i**2*w2r**3*w1i - 3*w3r*cs**2*k**3*rhogeq**2*vgsol*w2i*w2r**2 +& w3r*rhogeq*k*Kdrag*vdsol*w2r**3*w1r - w3r*Kdrag*rhogsol*w2r**3*w1r**2 - w3r*Kdrag*rhogsol*w2r**3*w1i**2 +& w3r*rhogeq*rhogsol*w2r**5*w1i - 4*w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w2i*w1i +& w3r**2*rhogeq*rhogsol*w2r**2*w2i*w1r**2 - w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r**2 +& w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r**2 + w3r*rhogeq*k*Kdrag*vdsol*w2r**2*w2i*w1i -& w3r*Kdrag*cs**2*k**2*rhogsol*w2r**3 - 2*w3r*rhogeq*cs**2*k**2*rhogsol*w2r**3*w1i +& 4*w3r*rhogeq*cs**2*k**2*rhogsol*w2r**2*w2i*w1r - w3r**2*rhogeq**2*k*vgsol*w2r**2*w2i*w1r -& w3r*rhogeq**2*k*vgsol*w2r**2*w2i*w1r**2 - w3r**2*Kdrag*rhogsol*w2r**3*w1r + w3r*Kdrag*rhogsol*w2r**4*w1r +& w3r*Kdrag*k*rhogeq*vgsol*w2r**3*w1r - 2*w2r*k*Kdrag**2*vgsol*w2i**3 + rhogeq*w2r**2*w3i*w1r*k*Kdrag*vdsol*w2i -& rhogeq*w3i*w1i*k*Kdrag*vdsol*w2r**3 + 2*w2r**2*w3r*rhogeq**2*k*vgsol*w2i**3 -& 2*w3i*cs**2*k**2*rhogeq*rhogsol*w2r**3*w1r - w3r*rhogeq**2*k*vgsol*w2r**2*w2i*w1i**2 -& rhogeq**2*k*vgsol*w2r**5*w1i - w2r**2*w3r**2*Kdrag*rhogsol*w1i*w2i + 2*w3r*rhogeq*rhogsol*w2i**2*w2r**3*w1i +& w3r**2*rhogeq*rhogsol*w2r**2*w2i*w1i**2 - w3r*w2r**2*Kdrag*rhogeq*k*vgsol*w2i*w1i +& rhogeq**2*cs**2*k**3*w2r**3*vgsol*w1i + 3*rhogeq*cs**4*k**4*w2r**2*rhogsol*w2i +& rhogeq*cs**2*k**3*Kdrag*vdsol*w2r**2*w1r - rhogeq*cs**2*k**3*Kdrag*vgsol*w2r**2*w1r -& 2*w2r**2*w3r*rhogeq*rhogsol*w2i**3*w1r - w3r*rhogeq*w2r**4*w1r*rhogsol*w2i + w3r*rhogeq**2*w2i*k*vgsol*w2r**4 +& 2*w2r*k*Kdrag**2*vdsol*w2i**3 - rhogeq*cs**2*k**3*Kdrag*vdsol*w2r**3 - w2r**2*w3r*w2i*k*Kdrag**2*vdsol +& 2*w2r**2*Kdrag*w3r*w2i**2*k*rhogeq*vgsol + rhogeq*w2r**2*w3i**2*w1r**2*rhogsol*w2i +& rhogeq**2*k*vgsol*w3i*w1r**2*w2r**3 - rhogeq*cs**4*k**4*w2r**2*rhogsol*w1i) !--break to avoid too many continuation lines rhod2i = rhod2i + rhodeq*(& rhogeq*cs**2*k**3*Kdrag*vgsol*w2r**3 + rhogeq*w2r**2*w3i**2*cs**2*k**2*rhogsol*w1i +& w3i**2*Kdrag*rhogsol*w2i**2*w1r**2 + rhogeq**2*cs**2*k**3*w3i*vgsol*w2r**3 +& 2*w3i*rhogeq*rhogsol*w2r**2*w2i**3*w1i + w3i*rhogeq*rhogsol*w2r**5*w1r + w3i*rhogeq*rhogsol*w2i*w2r**4*w1i +& w2r**2*w3r*w2i*k*Kdrag**2*vgsol + rhogsol*k**2*cs**2*Kdrag*w2r**4 - 2*w3i*rhogeq**2*k*vgsol*w2r**3*w2i**2 -& cs**2*k**2*rhogeq*rhogsol*w2r**4*w2i - 2*cs**2*k**2*rhogeq*rhogsol*w2r**2*w2i**3 - w3i*rhogeq**2*k*vgsol*w2r**5& + rhogeq**2*k*vgsol*w2i*w2r**4*w1r + vdsol*Kdrag*k*rhogeq*w2r**5 + 2*w3i*rhogeq*rhogsol*w2r**3*w2i**2*w1r +& w3i**2*Kdrag*rhogsol*w2i**2*w1i**2 + 2*vdsol*Kdrag*k*rhogeq*w2r**3*w2i**2 +& 2*vgsol*k*rhogeq*Kdrag*w2r**2*w2i**2*w1r + w3i*rhogsol*k**2*cs**2*Kdrag*w2r**2*w2i +& w3i*vgsol*k*Kdrag**2*w2r**3 - w2i*k*Kdrag**2*vdsol*w2r**2*w1r + rhogeq**2*w1i**2*w2r**3*k*vgsol*w3i -& w3i*vdsol*k*Kdrag**2*w2r**3 - w3i*Kdrag*rhogsol*w2r**4*w1i - 2*vgsol*k*rhogeq*Kdrag*w2r**3*w2i**2 -& Kdrag*w2i*rhogsol*w1i**2*w2r**2*w3i + w2i*k*Kdrag**2*vgsol*w2r**2*w1r + 3*Kdrag*w2r**3*k*rhogeq*vgsol*w3i*w1i +& Kdrag*w2r**3*k*rhogeq*vgsol*w1i**2 + w3r**2*Kdrag*rhogsol*w2i**2*w1i**2 -& rhogeq*w1i*rhogsol*k**2*cs**2*w3i**2*w2i**2 + w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1i**2 +& w3i*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1r**2 + 2*rhogeq**2*k*vgsol*w2r**2*w2i**3*w1r -& vgsol*k*rhogeq*Kdrag*w2r**5 + Kdrag*w2r**3*k*rhogeq*vgsol*w3i**2 - Kdrag*w2i*rhogsol*w3i*w2r**2*w1r**2 -& Kdrag*w2r**3*rhogsol*k**2*cs**2*w1r + 2*Kdrag*w2i**4*vgsol*k*rhogeq*w1r - 2*Kdrag*w2i**3*w2r*vgsol*k*rhogeq*w1i& + Kdrag*w2i**4*rhogsol*w3i*w1i + rhogeq*w1i*w2r**4*rhogsol*k**2*cs**2 +& 2*rhogeq*w1i*w2r**2*rhogsol*k**2*cs**2*w2i**2 - Kdrag*w3r*w2i**4*rhogsol*w1r +& w2r*w3i*cs**4*k**4*rhogeq*rhogsol*w1r + rhogeq*w1i*w3r*w2i**3*k*Kdrag*vdsol +& 2*Kdrag*w2r**3*rhogsol*w3i*w1r*w2i - 2*w2r*w3r*rhogeq*cs**4*k**4*rhogsol*w2i +& 2*w2r*w3r**2*rhogeq**2*cs**2*k**3*vgsol*w2i + rhogeq*w1i*w2i**4*rhogsol*k**2*cs**2 +& w2r*w3i*rhogeq**2*k*vgsol*w2i**2*w1r**2 + 2*w2r*w3r*cs**2*k**2*rhogeq*rhogsol*w2i**2*w1i -& 2*w2r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i + 2*w2r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i -& 2*w2r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i + w2r*w3r*rhogeq*cs**4*k**4*rhogsol*w1i +& 2*w2r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i - w2r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1i -& 2*w2r*rhogeq*cs**4*k**4*w1r*rhogsol*w2i + w2r*w3i*rhogeq**2*k*vgsol*w2i**2*w1i**2 +& w2r*w3i**2*rhogeq**2*vgsol*k*w2i**2*w1i - 2*w2r*w3r*rhogeq*cs**2*k**2*w1r**2*rhogsol*w2i -& 2*w2r*w3r*rhogeq*cs**2*k**2*w1i**2*rhogsol*w2i - w2r*w3i**2*rhogeq**2*cs**2*k**3*vgsol*w1i +& 2*w2r*w3i*cs**2*k**2*rhogeq*rhogsol*w2i**2*w1r - rhogeq**2*w1i**2*k*vgsol*w3r*w2i**3 +& rhogeq*w1i**2*rhogsol*k**2*cs**2*w3i**2*w2i + rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r**2*w2i -& w2r*w3r*rhogeq*cs**2*k**3*Kdrag*vdsol*w1r + 2*w2r*rhogeq**2*cs**2*k**3*w1r**2*vgsol*w2i +& w2r*w3r*rhogeq*cs**2*k**3*Kdrag*vgsol*w1r + 2*w2r*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2i -& w3i*cs**4*k**4*rhogeq*rhogsol*w1i*w2i - w3i*cs**2*k**2*rhogeq*rhogsol*w2i**2*w1r**2 +& w3r*rhogeq**2*cs**2*k**3*w2i**3*vgsol - w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r*w2i +& rhogeq**2*cs**2*k**3*w2i**3*w1r*vgsol + w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r*w2i -& rhogeq*w1i*rhogsol*k**2*cs**2*w3r**2*w2i**2 - w3r*w2i**3*k*Kdrag**2*vdsol +& w3r*Kdrag*rhogsol*k**2*cs**2*w1r*w2r**2 - w3r*Kdrag*k*rhogeq*vgsol*w2i**2*w1r**2 -& w3r*Kdrag*k*rhogeq*vgsol*w1r**2*w2r**2 - w3i*cs**2*k**2*rhogeq*rhogsol*w2i**2*w1i**2) !--break to avoid too many continuation lines rhod2i = rhod2i + rhodeq*( & w3i*cs**4*k**4*rhogeq*rhogsol*w2i**2 + rhogeq*cs**4*k**4*w2i**2*w1i*rhogsol -& w3r*Kdrag*k*rhogeq*vgsol*w1i**2*w2r**2 + w3r*Kdrag**2*k*vdsol*w2i**2*w1i + w3r*Kdrag**2*k*vdsol*w1i*w2r**2 +& w3r*Kdrag*rhogsol*k**2*cs**2*w2i**2*w1r - w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i -& w3r*rhogeq*cs**2*k**3*w2i**2*Kdrag*vdsol + w3r**2*rhogeq*w2i**3*w1i**2*rhogsol - 2*w2i*k*Kdrag**2*vgsol*w2r**3& - w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2i - w3r*Kdrag**2*k*vgsol*w2i**2*w1i - w3r*Kdrag**2*k*vgsol*w1i*w2r**2& - w3r*Kdrag*k*rhogeq*vgsol*w1i*w2i**3 - w3r*Kdrag*k*rhogeq*vgsol*w2i**2*w1i**2 +& w3r**2*rhogeq*w1r**2*w2i**3*rhogsol - w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol*w2i +& w3r*rhogeq*cs**4*k**4*w1r*rhogsol*w2i - w3r**2*rhogeq**2*w2i**3*w1r*k*vgsol -& w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i + w3r*rhogeq*cs**2*k**3*w2i**2*Kdrag*vgsol +& w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i - w3r**2*rhogeq*w2i**2*w1r*k*Kdrag*vgsol -& w3r**2*rhogeq*w1r*k*Kdrag*vgsol*w2r**2 + w3r**2*Kdrag*rhogsol*w1i**2*w2r**2 +& w3r**2*Kdrag*rhogsol*w2i**2*w1r**2 - Kdrag*rhogsol*k**2*cs**2*w3i*w1i*w2r**2 -& Kdrag*k*rhogeq*vgsol*w3i*w1r*w2i**3 - Kdrag*rhogsol*w1i**2*w2i**3*w3i +& w2r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1i - Kdrag*rhogsol*k**2*cs**2*w2i**2*w3i*w1i -& Kdrag**2*k*vgsol*w2i**2*w3i*w1r + w3i**2*Kdrag*rhogsol*w1r**2*w2r**2 + w3i**2*Kdrag*rhogsol*w1i**2*w2r**2 +& w3r**2*Kdrag*rhogsol*w1r**2*w2r**2 + w2r*w3r**2*rhogeq**2*vgsol*k*w2i**2*w1i -& 3*w2r*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w1i + w2r*w3r*vdsol*Kdrag*k*rhogeq*w2i**2*w1r -& w2r*w3r**2*rhogeq**2*cs**2*k**3*vgsol*w1i + 2*w2r*w3r*Kdrag*rhogsol*w2i**3*w1i -& w3i**2*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i - rhogeq*cs**2*k**3*w2i**2*w1r*Kdrag*vdsol -& Kdrag*rhogsol*w2i**3*w1r**2*w3i - w3i**2*rhogeq*w2i**2*w1r*k*Kdrag*vgsol -& w3i**2*rhogeq*w1r*k*Kdrag*vgsol*w2r**2 - rhogeq*w1r*k*Kdrag*vdsol*w2r**4 -& 2*rhogeq*w2i**2*w1r*k*Kdrag*vdsol*w2r**2 - 2*rhogeq*w1i*w3i**2*rhogsol*w2i**2*w2r**2 -& rhogeq*w1i*w3i**2*rhogsol*w2r**4 - 2*w3i*rhogeq*rhogsol*w2i**2*w1r**2*w2r**2 -& w3r*rhogeq**2*k*vgsol*w2i**3*w1r**2 - w3i*rhogeq*rhogsol*w1r**2*w2r**4 +& 2*w3i*rhogeq*rhogsol*k**2*cs**2*w2i**2*w2r**2 + w3i*rhogeq*rhogsol*k**2*cs**2*w2r**4 +& w3i**2*rhogeq*w1r**2*w2i**3*rhogsol - w3r**2*rhogeq*rhogsol*w2i**4*w1i -& 2*w3r**2*rhogeq*rhogsol*w2i**2*w1i*w2r**2 - w3i**2*rhogeq*rhogsol*w2i**4*w1i +& w3i**2*rhogeq*w2i**3*w1i**2*rhogsol - rhogeq*rhogsol*k**2*cs**2*w2i**5 +& w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2*w2i - w3i*rhogeq*rhogsol*w2i**4*w1r**2 +& w3i**2*cs**2*k**2*rhogeq*rhogsol*w1r**2*w2i + w3i*cs**2*k**2*rhogeq*rhogsol*w2i**4 +& 2*w2i*k*Kdrag**2*vdsol*w2r**3 - w3r*rhogeq*rhogsol*w2i**5*w1r - rhogeq*w1i**2*w2i**4*rhogsol*w3i -& cs**4*k**4*rhogeq*rhogsol*w2i**3 + w3r*w2i**3*k*Kdrag**2*vgsol - rhogeq*k*Kdrag*vdsol*w2i**4*w1r +& w3i*vdsol*Kdrag*k*rhogeq*w2i**3*w1r + w3i*rhogeq*rhogsol*w2i**5*w1i + w3r*rhogeq**2*k*vgsol*w2i**5 -& w3r*vdsol*Kdrag*k*rhogeq*w2i**4 - 2*w3r*vdsol*Kdrag*k*rhogeq*w2i**2*w2r**2 -& 2*Kdrag*w2i**3*k*rhogeq*vgsol*w2r*w3i - w2i**3*k*Kdrag**2*vdsol*w1r + w2i**3*k*Kdrag**2*vgsol*w1r +& 2*Kdrag*w3r*w2r**3*rhogsol*w1i*w2i + Kdrag*w2i**3*rhogsol*k**2*cs**2*w3i -& 2*rhogeq*w1i**2*w2i**2*rhogsol*w3i*w2r**2 - rhogeq*w1i**2*w2r**4*rhogsol*w3i - Kdrag*w2i**4*rhogsol*k**2*cs**2& + Kdrag*w2i**3*rhogsol*k**2*cs**2*w1i - w3r**2*rhogeq*rhogsol*w1i*w2r**4 - w3r*rhogeq*k*Kdrag*vdsol*w2r**4 +& 2*Kdrag*w3r*w2i**4*k*rhogeq*vgsol - w3i**2*rhogeq**2*w2i**3*w1r*k*vgsol + Kdrag**2*k*vdsol*w2i**2*w3i*w1r +& Kdrag**2*k*vdsol*w3i*w1r*w2r**2 - Kdrag**2*k*vgsol*w3i*w1r*w2r**2 + rhogeq*cs**2*k**3*w2i**2*w1r*Kdrag*vgsol +& rhogeq**2*w1r*w2i**5*k*vgsol) rhod2i = rhod2i/(w2i**2 + w2r**2)/rhogeq/(w2r**2 - 2*w3r*w2r + w2i**2 + w3i**2 - 2*w2i*w3i +& w3r**2)/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r + w1i**2)/Kdrag rhod1r =(2*w2i*w3r*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r*w3i**2 - w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w1r*w3i**2 +& rhogeq*w2r*w3i*w1r*k*Kdrag*vdsol*w2i**2*w3r**2 + w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w1r*w3i**2 -& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i*w1i*w3r**2 - Kdrag*w2i**2*k*rhogeq*vgsol*w2r*w3i*w1r*w3r**2 +& Kdrag*w2r**2*k*rhogeq*vgsol*w2i*w3i*w1i*w3r**2 + 4*w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w2r*w1i**2*w3r**2 -& rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r*w1r*w3r**2 + rhogeq*w2i*w3i*w1i*k*Kdrag*vdsol*w2r**2*w3r**2 +& w2i*w3r*rhogeq*k*Kdrag*vdsol*w2r**2*w1r*w3i**2 - 4*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w2r*w1r*w3r**2 +& 4*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w2r*w1r*w3r**2 + w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w3i**2 +& 2*rhogeq*cs**2*k**3*w3i**2*Kdrag*vgsol*w2r*w1r*w1i - 2*w2i*w3i**2*cs**2*k**2*rhogeq*rhogsol*w2r**2*w1r*w1i +& w1i**2*rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r*w1r - w1i**2*rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r*w1r +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i*w1i*w3r**2 - w3r*rhogeq*k*Kdrag*vdsol*w2r*w2i**2*w1i*w3i**2 +& 4*w3r*w2i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r*w3i*w1i - 2*rhogeq*cs**2*k**3*w3i**2*Kdrag*vdsol*w2r*w1r*w1i -& 2*w2r*rhogeq**2*cs**2*k**3*w1r*vgsol*w3i*w1i*w2i**2 - 4*w2i*w3r**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i*w3i**2 +& 3*w3r*w2r*Kdrag*rhogeq*k*vgsol*w2i**2*w1i*w3i**2 - w2i*w3r*Kdrag*k*rhogeq*vgsol*w2r**2*w1r*w3i**2 -& w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w3i**2 + rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r*w1r*w3r**2 +& 2*w3r*w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w2r**2 - 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r*w2i**2 -& 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r**3 - 4*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w1r*w2r**2 +& 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r**3 + 2*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r*w2i**2 +& 4*w3r*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w1r*w2r**2 + 2*w3r*w3i*cs**4*k**4*rhogeq*rhogsol*w2i**3 +& 4*w2r*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i*w1r*w3i**2 - 4*rhogeq**2*k*vgsol*w2r*w2i**2*w1r*w3i**3*w1i -& w3r*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i**4 - 2*w3r*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i**2*w2r**2 -& 2*w3r**3*Kdrag*rhogsol*w2r**2*w1i*w2i**2 - rhogeq*w3i**3*w2i*w1r*rhogsol*w2r**2*w1i**2 +& 2*w1r**2*rhogsol*k**4*cs**4*rhogeq*w2r*w2i*w3i - rhogeq*w3i**3*k*Kdrag*vdsol*w2r**2*w1i**2 +& w1i**2*rhogeq**2*k*vgsol*w2r*w2i**2*w1r*w3i**2 + w1i**3*w2i*rhogeq**2*k*vgsol*w2r**2*w3i**2 -& 2*w2r*rhogeq*w1i**3*rhogsol*k**2*cs**2*w3i**2*w2i + w1i**2*rhogeq**2*w2r**3*k*w1r*vgsol*w3i**2 -& 2*w2r*cs**2*k**2*rhogeq*rhogsol*w1r**2*w3i**2*w1i*w2i - 2*Kdrag*k*rhogeq*vgsol*w2r**3*w1i*w1r*w3i**2 -& 2*w2r*Kdrag*rhogeq*k*vgsol*w2i**2*w1i*w1r*w3i**2 + w1r**3*rhogeq**2*k*vgsol*w2r*w2i**2*w3i**2 -& 2*w1r**3*w2r*rhogeq**2*cs**2*k**3*vgsol*w2i*w3i - 2*w3i**2*cs**2*k**3*rhogeq**2*vgsol*w2r**2*w2i**2 -& w1r**3*w2r*rhogeq**2*cs**2*k**3*vgsol*w3i**2 - w3i*rhogsol*k**4*cs**4*rhogeq*w2r*w1i*w2i**2 -& w3i*rhogsol*k**4*cs**4*rhogeq*w2r**3*w1i - w2i*rhogeq*k*Kdrag*vdsol*w2r**2*w1r**2*w3i**2 +& w1r**3*rhogeq**2*w2r**3*k*vgsol*w3i**2 - w3i**2*rhogeq*k*Kdrag*vdsol*w2r**2*w1i**2*w2i -& w3i**2*cs**2*k**3*rhogeq**2*vgsol*w2r**4 + 2*rhogeq**2*cs**2*k**3*w3i*w1i*vgsol*w2r**2*w2i**2 -& rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r**3*w1r - 2*w3r*w3i**2*Kdrag*rhogsol*w2r**2*w1i*w2i**2 -& rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r*w1r*w2i**2 + rhogeq**2*cs**2*k**3*w3i*w1i*vgsol*w2r**4) !--break to avoid too many continuation lines rhod1r = rhod1r + (& 2*w2r*w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2*w2i**2 + 2*w2r**3*w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2 -& 2*rhogeq*w2r*w3i**2*cs**2*k**2*rhogsol*w1r**2*w2i**2 - 2*rhogeq*w2r**3*w3i**2*cs**2*k**2*rhogsol*w1r**2 +& rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r*w1r*w2i**2 + rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r**3*w1r +& w2r*w3r*rhogeq*cs**4*k**4*w1r*rhogsol*w2i**2 + w2r**3*w3r*rhogeq*cs**4*k**4*w1r*rhogsol +& w2r*w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol*w2i**2 + w2r**3*w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol +& w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i**2 + w2r**3*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol -& w2r**3*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol - w2r*w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2i**2 -& w2r**3*w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol - w2r*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i**2 +& 4*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w2i**3*w1i**2 + 4*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w2i*w1i**2*w2r**2 -& w3r*rhogeq**2*cs**2*k**3*w2r**4*w1r*vgsol + 2*w3r**2*rhogeq**2*cs**2*k**3*w2r**2*vgsol*w2i**2 +& w3r**2*rhogeq**2*cs**2*k**3*w2r**4*vgsol + w3r*rhogeq*cs**2*k**2*w2r**4*w1r**2*rhogsol +& 2*w3r*rhogeq*cs**2*k**2*w1i**2*w2r**2*rhogsol*w2i**2 - 4*w3r*w3i*cs**4*k**4*rhogeq*rhogsol*w1i*w2i**2 +& w3r*rhogeq*cs**2*k**2*w1i**2*w2r**4*rhogsol + 2*w3r*rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol*w2i**2 -& 2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w2r**4*w1i - 2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w1i*w2i**4 -& 4*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w1i*w2i**2*w2r**2 - w3i**3*Kdrag*rhogeq*k*vgsol*w2i**4 +& 2*w3i**3*Kdrag*rhogsol*w2r**2*w1r*w2i**2 + w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i**3*w1i +& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i*w1i*w2r**2 - w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w1r*w2r**2 -& w3r*rhogeq*cs**4*k**4*rhogsol*w2i**3*w1i + w3r*rhogeq*cs**2*k**3*w2i**3*Kdrag*vdsol*w1r +& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w1r*w2r**2 - w3r*rhogeq*cs**2*k**3*w2i**3*Kdrag*vgsol*w1r +& w3r*rhogeq*cs**2*k**2*w1i**2*rhogsol*w2i**4 + w3r**2*rhogeq**2*cs**2*k**3*vgsol*w2i**4 -& w3i*cs**4*k**4*rhogeq*rhogsol*w2i**3*w1r - w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w1r*w2r**2 -& w3r*rhogeq*cs**4*k**4*rhogsol*w2i*w1i*w2r**2 + w3r*rhogeq*cs**2*k**2*w1r**2*rhogsol*w2i**4 +& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**4*w1i + w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**3*w1r**2 +& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1r**2*w2r**2 - w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**3*w1i**2 -& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**2*w2r**2 - w3i**2*rhogeq**2*cs**2*k**3*vgsol*w2i**4 +& Kdrag*w2i**4*vgsol*k*rhogeq*w1i*w3r**2 - w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i**3*w1i -& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i*w1i*w2r**2 - 2*w3i**3*cs**2*k**3*rhogeq**2*vgsol*w2r*w1r*w2i -& rhogeq*w3i**3*w2i*w1r**3*rhogsol*w2r**2 + 2*w3i*w3r**2*Kdrag*rhogsol*w2r**2*w1r*w2i**2 -& w3i*w3r**2*Kdrag*rhogeq*k*vgsol*w2i**4 + Kdrag*w2i**4*k*rhogeq*vgsol*w3i**2*w1i +& 2*w1r**2*rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r**2 - 2*w1r**2*rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r**2 +& w1r**4*w2r*cs**2*k**2*rhogeq*rhogsol*w3i**2 + w3r*rhogeq**2*cs**2*k**3*w2r**3*vgsol*w3i**2 +& 2*w2i*w3r**2*rhogeq**2*k*vgsol*w2r**2*w1i*w3i**2 - w2i*w3r**4*Kdrag*rhogeq*k*vgsol*w2r**2 +& w2i*w3r**4*rhogeq**2*k*vgsol*w2r**2*w1i - 2*w2i*w3r**4*cs**2*k**2*rhogeq*rhogsol*w2r*w1i -& w3r*Kdrag*rhogsol*w2i**3*w1r**2*w3i**2 - Kdrag*w3r**3*w2r**4*rhogsol*w1i +& w3r*Kdrag*rhogsol*w2i**3*w1i**2*w3i**2 + 2*Kdrag*k*rhogeq*vgsol*w2i**3*w1r**2*w3i**2 -& 2*w1i**2*w2r*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i*w3i - w1i**2*w2r*rhogeq**2*cs**2*k**3*w1r*vgsol*w3i**2 -& w3r**3*w2r**3*k*Kdrag**2*vdsol + 2*Kdrag*k*rhogeq*vgsol*w2i**3*w1r**2*w3r**2 +& w1r**2*w2i*rhogeq**2*k*vgsol*w2r**2*w1i*w3i**2 + k*Kdrag**2*vgsol*w2i**3*w1i*w3i**2) !--break to avoid too many continuation lines rhod1r = rhod1r + (& k*Kdrag**2*vgsol*w2i**3*w1i*w3r**2 + w3r**3*w2r**3*k*Kdrag**2*vgsol +& 2*w1i**2*w2r*cs**2*k**2*rhogeq*rhogsol*w1r**2*w3i**2 - w3r**3*rhogeq*w2r**4*w1r**2*rhogsol -& rhogeq*w1i**2*w3r**3*w2r**4*rhogsol - w3r**4*rhogeq*w2r**3*w1r**2*rhogsol - k*Kdrag**2*vdsol*w2i**3*w1i*w3i**2& - w3r**4*Kdrag*rhogsol*w2r**3*w1i - w3r**3*Kdrag*rhogsol*w2i**3*w1r**2 + w3r**3*Kdrag*rhogsol*w2i**3*w1i**2 -& k*Kdrag**2*vdsol*w2i**3*w1i*w3r**2 - rhogeq**2*w3i**2*k*w1r**4*vgsol*w2r**2 +& Kdrag*w2r**3*rhogsol*w1i**2*w3i**3 + 2*w3r**2*rhogeq**2*w2r**3*k*w1r*vgsol*w3i**2 +& w2i*w3r**3*Kdrag*rhogsol*w2r**2*w1i**2 + w3r**4*rhogeq*w2r**2*w1i*k*Kdrag*vgsol -& w2i*w3r**3*Kdrag*rhogsol*w2r**2*w1r**2 + w3r**4*rhogeq**2*w2r**3*k*w1r*vgsol -& 2*w3r*rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol*w3i**2 - 2*w3r**2*Kdrag*rhogsol*w2r**3*w1i*w3i**2 +& 2*w2r*rhogeq**2*cs**2*k**3*w1r*vgsol*w3i**3*w1i + w3r**4*rhogeq**2*cs**2*k**3*w2r**2*vgsol +& 2*w3r*rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol*w3i**2 + 2*w3r**2*rhogeq**2*cs**2*k**3*w2r**2*vgsol*w3i**2 +& rhogeq**2*cs**2*k**3*w2r**2*vgsol*w1r**2*w3i*w1i - w1i**3*w3i*rhogsol*k**4*cs**4*rhogeq*w2r -& 2*w2r*cs**2*k**2*rhogeq*rhogsol*w1r**2*w3i**3*w1i + 3*w3r*Kdrag*k*rhogeq*vgsol*w2r**3*w1i*w3i**2 +& 2*rhogeq*w3i**3*cs**2*k**2*rhogsol*w1r*w2r**2*w1i + w3i**3*Kdrag*rhogsol*w2r**4*w1r -& 2*w3r**3*rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol + 2*w3r**3*rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol -& rhogeq*w2r**3*w3i**4*w1r**2*rhogsol - w3r**3*Kdrag**2*k*vgsol*w2r**2*w1r + w3r**3*Kdrag**2*k*vdsol*w2r**2*w1r +& 3*w3r**3*Kdrag*k*rhogeq*vgsol*w2r**3*w1i + 2*rhogeq*cs**4*k**4*w2r*rhogsol*w2i*w1i**2*w3i -& w3r*Kdrag**2*k*vgsol*w2r**2*w1r*w3i**2 + rhogeq*w2r**4*w3i**4*w1r*rhogsol - rhogeq**2*w2r**4*w3i**4*k*vgsol +& w3r**4*rhogeq*w2r**4*w1r*rhogsol + w3r*Kdrag**2*k*vdsol*w2r**2*w1r*w3i**2 -& w3r*Kdrag*rhogsol*k**2*cs**2*w1i*w2r**2*w3i**2 - w3r**4*rhogeq**2*w2r**4*vgsol*k +& w3r**3*rhogeq**2*cs**2*k**3*w2r**3*vgsol - w3r**3*Kdrag*rhogsol*k**2*cs**2*w1i*w2r**2 -& 2*w2i*w3r**2*Kdrag*rhogeq*k*vgsol*w2r**2*w3i**2 + 2*rhogeq*cs**4*k**4*w1r*rhogsol*w2r**2*w3i*w1i -& w3r**4*rhogeq*w1i**2*w2r**3*rhogsol - 2*w2i*w3r**3*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r +& 4*w3r**2*rhogeq*rhogsol*w2r**2*w2i**2*w1r*w3i**2 + w2i*w3r**3*Kdrag*cs**2*k**2*rhogsol*w2r**2 +& w2i*w3r*Kdrag*cs**2*k**2*rhogsol*w2r**2*w3i**2 - 2*rhogeq**2*cs**2*k**3*w2r**3*w1r*vgsol*w3i*w1i -& Kdrag*w2r**3*rhogsol*w3i**3*w1r**2 - w1r**3*rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r -& w3r**4*rhogeq*rhogsol*w2r*w2i**2*w1r**2 + w2i*w3r*Kdrag*rhogsol*w2r**2*w1i**2*w3i**2 +& 2*w3r**4*rhogeq*rhogsol*w2r**2*w2i**2*w1r + 2*w2i*w3r**3*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r -& rhogeq*w1i**2*w2r**3*rhogsol*w3i**4 - Kdrag*w2r**3*rhogsol*w1i*w3i**4 -& w2i*w3r*Kdrag*rhogsol*w2r**2*w1r**2*w3i**2 + w2i*w3r**3*rhogeq*k*Kdrag*vdsol*w2r**2*w1r +& 2*w3r**2*rhogeq*w2r**2*w1i*k*Kdrag*vgsol*w3i**2 - 4*w3r**2*rhogeq**2*k*vgsol*w2i**2*w2r**2*w3i**2 +& 2*w3r**2*rhogeq**2*k*vgsol*w2r*w2i**2*w1r*w3i**2 - 2*w3r*rhogeq*rhogsol*w2i**2*w1r**2*w2r**2*w3i**2 -& w3r**4*rhogeq*rhogsol*w2r*w2i**2*w1i**2 + w3i**3*Kdrag*rhogsol*w2i**4*w1r -& 2*w3r*rhogeq*rhogsol*w1i**2*w2r**2*w2i**2*w3i**2 - 2*w3r**2*rhogeq*rhogsol*w2r*w2i**2*w1i**2*w3i**2 +& w3r**4*rhogeq*rhogsol*w2i**4*w1r + w3i**3*vdsol*k*Kdrag**2*w2i**3 +& rhogeq*w1i*w3i**2*k*Kdrag*vdsol*w2r**2*w1r**2 + w3r**3*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w2r -& w3i**3*vgsol*k*Kdrag**2*w2i**3 + w3r**4*rhogeq**2*k*vgsol*w2r*w2i**2*w1r - w3r**3*Kdrag*rhogsol*w2i**4*w1i -& 2*w3r**2*rhogeq*rhogsol*w2r*w2i**2*w1r**2*w3i**2 - w3r**3*rhogeq*k*Kdrag*vdsol*w2r*w2i**2*w1i -& w3r**3*rhogeq*rhogsol*w2i**4*w1i**2 + 2*w3i**2*rhogsol*k**4*cs**4*rhogeq*w2r*w1i**2 +& 2*w2i*w3r**2*Kdrag*rhogsol*w2r**2*w1r*w3i**2 - w1r*Kdrag*rhogsol*k**2*cs**2*w2i**3*w3i**2 +& 2*w3r*rhogeq**2*k*vgsol*w2r**2*w2i**2*w1r*w3i**2 - w3r**3*rhogeq*rhogsol*w2i**4*w1r**2 -& w1r*Kdrag*rhogsol*k**2*cs**2*w2i**3*w3r**2 - 2*w3r*rhogeq**2*k*vgsol*w2r*w2i**2*w1r**2*w3i**2 -& w3i**4*rhogeq**2*vgsol*k*w2i**4 + w3r**4*Kdrag*rhogsol*w2i**3*w1r - w3r**4*rhogeq**2*vgsol*k*w2i**4 +& w3i**4*Kdrag*rhogsol*w2i**3*w1r - w3r*rhogeq*cs**4*k**4*w2r**2*rhogsol*w3i**2) !--break to avoid too many continuation lines rhod1r = rhod1r + (& 2*w3r*rhogeq**2*k*vgsol*w2r*w2i**2*w1i**2*w3i**2 + w3i**4*rhogeq*rhogsol*w2i**4*w1r +& 3*w3r**3*w2r*Kdrag*rhogeq*k*vgsol*w2i**2*w1i + 2*w3r**3*rhogeq**2*k*vgsol*w2r**2*w2i**2*w1r -& 2*w3r**3*rhogeq**2*k*vgsol*w2r*w2i**2*w1r**2 + 2*w3r**3*rhogeq**2*k*vgsol*w2r*w2i**2*w1i**2 -& 2*w3r**4*rhogeq**2*k*vgsol*w2i**2*w2r**2 - 2*w3r**3*rhogeq*rhogsol*w2i**2*w1r**2*w2r**2 +& w3r*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w2r*w3i**2 - 2*w3r**3*rhogeq*rhogsol*w1i**2*w2r**2*w2i**2 +& w1r*w3r**4*cs**2*k**2*rhogeq*rhogsol*w2i**2 + w1r*w3i**4*cs**2*k**2*rhogeq*rhogsol*w2i**2 +& 2*rhogeq**2*w1i**2*k*vgsol*w3r*w2r**3*w3i**2 + w2i*w3r**4*Kdrag*rhogsol*w2r**2*w1r -& 2*w3r**2*rhogeq*w2r**3*w1r**2*rhogsol*w3i**2 - w3r**3*rhogeq*cs**4*k**4*w2r**2*rhogsol +& w1r**3*rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r - 2*w3r**3*rhogeq**2*w1r**2*k*vgsol*w2r**3 +& 2*rhogeq**2*w1i**2*k*vgsol*w3r**3*w2r**3 - w2i*w3r**3*Kdrag*k*rhogeq*vgsol*w2r**2*w1r -& rhogeq*w1i*w3r**3*w2r**3*k*Kdrag*vdsol - 2*w3r*rhogeq**2*w1r**2*k*vgsol*w2r**3*w3i**2 +& w2r*w3r**3*rhogeq**2*cs**2*k**3*w1r**2*vgsol - w2r*w3r**3*rhogeq**2*cs**2*k**3*w1i**2*vgsol -& w1i**3*rhogeq*w2r**2*k*Kdrag*vgsol*w3i**2 + 2*w1r*w3i**2*cs**2*k**2*rhogeq*rhogsol*w2i**2*w3r**2 +& w2r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r**4 - rhogeq*w3i**4*cs**2*k**2*rhogsol*w1r*w2r**2 -& 2*w2r*w3r**2*Kdrag*rhogsol*w1i*w2i**2*w3i**2 - w2r*w3r**4*rhogeq**2*cs**2*k**3*w1r*vgsol -& rhogeq*w1i*w3r*w2r**3*k*Kdrag*vdsol*w3i**2 + w2r*w3r**3*rhogeq*cs**4*k**4*w1r*rhogsol -& Kdrag*w3r*w2r**4*rhogsol*w1i*w3i**2 - w3r*w2r**3*k*Kdrag**2*vdsol*w3i**2 + w3r*w2r**3*k*Kdrag**2*vgsol*w3i**2 +& rhogeq*rhogsol*w2r*w2i**2*w1i**3*w3i**3 - 4*rhogeq**2*w2r**3*k*w1r*vgsol*w3i**3*w1i +& w2r**2*k*Kdrag**2*vdsol*w3i**2*w1i**2 - w3r*rhogeq*w2r**4*w1r**2*rhogsol*w3i**2 -& rhogeq*w1i**2*w3r*w2r**4*rhogsol*w3i**2 + w3r*rhogeq**2*w2r**4*k*w1r*vgsol*w3i**2 -& w2r**2*k*Kdrag**2*vgsol*w3i**2*w1i**2 - Kdrag*rhogsol*k**2*cs**2*w1i*w2r**3*w3r**2 +& w2r*w3r**3*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol - w2r*w3r**3*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol +& w2r*w3r**4*cs**2*k**2*rhogeq*rhogsol*w1r**2 + 2*w2r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w3r**2*w3i**2 -& w3r**4*rhogeq*rhogsol*k**2*cs**2*w2r**2*w1r - rhogeq**2*w1i**4*k*vgsol*w3i**2*w2r**2 -& Kdrag*rhogsol*k**2*cs**2*w1i*w2r**3*w3i**2 + w2r*w3r*rhogeq**2*cs**2*k**3*w1r**2*vgsol*w3i**2 -& w2r*w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w3i**2 - w2r*w3r**4*Kdrag*rhogsol*w1i*w2i**2 +& rhogeq*w2r*w3i**3*w1r*k*Kdrag*vdsol*w2i**2 - 2*w2r*w3r**2*rhogeq**2*cs**2*k**3*w1r*vgsol*w3i**2 +& rhogeq**2*w3i**3*w1i**3*k*vgsol*w2r**2 + rhogeq*w2r*w3i**4*cs**2*k**2*rhogsol*w1r**2 +& w2r*w3r*rhogeq*cs**4*k**4*w1r*rhogsol*w3i**2 - 2*w2r*rhogeq*w1i**3*rhogsol*k**2*cs**2*w3i**3 -& rhogeq*cs**2*k**3*w3i**3*Kdrag*vdsol*w2r**2 + rhogeq*cs**4*k**4*w2r*rhogsol*w2i**2*w3i**2 +& w3r**3*rhogeq**2*w2r**4*k*w1r*vgsol - w1r**2*rhogeq*w2r**2*w1i*k*Kdrag*vgsol*w3i**2 -& rhogeq*cs**4*k**4*w2r*rhogsol*w2i**2*w3r**2 + 2*rhogeq**2*cs**2*k**3*w2r**2*vgsol*w1r**2*w3i**2 +& rhogeq*cs**2*k**3*w3i**3*Kdrag*vdsol*w2r*w1r + 2*rhogeq**2*w3i**3*w2r**2*w1i*k*vgsol*w2i**2 -& rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r**2*w3r**2 - w1r**2*w3i*rhogsol*k**4*cs**4*rhogeq*w2r*w1i -& 2*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2r**2*w3i**2 + rhogeq*cs**4*k**4*w2r**3*rhogsol*w3i**2 -& rhogeq*cs**4*k**4*w2r**3*rhogsol*w3r**2 + 2*w2r*w3r**2*cs**2*k**2*rhogeq*rhogsol*w1r**2*w3i**2 +& 2*w3r**2*rhogeq*w2r**4*w1r*rhogsol*w3i**2 - 2*w3r**2*rhogeq**2*w2r**4*vgsol*k*w3i**2 -& 2*rhogeq*w3i**2*cs**2*k**2*rhogsol*w1r*w2r**2*w3r**2 + rhogeq*w2i*w3i**3*w1i*k*Kdrag*vdsol*w2r**2 +& rhogeq*cs**2*k**3*w3i**3*Kdrag*vgsol*w2r**2 + rhogeq*cs**2*k**2*w2r**4*w1r*rhogsol*w3i**2 -& rhogeq*cs**2*k**2*w2r**4*w1r*rhogsol*w3r**2 + 2*rhogeq**2*cs**2*k**3*w2r**2*vgsol*w2i*w1i*w3i**2 -& 2*w2i*w3r*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r*w3i**2 + rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r**2*w3r**2 -& rhogeq*cs**2*k**3*w3i**3*Kdrag*vgsol*w2r*w1r + w2r*w3r**3*w2i**2*k*Kdrag**2*vgsol) !--break to avoid too many continuation lines rhod1r = rhod1r + (& rhogeq**2*w3i*w2r**4*w1i*k*vgsol*w3r**2 + rhogeq**2*w2r**3*w3i**4*k*w1r*vgsol +& 2*rhogeq*w2r**2*w3i**4*w1r*rhogsol*w2i**2 + rhogeq**2*w3i**3*w2r**4*w1i*k*vgsol -& 2*rhogeq*w2r*w3i**4*cs**2*k**2*rhogsol*w2i*w1i - w2r*w3r**3*w2i**2*k*Kdrag**2*vdsol -& rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2r**2*w3r**2 - rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w2r**2*w3i**2 +& rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w2r**2*w3r**2 + rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2r**2*w3i**2 +& w2r*w3r*w2i**2*k*Kdrag**2*vgsol*w3i**2 - rhogeq*cs**4*k**4*w1r*rhogsol*w2r**2*w3i**2 +& 3*rhogeq*cs**4*k**4*w1r*rhogsol*w2r**2*w3r**2 + rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2r**2*w3r**2 -& rhogeq*w2r*w3i**4*w1r**2*rhogsol*w2i**2 - 4*rhogeq*cs**4*k**4*w2r*rhogsol*w2i*w1i*w3i**2 -& 2*rhogeq**2*cs**2*k**3*w2r**3*w1r*vgsol*w3r**2 + rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w2r**2*w3i**2 +& Kdrag*k*rhogeq*vgsol*w2r**4*w1i*w3i**2 + Kdrag*k*rhogeq*vgsol*w2r**4*w1i*w3r**2 -& rhogeq**2*cs**2*k**3*w3i**3*w2i*vgsol*w2r**2 + w1i**3*w3r**3*rhogeq*rhogsol*w2i**3 +& 2*rhogeq**2*w2i*k*vgsol*w3i**3*w1r**2*w2r**2 - w2r*w3r*w2i**2*k*Kdrag**2*vdsol*w3i**2 -& rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w2r**2*w3r**2 - rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2r**2*w3i**2 -& w3i**4*cs**2*k**3*rhogeq**2*vgsol*w2r*w1r + 2*rhogeq*cs**2*k**2*w2r**2*w1r*rhogsol*w2i**2*w3i**2 -& 2*rhogeq*cs**2*k**2*w2r**2*w1r*rhogsol*w2i**2*w3r**2 + 2*rhogeq**2*w3i*w2r**2*w1i*k*vgsol*w2i**2*w3r**2 -& w3i**3*vgsol*k*Kdrag**2*w2i*w2r**2 + w3i**3*rhogsol*k**2*cs**2*Kdrag*w2r*w2i**2 +& w3i*rhogsol*k**2*cs**2*Kdrag*w2r**3*w3r**2 + w3i**3*rhogsol*k**2*cs**2*Kdrag*w2r**3 +& w3i**4*cs**2*k**3*rhogeq**2*vgsol*w2r**2 - rhogeq**2*cs**2*k**3*w3i*w2i*vgsol*w2r**2*w3r**2 +& 2*rhogeq**2*w2i*k*vgsol*w3i*w1r**2*w2r**2*w3r**2 - w3i*vgsol*k*Kdrag**2*w2i*w2r**2*w3r**2 +& w3i*Kdrag*rhogsol*w2r**4*w1r*w3r**2 - 2*w3r*rhogeq*cs**2*k**2*w1i**3*w2r**2*rhogsol*w3i +& 2*w3r*rhogeq**2*cs**2*k**3*w2r**3*vgsol*w3i*w1i - 2*w3r*w1r**2*w2i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r -& 2*w3r*w1i**3*w2i*rhogeq*cs**2*k**2*rhogsol*w2r**2 + 2*w3r*w1r**2*w2i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r +& 2*w3r*w1i**2*rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol + w3r*rhogeq*w1i**3*w3i**2*rhogsol*w2i*w2r**2 +& w3i*rhogsol*k**2*cs**2*Kdrag*w2r*w2i**2*w3r**2 + 2*w3r*w2r*w1i**3*rhogeq**2*cs**2*k**3*vgsol*w2i +& 2*w3r*w2r*rhogeq**2*cs**2*k**3*w1i**3*vgsol*w3i + w3r*w1i**4*rhogeq*cs**2*k**2*w2r**2*rhogsol +& 2*w3r*w2r*w1r**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i + w3r*w1i**2*rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol +& w3r*rhogeq*w2r**3*w1r**3*rhogsol*w3i**2 - 2*w3r*w2r*rhogeq*cs**2*k**3*w1i**2*Kdrag*vdsol*w3i +& 2*w3r*w2r*rhogeq*cs**2*k**3*w1i**2*Kdrag*vgsol*w3i - 2*w3r*w2r*cs**2*k**2*rhogeq*rhogsol*w1r**3*w3i**2 +& 2*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2r**2*w1r - 2*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2r**2*w1r +& 2*w3r*rhogeq**2*cs**2*k**3*w2r**2*vgsol*w2i*w1i*w1r - 2*w3r*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**3*w1r +& 4*w3r*rhogeq*rhogsol*k**2*cs**2*w2r**3*w1r*w3i*w1i + 2*w3r*w2r*Kdrag*rhogsol*w1i*w2i**2*w3i**2*w1r -& 2*w3r*w1r**2*rhogeq*cs**4*k**4*w2r**2*rhogsol + w3r*rhogeq*rhogsol*w2r*w2i**2*w1i**2*w3i**2*w1r -& w3r*w1r**2*w2r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol + w3r*w1r**2*w2r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol +& 2*w3r*w2r*rhogeq**2*cs**2*k**3*w1r**2*vgsol*w3i*w1i - 2*w3r*w2r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w3i**2*w1r +& w3r*rhogeq*rhogsol*w2r*w2i**2*w1r**3*w3i**2 - 4*w3r*rhogsol*k**4*cs**4*rhogeq*w2r*w2i*w3i*w1r +& w3r*w1r**3*rhogeq**2*cs**2*k**3*w2r**2*vgsol + w3r*rhogeq**2*w3i**2*k*w1r**3*vgsol*w2r**2 -& 4*w3r*rhogeq**2*cs**2*k**3*w2r**2*w1r*vgsol*w3i*w1i - 2*w3r*rhogeq*cs**2*k**2*w2r**2*w1r**2*rhogsol*w3i*w1i +& 2*w3r*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w2r*w3i*w1i + 4*w3r*w2i*cs**2*k**2*rhogeq*rhogsol*w2r*w1i*w3i**2*w1r -& 2*w3r*w1i**2*rhogeq*cs**2*k**2*rhogsol*w2r*w2i**2*w1r - 2*w3r*w1i**2*w2i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2r +& 2*w3r*w1i**2*w2i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r + w3r*rhogeq*w1i**2*w2r**3*rhogsol*w3i**2*w1r -& 2*w3r*rhogeq**2*cs**2*k**3*w3i*w2i*vgsol*w2r**2*w1r + w3r**3*rhogeq*w2r**3*w1r**3*rhogsol -& 2*w3r**3*rhogeq*w2r**2*w1i*k*Kdrag*vgsol*w1r - w3i*rhogsol*k**4*cs**4*rhogeq*w2r*w1i*w3r**2) !--break to avoid too many continuation lines rhod1r = rhod1r + (& w3i*vdsol*k*Kdrag**2*w2i*w2r**2*w3r**2 + 2*rhogeq*w2r**2*w3i**3*w1r**2*k*Kdrag*vgsol -& 4*w3r*rhogeq**2*cs**2*k**3*w2r*w1i**2*vgsol*w2i*w3i - w3r*w1r**4*w2r*rhogeq**2*cs**2*k**3*vgsol +& 4*w3r*w2i**2*cs**2*k**2*rhogeq*rhogsol*w2r*w1i*w1r*w3i + w3r*w1r**3*w2r*rhogeq*cs**4*k**4*rhogsol -& w3i**3*vgsol*k*rhogeq*Kdrag*w2r**4 - 2*w3r*rhogeq*w2r**2*w1i*k*Kdrag*vgsol*w3i**2*w1r +& w3r*rhogeq**2*w3i**2*k*w1r*vgsol*w2r**2*w1i**2 + 4*w3r*rhogeq**2*cs**2*k**3*w2r*w1r**2*vgsol*w2i*w3i +& 2*w3r*cs**2*k**3*rhogeq**2*w2r*vgsol*w2i*w1i*w3i**2 - 2*w3r*w2r**3*rhogeq*w1i**2*rhogsol*k**2*cs**2*w1r -& 2*w3r*w2r**3*cs**2*k**2*rhogeq*rhogsol*w1r**3 + rhogeq*w2r**3*w1r**2*rhogsol*w3i**3*w1i -& 2*w3r*rhogeq*cs**2*k**3*w3i*Kdrag*vdsol*w2r*w1r**2 - 4*w3r*w2i*rhogeq**2*k*vgsol*w2r**2*w1i*w3i**2*w1r -& 2*w3r*w1r**3*rhogeq*cs**2*k**2*rhogsol*w2r*w2i**2 - 2*w3r*w2i*w1i*rhogeq**2*cs**2*k**3*w1r*vgsol*w3i**2 +& w3i**3*vdsol*k*Kdrag**2*w2i*w2r**2 + 2*rhogeq*w2r**2*w3i*w1r**2*k*Kdrag*vgsol*w3r**2 -& 2*w3r*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1r**3 - 2*w3r*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**2*w1r +& 4*w3r*rhogeq**2*cs**2*k**3*w3i*w1i*vgsol*w2i**2*w1r + 2*w3r*rhogeq*cs**2*k**3*w3i*Kdrag*vgsol*w2r*w1r**2 +& 2*w3r*Kdrag*rhogsol*w2r**3*w1i*w3i**2*w1r - 2*w3r*w1i**2*w2r*rhogeq**2*cs**2*k**3*w1r**2*vgsol +& w3r*w1i**2*w2r*rhogeq*cs**4*k**4*w1r*rhogsol + w3r*w1i**3*w2r*rhogeq*cs**2*k**3*Kdrag*vdsol -& w3r*w1i**3*w2r*rhogeq*cs**2*k**3*Kdrag*vgsol - w3r*w1i**4*w2r*rhogeq**2*cs**2*k**3*vgsol +& w3r*rhogeq*w1i*w3i**2*rhogsol*w2i*w2r**2*w1r**2 - 2*w3r*w1r**2*w2i*rhogeq*cs**2*k**2*rhogsol*w2r**2*w1i +& Kdrag*w2i**2*rhogsol*w1i**2*w2r*w3i**3 + Kdrag*w2i**2*rhogsol*w1i**2*w2r*w3i*w3r**2 -& Kdrag*w2i**2*rhogsol*w3i*w2r*w1r**2*w3r**2 - Kdrag*w2i**2*rhogsol*w3i**3*w2r*w1r**2 -& Kdrag*w2r**2*k*rhogeq*vgsol*w2i*w3i**4 - w3i*vgsol*k*rhogeq*Kdrag*w2r**4*w3r**2 +& 2*w1r**3*rhogeq*cs**2*k**2*rhogsol*w2i**2*w3i**2 + Kdrag*w2r**3*rhogsol*w1i**2*w3i*w3r**2 -& w1r**3*w3i**3*rhogeq*rhogsol*w2i**3 - 4*w3r**3*w2i*rhogeq**2*k*vgsol*w2r**2*w1i*w1r -& w3i**3*rhogsol*k**4*cs**4*rhogeq*w2r*w1i + rhogeq*cs**2*k**2*w1i**2*w2r*rhogsol*w3i**4 -& w2r**3*k*Kdrag**2*vgsol*w1r*w3r**2 - 2*w1i**2*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w3i**2 -& 2*rhogeq**2*w1i**2*w2r**2*k*vgsol*w2i*w3i**3 - w2r**2*k*Kdrag**2*vdsol*w3i**3*w1i +& w2i**2*k*Kdrag**2*vdsol*w2r*w1r*w3i**2 + w1i**4*w2r*rhogeq*rhogsol*k**2*cs**2*w3i**2 +& w2r**2*k*Kdrag**2*vgsol*w3i**3*w1i + w2i**2*k*Kdrag**2*vdsol*w2r*w1r*w3r**2 -& 2*rhogeq**2*w1i**2*w2r**2*k*vgsol*w2i*w3i*w3r**2 + w2r**3*k*Kdrag**2*vdsol*w1r*w3i**2 -& w1r**2*vdsol*Kdrag*k*rhogeq*w2i**3*w3i**2 - w1r**2*vdsol*Kdrag*k*rhogeq*w2i**3*w3r**2 +& w2r**3*k*Kdrag**2*vdsol*w1r*w3r**2 - Kdrag*w2r**2*rhogsol*k**2*cs**2*w2i*w1r*w3i**2 -& w1r**2*Kdrag*k*rhogeq*vgsol*w1i*w2i**2*w3i**2 - w2i**2*k*Kdrag**2*vgsol*w2r*w1r*w3i**2 -& w2i**2*k*Kdrag**2*vgsol*w2r*w1r*w3r**2 - w1r**2*Kdrag*k*rhogeq*vgsol*w1i*w2i**2*w3r**2 -& Kdrag*w2r**2*rhogsol*k**2*cs**2*w2i*w1r*w3r**2 + w2i**2*k*Kdrag**2*vgsol*w3i**3*w1i +& 2*w3r*w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w1r**2 - w2i**2*k*Kdrag**2*vdsol*w3i**3*w1i +& w2i**2*k*Kdrag**2*vdsol*w3r**3*w1r - 2*w3r**3*w2r*cs**2*k**2*rhogeq*rhogsol*w1r**3 +& 2*w3r**3*w2r*Kdrag*rhogsol*w1i*w2i**2*w1r - w3i**4*rhogeq**2*cs**2*k**3*vgsol*w2i**2 -& Kdrag*w2i**2*k*rhogeq*vgsol*w2r*w3i**3*w1r + 2*Kdrag*w2i*vgsol*k*rhogeq*w1r**2*w2r**2*w3i**2 +& w3r**3*rhogeq*w2i*rhogsol*w1i*w2r**2*w1r**2 - 2*w3r**3*w2r*rhogeq*w1i**2*rhogsol*k**2*cs**2*w1r +& 2*Kdrag*w2i*vgsol*k*rhogeq*w1r**2*w2r**2*w3r**2 - Kdrag*w2r**3*k*rhogeq*vgsol*w3i*w1r*w3r**2 -& 2*rhogeq**2*w2r**2*w3i**4*k*vgsol*w2i**2 - Kdrag*w2i**2*rhogsol*w1i*w2r*w3i**4 -& rhogeq*w1i**2*w2i**2*rhogsol*w2r*w3i**4 - Kdrag*w2r**3*rhogsol*w3i*w1r**2*w3r**2) !--break to avoid too many continuation lines rhod1r = rhod1r + (& Kdrag*w2r**2*k*rhogeq*vgsol*w2i*w3i**3*w1i - 2*rhogeq*w1i**2*w2r**3*rhogsol*w3i**2*w3r**2 -& w2r**2*k*Kdrag**2*vdsol*w3i*w1i*w3r**2 + Kdrag*w2r**2*rhogsol*w2i*w1r*w3i**4 +& w2r**2*k*Kdrag**2*vgsol*w3i*w1i*w3r**2 - w2r**3*k*Kdrag**2*vgsol*w1r*w3i**2 +& 4*w3i**3*cs**2*k**2*rhogeq*rhogsol*w2i*w2r*w1i**2 + Kdrag*k*rhogeq*vgsol*w2r**2*w1i*w3i**4 +& 2*w3r**3*cs**2*k**3*rhogeq**2*w2r*vgsol*w2i*w1i + w3r**3*rhogeq*w2i*rhogsol*w1i**3*w2r**2 +& 2*w3r**3*Kdrag*rhogsol*w2r**3*w1i*w1r - Kdrag**2*k*vdsol*w2i*w1i*w2r**2*w3r**2 +& w3r**3*rhogeq**2*k*w1r**3*vgsol*w2r**2 + w3r*rhogeq*cs**2*k**2*w1r**4*rhogsol*w2i**2 +& Kdrag**2*k*vgsol*w2i*w1i*w2r**2*w3i**2 - 2*cs**2*k**3*rhogeq**2*vgsol*w2r*w1r*w2i**2*w3r**2 +& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w1r**3 + w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i*w1i*w1r**2 -& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w1r**3 + Kdrag**2*k*vgsol*w2i*w1i*w2r**2*w3r**2 +& w3r**3*rhogeq**2*k*w1r*vgsol*w2r**2*w1i**2 + rhogeq*w2r**3*w3i**3*w1r*k*Kdrag*vdsol +& 2*rhogsol*k**4*cs**4*rhogeq*w2r*w2i*w3i**3 + 2*rhogsol*k**4*cs**4*rhogeq*w2r*w2i*w3i*w3r**2 -& w2i**2*k*Kdrag**2*vgsol*w3r**3*w1r - Kdrag*w2r**2*rhogsol*k**2*cs**2*w3i*w1r*w3r**2 +& rhogeq**2*w1i*w2r**2*k*vgsol*w2i*w3i**4 - Kdrag*w2r**2*rhogsol*k**2*cs**2*w3i**3*w1r -& Kdrag*w2r**3*k*rhogeq*vgsol*w3i**3*w1r - w3i**4*vgsol*k*rhogeq*Kdrag*w2i**3 +& w3i*vdsol*k*Kdrag**2*w2i**3*w3r**2 - w3i*vgsol*k*Kdrag**2*w2i**3*w3r**2 +& 2*w2i**3*w1i*w3r*rhogeq**2*cs**2*k**3*w1r*vgsol + w3r**4*rhogeq**2*vgsol*k*w2i**3*w1i -& w3r*Kdrag*rhogsol*w2i**4*w1i*w3i**2 - Kdrag*rhogsol*k**2*cs**2*w1i*w2r*w2i**2*w3i**2 -& w2i**2*k*Kdrag**2*vgsol*w3r*w1r*w3i**2 - Kdrag*rhogsol*k**2*cs**2*w3i**3*w1r*w2i**2 -& Kdrag*rhogsol*k**2*cs**2*w1i*w2r*w2i**2*w3r**2 + rhogeq*w2r**3*w3i*w1r*k*Kdrag*vdsol*w3r**2 -& w3r**4*vgsol*k*rhogeq*Kdrag*w2i**3 + w2i**2*k*Kdrag**2*vgsol*w3i*w1i*w3r**2 +& 2*w3r*rhogeq*cs**2*k**2*w1i**2*rhogsol*w2i**2*w1r**2 - w2i**2*k*Kdrag**2*vdsol*w3i*w1i*w3r**2 +& w1i**3*rhogeq**2*cs**2*k**3*w3i*vgsol*w2r**2 + w2i**2*k*Kdrag**2*vdsol*w3r*w1r*w3i**2 -& 2*w3i**2*rhogeq**2*cs**2*k**3*vgsol*w2i**2*w3r**2 + 4*w3r**3*w2i*cs**2*k**2*rhogeq*rhogsol*w2r*w1i*w1r +& Kdrag*w2i**2*vgsol*k*rhogeq*w1i*w3r**4 + w3r**3*rhogeq*rhogsol*w2r*w2i**2*w1i**2*w1r -& 2*Kdrag*w2r**2*k*rhogeq*vgsol*w2i**2*w3i**3 + w2r*rhogeq**2*vgsol*k*w2i**2*w1r*w3i**4 -& Kdrag**2*k*vdsol*w2i*w1i*w2r**2*w3i**2 - 2*w2i**3*w1i**3*w3r*rhogeq*cs**2*k**2*rhogsol +& w3r**3*vdsol*Kdrag*k*rhogeq*w2i**3*w1r + 2*cs**2*k**3*rhogeq**2*vgsol*w2i**3*w1i*w3i**2 +& 2*w3i**2*Kdrag*rhogsol*w2i**3*w1r*w3r**2 - Kdrag*rhogsol*k**2*cs**2*w3i*w1r*w2i**2*w3r**2 +& 2*Kdrag*w2i**2*vgsol*k*rhogeq*w1i*w3r**2*w3i**2 - w3r*rhogeq*cs**4*k**4*rhogsol*w2i*w1i*w1r**2 -& w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w1r**3 + 2*w3r**2*rhogeq**2*vgsol*k*w2i**3*w1i*w3i**2 -& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i*w1i*w1r**2 + w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1r**4 -& 2*Kdrag*w2r**2*k*rhogeq*vgsol*w2i**2*w3i*w3r**2 - cs**2*k**3*rhogeq*vgsol*Kdrag*w2i**3*w3i**2 +& 2*w3r**2*rhogeq*rhogsol*w2i**4*w1r*w3i**2 + 2*Kdrag*k*rhogeq*vgsol*w2r**2*w1i*w2i**2*w3i**2 +& 2*Kdrag*k*rhogeq*vgsol*w2r**2*w1i*w2i**2*w3r**2 + w3r**3*rhogsol*k**2*cs**2*Kdrag*w2i**3 +& w3i*Kdrag*rhogsol*w2i**4*w1r*w3r**2 + cs**2*k**3*rhogeq*vgsol*Kdrag*w2i**3*w3r**2 +& w1r**2*w3r**3*rhogeq*rhogsol*w2i**3*w1i + 2*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**2*w1r**2 -& 2*w3r**2*vgsol*k*rhogeq*Kdrag*w2i**3*w3i**2 + w3r**4*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i +& w1r**2*vgsol*k*rhogeq**2*w2i**3*w1i*w3r**2 + w1r**2*vgsol*k*rhogeq**2*w2i**3*w1i*w3i**2) !--break to avoid too many continuation lines rhod1r = rhod1r + (& w1r**2*w3r*rhogeq*rhogsol*w2i**3*w1i*w3i**2 - 2*w2i*Kdrag*rhogsol*w2r**2*w1r*w3i**3*w1i +& rhogeq*rhogsol*w2r*w2i**2*w1r**2*w3i**3*w1i + w3r*rhogeq**2*k*vgsol*w2i**4*w1r*w3i**2 -& 2*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i**2*w1r + 2*w3r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i**2*w1r +& w3r**3*rhogeq**2*k*vgsol*w2i**4*w1r + cs**2*k**3*rhogeq*vdsol*Kdrag*w2i**3*w3i**2 -& cs**2*k**3*rhogeq*vdsol*Kdrag*w2i**3*w3r**2 - w3i**3*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**2 -& w3r*vgsol*k*rhogeq*Kdrag*w2i**3*w1r*w3i**2 + w3i*vgsol*k*rhogeq*Kdrag*w2i**3*w1i*w3r**2 -& w3i**3*cs**2*k**3*rhogeq**2*vgsol*w2i**3 + w3i**3*cs**2*k**3*rhogeq**2*vgsol*w2i*w1r**2 -& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**3*w3r**2 + w3r*vdsol*Kdrag*k*rhogeq*w2i**3*w1r*w3i**2 -& 2*w2i**3*w1i*w3i**3*Kdrag*rhogsol*w1r + w3r*rhogeq*cs**2*k**2*w1i**4*rhogsol*w2i**2 -& 2*w2i**3*w1i*w3r*rhogeq*cs**2*k**2*w1r**2*rhogsol - 2*w2i**2*w1i**2*w3i*cs**2*k**3*rhogeq*Kdrag*vgsol +& 2*w2i**2*w1i**2*w3i*cs**2*k**3*rhogeq*Kdrag*vdsol + 2*w3i**3*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w1i +& 2*w2i**2*w1i**2*w3r*rhogeq*cs**4*k**4*rhogsol - w3i**3*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i*w1i +& w3r*rhogsol*k**2*cs**2*Kdrag*w2i**3*w3i**2 - w3r**3*vgsol*k*rhogeq*Kdrag*w2i**3*w1r +& 2*w3r**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i*w3i**2 + w3i**3*vgsol*k*rhogeq*Kdrag*w2i**3*w1i +& 2*w2i*w1i**2*w3r*w3i*cs**4*k**4*rhogeq*rhogsol + w3i**3*rhogeq**2*k*vgsol*w2i**4*w1i -& 2*w2i**3*w1i*w3i*w3r**2*Kdrag*rhogsol*w1r + w3r**3*rhogeq*w1i**2*w2r**3*rhogsol*w1r +& w3r**3*rhogeq*rhogsol*w2r*w2i**2*w1r**3 + rhogeq*rhogsol*k**2*cs**2*w2i**4*w1r*w3i**2 -& rhogeq*rhogsol*k**2*cs**2*w2i**4*w1r*w3r**2 + w3i*vdsol*Kdrag*k*rhogeq*w2i**3*w1i*w3r**2 -& w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w1r*w1i**2 + w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i*w1i**3 +& w3i**3*vdsol*Kdrag*k*rhogeq*w2i**3*w1i + w3r*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w1r*w1i**2 -& 2*w3r**2*rhogeq**2*vgsol*k*w2i**4*w3i**2 - w3r*rhogeq*cs**4*k**4*rhogsol*w2i*w1i**3 -& w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w1r*w1i**2 + w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**4 -& w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i*w1i**3 - w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1i**2*w3r**2 +& w3i*cs**2*k**3*rhogeq**2*vgsol*w2i*w1r**2*w3r**2 + 2*w3i*cs**2*k**3*rhogeq**2*vgsol*w2i**2*w1i*w3r**2 +& 2*w3i*rhogsol*k**4*cs**4*rhogeq*w1i*w2i**2*w1r + 2*w3i*rhogeq**2*k*vgsol*w2i**3*w1r**2*w3r**2 -& 2*w3i*rhogeq**2*k*vgsol*w2i**3*w1i**2*w3r**2 - 2*w3r*rhogeq*cs**2*k**2*w1r**2*rhogsol*w2i**2*w3i**2 +& w3i*rhogeq**2*k*vgsol*w2i**4*w1i*w3r**2 + w3i**4*rhogeq**2*vgsol*k*w2i**3*w1i +& 2*w3i**3*rhogeq**2*k*vgsol*w2i**3*w1r**2 - 2*w3i**3*rhogeq**2*k*vgsol*w2i**3*w1i**2 -& w3i*cs**4*k**4*rhogeq*rhogsol*w2i*w1r*w3r**2 - w3r**4*rhogeq**2*cs**2*k**3*vgsol*w2i**2 -& w3r*Kdrag*rhogsol*k**2*cs**2*w1i*w2i**2*w3i**2 + w3r**3*rhogeq*cs**4*k**4*rhogsol*w2i**2 -& w3r*rhogeq*rhogsol*w2i**4*w1r**2*w3i**2 + rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i**2*w3r**2 -& rhogeq*cs**4*k**4*w1r*rhogsol*w2i**2*w3i**2 - rhogeq*cs**4*k**4*w1r*rhogsol*w2i**2*w3r**2 -& w3r**3*rhogeq*cs**4*k**4*rhogsol*w2i*w1i + 3*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i**2*w3i**2 +& 2*w2i*w1i*rhogeq*cs**4*k**4*w1r*rhogsol*w3i**2 + 2*w2i*w1i*rhogeq*cs**4*k**4*w1r*rhogsol*w3r**2 -& 2*w2i**3*w1i*rhogeq*cs**2*k**2*w1r*rhogsol*w3i**2 + 2*w2i**3*w1i*rhogeq*cs**2*k**2*w1r*rhogsol*w3r**2 -& rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w2i**2*w3r**2 + w3i*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i**2*w3r**2 -& w3i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i**2*w3r**2 - w3i**3*cs**4*k**4*rhogeq*rhogsol*w2i*w1r -& w3r**3*Kdrag*rhogsol*k**2*cs**2*w1i*w2i**2 - w3r*rhogeq*cs**4*k**4*rhogsol*w2i*w1i*w3i**2) !--break to avoid too many continuation lines rhod1r = rhod1r + (& w3r*rhogeq*cs**4*k**4*rhogsol*w2i**2*w3i**2 - 2*w3r**2*rhogeq**2*w1i**2*k*vgsol*w2r**2*w1r**2 +& w3r**2*w1r**3*rhogeq**2*w2r**3*k*vgsol - 2*w3r**3*rhogeq*cs**2*k**2*w1r**2*rhogsol*w2i**2 -& w3r**2*w3i*rhogeq*rhogsol*w2i*w1r*w2r**2*w1i**2 - w3r**2*w3i*rhogeq*k*Kdrag*vdsol*w2r**2*w1i**2 -& w3r*rhogeq*rhogsol*w2i**4*w1i**2*w3i**2 - 4*w3r**2*rhogeq**2*k*vgsol*w2r*w2i**2*w1r*w3i*w1i -& 4*w3r**2*w2r*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i*w1r - w2i**2*k*Kdrag**2*vdsol*w1r**2*w3r**2 +& w2i**2*k*Kdrag**2*vgsol*w1r**2*w3i**2 + w2i**2*k*Kdrag**2*vgsol*w1r**2*w3r**2 -& 4*vgsol*k*rhogeq**2*w2i**3*w1i*w3i**2*w3r*w1r - 4*vgsol*k*rhogeq**2*w2i**3*w1i*w3r**3*w1r -& 2*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w2i**2*w3r**2 + 2*w2i**2*w1i*Kdrag*rhogsol*k**2*cs**2*w1r*w3r**2 +& 2*w2i**2*w1i*Kdrag*rhogsol*k**2*cs**2*w1r*w3i**2 + w2i**2*w1i**2*Kdrag**2*k*vdsol*w3r**2 -& 3*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w2i**2*w3i**2 + 2*rhogeq**2*cs**2*k**3*w1r**2*vgsol*w2i**2*w3r**2 +& w3i**3*cs**2*k**3*rhogeq*Kdrag*vdsol*w2i**2 - w2i**2*w1i**2*Kdrag**2*k*vgsol*w3r**2 +& w2i**2*w1i**2*Kdrag**2*k*vdsol*w3i**2 - w2i**2*w1i**2*Kdrag**2*k*vgsol*w3i**2 -& w3i**3*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i**2 + w3i**4*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i +& w3i**3*cs**2*k**3*rhogeq*Kdrag*vgsol*w2i*w1i - w3r**3*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w1r +& w3r**3*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w1r - w2i**2*k*Kdrag**2*vdsol*w1r**2*w3i**2 +& 2*Kdrag*w2i**2*k*rhogeq*vgsol*w3i*w1r**2*w3r**2 + 2*w1r**2*rhogeq*cs**2*k**3*w2i*Kdrag*vgsol*w3r**2 -& w1r**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i*w3i**2 + w1r**2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i*w3r**2 +& 2*Kdrag*w2i**2*k*rhogeq*vgsol*w3i**3*w1r**2 - 2*w1r**2*rhogeq**2*w1i**2*k*vgsol*w2i**2*w3i**2 -& 2*w1r**2*rhogeq**2*w1i**2*k*vgsol*w2i**2*w3r**2 + Kdrag*w2i**2*k*rhogeq*vgsol*w3i**4*w1i -& w1i**2*vdsol*Kdrag*k*rhogeq*w2i**3*w3i**2 - w1i**2*vdsol*Kdrag*k*rhogeq*w2i**3*w3r**2 +& w1i**3*vdsol*Kdrag*k*rhogeq*w2i**2*w3r**2 + w1i**3*vdsol*Kdrag*k*rhogeq*w2i**2*w3i**2 -& w1i**2*w3i**3*rhogeq*rhogsol*w2i**3*w1r - w1i**2*w3i*rhogeq*rhogsol*w2i**3*w1r*w3r**2 -& Kdrag**2*k*vdsol*w2r**2*w1r**2*w3i**2 + 2*w1i**2*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w3i**2 +& 2*w1i**2*rhogeq*cs**2*k**2*w1r*rhogsol*w2i**2*w3i**2 - 2*rhogeq**2*cs**2*k**3*vgsol*w2i*w1i*w3r**3*w1r -& 2*rhogeq*cs**2*k**2*w1r*rhogsol*w2i**2*w3i**3*w1i + Kdrag**2*k*vgsol*w2r**2*w1r**2*w3i**2 -& 2*rhogeq*cs**2*k**2*w1r*rhogsol*w2i**2*w3r**2*w3i*w1i + w1i**3*vgsol*k*rhogeq**2*w2i**3*w3r**2 +& w1i**3*vgsol*k*rhogeq**2*w2i**3*w3i**2 - 2*rhogeq**2*w1i**2*k*vgsol*w3i**2*w2r**2*w1r**2 +& 2*Kdrag*rhogsol*k**2*cs**2*w1i*w2r**2*w3i**2*w1r + 2*w3r**2*Kdrag*rhogsol*k**2*cs**2*w1i*w2r**2*w1r -& 2*w3r**2*rhogeq*cs**2*k**2*w2r**2*w1r**3*rhogsol + w1i**3*w3r*rhogeq*rhogsol*w2i**3*w3i**2 +& w3r**2*w1r**3*w2r*rhogeq**2*cs**2*k**3*vgsol - w1r**4*rhogeq**2*k*vgsol*w2i**2*w3i**2 -& w1r**4*rhogeq**2*k*vgsol*w2i**2*w3r**2 - w1i**4*rhogeq**2*k*vgsol*w2i**2*w3r**2 -& w1i**4*rhogeq**2*k*vgsol*w2i**2*w3i**2 + w1r**2*vdsol*Kdrag*k*rhogeq*w2i**2*w1i*w3r**2 -& w1r**3*w3i*rhogeq*rhogsol*w2i**3*w3r**2 + w1r**2*vdsol*Kdrag*k*rhogeq*w2i**2*w1i*w3i**2 -& 2*w1r**2*rhogeq*cs**2*k**3*w2i*Kdrag*vdsol*w3r**2 - w3r**2*w2i*rhogeq*k*Kdrag*vdsol*w2r**2*w1r**2 -& w3r**2*rhogeq**2*w1r**4*k*vgsol*w2r**2 + w3r**2*w1r**4*w2r*cs**2*k**2*rhogeq*rhogsol -& w1i**3*Kdrag*k*rhogeq*vgsol*w2i**2*w3r**2 - w1i**3*Kdrag*k*rhogeq*vgsol*w2i**2*w3i**2) !--break to avoid too many continuation lines rhod1r = rhod1r + (& rhogeq**2*w3i**3*w1i*k*vgsol*w2r**2*w1r**2 + w3r**2*rhogeq*w1i*k*Kdrag*vdsol*w2r**2*w1r**2 +& w3r**2*rhogeq*rhogsol*w2r*w2i**2*w1r**2*w3i*w1i + w3r**2*w1i**2*rhogeq**2*k*vgsol*w2r*w2i**2*w1r -& 4*w3r**2*rhogeq**2*w2r**3*k*w1r*vgsol*w3i*w1i - 2*w3r**2*w1r**2*w2i*cs**2*k**2*rhogeq*rhogsol*w2r*w1i +& w3r**2*rhogeq*w1i**3*k*Kdrag*vdsol*w2r**2 + w3r**2*rhogeq*rhogsol*w2r*w2i**2*w1i**3*w3i -& w3r**2*rhogeq**2*w1i**4*k*vgsol*w2r**2 - w3r**2*Kdrag**2*k*vgsol*w2r**2*w1i**2 +& w3r**2*Kdrag**2*k*vdsol*w2r**2*w1i**2 - 2*w3r**2*w1i**3*w2i*cs**2*k**2*rhogeq*rhogsol*w2r +& w3r**2*w1i**3*w2i*rhogeq**2*k*vgsol*w2r**2 + w3r**2*rhogeq*w2r**3*w1r**2*rhogsol*w3i*w1i +& 2*w3r**2*rhogeq*rhogsol*k**2*cs**2*w2r**2*w1r*w3i*w1i - 2*w3r**2*w2r*cs**2*k**2*rhogeq*rhogsol*w1r**2*w3i*w1i +& w3r**2*w1i**2*rhogeq**2*w2r**3*k*w1r*vgsol - 2*w3r**2*w2r*rhogeq*w1i**3*rhogsol*k**2*cs**2*w3i -& 2*w3r**2*w2i*Kdrag*rhogsol*w2r**2*w1r*w3i*w1i - 2*w3r**2*w2r*Kdrag*rhogeq*k*vgsol*w2i**2*w1i*w1r -& 2*w3r**2*Kdrag*k*rhogeq*vgsol*w2r**3*w1i*w1r - w3r**2*w3i*rhogeq*rhogsol*w2i*w1r**3*w2r**2 -& w3r**2*w1i**3*rhogeq*w2r**2*k*Kdrag*vgsol + 2*w3r**2*w2r*rhogeq**2*cs**2*k**3*w1r*vgsol*w3i*w1i -& 2*w3r**2*w2r*rhogeq*cs**2*k**3*w1i*Kdrag*vdsol*w1r + 2*w3r**2*w2r*rhogeq*cs**2*k**3*w1i*Kdrag*vgsol*w1r +& w3r**2*w1r**3*rhogeq**2*k*vgsol*w2r*w2i**2 - 2*w3r**2*w2r*rhogeq*cs**4*k**4*w1r**2*rhogsol +& w1i**3*rhogeq**2*cs**2*k**3*vgsol*w2i*w3r**2 - w1i**3*rhogeq**2*cs**2*k**3*vgsol*w2i*w3i**2 -& w3r**2*w1r**2*rhogeq*w2r**2*w1i*k*Kdrag*vgsol + rhogeq*w1i**3*w3i**2*k*Kdrag*vdsol*w2r**2 +& w3r**2*w1r**2*w2i*rhogeq**2*k*vgsol*w2r**2*w1i + w2i**2*w1r**3*w3r**3*rhogeq**2*k*vgsol +& w3r**2*Kdrag**2*k*vgsol*w2r**2*w1r**2 - w3r**2*Kdrag**2*k*vdsol*w2r**2*w1r**2 +& w3r**2*w1i**4*w2r*rhogeq*rhogsol*k**2*cs**2 - rhogeq*w3i**3*k*Kdrag*vdsol*w2r**2*w1r**2 -& w2i**2*rhogeq**2*cs**2*k**3*w3i*w1i**3*vgsol + w3r**2*rhogeq*w1i**3*w2r**3*rhogsol*w3i -& w2i**2*w3r*rhogeq**2*cs**2*k**3*w1i**2*vgsol*w1r + 2*w3r**2*w2i*rhogeq*cs**2*k**2*rhogsol*w2r**2*w1i*w1r +& w3r**2*w3i*rhogeq**2*k*vgsol*w1i*w2r**2*w1r**2 - w3r**2*w3i*rhogeq*k*Kdrag*vdsol*w2r**2*w1r**2 -& w2i**2*w3r*rhogeq**2*cs**2*k**3*w1r**3*vgsol - w3r**2*w2i*w1i**2*rhogeq*k*Kdrag*vdsol*w2r**2 -& w2i**2*rhogeq**2*cs**2*k**3*w3i*w1i*vgsol*w1r**2 - 2*w2i**2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w1i*w1r**2 -& 2*w2i**2*Kdrag*k*rhogeq*vgsol*w1i*w3r**3*w1r - 2*w2i**2*w3r*w3i*cs**2*k**2*rhogeq*rhogsol*w1i**3 -& w2i**2*w1r**2*w3i*vdsol*Kdrag*k*rhogeq*w3r**2 - 2*w3r**2*w1i**2*rhogeq*rhogsol*k**2*cs**2*w2r**2*w1r +& w3r**2*w3i*rhogeq**2*k*vgsol*w1i**3*w2r**2 - 2*w3r**2*w2r*rhogeq**2*cs**2*k**3*w1r*vgsol*w2i*w3i +& 2*w3r**2*w1i**2*w2r*cs**2*k**2*rhogeq*rhogsol*w1r**2 + w3r**2*w1i**2*w2r*rhogeq**2*cs**2*k**3*w1r*vgsol +& w2i**2*w1i**3*rhogeq**2*w3i**3*k*vgsol + w2i**2*w1i**2*w3r*rhogeq**2*k*w1r*vgsol*w3i**2 +& w2i**2*w1i**3*rhogeq**2*w3i*k*vgsol*w3r**2 + w2i**2*w1i**2*w3r**3*rhogeq**2*k*w1r*vgsol -& w2i**2*w1r**2*w3i**3*vdsol*Kdrag*k*rhogeq - 2*w2i**2*Kdrag*k*rhogeq*vgsol*w1i*w3i**2*w3r*w1r -& w2i**2*w1i**2*w3i**3*vdsol*Kdrag*k*rhogeq - w2i**2*w1i**2*w3i*vdsol*Kdrag*k*rhogeq*w3r**2 +& w2i**2*w1r**3*w3r*rhogeq**2*k*vgsol*w3i**2 + w2i**2*w1r**2*rhogeq**2*w3i**3*w1i*k*vgsol +& w2i**2*w1r**2*rhogeq**2*w3i*w1i*k*vgsol*w3r**2 - 4*w3r*w2i*cs**2*k**3*rhogeq*Kdrag*vgsol*w2r*w3i*w1i +& w3r*w1r**4*rhogeq*cs**2*k**2*w2r**2*rhogsol + rhogeq*w1i**3*w2r**3*rhogsol*w3i**3) rhod1r = rhod1r*rhodeq/(w1i**2 - 2*w3i*w1i +& w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r)/(w3r**2 + w3i**2)/Kdrag/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i - 2*w2r*w1r& + w1i**2)/rhogeq/(w2i**2 + w2r**2) rhod1i = - ( - 2*rhogsol*Kdrag*w1i**3*rhodeq*w3r**2*w2i**3 - 2*rhodsol*rhogeq*Kdrag*w3i**4*w2i**2*w2r**2 -& rhodsol*rhogeq*Kdrag*w2r**4*w3i**4 - rhodsol*rhogeq*Kdrag*w3i**4*w2i**4 -& 2*rhogsol*Kdrag*w1i**3*rhodeq*w3r**2*w2i*w2r**2 - 2*rhodsol*rhogeq*Kdrag*w2r**4*w3i**2*w3r**2 -& 2*rhogsol*Kdrag*w1i**3*rhodeq*w3r**2*w2r**2*w3i - 2*rhogsol*Kdrag*w1i**3*rhodeq*w3r**2*w2i**2*w3i -& 2*rhogsol*Kdrag*w1i**3*rhodeq*w2r**2*w3i**3 - 2*rhogsol*Kdrag*w1i**3*rhodeq*w2i**2*w3i**3 -& 2*rhogsol*Kdrag*w1i**3*rhodeq*w2i**3*w3i**2 - rhodsol*rhogeq*Kdrag*w3r**4*w2i**4 -& rhodsol*rhogeq*Kdrag*w3r**4*w2r**4 - 2*rhodsol*rhogeq*Kdrag*w3r**4*w2i**2*w2r**2 -& 4*rhodsol*rhogeq*Kdrag*w3r**2*w2i**2*w2r**2*w3i**2 - 2*rhodsol*rhogeq*Kdrag*w3i**2*w2i**4*w3r**2 -& 2*rhogsol*Kdrag*w1i**3*rhodeq*w2i*w2r**2*w3i**2 + rhogsol*rhogeq*k**4*cs**4*w1i**3*rhodeq*w3r*w2r -& rhogsol*rhogeq*k**4*cs**4*w1i**3*rhodeq*w2i*w3i + w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r**2*w2i**2 -& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r*w3r*w3i**2 -& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r*w2r*w2i**2 - w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r*w2r**3 -& w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**2*w2i**4 - w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**4*w2i**2 +& w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**4*w2r**2 - vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w3r*w2i**3 +& w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**2*w2i**4 - w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w2r**4*w3i**2 -& 2*w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**2*w2i**2*w2r**2 -& vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w3r**3*w2i - w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**4*w2i**2 +& w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**2*w2r**4 - 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w2r**2*w3i**3 +& cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3i**4*w2i +& 2*w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**2*w2i**2*w2r**2 -& 2*w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**2*w2i**2*w3i**2 +& w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**4*w2r**2 + rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w3r*w2i**2*w3i**2 +& rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w3r*w2r**2*w3i**2 + rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w2r*w2i**2*w3i**2 +& rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w2r**3*w3r**2 + rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w2r*w2i**2*w3r**2 +& rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w2r**3*w3i**2 + rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w3r**3*w2i**2 +& rhogeq*k*Kdrag*vdsol*w1i**2*rhodeq*w3r**3*w2r**2 + 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w2i**2*w3i**3 +& 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w2i*w2r**2*w3i**2 +& cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3i*w2i**4 + 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w2i**3*w3i**2 +& 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3i*w2r**2*w2i**2 +& cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3i*w2r**4 +& 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3i**2*w2i*w3r**2 +& 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3r**2*w2i**2*w3i -& 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3r**2*w2r**2*w3i -& 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3r**2*w2i*w2r**2 -& 2*cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3r**2*w2i**3 -& 2*w1i*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w2i**3*w3i -& 2*w1i*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**2*w2i**2 -& 2*w1i*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i*w2i*w3r**2 -& 2*w1i*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w2i*w3i*w2r**2 +& 2*w1i*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**2*w2r**2 -& 2*w1i*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**3*w2i) !--break to avoid too many continuation lines rhod1i = rhod1i - (& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r**2*w3i**2 + cs**2*k**2*rhogsol*w1i**2*rhogeq*rhodeq*w3r**4*w2i +& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i**3*w3i + w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3i**2*w2i**2 +& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3i**3*w2i + w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i*w3i*w2r**2 -& 3*w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r**2*w2r**2 +& 4*w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r*w2r*w2i*w3i -& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r*w3r**3 + w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3i*w2i*w3r**2 +& 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w3r*w2r**2*w2i*w3i**2 + w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w3r**2*w2r**2*w3i +& w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w3r**2*w2i**2*w3i + w1r**2*rhogsol*rhogeq*rhodeq*w2r**4*w3i**3 -& 2*rhogsol*rhogeq*k**2*cs**2*w1r*rhodeq*w2r*w3i**4*w2i + 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w3r**3*w2i*w2r**2 +& 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w3i*w2i**2*w3r**2*w2r + 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w3i*w2r**3*w3r**2 +& 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w3r*w2i**3*w3i**2 - 4*vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w2r*w2i*w3i**2 -& vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w2r*w3i**3 - 2*rhogsol*rhogeq*k**2*cs**2*w1r*rhodeq*w2r*w2i*w3r**4 -& 4*rhogsol*rhogeq*k**2*cs**2*w1r*rhodeq*w2r*w2i*w3i**2*w3r**2 -& 2*rhogsol*rhogeq*k**2*cs**2*w1r*rhodeq*w3i*w3r*w2i**4 - 2*rhogsol*rhogeq*k**2*cs**2*w1r*rhodeq*w2r**4*w3i*w3r -& 4*rhogsol*rhogeq*k**2*cs**2*w1r*rhodeq*w3r*w3i*w2r**2*w2i**2 + 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w2i**3*w3r**3 -& vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w2r*w3i*w3r**2 -& vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w3r*w2i*w3i**2 -& vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w3r*w2i*w2r**2 -& 4*vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w3i*w3r*w2i**2 - vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w2r**3*w3i& - vgsol*Kdrag*w1i*k**3*cs**2*rhogeq*rhodeq*w2r*w3i*w2i**2 +& w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w2i*w2r**2*w3i**2 + 4*w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r*w2i*w3r**2 +& w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r*w3i*w3r**2 + w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w3r*w2i*w3i**2 +& w1r**2*rhogsol*rhogeq*rhodeq*w2r**2*w2i*w3r**4 + w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w2r**2*w3i**3 +& w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w2i**2*w3i**3 + w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w3r**2*w2i*w2r**2 +& w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w3r**2*w2i**3 + 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3i**3 +& 2*w1r*w1i*rhogsol*Kdrag*rhodeq*w3i**3*w2r**3 - 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2r**3*w3r**2 -& 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2r*w2i**2*w3r**2 + w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r**3*w3i& + w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r*w3i*w2i**2 + w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r*w3i**3 +& vgsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w3r**2*w2i**2 + w1r*vgsol*k*w1i**2*rhogeq**2*rhodeq*w2i**3*w3i**2 +& w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w3r**3*w2i + w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w3r*w2i**3 +& w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w3r*w2i*w2r**2 + vgsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w3r**2*w2r**2 +& vgsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w3i**2*w2i**2 + vgsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w2r**2*w3i**2 +& 2*w1i*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**2*w2r**2*w3i**2 -& 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w3r**3*w2r**2 +& 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w3r**3*w2i**2 +& 4*w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w3i*w3r*w2r**2 -& 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w3r*w2r**2*w3i**2) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w3r*w2i**2*w3i**2 +& 4*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2r*w2i*w3r**2*w3i +& 4*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2i*w3i*w3r*w2r**2 +& 4*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2i**3*w3i*w3r - 2*w1r*vdsol*Kdrag**2*k*w1i*rhodeq*w3r**2*w2r**2 -& 2*w1r*vdsol*Kdrag**2*k*w1i*rhodeq*w3i**2*w2i**2 - 2*w1r*vdsol*Kdrag**2*k*w1i*rhodeq*w2r**2*w3i**2 -& w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r**4*w2i + 4*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2r*w2i*w3i**3 +& 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2r*w2i**2*w3i**2 - 2*w1r*vdsol*Kdrag**2*k*w1i*rhodeq*w3r**2*w2i**2& - 2*w1r**3*rhogsol*Kdrag*rhodeq*w3r**3*w2r**2 - 2*w1r**3*rhogsol*Kdrag*rhodeq*w2r**3*w3i**2 +& w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r**3*w2i**2 - w1i**4*Kdrag*rhogeq*rhodsol*w2r**2*w3i**2 -& 2*w1r**3*rhogsol*Kdrag*rhodeq*w3r**3*w2i**2 - 2*w1r**3*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3r**2 +& w1r**2*rhogsol*Kdrag*rhodeq*w2i*w2r**2*w3i**3 + w1r**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i**3 +& w1r**2*rhogsol*Kdrag*rhodeq*w3i**4*w2r**2 - w1i**4*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2 -& w1i**4*Kdrag*rhogeq*rhodsol*w3r**2*w2r**2 - w1i**4*Kdrag*rhogeq*rhodsol*w3i**2*w2i**2 +& 2*w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**2*w3i**2 + 2*w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2r**2*w3i**2 +& 3*w1r**2*rhogsol*Kdrag*rhodeq*w3r*w2r**3*w3i**2 + w1r**2*rhogsol*Kdrag*rhodeq*w2r**4*w3i**2 +& w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2r**4 + w1r**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i*w3r**2 +& w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**4 + w1r**2*rhogsol*Kdrag*rhodeq*w2i*w3i*w2r**2*w3r**2 +& 3*w1r**2*rhogsol*Kdrag*rhodeq*w2r*w3r**3*w2i**2 + 2*w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**2*w2r**2 +& 3*w1r**2*rhogsol*Kdrag*rhodeq*w2r*w3r*w2i**2*w3i**2 + 2*w1r**2*rhogsol*Kdrag*rhodeq*w3i**2*w2i**2*w2r**2 +& w1r**2*rhogsol*Kdrag*rhodeq*w3i**2*w2i**4 + w1r**2*rhogsol*Kdrag*rhodeq*w3i**4*w2i**2 +& w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w3r*w2i**2*w3i**2 + w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w3r*w2r**2*w3i**2 +& w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w2r**3*w3i**2 + w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w2r*w2i**2*w3r**2 +& w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w2r*w2i**2*w3i**2 + w1r**2*rhogsol*Kdrag*rhodeq*w3r**4*w2i**2 +& w1r**2*rhogsol*Kdrag*rhodeq*w3r**4*w2r**2 + 3*w1r**2*rhogsol*Kdrag*rhodeq*w3r**3*w2r**3 -& 2*rhogsol*rhogeq*k**2*cs**2*w1i**3*rhodeq*w3i*w2i*w3r**2 - 2*rhogsol*rhogeq*k**2*cs**2*w1i**3*rhodeq*w3i**3*w2i& - 2*rhogsol*rhogeq*k**2*cs**2*w1i**3*rhodeq*w2i*w3i*w2r**2 -& 2*rhogsol*rhogeq*k**2*cs**2*w1i**3*rhodeq*w2i**3*w3i + 2*rhogsol*rhogeq*k**2*cs**2*w1i**3*rhodeq*w3r**2*w2r**2& - 2*rhogsol*rhogeq*k**2*cs**2*w1i**3*rhodeq*w3i**2*w2i**2 + w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w3r**3*w2i**2 +& w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w3r**3*w2r**2 + w1r**2*rhogeq*k*Kdrag*vdsol*rhodeq*w2r**3*w3r**2 +& rhogsol*rhogeq*w1i**3*rhodeq*w2r*w3r*w2i**2*w3i**2 - rhogsol*rhogeq*w1i**3*rhodeq*w2i*w2r**2*w3i**3 -& rhogsol*rhogeq*w1i**3*rhodeq*w2i**3*w3i**3 + w1i**4*rhogsol*Kdrag*rhodeq*w3r**2*w2i**2 +& rhogsol*rhogeq*w1i**3*rhodeq*w2r*w3r**3*w2i**2 - rhogsol*rhogeq*w1i**3*rhodeq*w2i**3*w3i*w3r**2 -& rhogsol*rhogeq*w1i**3*rhodeq*w2i*w3i*w2r**2*w3r**2 + rhogsol*rhogeq*w1i**3*rhodeq*w3r**3*w2r**3 +& rhogsol*rhogeq*w1i**3*rhodeq*w3r*w2r**3*w3i**2 + w1i**4*rhogsol*Kdrag*rhodeq*w3r**2*w2r**2 +& w1i**4*rhogsol*Kdrag*rhodeq*w3i**2*w2i**2 + w1i**4*rhogsol*Kdrag*rhodeq*w2r**2*w3i**2 +& w1r*Kdrag*rhogeq*k*vgsol*w1i**2*rhodeq*w3r**2*w2i**2 + w1r*Kdrag*rhogeq*k*vgsol*w1i**2*rhodeq*w3r**2*w2r**2 +& w1r*Kdrag*rhogeq*k*vgsol*w1i**2*rhodeq*w3i**2*w2i**2 + w1r*Kdrag*rhogeq*k*vgsol*w1i**2*rhodeq*w2r**2*w3i**2 +& w1i*vdsol*k*Kdrag**2*rhodeq*w3r**3*w2r**2 + w1i*vdsol*k*Kdrag**2*rhodeq*w2r**3*w3r**2 +& w1i*vdsol*k*Kdrag**2*rhodeq*w2r*w2i**2*w3r**2 + w1i*vdsol*k*Kdrag**2*rhodeq*w3r*w2i**2*w3i**2 +& w1i*vdsol*k*Kdrag**2*rhodeq*w3r*w2r**2*w3i**2 + w1i*vdsol*k*Kdrag**2*rhodeq*w2r**3*w3i**2) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & w1i*vdsol*k*Kdrag**2*rhodeq*w2r*w2i**2*w3i**2 + Kdrag*cs**2*k**2*rhogsol*w1i**2*rhodeq*w2r**2*w3i**2 -& rhogsol*w1r**3*rhogeq*rhodeq*w2r*w2i**2*w3i**3 - rhogsol*w1r**3*rhogeq*rhodeq*w3i**3*w2r**3 +& w1i*vdsol*k*Kdrag**2*rhodeq*w3r**3*w2i**2 - rhogsol*w1r**3*rhogeq*rhodeq*w3r*w2r**2*w2i*w3i**2 +& Kdrag*cs**2*k**2*rhogsol*w1i**2*rhodeq*w3r**2*w2i**2 + Kdrag*cs**2*k**2*rhogsol*w1i**2*rhodeq*w3r**2*w2r**2 +& Kdrag*cs**2*k**2*rhogsol*w1i**2*rhodeq*w3i**2*w2i**2 - w1i**2*Kdrag*rhogeq*rhodsol*w3i**4*w2i**2 -& w1i**2*Kdrag*rhogeq*rhodsol*w3i**4*w2r**2 - 4*w1i**2*Kdrag*rhogeq*rhodsol*w2i**3*w3i**3 -& rhogsol*w1r**3*rhogeq*rhodeq*w3r**3*w2i*w2r**2 - 2*w1i**2*Kdrag*rhogeq*rhodsol*w3r**2*w2r**2*w3i**2 -& w1i**2*Kdrag*rhogeq*rhodsol*w2r**4*w3i**2 - 2*w1i**2*Kdrag*rhogeq*rhodsol*w3i**2*w2i**2*w2r**2 -& 4*w1i**2*Kdrag*rhogeq*rhodsol*w2i*w2r**2*w3i**3 - 4*w1i**2*Kdrag*rhogeq*rhodsol*w2i*w3i*w2r**2*w3r**2 -& w1i**2*Kdrag*rhogeq*rhodsol*w3i**2*w2i**4 - rhogsol*w1r**3*rhogeq*rhodeq*w2i**3*w3r**3 -& rhogsol*w1r**3*rhogeq*rhodeq*w3i*w2i**2*w3r**2*w2r - rhogsol*w1r**3*rhogeq*rhodeq*w3i*w2r**3*w3r**2 -& rhogsol*w1r**3*rhogeq*rhodeq*w3r*w2i**3*w3i**2 - 2*w1i**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2*w2r**2 -& 4*w1i**2*Kdrag*rhogeq*rhodsol*w2i**3*w3i*w3r**2 - w1i**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**4 -& 2*w1i**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2*w3i**2 - w1i**2*Kdrag*rhogeq*rhodsol*w3r**4*w2r**2 -& w1i**2*Kdrag*rhogeq*rhodsol*w3r**2*w2r**4 + 2*w1i*Kdrag*rhogeq*rhodsol*w2r**4*w3r**2*w3i +& 4*w1i*Kdrag*rhogeq*rhodsol*w2r**2*w3i*w3r**2*w2i**2 + 2*w1i*Kdrag*rhogeq*rhodsol*w2r**4*w3i**3 +& 2*w1i*Kdrag*rhogeq*rhodsol*w2i**4*w3i**3 + 2*w1i*Kdrag*rhogeq*rhodsol*w2r**2*w2i*w3r**4 +& 4*w1i*Kdrag*rhogeq*rhodsol*w3i**2*w2r**2*w2i*w3r**2 + 4*w1i*Kdrag*rhogeq*rhodsol*w2i**3*w3i**2*w3r**2 -& 2*w1i*rhogsol*rhogeq*rhodeq*w3i**4*w2i**2*w2r**2 - w1i*rhogsol*rhogeq*rhodeq*w2r**4*w3i**4 -& w1i*rhogsol*rhogeq*rhodeq*w3i**4*w2i**4 + 2*w1i*Kdrag*rhogeq*rhodsol*w2i**3*w3r**4 -& 2*w1i*rhogsol*rhogeq*rhodeq*w2r**4*w3i**2*w3r**2 + 2*w1i*Kdrag*rhogeq*rhodsol*w2i**4*w3r**2*w3i +& 2*w1i*Kdrag*rhogeq*rhodsol*w2i*w2r**2*w3i**4 + 2*w1i*Kdrag*rhogeq*rhodsol*w3i**4*w2i**3 +& 4*w1i*Kdrag*rhogeq*rhodsol*w2r**2*w2i**2*w3i**3 - w1i**2*Kdrag*rhogeq*rhodsol*w3r**4*w2i**2 +& 2*w1r**2*rhogsol*rhogeq*rhodeq*w2r**2*w2i**2*w3i**3 - w1i*rhogsol*rhogeq*rhodeq*w3r**4*w2i**4 +& w1r**2*rhogsol*rhogeq*rhodeq*w2i**4*w3r**2*w3i + w1r**2*rhogsol*rhogeq*rhodeq*w2r**4*w3r**2*w3i +& w1r**2*rhogsol*rhogeq*rhodeq*w2i*w2r**2*w3i**4 + w1r**2*rhogsol*rhogeq*rhodeq*w2i**4*w3i**3 +& 2*w1r**2*rhogsol*rhogeq*rhodeq*w3i**2*w2r**2*w2i*w3r**2 +& 2*w1r**2*rhogsol*rhogeq*rhodeq*w2r**2*w3i*w3r**2*w2i**2 -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w2r**2*w2i**2 + 2*w1r**2*rhogsol*rhogeq*rhodeq*w2i**3*w3i**2*w3r**2& + w1r**2*rhogsol*rhogeq*rhodeq*w3i**4*w2i**3 - w1i*rhogsol*rhogeq*rhodeq*w3r**4*w2r**4 -& 2*w1i*rhogsol*rhogeq*rhodeq*w3r**4*w2i**2*w2r**2 - 4*w1i*rhogsol*rhogeq*rhodeq*w3r**2*w2i**2*w2r**2*w3i**2 -& 2*w1i*rhogsol*rhogeq*rhodeq*w3i**2*w2i**4*w3r**2 - w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w2i**4 -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2i**3*w3i**2 - 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2i**2*w3i**3 -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3r*w2i*w3i**2 - w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i**4*w2i -& w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w2r**4 - 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w3r*w2i**2*w2r -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w3r*w2r**3 -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2i*w2r**2*w3i**2 -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i**2*w2i*w3r**2 -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r**2*w2i**2*w3i + w1r**2*rhogsol*rhogeq*rhodeq*w2i**3*w3r**4 -& 2*w1r*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3r**3*w2i - vgsol*Kdrag*rhogeq*k**3*cs**2*w1i**3*rhodeq*w2r*w3i -& w1r*rhogsol*rhogeq*w1i**2*rhodeq*w3i**3*w2r**3 + rhogsol*w1r*k**4*cs**4*rhogeq*w1i**2*rhodeq*w2i*w3r -& w1r*rhogsol*rhogeq*w1i**2*rhodeq*w3i*w2r**3*w3r**2 - w1r*rhogsol*rhogeq*w1i**2*rhodeq*w3r*w2i**3*w3i**2 -& w1r*rhogsol*rhogeq*w1i**2*rhodeq*w3r*w2r**2*w2i*w3i**2 - w1r*rhogsol*rhogeq*w1i**2*rhodeq*w3r**3*w2i*w2r**2 -& w1r*rhogsol*rhogeq*w1i**2*rhodeq*w2i**3*w3r**3 + rhogsol*rhogeq*k**2*cs**2*w1i**4*rhodeq*w2r**2*w3i -& w1r*rhogsol*rhogeq*w1i**2*rhodeq*w3i*w2i**2*w3r**2*w2r - w1r*rhogsol*rhogeq*w1i**2*rhodeq*w2r*w2i**2*w3i**3) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & rhogsol*w1r*k**4*cs**4*rhogeq*w1i**2*rhodeq*w2r*w3i - vgsol*Kdrag*rhogeq*k**3*cs**2*w1i**3*rhodeq*w2i*w3r -& w1i*rhogsol*Kdrag*rhodeq*w2i*w2r**2*w3i**4 - w1i*rhogsol*Kdrag*rhodeq*w2i**4*w3i**3 -& w1i*rhogsol*Kdrag*rhodeq*w3i**4*w2i**3 + rhogsol*rhogeq*k**2*cs**2*w1i**4*rhodeq*w2i*w3r**2 -& 2*w1i*rhogsol*Kdrag*rhodeq*w2i**3*w3i**2*w3r**2 - w1i*rhogsol*Kdrag*rhodeq*w2i**4*w3r**2*w3i -& w1i*rhogsol*Kdrag*rhodeq*w2r**4*w3r**2*w3i - w1i*rhogsol*Kdrag*rhodeq*w2r**4*w3i**3 -& 2*w1i*rhogsol*Kdrag*rhodeq*w3i**2*w2r**2*w2i*w3r**2 - 2*w1i*rhogsol*Kdrag*rhodeq*w2r**2*w3i*w3r**2*w2i**2 -& 2*w1i*rhogsol*Kdrag*rhodeq*w2r**2*w2i**2*w3i**3 + rhogsol*rhogeq*k**2*cs**2*w1i**4*rhodeq*w2i**2*w3i +& rhogsol*rhogeq*k**2*cs**2*w1i**4*rhodeq*w2i*w3i**2 + rhogsol*rhogeq*k**4*cs**4*w1r**3*rhodeq*w2i*w3r +& rhogsol*rhogeq*k**4*cs**4*w1r**3*rhodeq*w2r*w3i + rhogsol*Kdrag*w1r**4*rhodeq*w3r**2*w2i**2 +& rhogsol*Kdrag*w1r**4*rhodeq*w3r**2*w2r**2 + rhogsol*Kdrag*w1r**4*rhodeq*w3i**2*w2i**2 +& rhogsol*Kdrag*w1r**4*rhodeq*w2r**2*w3i**2 - w1i*rhogsol*Kdrag*rhodeq*w2i**3*w3r**4 -& w1i*rhogsol*Kdrag*rhodeq*w2r**2*w2i*w3r**4 - vgsol*k**3*cs**2*rhogeq**2*w1r**4*rhodeq*w2i*w3r -& vgsol*k**3*cs**2*rhogeq**2*w1r**4*rhodeq*w2r*w3i + 2*w1r**3*Kdrag*rhogeq*rhodsol*w3r*w2i**2*w3i**2 +& 2*w1r**3*Kdrag*rhogeq*rhodsol*w3r*w2r**2*w3i**2 + 2*w1r**3*Kdrag*rhogeq*rhodsol*w2r**3*w3i**2 +& 2*w1r**3*Kdrag*rhogeq*rhodsol*w2r*w2i**2*w3i**2 + 2*w1r**3*Kdrag*rhogeq*rhodsol*w3r**3*w2i**2 +& 2*w1r**3*Kdrag*rhogeq*rhodsol*w3r**3*w2r**2 + 2*w1r**3*Kdrag*rhogeq*rhodsol*w2r**3*w3r**2 +& 2*w1r**3*Kdrag*rhogeq*rhodsol*w2r*w2i**2*w3r**2 + 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w3r**3*w2r**2 +& 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w2r**3*w3r**2 + 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w2r*w2i**2*w3r**2 +& 2*w1i**3*Kdrag*rhogeq*rhodsol*w2i**2*w3i**3 + 2*w1i**3*Kdrag*rhogeq*rhodsol*w2i**3*w3i**2 +& 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w3r**3*w2i**2 + 2*w1i**3*Kdrag*rhogeq*rhodsol*w3r**2*w2i**3 +& 2*w1i**3*Kdrag*rhogeq*rhodsol*w3r**2*w2r**2*w3i + 2*w1i**3*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2*w3i +& 2*w1i**3*Kdrag*rhogeq*rhodsol*w2i*w2r**2*w3i**2 + 2*w1i**3*Kdrag*rhogeq*rhodsol*w3r**2*w2i*w2r**2 +& 2*w1i**3*Kdrag*rhogeq*rhodsol*w2r**2*w3i**3 + 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w3r*w2i**2*w3i**2 +& 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w3r*w2r**2*w3i**2 + 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w2r**3*w3i**2 +& 2*w1r*w1i**2*Kdrag*rhogeq*rhodsol*w2r*w2i**2*w3i**2 + 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w2i**2*w3i**3 +& 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w2i*w2r**2*w3i**2 + 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w2i**3*w3i**2 +& 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w2r**2*w3i**3 - 2*w1r**2*w1i**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2 -& 2*w1r**2*w1i**2*Kdrag*rhogeq*rhodsol*w3r**2*w2r**2 - 2*w1r**2*w1i**2*Kdrag*rhogeq*rhodsol*w3i**2*w2i**2 -& 2*w1r**2*w1i**2*Kdrag*rhogeq*rhodsol*w2r**2*w3i**2 + 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i*w2r**2 +& 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**3 + 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2r**2*w3i +& 2*w1i*w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2*w3i - 2*w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2*w3i**2 -& 2*w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2r**2*w3i**2 - 4*w1r**2*Kdrag*rhogeq*rhodsol*w3r*w2r**3*w3i**2 -& w1r**2*Kdrag*rhogeq*rhodsol*w2r**4*w3i**2 - 2*w1r**2*Kdrag*rhogeq*rhodsol*w3i**2*w2i**2*w2r**2 -& w1r**2*Kdrag*rhogeq*rhodsol*w3i**2*w2i**4 - w1r**2*Kdrag*rhogeq*rhodsol*w3i**4*w2i**2 -& w1r**2*Kdrag*rhogeq*rhodsol*w3i**4*w2r**2 - w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2r**4 -& 2*w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2*w2r**2 - w1r**2*Kdrag*rhogeq*rhodsol*w3r**2*w2i**4 -& 4*w1r**2*Kdrag*rhogeq*rhodsol*w2r*w3r*w2i**2*w3i**2 - w1r**2*Kdrag*rhogeq*rhodsol*w3r**4*w2i**2 -& w1r**2*Kdrag*rhogeq*rhodsol*w3r**4*w2r**2 - 4*w1r**2*Kdrag*rhogeq*rhodsol*w2r*w3r**3*w2i**2 -& 4*w1r**2*Kdrag*rhogeq*rhodsol*w3r**3*w2r**3 + 2*w1r*Kdrag*rhogeq*rhodsol*w2r*w3i**4*w2i**2) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & 4*w1r*Kdrag*rhogeq*rhodsol*w3r**3*w2r**2*w2i**2 + 4*w1r*Kdrag*rhogeq*rhodsol*w2r**3*w3i**2*w3r**2 +& 4*w1r*Kdrag*rhogeq*rhodsol*w2r*w3i**2*w2i**2*w3r**2 + 2*w1r*Kdrag*rhogeq*rhodsol*w2r*w2i**2*w3r**4 +& 2*w1r*Kdrag*rhogeq*rhodsol*w3r**3*w2i**4 - 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w3r*w2r**2*w2i*w3i**2 -& 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w2r*w2i**2*w3i**3 - 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w3i**3*w2r**3 -& 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w3r*w2i**3*w3i**2 + 2*w1r*Kdrag*rhogeq*rhodsol*w2r**3*w3r**4 +& 2*w1r*Kdrag*rhogeq*rhodsol*w3r**3*w2r**4 + 2*w1r*Kdrag*rhogeq*rhodsol*w3r*w2r**4*w3i**2 +& 2*w1r*Kdrag*rhogeq*rhodsol*w3r*w2i**4*w3i**2 + 4*w1r*Kdrag*rhogeq*rhodsol*w3r*w2r**2*w3i**2*w2i**2 +& 2*w1r*Kdrag*rhogeq*rhodsol*w2r**3*w3i**4 - 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w2i**3*w3r**3 -& 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w3i*w2i**2*w3r**2*w2r - 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w3i*w2r**3*w3r**2 -& 4*w1i*w1r*Kdrag*rhogeq*rhodsol*w3r**3*w2i*w2r**2 - vdsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w3i**2*w2i**2 -& vdsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w2r**2*w3i**2 + vdsol*Kdrag*k**3*cs**2*rhogeq*w1r**3*rhodeq*w2i*w3i -& vdsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w3r**2*w2i**2 - vdsol*Kdrag*k**3*cs**2*rhogeq*w1r**3*rhodeq*w3r*w2r -& vdsol*Kdrag*k*rhogeq*w1r**3*rhodeq*w3r**2*w2r**2 - cs**2*k**2*rhogsol*Kdrag*w1r**2*rhodeq*w3r**2*w2i**2 -& cs**2*k**2*rhogsol*Kdrag*w1r**2*rhodeq*w3r**2*w2r**2 - cs**2*k**2*rhogsol*Kdrag*w1r**2*rhodeq*w3i**2*w2i**2 -& cs**2*k**2*rhogsol*Kdrag*w1r**2*rhodeq*w2r**2*w3i**2 - 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w3r*w2i*w3i**2& - 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w3i*w3r*w2i**2 -& 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w2r**3*w3i - 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w3r**3*w2i +& 2*vgsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r*w2i*w3i -& 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w2r*w2i*w3r**2 -& 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w3i*w3r*w2r**2 -& 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w2r*w3i*w2i**2 -& 2*rhogsol*rhogeq*k**2*cs**2*w1r**3*rhodeq*w2r*w2i*w3i**2 -& 2*vdsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w3r*w2i*w3i -& 2*vdsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r*w2i*w3i -& 2*vgsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r*w3r**2 -& 2*vgsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r**2*w3r +& 2*vgsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w3r*w2i*w3i +& 2*vdsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r*w3r**2 +& 2*vdsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r**2*w3r -& 2*rhogsol*k**4*cs**4*rhogeq*w1r**2*rhodeq*w3r*w2r*w2i - 2*rhogsol*k**4*cs**4*rhogeq*w1r**2*rhodeq*w3r*w2r*w3i -& 2*rhogsol*k**4*cs**4*rhogeq*w1r**2*rhodeq*w2r**2*w3i - vgsol*k**3*cs**2*rhogeq**2*w1r**3*rhodeq*w2i*w3i**2 -& 2*rhogsol*k**4*cs**4*rhogeq*w1r**2*rhodeq*w2i*w3r**2 + 2*vgsol*k**3*cs**2*rhogeq**2*w1r**3*rhodeq*w3r*w2r*w3i +& 2*vgsol*k**3*cs**2*rhogeq**2*w1r**3*rhodeq*w3r*w2r*w2i + vgsol*k**3*cs**2*rhogeq**2*w1r**3*rhodeq*w2r**2*w3i +& w1i*vdsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r*w3i) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & w1i*vdsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2i*w3r + vgsol*k**3*cs**2*rhogeq**2*w1r**3*rhodeq*w2i*w3r**2 -& vgsol*k**3*cs**2*rhogeq**2*w1r**3*rhodeq*w2i**2*w3i - w1i*vgsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2i*w3r -& w1i*vgsol*Kdrag*rhogeq*k**3*cs**2*w1r**2*rhodeq*w2r*w3i - w1r*rhogsol*Kdrag*rhodeq*w2r**3*w3i**4 -& w1r*rhogsol*Kdrag*rhodeq*w2r*w3i**4*w2i**2 - w1r*rhogsol*Kdrag*rhodeq*w3r*w2r**4*w3i**2 -& w1r*rhogsol*Kdrag*rhodeq*w3r*w2i**4*w3i**2 - 2*w1r*rhogsol*Kdrag*rhodeq*w3r*w2r**2*w3i**2*w2i**2 -& 2*w1r*rhogsol*Kdrag*rhodeq*w3r**3*w2r**2*w2i**2 - 2*w1r*rhogsol*Kdrag*rhodeq*w2r**3*w3i**2*w3r**2 -& 2*w1r*rhogsol*Kdrag*rhodeq*w2r*w3i**2*w2i**2*w3r**2 - w1r*rhogsol*Kdrag*rhodeq*w2r**3*w3r**4 -& w1r*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3r**4 - w1r*rhogsol*Kdrag*rhodeq*w3r**3*w2i**4 -& w1r*rhogsol*Kdrag*rhodeq*w3r**3*w2r**4 - 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3r**2 -& 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w3r*w2i**2*w3i**2 - 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w2r**3*w3i**2 -& 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w3r**3*w2i**2 - 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w3r**3*w2r**2 +& w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r*w2r**2*w3i**2 + w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2r**3*w3i**2 +& w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3i**2 + w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r*w2i**2*w3i**2 -& 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w2r**3*w3r**2 - 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w3r*w2r**2*w3i**2 -& 2*w1r*w1i**2*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3i**2 + w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r**3*w2r**2 +& w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2r**3*w3r**2 + w1r*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3r**2 -& 2*w1r**3*rhogsol*Kdrag*rhodeq*w2r**3*w3r**2 - 2*w1r**3*rhogsol*Kdrag*rhodeq*w3r*w2i**2*w3i**2 -& 2*w1r**3*rhogsol*Kdrag*rhodeq*w3r*w2r**2*w3i**2 - 2*w1r**3*rhogsol*Kdrag*rhodeq*w2r*w2i**2*w3i**2 -& 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w3r**3*w2i**2 - 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w3r**3*w2r**2 -& 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w2r**3*w3r**2 - 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w2r*w2i**2*w3r**2 -& 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w3r*w2i**2*w3i**2 - 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w3r*w2r**2*w3i**2& - 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w2r**3*w3i**2 - 2*w1i**2*vgsol*rhogeq*k*Kdrag*rhodeq*w2r*w2i**2*w3i**2 +& w1i*k*vgsol*rhogeq**2*rhodeq*w2r*w3i**4*w2i**2 + w1i*k*vgsol*rhogeq**2*rhodeq*w2r**3*w3i**4 +& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*w1i**2*rhodeq*w3r*w2r -& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*w1i**2*rhodeq*w2i*w3i - vgsol*k**3*cs**2*rhogeq**2*w1i**4*rhodeq*w2i*w3r -& vgsol*k**3*cs**2*rhogeq**2*w1i**4*rhodeq*w2r*w3i + w1i*k*vgsol*rhogeq**2*rhodeq*w3r*w2r**4*w3i**2 +& w1i*k*vgsol*rhogeq**2*rhodeq*w3r*w2i**4*w3i**2 + 2*w1i*k*vgsol*rhogeq**2*rhodeq*w3r*w2r**2*w3i**2*w2i**2 +& w1i*k*vgsol*rhogeq**2*rhodeq*w2r**3*w3r**4 + w1i*k*vgsol*rhogeq**2*rhodeq*w2r*w2i**2*w3r**4 +& w1i*k*vgsol*rhogeq**2*rhodeq*w3r**3*w2i**4 - vdsol*Kdrag*k*w1r*rhogeq*w1i**2*rhodeq*w3i**2*w2i**2 -& vdsol*Kdrag*k*w1r*rhogeq*w1i**2*rhodeq*w2r**2*w3i**2 - vdsol*Kdrag*k*w1r*rhogeq*w1i**2*rhodeq*w3r**2*w2r**2 +& w1i*k*vgsol*rhogeq**2*rhodeq*w3r**3*w2r**4 + 2*w1i*k*vgsol*rhogeq**2*rhodeq*w3r**3*w2r**2*w2i**2 +& 2*w1i*k*vgsol*rhogeq**2*rhodeq*w2r**3*w3i**2*w3r**2 + 2*w1i*k*vgsol*rhogeq**2*rhodeq*w2r*w3i**2*w2i**2*w3r**2 -& 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w2i**2*w3i**3 - 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w2i*w2r**2*w3i**2 -& 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i**2 - 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w2r**2*w3i**3 -& 2*vgsol*w1r**2*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2i*w3r -& 2*vgsol*w1r**2*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2r*w3i -& vdsol*Kdrag*k*w1r*rhogeq*w1i**2*rhodeq*w3r**2*w2i**2 - 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i*w2r**2 -& 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**3 - 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2r**2*w3i -& 2*w1i*w1r**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**2*w3i - vgsol*k*w1r*rhogeq**2*rhodeq*w3i**4*w2i**3 -& 2*vgsol*k*w1r*rhogeq**2*rhodeq*w2r**2*w2i**2*w3i**3 - 2*vgsol*k*w1r*rhogeq**2*rhodeq*w2r**2*w3i*w3r**2*w2i**2 -& vgsol*k*w1r*rhogeq**2*rhodeq*w2r**4*w3i**3 - vgsol*k*w1r*rhogeq**2*rhodeq*w2i**4*w3i**3 -& vgsol*k*w1r*rhogeq**2*rhodeq*w2i**4*w3r**2*w3i - vgsol*k*w1r*rhogeq**2*rhodeq*w2r**4*w3r**2*w3i -& vgsol*k*w1r*rhogeq**2*rhodeq*w2i*w2r**2*w3i**4 - vgsol*k*w1r*rhogeq**2*rhodeq*w2r**2*w2i*w3r**4 -& 2*vgsol*k*w1r*rhogeq**2*rhodeq*w3i**2*w2r**2*w2i*w3r**2 - 2*vgsol*k*w1r*rhogeq**2*rhodeq*w2i**3*w3i**2*w3r**2 +& rhogsol*w1i*k**4*cs**4*rhogeq*w1r**2*rhodeq*w3r*w2r - rhogsol*w1i*k**4*cs**4*rhogeq*w1r**2*rhodeq*w2i*w3i) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & 2*w1i**2*rhogsol*Kdrag*rhodeq*w3i**2*w2i**2*w2r**2 - vgsol*k*w1r*rhogeq**2*rhodeq*w2i**3*w3r**4 +& 2*w1i**2*rhogsol*Kdrag*rhodeq*w3r**2*w2r**2*w3i**2 + w1i**2*rhogsol*Kdrag*rhodeq*w3r**2*w2r**4 +& 2*w1i**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**2*w2r**2 + w1i**2*rhogsol*Kdrag*rhodeq*w3r*w2r**3*w3i**2 +& w1i**2*rhogsol*Kdrag*rhodeq*w2r*w3r*w2i**2*w3i**2 + w1i**2*rhogsol*Kdrag*rhodeq*w3r**3*w2r**3 +& 3*w1i**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i*w3r**2 + w1i**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**4 +& 3*w1i**2*rhogsol*Kdrag*rhodeq*w2i*w3i*w2r**2*w3r**2 + w1i**2*rhogsol*Kdrag*rhodeq*w3r**4*w2r**2 +& 2*w1i**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**2*w3i**2 + w1i**2*rhogsol*Kdrag*rhodeq*w2r**4*w3i**2 +& w1i**2*rhogsol*Kdrag*rhodeq*w3i**2*w2i**4 + w1i**2*rhogsol*Kdrag*rhodeq*w3i**4*w2i**2 +& w1i**2*rhogsol*Kdrag*rhodeq*w3i**4*w2r**2 + 3*w1i**2*rhogsol*Kdrag*rhodeq*w2i*w2r**2*w3i**3 +& 3*w1i**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i**3 + 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w2i**3*w3i +& 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w2i*w3i*w2r**2 +& 4*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w3i**2*w2i**2 -& 8*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2r*w2i*w3i + w1i**2*rhogsol*Kdrag*rhodeq*w2r*w3r**3*w2i**2 +& 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3r**3 +& 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w3i*w2i*w3r**2 +& 4*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w3r**2*w2r**2 - w1r*vgsol*k*Kdrag**2*rhodeq*w2i**2*w3i**3 -& w1r*vgsol*k*Kdrag**2*rhodeq*w2i**3*w3i**2 - w1r*vgsol*k*Kdrag**2*rhodeq*w2r**2*w3i**3 +& 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3r*w3i**2 +& 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2r*w2i**2 +& 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2r**3 + 2*w1i*vgsol*w1r*k**3*cs**2*rhogeq**2*rhodeq*w3i**3*w2i& - w1i*rhogsol*w1r**2*rhogeq*rhodeq*w2i**3*w3i**3 + w1i**2*rhogsol*Kdrag*rhodeq*w3r**4*w2i**2 -& w1i*rhogsol*w1r**2*rhogeq*rhodeq*w2i*w3i*w2r**2*w3r**2 + w1i*rhogsol*w1r**2*rhogeq*rhodeq*w3r*w2r**3*w3i**2 +& w1i*rhogsol*w1r**2*rhogeq*rhodeq*w2r*w3r*w2i**2*w3i**2 - w1i*rhogsol*w1r**2*rhogeq*rhodeq*w2i**3*w3i*w3r**2 -& w1i*rhogsol*w1r**2*rhogeq*rhodeq*w2i*w2r**2*w3i**3 - w1r*vgsol*k*Kdrag**2*rhodeq*w3r**2*w2i*w2r**2 -& w1r*vgsol*k*Kdrag**2*rhodeq*w3r**2*w2i**3 - w1r*vgsol*k*Kdrag**2*rhodeq*w3r**2*w2r**2*w3i -& w1r*vgsol*k*Kdrag**2*rhodeq*w3r**2*w2i**2*w3i - w1r*vgsol*k*Kdrag**2*rhodeq*w2i*w2r**2*w3i**2 +& w1i*rhogsol*w1r**2*rhogeq*rhodeq*w3r**3*w2r**3 + w1i*rhogsol*w1r**2*rhogeq*rhodeq*w2r*w3r**3*w2i**2 +& vgsol*Kdrag**2*k*rhodeq*w3r*w2r**2*w2i*w3i**2 + vgsol*Kdrag**2*k*rhodeq*w2r*w2i**2*w3i**3 +& vgsol*Kdrag**2*k*rhodeq*w3i**3*w2r**3 + vgsol*Kdrag**2*k*rhodeq*w3r**3*w2i*w2r**2 +& vgsol*Kdrag**2*k*rhodeq*w3i*w2r**3*w3r**2 + vgsol*Kdrag**2*k*rhodeq*w3r*w2i**3*w3i**2 -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w3i*w3r*w2i**2 -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w3i*w3r*w2r**2 + vgsol*Kdrag**2*k*rhodeq*w2i**3*w3r**3 -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w2r*w2i*w3r**2 + vgsol*Kdrag**2*k*rhodeq*w3i*w2i**2*w3r**2*w2r -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w3r**3*w2i -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w3r*w2i*w3i**2 -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w2r**3*w3i -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w2r*w3i*w2i**2 -& 2*w1r*rhogsol*k**2*cs**2*rhogeq*w1i**2*rhodeq*w2r*w2i*w3i**2 + 2*w1i*w1r*vgsol*k*Kdrag**2*rhodeq*w3r**2*w2r**2& + 2*w1i*w1r*vgsol*k*Kdrag**2*rhodeq*w3i**2*w2i**2 + 2*w1i*w1r*vgsol*k*Kdrag**2*rhodeq*w3r**2*w2i**2 +& 2*w1i*w1r*vgsol*k*Kdrag**2*rhodeq*w2r**2*w3i**2 + vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2r*w3i**3 -& 2*vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2r*w2i*w3i**2) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & 2*vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w3i*w3r*w2r**2 - vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2r**3*w3i& - vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2r*w3i*w2i**2 + vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w3r*w2i**3& - 2*vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w3i*w3r*w2i**2 -& vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w3r**3*w2i + vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w3r*w2i*w2r**2 -& vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w3r*w2i*w3i**2 - w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w3r*w2r**2*w2i*w3i**2& - w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w2r*w2i**2*w3i**3 + vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2r*w3i*w3r**2 -& w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w3r*w2i**3*w3i**2 - w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w3i**3*w2r**3 +& 2*vgsol*k**3*cs**2*rhogeq**2*w1i**2*rhodeq*w2r*w2i*w3r**2 - w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w3r**3*w2i*w2r**2 -& w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w2i**3*w3r**3 - w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w3i*w2i**2*w3r**2*w2r -& w1i*rhogeq*k*Kdrag*vdsol*rhodeq*w3i*w2r**3*w3r**2 + vdsol*Kdrag*k**3*cs**2*rhogeq*w1i**3*rhodeq*w2i*w3r +& vdsol*Kdrag*k**3*cs**2*rhogeq*w1i**3*rhodeq*w2r*w3i +& 2*w1r*vgsol*rhogeq**2*k**3*cs**2*w1i**2*rhodeq*w3r*w2r*w2i -& w1r*vgsol*rhogeq**2*k**3*cs**2*w1i**2*rhodeq*w2i**2*w3i -& w1r*vgsol*rhogeq**2*k**3*cs**2*w1i**2*rhodeq*w2i*w3i**2 +& w1r*vgsol*rhogeq**2*k**3*cs**2*w1i**2*rhodeq*w2i*w3r**2 +& 2*w1r*vgsol*rhogeq**2*k**3*cs**2*w1i**2*rhodeq*w3r*w2r*w3i +& w1r*vgsol*rhogeq**2*k**3*cs**2*w1i**2*rhodeq*w2r**2*w3i - vgsol*k**3*cs**2*rhogeq**2*w1i**3*rhodeq*w2r*w3r**2 -& vgsol*k**3*cs**2*rhogeq**2*w1i**3*rhodeq*w2r**2*w3r - w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i**2 +& vgsol*k**3*cs**2*rhogeq**2*w1i**3*rhodeq*w2i**2*w3r + 2*vgsol*k**3*cs**2*rhogeq**2*w1i**3*rhodeq*w2r*w2i*w3i +& vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r**3*w2r**2 - vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r**3*w2i**2 +& vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r**3*w3r**2 - w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**2*w3i -& w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2r**2*w3i**3 - w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2i**2*w3i**3 -& w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w2i*w2r**2*w3i**2 +& 4*w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w2i*w3i**2 -& w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i*w2r**2 - w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r**2*w2r**2*w3i +& w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2i*w2r**2 +& 4*w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3i*w3r*w2i**2 +& w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w3i*w3r**2 +& w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2i*w3i**2 - w1i*cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r**2*w2i**3 +& vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i**2*w3i**3 + 2*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3i**4*w2i +& w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r**3*w2i + w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2i**3 +& w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r**3*w3i + w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w3i*w2i**2 +& w1i*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w3i**3 + vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2i**3*w3i**2 +& vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2r**2*w2i*w3i**2 + 2*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w3r*w2i**4 +& vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r**3*w2i*w2r**2 + vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w2r**3*w3r**2 +& 4*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i*w3i**2*w3r**2 + 2*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i*w3r**4& + vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r*w2i**2*w3i**2 + vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2i**3*w3r**3 +& vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i*w2i**2*w3r**2*w2r +& 4*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r*w3i*w2r**2*w2i**2 + 2*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r**4*w3i*w3r& + vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3i**3*w2r**3 + 2*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r*w2i*w3r**2*w3i -& vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r*w2r**2*w3i**2 - vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r*w2i**2*w3r**2& + vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r*w2i**2*w3i**2) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & 2*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i*w3i*w3r*w2r**2 +& 2*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i**3*w3i*w3r + vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r**3*w3i**2 +& 2*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r*w2i*w3i**3 - vdsol*Kdrag**2*k*rhodeq*w3r*w2r**2*w2i*w3i**2 -& vdsol*Kdrag**2*k*rhodeq*w2r*w2i**2*w3i**3 - vdsol*Kdrag**2*k*rhodeq*w3r**3*w2i*w2r**2 -& vdsol*Kdrag**2*k*rhodeq*w3i*w2i**2*w3r**2*w2r - vdsol*Kdrag**2*k*rhodeq*w3i*w2r**3*w3r**2 -& vdsol*Kdrag**2*k*rhodeq*w3r*w2i**3*w3i**2 - vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r**3*w2r**2 +& vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w3r**3*w2i**2 - vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r**3*w3r**2 -& w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w3r**3*w2r**2 - w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w2r**3*w3r**2 -& w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w2r*w2i**2*w3r**2 +& 2*w1i*w1r*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i*w3i**2 +& 2*w1i*w1r*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r**2*w3i +& 2*w1i*w1r*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i**2*w3i -& w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w3r*w2i**2*w3i**2 - w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w3r*w2r**2*w3i**2 -& w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w2r**3*w3i**2 - w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w2r*w2i**2*w3i**2 -& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2i*w3i*w2r**2 - w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2i**3*w3i -& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3i**2*w2i**2 - w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r**2*w3i**2& + 2*w1i*w1r*vgsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i*w3r**2 -& 4*w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2r*w2i*w3i -& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3i**3*w2i - w1i*w1r**2*vgsol*k*rhogeq**2*rhodeq*w3r**3*w2i**2 -& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3i*w2i*w3r**2 -& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r**2*w2i**2 +& 3*w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r**2*w2r**2 - vgsol*k*rhogeq**2*w1i**3*rhodeq*w2r*w2i**2*w3i**2 +& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w3r**3 - vgsol*k*rhogeq**2*w1i**3*rhodeq*w2r**3*w3r**2 -& vgsol*k*rhogeq**2*w1i**3*rhodeq*w2r*w2i**2*w3r**2 - vgsol*k*rhogeq**2*w1i**3*rhodeq*w3r*w2i**2*w3i**2 -& vgsol*k*rhogeq**2*w1i**3*rhodeq*w3r*w2r**2*w3i**2 - vdsol*Kdrag**2*k*rhodeq*w2i**3*w3r**3 -& vgsol*k*rhogeq**2*w1i**3*rhodeq*w3r**3*w2r**2 - vgsol*k*rhogeq**2*w1i**3*rhodeq*w2r**3*w3i**2 +& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w3r*w3i**2 +& w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2r*w2i**2 + w1r*vgsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2r**3 -& vgsol*k*rhogeq**2*w1i**3*rhodeq*w3r**3*w2i**2 - vdsol*Kdrag**2*k*rhodeq*w3i**3*w2r**3 +& w1r*vdsol*k*Kdrag**2*rhodeq*w2i**2*w3i**3 + w1r*vdsol*k*Kdrag**2*rhodeq*w2i*w2r**2*w3i**2 +& w1r*vdsol*k*Kdrag**2*rhodeq*w2i**3*w3i**2 + w1r*vdsol*k*Kdrag**2*rhodeq*w3r**2*w2r**2*w3i +& w1r*vdsol*k*Kdrag**2*rhodeq*w3r**2*w2i**2*w3i + w1r*vdsol*k*Kdrag**2*rhodeq*w2r**2*w3i**3 +& w1r*vdsol*k*Kdrag**2*rhodeq*w3r**2*w2i*w2r**2 + w1r*vdsol*k*Kdrag**2*rhodeq*w3r**2*w2i**3 +& cs**2*k**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i*w3r**2 - cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r*w2r**3*w3i**2 -& cs**2*k**2*rhogsol*Kdrag*rhodeq*w2r*w3r*w2i**2*w3i**2 - cs**2*k**2*rhogsol*Kdrag*rhodeq*w3r**3*w2r**3 +& cs**2*k**2*rhogsol*Kdrag*rhodeq*w2i*w3i*w2r**2*w3r**2 - w1r**4*Kdrag*rhogeq*rhodsol*w2r**2*w3i**2 +& vgsol*Kdrag*k**3*cs**2*rhogeq*w1r**3*rhodeq*w3r*w2r - w1r**4*Kdrag*rhogeq*rhodsol*w3i**2*w2i**2 -& vgsol*Kdrag*k**3*cs**2*rhogeq*w1r**3*rhodeq*w2i*w3i - cs**2*k**2*rhogsol*Kdrag*rhodeq*w2r*w3r**3*w2i**2 +& cs**2*k**2*rhogsol*Kdrag*rhodeq*w2i*w2r**2*w3i**3 + cs**2*k**2*rhogsol*Kdrag*rhodeq*w2i**3*w3i**3 +& 2*w1i**2*rhogsol*rhogeq*rhodeq*w2i**3*w3i**2*w3r**2 + w1i**2*rhogsol*rhogeq*rhodeq*w2i**4*w3r**2*w3i +& w1i**2*rhogsol*rhogeq*rhodeq*w2r**4*w3r**2*w3i + w1i**2*rhogsol*rhogeq*rhodeq*w2r**4*w3i**3 +& 2*w1i**2*rhogsol*rhogeq*rhodeq*w3i**2*w2r**2*w2i*w3r**2) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & 2*w1i**2*rhogsol*rhogeq*rhodeq*w2r**2*w3i*w3r**2*w2i**2 + w1i**2*rhogsol*rhogeq*rhodeq*w2i*w2r**2*w3i**4 +& w1i**2*rhogsol*rhogeq*rhodeq*w2i**4*w3i**3 + w1i**2*rhogsol*rhogeq*rhodeq*w3i**4*w2i**3 +& 2*w1i**2*rhogsol*rhogeq*rhodeq*w2r**2*w2i**2*w3i**3 - w1r**4*Kdrag*rhogeq*rhodsol*w3r**2*w2i**2 -& w1r**4*Kdrag*rhogeq*rhodsol*w3r**2*w2r**2 + w1r**3*vgsol*k*rhogeq**2*rhodeq*w2i**2*w3i**3 +& w1r**3*vgsol*k*rhogeq**2*rhodeq*w2i*w2r**2*w3i**2 + w1r**3*vgsol*k*rhogeq**2*rhodeq*w2i**3*w3i**2 +& w1r**3*vgsol*k*rhogeq**2*rhodeq*w2r**2*w3i**3 + w1i**2*rhogsol*rhogeq*rhodeq*w2i**3*w3r**4 +& w1i**2*rhogsol*rhogeq*rhodeq*w2r**2*w2i*w3r**4 + w1r**3*vgsol*k*rhogeq**2*rhodeq*w3r**2*w2i*w2r**2 +& w1r**3*vgsol*k*rhogeq**2*rhodeq*w3r**2*w2r**2*w3i + w1r**3*vgsol*k*rhogeq**2*rhodeq*w3r**2*w2i**2*w3i +& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*w1i**2*rhodeq*w2i*w3i +& 2*w1r**2*rhogsol*rhogeq*k**2*cs**2*w1i**2*rhodeq*w2i*w3r**2 +& 2*w1r**2*rhogsol*rhogeq*k**2*cs**2*w1i**2*rhodeq*w2i**2*w3i -& w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*w1i**2*rhodeq*w3r*w2r - 2*rhogsol*rhogeq*k**4*cs**4*w1i**2*rhodeq*w3r*w2r*w3i& + 2*rhogsol*rhogeq*k**4*cs**4*w1i**2*rhodeq*w2i**2*w3i + 2*rhogsol*rhogeq*k**4*cs**4*w1i**2*rhodeq*w2i*w3i**2 +& 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2i**2*w3i**3 + 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2i*w2r**2*w3i**2 +& 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2i**3*w3i**2 + 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2i**2*w3i -& 2*rhogsol*rhogeq*k**4*cs**4*w1i**2*rhodeq*w3r*w2r*w2i +& 2*w1r**2*rhogsol*rhogeq*k**2*cs**2*w1i**2*rhodeq*w2i*w3i**2 +& 2*w1r**2*rhogsol*rhogeq*k**2*cs**2*w1i**2*rhodeq*w2r**2*w3i +& 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2i**3 + 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2r**2*w3i +& 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2r**2*w3i**3 + 2*Kdrag*vgsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w2r*w2i*w3i& + 2*Kdrag*vgsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w2r*w3i**2 -& 2*Kdrag*vdsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w2r*w3i**2 +& 2*Kdrag*vgsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w2i**2*w3r -& 2*Kdrag*vdsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w2i**2*w3r -& 2*Kdrag*vdsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w3r*w2i*w3i + w1r**3*vgsol*k*rhogeq**2*rhodeq*w3r**2*w2i**3 -& 2*Kdrag*vdsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w2r*w2i*w3i +& 2*Kdrag*vgsol*rhogeq*k**3*cs**2*w1i**2*rhodeq*w3r*w2i*w3i +& 2*w1i*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2i*w2r**2 - w1i*vgsol*Kdrag**2*k*rhodeq*w3r**3*w2i**2 -& w1i*vgsol*Kdrag**2*k*rhodeq*w3r**3*w2r**2 - w1i*vgsol*Kdrag**2*k*rhodeq*w2r**3*w3r**2 -& w1i*vgsol*Kdrag**2*k*rhodeq*w2r*w2i**2*w3r**2 - w1i*vgsol*Kdrag**2*k*rhodeq*w2r*w2i**2*w3i**2 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r**3*w2r**2 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i**2*w3r**2 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i*w3r**2*w3i -& 2*w1i*w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r*w3r**2 - 2*w1i*w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2i**2*w3r -& 2*w1i*w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r**2*w3r - 2*w1i*w1r*rhogsol*rhogeq*k**4*cs**4*rhodeq*w2r*w3i**2 -& w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w2i**3*w3i - 3*w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3i**2*w2i**2) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w2r**2*w3i**2 + 2*w1r**2*rhogsol*Kdrag*w1i**2*rhodeq*w3r**2*w2r**2 +& 2*w1r**2*rhogsol*Kdrag*w1i**2*rhodeq*w3i**2*w2i**2 + 2*w1r**2*rhogsol*Kdrag*w1i**2*rhodeq*w2r**2*w3i**2 -& w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3i*w2i*w3r**2 + w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r**2*w2r**2 +& w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w2r*w3r*w3i**2 + w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r**2*w2i**2 +& 4*w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r*w2r*w2i*w3i + w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r*w2r**3 +& w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r*w2r*w2i**2 - w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3i**3*w2i -& w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w2i*w3i*w2r**2 - 2*w1i*w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i*w3r**2& - 2*w1i*w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i**2*w3i -& 2*w1i*w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2i*w3i**2 -& 2*w1i*w1r*vdsol*Kdrag*rhogeq*k**3*cs**2*rhodeq*w2r**2*w3i + 2*w1r**2*rhogsol*Kdrag*w1i**2*rhodeq*w3r**2*w2i**2& + rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r**2*w2i**2*w3i - 2*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3i*w3r*w2r**3 -& 2*rhogsol*k**4*cs**4*rhogeq*rhodeq*w2r*w3r*w2i*w3i**2 + rhogsol*k**4*cs**4*rhogeq*rhodeq*w2i**3*w3i**2 +& rhogsol*k**4*cs**4*rhogeq*rhodeq*w2i*w2r**2*w3i**2 - rhogsol*k**4*cs**4*rhogeq*rhodeq*w2r**2*w3i**3 +& w1i*rhogsol*k**4*cs**4*rhogeq*rhodeq*w2r*w3r**3 + w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**4*w2i -& 2*rhogsol*k**4*cs**4*rhogeq*rhodeq*w2r*w3r**3*w2i - rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r**2*w2i*w2r**2 -& rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r**2*w2r**2*w3i - 2*rhogsol*k**4*cs**4*rhogeq*rhodeq*w3i*w3r*w2i**2*w2r +& w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i*w2r**4 + w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i*w2i**4 +& 2*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i*w2r**2*w2i**2 +& w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3r**4*w2i + 4*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w2r*w3r**3*w2i +& 2*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i**2*w2i*w3r**2 + vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w2i*w2r**2*w3i**3& + rhogsol*k**4*cs**4*rhogeq*rhodeq*w2i**2*w3i**3 - vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w2r*w3r*w2i**2*w3i**2 +& 4*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i*w3r*w2i**2*w2r +& 4*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w3i*w3r*w2r**3 +& 4*w1r**2*rhogsol*rhogeq*k**2*cs**2*rhodeq*w2r*w3r*w2i*w3i**2 - rhogsol*k**4*cs**4*rhogeq*rhodeq*w3r**2*w2i**3 -& vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w3r**3*w2r**3 + w1i*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3i**2 -& vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w2r*w3r**3*w2i**2 + vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w2i**3*w3i*w3r**2 +& vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w2i*w3i*w2r**2*w3r**2 - vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w3r*w2r**3*w3i**2 +& vdsol*Kdrag*k*w1r*rhogeq*rhodeq*w2i**3*w3i**3 + Kdrag*rhogeq*k*vgsol*rhodeq*w2r**3*w3i**4 +& Kdrag*rhogeq*k*vgsol*rhodeq*w2r*w3i**4*w2i**2 + 2*Kdrag*rhogeq*k*vgsol*rhodeq*w3r*w2r**2*w3i**2*w2i**2 -& w1i*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3r**2 -& w1i*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r**2*w3r +& w1i*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2i**2*w3r +& 2*w1i*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2i*w3i +& 2*w1i*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i*w3i +& 2*Kdrag*rhogeq*k*vgsol*rhodeq*w2r*w3i**2*w2i**2*w3r**2 + Kdrag*rhogeq*k*vgsol*rhodeq*w3r*w2r**4*w3i**2 +& Kdrag*rhogeq*k*vgsol*rhodeq*w3r*w2i**4*w3i**2 + Kdrag*rhogeq*k*vgsol*rhodeq*w3r**3*w2i**4 +& Kdrag*rhogeq*k*vgsol*rhodeq*w3r**3*w2r**4 + 2*Kdrag*rhogeq*k*vgsol*rhodeq*w2r**3*w3i**2*w3r**2 +& Kdrag*rhogeq*k*vgsol*rhodeq*w2r*w2i**2*w3r**4 + 2*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**3*w2r**2*w2i**2 -& vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3i**3 + vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3i*w2i**2 +& 2*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i*w3i**2 -& 2*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w3i*w3r*w2r**2 + vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r**3*w3i& - vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2i**3) !--break to avoid too many continuation lines rhod1i = rhod1i - ( & 2*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w3i*w3r*w2i**2 +& 2*w1r*cs**2*k**2*rhogsol*w1i*rhogeq*rhodeq*w2r**3*w3i**2 -& vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3i*w3r**2 -& vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2i*w2r**2 + vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w3r**3*w2i -& 2*vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i*w3r**2 -& vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w2i**2*w3i**2 + vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2r**2*w3i**2& - vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w3r*w2i**2*w3i**2 -& 2*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2i*w3i*w3r*w2r**2 -& 2*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w2i*w3r**2*w3i -& 2*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2i**3*w3i*w3r - vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r**3*w3i**2 -& 2*vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w2i*w3i**3 + vdsol*Kdrag*k**3*cs**2*rhogeq*rhodeq*w2r*w2i**2*w3r**2& + 2*vgsol*k**3*cs**2*rhogeq**2*w1i**3*rhodeq*w3r*w2i*w3i + vgsol*k**3*cs**2*rhogeq**2*w1i**3*rhodeq*w2r*w3i**2& + w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w3i**3*w2r**3 + w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w3i*w2i**2*w3r**2*w2r +& w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w3r*w2i**3*w3i**2 + w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w3r*w2r**2*w2i*w3i**2 +& w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w2r*w2i**2*w3i**3 - 2*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3i**2*w2i**2*w2r**2 +& w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w2i**3*w3r**3 + w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**3*w2i*w2r**2 +& w1i*Kdrag*rhogeq*k*vgsol*rhodeq*w3i*w2r**3*w3r**2 - w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3i**2*w2i**4 -& w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3i**4*w2i**2 - 3*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2i*w3i*w2r**2*w3r**2 -& 2*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2i**2*w2r**2 - w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2r**4*w3i**2 -& w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3i**4*w2r**2 - 3*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2i*w2r**2*w3i**3 -& 3*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2i**3*w3i**3 + vgsol*w1r**2*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2i*w3i**2 +& Kdrag*rhogeq*k*vgsol*rhodeq*w2r**3*w3r**4 - 2*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2i**2*w3i**2 -& 2*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2r**2*w3i**2 - w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2r*w3r*w2i**2*w3i**2& - 3*w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2i**3*w3i*w3r**2 - w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2i**4 -& w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**2*w2r**4 - w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**4*w2r**2 -& w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**3*w2r**3 - w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w2r*w3r**3*w2i**2 +& rhogsol*rhogeq*k**2*cs**2*w1r**4*rhodeq*w2i*w3r**2 + rhogsol*rhogeq*k**2*cs**2*w1r**4*rhodeq*w2i**2*w3i +& rhogsol*rhogeq*k**2*cs**2*w1r**4*rhodeq*w2i*w3i**2 + rhogsol*rhogeq*k**2*cs**2*w1r**4*rhodeq*w2r**2*w3i -& w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r**4*w2i**2 - w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r**4*w3r -& w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2i**4 - 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2i**3*w3i*w3r -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2i**2*w3r*w2r**2 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2i*w3i*w3r*w2r**2 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w3r*w2r**2*w3i**2 - w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3i**4 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w2i*w3i**3 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3i**2*w3r**2 -& 2*w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r**3*w3r**2 - w1i*vgsol*k**3*cs**2*rhogeq**2*rhodeq*w2r*w3r**4 -& w1i*vgsol*Kdrag**2*k*rhodeq*w3r*w2i**2*w3i**2 - w1i*vgsol*Kdrag**2*k*rhodeq*w3r*w2r**2*w3i**2 -& w1i*vgsol*Kdrag**2*k*rhodeq*w2r**3*w3i**2 - w1r*Kdrag*rhogeq*k*vgsol*rhodeq*w3r*w2r**3*w3i**2) rhod1i = rhod1i/(w1i**2 -& 2*w3i*w1i + w3r**2 + w1r**2 + w3i**2 - 2*w3r*w1r)/(w3r**2 + w3i**2)/Kdrag/(w2r**2 + w1r**2 + w2i**2 - 2*w2i*w1i& - 2*w2r*w1r + w1i**2)/rhogeq/(w2i**2 + w2r**2) endif !print*,'w1 = ',w1r,w1i !print*,'w2 = ',w2r,w2i !print*,'w3 = ',w3r,w3i !if (iplot.eq.2) then ! print "(a,3('(',es10.3,',',es10.3,') '))",' vgas = ',vg1r,vg1i,vg2r,vg2i,vg3r,vg3i ! if (Kdrag.gt.0.) then ! print "(a,3('(',es10.3,',',es10.3,') '))",' vdust = ',vd1r,vd1i,vd2r,vd2i,vd3r,vd3i ! endif !else ! print "(a,3('(',es10.3,',',es10.3,') '))",' rhog = ',rhog1r,rhog1i,rhog2r,rhog2i,rhog3r,rhog3i ! if (Kdrag.gt.0.) then ! print "(a,3('(',es10.3,',',es10.3,') '))",' rhod = ',rhod1r,rhod1i,rhod2r,rhod2i,rhod3r,rhod3i ! endif !endif !------------------------------- ! F I N A L S O L U T I O N !------------------------------- do i=1,size(xplot) xk = 2.*pi/lambda*(xplot(i)-x0) arg1 = xk - w1r*time arg2 = xk - w2r*time arg3 = xk - w3r*time vgas = vgeq & + vg1r*exp(w1i*time)*cos(arg1) - vg1i*exp(w1i*time)*sin(arg1) & + vg2r*exp(w2i*time)*cos(arg2) - vg2i*exp(w2i*time)*sin(arg2) & + vg3r*exp(w3i*time)*cos(arg3) - vg3i*exp(w3i*time)*sin(arg3) vdust = vdeq & + vd1r*exp(w1i*time)*cos(arg1) - vd1i*exp(w1i*time)*sin(arg1) & + vd2r*exp(w2i*time)*cos(arg2) - vd2i*exp(w2i*time)*sin(arg2) & + vd3r*exp(w3i*time)*cos(arg3) - vd3i*exp(w3i*time)*sin(arg3) rhogas = rhogeq & + rhog1r*exp(w1i*time)*cos(arg1) - rhog1i*exp(w1i*time)*sin(arg1) & + rhog2r*exp(w2i*time)*cos(arg2) - rhog2i*exp(w2i*time)*sin(arg2) & + rhog3r*exp(w3i*time)*cos(arg3) - rhog3i*exp(w3i*time)*sin(arg3) rhodust = rhodeq & + rhod1r*exp(w1i*time)*cos(arg1) - rhod1i*exp(w1i*time)*sin(arg1) & + rhod2r*exp(w2i*time)*cos(arg2) - rhod2i*exp(w2i*time)*sin(arg2) & + rhod3r*exp(w3i*time)*cos(arg3) - rhod3i*exp(w3i*time)*sin(arg3) select case(iplot) case(4) yplot(i) = rhodust case(3) yplot(i) = rhogas case(2) yplot(i) = vdust case default yplot(i) = vgas end select enddo return end subroutine exact_dustywave end module dustywaves splash/src/exact_ringspread.f90000644 000766 000000 00000022542 13261626263 017422 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !---------------------------------------------------------------------- ! Plots Lynden-Bell & Pringle (1978) solution for viscous ! ring spreading in an accretion disk. ! ! Input: radius in disk ! Output: surface density ! ! D. Price, UofExeter 25.2.08 !---------------------------------------------------------------------- module ringspread implicit none public :: exact_ringspread, ringspreadfunc private contains subroutine exact_ringspread(iplot,time,Mdisk,Rdisk,viscnu,xplot,yplot,ierr) implicit none integer, intent(in) :: iplot real, intent(in) :: time,Mdisk,Rdisk,viscnu real, intent(in), dimension(:) :: xplot real, intent(out), dimension(size(xplot)) :: yplot integer, intent(out) :: ierr real, parameter :: pi = 3.1415926536 integer :: i real :: R2,sigma,tvisc double precision :: tau,x ! ! check for errors in input parameters ! ierr = 0 if (Mdisk.le.0.) then print*,'error: mass <= 0 in exact_ringspread' ierr = 2 return elseif (Rdisk.le.0.) then print*,'error: rdisk < 0 in exact_ringspread' ierr = 3 return elseif (viscnu.le.tiny(viscnu)) then print*,'error: viscosity <= 0 in ringspreading solution' ierr = 4 return endif R2 = Rdisk*Rdisk tvisc = R2/(12.*viscnu) tau = time/tvisc print "(a,1pe9.2,a,1pe9.2,a,0pf6.2,a,f6.2)", & ' Plotting ring spreading solution: tau = ',tau,' nu = ',viscnu,' R0 = ',Rdisk,' M = ',Mdisk do i=1,size(xplot) x = xplot(i)/Rdisk sigma = Mdisk/real((pi*R2))*ringspreadfunc(x,tau) !print*,'x = ',xplot(i),Rdisk,tau,sigma select case(iplot) case(1) !--density yplot(i) = sigma case default !--pressure yplot(i) = 0. end select enddo return end subroutine exact_ringspread !---------------------------------------------------------------------- ! evaluates the surface density as a function of x and tau !---------------------------------------------------------------------- double precision function ringspreadfunc(x,tau) implicit none double precision, intent(in) :: x, tau double precision :: xfunc,besfunc,dummy,term if (tau.le.epsilon(tau) .or. x.le.tiny(x)) then ringspreadfunc = 0. else xfunc = 2.*x/tau term = exp(-(1.+x*x)/tau) !--prevent blowups at t=0: no point evaluating ! the Bessel function if the exp term is zero. if (term.gt.tiny(term)) then call bessik(xfunc,0.25d0,besfunc,dummy,dummy,dummy) else besfunc = 0. endif ringspreadfunc = 1./(tau*x**0.25)*term*besfunc !print*,'ringspreadfunc = ',x,xfunc,-(1.+x*x)/tau,exp(-(1.+x*x)/tau),ringspreadfunc,xfunc,tau,besfunc endif return end function ringspreadfunc !---------------------------------------------------------------------- ! remainder of this file are routines for evaluating the modified ! Bessel function in the above solution !---------------------------------------------------------------------- subroutine bessik(x,xnu,ri,rk,rip,rkp) implicit none integer, parameter :: maxit = 10000 double precision, intent(in) :: x, xnu double precision, intent(out) :: ri,rip,rk,rkp double precision, parameter :: eps = 1.e-10, fpmin = 1.e-30, & xmin = 2., pi = 3.141592653589793d0 ! Returns the modified Bessel functions ri = I\nu, rk = K\nu and their ! derivatives rip = I'\nu and rkp = K'\nu, for positive x and for ! xn = \nu .ge. 0. The relative accuracy is within one or two significant ! digits of eps. ! ! All internal arithmetic in double precision ! ! This routine written by Daniel Price, UofExeter 25/2/08 ! Adapted from Press et. al. (1992) Numerical Recipes in FORTRAN 77 ! integer:: i,l,nl double precision :: a,a1,b,c,d,del,del1,delh,dels,e,f,fact,fact2, & ff,gam1,gam2,gammi,gampl,h,p,pimu,q,q1,q2,qnew,ril,ril1,rimu,rip1,ripl, & ritemp,rk1,rkmu,rkmup,rktemp,s,sum,sum1,x2,xi,xi2,xmu,xmu2 if (x <= 0. .or. xnu < 0.) then print*,' bad arguments in bessik ',x,xnu ! return endif nl = int(xnu+0.5d0) ! nl is the number of downward recurrences of the I's xmu = xnu - nl ! and upward recurrences of K's. xmu lies between xmu2 = xmu*xmu ! -1/2 and 1/2 xi = 1.d0/x xi2 = 2.d0*xi h = xnu*xi if (h.lt.fpmin) h = fpmin b = xi2*xnu d = 0.d0 c = h do i=1,maxit b = b + xi2 d = 1.d0/(b + d) c = b + 1.d0/c del = c*d h = del*h if (abs(del-1.d0).lt.eps) goto 1 enddo print*,'x too large in bessik; try asymptotic expansion' 1 continue ril = fpmin ripl = h*ril ril1 = ril rip1 = ripl fact = xnu*xi do l=nl,1,-1 ritemp = fact*ril + ripl fact = fact - xi ripl = fact*ritemp + ril ril = ritemp enddo f = ripl/ril if (x < xmin) then x2 = 0.5d0*x pimu = pi*xmu if (abs(pimu).lt.eps) then fact = 1.d0 else fact = pimu/sin(pimu) endif d = -log(x2) e = xmu*d if (abs(e).lt.eps) then fact2 = 1.d0 else fact2 = sinh(e)/e endif ! Chebyshev evaluation of Gamma1, Gamma2 call beschb(xmu,gam1,gam2,gampl,gammi) ff = fact*(gam1*cosh(e) + gam2*fact2*d) ! f0 sum = ff e = exp(e) p = 0.5d0*e/gampl q = 0.5d0/(e*gammi) c = 1.d0 d = x2*x2 sum1 = p do i=1,maxit ff = (i*ff + p + q)/(i*i - xmu2) c = c*d/i p = p/(i - xmu) q = q/(i + xmu) del = c*ff sum = sum + del del1 = c*(p - i*ff) sum1 = sum1 + del1 if (abs(del).lt.abs(sum)*eps) goto 2 enddo print*,' bessk series failed to converge' 2 continue rkmu = sum rk1 = sum1*xi2 else b = 2.d0*(1.d0 + x) d = 1.d0/b delh = d h = delh q1 = 0.d0 q2 = 1.d0 a1 = 0.25d0 - xmu2 c = a1 q = c a = -a1 s = 1.d0 + q*delh do i=2,maxit a = a - 2*(i-1) c = -a*c/i qnew = (q1 - b*q2)/a q1 = q2 q2 = qnew q = q + c*qnew b = b + 2.d0 d = 1.d0/(b + a*d) delh = (b*d - 1.d0)*delh h = h + delh dels = q*delh s = s + dels if (abs(dels/s).lt.eps) goto 3 enddo print*,'bessik: failure to converge in cf2' 3 continue h = a1*h rkmu = sqrt(pi/(2.d0*x))*exp(-x)/s rk1 = rkmu*(xmu + x + 0.5d0 - h)*xi endif rkmup = xmu*xi*rkmu - rk1 rimu = xi/(f*rkmu - rkmup) ri = (rimu*ril1)/ril rip = (rimu*rip1)/ril do i=1,nl rktemp = (xmu + i)*xi2*rk1 + rkmu rkmu = rk1 rk1 = rktemp enddo rk = rkmu rkp = xnu*xi*rkmu - rk1 return end subroutine bessik subroutine beschb(x,gam1,gam2,gampl,gammi) implicit none integer, parameter :: nuse1=7,nuse2=8 double precision, intent(in) :: x double precision, intent(out) :: gam1,gam2,gammi,gampl ! ! Evaluates Gamma_1 and Gamma_2 by Chebyshev expansion for |x| < 1/2. ! Also returns 1/Gamma(1 + x) and 1/Gamma(1-x). ! ! In double precision, set NUSE1 = 7, NUSE2 = 8. ! In single precision, set NUSE1 = 2, NUSE2 = 5. ! ! This routine written by Daniel Price, UofExeter 25/2/08 ! Adapted from Press et. al. (1992) Numerical Recipes in FORTRAN 77 ! double precision :: xx,c1(7),c2(8) save c1,c2 data c1/-1.142022680371168d0, 6.5165112670737d-3, & 3.087090173086d-4, -3.4706269649d-6,6.9437664d-9, & 3.67795d-11,-1.356d-13/ data c2/1.843740587300905d0, -7.68528408447867d-2, & 1.2719271366546d-3, -4.9717367042d-6, -3.31261198d-8, & 2.423096d-10, -1.702d-13, -1.49d-15/ xx = 8.d0*x*x - 1.d0 ! multiply x by 2 to make range be -1 to 1, gam1 = chebev(-1.d0,1.d0,c1,NUSE1,xx) ! and then apply transformation for gam2 = chebev(-1.d0,1.d0,c2,NUSE2,xx) ! evaluating even Chebyshev series gampl = gam2 - x*gam1 gammi = gam2 + x*gam1 return end subroutine beschb double precision function chebev(a,b,c,m,x) implicit none integer, intent(in) :: m double precision, intent(in) :: a,b,x,c(m) ! ! Chebyshev evaluation: All arguments are input. c(1:m) is an array of ! Chebyshev coefficients, the first m elements of c output from chebft ! (which must have been called with the same a and b). The Chebyshev ! polynomial \sum_{k=1}^m c_k T_{k-1}(y) - c1/2 is evaluated at a ! point y = [ x - (b + a)/2 ] / [(b - a)/2], and the result is returned ! as the function value. ! ! This routine written by Daniel Price, UofExeter 25/2/08 ! Adapted from Press et. al. (1992) Numerical Recipes in FORTRAN 77 ! integer :: j double precision :: d, dd, sv, y, y2 if ((x-a)*(x-b).gt.0.) then print*,'error: x not in range in chebev' chebev = 0. return endif d = 0. dd = 0. y = (2.*x - a - b)/(b - a) ! change of variable y2 = 2.*y do j=m,2,-1 ! Clenshaw's recurrence sv = d d = y2*d - dd + c(j) dd = sv enddo chebev = y*d - dd + 0.5*c(1) ! last step is different end function chebev end module ringspread splash/src/options_render.f90000644 000766 000000 00000034113 13261626263 017127 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2017 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! Module containing settings and options relating to renderings ! includes default values of these options and submenu for changing them !------------------------------------------------------------------------- module settings_render use colourbar, only:ColourBarDisp,iplotcolourbarlabel,ColourBarPosx,ColourBarPosy,& ColourBarLen,ColourBarFmtStr,ColourBarWidth use labels, only:lenlabel use kernels, only:ikernel implicit none integer :: ncontours,npix,icolours,iColourBarStyle,iColourBarPos logical :: iplotcont_nomulti,ilabelcont logical :: icolour_particles,inormalise_interpolations logical :: ifastrender,idensityweightedinterpolation logical :: double_rendering character(len=lenlabel+20) :: projlabelformat integer :: iapplyprojformat namelist /renderopts/ npix,icolours,ncontours,iplotcont_nomulti, & icolour_particles,ColourBarDisp,inormalise_interpolations, & ifastrender,idensityweightedinterpolation,iColourBarStyle, & iplotcolourbarlabel,ilabelcont,projlabelformat,iapplyprojformat, & double_rendering,ikernel,ColourBarPosx,ColourBarPosy,ColourBarLen,& ColourBarFmtStr,ColourBarWidth,iColourBarPos contains !--------------------------------------------- ! set default values for these options !--------------------------------------------- subroutine defaults_set_render implicit none icolours = 2 ! colour scheme to use npix = 0 ! pixels in x direction for rendering iColourBarStyle = 1 ! whether or not to plot the colour bar and style iplotcont_nomulti = .false. ! plot contours icolour_particles = .false. ! colour particles instead of using pixels ncontours = 30 ! number of contours to plot ColourBarDisp = 5.0 ! displacement of colour bar label from edge inormalise_interpolations = .false. ! do not normalise interpolations ifastrender = .false. ! use accelerated rendering idensityweightedinterpolation = .false. iplotcolourbarlabel = .true. ilabelcont = .false. ! print numeric labels on contours projlabelformat = ' ' iapplyprojformat = 0 double_rendering = .false. ikernel = 0 ! just take default kernel ColourBarPosx = 0.75 ! default values used for floating colour bars ColourBarPosy = 0.7 ColourBarLen = 0.25 ColourBarWidth = 2. ColourBarFmtStr = 'BCMSTV' iColourBarPos = 3 return end subroutine defaults_set_render !--------------------------------------------- ! set default values for 360 video !--------------------------------------------- subroutine defaults_set_render_360 use colourbar, only:set_floating_bar_style iColourBarStyle = 7 call set_floating_bar_style(iColourBarStyle,4) end subroutine defaults_set_render_360 !----------------------------------------------------------------------------- ! options for rendered plots !----------------------------------------------------------------------------- subroutine submenu_render(ichoose) use colourbar, only:maxcolourbarstyles,labelcolourbarstyles,barisvertical,& isfloating,iscustombar,labelfloatingstyles,& set_floating_bar_style,maxfloatingstyles use colours, only:schemename,ncolourschemes,colour_demo use prompting, only:prompt,print_logical use params, only:maxplot use plotlib, only:plotlib_supports_alpha use filenames, only:fileprefix use kernels, only:select_kernel,kernelname,nkernels use projections3D, only:setup_integratedkernel implicit none integer, intent(in) :: ichoose character(len=5) :: string character(len=20) :: kname integer :: ians,i,ierr,icolourprev ! !--rendering options ! ians = ichoose print "(a)",'----------------- rendering options -------------------' if (ians.le.0 .or. ians.gt.8) then if (npix.gt.0) then write(string,"(i5)") npix else string = 'AUTO' endif kname = '' if (ikernel.ge.0 .and. ikernel.le.nkernels) kname = trim(kernelname(ikernel)) print 10,trim(string),icolours,print_logical(iplotcont_nomulti),ncontours, & iColourBarStyle,print_logical(icolour_particles), & print_logical(inormalise_interpolations),print_logical(ifastrender),& print_logical(idensityweightedinterpolation),trim(projlabelformat),& trim(kname) 10 format( & ' 0) exit ',/, & ' 1) set number of pixels ( ',a,' )',/, & ' 2) change colour scheme (',i2,' )',/, & ' 3) 2nd render/contour prompt ( ',a,' )',/, & ' 4) change number of contours (',i3,' )',/, & ' 5) colour bar options ( ',i2,' )',/,& ' 6) use particle colours not pixels ( ',a,' )',/,& ' 7) normalise interpolations ( ',a,' )',/,& ' 8) use accelerated rendering ( ',a,' )',/,& ' 9) use density weighted interpolation ( ',a,' )',/, & ' 10) customize label on projection plots ( ',a,' )',/,& ' 11) change kernel ( ',a,' )') call prompt('enter option',ians,0,11) endif ! !--options ! select case(ians) !------------------------------------------------------------------------ case(1) print "(5(/,a),/)",' Note: setting number of pixels = 0 means that ', & ' the number of pixels will be automatically ', & ' chosen to match the device used for plotting.', & ' The number of pixels is then determined by ', & ' the page size (set in the p)age menu).' call prompt('enter number of pixels along x axis (0=auto)',npix,0,100000) !------------------------------------------------------------------------ case(2) ierr = 1 icolourprev = icolours write(*,"(i2,a,1x)") (i,': '//trim(schemename(i)),i=1,ncolourschemes) write(*,"(i2,a,1x)") ncolourschemes+1,': demo plot of all schemes' print "(a)",'(-ve = inverse, 0 = contours only)' promptloop: do while (ierr /= 0) call prompt('enter colour scheme for rendering ',icolours,-ncolourschemes,ncolourschemes+1) ! ! demonstration plot of all colour schemes ! ierr = 0 if (abs(icolours).eq.ncolourschemes+1) then call colour_demo icolours = icolourprev ierr = 1 endif enddo promptloop !------------------------------------------------------------------------ case(3) if (icolours.eq.0) then print "(2(/,a),/)",' Warning: this option has no effect if colour scheme 0 is set', & ' (cannot plot contours on top of contours)' endif if (plotlib_supports_alpha) then call prompt(' allow contour/double render prompt?',iplotcont_nomulti) print "(3(/,a),/)",' Double rendering renders the first quantity in black and white', & ' and the second in colour with a transparent background ', & ' (such that data below the colour bar minimum appears transparent)' call prompt('use double rendering instead of contours?',double_rendering) else call prompt('allow contour plotting prompt?',iplotcont_nomulti) endif if (double_rendering) then print "(a)",' Second render prompt is '//trim(print_logical(iplotcont_nomulti)) else print "(a)",' Contour plotting prompt is '//trim(print_logical(iplotcont_nomulti)) endif if ((iplotcont_nomulti .or. icolours.eq.0) .and. .not.double_rendering) then call prompt('enter number of contours between min,max',ncontours,0,500) call prompt('plot contour labels?',ilabelcont) endif !------------------------------------------------------------------------ case(4) print "(5(/,a),/)",& ' To set contour levels and level labels manually, create a file called', & ' '''//trim(fileprefix)//'.contours'' in the working directory, with the following format:',& ' 1.0 label1 ', & ' 2.0 label2 ', & ' ...' call prompt('otherwise, enter number of contours between min,max',ncontours,0,500) call prompt('plot contour labels?',ilabelcont) !------------------------------------------------------------------------ case(5) do i=0,maxcolourbarstyles print "(i2,')',1x,a)",i,trim(labelcolourbarstyles(i)) enddo call prompt('enter colour bar style to use ',iColourBarStyle,0,maxcolourbarstyles) print "(a,/)",'colour bar style = '//trim(labelcolourbarstyles(iColourBarStyle)) if (iColourBarStyle.gt.0) then if (isfloating(iColourBarStyle)) then print "(5(a,/),a)",' Positioning of floating colour bar: ', & (trim(labelfloatingstyles(i)),i=1,maxfloatingstyles) call prompt('enter option',iColourBarPos,1,maxfloatingstyles) if (iColourBarPos >= 1 .and. iColourBarPos < maxfloatingstyles) ColourBarLen = 0.25 if (iColourBarPos == maxfloatingstyles) then call prompt('enter x position of colour bar as fraction of viewport',ColourBarPosx,-1.,1.5) call prompt('enter y position of colour bar as fraction of viewport',ColourBarPosy,-1.,1.5) call prompt('enter length of colour bar as fraction of viewport',ColourBarLen,0.,1.) else call set_floating_bar_style(iColourBarStyle,iColourBarPos) endif endif call prompt('plot colour bar label?',iplotcolourbarlabel) if (barisvertical(iColourBarStyle) .and. iplotcolourbarlabel) then call prompt('enter displacement of text from edge (character heights) ', & ColourBarDisp) endif if (iscustombar(iColourBarStyle)) then call prompt('enter width of colour bar in character heights',ColourBarWidth,0.,20.) if (barisvertical(iColourBarstyle)) then print "(a)",' A=axis,B=bottom,C=top,T=major ticks,S=minor ticks,N=labels,V=vertical,L=log,M=labels above' else print "(a)",' B=left,C=right,T=major ticks,S=minor ticks,N=labels,L=log,M=labels to left' if (ColourBarFmtStr.eq.'BCMSTV') then ColourBarFmtStr='BCNST' ! use default string for horizontal bars instead endif endif call prompt('enter format code string for colour bar ticks/numbering',ColourBarFmtStr) endif endif !------------------------------------------------------------------------ case(6) icolour_particles = .not.icolour_particles print "(a)",' Use particle colours instead of pixels is ' & //trim(print_logical(icolour_particles)) !------------------------------------------------------------------------ case(7) inormalise_interpolations = .not.inormalise_interpolations print "(a)",' Normalisation of interpolations is ' & //trim(print_logical(inormalise_interpolations)) !------------------------------------------------------------------------ case(8) ifastrender = .not.ifastrender print "(a)",' Accelerated rendering is '//trim(print_logical(ifastrender)) if (ifastrender) then print*,' Warning: this is slightly approximate (particle position' print*,' assumed to be at centre of pixel)' endif !------------------------------------------------------------------------ case(9) idensityweightedinterpolation = .not.idensityweightedinterpolation print "(a)",' Density weighted interpolation is '// & print_logical(idensityweightedinterpolation) !------------------------------------------------------------------------ case(10) print "(5(/,a),/,4(/,a),/)", & ' Example format strings: ', & ' \(2268) %l d%z %uz : this is the default format "\int rho [g/cm^3] dz [cm]"', & ' column %l : would print "column density" for density', & ' surface %l : would print "surface density"', & ' %l integrated through %z : would print "density integrated through z"', & ' Format codes: ', & ' %l : label for rendered quantity ', & ' %z : label for ''z'' ', & ' %uz : units label for z (only if physical units applied)' call prompt(' enter label format for projection plots: ',projlabelformat) call prompt(' enter which column to apply format to (0=all) ',iapplyprojformat,0,maxplot) !------------------------------------------------------------------------ case(11) do i=0,nkernels print "(1x,i1,')',1x,a)",i,trim(kernelname(i)) enddo call prompt(' enter kernel to use for interpolations (0=default)',ikernel,0,nkernels) call select_kernel(ikernel) call setup_integratedkernel ! need to redo the kernel table if kernel has changed end select return end subroutine submenu_render end module settings_render splash/src/read_data_gadget_jsb.f90000644 000766 000000 00000024116 13261626263 020154 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2016 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR OUTPUT FROM THE GADGET CODE ! AS MODIFIED BY JAMIE BOLTON ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,istart,ipos,nstepsread) use particle_data use params use labels use settings_data, only:ndim,ndimV,ncolumns,ncalc use mem_allocation implicit none integer, intent(IN) :: istart,ipos integer, intent(OUT) :: nstepsread character(LEN=*), intent(IN) :: rootname character(LEN=LEN(rootname)+10) :: datfile integer, dimension(maxparttypes) :: npartoftypei integer, dimension(:), allocatable :: iamtemp integer :: i,itype,icol,ifile,idashpos,ierr integer :: index1,index2,indexstart,indexend,Nmassesdumped integer :: ncol_max,npart_max,nstep_max,ntoti logical :: iexist,reallocate real(doub_prec) :: timetemp real(doub_prec), dimension(6) :: Massoftype real, dimension(:), allocatable :: dattemp1 real, dimension(:,:), allocatable :: dattemp nstepsread = 0 if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: ',trim(datfile),' file not found ***' return endif ! !--set parameters which do not vary between timesteps ! ndim = 3 ndimV = 3 ncol_max = 15 ! 3 x pos, 3 x vel, utherm, rho, Ne, h, pmass ! !--read data from snapshots ! i = istart print "(1x,a)",'reading Jamie Bolton''s modified GADGET format' write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open data file and read data ! open(11,ERR=81,file=datfile,status='old',form='unformatted') ! !--read header for this timestep ! read(11,ERR=70,end=80) npartoftypei,Massoftype,timetemp ntoti = int(sum(npartoftypei)) print*,'time : ',timetemp print*,'Npart (by type) : ',npartoftypei print*,'Mass (by type) : ',Massoftype print*,'N_gas : ',npartoftypei(1) print*,'N_total : ',ntoti ! !--if successfully read header, increment the nstepsread counter ! nstepsread = nstepsread + 1 ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,1) if (ntoti.gt.maxpart) then reallocate = .true. npart_max = int(1.1*ntoti) endif if (i.ge.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncol_max+ncalc,maxcol)) endif ! !--copy header into header arrays ! npartoftype(:,i) = npartoftypei time(i) = real(timetemp) if (ntoti.gt.0) then if (allocated(dattemp)) deallocate(dattemp) allocate(dattemp(3,ntoti)) ! !--read positions of all particles ! print*,'positions ',ntoti read (11, iostat=ierr) dattemp(1:3,1:ntoti) if (ierr /= 0) then print "(a)",'error encountered whilst reading positions ' return else do icol=1,3 dat(1:ntoti,icol,i) = dattemp(icol,1:ntoti) enddo endif ! !--same for velocities ! print*,'velocities ',ntoti read (11, iostat=ierr) dattemp(1:3,1:ntoti) if (ierr /= 0) then print "(a)",'error encountered whilst reading velocities' else do icol=4,6 dat(1:ntoti,icol,i) = dattemp(icol-3,1:ntoti) enddo endif ! !--read particle ID ! print*,'particle ID ',ntoti if (allocated(iamtemp)) deallocate(iamtemp) allocate(iamtemp(npart_max)) read (11, end=66,ERR=73) iamtemp(1:ntoti) deallocate(iamtemp) ! !--read particle masses ! !--work out total number of masses dumped Nmassesdumped = 0 do itype = 1,6 if (abs(Massoftype(itype)).lt.1.e-8) then Nmassesdumped = Nmassesdumped + Npartoftype(itype,i) endif enddo print*,'particle masses ',Nmassesdumped !--read this number of entries if (allocated(dattemp1)) deallocate(dattemp1) allocate(dattemp1(Nmassesdumped)) if (Nmassesdumped.gt.0) then read(11,end=66,err=74) dattemp1(1:Nmassesdumped) endif !--now copy to the appropriate sections of the .dat array indexstart = 1 index1 = 1 do itype=1,6 if (Npartoftype(itype,i).ne.0) then index2 = index1 + Npartoftype(itype,i) -1 if (abs(Massoftype(itype)).lt.1.e-8) then ! masses dumped indexend = indexstart + Npartoftype(itype,i) - 1 print*,'read ',Npartoftype(itype,i),' masses for type ', & itype,index1,'->',index2,indexstart,'->',indexend dat(index1:index2,7,i) = dattemp1(indexstart:indexend) else ! masses not dumped print*,'setting masses for type ',itype,' = ', & real(Massoftype(itype)),index1,'->',index2 dat(index1:index2,7,i) = real(Massoftype(itype)) endif index1 = index2 + 1 indexstart = indexend + 1 endif enddo deallocate(dattemp1) ! !--read other quantities for rest of particles ! print*,'gas properties ',npartoftype(1,i) do icol=8,15 !!print*,icol read (11, end=66,ERR=78) dat(1:npartoftype(1,i),icol,i) ! !--for some reason the smoothing length output by GADGET is ! twice the usual SPH smoothing length ! if (icol.eq.15) then dat(1:npartoftype(1,i),icol,i) = 0.5*dat(1:npartoftype(1,i),icol,i) endif enddo else ntoti = 1 npartoftype(1,i) = 1 dat(:,:,i) = 0. endif !!ntot(i-1) = j-1 ! !--now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. goto 68 66 continue print*,'*** end of file reached in ',trim(datfile),' ***' ! timestep there but data incomplete goto 68 68 continue ! !--close data file and return ! close(unit=11) ncolumns = ncol_max print*,'ncolumns = ',ncolumns print*,'>> Finished reading: steps =',nstepsread-istart+1, & 'last step ntot =',sum(npartoftype(:,istart+nstepsread-1)) return ! !--errors ! 70 continue print*,' *** Error encountered while reading timestep header ***' print*,' Npartoftype = ',Npartoftype(:,i) print*,' Massoftype = ',Massoftype return 73 continue print*,' *** Error encountered while reading particle ID ***' return 74 continue print*,' *** Error encountered while reading particle masses ***' return 78 continue print*,' *** Error encountered while reading gas particle properties ***' return 80 continue print*,' *** data file empty, no steps read ***' return 81 continue print*,' *** Error: can''t open data file ***' return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo ivx = 4 ipmass = 7 irho = 9 ! location of rho in data array ipr = 0 iutherm = 8 ! thermal energy ih = 15 ! smoothing length ! !--set labels of the quantities read in ! label(ix(1:ndim)) = labelcoord(1:ndim,1) label(irho) = '\gr' label(iutherm) = 'u' label(10) = 'NHp' label(11) = 'NHep' label(12) = 'NHepp' label(13) = 'NH0' label(14) = 'NHe0' label(ih) = 'h' label(ipmass) = 'particle mass' ! !--set labels for vector quantities ! iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'\d'//labelcoord(i,1) enddo !--set labels for each particle type ! ntypes = 6 labeltype(1) = 'gas' labeltype(2) = 'dark matter' labeltype(3) = 'boundary 1' labeltype(4) = 'boundary 2' labeltype(5) = 'star' labeltype(6) = 'sink / black hole' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2:6) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/exact_gresho.f90000644 000766 000000 00000003570 13261626263 016553 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2012 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! compute exact solution for gresho vortex problem ! ---------------------------------------------------------------------- module gresho implicit none contains subroutine exact_gresho(iplot,xplot,yplot,ierr) implicit none integer, intent(in) :: iplot real, intent(in), dimension(:) :: xplot real, intent(out), dimension(size(xplot)) :: yplot integer, intent(out) :: ierr print*,'plotting gresho vortex ' ! ! check for errors ! ierr = 0 select case(iplot) case(2) ! pressure where (xplot < 0.2) yplot = 5. + 12.5*xplot**2 elsewhere (xplot < 0.4) yplot = 9. + 12.5*xplot**2 - 20.*xplot + 4.*log(5.*xplot) elsewhere yplot = 3. + 4.*log(2.) end where case(1) ! vphi where (xplot < 0.2) yplot = 5.*xplot elsewhere (xplot < 0.4) yplot = 2. - 5.*xplot elsewhere yplot = 0. end where end select return end subroutine exact_gresho end module gresho splash/src/cubicsolve.f90000644 000766 000000 00000016133 13261626263 016235 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2011 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- module cubic implicit none contains !------------------------------------------------------------- ! this subroutine finds the real solutions to ! a cubic equation of the form ! ! a*x^3 + b*x^2 + c*x + d ! ! formulae taken from: ! Woan, The Cambridge Handbook of Physics Formulas, 2000, p51 ! ! input : a,b,c,d : coefficients of cubic polynomial ! output : x(3) : array containing up to 3 real solutions ! => x(1:nreal) non-zero, rest set to zero ! nreal : number of real solutions ! ! Daniel Price, 22/2/07 ! dprice@astro.ex.ac.uk !------------------------------------------------------------- subroutine cubicsolve(a,b,c,d,x,nreal,check) implicit none real, intent(in) :: a,b,c,d real, intent(out), dimension(3) :: x integer, intent(out) :: nreal logical, intent(in), optional :: check real :: p,q,det,sqrtdet real :: a2,b2,u,v,y1,y2,y3,term,phi real, parameter :: eps = 1000.*epsilon(0.) real, parameter :: pi = 3.14159265358979323846 integer :: i x = 0. ! !--handle all trivial cases (quadratic, linear, all zero) ! if (abs(a).lt.eps) then det = c**2 - 4.*b*d if (det.lt.0.) then ! no solutions to quadratic nreal = 0 else if (abs(b).lt.eps) then !--no solutions if a = 0, b = 0, c = 0 if (abs(c).lt.eps) then nreal = 0 else !--solve linear equation if a = 0, b = 0 nreal = 1 x(1) = -d/c endif else !--solve quadratic for a = 0 nreal = 2 sqrtdet = sqrt(det) x(1) = 0.5*(-c + sqrtdet)/b x(2) = 0.5*(-c - sqrtdet)/b endif endif else ! !--cubic solution ! a2 = a**2 b2 = b**2 p = (c/a - b2/(3.*a2)) q = (2.*b**3/(27.*a2*a) - b*c/(3.*a2) + d/a) det = (p**3)/27. + 0.25*q**2 ! !--determine number of solutions ! if (det.lt.0.) then !--3 distinct real roots nreal = 3 term = sqrt(abs(p)/3.) phi = ACOS(-0.5*q*term**(-3)) !--these are the solutions to the reduced cubic ! y^3 + py + q = 0 y1 = 2.*term*COS(phi/3.) y2 = -2.*term*COS((phi + pi)/3.) y3 = -2.*term*COS((phi - pi)/3.) else !--1 real, 2 complex roots term = -0.5*q + sqrt(det) !--must take cube root of positive quantity, then give sign later ! (otherwise gives NaNs) u = (abs(term))**(1/3.)*SIGN(1.0,term) term = -0.5*q - sqrt(det) v = (abs(term))**(1/3.)*SIGN(1.0,term) nreal = 1 y1 = u + v !--if det=0, 3 real roots, but at least 2 equal, so max of 2 unique roots) if (abs(det).lt.tiny(det)) then nreal = 2 y2 = -(u + v)/2. endif y3 = 0. endif !--return solutions to original cubic, not reduced cubic term = b/(3.*a) if (nreal.ge.1) x(1) = y1 - term if (nreal.ge.2) x(2) = y2 - term if (nreal.ge.3) x(3) = y3 - term endif if (present(check)) then if (check) then !--verify the cubic solution print*,'verifying: ',a,'x^3 + ',b,'x^2 + ',c,'x + ',d do i=1,nreal term = a*x(i)**3 + b*x(i)**2 + c*x(i) + d if (abs(term).lt.eps) then print*,'root ',i,':',x(i),'f=',term,': OK' else print*,'root ',i,':',x(i),'f=',term,': FAILED',eps endif enddo endif endif return end subroutine cubicsolve !------------------------------------------------------------- ! this subroutine returns both the real and complex ! solutions to a cubic equation of the form ! ! x^3 + b*x^2 + c*x + d ! ! input : b,c,d : coefficients of cubic polynomial ! output : x(3) : array of 3 COMPLEX solutions ! nreal : number of real solutions ! ! The form of the equation above means that we ! do not need to handle trivial cases (quadratic, etc.) ! and that there will always be 3 solutions. ! ! Daniel Price, daniel.price@monash.edu 21/01/2011 ! !------------------------------------------------------------- subroutine cubicsolve_complex(b,c,d,x,nreal,check) implicit none real, intent(in) :: b,c,d complex, intent(out), dimension(3) :: x integer, intent(out), optional :: nreal logical, intent(in), optional :: check double precision :: p,q,q2,xi double precision :: b2,term,termA,det,phi real :: termr,termi double precision :: fx,dfx real, parameter :: eps = 1000.*epsilon(0.) double precision, parameter :: pi = 3.14159265358979323846d0 integer :: i,j,nroots x = (0.,0.) ! !--preliminaries ! b2 = b*b p = (c - b2/3.) q = (2.*b2*b - 9.*b*c + 27.*d)/27. q2 = q*q det = (p*p*p)/27. + 0.25*q2 if (det < 0) then !--3 distinct real roots nroots = 3 term = sqrt(abs(p)/3.) phi = ACOS(-0.5*q*term**(-3)) !--these are the solutions to the reduced cubic ! y^3 + py + q = 0 x(1) = real(2.d0*term*COS(phi/3.d0)) x(2) = real(-2.d0*term*COS((phi + pi)/3.d0)) x(3) = real(-2.d0*term*COS((phi - pi)/3.d0)) else !--1 real, two complex nroots = 1 if (abs(det).lt.tiny(det)) nroots = 2 term = -0.5*q + sqrt(det) termA = (abs(term))**(1.d0/3.d0)*SIGN(1.0d0,term) x(1) = real(termA - p/(3.*termA)) termr = real(-0.5*termA + p/(6.*termA)) ! convert from double prec. termi = real(0.5*sqrt(3.d0)*(termA + p/(3.d0*termA))) x(2) = cmplx(termr,termi) x(3) = cmplx(termr,-termi) endif !--return solutions to original cubic, not reduced cubic x(:) = x(:) - b/3. !--if determinant is small, take a couple of Newton-Raphson iterations ! to beat down the error if (abs(det).lt.eps) then do i=1,nroots xi = dble(x(i)) do j=1,3 fx = xi*(xi*(xi + b) + c) + d dfx = xi*(3.d0*xi + 2.d0*b) + c if (abs(dfx).gt.0.) xi = xi - fx/dfx enddo x(i) = real(xi) enddo endif if (present(nreal)) nreal = nroots !--the following lines can be used for debugging if (present(check)) then if (check) then !--verify the cubic solution print*,'verifying: x^3 + ',b,'x^2 + ',c,'x + ',d do i=1,3 term = real(x(i)**3 + b*x(i)**2 + c*x(i) + d) if (abs(term).lt.eps) then print*,'root ',i,':',x(i),'f=',term,': OK' else print*,'root ',i,':',x(i),'f=',term,': FAILED',eps endif enddo endif endif return end subroutine cubicsolve_complex end module cubic splash/src/read_data_pbob.f90000644 000766 000000 00000026410 13261626263 017004 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2015 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR .PBOB FILES BY DAVID BROWN ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxpart,maxplot,maxstep) : main data array ! ! npartoftype(maxstep): number of particles of each type in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! (used in calc_quantities for calculating the pressure) ! ! most of these values are stored in global arrays ! in the module 'particle_data' ! !------------------------------------------------------------------------- ! ! The module below contains interface routines to c functions ! that perform the actual read of the .pbob file information ! !------------------------------------------------------------------------- module pbobread use params, only:maxplot,doub_prec use labels, only:lenlabel use, intrinsic :: iso_c_binding, only:c_int,c_double,c_char implicit none character(len=lenlabel), dimension(maxplot) :: blocklabel integer, parameter :: maxtypes = 6 interface subroutine read_pbob_header(filename,npart,ncol,nsteps,ndim,ndimV,time,ierr) bind(c) import character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(out) :: npart,ncol,nsteps,ndim,ndimV,ierr real(kind=c_double), intent(out) :: time end subroutine read_pbob_header subroutine read_pbob_data(filename,np,time_slice,time_val,ierr) bind(c) import implicit none character(kind=c_char), dimension(*), intent(in) :: filename integer(kind=c_int), intent(in), value :: np integer(kind=c_int), intent(in), value :: time_slice real(kind=c_double), intent(out) :: time_val integer(kind=c_int), intent(out) :: ierr end subroutine read_pbob_data end interface end module pbobread !------------------------------------------------------------------------- ! ! The routine that reads the data into splash's internal arrays ! !------------------------------------------------------------------------- subroutine read_data(rootname,istepstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol,maxstep,iamtype use params, only:doub_prec use settings_data, only:ndim,ndimV,ncolumns,ncalc,ipartialread, & ntypes,debugmode !,iverbose use mem_allocation, only:alloc use labels, only:labeltype,print_types use asciiutils, only:cstring use pbobread, only:read_pbob_header,read_pbob_data implicit none integer, intent(in) :: istepstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: rootname character(len=len(rootname)+10) :: datfile integer :: i,j,itype,ierr integer :: ncolstep,npart_max,nstep_max,ntoti logical :: iexist,reallocate,goterrors real(doub_prec) :: timetemp nstepsread = 0 goterrors = .false. if (len_trim(rootname).gt.0) then datfile = trim(rootname) else print*,' **** no data read **** ' return endif ! !--check if first data file exists ! print "(1x,a)",'reading PBOB format' inquire(file=datfile,exist=iexist) if (.not.iexist) then ! !--append .silo on the end if not already present ! datfile=trim(rootname)//'.pbob' inquire(file=datfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(rootname)//': file not found ***' return endif endif ! !--read data from snapshots ! i = istepstart write(*,"(23('-'),1x,a,1x,23('-'))") trim(datfile) ! !--open file and read header information ! if (debugmode) print*,'DEBUG: reading header...' call read_pbob_header(cstring(datfile),ntoti,ncolstep,nstep_max,ndim,ndimV,timetemp,ierr) if (ierr /= 0) then print "(a)", '*** ERROR READING HEADER ***' return endif ncolumns = ncolstep !if (iverbose >= 1) print "(a,1x,i10,a,es10.3)",' ndim: ',ndim,' time: ',timetemp !if (iverbose >= 1) print "(2(a,1x,i10))",' npart: ',ntoti,' ncolumns: ',ncolstep ! !--now read data ! reallocate = .false. npart_max = maxpart nstep_max = max(maxstep,nstep_max) if (ntoti.gt.maxpart) then reallocate = .true. if (maxpart.gt.0) then ! if we are reallocating, try not to do it again npart_max = int(1.1*ntoti) else ! if first time, save on memory npart_max = int(ntoti) endif endif if (i.ge.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate .or. .not.(allocated(dat))) then call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol),mixedtypes=.true.) reallocate = .false. endif ! !--copy header data into allocated arrays ! npartoftype(1,i) = ntoti time(i) = real(timetemp) !print*,' time = ',timetemp masstype(:,i) = 0. ! all masses read from file ! !--read particle data ! got_particles: if (ntoti > 0) then do while(ierr == 0 .and. i <= nstep_max) if (i.gt.maxstep .and. i.ne.1) then nstep_max = i + max(10,INT(0.1*nstep_max)) reallocate = .true. endif ! !--reallocate memory for main data array ! if (reallocate) call alloc(npart_max,nstep_max,max(ncolumns+ncalc,maxcol),mixedtypes=.true.) call read_pbob_data(cstring(datfile),ntoti,i,timetemp,ierr) if (ierr == 0) then print "(a,i3,a,es10.3)",' time slice #',i,' time = ',timetemp time(i) = real(timetemp) call set_labels ! sets ntypes and labeltype if (size(iamtype(:,i)).gt.1) then npartoftype(:,i) = 0 do j=1,ntoti itype = iamtype(j,i) if (itype > 0 .and. itype <= ntypes) then npartoftype(itype,i) = npartoftype(itype,i) + 1 else ! catch-all "unknown" type npartoftype(ntypes+1,i) = npartoftype(ntypes+1,i) + 1 endif enddo endif i = i + 1 nstepsread = nstepsread + 1 call print_types(npartoftype(:,i),labeltype) endif enddo endif got_particles ! !--now memory has been allocated, set arrays which are constant for all time ! gamma = 5./3. ipartialread = .false. ! !--cover the special case where no particles have been read ! if (ntoti.le.0) then npartoftype(1,i) = 1 dat(:,:,i) = 0. endif return end subroutine read_data subroutine read_pbob_data_fromc(icol,istep,np,temparr,itype,tag) bind(c) use, intrinsic :: iso_c_binding, only:c_int,c_double,c_char use particle_data, only:dat,iamtype use settings_data, only:debugmode use labels, only:label use asciiutils, only:fstring use pbobread, only:blocklabel integer(kind=c_int), intent(in),value :: icol,istep,np integer(kind=c_int), intent(in) :: itype(np) real(kind=c_double), intent(in) :: temparr(np) character(kind=c_char), intent(in) :: tag(256) integer(kind=c_int) :: i,icolput icolput = icol if (debugmode) print "(a,i2,a,i2,a)",'DEBUG: reading column ',icol,' -> '//trim(label(icolput)) ! check column is within array limits if (icolput.gt.size(dat(1,:,1)) .or. icolput.eq.0) then print "(a,i2,a)",' ERROR: column = ',icolput,' out of range in read_pbob_data_fromc' return endif if (istep > size(dat(1,1,:)) .or. istep <= 0) then print "(a,i2,a)",' ERROR: step = ',istep,' out of range in read_pbob_data_fromc' return endif blocklabel(icol) = trim(fstring(tag)) ! ensure no array overflows nmax = min(np,size(dat(:,1,1))) ! copy data into main splash array dat(1:nmax,icolput,istep) = real(temparr(1:nmax)) ! set particle type if (size(iamtype(:,1)).gt.1) then do i=1,nmax iamtype(i,istep) = int(itype(i),kind=kind(iamtype)) enddo endif return end subroutine read_pbob_data_fromc !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels, only:label,iamvec,labelvec,labeltype,ix,ivx,ipmass, & ih,irho,ipr,iutherm,iax!,iBfirst,idivB use params use settings_data, only:ndim,ndimV,ntypes,UseTypeInRenderings use geometry, only:labelcoord use system_utils, only:envlist,ienvironment use pbobread, only:blocklabel use asciiutils, only:lcase implicit none integer :: i,icol if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif ix = 0 iutherm = 0 do i=1,ndim ix(i) = i enddo do icol=1,size(blocklabel) select case(trim(lcase(blocklabel(icol)))) case('x') ix(1) = icol case('vx') ivx = icol case('ax') iax = icol case('h') ih = icol case('p') ipr = icol case('mass','m') ipmass = icol case('density','rho') irho = icol end select label(icol) = trim(blocklabel(icol)) enddo !! set labels of the quantities read in !if (ix(1).gt.0) label(ix(1:ndim)) = labelcoord(1:ndim,1) ! set labels for vector quantities if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = trim(labelvec(ivx))//'_'//labelcoord(i,1) enddo endif if (iax.gt.0) then iamvec(iax:iax+ndimV-1) = iax labelvec(iax:iax+ndimV-1) = 'a' do i=1,ndimV label(iax+i-1) = trim(labelvec(iax))//'_'//labelcoord(i,1) enddo endif ! set labels for each particle type ntypes = 3 labeltype(1) = 'interior' labeltype(2) = 'ghost' labeltype(3) = 'boundary' UseTypeInRenderings(:) = .false. UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_flash_hdf5.f90000644 000766 000000 00000021465 13261626263 020072 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR FLASH HDF5 TRACER PARTICLES ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- module flash_hdf5read use, intrinsic :: iso_c_binding, only:c_int,c_float,c_char !interface to the c versions interface subroutine read_flash_hdf5_header(filename,time,npart,ncol,ierr) bind(c) import character(kind=c_char,len=1), intent(in) :: filename real(kind=c_float), intent(out) :: time integer(kind=c_int), intent(out) :: npart,ncol,ierr end subroutine read_flash_hdf5_header subroutine read_flash_hdf5_data(filename,npart,ncol,isrequired,ierr) bind(c) import character(kind=c_char,len=1), intent(in) :: filename integer(kind=c_int), intent(in) :: npart,ncol integer(kind=c_int), dimension(ncol), intent(in) :: isrequired integer(kind=c_int), intent(out) :: ierr end subroutine read_flash_hdf5_data end interface contains ! ! function which maps from the order in which columns ! are read from the HDF5 file to the order in which they ! are stored in SPLASH. Differs because there are a couple ! of useless arrays that we do not read/store (ie. first column ! is on/off tag, 5th column is particle ID which we use to order ! the particles) ! integer function icolshuffle(icol) implicit none integer, intent(in) :: icol select case(icol) case(1) icolshuffle = 4 case(2,3,4) icolshuffle = icol - 1 case(5) icolshuffle = 0 case default icolshuffle = icol end select end function icolshuffle end module flash_hdf5read subroutine read_data(dumpfile,indexstart,ipos,nstepsread) use particle_data, only:dat,npartoftype,masstype,time,gamma,maxpart,maxcol use params use settings_data, only:ndim,ndimV,ncolumns,ncalc,required,ipartialread,lowmemorymode use mem_allocation, only:alloc use flash_hdf5read use asciiutils, only:cstring use labels, only:ih,irho use system_utils, only:renvironment implicit none integer, intent(in) :: indexstart,ipos integer, intent(out) :: nstepsread character(len=*), intent(in) :: dumpfile integer :: i,j,ncolstep,ilastrequired integer :: nprint,npart_max,nstep_max,ierr integer, dimension(0:maxplot) :: isrequired logical :: iexist real :: tread,hfact,totmass nstepsread = 0 nstep_max = 0 npart_max = maxpart ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: '//trim(dumpfile)//': file not found ***' return endif ! !--fix number of spatial dimensions (0 means no particle coords) ! ndim = 3 ndimV = 3 j = indexstart nstepsread = 0 print "(a)",' reading FLASH tracer particles (HDF5) data format ' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) call read_flash_hdf5_header(cstring(dumpfile),tread,nprint,ncolstep,ierr) ncolstep = ncolstep - 1 ! subtract particle ID column print "(a,i10,a,es10.3,a,i2)",' npart = ',nprint,' time = ',tread call set_labels if (ih.gt.0 .and. required(ih)) required(irho) = .true. ! !--(re)allocate memory ! nstep_max = max(nstep_max,indexstart,1) if (.not.allocated(dat) .or. (nprint.gt.maxpart) .or. (ncolstep+ncalc).gt.maxcol) then npart_max = max(npart_max,nprint,maxpart) if (lowmemorymode) then ilastrequired = 0 do i=1,ncolstep+ncalc if (required(i)) ilastrequired = i enddo call alloc(npart_max,j,ilastrequired) else call alloc(npart_max,nstep_max,max(ncolstep+ncalc,maxcol)) endif endif ! !--set the necessary parameters ! ncolumns = ncolstep nstepsread = nstepsread + 1 npartoftype(:,j) = 0 npartoftype(1,j) = nprint totmass = renvironment('FSPLASH_TOTMASS',-1.0) if (totmass.gt.0.) then print "(a,1pe10.3)",' setting total mass for all particles using FSPLASH_TOTMASS=',totmass else print "(a)",' FSPLASH_TOTMASS not set, assuming total mass of all particles is 1.0' totmass = 1.0 endif masstype(1,j) = totmass/real(nprint) time(j) = tread gamma(j) = 5./3. ! !--map "required" array to integers ! also remap to the order read from the c data read ! isrequired(:) = 0 do i=1,ncolstep if (icolshuffle(i).ne.0 .and. required(icolshuffle(i))) then !print*,'required '//trim(label(icolshuffle(i)))//' so must read ',i isrequired(i) = 1 endif enddo if (.not.all(required(1:ncolstep))) then ipartialread = .true. else ipartialread = .false. endif ! !--now read the timestep data in the dumpfile ! (to avoid Fortran calling C with the array, we don't actually ! pass the dat array here - instead we get c to ! "call back" to fill the dat array, below) ! call read_flash_hdf5_data(cstring(dumpfile),nprint,ncolstep+1,isrequired(1:ncolstep+1),ierr) if (required(ih)) then hfact = 1.2 hfact = renvironment('FSPLASH_HFACT',1.2) print "(a,i2,a,f5.2,a)",' creating smoothing length in column ',ih,' using h =',hfact,'(m/rho)^(1/3)' dat(1:nprint,ih,j) = hfact*(masstype(1,j)/dat(1:nprint,irho,j))**(1./3.) endif return end subroutine read_data subroutine receive_data_fromc(icol,npart,temparr,id) bind(c) use, intrinsic :: iso_c_binding, only:c_int,c_double use particle_data, only:dat use flash_hdf5read, only:icolshuffle use labels, only:label implicit none integer(kind=c_int), intent(in) :: icol,npart real(kind=c_double), dimension(npart), intent(in) :: temparr integer(kind=c_int), dimension(npart), intent(in) :: id integer(kind=c_int) :: i,icolput icolput = icolshuffle(icol) if (icolput.gt.size(dat(1,:,1)) .or. icolput.eq.0) then print "(a,i2,a)",' ERROR: column = ',icolput,' out of range in receive_data_fromc' return endif print "(a,i2,a)",' reading column ',icol,' -> '//trim(label(icolput)) do i=1,npart if (id(i).lt.1 .or. id(i).gt.npart) then print*,' ERROR in particle id = ',id(i) else dat(id(i),icolput,1) = real(temparr(i)) endif enddo return end subroutine receive_data_fromc !!------------------------------------------------------------------- !! set labels for each column of data !! !! read these from a file called 'columns' in the current directory !! then take sensible guesses as to which quantities are which !! from the column labels !! !!------------------------------------------------------------------- subroutine set_labels use labels, only:label,labeltype,ix,irho,ipmass,ih,ivx,iamvec,labelvec use params use settings_data, only:ntypes,ndim,ndimV,UseTypeInRenderings,ncolumns use geometry, only:labelcoord implicit none integer :: i do i=1,ndim ix(i) = i label(i) = labelcoord(i,1) enddo ih = 5 ivx = 6 ipmass = 0 label(4) = 'density (from grid)' label(ih) = 'smoothing length' irho = 4 if (ncolumns.ge.9) then irho = 9 label(9) = 'density (on particles)' else irho = 4 endif if (ivx.gt.0) then iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo endif ! !--set labels for each particle type ! ntypes = 1 labeltype(1) = 'tracer' UseTypeInRenderings(1) = .true. !----------------------------------------------------------- return end subroutine set_labels splash/src/read_data_jjm_multiphase.f90000644 000766 000000 00000016767 13261626263 021113 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- !------------------------------------------------------------------------- ! this subroutine reads from the data file(s) ! change this to change the format of data input ! ! THIS VERSION IS FOR JOE'S 2D SPH CODE ! ! the data is stored in the global array dat ! ! >> this subroutine must return values for the following: << ! ! ncolumns : number of data columns ! ndim, ndimV : number of spatial, velocity dimensions ! nstepsread : number of steps read from this file ! ! maxplot,maxpart,maxstep : dimensions of main data array ! dat(maxplot,maxpart,maxstep) : main data array ! ! npartoftype(1:6,maxstep) : number of particles of each type in each timestep ! ntot(maxstep) : total number of particles in each timestep ! iam(maxpart,maxstep): integer identification of particle type ! ! time(maxstep) : time at each step ! gamma(maxstep) : gamma at each step ! ! most of these values are stored in global arrays ! in the module 'particle_data' !------------------------------------------------------------------------- subroutine read_data(rootname,indexstart,ipos,nstepsread) use particle_data use params use settings_data, only:ndim,ndimV,ncolumns use mem_allocation implicit none integer, intent(IN) :: indexstart,ipos integer, intent(OUT) :: nstepsread character(LEN=*), intent(IN) :: rootname integer :: i,j,ifile,ierr,npart,nweird,nbnd,nmud integer :: istep,nprint,npart_max,nstep_max,icol,ncolstep logical :: iexist character(LEN=LEN(rootname)+4) :: dumpfile real :: timei,dti,hi,pmass,totmass,rhozero nstepsread = 0 nstep_max = 0 npart_max = maxpart ifile = 1 dumpfile = trim(rootname) if (index(dumpfile,'.plt').eq.0) dumpfile = trim(rootname)//'.plt' ! !--check if first data file exists ! inquire(file=dumpfile,exist=iexist) if (.not.iexist) then print "(a)",' *** error: ',trim(dumpfile),' file not found ***' return endif ! !--fix number of spatial dimensions ! ndim = 2 ndimV = 2 ncolstep = 8 ncolumns = 8 ! number of columns in file ! !--allocate memory initially ! nstep_max = max(nstep_max,indexstart,2) j = indexstart nstepsread = 0 print "(1x,a)",'reading Joe Monaghan''s multiphase code format' write(*,"(26('>'),1x,a,1x,26('<'))") trim(dumpfile) ! !--open the file and read the number of particles ! open(unit=15,iostat=ierr,file=dumpfile,status='old',form='formatted') if (ierr /= 0) then print*,'*** ERROR OPENING ',trim(dumpfile),' ***' else ! !--read the number of particles in the first step, ! allocate memory and rewind ! read(15,*,end=55,iostat=ierr) istep,nprint,hi print*,'first time = ',hi,' npart = ',nprint if (.not.allocated(dat) .or. (nprint.gt.npart_max)) then npart_max = max(npart_max,INT(1.1*(nprint))) call alloc(npart_max,nstep_max,ncolumns,mixedtypes=.true.) endif rewind(15) endif if (ierr /= 0) then print*,'*** ERROR READING TIMESTEP HEADER ***' else oversteps: do ! !--loop over the timesteps in this file ! npart_max = max(npart_max,nprint) ! !--allocate/reallocate memory if j > maxstep ! if (j.gt.maxstep) then call alloc(maxpart,2*j,maxcol,mixedtypes=.true.) endif ! !--now read the timestep data in the dumpfile ! read(15,*,end=55,iostat=ierr) istep,nprint,hi,timei,dti ! read(15,*) nbnd = 0 npart = 0 nweird = 0 nmud = 0 do i=1,nprint read(15,*,end=55,iostat=ierr) (dat(i,icol,j),icol = 1,ncolstep),iamtype(i,j) select case(iamtype(i,j)) case(0) nbnd = nbnd + 1 iamtype(i,j) = 3 case(1) npart = npart + 1 iamtype(i,j) = 1 case(2) nmud = nmud + 1 iamtype(i,j) = 2 case default nweird = nweird + 1 iamtype(i,j) = 4 end select !print*,i,(dat(i,icol,j),icol = 1,ncolstep),iamtype(i,j) !--make a fake column for mass enddo 600 format(2x,7(e12.5),1(i5)) time(j) = timei if (ierr /= 0) then print*,'got to ',i,' step ',j print "(a)",'|*** ERROR READING TIMESTEP ***' return else nstepsread = nstepsread + 1 endif npartoftype(:,j) = 0 npartoftype(1,j) = npart npartoftype(2,j) = nmud npartoftype(3,j) = nbnd npartoftype(4,j) = nweird print*,'nwater=',npart,' nmud=',nmud,' nbnd=',nbnd if (nweird.gt.0) print*,' WARNING: ',nweird,' particles of unknown type' print*,j,' time = ',time(j) gamma(j) = 1.666666666667 j = j + 1 enddo oversteps endif 55 continue ! !--reached end of file ! close(15) print*,'nstepsread = ',nstepsread print*,'>> end of dump file: nsteps =',j-1,'nfluid = ',npartoftype(1,j-1),'nbound=',npartoftype(2,j-1) return end subroutine read_data !!------------------------------------------------------------ !! set labels for each column of data !!------------------------------------------------------------ subroutine set_labels use labels use params use settings_data use geometry, only:labelcoord implicit none integer :: i if (ndim.le.0 .or. ndim.gt.3) then print*,'*** ERROR: ndim = ',ndim,' in set_labels ***' return endif if (ndimV.le.0 .or. ndimV.gt.3) then print*,'*** ERROR: ndimV = ',ndimV,' in set_labels ***' return endif do i=1,ndim ix(i) = i enddo label(ix(1:ndim)) = labelcoord(1:ndim,1) ivx = ndim+1 ipr = ndim+ndimV+2 label(ipr) = 'pressure' ih = ndim+ndimV+3 ! smoothing length label(ih) = 'h' irho = ndim+ndimV+1 ! location of rho in data array label(irho) = 'density' iutherm = 0 ! thermal energy ! label(iutherm) = 'u' ipmass = 8 ! particle mass label(ipmass) = 'particle mass' iamvec(ivx:ivx+ndimV-1) = ivx labelvec(ivx:ivx+ndimV-1) = 'v' do i=1,ndimV label(ivx+i-1) = 'v\d'//labelcoord(i,1) enddo ! !--set labels for each particle type ! ntypes = 4 !!maxparttypes labeltype(1) = 'fluid' labeltype(2) = 'mud' labeltype(3) = 'boundary' labeltype(4) = 'unknown' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. UseTypeInRenderings(4) = .false. !----------------------------------------------------------- return end subroutine set_labels splash/src/exact_wave.f90000644 000766 000000 00000004225 13261626263 016224 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2009 Daniel Price. All rights reserved. ! Contact: daniel.price@sci.monash.edu.au ! !----------------------------------------------------------------- ! ---------------------------------------------------------------------- ! compute exact solution for a linear wave ! plots a sine function with a given amplitude, period and wavelength ! ---------------------------------------------------------------------- module wave implicit none contains subroutine exact_wave(time,ampl,period,lambda,x0,ymean,xplot,yplot,ierr) implicit none integer :: i real, parameter :: pi = 3.1415926536 real, intent(in) :: time, ampl, period, lambda, x0, ymean real, intent(in), dimension(:) :: xplot real, intent(out), dimension(size(xplot)) :: yplot integer, intent(out) :: ierr real :: omega print*,'plotting sine wave... mean = ',ymean print*,' lambda = ',lambda,' ampl = ',ampl,' period = ',period ! ! check for errors ! ierr = 0 if (lambda.le.0.) then print*,'error: lambda <= 0' ierr = 1 return endif if (abs(period).gt.tiny(period)) then omega = 2.*pi/period else print*,'warning: period <= 0' omega = 0. endif do i=1,size(xplot) if (abs(ymean).le.0.) then yplot(i) = ymean + ampl*sin(2.*pi/lambda*(xplot(i)-x0) - omega*time) else yplot(i) = ymean*(1. + ampl*sin(2.*pi/lambda*(xplot(i)-x0) - omega*time)) endif enddo return end subroutine exact_wave end module wave splash/src/partutils.f90000644 000766 000000 00000015131 13261626263 016123 0ustar00dpricewheel000000 000000 !----------------------------------------------------------------- ! ! This file is (or was) part of SPLASH, a visualisation tool ! for Smoothed Particle Hydrodynamics written by Daniel Price: ! ! http://users.monash.edu.au/~dprice/splash ! ! SPLASH comes with ABSOLUTELY NO WARRANTY. ! This is free software; and you are welcome to redistribute ! it under the terms of the GNU General Public License ! (see LICENSE file for details) and the provision that ! this notice remains intact. If you modify this file, please ! note section 2a) of the GPLv2 states that: ! ! a) You must cause the modified files to carry prominent notices ! stating that you changed the files and the date of any change. ! ! Copyright (C) 2005-2014 Daniel Price. All rights reserved. ! Contact: daniel.price@monash.edu ! !----------------------------------------------------------------- !----------------------------------------------------------- ! ! utility routines to do with particle identification ! !----------------------------------------------------------- module part_utils use params, only:int1 implicit none public :: igettype,get_tracked_particle public :: locate_nth_particle_of_type public :: locate_first_two_of_type public :: get_binary private contains !--------------------------------------------- ! utility returning the type of particle i ! when particles are ordered by type !--------------------------------------------- pure integer function igettype(i,noftype) use params, only:maxparttypes integer, intent(in) :: i integer, dimension(maxparttypes), intent(in) :: noftype integer :: ntot,ntot1,jtype ntot = 0 igettype = 1 ! so even if in error, will not lead to seg fault over_types: do jtype=1,maxparttypes ntot1 = ntot + noftype(jtype) if (i.gt.ntot .and. i.le.ntot1) then igettype = jtype exit over_types endif ntot = ntot1 enddo over_types end function igettype !------------------------------------------------------------------- ! routine to find which particle is being tracked, when it is ! given in the form of type:offset !------------------------------------------------------------------- integer function get_tracked_particle(itype,ioffset,noftype,iamtype) integer, intent(in) :: itype,ioffset integer, dimension(:), intent(in) :: noftype integer(kind=int1), dimension(:), intent(in) :: iamtype integer :: ntot if (itype.le.0 .or. itype.gt.size(noftype)) then !--type not set, itrackpart = itrackoffset get_tracked_particle = ioffset else !--want to select nth particle of a particular type call locate_nth_particle_of_type(ioffset,get_tracked_particle, & itype,iamtype,noftype,ntot) endif end function get_tracked_particle !------------------------------------------------------------------- ! routine to locate first two particles of a given type in the data !------------------------------------------------------------------- subroutine locate_first_two_of_type(i1,i2,itype,iamtype,noftype,ntot) integer, intent(out) :: i1,i2,ntot integer, intent(in) :: itype integer(kind=int1), dimension(:), intent(in) :: iamtype integer, dimension(:), intent(in) :: noftype integer :: i,nfound !--locate first two sink particles in the data ntot = sum(noftype) if (size(iamtype(:)).eq.1) then i1 = sum(noftype(1:itype-1)) + 1 i2 = i1 + 1 else i1 = 0 i2 = 0 i = 0 nfound = 0 do while ((i1.eq.0 .or. i2.eq.0) .and. i.le.ntot) i = i + 1 if (iamtype(i).eq.itype) nfound = nfound + 1 if (nfound.eq.1) i1 = i if (nfound.eq.2) i2 = i enddo endif end subroutine locate_first_two_of_type !------------------------------------------------------------- ! routine to locate nth particle of a given type in the data !------------------------------------------------------------- pure subroutine locate_nth_particle_of_type(n,ipos,itype,iamtype,noftype,ntot) integer, intent(out) :: ipos,ntot integer, intent(in) :: n,itype integer(kind=int1), dimension(:), intent(in) :: iamtype integer, dimension(:), intent(in) :: noftype integer :: i,nfound ntot = sum(noftype) if (size(iamtype(:)).eq.1) then ipos = sum(noftype(1:itype-1)) + n else ipos = 0 i = 0 nfound = 0 do while (ipos.eq.0 .and. i.le.ntot) i = i + 1 if (iamtype(i).eq.itype) nfound = nfound + 1 if (nfound.eq.n) ipos = i enddo endif end subroutine locate_nth_particle_of_type !---------------------------------------------------------- ! routine to get properties of particle binary system ! INPUT: ! i1, i2 : indexes of two particles to use ! dat(npart,ncolumns) : particle data ! ix(ndim) : columns containing positions ! ivx : column of first velocity component ! ipmass : column containing mass ! OUTPUT: ! x0 : centre of mass position ! v0 : velocity of centre of mass ! angle : angle of binary about centre of mass (radians) !---------------------------------------------------------- subroutine get_binary(i1,i2,dat,x0,v0,angle,ndim,ndimV,ncolumns,ix,ivx,ipmass,iverbose,ierr) integer, intent(in) :: i1,i2,ndim,ndimV,ncolumns,ivx,ipmass,iverbose integer, dimension(ndim), intent(in) :: ix real, dimension(:,:), intent(in) :: dat real, dimension(ndim), intent(out) :: x0,v0 real, intent(out) :: angle integer, intent(out) :: ierr integer :: max real, dimension(ndim) :: x1,x2,v1,v2,dx real :: m1,m2,dmtot ierr = 0 max = size(dat(:,1)) if (i1 <= 0 .or. i2 <= 0 .or. i1 > max .or. i2 > max) then if (iverbose >= 2) print*,' star 1 = ',i1,' star 2 = ',i2 print "(a)",' ERROR locating sink particles in the data' ierr = 1 return endif x1 = 0. x2 = 0. x1(1:ndim) = dat(i1,ix(1:ndim)) x2(1:ndim) = dat(i2,ix(1:ndim)) !--get centre of mass if (ipmass > 0 .and. ipmass <= ncolumns) then m1 = dat(i1,ipmass) m2 = dat(i2,ipmass) else m1 = 1. m2 = 1. endif dmtot = 1./(m1 + m2) x0 = (m1*x1 + m2*x2)*dmtot if (iverbose >= 1) then print "(a,3(1x,es10.3),a,es10.3)",' :: star 1 pos =',x1(1:ndim),' m = ',m1 print "(a,3(1x,es10.3),a,es10.3)",' :: star 2 pos =',x2(1:ndim),' m = ',m2 print "(a,3(1x,es10.3))",' :: c. of mass =',x0(1:ndim) endif !--work out angle needed to rotate into corotating frame dx = x0 - x1 angle = -atan2(dx(2),dx(1)) !--get velocities if (ivx > 0 .and. ivx + ndimV <= ncolumns) then v1 = dat(i1,ivx:ivx+ndimV-1) v2 = dat(i2,ivx:ivx+ndimV-1) v0 = (m1*v1 + m2*v2)*dmtot if (iverbose >= 1) print "(a,3(1x,es10.3))",' :: vel c of m =',v0(1:ndimV) else v0 = 0. endif end subroutine get_binary end module part_utils splash/src/H5Part/H5PartF.c000644 000766 000000 00000023410 13261626263 016233 0ustar00dpricewheel000000 000000 /* * This is my modified H5PartF.c for use in SPLASH * Main changes are that the strings are now passed * directly from Fortran, already with the null character appended * * Modified from the original H5Part Fortran interface * by Daniel Price 08/04/10 */ #include "H5Part.h" #include char _H5Part_flagsfor2c ( char * flags ) { char fbits = 0x00; flags = strtok ( flags, "," ); while ( flags != NULL ) { if ( strcmp ( flags, "vfd_mpiposix" ) == 0 ) fbits |= H5PART_VFD_MPIPOSIX; else if ( strcmp ( flags, "fs_lustre" ) == 0 ) fbits |= H5PART_FS_LUSTRE; flags = strtok ( NULL, "," ); } return fbits; } /* open/close interface */ h5part_int64_t h5ptc_openr ( const char *file_name ) { H5PartFile* f = H5PartOpenFile ( file_name, H5PART_READ ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_openw ( const char *file_name ) { H5PartFile* f = H5PartOpenFile ( file_name, H5PART_WRITE ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_opena ( const char *file_name ) { H5PartFile* f = H5PartOpenFile ( file_name, H5PART_APPEND ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_openr_align ( const char *file_name, const h5part_int64_t *align ) { H5PartFile* f = H5PartOpenFileAlign ( file_name, H5PART_READ, *align ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_openw_align ( const char *file_name, const h5part_int64_t *align ) { H5PartFile* f = H5PartOpenFileAlign ( file_name, H5PART_WRITE, *align ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_opena_align ( const char *file_name, const h5part_int64_t *align ) { H5PartFile* f = H5PartOpenFileAlign ( file_name, H5PART_APPEND, *align ); return (h5part_int64_t)(size_t)f; } #ifdef PARALLEL_IO h5part_int64_t h5ptc_openr_par ( const char *file_name, MPI_Fint *fcomm ) { MPI_Comm ccomm = MPI_Comm_f2c (*fcomm); H5PartFile* f = H5PartOpenFileParallel ( file_name, H5PART_READ, ccomm ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_openw_par ( const char *file_name, MPI_Fint *fcomm ) { MPI_Comm ccomm = MPI_Comm_f2c (*fcomm); H5PartFile* f = H5PartOpenFileParallel ( file_name, H5PART_WRITE, ccomm ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_opena_par ( const char *file_name, MPI_Fint *fcomm ) { MPI_Comm ccomm = MPI_Comm_f2c (*fcomm); H5PartFile* f = H5PartOpenFileParallel ( file_name, H5PART_APPEND, ccomm ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_openr_par_align ( const char *file_name, MPI_Fint *fcomm, const h5part_int64_t *align ) { MPI_Comm ccomm = MPI_Comm_f2c (*fcomm); H5PartFile* f = H5PartOpenFileParallelAlign ( file_name, H5PART_READ, ccomm, *align ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_openw_par_align ( const char *file_name, MPI_Fint *fcomm, const h5part_int64_t *align, const char *flags ) { MPI_Comm ccomm = MPI_Comm_f2c (*fcomm); char fbits = H5PART_WRITE | _H5Part_flagsfor2c ( flags ); H5PartFile* f = H5PartOpenFileParallelAlign ( file_name, fbits, ccomm, *align ); return (h5part_int64_t)(size_t)f; } h5part_int64_t h5ptc_opena_par_align ( const char *file_name, MPI_Fint *fcomm, const h5part_int64_t *align, const char *flags ) { MPI_Comm ccomm = MPI_Comm_f2c (*fcomm); char fbits = H5PART_APPEND | _H5Part_flagsfor2c ( flags ); H5PartFile* f = H5PartOpenFileParallelAlign ( file_name, fbits, ccomm, *align ); return (h5part_int64_t)(size_t)f; } #endif h5part_int64_t h5ptc_close ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartCloseFile ( filehandle ); } /*==============Writing and Setting Dataset info========*/ h5part_int64_t h5ptc_readstep ( const h5part_int64_t *f, const h5part_int64_t *step, h5part_float64_t *x, h5part_float64_t *y, h5part_float64_t *z, h5part_float64_t *px, h5part_float64_t *py, h5part_float64_t *pz, h5part_int64_t *id ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartReadParticleStep ( filehandle,(*step)-1,x,y,z,px,py,pz,id); } h5part_int64_t h5ptc_setnpoints ( const h5part_int64_t *f, h5part_int64_t *np ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartSetNumParticles ( filehandle, *np ); } h5part_int64_t h5ptc_setnpoints_strided ( const h5part_int64_t *f, h5part_int64_t *np, h5part_int64_t *stride ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartSetNumParticlesStrided ( filehandle, *np, *stride ); } h5part_int64_t h5ptc_setstep ( const h5part_int64_t *f, h5part_int64_t *step ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartSetStep ( filehandle, (*step)-1 ); } h5part_int64_t h5ptc_writedata_r8 ( const h5part_int64_t *f, const char *name, const h5part_float64_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteDataFloat64 ( filehandle, name, data ); return herr; } h5part_int64_t h5ptc_writedata_r4 ( const h5part_int64_t *f, const char *name, const h5part_float32_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteDataFloat32 ( filehandle, name, data ); return herr; } h5part_int64_t h5ptc_writedata_i8 ( const h5part_int64_t *f, const char *name, const h5part_int64_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteDataInt64 ( filehandle, name, data ); return herr; } h5part_int64_t h5ptc_writedata_i4 ( const h5part_int64_t *f, const char *name, const h5part_int32_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteDataInt32 ( filehandle, name, data ); return herr; } /*==============Reading Data Characteristics============*/ h5part_int64_t h5ptc_getnsteps ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartGetNumSteps ( filehandle ); } h5part_int64_t h5ptc_getndatasets ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartGetNumDatasets ( filehandle ); } h5part_int64_t h5ptc_getnpoints ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartGetNumParticles ( filehandle ); } h5part_int64_t h5ptc_getdatasetname ( const h5part_int64_t *f, const h5part_int64_t *index, char *name, const int l_name ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartGetDatasetName ( filehandle, *index, name, l_name ); return herr; } h5part_int64_t h5ptc_getdatasetinfo ( const h5part_int64_t *f, const h5part_int64_t *index, char *name, h5part_int64_t *type, h5part_int64_t *nelem, const h5part_int64_t *l_name ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t type_tmp; h5part_int64_t herr = H5PartGetDatasetInfo ( filehandle, *index, name, *l_name, &type_tmp, nelem ); if (type_tmp == H5PART_INT64) { *type = 1; } else if (type_tmp == H5PART_INT32) { *type = 2; } else if (type_tmp == H5PART_FLOAT64) { *type = 3; } else if (type_tmp == H5PART_FLOAT32) { *type = 4; } else if (type_tmp == H5PART_CHAR) { *type = 5; } else if (type_tmp == H5PART_STRING) { *type = 6; } else { *type = 0; } return herr; } /*=============Setting and getting views================*/ h5part_int64_t h5ptc_setview ( const h5part_int64_t *f, const h5part_int64_t *start, const h5part_int64_t *end ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartSetView ( filehandle, *start, *end ); } h5part_int64_t h5ptc_setview_indices ( const h5part_int64_t *f, const h5part_int64_t *indices, const h5part_int64_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartSetViewIndices ( filehandle, indices, *nelem ); } h5part_int64_t h5ptc_resetview ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartResetView ( filehandle ); } h5part_int64_t h5ptc_hasview ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartHasView ( filehandle ); } h5part_int64_t h5ptc_getview ( const h5part_int64_t *f, h5part_int64_t *start, h5part_int64_t *end ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartGetView ( filehandle, start, end); } /*==================Reading data ============*/ h5part_int64_t h5ptc_readdata_r8 ( const h5part_int64_t *f, const char *name, h5part_float64_t *array ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadDataFloat64 ( filehandle, name, array ); return herr; } h5part_int64_t h5ptc_readdata_r4 ( const h5part_int64_t *f, const char *name, h5part_float32_t *array ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadDataFloat32 ( filehandle, name, array ); return herr; } h5part_int64_t h5ptc_readdata_i8 ( const h5part_int64_t *f, const char *name, h5part_int64_t *array ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadDataInt64 ( filehandle, name, array ); return herr; } h5part_int64_t h5ptc_readdata_i4 ( const h5part_int64_t *f, const char *name, h5part_int32_t *array ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadDataInt32 ( filehandle, name, array ); return herr; } /* h5part_int64_t h5ptc_set_verbosity_level ( const h5part_int64_t *level ) { return H5PartSetVerbosityLevel ( *level ); } */ splash/src/H5Part/H5PartAttrib.f90000644 000766 000000 00000064114 13261626263 017455 0ustar00dpricewheel000000 000000 !------------------------------------------------------------- ! ! Fortran 90 interface to H5Part, using ! the Fortran 2003 C interoperability module (iso_c_binding) ! ! Written by Daniel Price 08/04/10 ! daniel.price@sci.monash.edu.au ! ! We first specify the interfaces to the C interface routines ! used to handle the H5partfile container object. However, all ! string conversion is done in the Fortran, not in the C. ! ! This module contains the attributes interface ! !------------------------------------------------------------- module h5partattrib use, intrinsic :: iso_c_binding, only:c_char,c_int,c_int64_t,c_double,c_float implicit none ! ! interfaces provided by this module ! public :: h5pt_getnfileattribs,h5pt_getfileattribinfo public :: h5pt_getnstepattribs,h5pt_getstepattribinfo public :: h5pt_writefileattrib,h5pt_readfileattrib public :: h5pt_writestepattrib,h5pt_readstepattrib ! ! the type-specific routines are also public ! (could make these private to allow only ! the generic interface to be used) ! public :: h5pt_writefileattrib_i4,h5pt_writefileattrib_i8, & h5pt_writefileattrib_r4,h5pt_writefileattrib_r8, & h5pt_writefileattrib_string public :: h5pt_readfileattrib_i4,h5pt_readfileattrib_i8, & h5pt_readfileattrib_r4,h5pt_readfileattrib_r8, & h5pt_readfileattrib_string public :: h5pt_writestepattrib_i4,h5pt_writestepattrib_i8, & h5pt_writestepattrib_r4,h5pt_writestepattrib_r8, & h5pt_writestepattrib_string public :: h5pt_readstepattrib_i4,h5pt_readstepattrib_i8, & h5pt_readstepattrib_r4,h5pt_readstepattrib_r8, & h5pt_readstepattrib_string private ! ! generic interface for writing file attributes of any type ! interface h5pt_writefileattrib module procedure h5pt_writefileattrib_i4,h5pt_writefileattrib_i8, & h5pt_writefileattrib_r4,h5pt_writefileattrib_r8, & h5pt_writefileattrib_string end interface h5pt_writefileattrib ! ! generic interface for reading file attributes of any type ! interface h5pt_readfileattrib module procedure h5pt_readfileattrib_i4,h5pt_readfileattrib_i8, & h5pt_readfileattrib_r4,h5pt_readfileattrib_r8, & h5pt_readfileattrib_string end interface h5pt_readfileattrib ! ! generic interface for writing step attributes of any type ! interface h5pt_writestepattrib module procedure h5pt_writestepattrib_i4,h5pt_writestepattrib_i8, & h5pt_writestepattrib_r4,h5pt_writestepattrib_r8, & h5pt_writestepattrib_string end interface h5pt_writestepattrib ! ! generic interface for reading step attributes of any type ! interface h5pt_readstepattrib module procedure h5pt_readstepattrib_i4,h5pt_readstepattrib_i8, & h5pt_readstepattrib_r4,h5pt_readstepattrib_r8, & h5pt_readstepattrib_string end interface h5pt_readstepattrib !--------------------------- ! ! interfaces to c routines ! !--------------------------- interface ! ! file attributes: interfaces to c routines ! integer(kind=c_int64_t) function h5pt_getnfileattribs (filehandle) bind(C,name="h5ptc_getnfileattribs") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function integer(kind=c_int64_t) function h5ptc_getfileattribinfo (filehandle,idx,name,nelem,l_name) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: idx !< index of the attribute to query (starting from 0) character(kind=c_char), dimension(1), intent(out) :: name !< buffer to read the attribute name into integer(kind=c_int64_t), intent(out) :: nelem !< number of elements in the attribute's array integer(kind=c_int64_t), intent(in) :: l_name !< size of name end function integer(kind=c_int64_t) function h5ptc_writefileattrib_r8 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readfileattrib_r8 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(out) :: data(*) !< buffer to read value into end function integer(kind=c_int64_t) function h5ptc_writefileattrib_r4 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readfileattrib_r4 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(out) :: data(*) !< buffer to read value into end function integer(kind=c_int64_t) function h5ptc_writefileattrib_i8 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readfileattrib_i8 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(out) :: data(*) !< buffer to read value into end function integer(kind=c_int64_t) function h5ptc_writefileattrib_i4 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readfileattrib_i4 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(out) :: data(*) !< buffer to read value into end function integer(kind=c_int64_t) function h5ptc_writefileattrib_string (filehandle,name,value) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute character(kind=c_char), dimension(1), intent(in) :: value !< the string value to store end function integer(kind=c_int64_t) function h5ptc_readfileattrib_string (filehandle,name,value) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute character(kind=c_char), dimension(1), intent(out) :: value !< buffer to read the string value into end function ! ! step attributes: interfaces to c routines ! integer(kind=c_int64_t) function h5pt_getnstepattribs (filehandle) bind(C,name="h5ptc_getnstepattribs") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function integer(kind=c_int64_t) function h5ptc_getstepattribinfo (filehandle,idx,name,nelem,l_name) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: idx !< index of the attribute to query (starting from 0) character(kind=c_char), dimension(1), intent(out) :: name !< buffer to read the attribute name into integer(kind=c_int64_t), intent(out) :: nelem !< number of elements in the attribute's array integer(kind=c_int64_t), intent(in) :: l_name !< number of elements in the attribute's array end function integer(kind=c_int64_t) function h5ptc_writestepattrib_r8 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readstepattrib_r8 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(out) :: data(*) !< buffer to read value into end function integer(kind=c_int64_t) function h5ptc_writestepattrib_r4 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readstepattrib_r4 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(out) :: data(*) !< buffer to read value into end function integer(kind=c_int64_t) function h5ptc_writestepattrib_i8 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readstepattrib_i8 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(out) :: data(*) !< buffer to read value into end function integer(kind=c_int64_t) function h5ptc_writestepattrib_i4 ( filehandle, name, data, nelem) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array end function integer(kind=c_int64_t) function h5ptc_readstepattrib_i4 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(out) :: data(*) !< buffer to read value into end function ! ! read/write string ! integer(kind=c_int64_t) function h5ptc_writestepattrib_string (filehandle,name,value) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute character(kind=c_char), dimension(1), intent(in) :: value !< the string value to store end function integer(kind=c_int64_t) function h5ptc_readstepattrib_string (filehandle,name,value) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the attribute character(kind=c_char), dimension(1), intent(out) :: value !< buffer to read the string value into end function end interface contains !--------------------------------------------------------------------------- ! ! wrappers for functions with string arguments: ! converts strings into C strings ! !--------------------------------------------------------------------------- ! ! file attributes ! integer(kind=c_int64_t) function h5pt_getfileattribinfo (filehandle,idx,name,nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: idx !< index of the attribute to query (starting from 0) character(len=*), intent(out) :: name !< buffer to read the attribute name into integer(kind=c_int64_t), intent(out) :: nelem !< number of elements in the attribute's array integer(kind=c_int64_t) :: l_name !< size of name l_name = len(name) h5pt_getfileattribinfo = h5ptc_getfileattribinfo (filehandle,idx,name,nelem,l_name) name = fstring(name) end function integer(kind=c_int64_t) function h5pt_writefileattrib_r8 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writefileattrib_r8 = h5ptc_writefileattrib_r8 ( filehandle, cstring(name), data, nelem) end function h5pt_writefileattrib_r8 integer(kind=c_int64_t) function h5pt_readfileattrib_r8 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(out) :: data(*) !< buffer to read value into h5pt_readfileattrib_r8 = h5ptc_readfileattrib_r8 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_writefileattrib_r4 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writefileattrib_r4 = h5ptc_writefileattrib_r4( filehandle, cstring(name), data, nelem) end function integer(kind=c_int64_t) function h5pt_readfileattrib_r4 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(out) :: data(*) !< buffer to read value into h5pt_readfileattrib_r4 = h5ptc_readfileattrib_r4( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_writefileattrib_i8 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writefileattrib_i8 = h5ptc_writefileattrib_i8( filehandle, cstring(name), data, nelem) end function integer(kind=c_int64_t) function h5pt_readfileattrib_i8 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(out) :: data(*) !< buffer to read value into h5pt_readfileattrib_i8 = h5ptc_readfileattrib_i8( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_writefileattrib_i4 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writefileattrib_i4 = h5ptc_writefileattrib_i4( filehandle, cstring(name), data, nelem) end function integer(kind=c_int64_t) function h5pt_readfileattrib_i4 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(out) :: data(*) !< buffer to read value into h5pt_readfileattrib_i4 = h5ptc_readfileattrib_i4 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_writefileattrib_string ( filehandle, name, value) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the attribute character(len=*), intent(in) :: value !< the string value to store h5pt_writefileattrib_string = h5ptc_writefileattrib_string ( filehandle, cstring(name), cstring(value)) end function integer(kind=c_int64_t) function h5pt_readfileattrib_string ( filehandle, name, value) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the attribute character(len=*), intent(out) :: value !< buffer to read the string value into h5pt_readfileattrib_string = h5ptc_readfileattrib_string ( filehandle, cstring(name), value) value = fstring(value) end function ! ! step attributes: ! integer(kind=c_int64_t) function h5pt_getstepattribinfo (filehandle,idx,name,nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: idx !< index of the attribute to query (starting from 0) character(len=*), intent(out) :: name !< buffer to read the attribute name into integer(kind=c_int64_t), intent(out) :: nelem !< number of elements in the attribute's array integer(kind=c_int64_t) :: l_name !< number of elements in the attribute's array l_name = len(name) h5pt_getstepattribinfo = h5ptc_getstepattribinfo (filehandle,idx,name,nelem,l_name) name = fstring(name) end function integer(kind=c_int64_t) function h5pt_writestepattrib_r8 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writestepattrib_r8 = h5ptc_writestepattrib_r8 ( filehandle, cstring(name), data, nelem) end function integer(kind=c_int64_t) function h5pt_readstepattrib_r8 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_double), intent(out) :: data(*) !< buffer to read value into h5pt_readstepattrib_r8 = h5ptc_readstepattrib_r8 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_writestepattrib_r4 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writestepattrib_r4 = h5ptc_writestepattrib_r4 ( filehandle, cstring(name), data, nelem) end function integer(kind=c_int64_t) function h5pt_readstepattrib_r4 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute real(kind=c_float), intent(out) :: data(*) !< buffer to read value into h5pt_readstepattrib_r4 = h5ptc_readstepattrib_r4 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_writestepattrib_i8 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writestepattrib_i8 = h5ptc_writestepattrib_i8 ( filehandle, cstring(name), data, nelem) end function integer(kind=c_int64_t) function h5pt_readstepattrib_i8 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int64_t), intent(out) :: data(*) !< buffer to read value into h5pt_readstepattrib_i8 = h5ptc_readstepattrib_i8 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_writestepattrib_i4 ( filehandle, name, data, nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(in) :: data(*) !< the array of data to write into the attribute integer(kind=c_int64_t), intent(in) :: nelem !< the number of elements in the array h5pt_writestepattrib_i4 = h5ptc_writestepattrib_i4 ( filehandle, cstring(name), data, nelem) end function integer(kind=c_int64_t) function h5pt_readstepattrib_i4 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned at file open character(len=*), intent(in) :: name !< the name of the attribute integer(kind=c_int), intent(out) :: data(*) !< buffer to read value into h5pt_readstepattrib_i4 = h5ptc_readstepattrib_i4 ( filehandle, cstring(name), data ) end function ! ! read/write string ! integer(kind=c_int64_t) function h5pt_writestepattrib_string ( filehandle, name, value) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the attribute character(len=*), intent(in) :: value !< the string value to store h5pt_writestepattrib_string = h5ptc_writestepattrib_string ( filehandle, cstring(name), cstring(value)) end function integer(kind=c_int64_t) function h5pt_readstepattrib_string ( filehandle, name, value) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the attribute character(len=*), intent(out) :: value !< buffer to read the string value into h5pt_readstepattrib_string = h5ptc_readstepattrib_string ( filehandle, cstring(name), value) value = fstring(value) end function !--------------------------------------------------------------------------- ! ! function to safely convert a string to c format (ie. with a terminating ! ascii null character) ! !--------------------------------------------------------------------------- function cstring(string) implicit none character(len=*), intent(in) :: string character(len=len(string)+1) :: cstring cstring = trim(string)//char(0) end function cstring !--------------------------------------------------------------------------- ! ! function to safely convert a string from c format (ie. with a terminating ! ascii null character) back to a normal Fortran string ! !--------------------------------------------------------------------------- function fstring(string) implicit none character(len=*), intent(in) :: string !< the name of the dataset character(len=len(string)) :: fstring integer :: idx idx = index(string,char(0)) if (idx.gt.1) then fstring = string(1:idx-1) else fstring = '' endif end function fstring end module h5partattrib splash/src/H5Part/H5Part.F90000644 000766 000000 00000063717 13261626263 016257 0ustar00dpricewheel000000 000000 !------------------------------------------------------------- ! ! Fortran 90 interface to H5Part, using ! the Fortran 2003 C interoperability module (iso_c_binding) ! ! Written by Daniel Price 08/04/10 ! daniel.price@sci.monash.edu.au ! ! We first specify the interfaces to the C interface routines ! used to handle the H5partfile container object. However, all ! string conversion is done in the Fortran, not in the C. ! !------------------------------------------------------------- module h5part use, intrinsic :: iso_c_binding, only:c_char,c_int,c_int64_t,c_double,c_float implicit none integer(kind=c_int64_t), parameter, public :: H5PART_INT64 = 1 integer(kind=c_int64_t), parameter, public :: H5PART_INT32 = 2 integer(kind=c_int64_t), parameter, public :: H5PART_FLOAT64 = 3 integer(kind=c_int64_t), parameter, public :: H5PART_FLOAT32 = 4 integer(kind=c_int64_t), parameter, public :: H5PART_CHAR = 5 integer(kind=c_int64_t), parameter, public :: H5PART_STRING = 6 character(len=7), dimension(6), parameter, public :: & h5part_type = (/'INT64 ',& 'INT32 ',& 'FLOAT64',& 'FLOAT32',& 'CHAR ',& 'STRING '/) ! ! interfaces provided by this module ! public :: h5pt_openr,h5pt_openw,h5pt_opena,h5pt_close public :: h5pt_openr_align,h5pt_openw_align,h5pt_opena_align #ifdef PARALLEL_IO public :: h5pt_openr_par,h5pt_openw_par,h5pt_opena_par public :: h5pt_openr_par_align,h5pt_openw_par_align,h5pt_opena_par_align #endif public :: h5pt_setnpoints,h5pt_setnpoints_strided public :: h5pt_getnpoints public :: h5pt_setstep,h5pt_getnsteps,h5pt_getndatasets public :: h5pt_getdatasetname,h5pt_getdatasetinfo public :: h5pt_setview,h5pt_setview_indices public :: h5pt_getview public :: h5pt_resetview,h5pt_hasview public :: h5pt_set_verbosity_level public :: h5pt_writedata public :: h5pt_readdata ! ! the type-specific routines are also public ! (could make these private to allow only ! the generic interface to be used) ! public :: h5pt_writedata_r8,h5pt_writedata_r4, & h5pt_writedata_i8,h5pt_writedata_i4 public :: h5pt_readdata_r8,h5pt_readdata_r4, & h5pt_readdata_i8,h5pt_readdata_i4 private ! ! generic interface for writing data of any type ! interface h5pt_writedata module procedure h5pt_writedata_i4,h5pt_writedata_i8, & h5pt_writedata_r4,h5pt_writedata_r8 end interface h5pt_writedata ! ! generic interface for reading data of any type ! interface h5pt_readdata module procedure h5pt_readdata_i4,h5pt_readdata_i8, & h5pt_readdata_r4,h5pt_readdata_r8 end interface h5pt_readdata !--------------------------- ! ! interfaces to c routines ! !--------------------------- interface ! ! Opening and closing files ! integer(kind=c_int64_t) function h5ptc_openr( filename ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for reading end function integer(kind=c_int64_t) function h5ptc_openw ( filename ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for writing end function integer(kind=c_int64_t) function h5ptc_opena ( filename ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for appending end function integer(kind=c_int64_t) function h5ptc_openr_align ( filename, align ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for reading integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes end function integer(kind=c_int64_t) function h5ptc_openw_align ( filename, align ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for writing integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes end function integer(kind=c_int64_t) function h5ptc_opena_align ( filename, align ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for appending integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes end function integer(kind=c_int64_t) function h5pt_close ( filehandle ) bind(C,name="h5ptc_close") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function #ifdef PARALLEL_IO ! ! Opening files (parallel I/O) ! integer(kind=c_int64_t) function h5ptc_openr_par ( filename, mpi_communicator ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for reading integer, intent(in) :: mpi_communicator !< the MPI communicator used by the program end function integer(kind=c_int64_t) function h5ptc_openw_par ( filename, mpi_communicator ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for writing integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program end function integer(kind=c_int64_t) function h5ptc_opena_par ( filename, mpi_communicator ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for appending integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program end function integer(kind=c_int64_t) function h5ptc_openr_par_align ( filename, mpi_communicator, align ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for reading integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes end function integer(kind=c_int64_t) function h5ptc_openw_par_align ( filename, mpi_communicator, align, flags ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for writing integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes character(kind=c_char), dimension(1), intent(in) :: flags !< additional flags end function integer(kind=c_int64_t) function h5ptc_opena_par_align ( filename, mpi_communicator, align, flags ) bind(C) import character(kind=c_char), dimension(1), intent(in) :: filename !< the filename to open for appending integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes character(kind=c_char), dimension(1), intent(in) :: flags !< additional flags end function #endif ! ! Setting up the data model ! (where no strings are passed these are just interfaces to the c routines that convert the filehandle) ! integer(kind=c_int64_t) function h5pt_setnpoints ( filehandle, npoints ) bind(C,name="h5ptc_setnpoints") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: npoints !< the number of particles on *this* processor end function integer(kind=c_int64_t) function h5pt_setnpoints_strided ( filehandle, npoints, stride ) & bind(C,name="h5ptc_setnpoints_strided") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: npoints !< the number of particles on *this* processor integer(kind=c_int64_t), intent(in) :: stride !< the stride value (e.g. the number of fields in the particle data array) end function integer(kind=c_int64_t) function h5pt_setstep (filehandle,step) bind(C,name="h5ptc_setstep") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: step !< a timestep value >= 1 end function integer(kind=c_int64_t) function h5pt_getnsteps (filehandle) bind(C,name="h5ptc_getnsteps") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function integer(kind=c_int64_t) function h5pt_getndatasets (filehandle) bind(C,name="h5ptc_getndatasets") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function integer(kind=c_int64_t) function h5pt_getnpoints (filehandle) bind(C,name="h5ptc_getnpoints") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function integer(kind=c_int64_t) function h5ptc_getdatasetname (filehandle,index,name,l_name) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: index !< index of dataset to query (starting from 0) character(kind=c_char), dimension(1), intent(out) :: name !< buffer to read the dataset name into integer(kind=c_int64_t), intent(in) :: l_name !< size of name end function integer(kind=c_int64_t) function h5ptc_getdatasetinfo (filehandle,index,name,data_type,nelem,l_name) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: index !< index of dataset to query (starting from 0) character(kind=c_char), dimension(1), intent(out) :: name !< buffer to read the dataset name into integer(kind=c_int64_t), intent(out) :: data_type !< type of data in dataset integer(kind=c_int64_t), intent(out) :: nelem !< number of elements integer(kind=c_int64_t), intent(in) :: l_name !< size of name end function ! ! Setting and getting views ! (as no strings are passed these are just interfaces to the c routines that convert the filehandle) ! integer(kind=c_int64_t) function h5pt_setview (filehandle,start,end) bind(C,name="h5ptc_setview") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: start !< offset of the first particle in the view integer(kind=c_int64_t), intent(in) :: end !< offset of the last particle in the view (inclusive) end function integer(kind=c_int64_t) function h5pt_setview_indices (filehandle,indices,nelem) bind(C,name="h5ptc_setview_indices") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: indices(*) !< list of indicies to select in this view integer(kind=c_int64_t), intent(in) :: nelem !< number of particles in the list end function integer(kind=c_int64_t) function h5pt_resetview (filehandle) bind(C,name="h5ptc_resetview") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function integer(kind=c_int64_t) function h5pt_hasview (filehandle) bind(C,name="h5ptc_hasview") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open end function integer(kind=c_int64_t) function h5pt_getview (filehandle,start,end) bind(C,name="h5ptc_getview") import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(out) :: start !< buffer to store the offset of the first particle in the view integer(kind=c_int64_t), intent(out) :: end !< buffer to store the offset of the last particle in the view (inclusive) end function ! ! Reading and writing datasets ! integer(kind=c_int64_t) function h5ptc_writedata_r8 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset real(kind=c_double), intent(in) :: data(*) !< the array of float64 data to write end function integer(kind=c_int64_t) function h5ptc_readdata_r8 (filehandle,name,data) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset real(kind=c_double), intent(out) :: data(*) !< array to read float64 data into end function integer(kind=c_int64_t) function h5ptc_writedata_r4 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset real(kind=c_float), intent(in) :: data(*) !< the array of float32 data to write end function integer(kind=c_int64_t) function h5ptc_readdata_r4 (filehandle,name,data) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset real(kind=c_float), intent(out) :: data(*) !< array to read float32 data into end function integer(kind=c_int64_t) function h5ptc_writedata_i8 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset integer(kind=c_int64_t), intent(in) :: data(*) !< the array of int64 data to write end function integer(kind=c_int64_t) function h5ptc_readdata_i8 (filehandle,name,data) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset integer(kind=c_int64_t), intent(out) :: data(*) !< array to read int64 data into end function integer(kind=c_int64_t) function h5ptc_writedata_i4 ( filehandle, name, data ) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset integer(kind=c_int), intent(in) :: data(*) !< the array of int32 data to write end function integer(kind=c_int64_t) function h5ptc_readdata_i4 (filehandle,name,data) bind(C) import integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(kind=c_char), dimension(1), intent(in) :: name !< the name of the dataset integer(kind=c_int), intent(out) :: data(*) !< array to read int32 data into end function ! ! routines where no conversion of anything is needed: i.e. just call H5Part C routines directly ! integer(kind=c_int64_t) function h5pt_set_verbosity_level ( level ) bind(C,name="H5PartSetVerbosityLevel") import integer(kind=c_int64_t), intent(in) :: level !< the level from 0 (no output) to 5 (most detailed) end function end interface contains !--------------------------------------------------------------------------- ! ! wrappers for functions with string arguments: ! converts strings into C strings ! !--------------------------------------------------------------------------- integer(kind=c_int64_t) function h5pt_openr( filename ) implicit none character(len=*), intent(in) :: filename !< the filename to open for reading h5pt_openr = h5ptc_openr( cstring(filename) ) end function integer(kind=c_int64_t) function h5pt_openw ( filename ) implicit none character(len=*), intent(in) :: filename !< the filename to open for writing h5pt_openw = h5ptc_openw ( cstring(filename) ) end function integer(kind=c_int64_t) function h5pt_opena ( filename ) implicit none character(len=*), intent(in) :: filename !< the filename to open for appending h5pt_opena = h5ptc_opena ( cstring(filename) ) end function integer(kind=c_int64_t) function h5pt_openr_align ( filename, align ) implicit none character(len=*), intent(in) :: filename !< the filename to open for reading integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes h5pt_openr_align = h5ptc_openr_align ( cstring(filename), align ) end function integer(kind=c_int64_t) function h5pt_openw_align ( filename, align ) implicit none character(len=*), intent(in) :: filename !< the filename to open for writing integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes h5pt_openw_align = h5ptc_openw_align ( cstring(filename), align ) end function integer(kind=c_int64_t) function h5pt_opena_align ( filename, align ) implicit none character(len=*), intent(in) :: filename !< the filename to open for appending integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes h5pt_opena_align = h5ptc_opena_align ( cstring(filename), align ) end function #ifdef PARALLEL_IO ! ! opening files (parallel I/O) ! integer(kind=c_int64_t) function h5pt_openr_par ( filename, mpi_communicator ) implicit none character(len=*), intent(in) :: filename !< the filename to open for reading integer, intent(in) :: mpi_communicator !< the MPI communicator used by the program h5pt_openr_par = h5ptc_openr_par ( cstring(filename), mpi_communicator ) end function integer(kind=c_int64_t) function h5pt_openw_par ( filename, mpi_communicator ) implicit none character(len=*), intent(in) :: filename !< the filename to open for writing integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program h5pt_openw_par = h5ptc_openw_par ( cstring(filename), mpi_communicator ) end function integer(kind=c_int64_t) function h5pt_opena_par ( filename, mpi_communicator ) implicit none character(len=*), intent(in) :: filename !< the filename to open for appending integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program h5pt_opena_par = h5ptc_opena_par ( cstring(filename), mpi_communicator ) end function integer(kind=c_int64_t) function h5pt_openr_par_align ( filename, mpi_communicator, align ) implicit none character(len=*), intent(in) :: filename !< the filename to open for reading integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes h5pt_openr_par_align = h5ptc_openr_par_align ( cstring(filename), mpi_communicator, align ) end function integer(kind=c_int64_t) function h5pt_openw_par_align ( filename, mpi_communicator, align, flags ) implicit none character(len=*), intent(in) :: filename !< the filename to open for writing integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes character(len=*), intent(in) :: flags !< additional flags h5pt_openw_par_align = h5ptc_openw_par_align ( cstring(filename), mpi_communicator, align, cstring(flags) ) end function integer(kind=c_int64_t) function h5pt_opena_par_align ( filename, mpi_communicator, align, flags ) implicit none character(len=*), intent(in) :: filename !< the filename to open for appending integer, intent(in) :: mpi_communicator !< the MPI_Communicator used by the program integer(kind=c_int64_t), intent(in) :: align !< alignment value in bytes character(len=*), intent(in) :: flags !< additional flags h5pt_opena_par_align = h5ptc_opena_par_align ( cstring(filename), mpi_communicator, align, cstring(flags) ) end function #endif ! ! Setting up the data model ! integer(kind=c_int64_t) function h5pt_getdatasetname (filehandle,index,name) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: index !< index of dataset to query (starting from 0) character(len=*), intent(out) :: name !< buffer to read the dataset name into integer(kind=c_int64_t) :: l_name l_name = len(name) h5pt_getdatasetname = h5ptc_getdatasetname(filehandle,index,name,l_name) name = fstring(name) end function h5pt_getdatasetname integer(kind=c_int64_t) function h5pt_getdatasetinfo(filehandle,idx,name,data_type,nelem) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open integer(kind=c_int64_t), intent(in) :: idx !< index of dataset to query (starting from 0) character(len=*), intent(out) :: name !< buffer to read the dataset name into integer(kind=c_int64_t), intent(out) :: data_type !< type of data in dataset integer(kind=c_int64_t), intent(out) :: nelem !< number of elements integer(kind=c_int64_t) :: l_name l_name = len(name) h5pt_getdatasetinfo = h5ptc_getdatasetinfo(filehandle,idx,name,data_type,nelem,l_name) name = fstring(name) end function h5pt_getdatasetinfo ! ! Reading and writing datasets ! integer(kind=c_int64_t) function h5pt_writedata_r8 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset real(kind=c_double), intent(in) :: data(*) !< the array of float64 data to write h5pt_writedata_r8 = h5ptc_writedata_r8 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_readdata_r8 ( filehandle, name, data) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset real(kind=c_double), intent(out) :: data(*) !< array to read float64 data into h5pt_readdata_r8 = h5ptc_readdata_r8 ( filehandle, cstring(name), data) end function integer(kind=c_int64_t) function h5pt_writedata_r4 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset real(kind=c_float), intent(in) :: data(*) !< the array of float32 data to write h5pt_writedata_r4 = h5ptc_writedata_r4 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_readdata_r4 ( filehandle, name, data) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset real(kind=c_float), intent(out) :: data(*) !< array to read float32 data into h5pt_readdata_r4 = h5ptc_readdata_r4 ( filehandle, cstring(name), data) end function integer(kind=c_int64_t) function h5pt_writedata_i8 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset integer(kind=c_int64_t), intent(in) :: data(*) !< the array of int64 data to write h5pt_writedata_i8 = h5ptc_writedata_i8 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_readdata_i8 ( filehandle, name, data) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset integer(kind=c_int64_t), intent(out) :: data(*) !< array to read int64 data into h5pt_readdata_i8 = h5ptc_readdata_i8 ( filehandle, cstring(name), data) end function integer(kind=c_int64_t) function h5pt_writedata_i4 ( filehandle, name, data ) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset integer(kind=c_int), intent(in) :: data(*) !< the array of int32 data to write h5pt_writedata_i4 = h5ptc_writedata_i4 ( filehandle, cstring(name), data ) end function integer(kind=c_int64_t) function h5pt_readdata_i4 (filehandle,name,data) implicit none integer(kind=c_int64_t), intent(in) :: filehandle !< the handle returned during file open character(len=*), intent(in) :: name !< the name of the dataset integer(kind=c_int), intent(out) :: data(*) !< array to read int32 data into h5pt_readdata_i4 = h5ptc_readdata_i4 ( filehandle, cstring(name), data) end function !--------------------------------------------------------------------------- ! ! function to safely convert a string to c format (ie. with a terminating ! ascii null character) ! !--------------------------------------------------------------------------- function cstring(string) implicit none character(len=*), intent(in) :: string character(len=len(string)+1) :: cstring cstring = trim(string)//char(0) end function cstring !--------------------------------------------------------------------------- ! ! function to safely convert a string from c format (ie. with a terminating ! ascii null character) back to a normal Fortran string ! !--------------------------------------------------------------------------- function fstring(string) implicit none character(len=*), intent(in) :: string !< the name of the dataset character(len=len(string)) :: fstring integer :: idx idx = index(string,char(0)) if (idx.gt.1) then fstring = string(1:idx-1) else fstring = '' endif end function fstring end module h5part splash/src/H5Part/README000644 000766 000000 00000003164 13261626263 015542 0ustar00dpricewheel000000 000000 Website: http://vis.lbl.gov/Research/AcceleratorSAPP/ Particle based simulations of accelerator beam-lines, especially in six dimensional phase space, generate vast amounts of data. Even though a subset of statistical information regarding phase space or analysis needs to be preserved, reading and writing such enormous restart files on massively parallel supercomputing systems remains challenging. H5Part is a very simple data storage schema and provides an API that simplifies the reading/writing of the data to the HDF5 file format. An important foundation for a stable visualization and data analysis environment is a stable and portable file storage format and its associated APIs. The presence of a "common file storage format," including associated APIs, will help foster a fundamental level of interoperability across the project's software infrastructure. It will also help ensure that key data analysis capabilities are present during the earliest phases of the software development effort. H5Part is built on top of the HDF5 (Hierarchical Data Format). HDF5 offers a self-describing machine-independent binary file format that supports scalable parallel I/O performance for MPI codes on a variety of supercomputing systems, and works equally well on laptop computers. The API is available for C, C++, and Fortran codes. The H5Part file format and APIs enable disparate research groups with different simulation implementations to transparently share datasets and data analysis tools. For instance, the common file format will enable groups that depend on completely different simulation implementations to share data analysis tools. splash/src/H5Part/COPYING000644 000766 000000 00000007343 13261626263 015720 0ustar00dpricewheel000000 000000 *** Copyright Notice *** H5Part Copyright (c) 2006-2009, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy) and the Paul Scherrer Institut (Switzerland). All rights reserved. If you have questions about your rights to use or distribute this software, please contact Berkeley Lab's Technology Transfer Department at TTD@lbl.gov referring to "H5Part (LBNL Ref CR-2255)" NOTICE. This software was developed under partial funding from the U.S. Department of Energy. As such, the U.S. Government has been granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, prepare derivative works, and perform publicly and display publicly. Beginning five (5) years after the date permission to assert copyright is obtained from the U.S. Department of Energy, and subject to any subsequent five (5) year renewals, the U.S. Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, prepare derivative works, distribute copies to the public, perform publicly and display publicly, and to permit others to do so. *** License agreement *** H5Part Copyright (c) 2006-2009, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy) and the Paul Scherrer Institut (Switzerland). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory, U.S. Dept. of Energy, Paul Scherrer Institut (Switzerland) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to Lawrence Berkeley National Laboratory, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form. splash/src/H5Part/H5PartAttribF.c000644 000766 000000 00000016122 13261626263 017403 0ustar00dpricewheel000000 000000 /* * This is my modified H5PartAttribF.c for use in SPLASH * Main changes are that the strings are now passed * directly from Fortran, already with the null character appended * * Modified from the original H5Part Fortran interface * by Daniel Price 08/04/10 */ #include "H5Part.h" h5part_int64_t h5ptc_writefileattrib_r8 ( h5part_int64_t *f, const char *name, const h5part_float64_t *data, const h5part_float64_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteFileAttrib ( filehandle, name, H5PART_FLOAT64, data, *nelem); return herr; } h5part_int64_t h5ptc_readfileattrib_r8 ( h5part_int64_t *f, const char *name, const h5part_float64_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadFileAttrib ( filehandle, name, (void*)data); return herr; } h5part_int64_t h5ptc_writefileattrib_r4 ( h5part_int64_t *f, const char *name, const h5part_float32_t *data, const h5part_float32_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteFileAttrib ( filehandle, name, H5PART_FLOAT32, data, *nelem); return herr; } h5part_int64_t h5ptc_readfileattrib_r4 ( h5part_int64_t *f, const char *name, const h5part_float32_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadFileAttrib ( filehandle, name, (void*)data); return herr; } h5part_int64_t h5ptc_writefileattrib_i8 ( h5part_int64_t *f, const char *name, const h5part_int64_t *data, const h5part_int64_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteFileAttrib ( filehandle, name, H5PART_INT64, data, *nelem); return herr; } h5part_int64_t h5ptc_readfileattrib_i8 ( h5part_int64_t *f, const char *name, const h5part_int64_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadFileAttrib ( filehandle, name, (void*)data); return herr; } h5part_int64_t h5ptc_writefileattrib_i4 ( h5part_int64_t *f, const char *name, const h5part_int32_t *data, const h5part_int32_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteFileAttrib ( filehandle, name, H5PART_INT32, data, *nelem); return herr; } h5part_int64_t h5ptc_readfileattrib_i4 ( h5part_int64_t *f, const char *name, const h5part_int32_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadFileAttrib ( filehandle, name, (void*)data); return herr; } h5part_int64_t h5ptc_writestepattrib_r8 ( h5part_int64_t *f, const char *name, const h5part_float64_t *data, const h5part_float64_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteStepAttrib ( filehandle, name, H5PART_FLOAT64, data, *nelem); return herr; } h5part_int64_t h5ptc_readstepattrib_r8 ( h5part_int64_t *f, const char *name, const h5part_float64_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadStepAttrib ( filehandle, name, (void*)data); return herr; } h5part_int64_t h5ptc_writestepattrib_r4 ( h5part_int64_t *f, const char *name, const h5part_float32_t *data, const h5part_float32_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteStepAttrib ( filehandle, name, H5PART_FLOAT32, data, *nelem); return herr; } h5part_int64_t h5ptc_readstepattrib_r4 ( h5part_int64_t *f, const char *name, const h5part_float32_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadStepAttrib ( filehandle, name, (void*)data); return herr; } h5part_int64_t h5ptc_writestepattrib_i8 ( h5part_int64_t *f, const char *name, const h5part_int64_t *data, const h5part_int64_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteStepAttrib ( filehandle, name, H5PART_INT64, data, *nelem); return herr; } h5part_int64_t h5ptc_readstepattrib_i8 ( h5part_int64_t *f, const char *name, const h5part_int64_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadStepAttrib ( filehandle, name, (void*)data); return herr; } h5part_int64_t h5ptc_writestepattrib_i4 ( h5part_int64_t *f, const char *name, const h5part_int32_t *data, const h5part_int32_t *nelem ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteStepAttrib ( filehandle, name, H5PART_INT32, data, *nelem); return herr; } h5part_int64_t h5ptc_readstepattrib_i4 ( h5part_int64_t *f, const char *name, const h5part_int32_t *data ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadStepAttrib ( filehandle, name, (void*)data); return herr; } /*=================== Attributes ================*/ h5part_int64_t h5ptc_writefileattrib_string ( const h5part_int64_t *f, const char *attrib_name, const char *attrib_value ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteFileAttribString ( filehandle, attrib_name, attrib_value ); return herr; } h5part_int64_t h5ptc_writestepattrib_string ( const h5part_int64_t *f, const char *attrib_name, const char *attrib_value ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartWriteStepAttribString ( filehandle, attrib_name, attrib_value ); return herr; } /* Reading attributes ************************* */ h5part_int64_t h5ptc_getnstepattribs ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartGetNumStepAttribs ( filehandle ); } h5part_int64_t h5ptc_getnfileattribs ( const h5part_int64_t *f ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; return H5PartGetNumFileAttribs ( filehandle ); } h5part_int64_t h5ptc_getstepattribinfo ( const h5part_int64_t *f, const h5part_int64_t *idx, char *name, h5part_int64_t *nelem, const h5part_int64_t *l_name ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t type; h5part_int64_t herr = H5PartGetStepAttribInfo ( filehandle, *idx, name, *l_name, &type, nelem); return herr; } h5part_int64_t h5ptc_getfileattribinfo ( const h5part_int64_t *f, const h5part_int64_t *idx, char *name, h5part_int64_t *nelem, const h5part_int64_t *l_name ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t type; h5part_int64_t herr = H5PartGetFileAttribInfo ( filehandle, *idx, name, *l_name, &type, nelem); return herr; } h5part_int64_t h5ptc_readstepattrib_string ( const h5part_int64_t *f, const char *attrib_name, char *attrib_value ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadStepAttrib ( filehandle, attrib_name, attrib_value ); return herr; } h5part_int64_t h5ptc_readfileattrib_string ( const h5part_int64_t *f, const char *attrib_name, char *attrib_value ) { H5PartFile *filehandle = (H5PartFile*)(size_t)*f; h5part_int64_t herr = H5PartReadFileAttrib ( filehandle, attrib_name, attrib_value ); return herr; } splash/build/Makefile000644 000766 000000 00000105600 13261626263 015525 0ustar00dpricewheel000000 000000 ##-------------------------------------------------------------------## ## Makefile for compiling SPLASH and linking with ## ## required libraries ## ## ## ## Written by Daniel Price ## ## University of Exeter, UK, 2004-2008 ## ## Monash University, Australia, 2008- ## ## ## ## requires GNU make (on some systems this is 'gmake' instead ## ## of 'make') ## ## ## ## see the INSTALL file for detailed installation instructions ## ##-------------------------------------------------------------------## .KEEP_STATE: KNOWN_SYSTEM=no SRCDIR=../src BINDIR=../bin TESTDIR=../tests VPATH=$(SRCDIR) $(SRCDIR)/H5Part SHELL=/bin/bash # # some default settings for unix systems # FC= FFLAGS= # # change the line below if SPLASH does not find the X11 libraries # (some settings of the SYSTEM variable for specific machines overwrite this) # # X11_LIBS= -L/usr/X11R6/lib64 -lX11 X11_DIR= /usr/X11/ X11_LIBS= -L$(X11_DIR)/lib -lX11 X11_CFLAGS= -I$(X11_DIR)/include CAIRO_LIBS= -L$(X11_DIR)/lib -lcairo CAIRO_CFLAGS= -I$(X11_DIR)/include #-------------------------------------------------------------- # here we choose the backend plotting library for splash # (giza is the default backend for splash v2.x, but can # still be compiled with PGPLOT, just with disabled features) #-------------------------------------------------------------- ifneq ($(BACKEND), pgplot) #-- C A I R O -- GIZA_DIR= $(PREFIX) VPATH+= $(GIZA_DIR)/include PLOTLIB= giza-fortran.F90 plotlib_giza.f90 PGPLOTLIBS = -L$(GIZA_DIR)/lib -lgiza $(CAIRO_LIBS) CFLAGS= -fPIC -Wall -Wextra -O3 -g CC = gcc else #-- P G P L O T -- PLOTLIB= plotlib_pgplot.f90 # # change the line below depending on where/how you have installed PGPLOT # (some settings of the SYSTEM variable for specific machines overwrite this) # PGPLOTLIBS = -L$(PGPLOT_DIR) -lpgplot -lpng endif #-------------------------------------------------------------- # # If you need the HDF5 libraries, edit the lines below # possibly adding a -L/libpath/ and a -I/includepath/ # HDF5LIBS = -L$(HDF5ROOT)/lib -lhdf5 HDF5INCLUDE = -I$(HDF5ROOT)/include H5PART_LIBS = -L$(H5PART_DIR)/lib -lH5Part H5PART_INCLUDE = -I$(H5PART_DIR)/include # # this file contains system-dependent routines like getarg, iargc etc. # SYSTEMFILE= system_f2003.f90 # # this can be used to static link the pgplot libraries if all else fails # STATICLIBS= # # set the parallel flag to 'yes' to compile with openMP directives # #PARALLEL=no ifndef OPENMP OPENMP=yes endif # # the openMP flags should be set in the lines defining your system type # (ie. leave line below blank) OMPFLAGS= # # the endian flag can be used to compile the code to read BIG or LITTLE endian data # some compilers also allow this to be done at runtime (e.g. g95, ifort) by setting # an environment variable appropriately (e.g. G95_ENDIAN or F_UFMTENDIAN) # #ENDIAN= #ENDIAN='BIG' #ENDIAN='LITTLE' # # default destination for installed binaries # DESTDIR= PREFIX=/usr/local/ # # default C compiler # ifndef CC CC = gcc endif ifndef CXX CXX = g++ endif # default C++ library linking # (different between Mac/Linux) UNAME=$(shell uname) ifeq ($(UNAME), Darwin) CXXLIBS= -lc++ else CXXLIBS= -lstdc++ endif #-------------------------------------------------------------- # the following are general settings for particular compilers # # set the environment variable 'SYSTEM' to one of those # listed to use the appropriate settings # # e.g. in tcsh use # setenv SYSTEM 'g95' # # in bash the equivalent is # export SYSTEM='g95' # #-------------------------------------------------------------- ifeq ($(SYSTEM), gfortran) # gfortran compiler (part of gcc 4.x.x) FC= gfortran FFLAGS= -O3 -frecord-marker=4 DBLFLAGS= -fdefault-real-8 -fdefault-double-8 SYSTEMFILE= system_f2003.f90 DEBUGFLAG= -Wall -Wextra -pedantic -g -frange-check -fcheck=all -fbacktrace \ -finit-real=NaN #-ffpe-trap=invalid,zero,overflow OMPFLAGS= -fopenmp ENDIANFLAGBIG= -fconvert=big-endian ENDIANFLAGLITTLE= -fconvert=little-endian KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),g95) # using the g95 compiler FC= g95 FFLAGS= -O3 -ffast-math DBLFLAGS= -r8 SYSTEMFILE= system_f2003.f90 # this is for Fortran 2003 compatible compilers DEBUGFLAG= -Wall -Wextra -Wno=165 -g -fbounds-check -ftrace=full -freal=NaN ENDIANFLAGBIG= -fendian='BIG' ENDIANFLAGLITTLE= -fendian='LITTLE' PARALLEL= no CC = gcc -m32 KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),nagf95) # NAG f95 compiler FC= f95 FFLAGS= -O3 DBLFLAGS= -r8 SYSTEMFILE= system_unix_NAG.f90 PARALLEL= no KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),sunf95) # sun f95 compiler on linux FC= sunf95 FFLAGS= -fast -ftrap=%none OMPFLAGS= -openmp DBLFLAGS= -xtypemap=real:64,double:64 DEBUGFLAG= -g -C -w4 -errtags -erroff=COMMENT_1582,COMMENT_1744 -ftrap=%all SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -xfilebyteorder=big16:%all ENDIANFLAGLITTLE= -xfilebyteorder=little16:%all KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),ifort) # this is for the intel fortran compiler (version 10) FC= ifort # FFLAGS= -O3 -nbs -i_dynamic FFLAGS= -O3 -nbs OMPFLAGS= -openmp DBLFLAGS= -r8 DEBUGFLAG= -C -g SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -convert big_endian ENDIANFLAGLITTLE= -convert little_endian # or use setenv F_UFMTENDIAN=big or little at runtime KNOWN_SYSTEM=yes CC=icc CFLAGS=-Wall -O3 -fPIC endif ifeq ($(SYSTEM),ifort8) # this is for the intel fortran compiler (version 8) FC= ifort FFLAGS= -O3 -Vaxlib -nbs OMPFLAGS= -openmp DBLFLAGS= -r8 DEBUGFLAG= -C -g SYSTEMFILE= system_unix.f90 ENDIANFLAGBIG= -convert big_endian ENDIANFLAGLITTLE= -convert little_endian # or use setenv F_UFMTENDIAN=big or little at runtime KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),pgf90) # this is for the Portland Group Fortran 90 compiler (tested with version 7.2-5) FC= pgf90 FFLAGS= -fast -mcmodel=medium -Mbackslash -Ktrap=none DBLFLAGS= -r8 DEBUGFLAG= -C -g -gopt -Mbounds -Mchkfpstk -Mchkptr -Mchkstk -Mcoff \ -Mdwarf1 -Mdwarf2 -Melf -Mpgicoff -traceback OMPFLAGS= -mp SYSTEMFILE= system_unix.f90 ENDIANFLAGBIG= -Mbyteswapio # only works on a little-endian machine ENDIANFLAGLITTLE= KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),pathf95) # this is for the Pathscale f95 compiler FC= pathf95 FFLAGS= -Ofast -mcmodel=medium DBLFLAGS= -r8 DEBUGFLAG= -C -g OMPFLAGS= -openmp SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -convert big_endian ENDIANFLAGLITTLE= -convert little_endian KNOWN_SYSTEM=yes endif #-------------------------------------------------------------- # # the following presets are machine-specific # (ie. relate to both the compiler and a specific # installation of pgplot) # #-------------------------------------------------------------- ifeq ($(SYSTEM),ukaff1a) # this is for ukaff1a FC= xlf90_r #-Wl,-t FFLAGS= -O3 -qnoescape -qsuffix=f=f90 -qextname OMPFLAGS= -qsmp=omp DEBUGFLAG= -C -g SYSTEMFILE= system_f2003.f90 X11_LIBS= -L/usr/X11R6/lib -lX11 PGPLOTLIBS= STATICLIBS= /home/dprice/pgplot/libpgplot.a /home/dprice/plot/libg2cmodified.a KNOWN_SYSTEM=yes # EXT=_temp endif ifeq ($(SYSTEM),ukaff1b) # this is for the new nodes on ukaff FC= pathf95 FFLAGS= -Ofast -mcmodel=medium DEBUGFLAG= -C -g OMPFLAGS= -openmp SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -convert big_endian ENDIANFLAGLITTLE= -convert little_endian X11_LIBS= -L/home/dprice/lib -lX11 -lpng PGPLOTLIBS= -L/home/dprice/pgplot2 -lpgplot EXT=2 # call the binary something different KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),zen) # this is for the intel fortran compiler FC= ifort FFLAGS= -O3 -mcmodel=medium -axT -warn all #-ipo #-assume nounderscore OMPFLAGS= -openmp DEBUGFLAG= -check all -traceback -g SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -convert big_endian ENDIANFLAGLITTLE= -convert little_endian X11_LIBS=-L/home/djp212/lib -lX11 PGPLOTLIBS= -lpng #-L${PGPLOT_DIR} -lpgplot -lpng STATICLIBS= /home/djp212/pgplot/libpgplot.a # or use setenv F_UFMTENDIAN=big or little at runtime # EXT=-dev KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),mymac) # these are the settings for a Mac G4 running Panther # using g95 with pgplot installed via fink FC= g95 FFLAGS= -O3 -ffast-math DEBUGFLAG= -Wall -Wextra -Wno=165 -g -fbounds-check -ftrace=full PGPLOTLIBS= -L/sw/lib/pgplot -lpgplot -lg2c -L/sw/lib -lpng \ -laquaterm -lcc_dynamic -Wl,-framework -Wl,Foundation SYSTEMFILE= system_f2003.f90 PARALLEL= no KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),intelmac) # these are the settings for an Intel Macbook running Tiger # using gfortran with pgplot installed via fink FC= gfortran FFLAGS= -O3 -ffast-math DEBUGFLAG= -Wall -Wextra -Wno=165 -g -frange-check PGPLOTLIBS= -L/sw/lib/pgplot -lpgplot -L/sw/lib -lpng \ -laquaterm -Wl,-framework -Wl,Foundation -lSystemStubs SYSTEMFILE= system_f2003.f90 PARALLEL= no KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM), gfortran_macosx) # gfortran with pgplot installed via fink FC= gfortran FFLAGS= -O3 -Wall PGPLOTLIBS= -L/sw/lib/pgplot -lpgplot -lg2c -L/sw/lib -lpng \ -laquaterm # -lSystemStubs use this on OS/X Tiger SYSTEMFILE= system_unix.f90 DEBUGFLAG= -g -frange-check OMPFLAGS= -fopenmp ENDIANFLAGBIG= -fconvert=big-endian ENDIANFLAGLITTLE= -fconvert=little-endian KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),myg95) # using the g95 compiler FC= myg95 FFLAGS= -O3 -ffast-math X11_LIBS= -L/usr/X11R6/lib64 -lX11 PGPLOTLIBS = -L/usr/local64/pgplot -lpgplot -lpng -lg2c SYSTEMFILE= system_f2003.f90 # this is for Fortran 2003 compatible compilers ENDIANFLAGBIG= -fendian='BIG' ENDIANFLAGLITTLE= -fendian='LITTLE' DBLFLAGS= -r8 PARALLEL= no KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),maccluster) # using the g95 compiler on the iMac cluster in Exeter ifeq ($(MACHTYPE),i386) # this stuff is for building universal binaries across intel/ppc macs FC= myg95 EXT= _i386 else FC= g95 EXT= _ppc endif FFLAGS= -O3 -ffast-math -Wall -Wextra -Wno=165 X11_LIBS= -L/usr/X11R6/lib -lX11 PGPLOTLIBS= -lSystemStubs STATICLIBS= /AstroUsers/djp212/pgplot/libpgplot.a SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -fendian='BIG' ENDIANFLAGLITTLE= -fendian='LITTLE' DEBUGFLAG=-fbounds-check -ftrace=full PARALLEL= no KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),astromac) # using the g95 compiler on the Astrophysics cluster in Exeter FC= g95 FFLAGS= -O3 -ffast-math -Wall -Wextra -Wno=165 X11_LIBS= -L/usr/X11R6/lib -lX11 PGPLOTLIBS= -L/opt/local/lib -lpng -lg2c STATICLIBS= /usr/local/pgplot/libpgplot.a SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -fendian='BIG' ENDIANFLAGLITTLE= -fendian='LITTLE' DBLFLAGS= -r8 DEBUGFLAG=-fbounds-check -ftrace=full PARALLEL= no KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),spectrum) # sun f95 compiler on linux FC= f95 FFLAGS= -fast -ftrap=%none OMPFLAGS= -openmp DBLFLAGS= -xtypemap=real:64,double:64 DEBUGFLAG= -g -C -w4 -errtags -erroff=COMMENT_1582,COMMENT_1744 -ftrap=%all SYSTEMFILE= system_f2003.f90 ENDIANFLAGBIG= -xfilebyteorder=big16:%all ENDIANFLAGLITTLE= -xfilebyteorder=little16:%all X11_LIBS = -L/usr/X11R6/lib64 -lX11 KNOWN_SYSTEM=yes endif ifeq ($(SYSTEM),cody) FC= gfortran FFLAGS= -O3 -Wall SYSTEMFILE= system_f2003.f90 DBLFLAGS= -fdefault-real-8 -fdefault-double-8 DEBUGFLAG= -g -frange-check OMPFLAGS= -fopenmp ENDIANFLAGBIG= -fconvert=big-endian ENDIANFLAGLITTLE= -fconvert=little-endian SNFLAGS= -L$(HOME)/tree16/Objfiles/g5 -lsw CFLAGS = -g -O2 -Wall -I$(HOME)/tree16/include -fbounds-check CC = gcc KNOWN_SYSTEM=yes endif # # these are the flags used for linking # LDFLAGS= $(X11_LIBS) $(PGPLOTLIBS) # # this is an option to change the endian-ness at compile time # (provided the appropriate flags are specified for the compiler) # ifeq ($(ENDIAN), BIG) FFLAGS += ${ENDIANFLAGBIG} endif ifeq ($(ENDIAN), LITTLE) FFLAGS += ${ENDIANFLAGLITTLE} endif # compile in parallel ifeq ($(PARALLEL),yes) FFLAGS += $(OMPFLAGS) else ifeq ($(OPENMP),yes) FFLAGS += $(OMPFLAGS) endif endif # compile in double precision ifeq ($(DOUBLEPRECISION), yes) FFLAGS += ${DBLFLAGS} endif # add debugging flags at compile time ifeq ($(DEBUG),yes) FFLAGS += $(DEBUGFLAG) endif # rename the executable for development work ifeq ($(DEV),yes) EXT= -dev endif # link with hdf5 libraries ifeq ($(HDF5),yes) CFLAGS += $(HDF5INCLUDE) LDFLAGS += $(HDF5LIBS) endif # # MPI... no splash doesn't use it # but sometimes you need to compile # with the mpi compiler (e.g. to link # correctly to MPI-HDF5 libraries) # This just changes the compiler name # whilst keeping the flags the same # if MPI is set to "yes" # ifeq ($(MPI),yes) FC= mpif90 -DPARALLEL_IO CC=mpicc -DPARALLEL_IO endif # # If PGPLOT was compiled with a different compiler to the one used here, # need to link to the libraries for that compiler. We attempt to do this # automatically below by looking in the PGPLOT makefile. # # If the relevant library is not found, may also need -L/dir/ for the directory # where the corresponding library is located (e.g. -L/usr/local/gfortran/lib -lgfortran) # # (information about what is done here is printed via the checkpgplot target, below) # ifeq ($(BACKEND),pgplot) ifdef PGPLOT_DIR PGPLOT_COMP=${shell if [ -e $$PGPLOT_DIR/makefile ]; then grep 'FCOMPL=' $$PGPLOT_DIR/makefile | cut -d= -f2; else echo unknown; fi} ifneq (X$(FC), X) # make sure it is not just accidentally blank ifneq ($(PGPLOT_COMP), $(FC)) # g77-compiled PGPLOT ifeq ($(PGPLOT_COMP), g77) PGPLOTLIBS+=-lg2c endif # gfortran-compiled PGPLOT ifeq ($(PGPLOT_COMP), gfortran) PGPLOTLIBS+=-lgfortran endif # g95-compiled PGPLOT ifeq ($(PGPLOT_COMP), g95) PGPLOTLIBS+=-lg95 endif endif endif endif endif ifeq ($(FC),gfortran) GFORTRAN_VERSION=${shell $(FC) -dumpversion | head -1 | awk '{print $$NF}'} GFORTRAN_VMAJOR:=${shell echo "$(GFORTRAN_VERSION)" | cut -f1 -d.} GFORTRAN_VMINOR:=${shell echo "$(GFORTRAN_VERSION)" | cut -f2 -d.} GFORTRAN_GE_4_4:=$(shell [ $(GFORTRAN_VMAJOR) -gt 4 -o \( $(GFORTRAN_VMAJOR) -eq 4 -a $(GFORTRAN_VMINOR) -ge 4 \) ] && echo true) else GFORTRAN_GE_4_4:=true endif # define the implicit rule to make a .o file from a .f90/.f95 file # (some Make versions don't know this) %.o : %.f90 $(FC) $(FFLAGS) -c $< -o $@ %.o : %.F90 $(FC) $(FPPFLAGS) $(FFLAGS) -c $< -o $@ %.o : %.f95 $(FC) $(FFLAGS) -c $< -o $@ %.o : %.c $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ # modules must be compiled in the correct order to check interfaces # really should include all dependencies but I am lazy SOURCES= $(PLOTLIB) globaldata.f90 asciiutils.f90 \ labels.f90 partutils.f90 transform.f90 setpage.f90 sort.f90 \ prompting.f90 promptlist.f90 geometry.f90 kernels.f90 \ interpolation.f90 plotutils.f90 colourbar.f90 \ colours.f90 colourparts.f90 timing.f90 pagecolours.f90 \ units.f90 limits.f90 geomutils.f90 dataread_utils.f90 \ write_data_gadget.f90 write_data_phantom.f90 write_pixmap.f90 \ write_griddata.f90 write_sphdata.f90 \ $(SYSTEMFILE) system_utils.f90 \ cubicsolve.f90 discplot.f90 \ fparser.f90 parsetext.f90 \ exact_function.f90 exact_dustywaves.f90 exact_planetdisc.f90 \ exact_fromfile.f90 exact_Cshock.f90 exact_mhdshock.f90 \ exact_polytrope.f90 exact_rhoh.f90 exact_rochelobe.f90 \ exact_sedov.f90 exact_shock.f90 exact_shock_sr.f90 exact_wave.f90 \ exact_toystar1D.f90 exact_toystar2D.f90 \ exact_densityprofiles.f90 exact_torus.f90 exact_bondi.f90 \ exact_ringspread.f90 exact_gresho.f90 exact.f90 shapes.f90 \ allocate.f90 titles.f90 calc_quantities.f90 contours.f90 \ interpolate3D_projection.F90 legends.f90 \ options_render.f90 options_particleplots.f90 \ adjust_data.f90 get_data.f90 \ options_data.f90 options_limits.f90 options_page.f90 \ options_powerspec.f90 \ options_vecplot.f90 options_xsecrotate.f90 pdfs.f90 \ rotate.f90 interpolate1D.f90 interpolate2D.f90 \ interpolate3D.F90 interpolate3D_xsec.f90 \ interpolate3D_proj_geom.F90 \ interpolate3D_opacity.f90 interpolate_vec.f90 \ particleplot.f90 interactive.f90 analysis.f90 \ convert_grid.f90 convert.f90 \ fieldlines.f90 \ powerspectrums.f90 render.f90 \ plotstep.f90 timestepping.f90 \ defaults.f90 menu.f90 \ splash.f90 OBJECTS1 = $(SOURCES:.f90=.o) $(STATICLIBS) OBJECTS= $(OBJECTS1:.F90=.o) # # Now compile with the appropriate data read file # (move yours to the top so that you can simply type "make") # all: mhutch ascii gadget vine sphNG ndspmhd srosph dragon seren tipsy @echo; echo ' SPLASH successfully compiled! '; @echo; echo ' Use "sudo make install" to copy the binaries to $(DESTDIR)$(PREFIX)/bin'; echo; ascii: checksystem $(OBJECTS) read_data_ascii.o $(FC) $(FFLAGS) -o $(BINDIR)/asplash$(EXT) $(OBJECTS) read_data_ascii.o $(LDFLAGS) #--build universal binary on mac cluster ifeq ($(SYSTEM), maccluster) lipo -create $(BINDIR)/asplash_ppc $(BINDIR)/asplash_i386 -output $(BINDIR)/asplash || cp $(BINDIR)/asplash$(EXT) $(BINDIR)/asplash endif cp $(BINDIR)/asplash $(BINDIR)/splash mbatesph: checksystem $(OBJECTS) read_data_mbate.o $(FC) $(FFLAGS) -o $(BINDIR)/bsplash $(OBJECTS) read_data_mbate.o $(LDFLAGS) #---h5part reader--- H5PARTSRCC=H5PartF.c H5PartAttribF.c H5PARTSRCF90=H5Part.f90 H5PartAttrib.f90 H5PARTOBJ=$(H5PARTSRCC:.c=.o) $(H5PARTSRCF90:.f90=.o) h5part: checksystem checkh5part $(OBJECTS) $(H5PARTOBJ) read_data_h5part.o $(FC) $(FFLAGS) -o $(BINDIR)/h5splash $(OBJECTS) $(H5PARTOBJ) read_data_h5part.o $(LDFLAGS) $(H5PART_LIBS) $(HDF5LIBS) read_data_h5part.o: read_data_h5part.f90 $(FC) $(FFLAGS) $(H5PART_INCLUDE) -o $@ -c $< H5PartF.o: H5PartF.c $(CC) -c $(CFLAGS) $(HDF5INCLUDE) $(H5PART_INCLUDE) $< -o $@ H5PartAttribF.o: H5PartAttribF.c $(CC) -c $(CFLAGS) $(HDF5INCLUDE) $(H5PART_INCLUDE) $< -o $@ #------------------ gadget: checksystem $(OBJECTS) read_data_gadget.o $(FC) $(FFLAGS) -o $(BINDIR)/gsplash $(OBJECTS) read_data_gadget.o $(LDFLAGS) gadgetdualendian: checksystem $(OBJECTS) read_data_gadget.o read_data_gadget_otherendian.o $(FC) $(FFLAGS) -o $(BINDIR)/gsplash $(OBJECTS) read_data_gadget.o read_data_gadget_otherendian.o $(LDFLAGS) read_data_gadget_otherendian.o: read_data_gadget.o cat read_data_gadget.f90 | awk "/subroutine read_data/,/end subroutine read_data/ { print }" | sed 's/subroutine read_data/subroutine read_data_otherendian/' > read_data_gadget_otherendian.f90 $(FC) $(FFLAGS) $(ENDIANFLAGBIG) -c read_data_gadget_otherendian.f90 -o read_data_gadget_otherendian.o gadget_jsb: checksystem $(OBJECTS) read_data_gadget_jsb.o $(FC) $(FFLAGS) -o $(BINDIR)/gsplash-jsb $(OBJECTS) read_data_gadget_jsb.o $(LDFLAGS) #---falcON HDF5 read --- falcon: falcON falcON: falcON_hdf5 falcON_hdf5: checksystem checkhdf5 $(OBJECTS) read_data_falcON_hdf5_utils.o read_data_falcON_hdf5.o $(FC) $(FFLAGS) -o $(BINDIR)/fsplash $(OBJECTS) read_data_falcON_hdf5_utils.o read_data_falcON_hdf5.o $(LDFLAGS) $(CXXLIBS) $(HDF5LIBS) -lhdf5_cpp read_data_falcON_hdf5_utils.o: read_data_falcON_hdf5_utils.cc $(CXX) $(CXXFLAGS) -std=c++11 $(HDF5INCLUDE) -c $< -o $@ #---HDF5 gadget read --- gadgethdf5: gadget_hdf5 gadget_hdf5: checksystem checkhdf5 $(OBJECTS) read_data_gadget_hdf5_utils.o read_data_gadget_hdf5.o $(FC) $(FFLAGS) -o $(BINDIR)/gsplash-hdf5 $(OBJECTS) read_data_gadget_hdf5_utils.o read_data_gadget_hdf5.o $(LDFLAGS) $(HDF5LIBS) read_data_gadget_hdf5_utils.o: read_data_gadget_hdf5_utils.c $(CC) $(CFLAGS) $(HDF5INCLUDE) -c $< -o $@ #---AMUSE HDF5 read --- amuse: amuse_hdf5 amuse_hdf5: checksystem checkhdf5 $(OBJECTS) read_data_amuse_hdf5_utils.o read_data_amuse_hdf5.o $(FC) $(FFLAGS) -o $(BINDIR)/amsplash-hdf5 $(OBJECTS) read_data_amuse_hdf5_utils.o read_data_amuse_hdf5.o $(LDFLAGS) $(HDF5LIBS) read_data_amuse_hdf5_utils.o: read_data_amuse_hdf5_utils.c $(CC) $(CFLAGS) $(HDF5INCLUDE) -c $< -o $@ #---CACTUS HDF5 read --- cactus: cactus_hdf5 cactus_hdf5: checksystem checkhdf5 $(OBJECTS) read_data_cactus_hdf5_futils.o read_data_cactus_hdf5_utils.o read_data_cactus_hdf5.o $(FC) $(FFLAGS) -o $(BINDIR)/csplash-hdf5 $(OBJECTS) read_data_cactus_hdf5_futils.o read_data_cactus_hdf5_utils.o read_data_cactus_hdf5.o $(LDFLAGS) $(HDF5LIBS) read_data_cactus_hdf5_utils.o: read_data_cactus_hdf5_utils.c $(CC) $(CFLAGS) $(HDF5INCLUDE) -c $< -o $@ #---PBOB read --- OBJPBOB=read_data_pbob_utils.o read_data_pbob.o read_pbob.o read_particle.o pbob: checksystem checkpbob $(OBJECTS) $(OBJPBOB) $(FC) $(FFLAGS) -I$(PBOB_DIR)/include -o $(BINDIR)/psplash $(OBJECTS) $(OBJPBOB) $(LDFLAGS) read_data_pbob_utils.o: read_data_pbob_utils.c $(CC) $(CFLAGS) -D_FILE_OFFSET_BITS=64 -I$(PBOB_DIR)/include -c $< -o $@ read_pbob.o: read_pbob.c $(CC) $(CFLAGS) -D_FILE_OFFSET_BITS=64 -I$(PBOB_DIR)/include -c $< -o $@ read_particle.o: read_particle.c $(CC) $(CFLAGS) -D_FILE_OFFSET_BITS=64 -I$(PBOB_DIR)/include -c $< -o $@ ifdef PBOB_DIR VPATH+=$(PBOB_DIR) endif #---SILO read --- silo: checksystem checksilo $(OBJECTS) read_data_silo_utils.o read_data_silo.o $(FC) $(FFLAGS) -o $(BINDIR)/silosplash $(OBJECTS) read_data_silo_utils.o read_data_silo.o $(LDFLAGS) $(HDF5LIBS) -lsiloh5 read_data_silo_utils.o: read_data_silo_utils.c $(CC) $(CFLAGS) $(HDF5INCLUDE) -c $< -o $@ #------------------ bauswein: checksystem $(OBJECTS) read_data_bauswein.o $(FC) $(FFLAGS) -o $(BINDIR)/bsplash $(OBJECTS) read_data_bauswein.o $(LDFLAGS) dragon: checksystem $(OBJECTS) read_data_dragon.o $(FC) $(FFLAGS) -o $(BINDIR)/dsplash $(OBJECTS) read_data_dragon.o $(LDFLAGS) seren: checksystem $(OBJECTS) read_data_seren.o $(FC) $(FFLAGS) -o $(BINDIR)/srsplash $(OBJECTS) read_data_seren.o $(LDFLAGS) vine: checksystem $(OBJECTS) read_data_VINE.o $(FC) $(FFLAGS) -o $(BINDIR)/vsplash $(OBJECTS) read_data_VINE.o $(LDFLAGS) kitp: checksystem $(OBJECTS) read_data_kitp.o $(FC) $(FFLAGS) $(LDFLAGS) -o $(BINDIR)/ksplash $(OBJECTS) read_data_kitp.o ndspmhd: checksystem $(OBJECTS) read_data_ndspmhd.o $(FC) $(FFLAGS) -o $(BINDIR)/nsplash $(OBJECTS) read_data_ndspmhd.o $(LDFLAGS) dansph: checksystem $(OBJECTS) read_data_dansph_old.o $(FC) $(FFLAGS) -o $(BINDIR)/dsplash $(OBJECTS) read_data_dansph_old.o $(LDFLAGS) foulkes: checksystem $(OBJECTS) read_data_foulkes.o $(FC) $(FFLAGS) -o $(BINDIR)/fsplash $(OBJECTS) read_data_foulkes.o $(LDFLAGS) flash_hdf5: checksystem checkhdf5 $(OBJECTS) read_data_flash_hdf5_utils.o read_data_flash_hdf5.o $(FC) $(FFLAGS) -o $(BINDIR)/fsplash $(OBJECTS) read_data_flash_hdf5_utils.o read_data_flash_hdf5.o $(LDFLAGS) flashhdf5: $(MAKE) flash_hdf5 HDF5=yes read_data_flash_hdf5_utils.o: read_data_flash_hdf5_utils.c $(CC) -c $(CFLAGS) $(HDF5INCLUDE) $(H5PART_INCLUDE) $< -o $@ flash: flashhdf5 jjm: checksystem $(OBJECTS) read_data_jjm.o $(FC) $(FFLAGS) -o $(BINDIR)/jsplash $(OBJECTS) read_data_jjm.o $(LDFLAGS) jules: checksystem $(OBJECTS) read_data_jules.o $(FC) $(FFLAGS) -o $(BINDIR)/jsplash $(OBJECTS) read_data_jules.o $(LDFLAGS) oilonwater: checksystem $(OBJECTS) read_data_oilonwater.o $(FC) $(FFLAGS) -o $(BINDIR)/osplash $(OBJECTS) read_data_oilonwater.o $(LDFLAGS) mhutch: checksystem $(OBJECTS) read_data_mhutch.o $(FC) $(FFLAGS) -o $(BINDIR)/msplash $(OBJECTS) read_data_mhutch.o $(LDFLAGS) RSPH: rsph rsph: checksystem $(OBJECTS) read_data_rsph.o $(FC) $(FFLAGS) -o $(BINDIR)/rsplash $(OBJECTS) read_data_rsph.o $(LDFLAGS) scwsph: checksystem $(OBJECTS) read_data_scw.o $(FC) $(FFLAGS) -o $(BINDIR)/wsplash $(OBJECTS) read_data_scw.o $(LDFLAGS) snsplash: snsph snsph: $(OBJECTS) read_data_snsph.o read_data_snsph_utils.o $(FC) $(FFLAGS) -o $(BINDIR)/snsplash $(OBJECTS) read_data_snsph.o read_data_snsph_utils.o $(LDFLAGS) $(SNFLAGS) srosph: checksystem $(OBJECTS) read_data_sro.o $(FC) $(FFLAGS) -o $(BINDIR)/rsplash $(OBJECTS) read_data_sro.o $(LDFLAGS) spyros: checksystem $(OBJECTS) read_data_spyros.o $(FC) $(FFLAGS) -o $(BINDIR)/ssplash $(OBJECTS) read_data_spyros.o $(LDFLAGS) sphNG: checksystem $(OBJECTS) read_data_sphNG.o read_data_sphNG_otherendian.o $(FC) $(FFLAGS) -o $(BINDIR)/ssplash$(EXT) $(OBJECTS) read_data_sphNG.o read_data_sphNG_otherendian.o $(LDFLAGS) #--build universal binary on mac cluster ifeq ($(SYSTEM), maccluster) lipo -create $(BINDIR)/ssplash_ppc $(BINDIR)/ssplash_i386 -output $(BINDIR)/ssplash || cp $(BINDIR)/ssplash$(EXT) $(BINDIR)/ssplash endif sphysics: checksystem $(OBJECTS) read_data_sphysics.o $(FC) $(FFLAGS) -o $(BINDIR)/dsplash $(OBJECTS) read_data_sphysics.o $(LDFLAGS) read_data_sphNG_otherendian.o: read_data_sphNG.o @echo "!--This file is automatically generated during the make: do not edit" > $(SRCDIR)/${@:.o=.f90} cat $(SRCDIR)/read_data_sphNG.f90 | awk "/subroutine read_data/,/end subroutine read_data/ { print }" | sed 's/subroutine read_data/subroutine read_data_otherendian/' >> $(SRCDIR)/${@:.o=.f90} $(FC) $(FFLAGS) $(ENDIANFLAGBIG) -c $(SRCDIR)/${@:.o=.f90} -o $@ jjmmulti: checksystem $(OBJECTS) read_data_multiphase.o $(FC) $(FFLAGS) -o $(BINDIR)/jsplash $(OBJECTS) read_data_jjm_multiphase.o $(LDFLAGS) tipsy: checksystem $(OBJECTS) read_data_tipsy.o $(FC) $(FFLAGS) -o $(BINDIR)/tsplash $(OBJECTS) read_data_tipsy.o $(LDFLAGS) vanaverbeke: checksystem $(OBJECTS) read_data_vanaverbeke.o $(FC) $(FFLAGS) -o $(BINDIR)/vsplash $(OBJECTS) read_data_vanaverbeke.o $(LDFLAGS) ucla: checksystem $(OBJECTS) read_data_UCLA.o $(FC) $(FFLAGS) -o $(BINDIR)/usplash $(OBJECTS) read_data_UCLA.o $(LDFLAGS) urban: checksystem $(OBJECTS) read_data_urban.o $(FC) $(FFLAGS) -o $(BINDIR)/usplash $(OBJECTS) read_data_urban.o $(LDFLAGS) aly: checksystem $(OBJECTS) read_data_aly.o $(FC) $(FFLAGS) -o $(BINDIR)/splash-aly $(OBJECTS) read_data_aly.o $(LDFLAGS) sky: ucla steve: foulkes sigfried: vanaverbeke gasoline: tipsy myall: mhutch ndspmhd dansph sphNG srosph gadget mbatesph tipsy ascii SRCGRID2PDF= $(SYSTEMFILE) globaldata.f90 prompting.f90 transform.f90 \ asciiutils.f90 write_griddata.f90 pdfs.f90 grid2pdf.f90 OBJGRID2PDF= ${SRCGRID2PDF:.f90=.o} grid2pdf: checksystem $(OBJGRID2PDF) $(FC) $(FFLAGS) -o $(BINDIR)/$@ $(OBJGRID2PDF) checksystem: ifeq ($(KNOWN_SYSTEM), yes) @echo "" @echo "Compiling splash for $(SYSTEM) system..........." @echo "" ifeq ($(ENDIAN), BIG) @echo "Flags set for conversion to BIG endian" endif ifeq ($(ENDIAN), LITTLE) @echo "Flags set for conversion to LITTLE endian" endif ifeq ($(PARALLEL), yes) @echo "Compiling the PARALLEL code" else ifeq ($(OPENMP), yes) @echo "Compiling the PARALLEL code" else @echo "Compiling the SERIAL code" endif endif ifeq ($(FC),gfortran) ifeq ($(GFORTRAN_GE_4_4),true) @echo "compiling with gfortran v$(GFORTRAN_VERSION) (OK)" else ${error gfortran v$(GFORTRAN_VERSION) is too old to compile this version of splash: please upgrade your gfortran} endif endif else @echo "" @echo " Makefile for splash by Daniel Price " @echo " -- see INSTALL file for detailed instructions" @echo "" @echo " make: ERROR: value of SYSTEM=$(SYSTEM) not recognised..." @echo " => set the environment variable SYSTEM to one listed " @echo " in build/Makefile and try again" @echo "" @${MAKE} compilers @$(MAKE) err; endif compilers: @echo "I suggest one of the following, based on detected Fortran compilers..."; echo; @if type -p ifort > /dev/null; then echo "make SYSTEM=ifort"; fi; @if type -p pathf90 > /dev/null; then echo "make SYSTEM=pathf90"; fi; @if type -p pgf90 > /dev/null; then echo "make SYSTEM=pgf90"; fi; @if type -p xlf90_r > /dev/null; then echo "make SYSTEM=ukaff1a [uses xlf90_r]"; fi; @if type -p gfortran > /dev/null; then echo "make SYSTEM=gfortran"; fi; @if type -p g95 > /dev/null; then echo "make SYSTEM=g95"; fi; @echo "(end of possible selections)"; echo; checkpgplot: ifeq (X${PGPLOT_DIR}, X) @echo; echo "ERROR: PGPLOT_DIR should be set before compiling splash"; echo; ${MAKE} err; else @if [ -d $$PGPLOT_DIR ]; then echo; echo "PGPLOT_DIR=$$PGPLOT_DIR"; echo; else echo; echo "ERROR: Directory given by PGPLOT_DIR=$$PGPLOT_DIR does not exist"; echo; ${MAKE} err; fi; endif ifneq ($(PGPLOT_COMP),$(FC)) @echo; echo "*** WARNING: PGPLOT appears to have been compiled with a different Fortran"; echo " compiler (${PGPLOT_COMP}) to the one you are using to compile SPLASH (${FC}),"; echo " so may need to link to the relevant compiler libraries ***"; # g77-compiled PGPLOT @if [ "${PGPLOT_COMP}" = "g77" ]; then echo " [Adding -lg2c to the link flags for g77-compiled PGPLOT]"; fi; # gfortran-compiled PGPLOT @if [ "${PGPLOT_COMP}" = "gfortran" ]; then echo " [Adding -lgfortran to the link flags for gfortran-compiled PGPLOT]"; fi; # g95-compiled PGPLOT @if [ "${PGPLOT_COMP}" = "g95" ]; then echo " [Adding -lg95 to the link flags for g95-compiled PGPLOT]"; fi; @echo # else # @echo "PGPLOT was compiled with ${PGPLOT_COMP}"; endif checkh5part: checkhdf5 ifeq (X${H5PART_DIR}, X) @echo; echo "ERROR: H5PART_DIR should be set before compiling splash with h5part read/write"; echo; ${MAKE} err; else @if [ -d $$H5PART_DIR ]; then echo; echo "H5PART_DIR=$$H5PART_DIR"; echo; else echo; echo "ERROR: Directory given by H5PART_DIR=$$H5PART_DIR does not exist"; echo; ${MAKE} err; fi; endif checkhdf5: ifeq (X${HDF5ROOT}, X) @echo; echo "ERROR: HDF5ROOT should be set before compiling splash with HDF5 utilities"; echo; ${MAKE} err; else @if [ -d $$HDF5ROOT ]; then echo; echo "HDF5ROOT=$$HDF5ROOT"; echo; else echo; echo "ERROR: Directory given by HDF5ROOT=$$HDF5ROOT does not exist"; echo; ${MAKE} err; fi; endif checksilo: checkhdf5 ifeq (X${SILO_DIR}, X) @echo; echo "ERROR: SILO_DIR should be set before compiling splash with SILO reader"; echo; ${MAKE} err; else @if [ -d $$SILO_DIR ]; then echo; echo "SILO_DIR=$$SILO_DIR"; echo; else echo; echo "ERROR: Directory given by SILO_DIR=$$SILO_DIR does not exist"; echo; ${MAKE} err; fi; endif checkpbob: ifeq (X${PBOB_DIR}, X) @echo; echo "ERROR: PBOB_DIR should be set before compiling splash with PBOB reader"; echo; ${MAKE} err; else @if [ -d $$PBOB_DIR ]; then echo; echo "PBOB_DIR=$$PBOB_DIR"; echo; else echo; echo "ERROR: Directory given by PBOB_DIR=$$PBOB_DIR does not exist"; echo; ${MAKE} err; fi; endif # # install option, copies any binaries compiled to /usr/local/bin/ # run `make' first, then `make install'. Could in principle # have install compile it as well, but environment variables # will not be defined if "make" is run using sudo, so better # to do the two separately # install: destdircheck installcheck @cd $(BINDIR); for x in *splash*; do echo "copying $$x -> $(DESTDIR)$(PREFIX)/bin/$$x"; cp $$x $(DESTDIR)$(PREFIX)/bin/; done; @echo; echo 'installation complete'; installcheck: @if test -e $(BINDIR)/asplash && test -e $(BINDIR)/gsplash; then echo; \ echo 'compiled binaries install to $(DESTDIR)$(PREFIX)/bin'; \ echo '(use "sudo make install" if Permission denied)'; \ echo; else echo;\ echo 'run "make" first, followed by "sudo make install"'; echo;\ $(MAKE) err; fi destdircheck: installcheck @if test -d $(DESTDIR)$(PREFIX)/bin; then echo $(DESTDIR)$(PREFIX)/bin exists and is a directory; else \ echo; echo "*** ERROR in make install ***"; echo "$(DESTDIR)$(PREFIX)/bin is not a valid directory"; echo;\ $(MAKE) err; fi; installclean: destdircheck @for x in $(DESTDIR)$(PREFIX)/bin/?splash; do rm $$x; done; distclean: installclean cleanall: clean installclean err: $(error aborting); ## other stuff plotlib_pgplot.o: checkpgplot plotlib_giza.o : giza-fortran.o giza-fortran.o : giza-fortran.F90 $(GIZA_DIR)/lib/libgiza.a $(FC) $(FFLAGS) -I$(GIZA_DIR)/include/ -c $< -o $@ .PHONY: gizabuild libgiza giza: @echo "Compiling local copy of giza..." cd ../giza; ./configure --prefix=$(PWD)/../giza CC=$(CC) CAIRO_LIBS="$(CAIRO_LIBS)" CAIRO_CFLAGS="$(CAIRO_CFLAGS)" X11_LIBS="$(X11_LIBS)" X11_CFLAGS="$(X11_CFLAGS)" cd ../giza; $(MAKE) $(MAKECMDFLAGS) install CC="$(CC)" CFLAGS="$(CFLAGS)" docs: doc doc: cd ../docs; pdflatex splash; pdflatex splash htmldocs: htmldoc htmldoc: doc cleanhtmldocs cd ../docs; \ hevea png.hva splash.tex -o html/splash.html; \ hevea png.hva splash.tex -o html/splash.html; \ bibhva html/splash; \ sed 's/et~al./et al./g' html/splash.hbbl > html/tmp.hbbl; \ mv html/tmp.hbbl html/splash.hbbl; \ hevea png.hva splash.tex -o html/splash.html; \ hevea png.hva splash.tex -o html/splash.html; \ imagen -png -pdf -mag 2000 html/splash; \ cd html; hacha -tocbis splash.html; \ rm *.gif splash.h{tml,aux,toc,bbl} splash.blg splash.image.tex ../splash.image.out; cleanhtmldocs: rm -f ../docs/html/*.h{tml,toc,bbl,aux} \ ../docs/html/*.image* ../docs/html/*.gif \ ../docs/html/splash*.{png,css,blg}; rm -f ../docs/splash.image.out ../docs/splash.out tar: tar cf splash.tar Makefile $(SOURCES) read_data*.f90 targz: tar cf splash.tar Makefile $(SOURCES) read_data*.f90 gzip splash.tar ## unit tests of various modules as I write them .PHONY: tests test: test1 test2 test3 test_slicer test_prompt test-parse test1: interpolate3D_projection.o interpolate3D_xsec.o test_interpolate3D.o $(FC) $(FFLAGS) $(LDFLAGS) -o $(BINDIR)/test_interpolation3D $(TESTDIR)/test_interpolate3D.o interpolate3D_projection.o interpolate3D_xsec.o test2: transform.o $(TESTDIR)//test_transform.o $(FC) $(FFLAGS) $(LDFLAGS) -o $(BINDIR)/test_transform $(TESTDIR)/test_transform.o transform.o test3: fieldlines.o $(TESTDIR)//test_fieldlines.o $(FC) $(FFLAGS) $(LDFLAGS) -o $(BINDIR)/test_fieldlines $(TESTDIR)/test_fieldlines.o fieldlines.o test_slicer: interpolate3D_projection.o interpolate3D_xsec.o test_slicer3D.o $(FC) $(FFLAGS) $(LDFLAGS) -o $(BINDIR)/test_slicer3D $(TESTDIR)/test_slicer3D.o interpolate3D_projection.o interpolate3D_xsec.o test_prompt: prompting.o test_prompting.o $(FC) $(FFLAGS) $(LDFLAGS) -o $(BINDIR)/test_prompt prompting.o test_prompting.o test-parse: $(PLOTLIB) asciiutils.o fparser.o parsetext.o $(TESTDIR)/test-parsetext.o $(FC) $(FFLAGS) -o $(BINDIR)/test-parse giza-fortran.o plotlib_giza.o fparser.o asciiutils.o parsetext.o $(TESTDIR)/test-parsetext.o $(LDFLAGS) # #--code dependencies: MAY BE INCOMPLETE - I generate this automatically every so often # include .depends clean: rm -f *.o *.mod $(BINDIR)/?splash $(BINDIR)/splash #cleangiza: #ifneq ($(BACKEND),pgplot) # ${MAKE} -C $(GIZA_DIR) clean #endif splash/build/.depends000644 000766 000000 00000016430 13261626263 015512 0ustar00dpricewheel000000 000000 globaldata.o multiplot.mod settings_data.mod labels.mod filenames.mod particle_data.mod params.mod: globaldata.f90 asciiutils.o asciiutils.mod: asciiutils.f90 setpage.o pagesetup.mod: setpage.f90 transform.o transforms.mod: transform.f90 prompting.o prompting.mod: prompting.f90 geometry.o geometry.mod: geometry.f90 plotutils.o plotutils.mod: plotutils.f90 colourbar.o colourbar.mod: pagesetup.mod colourbar.f90 colours.o colours.mod: colours.f90 colourparts.o colourparts.mod: colourparts.f90 shapes.o shapes.mod: transforms.mod prompting.mod params.mod shapes.f90 units.o settings_units.mod: settings_data.mod labels.mod prompting.mod params.mod units.f90 write_pixmap.o write_pixmap.mod: colours.mod filenames.mod write_pixmap.f90 write_sphdata.o write_sphdata.mod: params.mod settings_units.mod labels.mod write_sphdata.f90 analysis.o analysis.mod: prompting.mod params.mod filenames.mod asciiutils.mod labels.mod analysis.f90 discplot.o disc.mod: transforms.mod discplot.f90 exact_fromfile.o exactfromfile.mod: exact_fromfile.f90 exact_mhdshock.o mhdshock.mod: exact_mhdshock.f90 exact_polytrope.o polytrope.mod: exact_polytrope.f90 exact_rhoh.o rhoh.mod: exact_rhoh.f90 exact_sedov.o sedov.mod: exact_sedov.f90 exact_shock.o shock.mod: exact_shock.f90 exact_shock_sr.o shock_sr.mod: exact_shock_sr.f90 exact_wave.o wave.mod: exact_wave.f90 exact_toystar1D.o toystar1d.mod: exact_toystar1D.f90 exact_toystar2D.o toystar2d.mod: exact_toystar2D.f90 exact_densityprofiles.o densityprofiles.mod: exact_densityprofiles.f90 exact_torus.o torus.mod: exact_torus.f90 exact_ringspread.o ringspread.mod: exact_ringspread.f90 exact.o exact.mod: transforms.mod ringspread.mod densityprofiles.mod wave.mod toystar2d.mod toystar1d.mod torus.mod shock_sr.mod shock.mod sedov.mod rhoh.mod polytrope.mod mhdshock.mod exactfromfile.mod labels.mod filenames.mod prompting.mod settings_data.mod exact.f90 limits.o limits.mod: asciiutils.mod settings_data.mod particle_data.mod geometry.mod labels.mod params.mod limits.f90 allocate.o mem_allocation.mod: particle_data.mod allocate.f90 titles.o titles.mod: filenames.mod asciiutils.mod titles.f90 system_f2003.o system_commands.mod: system_f2003.f90 system_utils.o system_utils.mod: system_commands.mod system_utils.f90 options_render.o settings_render.mod: params.mod prompting.mod colours.mod labels.mod colourbar.mod options_render.f90 options_particleplots.o settings_part.mod: multiplot.mod geometry.mod prompting.mod particle_data.mod settings_render.mod limits.mod labels.mod exact.mod settings_data.mod params.mod options_particleplots.f90 calc_quantities.o calcquantities.mod: settings_units.mod mem_allocation.mod settings_part.mod settings_data.mod particle_data.mod labels.mod calc_quantities.f90 get_data.o getdata.mod: system_utils.mod asciiutils.mod settings_units.mod calcquantities.mod geometry.mod labels.mod prompting.mod particle_data.mod settings_part.mod settings_data.mod limits.mod filenames.mod exact.mod get_data.f90 convert.o convert.mod: prompting.mod getdata.mod analysis.mod write_sphdata.mod filenames.mod settings_data.mod particle_data.mod convert.f90 options_data.o options_data.mod: settings_units.mod labels.mod limits.mod calcquantities.mod getdata.mod prompting.mod filenames.mod params.mod settings_data.mod options_data.f90 options_limits.o settings_limits.mod: transforms.mod labels.mod limits.mod prompting.mod calcquantities.mod settings_data.mod filenames.mod multiplot.mod options_limits.f90 options_page.o settings_page.mod: filenames.mod prompting.mod params.mod shapes.mod settings_limits.mod options_page.f90 options_powerspec.o settings_powerspec.mod: prompting.mod labels.mod limits.mod settings_data.mod options_powerspec.f90 options_vecplot.o settings_vecplot.mod: limits.mod labels.mod settings_data.mod prompting.mod options_vecplot.f90 options_xsecrotate.o settings_xsecrot.mod: transforms.mod multiplot.mod calcquantities.mod settings_data.mod prompting.mod limits.mod labels.mod filenames.mod options_xsecrotate.f90 pdfs.o pdfs.mod: asciiutils.mod filenames.mod plotutils.mod transforms.mod prompting.mod pdfs.f90 rotate.o rotation.mod: rotate.f90 interpolate1D.o interpolations1d.mod: interpolate1D.f90 interpolate2D.o interpolations2d.mod: interpolate2D.f90 interpolate3D.o interpolations3d.mod: interpolate3D.F90 interpolate3D_xsec.o xsections3d.mod: interpolate3D_xsec.f90 interpolate3D_projection.o projections3d.mod: interpolate3D_projection.F90 interpolate3D_opacity.o interpolate3d_opacity.mod: colours.mod projections3d.mod interpolate3D_opacity.f90 interpolate_vec.o interpolate_vec.mod: interpolate_vec.f90 interactive.o interactive_routines.mod: colours.mod settings_part.mod labels.mod calcquantities.mod filenames.mod transforms.mod settings_limits.mod settings_data.mod limits.mod settings_vecplot.mod settings_page.mod multiplot.mod shapes.mod settings_xsecrot.mod colourbar.mod interactive.f90 fieldlines.o fieldlines.mod: fieldlines.f90 legends.o legends.mod: legends.f90 particleplot.o particleplots.mod: geometry.mod interpolations2d.mod settings_part.mod settings_data.mod labels.mod params.mod particleplot.f90 powerspectrums.o powerspectrums.mod: interpolations3d.mod powerspectrums.f90 render.o render.mod: settings_vecplot.mod legends.mod plotutils.mod colourbar.mod render.f90 plotstep.o timestep_plotting.mod: asciiutils.mod rotation.mod fieldlines.mod interpolate_vec.mod geometry.mod shapes.mod legends.mod colourbar.mod write_pixmap.mod exactfromfile.mod disc.mod pagesetup.mod render.mod xsections3d.mod interpolate3d_opacity.mod interpolations3d.mod interpolations2d.mod interpolations1d.mod powerspectrums.mod particleplots.mod interactive_routines.mod transforms.mod colourparts.mod settings_units.mod settings_vecplot.mod settings_limits.mod toystar2d.mod toystar1d.mod exact.mod filenames.mod pdfs.mod projections3d.mod particle_data.mod settings_powerspec.mod settings_xsecrot.mod settings_render.mod settings_part.mod settings_page.mod settings_data.mod titles.mod prompting.mod multiplot.mod limits.mod labels.mod colours.mod params.mod plotstep.f90 timestepping.o timestepping.mod: params.mod settings_part.mod getdata.mod timestep_plotting.mod settings_page.mod settings_data.mod particle_data.mod filenames.mod timestepping.f90 defaults.o defaults.mod: shapes.mod titles.mod settings_units.mod settings_powerspec.mod settings_xsecrot.mod settings_vecplot.mod settings_render.mod settings_part.mod options_data.mod settings_limits.mod multiplot.mod exact.mod settings_page.mod settings_data.mod particle_data.mod limits.mod labels.mod filenames.mod defaults.f90 menu.o mainmenu.mod: settings_units.mod params.mod timestepping.mod getdata.mod geometry.mod defaults.mod transforms.mod prompting.mod multiplot.mod settings_xsecrot.mod settings_vecplot.mod settings_render.mod settings_page.mod settings_part.mod settings_limits.mod settings_data.mod options_data.mod limits.mod labels.mod filenames.mod menu.f90 splash.o: settings_page.mod timestepping.mod analysis.mod write_sphdata.mod convert.mod write_pixmap.mod asciiutils.mod system_utils.mod system_commands.mod settings_xsecrot.mod settings_data.mod projections3d.mod mem_allocation.mod mainmenu.mod limits.mod defaults.mod getdata.mod filenames.mod splash.f90 read_data_sphNG.o sphngread.mod: settings_units.mod geometry.mod calcquantities.mod labels.mod system_utils.mod mem_allocation.mod settings_data.mod particle_data.mod params.mod read_data_sphNG.f90 splash/scripts/movie.sh000755 000766 000000 00000001036 13261626263 016131 0ustar00dpricewheel000000 000000 #!/bin/bash # # script to make an mpeg4 movie from png files # by default, takes splash_*.png as inputs and movie.mp4 as output # requires ffmpeg utility # # DJP, Feb 2014. edited by KAH, Aug 2017 # opts='-r 10 -vb 50M -bt 100M -vcodec mpeg4 -vf setpts=4.*PTS' if [ $# -le 0 ]; then ffmpeg -i splash_%04d.png $opts movie.mp4 fi if [ $# -eq 1 ]; then ffmpeg -i $1_%04d.png $opts movie.mp4 fi if [ $# -eq 2 ]; then ffmpeg -i $1_%04d.png $opts $2.mp4 fi if [ $# -ge 3 ]; then echo "usage: $0 infile_prefix outfile_prefix" exit fi splash/scripts/getav.pl000755 000766 000000 00000001500 13261626263 016115 0ustar00dpricewheel000000 000000 #!/usr/bin/perl # adds up the L2 errors from supersphplot output; calculates average use strict; use warnings; use List::Util qw(sum); my $file = ' '; my $avg = 0; if ($#ARGV < 0) { print "script which parses supersphplot output for L2 errors \n"; print "and calculates the average. Written by D. Price. \n\n"; die "Usage: $0 filename(s) \n"; } foreach $file (@ARGV) { open my $fh, '<', $file or die "Can't open $file: $!"; my @errors; while ( <$fh> ) { if ( my ($error) = m/L2 error\s+=\s+(\S*)\s+/ ) { #print "error = $error \n"; push @errors, $error; } } my $nerrors = scalar(@errors); if ($nerrors > 0) { $avg = sum(@errors) / $nerrors; } else { $avg = 0; } print "$file: Average of $nerrors errors: $avg\n"; } #print "$_\n" for @errors; splash/scripts/cpfiles.sh000755 000766 000000 00000001662 13261626263 016444 0ustar00dpricewheel000000 000000 #!/bin/bash # # short script to copy all splash # files to a new prefix # # ie. splash.defaults, splash.limits, splash.units etc. # become new.defaults, new.limits, new.units # # SPLASH can be invoked to use the new settings files # using the -p command line option # if [ $# -lt 1 ] || [ $# -gt 2 ]; then echo 'SPLASH files copy utility -- ' echo 'copies splash.defaults, splash.limits etc. ' echo ' to new.defaults, new.limits etc. (use with splash -p new)' echo echo "Usage $0 newprefix [oldprefix]"; echo echo '(default old prefix is "splash")'; exit; else new=$1; if [ $# -eq 2 ]; then old=$2; else old='splash'; fi for ext in defaults limits units titles anim legend columns filenames func spirals; do if [ -e $old.$ext ]; then cp $old.$ext $new.$ext; echo "$old.$ext -> $new.$ext"; else echo "$old.$ext does not exist"; fi done fi splash/scripts/splash_parallel.pl000755 000766 000000 00000017072 13261626263 020170 0ustar00dpricewheel000000 000000 #!/usr/bin/perl # # farms out jobs to a list of machines, finding available CPU # use strict; use warnings; use IO::Handle; #--------------------------------------------------------- # check args #--------------------------------------------------------- if ($#ARGV < 1 ) { die "Usage: $0 nfilesperplot file1 [file2 file3 ... filen] \n also with a file called input \n"; } #--------------------------------------------------------- # set farming method options are ssh, xgrid #--------------------------------------------------------- #my $farmusing='ssh'; my $farmusing='none'; #my $farmusing='xgrid'; my $delay=3; #--------------------------------------------------------- # set name and location of the supersphplot executable #--------------------------------------------------------- my $home=`cd; pwd -P`; chomp ($home); my $exe="$home/splash/bin/ssplash -readpix chf"; my $pwd = `pwd`; chomp($pwd); #--------------------------------------------------------- # set other default options #--------------------------------------------------------- my $inputfile='input'; my $pgplotfile='pgplot'; my $pgplotdev='none'; # doesn't matter my $pgplotdir= $ENV{'PGPLOT_DIR'}; # get this from the environment my $ldpath= $ENV{'LD_LIBRARY_PATH'}; # get this from the environment my $run; #--------------------------------------------------------- # get filenames and nfilesperplot from command line #--------------------------------------------------------- my ($nfilesperplot,@files) = @ARGV; #--------------------------------------------------------- # work out how many files should be sent as command line arguments to each run #--------------------------------------------------------- if ($nfilesperplot < 1) { die "error: nfilesperplot must be > 0\n" }; my $nfiles=$#files + 1; my $nruns=$nfiles/$nfilesperplot; print "nfiles = $nfiles; nruns = $nruns\n"; #--------------------------------------------------------- # get generic input from input file #--------------------------------------------------------- my @inputs = `cat $inputfile` or die "error: input file does not exist\n"; #open my $fh,'<',$inputfile or die "error: input file does not exist\n"; my $line; my $nline = 0; my $devline = 0; foreach $line (@inputs) { if ( $line =~ m|(/.+)\s| ) { $pgplotdev = $1; $devline = $nline; } $nline = $nline + 1; } #close $fh; #--------------------------------------------------------- # work out what device we are writing to and therefore # what the filenames should be #--------------------------------------------------------- print "PGPLOT device = $pgplotdev $inputs[$devline]\n"; if ( $pgplotdev eq '/xw' or $pgplotdev eq '/aqt' ) { die "must choose a non-interactive PGPLOT device\n"; } #--------------------------------------------------------- # work out the filename extension from the device name #--------------------------------------------------------- my ($ext) = $pgplotdev =~ m|/(.+)|; #print "extension = $ext \n"; #--------------------------------------------------------- # farm out the jobs #--------------------------------------------------------- my $filestart = 0; my $fileend = $filestart + $nfilesperplot; my $n = 0; my $tempdir = 'temp'; system "mkdir $tempdir" || die("error creating temporary directory $tempdir"); for ($run=1;$run<=$nruns;$run++) { my @inputstemp = @inputs; my $num = sprintf("%04d",$run); my $pgplotfile = "pgplot_$num.$ext"; #my $pgplotfile = "pgplot.$ext\_$run"; $inputstemp[$devline] = "$pgplotfile$pgplotdev\n"; print "------------ run $run : $pgplotfile ------------\n"; my @argsn=@files[$filestart..$fileend-1]; #---write the input file open(INPUTF,"> $tempdir/input$run") || die("can't write temporary files"); foreach $line (@inputstemp) { chomp($line); print INPUTF "$line \n"; } close(INPUTF); #---write the executable script--- my $outfile = "$tempdir/run$run.output"; open(RUNSCR,"> $tempdir/run$run.csh") || die("can't write run script"); print RUNSCR "#!/bin/tcsh \n"; print RUNSCR "setenv PGPLOT_DIR $pgplotdir \n"; # print RUNSCR "source $home/.cshrc \n"; # print RUNSCR "setenv LD_LIBRARY_PATH $ldpath \n"; print RUNSCR "setenv F_UFMTENDIAN 'big;little:168' \n"; print RUNSCR "setenv PGPLOT_FONT $home/pgplot/grfont.dat_big \n"; print RUNSCR "cd $pwd \n"; print RUNSCR "$exe @argsn < $tempdir/input$run >& $outfile \n"; close(RUNSCR); system "chmod a+x ./$tempdir/run$run.csh"; if ( -e "$outfile" ) { system "rm $outfile" }; my $commandline = "./$tempdir/run$run.csh"; if ( -s $pgplotfile ) { print "skipping $pgplotfile which already exists \n"; } else { if ($farmusing eq 'ssh') { farmjob_ssh( $commandline, $n ); sleep $delay; } elsif ($farmusing eq 'xgrid') { farmjob_xgrid( $commandline ); } elsif ($farmusing eq 'none') { print "no job farmed: using farming method \'none\' \n"; } else { die "unknown farming method \n"; } } #--------------------------------- $filestart = $fileend; $fileend = $filestart + $nfilesperplot; } exit; #--------------------------------------------------------- # subroutine to farm job via Apple's Xgrid #--------------------------------------------------------- sub farmjob_xgrid { my $commandline=shift; my $xgridauth = "-hostname cytosine.ex.ac.uk -auth Kerberos -gid 2"; my $jobid = `xgrid $xgridauth -job submit $commandline` || die "xgrid not found \n"; ($jobid) = $jobid =~ m/jobIdentifier\s+=\s+(\d+);/; # \s matches spaces (+ = at least one) \d decimals print "farmed via xgrid: job id = $jobid \n"; system "echo echo deleting... >> cleanxgrid; echo xgrid $xgridauth -job delete -id $jobid >> cleanxgrid"; system "echo echo getting results... >> getresultsxgrid; echo xgrid $xgridauth -job results -id $jobid >> getresultsxgrid"; sleep 1; # avoid multiple rapid-fire requests to xgrid server } #--------------------------------------------------------- # subroutine to farm jobs via simple ssh commands # to a list of machines #--------------------------------------------------------- sub farmjob_ssh { my ($commandline,$n)=@_; my (@machines) = `cat machinelist` or die "ERROR: for ssh version must list machines in file machinelist \n"; my $nmachines = $#machines; if ( $nmachines < 1 ) { die "ERROR: no machines specified in file machinelist \n"; } my $pwd = `pwd`; chomp($pwd); my $njobsrun = 0; my $loadavmax = 0.5; my $bs = '\\'; # loop through all available machines looking for spare CPU while ($njobsrun < 1) { #--set which machine to try if ($n > $nmachines) { print "waiting for machines to become free \n"; sleep 30; $n = 0; }; my $machine = $machines[$n]; chomp($machine); my $loadav = 0.0; print "trying $machine: "; #--get load average for this machine using uptime command if ($loadavmax > 0.0) { my $uptime=`ssh $machine uptime`; ($loadav) = $uptime =~ m/load ave.+:\s(\d+\.\d+)./; print " load average last 1 minute = $loadav "; } #--set job running if not busy if ( $loadav < $loadavmax ) { print " ...OK \n"; $njobsrun = $njobsrun + 1; # run the job print "running $commandline on machine $machine at nice +19\n"; system "ssh -f $machine 'cd $pwd; nice +19 $commandline < /dev/null >& /dev/null &'"; } else { print " ...too busy \n"; } $n = $n + 1; } $_[1] = $n; } splash/docs/version_history_tex.tex000644 000766 000000 00000030711 13261626263 020566 0ustar00dpricewheel000000 000000 2.8.0 & 06/04/18 & 360/4pi video mode added; automatically read labels from ascii file headers; nearest sensible unit (e.g. au or pc) used by default; cactus hdf5 data read; kernel-smoothed particle plots of arbitrary quantities; Viridis, Ocean and Inferno colour schemes; can customise line colours; Bondi flow exact solution; option for ticks but no labels; correct units in surface density plots; colour bar on top or left; support for multi-grain dust in Phantom; bug fix with NaNs in ascii files \\ 2.7.0 & 03/05/17 & Hollywood mode added (ctrl-m in interactive mode); better handling of dust/gas phantom data; added rotated cartesian geometry; rendering implemented in r-phi coordinates; added Fortran 2008 intrinsics to function parser; better rectangle plotting; better falcON data read; Ogilvie-Lubow exact solution for planet-disc interaction; tipsy read now works when splash compiled in double precision; splash to gridascii2 implemented; bugs with r-phi rendering fixed \\ 2.6.0 & 22/10/15 & SILO, falcON and .pbob data reads implemented; bug fixes in gadget-hdf5 reader; can recognise particle types in ascii read; more robust sphNG read; dust fraction recognised in Phantom data read; Toomre Q works in physical units; bug fix with disappearing units labels; bug fix in shock tube solution; added splash calc delta; splash to ascii keeps precision; better power spectra \\ 2.5.1 & 29/01/15 & error bar style options; support for 5K displays; can plot vectors and render with colours if h not read; range restrictions apply during splash to grid; improved line-style legend; now up to 6 line styles; fixes to amuse-hdf5 read; phantom read handles star/dm particles; various bugs fixed \\ 2.5.0 & 22/08/14 & instant multiplots by giving multiple columns as y axis; ability to plot multiple exact solution files on same plot; compiles in parallel by default; support for tagged sphNG/Phantom format; AMUSE hdf5 format reader added; various bug fixes \\ 2.4.1 & 01/04/14 & Roche-lobe plotting vastly improved; newunit= issue fixed; bug fix with reading sink velocities from Phantom; other minor bug fixes \\ 2.4.0 & 21/02/14 & time formatting in legend can include general functions; option to include sinks in opacity rendering; supports one-fluid dust visualisation; C-shock exact solution; better polytrope solution \\ 2.3.1 & 11/11/13 & SPLASH\_COROTATE option to plot in frame corotating with sinks; bug fixes with handling of dead/accreted/boundary particles in sphNG/phantom; various other bugs fixed \\ 2.3.0 & 09/08/13 & can customise time formatting in legend; improvements to legends; less verboseness; splash can read and plot pixel maps produced with -o ascii; 3D vector field plotting improved; bug fix with gfortran 4.8 \\ 2.2.2 & 10/05/13 & particle tracking by type implemented; can interpolate specific columns in splash to grid; SPLASH\_CENTRE\_ON\_SINK option generic to all data reads; Aly Reheam format added; option for 2nd y axis on plots; bug fix with X11 linking on Ubuntu; can read gadget ICs files \\ 2.2.1 & 21/02/13 & minor bug with axes plotting fixed; Wendland kernels added; bugs with exact solution plotting fixed; bug fix with tracking of dark matter particles \\ 2.2.0 & 16/11/12 & option to use different kernels for interpolation; floating/inset colour bars added; splash to gadget conversion implemented; splash to grid works in 2D; improved interfaces to shapes and animation sequences; automatically turns on dark matter particle plotting if no gas; interactive mode help displayed automatically \\ 2.1.1 & 31/08/12 & irregular/circular particle selection using shift-left click/middle click; improved h5part and GADGET HDF5 data reads; splash can be compiled in double precision; bug fixes with calculated quantities + change of coordinate systems; improved vector plot legend; option for box+numbers but no labels added \\ 2.1.0 & 16/05/12 & 3D vector field visualisation added; GADGET HDF5 read implemented; page sizes can be specified in pixels; limits can auto-adapt to device aspect ratio; more general exact solution from file option; tiling works with one colour bar per row; splash calc handles different particle types \\ 2.0-beta & 29/08/11 & new giza backend --- antialiased lines; real fonts; pdf, eps and svg drivers; fewer build dependencies (only cairo, X11); support for semi-transparent text; Double-rendering (with transparent background) implemented \\ 1.15.0 & 29/08/11 & Multiplot with different particle types implemented; calculated quantities list is now pre-filled automatically; preliminary support for r-phi and r-z rendering; outlined solid markers implemented; better handling of multiple types; manual contour levels can be specified in splash.contours; parallel splash to grid; better support for non-square pixels; clipping of numbers at edge of viewport fixed \\ 1.14.1 & 17/03/11 & SEREN data read added; dragon read updated; build follows Gnu conventions on DEST and DESTDIR (for macports build); can have up to 12 particle types; exact solutions re-ordered; dusty wave exact solution added \\ 1.14.0 & 06/12/10 & Can flip between rendered quantities in interactive mode using f,F; SPLASH\_DEFAULTS variable can be set for system-wide defaults; can plot arbitrary functions of x,t as exact solution; added data read for H5PART format; GADGET read across multiple files implemented; error bars can be plotted for x and y axis simultaneously; default rotation angles set if 3D perspective turned on; new directory layout and more helpful error messages during build; PGPLOT linking is easier to get right \\ 1.13.1 & 26/02/10 & bugs with new calc quantities module fixed; generic library interface implemented so backend can be changed easily; bug fix with auto pixel selection; simpler foreground/background colour setting; added subgrid interpolation warning \\ 1.13.0 & 25/02/10 & function parser incorporated; calculated quantities can now be specified at runtime; arbitrary function plotting implemented as an exact solution; command-line SPH->grid conversion ("splash to grid") implemented; ctrl-t in interactive mode adds arbitrary text box; better line style/colour changing; bug fix with tiling and y-axis labels; various other bug fixes \\ 1.12.2 & 15/07/09 & Variable marker sizes added, can plot particles as circles with size proportional to h; dark matter rendering with block-labelled GADGET format fixed; VINE read handles star particles; TIPSY read with ifort10.0.0 works; snsph read added; splash to phantom added; does not override labels for coords, vectors by default; bug fixes with contouring options; stability bug fixes with older compilers; more robust memory handling; bug fix with automatic pixel selection causing seg fault \\ 1.12.1 & 20/04/09 & Can edit/delete text shapes interactively, also the colour bar label; can customise the label on projection plots; contour levels better defined; SPLASH\_HMIN\_CODEUNITS added; option for numeric labelling of contours; contour limits can be set separately to render limits for same quantity; minor bug fixes \\ 1.12.0 & 22/12/08 & command-line plotting implemented; ln transform added; bug fixes in GADGET read; backspace over annotation (legends,titles,axes,colour bar) in interactive mode removes it; "splash calc" command line utility calculates time sequences of global quantities from a sequence of dump files; bug fix causing seg fault \\ 1.11.1 & 13/10/08 & automatic number of pixels and exact pixel boundaries implemented; mass does not have to be read from dump file; frame changes are per-page not per-dump file for animation sequences; lower stacksize footprint; bug fix with circles of interaction; bug fixes with block-labelled GADGET read; Steve Foulkes data read added \\ 1.11.0 & 15/08/08 & ability to use subset of particles in restricted parameter range(s); probability density function plot option; plot-hugging colour bars added; ability to annotate plot with a range of shapes; v, V, w and H implemented in interactive mode for >1 panel; various bug fixes \\ 1.10.2 & 08/05/08 & disc surface density / toomre q parameter plotting added; flash colour schemes added; splash to binary convert option; can change order in which particle types are plotted; splash.columns file overrides default column label settings; vanaverbeke format read; various bug fixes \\ 1.10.1 & 11/03/08 & "splash to" command line option converts binary dumps to ascii format; vector plots + rotation now implemented; block labelled GADGET format read; ring-spreading exact solution added; other minor changes \\ 1.10.0 & 29/11/07 & horizontal colour bars implemented; -p, -o command line options; can have mixed types in data reads; TIPSY and DRAGON data reads; density weighted rendering; normalisation option applies to column density plots; improved particle tracking; save as option; various bug fixes \\ 1.9.2 & 12/09/07 & improvements to ascii read including asplash -e option; smarter foreground/background colour changing for titles; min=max problem fixed (caught by splash not pgplot); fixed vector arrow length option; other minor changes and bug fixes \\ 1.9.1 & 11/07/07 & environment variables + improvements to gadget data read; better prompting; 3 new colour schemes; improved legend/title options; other minor changes \\ 1.9.0 & 21/05/07 & animation sequences implemented; origin settings now affect radius calculation and are relative to tracked particle; automatic line width choice for postscript devices; w key adapts vector arrows; vastly improved userguide \\ 1.8.1 & 28/03/07 & option to hide vector arrows where there are no particles added; smoother 3D plotting at low pixel numbers; (smoother vector plots); bug fixes with a); issues with round-off error with z integration of vectors fixed \\ 1.8.0 & 15/03/07 & hidden particles not used in rendering; units for z integration addded; a) and g) implemented in interactive mo de for multiple-plots-per-page; improved cross section using x in interactive mode \\ 1.7.2 & 19/02/07 & Menu shortcuts implemented; bug fix/ more sensible transformation of angular vector components in different co-ordinate systems; improvements to interactive zoom and origin recentreing; improved colour-by-type option; restrictions on page size removed; minor bug fixes \\ 1.7.1 & 04/01/07 & command line options for defaults and limits files added; minor bug fixes \\ 1.7.0 & 13/12/06 & renamed SPLASH instead of SUPERSPHPLOT; much faster data read for gadget and sphNG reads (only required columns read); physical units can be saved to file; new menu formats; various other bug fixes \\ 1.6.2 & 24/10/06 & fast particle plotting and streamline plotting implemented; more bug fixes with interactive mode on multiplots; various other bug fixes \\ 1.6.1 & 24/08/06 & bug fixes to 1.6.0; further improvements to interactive mode on multiplots \\ 1.6 & 11/08/06 & interactive mode on multiple plots per page; highly optimised interpolation + parallel version; new Makefile; various bug fixes \\ 1.5.4 & 06/07/06 & Handles multiple SPH/non-SPH particle types; axes redrawn after plotting; minor bug fixes \\ 1.5.3 & 03/07/06 & minor bug fixes/improvements to multiple plots per page, colour bar labelling, tiled plots and legend. Accelerated rendering option for projections \\ 1.5.2 & 11/05/06 & S) option for saving limits and defaults; MUCH faster interactive replotting (no unnecessary re-rendering); a few other minor things \\ 1.5.1 & 26/04/06 & docs updated for v1.5, other minor changes \\ 1.5 & 17/03/06 & 3D perspective added, 3D opacity rendering, improved rotation, colour schemes, adjustable vector arrows (+legend), improved timestepping behaviour, speed enhancements, physical unit rescaling \\ 1.0.5 & 28/09/05 & error calculation for exact solutions; legend for plot markers; exact(densityprofiles) added; more colour schemes; unit rescaling improved; other minor changes and bug fixes \\ 1.0.4 & 17/08/05 & better colour schemes; interactive colour scheme changing; various minor changes and bug fixes \\ 1.0.3 & 05/07/05 & rescale data option; better page setup; improved zooming; interactive particle tracking + various minor changes and bug fixes \\ 1.0.2 & 01/06/05 & much improved ascii data read; better line plotting; zoom on powerspectrum plots; calculate quantities switch + various bug fixes \\ 1.0.1 & 17/05/05 & better colour bar behaviour on multiplots; various minor improvements \\ 1.0 & 17/04/05 & first "official" release: version given to many people at IPAM meeting and put on web.\\ 0.667 & 12/04 & This version was released to a limited number of people who had specifically requested a copy. \\ 0.666 & 10/04 & This version was released to one or two people and had some bugs still buried. \\ splash/docs/figs/000755 000766 000000 00000000000 13261626263 014644 5ustar00dpricewheel000000 000000 splash/docs/html/000755 000766 000000 00000000000 13261626263 014660 5ustar00dpricewheel000000 000000 splash/docs/version_history000644 000766 000000 00000027107 13261626263 017114 0ustar00dpricewheel000000 000000 2.8.0: 360/4pi video mode added; automatically read labels from ascii file headers; nearest sensible unit (e.g. au or pc) used by default; cactus hdf5 data read; kernel-smoothed particle plots of arbitrary quantities; Viridis, Ocean and Inferno colour schemes; can customise line colours; Bondi flow exact solution; option for ticks but no labels; correct units in surface density plots; colour bar on top or left; support for multi-grain dust in Phantom; bug fix with NaNs in ascii files 2.7.0: Hollywood mode added (ctrl-m in interactive mode); better handling of dust/gas phantom data; added rotated cartesian geometry; rendering implemented in r-phi coordinates; added Fortran 2008 intrinsics to function parser; better rectangle plotting; better falcON data read; Ogilvie-Lubow exact solution for planet-disc interaction; tipsy read now works when splash compiled in double precision; splash to gridascii2 implemented; bugs with r-phi rendering fixed 2.6.0: SILO, falcON and .pbob data reads implemented; bug fixes in gadget-hdf5 reader; can recognise particle types in ascii read; more robust sphNG read; dust fraction recognised in Phantom data read; Toomre Q works in physical units; bug fix with disappearing units labels; bug fix in shock tube solution; added splash calc delta; splash to ascii keeps precision; better power spectra 2.5.1: error bar style options; support for 5K displays; can plot vectors and render with colours if h not read; range restrictions apply during splash to grid; improved line-style legend; now up to 6 line styles; fixes to amuse-hdf5 read; phantom read handles star/dm particles; various bugs fixed 2.5.0: instant multiplots by giving multiple columns as y axis; ability to plot multiple exact solution files on same plot; compiles in parallel by default; support for tagged sphNG/Phantom format; AMUSE hdf5 format reader added; various bug fixes 2.4.1: Roche-lobe plotting vastly improved; newunit= issue fixed; bug fix with reading sink velocities from Phantom; other minor bug fixes 2.4.0: time formatting in legend can include general functions; option to include sinks in opacity rendering; supports one-fluid dust visualisation; C-shock exact solution; better polytrope solution 2.3.1: SPLASH\_COROTATE option to plot in frame corotating with sinks; bug fixes with handling of dead/accreted/boundary particles in sphNG/phantom; various other bugs fixed 2.3.0: can customise time formatting in legend; improvements to legends; less verboseness; splash can read and plot pixel maps produced with -o ascii; 3D vector field plotting improved; bug fix with gfortran 4.8 2.2.2: particle tracking by type implemented; can interpolate specific columns in splash to grid; SPLASH\_CENTRE\_ON\_SINK option generic to all data reads; Aly Reheam format added; option for 2nd y axis on plots; bug fix with X11 linking on Ubuntu; can read gadget ICs files 2.2.1: minor bug with axes plotting fixed; Wendland kernels added; bugs with exact solution plotting fixed; bug fix with tracking of dark matter particles 2.2.0: option to use different kernels for interpolation; floating/inset colour bars added; splash to gadget conversion implemented; splash to grid works in 2D; improved interfaces to shapes and animation sequences; automatically turns on dark matter particle plotting if no gas; interactive mode help displayed automatically 2.1.1: irregular/circular particle selection using shift-left click/middle click; improved h5part and GADGET HDF5 data reads; splash can be compiled in double precision; bug fixes with calculated quantities + change of coordinate systems; improved vector plot legend; option for box+numbers but no labels added 2.1.0: 3D vector field visualisation added; GADGET HDF5 read implemented; page sizes can be specified in pixels; limits can auto-adapt to device aspect ratio; more general exact solution from file option; tiling works with one colour bar per row; splash calc handles different particle types 1.15.0: Multiplot with different particle types implemented; calculated quantities list is now pre-filled automatically; preliminary support for r-phi and r-z rendering; outlined solid markers implemented; better handling of multiple types; manual contour levels can be specified in splash.contours; parallel splash to grid; better support for non-square pixels; clipping of numbers at edge of viewport fixed 1.14.1: SEREN data read added; dragon read updated; build follows Gnu conventions on DEST and DESTDIR (for macports build); can have up to 12 particle types; exact solutions re-ordered; dusty wave exact solution added 1.14.0: Can flip between rendered quantities in interactive mode using f,F; SPLASH\_DEFAULTS variable can be set for system-wide defaults; can plot arbitrary functions of x,t as exact solution; added data read for H5PART format; GADGET read across multiple files implemented; error bars can be plotted for x and y axis simultaneously; default rotation angles set if 3D perspective turned on; new directory layout and more helpful error messages during build; PGPLOT linking is easier to get right 1.13.1: bugs with new calc quantities module fixed; generic library interface implemented so backend can be changed easily; bug fix with auto pixel selection; simpler foreground/background colour setting; added subgrid interpolation warning 1.13.0: function parser incorporated; calculated quantities can now be specified at runtime; arbitrary function plotting implemented as an exact solution; command-line SPH->grid conversion ("splash to grid") implemented; ctrl-t in interactive mode adds arbitrary text box; better line style/colour changing; bug fix with tiling and y-axis labels; various other bug fixes 1.12.2: Variable marker sizes added, can plot particles as circles with size proportional to h; dark matter rendering with block-labelled GADGET format fixed; VINE read handles star particles; TIPSY read with ifort10.0.0 works; snsph read added; splash to phantom added; does not override labels for coords, vectors by default; bug fixes with contouring options; stability bug fixes with older compilers; more robust memory handling; bug fix with automatic pixel selection causing seg fault 1.12.1: Can edit/delete text shapes interactively, also the colour bar label; can customise the label on projection plots; contour levels better defined; SPLASH\_HMIN\_CODEUNITS added; option for numeric labelling of contours; contour limits can be set separately to render limits for same quantity; minor bug fixes 1.12.0: command-line plotting implemented; ln transform added; bug fixes in GADGET read; backspace over annotation (legends,titles,axes,colour bar) in interactive mode removes it; "splash calc" command line utility calculates time sequences of global quantities from a sequence of dump files; bug fix causing seg fault 1.11.1: automatic number of pixels and exact pixel boundaries implemented; mass does not have to be read from dump file; frame changes are per-page not per-dump file for animation sequences; lower stacksize footprint; bug fix with circles of interaction; bug fixes with block-labelled GADGET read; Steve Foulkes data read added 1.11.0: ability to use subset of particles in restricted parameter range(s); probability density function plot option; plot-hugging colour bars added; ability to annotate plot with a range of shapes; v, V, w and H implemented in interactive mode for >1 panel; various bug fixes 1.10.2: disc surface density / toomre q parameter plotting added; flash colour schemes added; splash to binary convert option; can change order in which particle types are plotted; splash.columns file overrides default column label settings; vanaverbeke format read; various bug fixes 1.10.1: "splash to" command line option converts binary dumps to ascii format; vector plots + rotation now implemented; block labelled GADGET format read; ring-spreading exact solution added; other minor changes 1.10.0: horizontal colour bars implemented; -p, -o command line options; can have mixed types in data reads; TIPSY and DRAGON data reads; density weighted rendering; normalisation option applies to column density plots; improved particle tracking; save as option; various bug fixes 1.9.2: improvements to ascii read including asplash -e option; smarter foreground/background colour changing for titles; min=max problem fixed (caught by splash not pgplot); fixed vector arrow length option; other minor changes and bug fixes 1.9.1: environment variables + improvements to gadget data read; better prompting; 3 new colour schemes; improved legend/title options; other minor changes 1.9.0: animation sequences implemented; origin settings now affect radius calculation and are relative to tracked particle; automatic line width choice for postscript devices; w key adapts vector arrows; vastly improved userguide 1.8.1: option to hide vector arrows where there are no particles added; smoother 3D plotting at low pixel numbers; (smoother vector plots); bug fixes with a); issues with round-off error with z integration of vectors fixed 1.8.0: hidden particles not used in rendering; units for z integration addded; a) and g) implemented in interactive mo de for multiple-plots-per-page; improved cross section using x in interactive mode 1.7.2: Menu shortcuts implemented; bug fix/ more sensible transformation of angular vector components in different co-ordinate systems; improvements to interactive zoom and origin recentreing; improved colour-by-type option; restrictions on page size removed; minor bug fixes 1.7.1: command line options for defaults and limits files added; minor bug fixes 1.7.0: renamed SPLASH instead of SUPERSPHPLOT; much faster data read for gadget and sphNG reads (only required columns read); physical units can be saved to file; new menu formats; various other bug fixes 1.6.2: fast particle plotting and streamline plotting implemented; more bug fixes with interactive mode on multiplots; various other bug fixes 1.6.1: bug fixes to 1.6.0; further improvements to interactive mode on multiplots 1.6: interactive mode on multiple plots per page; highly optimised interpolation + parallel version; new Makefile; various bug fixes 1.5.4: Handles multiple SPH/non-SPH particle types; axes redrawn after plotting; minor bug fixes 1.5.3: minor bug fixes/improvements to multiple plots per page, colour bar labelling, tiled plots and legend. Accelerated rendering option for projections 1.5.2: S) option for saving limits and defaults; MUCH faster interactive replotting (no unnecessary re-rendering); a few other minor things 1.5.1: docs updated for v1.5, other minor changes 1.5: 3D perspective added, 3D opacity rendering, improved rotation, colour schemes, adjustable vector arrows (+legend), improved timestepping behaviour, speed enhancements, physical unit rescaling 1.0.5: error calculation for exact solutions; legend for plot markers; exact(densityprofiles) added; more colour schemes; unit rescaling improved; other minor changes and bug fixes 1.0.4: better colour schemes; interactive colour scheme changing; various minor changes and bug fixes 1.0.3: rescale data option; better page setup; improved zooming; interactive particle tracking + various minor changes and bug fixes 1.0.2: much improved ascii data read; better line plotting; zoom on powerspectrum plots; calculate quantities switch + various bug fixes 1.0.1: better colour bar behaviour on multiplots; various minor improvements 1.0 : first "official" release: version given to many people at IPAM meeting and put on web. 0.667 : This version was released to a limited number of people who had specifically requested a copy. 0.666 : This version was released to one or two people and had some bugs still buried. splash/docs/version000644 000766 000000 00000000006 13261626263 015320 0ustar00dpricewheel000000 000000 2.8.0 splash/docs/splash.bbl000644 000766 000000 00000004263 13261626263 015674 0ustar00dpricewheel000000 000000 \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{{Dai} and {Woodward}}{1994}]{dw94} {Dai}, W. and P.~R. {Woodward}: 1994, `{Extension of the Piecewise Parabolic Method to Multidimensional Ideal Magnetohydrodynamics}'. \newblock {\em J. Comp. Phys.} {\bf 115}, 485--514. \bibitem[\protect\citeauthoryear{{Monaghan} and {Price}}{2004}]{mp04} {Monaghan}, J.~J. and D.~J. {Price}: 2004, `{Toy stars in one dimension}'. \newblock {\em MNRAS} {\bf 350}, 1449--1456. \bibitem[\protect\citeauthoryear{{Press} et~al.}{1992}]{numericalrecipes} {Press}, W.~H., S.~A. {Teukolsky}, W.~T. {Vetterling}, and B.~P. {Flannery}: 1992, {\em {Numerical recipes in FORTRAN. The art of scientific computing}}. \newblock Cambridge: University Press, 1992, 2nd ed. \bibitem[\protect\citeauthoryear{{Price}}{2007}]{splashpaper} {Price}, D.~J.: 2007, `{SPLASH: An Interactive Visualisation Tool for Smoothed Particle Hydrodynamics Simulations}'. \newblock {\em Publ. Astron. Soc. Aust.} {\bf 24}, 159--173. \bibitem[\protect\citeauthoryear{{Price}}{2012}]{price12} {Price}, D.~J.: 2012, `{Smoothed Particle Hydrodynamics and Magnetohydrodynamics}'. \newblock {\em J. Comp. Phys.} {\bf 231}, 759--794. \bibitem[\protect\citeauthoryear{{Price} and {Bate}}{2007}]{pb07} {Price}, D.~J. and M.~R. {Bate}: 2007, `{The impact of magnetic fields on single and binary star formation}'. \newblock {\em MNRAS} {\bf 377}, 77--90. \bibitem[\protect\citeauthoryear{{Price} and {Monaghan}}{2007}]{pm07} {Price}, D.~J. and J.~J. {Monaghan}: 2007, `{An energy-conserving formalism for adaptive gravitational force softening in smoothed particle hydrodynamics and N-body codes}'. \newblock {\em MNRAS} {\bf 374}, 1347--1358. \bibitem[\protect\citeauthoryear{{Ryu} and {Jones}}{1995}]{rj95} {Ryu}, D. and T.~W. {Jones}: 1995, `{Numerical magetohydrodynamics in astrophysics: Algorithm and tests for one-dimensional flow`}'. \newblock {\em ApJ} {\bf 442}, 228--258. \bibitem[\protect\citeauthoryear{{Toro}}{1992}]{toro92} {Toro}, E.~F.: 1992, `{The Weighted Average Flux Method Applied to the Euler Equations}'. \newblock {\em Philosophical Transactions: Physical Sciences and Engineering} {\bf 341}, 499--530. \end{thebibliography} splash/docs/bibstyle.bst000755 000766 000000 00000050650 13261626263 016254 0ustar00dpricewheel000000 000000 % This style produces citations in the `author-year' format. % It supports two forms of citation: the \cite command produces: (Author, year) % in the text; the \cite* command only: (year) . ENTRY { address author booktitle chapter edition editor howpublished institution journal key note number organization pages publisher school series title type volume year } {} { label extra.label sort.label } INTEGERS { output.state before.all mid.sentence after.sentence after.block after.colon } FUNCTION {init.state.consts} { #0 'before.all := #1 'mid.sentence := #2 'after.sentence := #3 'after.block := #4 'after.colon := } STRINGS { s t } FUNCTION {output.nonnull} { 's := output.state mid.sentence = { ", " * write$ } { output.state after.block = { add.period$ write$ newline$ "\newblock " write$ } { output.state before.all = 'write$ { output.state after.colon = 'write$ { add.period$ " " * write$ } if$ } if$ } if$ mid.sentence 'output.state := } if$ s } FUNCTION {output.nonnull.extra} { 's := output.state mid.sentence = { " " * write$ } { output.state after.block = { add.period$ write$ newline$ "\newblock " write$ } { output.state before.all = 'write$ { output.state after.colon = 'write$ { add.period$ " " * write$ } if$ } if$ } if$ mid.sentence 'output.state := } if$ s } FUNCTION {output} { duplicate$ empty$ 'pop$ 'output.nonnull if$ } FUNCTION {output.extra} { duplicate$ empty$ 'pop$ 'output.nonnull.extra if$ } FUNCTION {output.check} { 't := duplicate$ empty$ { pop$ "empty " t * " in " * cite$ * warning$ } 'output.nonnull if$ } FUNCTION {output.check.extra} { 't := duplicate$ empty$ { pop$ "empty " t * " in " * cite$ * warning$ } 'output.nonnull.extra if$ } FUNCTION {output.year.check} { year empty$ { "empty year in " cite$ * warning$ } { write$ ": " year * extra.label * mid.sentence 'output.state := } if$ } FUNCTION {output.bibitem} { newline$ "\bibitem[" write$ label write$ "]{" write$ cite$ write$ "}" write$ newline$ "" before.all 'output.state := } FUNCTION {fin.entry} { add.period$ write$ newline$ } FUNCTION {new.block} { output.state before.all = 'skip$ { after.block 'output.state := } if$ } FUNCTION {new.sentence} { output.state after.block = 'skip$ { output.state before.all = 'skip$ { after.sentence 'output.state := } if$ } if$ } FUNCTION {not} { { #0 } { #1 } if$ } FUNCTION {and} { 'skip$ { pop$ #0 } if$ } FUNCTION {or} { { pop$ #1 } 'skip$ if$ } FUNCTION {new.block.checkb} { empty$ swap$ empty$ and 'skip$ 'new.block if$ } FUNCTION {field.or.null} { duplicate$ empty$ { pop$ "" } 'skip$ if$ } FUNCTION {boldface} { duplicate$ empty$ { pop$ "" } { "{\bf " swap$ * "}" * } if$ } FUNCTION {emphasize} { duplicate$ empty$ { pop$ "" } { "{\em " swap$ * "}" * } if$ } INTEGERS { nameptr namesleft numnames } FUNCTION {format.names} { 's := #1 'nameptr := s num.names$ 'numnames := numnames 'namesleft := { namesleft #0 > } { nameptr #1 > { s nameptr "{f. }{vv~}{ll}{, jj}" format.name$ 't := } { s nameptr "{vv~}{ll}{, jj}{, f.}" format.name$ 't := } if$ nameptr #1 > { namesleft #1 > { ", " * t * } { numnames #2 > { "," * } 'skip$ if$ t "others" = { " et~al." * } { " and " * t * } if$ } if$ } 't if$ nameptr #1 + 'nameptr := namesleft #1 - 'namesleft := } while$ } FUNCTION {format.ed.names} { 's := #1 'nameptr := s num.names$ 'numnames := numnames 'namesleft := { namesleft #0 > } { nameptr #1 > { s nameptr "{f. }{vv~}{ll}{, jj}" format.name$ 't := } { s nameptr "{f. }{vv~}{ll}{, jj}" format.name$ 't := } if$ nameptr #1 > { namesleft #1 > { ", " * t * } { numnames #2 > { "," * } 'skip$ if$ t "others" = { " et~al." * } { " and " * t * } if$ } if$ } 't if$ nameptr #1 + 'nameptr := namesleft #1 - 'namesleft := } while$ } FUNCTION {format.authors} { author empty$ { "" } { author format.names } if$ } FUNCTION {format.key} { empty$ { key field.or.null } { "" } if$ } FUNCTION {format.editors} { editor empty$ { "" } { editor format.names editor num.names$ #1 > { " (eds.)" * } { " (ed.)" * } if$ } if$ } FUNCTION {format.editors.extra} { editor empty$ { "" } { editor format.ed.names editor num.names$ #1 > { " (eds.)" * } { " (ed.)" * } if$ } if$ } FUNCTION {format.title} { title empty$ { "" } { "`" title "'" * * } if$ } FUNCTION {n.dashify} { 't := "" { t empty$ not } { t #1 #1 substring$ "-" = { t #1 #2 substring$ "--" = not { "--" * t #2 global.max$ substring$ 't := } { { t #1 #1 substring$ "-" = } { "-" * t #2 global.max$ substring$ 't := } while$ } if$ } { t #1 #1 substring$ * t #2 global.max$ substring$ 't := } if$ } while$ } FUNCTION {first.page.number} { 't := "" { t "" = { #0 } { t #1 #1 substring$ "-" = not } if$ } { t #1 #1 substring$ * t #2 global.max$ substring$ 't := } while$ } FUNCTION {format.btitle} { title emphasize } FUNCTION {tie.or.space.connect} { duplicate$ text.length$ #3 < { "~" } { " " } if$ swap$ * * } FUNCTION {either.or.check} { empty$ 'pop$ { "can't use both " swap$ * " fields in " * cite$ * warning$ } if$ } FUNCTION {format.bvolume} { volume empty$ { "" } { "Vol." volume tie.or.space.connect series empty$ 'skip$ { " of " * series emphasize * } if$ "volume and number" number either.or.check } if$ } FUNCTION {format.number.series} { volume empty$ { number empty$ { series field.or.null } { output.state mid.sentence = { "No." } { "No." } if$ number tie.or.space.connect series empty$ { "there's a number but no series in " cite$ * warning$ } { " in " * series * } if$ } if$ } { "" } if$ } FUNCTION {format.edition} { edition empty$ { "" } { output.state mid.sentence = { edition "l" change.case$ " edition" * } { edition "t" change.case$ " edition" * } if$ } if$ } INTEGERS { multiresult } FUNCTION {multi.page.check} { 't := #0 'multiresult := { multiresult not t empty$ not and } { t #1 #1 substring$ duplicate$ "-" = swap$ duplicate$ "," = swap$ "+" = or or { #1 'multiresult := } { t #2 global.max$ substring$ 't := } if$ } while$ multiresult } FUNCTION {format.pages} { pages empty$ { "" } { pages multi.page.check { "pp." pages n.dashify tie.or.space.connect } { "p." pages tie.or.space.connect } if$ } if$ } FUNCTION {format.page} { pages empty$ { "" } { "p.~" pages first.page.number * } if$ } FUNCTION {format.vol.num.pages} { volume field.or.null volume empty$ 'skip$ { boldface } if$ number empty$ 'skip$ { "(" number * ")" * * volume empty$ { "there's a number but no volume in " cite$ * warning$ } 'skip$ if$ } if$ pages empty$ 'skip$ { duplicate$ empty$ { pop$ format.pages } { ", " * pages n.dashify * } if$ } if$ } FUNCTION {format.vol.num.page} { volume field.or.null volume empty$ 'skip$ { boldface } if$ number empty$ 'skip$ { "(" number * ")" * * volume empty$ { "there's a number but no volume in " cite$ * warning$ } 'skip$ if$ } if$ pages empty$ 'skip$ { duplicate$ empty$ { pop$ format.pages } { ", " * pages first.page.number * } if$ } if$ } FUNCTION {format.chapter.pages} { chapter empty$ 'format.pages { type empty$ { "Chapt." } { type "l" change.case$ } if$ chapter tie.or.space.connect pages empty$ 'skip$ { ", " * format.pages * } if$ } if$ } FUNCTION {format.in.ed.booktitle} { booktitle empty$ { "" } { editor empty$ { "In: " booktitle emphasize * } { "In: " format.editors.extra * ": " * booktitle emphasize * } if$ } if$ } FUNCTION {format.in.booktitle.or.series} { booktitle empty$ { series empty$ { "" } { "In: " series emphasize * } if$ } { editor empty$ { "In: " booktitle emphasize * } { "In: " format.editors.extra * ": " * booktitle emphasize * } if$ } if$ } FUNCTION {format.thesis.type} { type empty$ 'skip$ { pop$ type "t" change.case$ } if$ } FUNCTION {format.tr.number} { type empty$ { "Technical Report" } 'type if$ number empty$ { "t" change.case$ } { number tie.or.space.connect } if$ } FUNCTION {format.article.crossref} { "In" " \cite{" * crossref * "}" * } FUNCTION {format.book.crossref} { volume empty$ { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ "In " } { "Vol." volume tie.or.space.connect " of " * } if$ "\cite{" * crossref * "}" * } FUNCTION {format.incoll.inproc.crossref} { "In" " \cite{" * crossref * "}" * } FUNCTION {article} { output.bibitem format.authors "author" output.check author format.key output output.year.check format.title "title" output.check new.block crossref missing$ { journal emphasize "journal" output.check.extra format.vol.num.pages output.extra } { format.article.crossref output.nonnull format.pages output } if$ new.block note output fin.entry } FUNCTION {book} { output.bibitem author empty$ { format.editors "author and editor" output.check editor format.key output } { format.authors output.nonnull crossref missing$ { "author and editor" editor either.or.check } 'skip$ if$ } if$ output.year.check format.btitle "title" output.check crossref missing$ { format.bvolume output format.number.series output new.block address empty$ 'skip$ { address ": " * output after.colon 'output.state := } if$ publisher "publisher" output.check.extra } { new.block format.book.crossref output.nonnull } if$ format.edition output new.block note output fin.entry } FUNCTION {booklet} { output.bibitem format.authors output author format.key output output.year.check format.btitle "title" output.check new.block howpublished output address output new.block note output fin.entry } FUNCTION {inbook} { output.bibitem author empty$ { format.editors "author and editor" output.check editor format.key output } { format.authors output.nonnull crossref missing$ { "author and editor" editor either.or.check } 'skip$ if$ } if$ output.year.check format.btitle "title" output.check crossref missing$ { format.bvolume output format.chapter.pages "chapter and pages" output.check format.number.series output new.block address empty$ 'skip$ { address ": " * output after.colon 'output.state := } if$ publisher "publisher" output.check.extra } { format.chapter.pages "chapter and pages" output.check new.block format.book.crossref output.nonnull } if$ format.edition output new.block note output fin.entry } FUNCTION {incollection} { output.bibitem format.authors "author" output.check author format.key output output.year.check format.title "title" output.check new.block crossref missing$ { format.in.ed.booktitle "booktitle" output.check format.bvolume output format.number.series output new.block address empty$ 'skip$ { address ": " * output after.colon 'output.state := } if$ publisher "publisher" output.check.extra format.edition output format.chapter.pages output } { format.incoll.inproc.crossref output.nonnull format.chapter.pages output } if$ new.block note output fin.entry } FUNCTION {inproceedings} { output.bibitem format.authors "author" output.check author format.key output output.year.check format.title "title" output.check new.block crossref missing$ { format.in.booktitle.or.series "booktitle or series" output.check format.bvolume output new.sentence address output format.pages output } { format.incoll.inproc.crossref output.nonnull format.pages output } if$ publisher output new.block note output fin.entry } FUNCTION {conference} { inproceedings } FUNCTION {manual} { output.bibitem format.authors output author format.key output output.year.check format.title "title" output.check new.block organization output address output format.edition output new.block note output fin.entry } FUNCTION {mastersthesis} { output.bibitem format.authors "author" output.check author format.key output output.year.check format.title "title" output.check new.block "Master's thesis" format.thesis.type output.nonnull school "school" output.check address output new.block note output fin.entry } FUNCTION {misc} { output.bibitem format.authors output author format.key output output.year.check format.title output new.block howpublished output new.block note output fin.entry } FUNCTION {phdthesis} { output.bibitem format.authors "author" output.check author format.key output output.year.check format.title "title" output.check new.block "Ph.D. thesis" format.thesis.type output.nonnull school "school" output.check address output new.block note output fin.entry } FUNCTION {proceedings} { output.bibitem format.editors output editor format.key output output.year.check format.title "title" output.check format.bvolume output format.number.series output new.block address empty$ 'skip$ { address ": " * output after.colon 'output.state := } if$ organization output.extra publisher output new.block note output fin.entry } FUNCTION {techreport} { output.bibitem format.authors "author" output.check author format.key output output.year.check format.title "title" output.check new.block format.tr.number output.nonnull institution "institution" output.check address output new.block note output fin.entry } FUNCTION {unpublished} { output.bibitem format.authors "author" output.check author format.key output output.year.check format.title "title" output.check new.block note "note" output.check fin.entry } FUNCTION {default.type} { misc } MACRO {jan} {"Jan."} MACRO {feb} {"Feb."} MACRO {mar} {"Mar."} MACRO {apr} {"Apr,"} MACRO {may} {"May"} MACRO {jun} {"June"} MACRO {jul} {"July"} MACRO {aug} {"Aug."} MACRO {sep} {"Sept."} MACRO {oct} {"Oct."} MACRO {nov} {"Nov."} MACRO {dec} {"Dec."} READ FUNCTION {sortify} { purify$ "l" change.case$ } INTEGERS { len } FUNCTION {chop.word} { 's := 'len := s #1 len substring$ = { s len #1 + global.max$ substring$ } 's if$ } FUNCTION {format.lab.names} { 's := s #1 "{vv~}{ll}" format.name$ s num.names$ duplicate$ #2 > { pop$ " et~al." * } { #2 < 'skip$ { s #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = { " et~al." * } { " and " * s #2 "{vv~}{ll}" format.name$ * } if$ } if$ } if$ } FUNCTION {author.key.label} { author empty$ { key empty$ { cite$ #1 #3 substring$ } 'key if$ } { author format.lab.names } if$ } FUNCTION {author.editor.key.label} { author empty$ { editor empty$ { key empty$ { cite$ #1 #3 substring$ } 'key if$ } { editor format.lab.names } if$ } { author format.lab.names } if$ } FUNCTION {editor.key.label} { editor empty$ { key empty$ { cite$ #1 #3 substring$ } 'key if$ } { editor format.lab.names } if$ } FUNCTION {calc.label} { type$ "book" = type$ "inbook" = or 'author.editor.key.label { type$ "proceedings" = 'editor.key.label 'author.key.label if$ } if$ "\protect\citeauthoryear{" swap$ * "}{" * year field.or.null purify$ #-1 #4 substring$ * 'label := } FUNCTION {sort.format.names} { 's := #1 'nameptr := "" s num.names$ 'numnames := numnames 'namesleft := { namesleft #0 > } { nameptr #1 > { " " * } 'skip$ if$ s nameptr "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" format.name$ 't := nameptr numnames = t "others" = and { "et al" * } { t sortify * } if$ nameptr #1 + 'nameptr := namesleft #1 - 'namesleft := } while$ } FUNCTION {sort.format.title} { 't := "A " #2 "An " #3 "The " #4 t chop.word chop.word chop.word sortify #1 global.max$ substring$ } FUNCTION {author.sort} { author empty$ { key empty$ { "to sort, need author or key in " cite$ * warning$ "" } { key sortify } if$ } { author sort.format.names } if$ } FUNCTION {author.editor.sort} { author empty$ { editor empty$ { key empty$ { "to sort, need author, editor, or key in " cite$ * warning$ "" } { key sortify } if$ } { editor sort.format.names } if$ } { author sort.format.names } if$ } FUNCTION {editor.sort} { editor empty$ { key empty$ { "to sort, need editor or key in " cite$ * warning$ "" } { key sortify } if$ } { editor sort.format.names } if$ } FUNCTION {presort} { calc.label label sortify " " * type$ "book" = type$ "inbook" = or 'author.editor.sort { type$ "proceedings" = 'editor.sort 'author.sort if$ } if$ #1 entry.max$ substring$ 'sort.label := sort.label * " " * title field.or.null sort.format.title * #1 entry.max$ substring$ 'sort.key$ := } ITERATE {presort} SORT % by label, sort.label, title---for final label calculation STRINGS { last.label next.extra } INTEGERS { last.extra.num } FUNCTION {initialize.extra.label.stuff} { #0 int.to.chr$ 'last.label := "" 'next.extra := #0 'last.extra.num := } FUNCTION {forward.pass} { last.label label = { last.extra.num #1 + 'last.extra.num := last.extra.num int.to.chr$ 'extra.label := } { "a" chr.to.int$ 'last.extra.num := "" 'extra.label := label 'last.label := } if$ } FUNCTION {reverse.pass} { next.extra "b" = { "a" 'extra.label := } 'skip$ if$ label extra.label * "}" * 'label := extra.label 'next.extra := } EXECUTE {initialize.extra.label.stuff} ITERATE {forward.pass} REVERSE {reverse.pass} FUNCTION {bib.sort.order} { sort.label " " * year field.or.null sortify * " " * title field.or.null sort.format.title * #1 entry.max$ substring$ 'sort.key$ := } ITERATE {bib.sort.order} SORT % by sort.label, year, title---giving final bibliography order FUNCTION {begin.bib} { preamble$ empty$ 'skip$ { preamble$ write$ newline$ } if$ "\begin{thebibliography}{}" write$ newline$ } EXECUTE {begin.bib} EXECUTE {init.state.consts} ITERATE {call.type$} FUNCTION {end.bib} { newline$ "\end{thebibliography}" write$ newline$ } EXECUTE {end.bib} splash/docs/splash.tex000755 000766 000000 00000617072 13261626263 015750 0ustar00dpricewheel000000 000000 %&program=pdflatex \documentclass[a4paper,10pt]{article} \usepackage{natbib,rotating,longtable,url,amsmath,nameref} %% font \usepackage{mathptmx,graphicx} \usepackage[pdftex, pdfborder={0 0 0}]{hyperref} \usepackage{ulem} \hypersetup{ colorlinks, citecolor=blue, linkcolor=blue, urlcolor=blue } \def\thisyear{2018\unskip} %HEVEA\usepackage{hevea} %HEVEA\htmlfoot{\@hr{\textwidth}{1pt}\textsc{SPLASH}: A visualisation tool for SPH data \copyright 2004--\thisyear Daniel Price. \newline \url{http://users.monash.edu.au/~dprice/splash/}} %HEVEA\setlinkstext{\imgsrc[ALT="Previous"]{arrow-left.png}}{\imgsrc[ALT="Up"]{arrow-up.png}}{\imgsrc[ALT="Next"]{arrow-right.png}} %HEVEA\toplinks{../index.html}{../index.html}{intro.html} %HEVEA\tocnumber %% use numbers in table of contents % Now we redefine certain commands of hyperref.sty % \makeatletter % The following for \url \def\url@#1{\hyper@linkurl{\uline{\Hurl{#1}}}{#1}} % The following for \href \def\hyper@link@[#1]#2#3#4{% \protected@edef\Hy@tempa{#2}% \ifx\Hy@tempa\@empty \hyper@link{#1}{#3}{\uline{#4}}% \else \expandafter\hyper@readexternallink#2\\{#1}{#3}{\uline{#4}}% \fi } \makeatother \graphicspath{{figs/}} \setlength{\topmargin}{-2.5cm} \setlength{\textheight}{26.5cm} \setlength{\oddsidemargin}{-0.5cm} \setlength{\evensidemargin}{-0.5cm} \setlength{\textwidth}{17cm} \newcommand{\splash}{\textsc{splash }} \newcommand{\giza}{\textsc{giza }} \begin{titlepage} \title{Visualisation of Smoothed Particle Hydrodynamics data using \splash - v\input{version}} \author{Daniel Price} \end{titlepage} \begin{document} % html div style %\begin{divstyle}{wrap} \begin{figure} \begin{center} \includegraphics[width=\textwidth]{hyperbolic.pdf} \end{center} \end{figure} \maketitle \tableofcontents%HEVEA\cutname{contents.html} \newpage \section{Introduction}%HEVEA\cutname{intro.html} While many wonderful commercial software packages exist for visualising scientific data (such as the widely used Interactive Data Language), I found they were cumbersome for particle-based data. Much of what I wanted to do was specific to SPH, like interpolating to an array of pixels using the kernel. While generic routines exist for such tasks, I could not explain how they worked, and they were slow. Also, while interactive gizmos are handy, it was more difficult to perform the same tasks non-interactively, as required for the production of animations. The major work in the visualisation of SPH data is not the image production itself but the manipulation of data prior to plotting. Much of this manipulation makes sense within an SPH framework. \splash is designed for this specific task - to use SPH tools to analyse SPH data. Publishable images and animations can be obtained efficiently from the raw data with a minimum amount of user effort. The development of powerful visualisation tools has enabled me to pick up on effects present in my simulation results that I would not otherwise have noticed --- the difference between a raw particle plot and a rendered image can be substantial. A key goal of \splash is to eliminate the use of crap-looking particle plots as a means of representing SPH data. \subsection{What it does} \splash is a utility for visualisation of output from (astrophysical) simulations using the Smoothed Particle Hydrodynamics (SPH) method in one, two and three dimensions. It is written in Fortran 90/95/20xx and utilises \giza\unskip, a custom-build backend graphics library to do the actual plotting. In particular the following features are included: \begin{itemize} \item Rendering of particle data to an array of pixels using the SPH kernel \item Cross-sections through 2D and 3D data (as both particle plots and rendered images). \item Fast projections through 3D data (i.e., column density plots, or integration of other quantities along the line of sight) \item Surface renderings of 3D data. \item Vector plots of the velocity (and other vector quantities), including vector plots in a cross section slice in 3D. \item Rotation and animation sequence generation for 3D data. \item Automatic stepping through timesteps, making animations simple to produce. \item Interactive mode for detailed examination of timestep data (e.g. zooming, rotating, stepping forwards/backwards, log axes, adapting limits). \item Remote visualisation via simple X-Windows forwarding \item Multiple plots on page, including option to automatically tile plots if $y-$ and $x-$ limits are the same. \item Plot limits can be fixed, adaptive or particle tracking. \item Exact solutions for common SPH test problems (e.g. shock tubes, sedov blast wave). \item Calculation of quantities not dumped (e.g. pressure, entropy) \item Conversion of binary dump files to ascii format. \item Interpolation of SPH data to 2D and 3D grids. \item Transformation to different coordinate systems (for both coordinates and vector components) and rescaling of data into physical units. \item Straightforward production of both bitmap (png) and vector (eps, pdf) images which can then be converted into animations or inserted into \LaTeX documents. \end{itemize} \subsection{What it doesn't do} \splash is geared towards gas dynamics simulations with SPH and has basically grown out of my visualisation needs. Thus it is not particularly useful for things like water and solids in SPH. An SPH visualisation tool geared towards the non-gaseous side of things you may want to have a look at is {\it pv-meshless}, by John Biddiscombe: \url{https://twiki.cscs.ch/twiki/bin/view/ParaViewMeshless} \splash also doesn't make coffee. \subsection{\splash\unskip, the paper} The algorithms implemented in \splash are not described here, but instead described in a paper \citep{splashpaper} (Publications of the Astronomical Society of Australia, 24, 159-173), available from: \url{http://www.publish.csiro.au/?paper=AS07022} \noindent This paper should be cited if you use \splash for scientific purposes, and please do so as it is my only form of thanks. \subsection{Version History} \begin{longtable}{|l|l|p{0.75\textwidth}|} \hline \input{version_history_tex} \hline \end{longtable} \subsection{Licence} \splash - a visualisation tool for SPH data \copyright 2004-\thisyear Daniel Price. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \section{Getting started}%HEVEA\cutname{gettingstarted.html} \subsection{Compiling the code} The basic steps for installation are as follows: \begin{enumerate} \item make sure you have a recent Fortran compiler (such as gfortran) \item compile \splash and \giza \item write a read\_data subroutine so that \splash can read your data format \end{enumerate} \subsubsection{ Fortran compilers} By now, many Fortran 90/95/2003 compilers exist. The most widely available are: \begin{itemize} \item gfortran, the free Gnu Fortran Compiler \\\url{http://gcc.gnu.org/wiki/GFortran} \item ifort, one of the most widely available commercial compilers (and is very good) with (limited) free licence for Linux. \url{http://software.intel.com/en-us/articles/intel-compilers/} \item Oracle Solaris Studio (formerly Sun studio), which contains an excellent free Fortran compiler for Linux and Solaris \\ \url{http://www.oracle.com/us/products/tools/050872.html} \end{itemize} All of these successfully compile \splash and the \giza library. %\subsubsection{\giza} %The PGPLOT graphics subroutine library is freely downloadable from %\begin{quote} %\url{http://www.astro.caltech.edu/~tjp/pgplot/} %\end{quote} %or by ftp from %\begin{quote} %\url{ftp://ftp.astro.caltech.edu/pub/pgplot/pgplot5.2.tar.gz} %\end{quote} %however check to see if it is already installed on your system (if so, the libraries are %usually located in /usr/local/pgplot). For details of the actual plotting subroutines %used by the \splash source code, you may want to refer to the PGPLOT userguide: %\begin{quote} %\url{http://www.astro.caltech.edu/~tjp/pgplot/contents.html} %\end{quote} \subsubsection{ Compiling and linking with \giza} A copy of \giza is included in the \splash distribution and is compiled automatically along with \splash. \giza is also available as a standalone project at: \begin{quote} \url{http://giza.sourceforge.net/} \end{quote} For detailed instructions on compiling and linking with \giza (or the older \textsc{pgplot} library used in \splash v1.x), refer to the INSTALL file in the root directory of the \splash distribution, or at: \begin{quote} \url{http://users.monash.edu.au/~dprice/splash/download/INSTALL}. \end{quote} A successful `make' will produce a binary for each of the main supported SPH data formats -- for example for ascii formats the binary is called `asplash' (by convention the first letter refers to the data format for which \splash has been compiled). Details of these are given below. \subsubsection{ Reading your data} The most important part is getting \splash to read *your* data format. If you are using a publically available code, it is reasonably likely that I have already written a read data subroutine which will read your dumps. If not it is best to look at some of the other examples and change the necessary parts to suit your data files. Note that reading directly from unformatted data files is *much* faster than reading from formatted (ascii) output. A standard ``make'' will create the binaries listed in Table~\ref{tab:defaultreads} which read the corresponding data formats listed in the third column. Table~\ref{tab:otherreads} lists other data reads implemented but not compiled by default. \begin{table}[h!] \begin{tabular}{lp{0.2\textwidth}lp{0.34\textwidth}} \splash binary & Formats read & read\_data file & Comments \\ \hline asplash, splash & ascii & \verb+read_data_ascii.f90+ & Generic data read for n-column ascii formats. Automatically determines number of columns and skips header lines. Can recognise SPH particle data based on the column labels. Use `asplash -e' to plot non-SPH data (e.g. energy vs time files).\\ dsplash & \textsc{dragon} & \verb+read_data_dragon.f90+ & see environment variable options. \\ gsplash & \textsc{gadget}, \textsc{gadget-2}, \textsc{gadget-3} & \verb+read_data_gadget.f90+ & Handles both default and block-labelled formats (see environment variable options). \\ nsplash & \textsc{ndspmhd} & \verb+read_data_dansph.f90+ & Format for the \textsc{ndspmhd} SPH/SPMHD code (publicly available from my website). \\ rsplash & \textsc{magma} & \verb+read_data_srosph.f90+ & Stephan Rosswog's code \\ ssplash & sphNG, \textsc{phantom} & \verb+read_data_sphNG.f90+ & sphNG is Matthew Bate's SPH code. \\ srsplash & \textsc{seren} & \verb+read_data_seren.f90+ & The \textsc{SEREN} SPH code (Hubber, McLeod et al.) \\ tsplash & \textsc{gasoline}, \textsc{tipsy} & \verb+read_data_tipsy.f90+ & Reads both binary and ascii TIPSY files (determined automatically). \\ vsplash & \textsc{vine} & \verb+read_data_VINE.f90+ & see environment variable options. \\ \hline \end{tabular} \caption{Binaries and data reads compiled by default} \label{tab:defaultreads} \end{table} \begin{table}[h!] \begin{tabular}{lllp{0.25\textwidth}} Format & Binary & read\_data file & Comments \\ \hline h5part & h5splash & \verb+read_data_h5part.f90+ & Reads general files written with the h5part library. Requires linking against H5PART and HDF5 libraries \\ \textsc{gadget} HDF5 & gsplash-hdf5 & \verb+read_data_gadget_hdf5.f90+ & Reads HDF5 format from the \textsc{gadget} code. Requires linking against HDF5 libraries \\ \textsc{amuse} HDF5 & amsplash-hdf5 & \verb+read_data_amuse_hdf5.f90+ & Reads HDF5 format from the \textsc{amuse} framework. \\ \verb+.silo+ format (particle data only) & silosplash & \verb+read_data_silo.f90+ & a nice standardised HDF5 particle format. Requires {\sc silo} libraries. \\ SNSPH & snsplash & \verb+read_data_snsph.f90+ & Supernova SPH (Chris Fryer et al.). Requires libsw. \\ falcON & fsplash & \verb+read_data_falcON.f90+ & Walter Dehnen's SPH code format (uses HDF5) \\ Andreas Bauswein's code & bsplash & \verb+read_data_bauswein.f90+ & \\ Sigfried Vanaverbeke's code & vsplash & \verb+read_data_vanaverbeke.f90+ & \\ Regularised SPH (Steinar B{\o}rve) & rsplash & \verb+read_data_rsph.f90+ & \\ FLASH tracer particles & fsplash & \verb+read_data_flash_hdf5.f90+ & Reads tracer particle output from the FLASH code. Requires linking against HDF5 libraries \\ Sky King/Nikos Mastrodemos & usplash & \verb+read_data_UCLA.f90+ & A good example of a simple ascii format reader \\ Jamie Bolton GADGET & gsplash\_jsb & \verb+read_data_gadget_jsb.f90+ & Reads extra arrays before the SPH smoothing length \\ Old Matthew Bate code & bsplash & \verb+read_data_mbate.f90+ & similar to the original Benz SPH code format \\ Foulkes/Haswell/Murray & fsplash & \verb+read_data_foulkes.f90+ & An ascii format \\ Andrea Urban format & usplash & \verb+read_data_urban.f90+ & An ascii format \\ \verb+.pbob+ format & psplash & \verb+read_data_pbob.f90+ & David Brown's SPH code \\ \hline \end{tabular} \caption{Other data reads implemented but not compiled by default} \label{tab:otherreads} \end{table} Further details on writing your own subroutine are given in appendix~\ref{sec:writeyourown}. The *easiest* way is to i) email me a sample data file and ii) the subroutine you used to write it, and I will happily create a data read for your file format. \subsection{Environment variables} \label{sec:envvariables} Several runtime options for \splash can be set using environment variables. These are variables set from your unix shell. In the bash shell, environment variables are set from the command line using \begin{verbatim} export VAR='blah' \end{verbatim} or by putting this command in your \verb+.bash_profile+/\verb+.bashrc+. In csh, the equivalent is \begin{verbatim} setenv VAR 'blah' \end{verbatim} or by putting the above in your \verb+.cshrc+ file. %\subsubsection{ PGPLOT} % Several useful environment variables can be set for PGPLOT and several of them %are very useful for \splash. Firstly, to get the basic installation working it is usually necessary to set the environment variable PGPLOT\_DIR to the location of the pgplot directory and possible also PGPLOT\_FONT to specify the location of the font file (see the online faq for font problems), i.e., add the appropriate modification of the following line(s) to your .bashrc (or equivalent) file: %\begin{verbatim} %export PGPLOT_DIR=/mypgplotdir/pgplot %export PGPLOT_FONT=/mypgplotdir/pgplot/grfont.dat %\end{verbatim} % %Some other useful things to set which control the runtime behaviour of PGPLOT include: %\begin{verbatim} %export PGPLOT_DEV=/xwin %export PGPLOT_BACKGROUND=white %export PGPLOT_FOREGROUND=black %\end{verbatim} %The first command sets the default device to the X-window, rather than the /null %device. The latter two commands set the background and foreground colours of the %plotting page. Note that these environment variables should be set \emph{before} %invoking \splash (it is simplest to set them upon starting the shell by placing %them in your .bashrc or tcsh/csh equivalent file). For other environment %variables which can be set, refer to the PGPLOT user guide. \subsubsection{ Changing the font} Several environment variables affect the backend plotting library. Probably the most useful is the ability to change font: \begin{verbatim} export GIZA_FONT='Helvetica' \end{verbatim} where the name is a reasonable guess as to the font you want to use (the default is `Times'). In particular, if you are having trouble displaying unicode characters such as greek letters, you can just change the font until you find one that works. \subsubsection{ Endian changing} On some compilers, the endian-ness (byte order) when reading unformatted binary data files can be changed at runtime. This is useful for looking at files on different systems to the one on which they were created (e.g. x86 machines create little-endian files by default, whereas IBM/powerpc machines create big-endian). Environment variables for changing the endian-ness of the data read for some common compilers are given below: \begin{table}[h!] \begin{tabular}{lllll} Compiler & Environment variable & Setting for big endian & Setting for little endian & Other options \\ \hline \verb+gfortran+ & \verb+GFORTRAN_CONVERT_UNIT+ & \verb+big_endian+ & \verb+little_endian+ & \verb+swap+ \\ \verb+ifort+ & \verb+F_UFMTENDIAN+ & \verb+big+ & \verb+little+ & \\ \hline \end{tabular} \end{table} For compilers without this feature, almost all can change the endian-ness at compile time, and the appropriate flags for doing so can be set using \begin{verbatim} export ENDIAN='BIG' \end{verbatim} or LITTLE before \emph{compiling} \splash (this adds the appropriate compile-time flags for the compiler selected using the SYSTEM environment variable in the \splash Makefile). \subsubsection{ Variables affecting all data reads} Environment variables that affect all data reads are:\newline \begin{longtable}{p{0.35\textwidth}p{0.55\textwidth}} SPLASH\_DEFAULTS & gives the name of a system-wide \verb+splash.defaults+ file (and splash.limits etc.) that will be used if there is none in the current directory. e.g. \verb+export SPLASH_DEFAULTS=/home/me/splash.defaults+ \\ SPLASH\_KERNEL & changes the smoothing kernel used in the interpolations (e.g. `cubic' or `quintic'). Can also be changed in the r)ender menu. \\ SPLASH\_DEBUG & if set to `yes' or `true', turns on very verbose debugging output. Useful to trace code crashes (but of course, this never happens\ldots).\\ SPLASH\_CENTRE\_ON\_SINK & if set to a number n, centres coordinates and velocities on the n{\it th} sink/star particle (e.g. \verb+export SPLASH_CENTRE_ON_SINK=2+). \\ SPLASH\_COROTATE & plot in corotating frame based on locations of two sink particles \\ SPLASH\_HMIN\_CODEUNITS & if given a value $>$0 enforces a minimum smoothing length, specified in code units as read from the dump file, on all the particles. This can be used to ``dumb-down'' the resolution of SPH simulations, e.g. to match observational resolution. If this variable is set it is \emph{highly} recommended that the ``use accelerated rendering'' option in the r)ender menu is also turned on as quite slow rendering can otherwise result. \\ SPLASH\_VZERO\_CODEUNITS & if set to a comma separated list of vector components (e.g. \verb+export SPLASH_VZERO_CODEUNITS='0.0,1.0,0.0'+), can be used to subtract a mean velocity field from all particles --- specified in code units as read from the dump file. \\ SPLASH\_MARGIN\_XMIN & can be used to manually adjust the left horizontal page margin (set to fraction of viewport, negative values are allowed). \\ SPLASH\_MARGIN\_XMAX & right horizontal page margin (set to fraction of viewport). \\ SPLASH\_MARGIN\_YMIN & bottom (vertical) page margin (set to fraction of viewport). \\ SPLASH\_MARGIN\_YMAX & top (vertical) page margin (set to fraction of viewport). \end{longtable} \subsubsection{ Ascii data read} \label{sec:asplash} For several data reads there are environment variables which can be set at runtime which are specific to the data read. For the ascii data read (`asplash') these are:\newline \begin{tabular}{p{0.35\textwidth}p{0.55\textwidth}} ASPLASH\_NCOLUMNS & if given a value $>$0 sets the number of columns to be read from ascii data (overrides the automatic number of columns determination). \\ ASPLASH\_NHEADERLINES & if given a value $>=$0 sets the number of header lines to skip (overrides the automatic determination). \\ ASPLASH\_COLUMNSFILE & can be used to provide the location of (path to) the default `columns' file containing the labels for ascii data (e.g. setenv ASPLASH\_COLUMNSFILE '/home/me/mylabels'). Overridden by the presence of a local `columns' file. \\ ASPLASH\_TIMEVAL & if given a nonzero value sets the time to use in the legend (fixed for all files) \\ ASPLASH\_GAMMAVAL & if given a nonzero value sets gamma to use in exact solution calculations (fixed for all files) \\ ASPLASH\_HEADERLINE\_TIME & sets the integer line number where the time appears in the header \\ ASPLASH\_HEADERLINE\_GAMMA & sets the integer line number where gamma appears in the header \\ \end{tabular} \subsubsection{ GADGET data read} \label{sec:gsplash} For the GADGET read (`gsplash') the environment variable options are:\newline \begin{tabular}{p{0.35\textwidth}p{0.55\textwidth}} GSPLASH\_FORMAT & if set = 2, reads the block labelled GADGET format instead of the default (non block labelled) format. \\ GSPLASH\_USE\_Z & if `YES' or `TRUE' uses the redshift in the legend instead of code time. \\ GSPLASH\_DARKMATTER\_HSOFT & if given a value $>$ 0.0 will assign a smoothing length to dark matter particles for which rendered plots of column density can then be made. \\ GSPLASH\_EXTRACOLS & if set to a comma separated list of column labels, will attempt to read additional columns containing gas particle properties beyond the end of the file (not applicable if GSPLASH\_FORMAT=2). \\ GSPLASH\_STARPARTCOLS & if set to a comma separated list of column labels, will attempt to read additional columns containing star particle properties beyond the end of the file (and after any extra gas particle columns) (not applicable if GSPLASH\_FORMAT=2). \\ GSPLASH\_CHECKIDS & if set to `YES' or `TRUE', reads and checks particle IDs, excluding particles with negative IDs as accreted (gives them a negative smoothing length which means they are ignored in renderings). \\ GSPLASH\_HSML\_COLUMN & if set to a positive integer, specifies the location of the smoothing length in the columns, overriding any default settings. \\ GSPLASH\_IGNORE\_IFLAGCOOL & if set to 'YES' or `TRUE', does not assume that extra columns are present even if the cooling flag is set in the header. \\ \end{tabular} For the GADGET read gsplash will also look for, and read if present, files called \verb+snapshot_xxx.hsml+ and/or \verb+snapshot_xxx.dens+ (where \verb+snapshot_xxx+ is the name of the corresponding GADGET dump file) which contain smoothing lengths and/or a density estimate for dark matter particles (these should just be one-column ascii files). \subsubsection{ VINE data read} For the VINE read (`vsplash') the environment variable options are:\newline \begin{longtable}{p{0.35\textwidth}p{0.55\textwidth}} VSPLASH\_HFAC & if `YES' or `TRUE' multiplies the smoothing length read from the dump file by a factor of 2.8 (for use with older VINE dumps where the smoothing length is defined as in a Plummer kernel rather than as the usual SPH smoothing length). \\ VSPLASH\_MHD & if `YES' or `TRUE' reads VINE dumps containing MHD arrays (note that setting VINE\_MHD also works). \\ \end{longtable} \subsubsection{ sphNG data read} For the sphNG and PHANTOM read (`ssplash') the environment variable options are:\newline \begin{longtable}{p{0.35\textwidth}p{0.55\textwidth}} SSPLASH\_CENTRE\_ON\_SINK \newline(**obsolete**) & if `YES' or `TRUE' resets the positions such that the sink particle is positioned at the origin (applies only where there is one, and only one, sink particle present). This option is obsolete: use SPLASH\_CENTRE\_ON\_SINK instead.\\ SSPLASH\_RESET\_CM & if `YES' or `TRUE' resets the positions such that the centre of mass is exactly at the origin. \\ SSPLASH\_OMEGA & if non-zero, subtracts solid body rotation with omega as specified to give velocities in co-rotating frame. \\ SSPLASH\_OMEGAT & if non-zero, subtracts solid body rotation with omega as specified to give positions and velocities in co-rotating frame. \\ SSPLASH\_TIMEUNITS & sets default time units, either 's', 'min', 'hrs', 'days', 'yrs' or 'tfreefall' (NB: text is used verbatim in legend). \end{longtable} \subsubsection{ \textsc{dragon} data read} For the \textsc{dragon} read (`dsplash') the environment variable options are:\newline \begin{tabular}{p{0.35\textwidth}p{0.55\textwidth}} DSPLASH\_EXTRACOLS & specifies number of extra columns present in the file which are dumped after the itype array \end{tabular} \subsubsection{ Stephan Rosswog data read} For the srosph read (`rsplash') the environment variable options are:\newline \begin{tabular}{p{0.35\textwidth}p{0.55\textwidth}} RSPLASH\_FORMAT & can be `MHD' or `HYDRO' which read the appropriate data format from either the MHD or hydrodynamic codes \\ RSPLASH\_RESET\_COM & if `YES' or `TRUE' resets the positions such that the centre of mass is exactly at the origin. \\ RSPLASH\_COROTATING & if `YES' or `TRUE' then velocities are transformed to corotating frame \\ RSPLASH\_HFACT & can be changed to give correct parameter in $h=h_{fact}(m/\rho)^{1/3}$ used to set the particle masses when rendering minidumps (i.e., when the mass is not dumped). Default is RSPLASH\_HFACT=1.5 \end{tabular} \subsubsection{ \textsc{ndspmhd} data read} For the \textsc{ndspmhd} read (`nsplash') the environment variable options are:\newline \begin{tabular}{p{0.35\textwidth}p{0.55\textwidth}} NSPLASH\_BARYCENTRIC & plots barycentric quantities for one-fluid dust instead of creating fake second set of particles \end{tabular} \subsubsection{ H5Part data read} For the H5PART read (`h5splash') the environment variable options are:\newline \begin{tabular}{p{0.35\textwidth}p{0.55\textwidth}} H5SPLASH\_NDIM & number of spatial dimensions $d$ (overrides value inferred from data) \\ H5SPLASH\_HFAC & factor to use to compute h from $h = h_{fac} *(m/\rho)^{1/d}$ if smoothing length not present in data \\ H5SPLASH\_HSML & value for global smoothing length h (if h not present in data) \\ H5SPLASH\_TYPEID & name of the dataset containing the particle type identification (default is ``MatID'') \end{tabular} \subsection{Command line options} \label{sec:commandline} \splash has a number of command line options which can be used to change various things about the runtime behaviour. Typing \verb+splash -v+ gives a complete and up-to-date list of options. Currently these are: \begin{verbatim} Command line options: -p fileprefix : change prefix to ALL settings files read/written by splash -d defaultsfile : change name of defaults file read/written by splash -l limitsfile : change name of limits file read/written by splash -e, -ev : use default options best suited to ascii evolution files (ie. energy vs time) -lm, -lowmem : use low memory mode [applies only to sphNG data read at present] -o pixformat : dump pixel map in specified format (use just -o for list of formats) Command line plotting mode: -x column : specify x plot on command line (ie. do not prompt for x) -y column : specify y plot on command line (ie. do not prompt for y) -r[ender] column : specify rendered quantity on command line (ie. no render prompt) (will take columns 1 and 2 as x and y if -x and/or -y not specified) -vec[tor] column : specify vector plot quantity on command line (ie. no vector prompt) -c[ontour] column : specify contoured quantity on command line (ie. no contour prompt) -dev device : specify plotting device on command line (ie. do not prompt) convert mode ("splash to X dumpfiles"): splash to ascii : convert SPH data to ascii file dumpfile.ascii to binary : convert SPH data to simple unformatted binary dumpfile.binary write(1) time,npart,ncolumns do i=1,npart write(1) dat(1:ncolumns),itype enddo to phantom : convert SPH data to binary dump file for PHANTOM to gadget : convert SPH data to default GADGET snapshot file format Grid conversion mode ("splash to X dumpfiles"): splash to grid : interpolate basic SPH data (density, plus velocity if present in data) to 2D or 3D grid, write grid data to file (using default output=ascii) to gridascii : as above, grid data written in ascii format to gridbinary : as above, grid data in simple unformatted binary format: write(unit) nx,ny,nz,ncolumns,time [ 4 bytes each ] write(unit) (((rho(i,j,k),i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ] write(unit) (((vx(i,j,k), i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ] write(unit) (((vy(i,j,k), i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ] write(unit) (((...(i,j,k),i=1,nx),j=1,ny),k=1,nz) [ 4 bytes each ] allto grid : as above, interpolating *all* columns to the grid (and output file) allto gridascii : as above, with ascii output allto gridbinary : as above, with binary output Analysis mode ("splash calc X dumpfiles") on a sequence of dump files: splash calc energies : calculate KE,PE,total energy vs time output to file called 'energy.out' calc massaboverho : mass above a series of density thresholds vs time output to file called 'massaboverho.out' calc max : maximum of each column vs. time output to file called 'maxvals.out' calc min : minimum of each column vs. time output to file called 'minvals.out' calc diff : (max - min) of each column vs. time output to file called 'diffvals.out' calc amp : 0.5*(max - min) of each column vs. time output to file called 'ampvals.out' calc delta : 0.5*(max - min)/mean of each column vs. time output to file called 'deltavals.out' calc mean : mean of each column vs. time output to file called 'meanvals.out' calc rms : (mass weighted) root mean square of each column vs. time output to file called 'rmsvals.out' the above options all produce a small ascii file with one row per input file. the following option produces a file equivalent in size to one input file (in ascii format): calc timeaverage : time average of *all* entries for every particle output to file called 'time_average.out' calc ratio : ratio of *all* entries in each file compared to first output to file called 'ratio.out' \end{verbatim} Command-line options can be entered in any order on the command line (even after the dump file names). For more information on the convert utility (`splash to ascii') see \S\ref{sec:convert}. For details of the \verb+-o ppm+ or \verb+-o ascii+ option see \S\ref{sec:writepixmap}. For details of the \verb+-ev+ option, see \S\ref{sec:evsplash}. \section{Basic \splash usage}%HEVEA\cutname{basics.html} \label{sec:basic} \subsection{Simple two column plot} Once you have successfully compiled \splash with a read data file that will read your data format, \splash is invoked with the name of the data file(s) on the command line, e.g. \begin{verbatim} splash myrun*.dat \end{verbatim} where splash should be replaced with `asplash', `gsplash' etc. depending on the data format. \\ After a successful data read, the menu should appear as something like the following (the example given is for a ``minidump'' from Stephan Rosswog's SPH code): \begin{verbatim} dprice$ rsplash minidump.00001 \end{verbatim} \begin{verbatim} _ _ (_) _ _ _ _ (_)_ _ (_) ___ _ __ | | __ _ ___| |__ (_) _ (_) _ (_) _ / __| '_ \| |/ _` / __| '_ \ _ (_) (_) _ (_) \__ \ |_) | | (_| \__ \ | | | _ (_) _ (_) _ |___/ .__/|_|\__,_|___/_| |_| (_) _ (_) (_) (_)|_| (_) (_) (_)(_) (_)(_) (_)(_) ( B | y ) ( D | a | n | i | e | l ) ( P | r | i | c | e ) ...etc... \end{verbatim} \begin{verbatim} You may choose from a delectable sample of plots ------------------------------------------------------- 1) x 7) particle mass 2) y 8) B\dx 3) z 9) B\dy 4) h 10) B\dz 5) \gr 11) div B 6) T ------------------------------------------------------- 12) multiplot [ 4 ] m) set multiplot ------------------------------------------------------- d(ata) p(age) o(pts) l(imits) le(g)end h(elp) r(ender) v(ector) x(sec/rotate) s,S(ave) q(uit) ------------------------------------------------------- Please enter your selection now (y axis or option): \end{verbatim} The simplest plot is of two quantities which are not both coordinates. For example, to plot density vs smoothing length, type \begin{verbatim} Please enter your selection now (y axis or option): 5 (x axis) (default=1): 4 Graphics device/type (? to see list, default /xwin): /xw \end{verbatim} The \verb+default=+ refers to the default value assigned if you just press the return key. The last prompt asks for the device to which output should be directed. A full list of available graphics devices is given by typing `?' at the prompt. Some of the most useful devices are given in table \ref{tab:devices}. In the above we have selected the X-window driver which means that the output is sent to the screen (provided X-windows is running), as demonstrated in the screenshot shown in Figure \ref{fig:rhoh}. Many useful tasks can now be achieved by moving the mouse to the plot window and selecting areas or pressing keystrokes -- this is ``interactive mode''. Pressing `h' in the plot window shows (in the terminal) the full list of commands. Of the more useful ones are: pressing `l' with the mouse over the colour bar to use a logarithmic axis, press 'a' on either the colour bar or inside the plot to adapt the plot limits, select an area with the mouse to zoom. See also \S\ref{sec:interactive}. To exit the plot, move the mouse to the plot window and press 'q' (quit). To exit \splash altogether press 'q' again from the \splash main menu (in the terminal). \begin{figure}[ht] \begin{center} \includegraphics[width=0.8\textwidth]{rhoh.jpg} \caption{Screenshot of simple two column plot to an X-window} \label{fig:rhoh} \end{center} \end{figure} \begin{table}[h] \centering \begin{tabular}{|l|l|l|l|} \hline \verb+/xw+, \verb+/xwin+ & X-Window (interactive) & \verb+/png+ & Portable Network Graphics (bitmap) \\ \verb+/eps+ & Encapsulated postscript (one file per page) & \verb+/svg+ & Scalable Vector Graphics \\ \verb+/pdf+ & PDF & \verb+/null+ & null device (no output) \\ \verb+/ps+ & Postscript (all pages in one file) & & \\ \hline \end{tabular} \caption{Commonly used graphics devices available in \giza} \label{tab:devices} \end{table} \subsection{Rendered plots} \label{sec:renderplot} A more complicated plot is where both the $x-$ and $y-$ axes refer to coordinates. For example \begin{verbatim} Please enter your selection now (y axis or option):2 (x axis) (default=1): 1 (render) (0=none) ([0:11], default=0):5 (vector plot) (0=none, 8=B) ([0:8], default=0):0 Graphics device/type (? to see list, default /xwin): /xw \end{verbatim} Notice that in this case that options appeared for rendered and vector plots. Our choice of ``5'' at the (render) prompt corresponds to column 5, which in this case is the density, producing the plot shown in the screenshot in Figure~\ref{fig:renderplot}. \begin{figure}[h] \begin{center} \includegraphics[width=0.8\textwidth]{renderplot.jpg} \caption{Screenshot of 3D column density plot to an X-window} \label{fig:renderplot} \end{center} \end{figure} Note that the render prompts only appear if, in the read\_data subroutine, values are set for the integer parameters irho, ipmass and ih corresponding to the locations of density, particle mass and smoothing length in the data arrays and provided the number of coordinate dimensions is 2 or greater (\splash can be used for SPH codes in 1, 2 and 3 dimensions and even for plotting ascii data where there are no ``coordinates''). \subsection{Cross section slice} To plot a cross section slice instead of a projection in 3D, type 'x' at the main menu to open the 'cross section/3D plotting options' menu and choose option 1 ``switch between cross section and projection''. Then re-plot the rendered plot again (exactly as in the previous example \S\ref{sec:renderplot}), setting the slice position at the prompt: \begin{verbatim} enter z position for cross-section slice: ([-8.328:8.327], default=0.000): \end{verbatim} which produces the plot shown in the screenshot in Figure~\ref{fig:renderplot_xsec}. \begin{figure}[h] \begin{center} \includegraphics[width=0.8\textwidth]{renderplot_xsec.jpg} \caption{Screenshot of 3D cross section slice plot to an X-window} \label{fig:renderplot_xsec} \end{center} \end{figure} \subsection{Vector plots} A prompt to plot vector arrows on top of rendered plots (or on top of particle plots) appears whenever vectors are present in the data (for details of how to specify this in your data read, see \S\ref{sec:writeyourown}), taking the form: \begin{verbatim} (vector plot) (0=none, 8=B) ([0:8], default=0):0 \end{verbatim} where the number refers to the column of the first component of the vector quantity. Vector plots in 3D show either the integral of each component along the line of sight or, for cross sections, the vector arrows in a cross section slice (depending on whether a projection or cross section has been selected for 3D plots -- see the rendering examples given previously). In 2D vector plots simply show the vector arrows mapped to a pixel array using the SPH kernel. Settings related to vector plots can be changed via the v)ector plot submenu (\S\ref{sec:vectorplots}). The size of the arrows is set by the maximum plot limit over all of the vector components. Alternatively the arrow size can be changed interactively using 'v', 'V' (to decrease and increase the arrow size respectively) and 'w' (to automatically adjust the arrow size so that the longest arrow is of order one pixel width). \subsection{Contour plots} To plot contours of a quantity instead of a rendered plot, simply set the colour scheme used for rendering to 0 (contours only) via the ``change colour scheme'' option in the r)ender menu (type ``r2'' from the main menu as a shortcut to option 2 in the render menu). Contours of an additional quantity can also be plotted on top of a render plot. However the prompt for an additional contour plot does not appear by default -- it can be turned on via the ``plot contours'' option in the r)ender menu (type ``r3'' at the main menu as a shortcut). With this option set \emph{and a non-zero response to the render prompt}, a prompt appears below the render prompt: \begin{verbatim} (render) (0=none) ([0:11], default=0):5 (contours) (0=none) ([0:11], default=0):6 \end{verbatim} Entering the column to use in the contour plot at this prompt (e.g. column 6 in the above example would correspond to the temperature) gives a rendered plot with overlaid contours. Entering the same quantity used in the rendering at this prompt (e.g. column 5 in the above example) triggers a subsequent prompt for the contour limits which can then be set differently to those used in the render plot. In this way it is possible to make a plot where the density of one particle type is shown by the rendered plot and the density of another particle type (with different limits) is shown by contours. This can be achieved because once contour plotting is turned on, the contribution of a given particle type to either the contours or rendered plots can be turned on or off via the ``turn on/off particles by type'' option in the particle plot o)ptions menu. \subsection{Moving forwards and backwards through data files} If you have put more than one file on the command line (or alternatively the file contains more than one dump), it is then possible to move forwards and backwards through the data by pressing the space bar with the cursor in the plot window (this is ``interactive mode''). To see the keystrokes for moving backwards or moving forwards/backwards by a specified number of steps, press 'h' in interactive mode. If you plot to a non-interactive device, \splash simply cycles through all the files on the command line automatically. \subsection{Zooming in and out / changing plot limits} Having plotted to an interactive device (e.g. /xw), tasks such as zooming in and out, selecting, colouring and hiding particles, changing the limits of both the plot and the colour bar and many other things can be achieved using either the mouse (i.e., selecting an area on which to zoom in) or by a combination of the mouse and a keystroke (e.g. move the mouse over a particle and press 'c' to see the size of the smoothing circle for that particle). One of the most useful commands in interactive mode is 'a' (adapt plot limits) which can be used to restore the plot limits to the maximum values for the data currently plotted (similarly pressing 'a' on the colour bar resets the colour bar limits to the minimum and maximum values of the rendered quantity). Pressing 'h' in interactive mode (that is, with your mouse in the plotting window) gives the full list of interactive commands (note that the text appears in the terminal from which \splash was invoked). Press 's' in the plot window to save changes between timesteps, otherwise the settings will revert when you move to the next timestep. These tasks can also be achieved non-interactively by a series of drop-down submenus invoked from the main menu by typing a single character. For example limits changing options are contained in the l)imits submenu, so to manually set plot limits we would type ``l'' from the main menu, then ``2'' for option 2 (set manual limits) and follow the prompts to set the limits for a particular data column. \subsection{Producing an encapsulated postscript figure for a paper} \label{sec:postscript} Producing a postscript plot suitable for inclusion in a \LaTeX file is simple: at the device prompt, type \begin{verbatim} Graphics device/type (? to see list, default /xw): /eps \end{verbatim} that is, instead of ``/xw'' (for an X-window), simply type ``/eps'' or ``.eps'' to use the encapsulated postscript driver. This produces a file which by default is called \verb+splash.eps+, or if multiple files have been read, a sequence of files called \verb+splash_0000.eps+, \verb+splash_0001.eps+, etc. To specify both the device and filename, type the full filename (e.g. \verb+myfile.eps+) as the device. Files produced in this way can be directly incorporated into \LaTeX using standard packages such as graphicx, psfig or epsfig. Note that postscript devices do not have a `background' colour, so plots with a `black' background and `white' foreground will have invisible axes labels when viewed in (e.g.) gv (actually, they are there in white but the background is transparent - try inserting the figure into Keynote or Powerpoint with a dark background). For plots in papers you will therefore need to use a `black' or similarly dark foreground colour (set via the p)age submenu). When setting the foreground and background colours an option appears such that annotation drawn over the rendered region can be drawn in the opposite colour - thus enabling black axes labels (off the plot) but white text in the legend (over the rendered area). \subsection{Producing a sequence of plots for a movie} \label{sec:movies} To make a movie of your simulation, first specify all of the files you want to use on the command line: \begin{verbatim} > splash dump_* \end{verbatim} and use an interactive device to adjust options until it looks right (hint: for the nicest movies, best thing is to delete nearly all of the annotation, e.g. using the backspace key in interactive mode). If in interactive mode type 's' to save the current settings, then plot the same thing again but to a non-interactive device. For example, to generate a sequence of png files: \begin{verbatim} Graphics device/type (? to see list, default /xw): /png \end{verbatim} This will generate a series of images named \verb+splash_0000.png+, \verb+splash_0001.png+, \verb+splash_0002.png+ corresponding to each new plotting page generated (or enter ``\verb+myfile.png+'' at the device prompt to generate \verb+myfile_0000.png+, \verb+myfile_0001.png+, \verb+myfile_0002.png+\ldots). Having obtained a sequence of images there are a variety of ways to make these into an animation using both free and commercial software. Suggestions on software packages to use for Mac, Linux and Windows can be found in the online faq (\url{http://users.monash.edu.au/~dprice/splash/faqs.html}). I generally use the application ``graphic converter'' on Mac OS/X which makes quicktime movies from a sequence of images. \subsection{Ten quick hints for producing good-looking plots} In this section I have listed ten quick suggestions for simple changes to settings which can improve the look of a visualisation substantially compared to the default options. These are as follows: \begin{enumerate} \item {\bf Log the colour bar.} To do this simply move the cursor over the colour bar and hit ``l'' (for log). Or non-interactively via the ``apply log or inverse transformations to columns'' option in the l)imits menu. \item {\bf Adjust the colour bar limits}. Position the mouse over the colour bar and left-click. To revert to the widest max/min possible for the data plotted, press `a' with the cursor positioned over the colour bar. Limits can also be set manually in the l)imits submenu. \item {\bf Try changing the colour scheme}. Press `m' or `M' in interactive mode to cycle forwards or backwards through the available colour schemes. \item {\bf Change the paper size}. To produce high-resolution images/movies, use the ``change paper size'' option in the p)age menu to set the paper size in pixels. \item {\bf Try using normalised interpolations}. If your simulation does \emph{not} involve free surfaces (or alternatively if the free surfaces are not visible in the figure), turning the ``normalise interpolations'' option on (in the r)ender submenu) may improve the smoothness of the rendering. This is turned off by default because it leads to funny-looking edges. \item {\bf Remove annotation/axes}. For movies, often axes are unnecessary and detract from the visual appeal. Axes, the colour bar and the various legends can be turned off in interactive mode by positioning the cursor appropriately and pressing backspace. Alternatively each can be turned off manually -- axes via the ``axes options'' option in the p)age submenu; the colour bar by the ``colour bar options'' entry in the r)ender menu and the legends via options in the leg)end menu. \item {\bf Change axes/page colours}. The background colour (colour of the page) and foreground colour (used for axes etc) can be changed vie the ``set foreground/background colours'' option in the p)age submenu. \item {\bf Move the legend or turn it off}. The time legend can be moved by positioning the mouse and pressing `G' in interactive mode. The legend can be turned off in the le(g)end submenu or by pressing backspace in interactive mode. Similarly the vector plot legend can be turned on/off in the v)ector submenu and moved by positioning the cursor and pressing `H'. \item {\bf Use physical units on the axes}. These can be set via the d)ata submenu. See \S\ref{sec:changingunits} for more details. \item {\bf Save settings to disk!} Don't waste your effort without being able to reproduce the plot you have been working on. Pressing `s' in interactive mode only saves the current settings for subsequent timesteps. Pressing `s' from the main menu saves these settings to disk. Pressing `S' from the main menu saves both the plot options \emph{and} the plot limits, so that the current plot can be reproduced exactly when \splash is next invoked. Adding an ``a'', as in ``SA'', ``SA'' or ``sa'' to the save options gives a prompt for a different prefix to the filenames (e.g. \verb+splash.defaults+ becomes \verb+myplot.defaults+), which \splash can be invoked to use via the \verb+-p+ command line option (e.g. \verb+splash -p myplot file1 file2...+). \end{enumerate} \section{Changing plot settings}%HEVEA\cutname{settings.html} %HEVEA\cutdef{subsection} The plot settings may be changed in a series of submenus. The options set using the submenus can be saved using the (s)ave option from the menu. This saves all of the current options to a file called \verb+splash.defaults+ in the current directory, which is automatically read upon starting \splash the next time. To revert to default options, simply delete this file. Pressing `S' from the main menu saves both the \verb+splash.defaults+ file and also saves the plot limits to a file called \verb+splash.limits+. This file is a simple two-column ascii file corresponding to the minimum and maximum plot limits for each column of data. Thus saving using 'S' means that exactly the same plot can be plotted next time \splash is invoked, where saving using 's' means that the plot settings will be the same although the limits will be different. To reset the plot limits either adjust the limits and press 'S' again or simply delete the splash.limits file. \subsection{set (m)ultiplot}%HEVEA\cutname{multiplot.html} \label{sec:multiplot} \subsubsection{ Plotting more than one column from the same file on the same page (multiplot)} \label{sec:multiplotsetup} Press 'm' (``set multiplot'') from the main menu to set up a multiplot. Note that a ``multiplot'' (multiple columns plotted from the same file) is different to plotting ``multiple plots per page'' (divide the plotting page up into panels). The number of panels across and down on a page can be changed (see \ref{sec:nacrossndown}) irrespective of whether or not you are also plotting multiple columns from the same file. Once you have gone through the options to set up a multiplot, to actually plot what you have set simply type the number of the column corresponding to ``multiplot'' at the $y-$axis prompt. \subsubsection{ Plotting each particle type in a different panel (multiplot)} To make a plot using different particle types in each panel (e.g. gas density in one panel, dust or dark matter density in another), use 'm' (``set multiplot'') from the main menu. If multiple types are present in the data read, the option appears to specify the particular types you want to use for each plot. For example, after pressing `m' at the main menu we eventually arrive at the question: \begin{verbatim} use all active particle types? (default=yes): n \end{verbatim} Answering ``no'' brings up a possible list of types: \begin{verbatim} 1: use gas particles 2: use ghost particles 3: use sink particles 4: use star particles 5: use unknown/dead particles Enter type or list of types to use ([1:5], default=1): 1,3 \end{verbatim} Thus entering e.g. ``1,3'' specifies that only gas and sink particles should be used for this plot. Note that this is more specific than simply turning particle types on and off for \emph{all} plots, which can be achieved via the ``turn on/off particles by type'' option in the o) menu (see \S\ref{sec:plotparticlesbytype}). \subsection{(d)ata options}%HEVEA\cutname{dmenu.html} The following can all be achieved from the d)ata options menu: \subsubsection{ Re-reading the initial data / changing the dump file} \label{sec:d1} The data can be re-read from the dump file or a new dump file can be selected by choosing the d)ata menu, option 1 (or just ``d1'' from the main menu). In practise it is usually faster to exit \splash and restart with the new dump file name on the command line (remember to save by pressing 'S' from the main menu before exiting to save both the current settings and the plot limits -- then you can continue plotting with the current settings using a new dump file). If you have placed more than one file on the command line, then pressing space in interactive mode will read (and plot) the next file (press 'h' in interactive mode for a full list of commands - you can move forwards and backwards using arbitrary jumps). For non-interactive devices or where interactive mode is turned off dump files are cycled through automatically, plotting the same plot for each file/timestep. \subsubsection{ Using only a subset of data files / plotting every $n-$th dump file} \label{sec:subsetofsteps} When \splash is invoked with more than one filename on the command line (for example, where all files are selected with something like ``splash DUMP*'') it is often helpful to use only a subset of the files. This can be set in the d)ata menu, selecting option 2 ``change number of timesteps used''. This prompts something like: \begin{verbatim} Start at timestep ([1:10], default=1): End at timestep ([1:10], default=10): Frequency of steps to read ([1:10], default=1): \end{verbatim} so that the beginning, end and frequency (e.g. 2 would mean read every second step) of dump files to use can be set. To plot a subset of the data files in *any* order, see \S\ref{sec:selectedstepsonly}. Of course, another way to achieve the same thing is to explicitly order the files on the command line. A method I often use is to write all filenames to a file, e.g. \begin{verbatim} > ls DUMP* > splash.filenames \end{verbatim} then edit the file to list only the files I want to use, then invoke \splash with no files on the command line: \begin{verbatim} > splash \end{verbatim} which will use the list of files specified in the \verb+splash.filenames+ file. \subsubsection{ Plotting a subset of data files in non-sequential order} \label{sec:selectedstepsonly} A subset of data files from the command line can be chosen in any order using the ``plot selected steps only'' option from the d)ata submenu, which then prompts the user to enter something like the following: \begin{verbatim} Enter number of steps to plot ([1:10], default=0):5 Enter step 1 ([1:10], default=1):5 Enter step 2 ([1:10], default=2):2 Enter step 3 ([1:10], default=3):1 Enter step 4 ([1:10], default=4):4 Enter step 5 ([1:10], default=5):3 \end{verbatim} Note that only a limited number of steps can be selected in this way. An alternative way is to order the files on the command line before invoking \splash (see \S\ref{sec:subsetofsteps}). \subsubsection{ Plotting more than one file without re-reading the data from disk} \label{sec:buffering} For small data sets (or a small number of dump files) it is often useful to read all of the data into memory so that you can move rapidly forwards and backwards between dumps (e.g. in interactive mode, or where both dumps are plotted on the same page) without unnecessary re-reading of data from disk. This is achieved by turning ``buffering of data'' on in the d)ata menu (provided you have the memory of course!!). Non-buffered data means that only one file at a time is read. \subsubsection{ Calculating additional quantities not dumped} Turn ``calculate extra quantities'' on in the d)ata menu. As of \splash version 1.13.0 it is possible to specify new columns of data as completely arbitrary functions of the data read from the SPH particles. Option d5 in the data menu leads, for a typical data read, to a prompt similar to the following: \begin{verbatim} Specify a function to calculate from the data Valid variables are the column labels, 't', 'gamma', 'x0', 'y0' and 'z0' (origin setting) Spaces, escape sequences (\d) and units labels are removed from variable names Note that previously calculated quantities can be used in subsequent calculations Examples based on current data: r = sqrt((x-x0)**2 + (y-y0)**2 + (z-z0)**2) pressure = (gamma-1)*density*u |v| = sqrt(vx**2 + vy**2 + vz**2) Enter function string to calculate (blank for none) (default=""): \end{verbatim} Thus, one can for example calculate the pressure from the density and thermal energy according by copying the second example given. Note that the function calculation is completely general and can use any of the columns read from the file, the time for each step (`\verb+t+'), the adiabatic index $\gamma$ (`\verb+gamma+') and the current origin setting (\verb+x0+, \verb+y0+ and \verb+z0+). Previously calculated quantities can also be used - e.g. in the above example we could further compute, say, an entropy variable using \verb+s=pressure/density^gamma+ after the pressure has been specified. The resultant quantities appear in the main splash menu as standard columns just as if they had been read from the original data file. The origin for the calculation of radius can be changed via the ``rotation on/off/settings'' option in the x) submenu. If particle tracking limits are set (see \S\ref{sec:track}) the radius is calculated relative to the particle being tracked. Note that if you simply want to multiply a column by a fixed number (e.g. say you have sound speed squared and you want to plot temperature) - this can also be achieved by defining a unit for the column (i.e., a factor by which to multiply the column by) -- see \S\ref{sec:physicalunits} for details. The corresponding label can be changed by creating a \verb+splash.columns+ file (or for the ascii read just a file called `columns') containing labels which are used to override the default ones from the data read (one per line) -- see \S\ref{sec:columnsfile} for more details. See also \S\ref{sec:geom} for how to transform vectors (and positions) into different coordinate systems. \subsubsection{ Plotting data in physical units} \label{sec:physicalunits} Data can be plotted in physical units by turning on the ``use physical units'' option in the d)ata submenu. The settings for transforming the data into physical units may be changed via the ``change physical unit settings'' option in the d)ata menu. (see \S\ref{sec:changingunits}) For some data reads (sphNG, srosph) the scalings required to transform the data into physical units are read from the dump file. These are used as the default values but are overridden as soon as changes are made by the user (that is, by the presence of a `splash.units' file) (see \S\ref{sec:changingunits}). \subsubsection{ Rescaling data columns} See \S\ref{sec:physicalunits}. \subsubsection{ Changing the default column labels} \label{sec:columnsfile} The labelling of columns is usually specific to the data format read (except in the case of the ascii read, asplash, where columns are labelled by the creation of a file called `columns'). Aside from changing the labels in the \verb+read_data+ file specific to the format you are reading, it is also possible to override the labelling of columns at runtime by creating a file called \verb+splash.columns+ (or with a different prefix if the \verb+-p+ command line option is used), with one label per line corresponding to each column read from the dump file, e.g. \begin{verbatim} column 1 column 2 column 3 my quantity another quantity \end{verbatim} Note that the labels in the \verb+splash.columns+ file \emph{will not} override the labels of coordinate axes or labels for vector quantities (as these require the ability to be changed by plotting in different coordinate systems -- see \S\ref{sec:geom}). \subsubsection{ Plotting column density in g/cm$^{2}$ without having x,y,z in cm} See \S\ref{sec:changingunits}. In addition to units for each column (and a unit for time -- see \S\ref{sec:timeunits}) a unit can be set for the length scale added in 3D column integrated plots. The prompt for this appears after the units of either $x$, $y$, $z$ or $h$ has been changed via the ``change physical unit settings'' option in the d)ata menu. The length unit for integration is saved in the first row of the splash.units file, after the units for time. See \S\ref{sec:setprojlabel} for details on changing the default labelling scheme for 3D column integrated (projection) plots. \subsubsection{ Changing physical unit settings} \label{sec:changingunits} The settings for transforming the data into physical units may be changed via the ``change physical unit settings'' option in the d)ata menu. To apply the physical units to the data select the ``use physical units'' option in the d)ata submenu. The transformation used is $new= old*units$ where ``old'' is the data as read from the dump file and ``new'' is the value actually plotted. The data menu option also prompts for a units label which is appended to the usual label. Brackets and spaces should be explicitly included in the label as required. Once units have been changed, the user is prompted to save the unit settings to a file called \verb+splash.units+. Another way of changing units is simply to edit this file yourself in any text editor (the format is fairly self-explanatory). To revert to the default unit settings simply delete this file. To revert to code units turn ``use physical units'' off in the d)ata menu. A further example of where this option can be useful is where the $y-$axis looks crowded because the numeric axis labels read something like $1\times 10^{-4}$. The units option can be used to rescale the data so that the numeric label reads $1$ (by setting $units=10^{4}$) whilst the label string is amended to read $y [\times 10^{-4}]$ by setting the units label to $ [ \times 10^{-4}]$. \subsubsection{ Changing the axis label to something like $x$ $[ \times 10^{4} ]$} See \S\ref{sec:changingunits}. \subsubsection{ Changing the time units} \label{sec:timeunits} Units for the time used in the legend can be changed using the ``change physical unit settings'' in the d)ata menu. Changing the units of column zero corresponds to the time (appears as the first row in the `splash.units' file). \subsection{(i)nteractive mode}%HEVEA\cutname{interactive.html} \label{sec:interactive} The menu option i) turns on/off interactive mode (alternatively use ``interactive mode on/off'' in the p)age submenu). With this option turned on (the default) and an appropriate device selected (i.e., the X-window, not /gif or /ps), after each plot the program waits for specific commands from the user. With the cursor positioned anywhere in the plot window (but not outside it!), many different commands can be invoked. Some functions you may find useful are: Move through timesteps by pressing the space bar (press `b' to go back); zoom/select particles by selecting an area with the mouse; rotate the particles by using the $<$, $>$,[, ] and $\backslash$, / keys; log the axes by holding the cursor over the appropriate axis and pressing the `l' key. Press `q' in the plot window to quit interactive mode. A full list of these commands is obtained by holding the cursor in the plot window and pressing the `h' key (h for help). Note that changes made in interactive mode will only be saved by pressing the `s' (for save) key. Otherwise pressing the space bar (to advance to the next timestep) erases the changes made whilst in interactive mode. A more limited interactive mode applies when there is more than one plot per page. Many more commands could be added to the interactive mode, limited only by your imagination. Please send me your suggestions! \subsubsection{ Adapting the plot limits} Press `a' in interactive mode to adapt the plot limits to the current minimum and maximum of the quantity being plotted. With the mouse over the colour bar, this applies to the colour bar limits. Also works even when the page is subdivided into panels. To adapt the size of the arrows on a vector plot, press `w'. To use ``adaptive plot limits'' (where the limits change at every timestep), see \S\ref{sec:adapt}. \subsubsection{ Making the axes logarithmic} Press 'l' in interactive mode with the mouse over either the x or y axis or the colour bar to use a logarithmic axis. Pressing 'l' again changes back to linear axes. To use logarithmic labels as well as logarithmic axes, see \S\ref{sec:loglabels}. \subsubsection{Cycling through data columns interactively} Use `f' in interactive mode on a rendered plot to interactively `flip' forwards to the next quantity in the data columns (e.g. thermal energy instead of density). Use 'F' to flip backwards. \subsubsection{ Colouring a subset of the particles and retaining this colour through other timesteps} \label{sec:colourparts} \begin{figure} \centering \includegraphics[width=0.8\textwidth]{colourparts.pdf} %\begin{tabular}{cc} %\includegraphics[angle=270,width=0.4\textwidth]{colourparts.pdf} & %\includegraphics[angle=270,width=0.4\textwidth]{colourparts_highdens.pdf} %\end{tabular} \caption{Example of particles coloured interactively using the mouse (left) and selection using a parameter range (right), which is the same as the plot on the left but showing only particles in a particular density range (after an intermediate plot of density vs x on which I selected a subset of particles and hit 'p')} \label{fig:colourparts} \end{figure} In interactive mode, select a subset of the particles using the mouse (that is left click and resize the box until it contains the region you require), then press either 1-9 to colour the selected particles with colours corresponding to plotting library colour indices 1-9, press 'p' to plot only those particles selected (hiding all other particles), or 'h' to hide the selected particles. An example is shown in the left panel of Figure~\ref{fig:colourparts}. Particles retain these colours between timesteps and even between plots. This feature can therefore be used to find particles within a certain parameter range (e.g. by plotting density with x, selecting/colouring particles in a given density range, then plotting x vs y in which the particles will appear as previously selected/coloured). An example of this feature is shown in the right panel of Figure~\ref{fig:colourparts} where I have plotted an intermediate plot of density vs x on which I selected a subset of particles and hit 'p' (to plot only that subset), then re-plotted x vs y with the new particle selections. To ``un-hide'' or ``de-colour'' particles, simply select the entire plotting area and press ``1'' to restore all particles to the foreground colour index. Particles hidden in this manner are also no longer used in the rendering calculation. Thus it is possible to render using only a subset of the particles (e.g. using only half of a box, or only high density particles). An example is shown in Figure~\ref{fig:rendersubset}. To colour the particles according to the value of a particular quantity, see \S\ref{sec:colournotrender}. Note that selection in this way is based on the particle \emph{identity}, meaning that the parameter range itself is not preserved for subsequent timesteps, but rather the subset of particles selected from the initial timestep. This can be useful for working out which particles formed a particular object in a simulation by selecting only particles in that object at the end time, and moving backwards through timesteps retaining that selection. \subsubsection{ Working out which particles formed a particular object in a simulation} This can be achieved by selecting and colouring particles at a particular timestep and plotting the same selection at an earlier time. See \S\ref{sec:colourparts} for details. \subsubsection{ Plotting only a subset of the particles} To turn plotting of certain particle \emph{types} on and off, see \S\ref{sec:plotparticlesbytype}. To select a subset of the particles based on restrictions of a particular parameter or by spatial region see \S\ref{sec:colourparts}. \subsubsection{ Rendering using only a subset of the particles} \label{sec:rendersubset} Particles can be selected and `hidden' interactively (see \S\ref{sec:colourparts}) -- for rendered plots `hidden' particles are also not used in the interpolation calculation from the particles to the pixel array. An example is shown in Figure~\ref{fig:rendersubset}, where I have taken one of the rendered examples in \S\ref{sec:basic}, selected half of the domain with the mouse and pressed 'p' to plot only the selected particles. The result is the plot shown. \begin{figure}[h] \begin{center} %\includegraphics[angle=270,width=0.5\textwidth]{rendersubset.pdf} \includegraphics[width=0.5\textwidth]{rendersubset.pdf} \caption{Example of rendering using only a subset of the particles. Here I have selected only particles on the right hand side of the plot using the mouse and hit 'p' to plot only those particles.} \label{fig:rendersubset} \end{center} \end{figure} Note that the selection done in this manner is by default a restriction based on \emph{particle identity} -- that is, the same particles will be used for the plot in subsequent dumps (allowing one to easily track the Lagrangian evolution of a patch of gas). However \splash also has the ability to select based on particular parameter ranges (i.e., independent of time), called a `parameter range restriction' which is also more powerful in the sense that it can be saved to the \verb+splash.limits+ file -- see \S\ref{sec:rangerestrict} for more details. A range restriction can be set in interactive mode by selecting the restricted box using the mouse and pressing `x', `y' or `r' to restrict the particles used to the x, y (or r for both x and y) range of the selected box respectively. Pressing `S' at the main menu will save such range restrictions to the \verb+splash.limits+ file. \subsubsection{ Tracking a set of particles through multiple timesteps} See \S\ref{sec:rendersubset}. \subsubsection{ Taking an oblique cross section interactively} \label{sec:obliquexsec} It is possible to take an oblique cross section through 3D data using a combination of rotation and cross section slice plotting. To set the position interactively, press 'x' in interactive mode to draw the position of the cross section line (e.g. on an x-y plot this then produces a z-x plot with the appropriate amount of rotation to give the cross section slice in the position selected). Note that this will work even if the current plot is a 3D column integrated projection (in this case the setting ``projection or cross section'' changes to ``cross section'' in order to plot the slice). \subsection{(p)age options}%HEVEA\cutname{pmenu.html} \label{sec:optionspage} Options related to the page setup are changed in the p)age submenu. \subsubsection{ Overlaying timesteps/multiple dump files on top of each other} \label{sec:nstepsontopofeachother} It is possible to over-plot data from one file on top of data from another using the ``plot n steps on top of each other'' option from the p)age submenu. Setting $n$ to a number greater than one means that the page is not changed until $n$ steps have been plotted. Following the prompts, it is possible to change the colour of all particles between steps and the graph markers used and plot an associated legend (see below). Note that this option can also be used in combination with a multiplot (see \S\ref{sec:multiplot}) -- for example plotting the density vs x and pressure vs x in separate panels, then with $n > 1$ all timesteps will be plotted in \emph{each} panel). When more than one timestep is plotted per page with different markers/colours, an additional legend can be plotted (turn this on in the le(g)end submenu, or when prompted whilst setting the "plot n steps on top of each other" option). The text for this legend is just the filename by default (if one timestep per file) or just something dull like 'step 1' (if more than one timestep per file). To change the legend text, create a file called \verb+legend+ in the working directory, with one label per line. The position of the legend can be changed either manually via the ``legend and title options'' in the p)age submenu, or by positioning the mouse in interactive mode and pressing 'G' (similar keys apply for moving plot titles and the legend for vector plots -- press 'h' in interactive mode for a full list). \subsubsection{ Plotting results from multiple files in the same panel} See \ref{sec:nstepsontopofeachother}. \subsubsection{ Plotting more than one dump file on the same page} Note that this is slightly different to ``plotting more than one dump file on the same panel'' \subsubsection{ Changing axes settings} \label{sec:axessettings} Axes settings can be changed in the p)age submenu, by choosing ``axes options''. The options are as follows: \begin{verbatim} -4 : draw box and major tick marks only; -3 : draw box and tick marks (major and minor) only; -2 : draw no box, axes or labels; -1 : draw box only; 0 : draw box and label it with coordinates; 1 : same as AXIS=0, but also draw the coordinate axes (X=0, Y=0); 2 : same as AXIS=1, but also draw grid lines at major increments of the coordinates; 3 : draw box, ticks and numbers but no axes labels; 4 : same as AXIS=0, but with a second y-axis scaled and labelled differently 10 : draw box and label X-axis logarithmically; 20 : draw box and label Y-axis logarithmically; 30 : draw box and label both axes logarithmically. \end{verbatim} \subsubsection{ Turning axes off} Plot axes can be turned off by choosing ``axes options'' in the p)age submenu or by deleting them using the backspace key in interactive mode. See \S\ref{sec:axessettings} for more details. \subsubsection{ Turning axes labels off} Axes labels and numbering can be turned off via the ``axes options'' option in the p)age submenu or by deleting them using the backspace key in interactive mode. See \S\ref{sec:axessettings} for more details. \subsubsection{ Using logarithmic axes labels} \label{sec:loglabels} Logarithmic axes (that is where the quantity plotted is logged) can be set via the ``apply log or inverse transformations'' option in the l)imits submenu or simply by pressing 'l' with the cursor over the desired axis (or the colour bar) in interactive mode. By default the axes labels reads $log(x)$ and the number next to the axis is $-4$ when $x$ is 10$^{-4}$. Logarithmic axes labels (i.e., where the label reads $x$ and the number next to the axis is $10^{-4}$ with a logarithmic scale) can be specified by choosing the ``axes options'' option in the p)age submenu and setting the axes option to 10, 20 or 30 as necessary (see \S\ref{sec:axessettings} for more details). \subsubsection{ Plotting a second, rescaled y-axis on the right hand side of a plot} A second y axis can be added by selecting the axis=4 option in the ``axes option'' in the p)age submenu (see \S\ref{sec:axes settings}). This will prompt for the scaling and alternative label: \begin{verbatim} enter axis option ([-4:30], default=0): 4 enter scale factor for alternative y axis ([0.000:], default=1.000): 10.0 enter label for alternative y axis (default=""): y [other units] \end{verbatim} \subsubsection{ Changing the size of the plotting surface} \label{sec:papersize} The physical size of the viewing surface used for plotting can be changed via the ``change paper size'' option in the p)age submenu. This affects the size of the X-window (if plotted to the screen) and the size of .png or images generated (if plotted to these devices). Several preset options are provided or the paper size in x and y can be explicitly specified in inches or pixels. \subsubsection{ Dividing the plotting page into panels} \label{sec:nacrossndown} The plotting page can be divided into panels using the ``subdivide page into panels'' option in the p)age submenu. Note that for multiple plots per page (i.e., nacross $\times$ ndown $> 1$) a more limited interactive mode applies (basically because the data used for the plots is no longer stored in memory if there is more than one plot on the same page meaning that functionality such as selecting particles must be turned off). \subsubsection{ Tiling plots with the same $x-$ and $y-$ axes} \label{sec:tiling} Plots with the same $x-$ and $y-$ axes are tiled if the tiling option from the (p)age options menu (\S\ref{sec:optionspage}) is set. Tiling means that only one axis is shown where multiple plots share the same x or y axis and that the plots are placed as close to each other as possible. For rendered plots a shared colour bar is plotted which spans the full length of the page. \subsubsection{ Using non-proportional scales for spatial dimensions} \label{sec:squarexy} By default if the x and y axes are both spatial coordinates, the axes are scaled proportionately. This can be changed via the ``spatial dimensions have same scale'' option in the p)age submenu. \subsubsection{ Using non-square axes on coordinate plots} See \S\ref{sec:squarexy}. \subsubsection{ Changing the character height for axes, labels and legends} The character height used for axes, labels and legends can be changed via the p)age setup options submenu. Note that the character height is relative to the paper size (which can also be changed -- see \S\ref{sec:papersize}). \subsubsection{ Using a thicker line width on plots} The line width used for axes and text can be changed via the p)age submenu. Note that line width changes are not always obvious when plotting to an interactive device (e.g. an X-window) but influence non-interactive devices strongly. \subsubsection{ Changing the foreground and background colours} \label{sec:pagecolours} The background and foreground colour of a plot can be changed vie the ``set foreground/background colours'' option in the p)age submenu. Note that the background colour setting has no effect on postscript devices (see \S\ref{sec:postscript} for more details). \subsubsection{ Plotting axes, legends and titles in white even when the labels are plotted in black} By default, axes, legends and titles are plotted in the foreground colour (e.g. black). However if the plot itself is also largely black (e.g. when rendering or when lots of particles are plotted) it can be useful to overplot those parts of the axes and labelling which lie on top of the plotting surface in the background colour (e.g. white). A prompt for this is given when setting the ``set foreground/background colours'' option in the p)age submenu. The prompt appears as follows: \begin{verbatim} ---------------- page setup options ------------------- ... 9) set foreground/background colours enter option ([0:8], default=0):9 Enter background colour (by name, e.g. "black") (default=""):white Enter foreground colour (by name, e.g. "white") (default=""):black Overlaid (that is, drawn inside the plot borders) axis ticks, legend text and titles are by default plotted in the foreground colour [i.e., black]. Do you want to plot these in background colour [i.e., white] instead ? (default=no):y \end{verbatim} In the above I have selected a background colour of white, a foreground colour of black. Answering yes to the last question means that those parts of the axes which lie on top of the viewing surface (and any labels) will be plotted in white (the background colour) instead of the foreground colour (black). \subsection{le(g)end and title options}%HEVEA\cutname{gmenu.html} \subsubsection{ Adding titles to plots / repositioning titles} \label{sec:title} Plots may be titled individually by creating a file called \verb+splash.titles+ in the current directory, with the title on each line corresponding to the position of the plot on the page. Thus the title is the same between timesteps unless the steps are plotted together on the same physical page. Leave blank lines for plots without titles. For example, creating a file called \verb+splash.titles+ in the current directory, containing the text: \begin{verbatim} plot one plot two plot three \end{verbatim} and positioning the title using the default options, will produce a plot with one of these titles on each panel. \subsubsection{ Turning off/moving the time legend} \label{sec:legendoff} The position of the time legend can be set interactively by positioning the mouse in the plot window and pressing 'G'. To set the position non-interactively and/or change additional settings such as the justification, use the ``time legend on/off/settings'' option in the le(g)end submenu. \subsubsection{ Changing the text in the time legend} \label{sec:timelegendtext} The text which appears the time legend (by default this is ``t='') can be changed via the ``time legend on/off/settings'' option in the le(g)end submenu. To rescale the \emph{value} of the time displayed in the time legend (default value is as read from the dump file), see \S\ref{sec:timeunits}. \subsubsection{ Making the legend read ``z='' instead of ``t=''} See \ref{sec:timelegendtext}. An option to change the legend text is provided in the ``time legend on/off/settings'' option in the le(g)end submenu. The numeric value of the time legend is as read into the \verb+time+ array in the read\_data routine. This value can be rescaled by setting a unit for time (see \S\ref{sec:timeunits}). \subsubsection{ Plotting the time legend on the first row/column of panels / nth panel only} An option to plot the time legend on the first row or column of panels or on a single panel only appears in the in the le(g)end submenu. \subsubsection{ Plotting a length scale on coordinate plots} An option to plot a length scale (i.e., \verb+|---|+ with a label below it indicating the length) on coordinate plots (i.e., plots where both $x-$ and $y-$axes refer to particle coordinates) is provided in the le(g)end submenu. \subsubsection{ Annotating a plot with squares, rectangles, arrows, circles and text} Use the ``annotate plot'' option in the le(g)end submenu to annotate plots with a range of geometric objects (squares, rectangles, arrows, circles and text) with full control over attributes such as line width, line style, colour, angle and fill style. Text annotation can also be added/deleted in interactive mode using \verb+ctrl-t+ (to add) and the backspace key (to delete). Text can also be added to plots by adding titles (\S\ref{sec:title}) which can be different in different panels. Text labels added using shape annotation differ from titles by the fact that they must appear the same in each panel and are positioned according to the world co-ordinates of the plot (rather than relative to the viewport). Shape text can also be displayed at arbitrary angles. An option to plot length scales (\verb+|---|+) on coordinate plots is implemented separately via the ``plot scale on coordinate plots'' option in the le(g)end menu. \subsubsection{ Adding your name to a plot/movie} Arbitrary text annotation can be added/removed in interactive mode using \verb+ctrl-t+ (to add) and the backspace key (to delete) or via the ``annotate plot'' option in the le(g)end menu. \subsection{particle plot (o)ptions}%HEVEA\cutname{omenu.html} \label{sec:opts} The following are tasks which can be achieved via options in the o) menu [particle plot o)ptions]. \subsubsection{ Plotting non-gas particles (e.g. ghosts, boundary, sink particles)} \label{sec:plotparticlesbytype} Particles of different types can be turned on or off (i.e., plotted or not) using the ``turn on/off particles by type'' option in the particle plot o)ptions submenu. This option also prompts to allow particles of non-SPH types to be plotted on top of rendered plots (useful for sink or star particles - this option does not apply to SPH particle types). Turning SPH particle types on or off also determines whether or not they will be used in the rendering calculation (i.e., the interpolation to pixels). This particularly applies to ghost particles, where ghost particles will only be used in the rendering if they are turned on via this menu option. (The fact that particles of a given type are SPH particles or not is specified by the \verb+UseTypeInRendering+ flags in the set\_labels part of the read\_data file). \subsubsection{ Plotting non-gas particles on top of rendered plots} An option to plot non-SPH particles on top of rendered plots (e.g. sink particles) can be set when turning particle types on/off via the ``turn on/off particles by type'' option in the particle plot o)ptions submenu (see \S\ref{sec:plotparticlesbytype}). \subsubsection{ Using ghost particles in the rendering} See \ref{sec:plotparticlesbytype}. \subsubsection{ Turn off plotting of gas particles} Particles can be turned on or off by type via the ``turn on/off particles by type'' option in the particle plot o)ptions submenu. See \ref{sec:plotparticlesbytype}. \subsubsection{ Plotting dark matter particles} \label{sec:darkmatter} To plot dark matter particles (e.g. for the gadget read) the particle type corresponding to dark matter particles must be turned on via the ``turn on/off particles by type'' option in the o) submenu. Turning this option on means that dark matter particles will appear on particle plots. To make a rendered plot of dark matter (e.g. showing column density), it is necessary to define smoothing lengths and a fake ``density'' for the dark matter particles. If your data read already supplies individual smoothing lengths for dark matter particles, the only thing to do is define a fake density field with a constant value (e.g. $\rho = 1$ for all dark matter particles). The actual density value does not matter, so long as it is non-zero, as the rendering for density does not use it unless the ``normalise interpolations'' option in the r)ender menu is set (which it is not by default). This is because SPLASH constructs the weight: \begin{equation} w_{part} = \frac{m_{part}}{\rho_{part} h_{part}^{\nu}}, \end{equation} (see \citealt{splashpaper}) and then interpolates for any quantity A using \begin{equation} A_{pixels} = \sum_{part} w_{part} A_{part} W_{kernel}, \end{equation} so if $A = \rho$ then the actual rho value cancels. For the GADGET data read you can define the smoothing length for dark matter particles by setting the environment variable GSPLASH\_DARKMATTER\_HSOFT (see \S\ref{sec:gsplash} for details), which also triggers the creation of a fake density column as required. With this variable set dark matter particles are treated identically to SPH particles and can be rendered as usual (although the only meaningful quantity to render is the density). A much better way is to define smoothing lengths individually for dark matter particles, for example based on a local number density estimate from the relation \begin{equation} h \propto n^{-1/3}, \hspace{0.5cm} \textrm{where} \hspace{0.5cm} n_{i} = \sum_{j} W_{ij}. \end{equation} Actually, none of this should be necessary, as the gravity for dark matter should be softened with smoothing lengths defined like this in the first place. The historical practise of fixed softening lengths has arisen only because of confusion about what softening really means (and worries about energy conservation with adaptive softening lengths). What you are trying to do is solve Poisson's equation for the dark matter density field, defined with a kernel density estimate and using fixed softening lengths is not a way to get a good density... but don't get me started, read \citet{pm07} instead. Note that for simulations using both SPH and dark matter particles, dark matter particles will contribute (incorrectly) to the SPH rendering when the environment variable is set and the plotting of dark matter particles is turned on. Thus to plot just gas column density in this case, dark matter particles must be turned off [via the o) menu option], and similarly to plot just dark matter density if both SPH and dark matter particles are present, SPH particles must be turned off. \subsubsection{ Plotting a column density plot of dark matter/N-body particles} See \ref{sec:darkmatter}. \subsubsection{ Plotting sink particles} \label{sec:plotsinks} Sink particles will be plotted on particle plots once turned on via the ``turn on/off particles by type'' option in the particle plot o)ptions submenu. Setting this option also gives a prompt for whether or not to plot sink particles on top of rendered plots (to which the answer should be yes). See \ref{sec:plotparticlesbytype} for more details. To plot sink particles as a circle scaled to the sink radius, select the appropriate marker type (32-35) in the ``change graph markers for each type'' option in the o) menu. This allows plotting of particles of a given type with circles, filled or open, proportional to their smoothing lengths. Thus, the smoothing length for sink particles needs to be set to their accretion radius (or at least proportional to it). A good option for sinks (v1.15 onwards) is to print ``outlined'' filled circles (marker 34) --- these show up on both black or white backgrounds. \subsubsection{ Plotting sink particles with size proportional to the sink radius} See \ref{sec:plotsinks}. \subsubsection{ Plotting a point mass particle with physical size} See \ref{sec:plotsinks}. \subsubsection{ Changing graph markers for each particle type} The graph markers used to plot each particle type can be changed via the ``change graph markers for each type'' option in the particle plot o)ptions submenu. The full list of available markers is given in the documentation for \giza (also similar to the markers used in \textsc{pgplot}). SPLASH also allows the particles to be marked by a circle proportional to the smoothing length for that particle, implemented as marker types 32-35 under the ``change graph markers for each type'' option in the o) menu. \subsubsection{ Plotting each particle type in a different colour} \label{sec:partcolours} Each particle type can be plotted in a different colour via the ``set colour for each particle type'' option in the particle plot o)ptions submenu (press `o' from the main menu). \subsubsection{ Changing the order in which different particle types are plotted} The order in which particle types are plotted can be changed via the ``change plotting order of types'' option in the particle plot o)ptions submenu. Thus for example it is possible to make dark matter particles be plotted on top of gas particles rather than the default which is vice-versa. Note that at present this is only implemented for particle types which are stored contiguously (one after the other) in the data read, rather than mixed in with each other. \subsubsection{ Plotting using lines instead of dots (e.g. for energy vs time plots)} \label{sec:lines} An option to plot a line joining all of the points on a plot can be set via the ``plot line joining particles'' option in the particle plot o)ptions submenu. When set, this option plots a line connecting the (gas only) particles in the order that they appear in the data array. Useful mainly in one dimension or when plotting ascii data, although can give an indication of the relative closeness of the particles in memory and in physical space in higher dimensions. The line colours and styles can be changed. To plot the line only with no particles, turn off gas particles using the ``turn on/off particles by type option'' from the o) submenu. \subsubsection{ Plotting multiple lines with different colours/line styles and a legend} When multiple timesteps are plotted on the same physical page, the line style can be changed instead of the colour (this occurs when the change colour option is chosen for multiple steps per page -- see the ``change plots per page" option in the p)age options submenu [\S\ref{sec:optionspage}]). \subsubsection{ Joining the dots} See \ref{sec:lines}. \subsubsection{ Plotting the size of the smoothing circle around selected particles} \label{sec:smoothingcircle} On coordinate plots this option plots a circle of radius $2h$ around selected particles. This is primarily useful in debugging neighbour finding routines. Where only one of the axes is a coordinate this function plots an error bar of length $2h$ in either direction is plotted in the direction of the coordinate axis. See also \S\ref{sec:findingaparticle} for more details. \subsubsection{ Locating a particular particle in the data set} \label{sec:findingaparticle} The best way to locate a particular particle in the data set is to use the ``plot smoothing circles'' option in the particle plot o)ptions submenu, e.g: \begin{verbatim} Please enter your selection now (y axis or option):o5 ------------- particle plot options ------------------- Note that circles of interaction can also be set interactively Enter number of circles to draw ([0:100], default=0):1 Enter particle number to plot circle around ([1:959], default=1): 868 \end{verbatim} then upon plotting a coordinate plot (e.g. x vs y), particle 868 will be plotted with a circle of size $2h$ which makes it easy to distinguish from the other particles. See also \S\ref{sec:smoothingcircle}. \subsubsection{ Making sure absolutely all particles are plotted} By default \splash uses ``fast particle plotting'' for particle plots -- this is where the plotting surface is divided into pixels and only a limited number of particles per pixels is plotted (preventing slowdown due to lots of particles being plotted indistinguishably on top of each other). Use of this optimisation can be turned off \emph{just in case} in the particle plot o)ptions submenu, although there should almost never be a good reason to do so. \subsubsection{ Plotting in different coordinate systems (e.g. cylindrical coordinates)} \label{sec:geom} The coordinates of position and of all vector components can be transformed into non-cartesian coordinate systems using the ``change coordinate system'' option in the particle plot o)ptions submenu. For example, a dump file with columns as follows: \begin{verbatim} ------------------------------------------------------- 1) x 6) log density 2) y 7) v\dx 3) z 8) v\dy 4) particle mass 9) v\dz 5) h ------------------------------------------------------- 10) multiplot [ 4 ] m) set multiplot ------------------------------------------------------- Please enter your selection now (y axis or option): \end{verbatim} choosing o), option 7) and choosing cylindrical coordinates then produces; \begin{verbatim} You may choose from a delectable sample of plots ------------------------------------------------------- 1) r 6) log density 2) phi 7) v\dr 3) z 8) v\dphi 4) particle mass 9) v\dz 5) h ------------------------------------------------------- ... \end{verbatim} transforming both coordinates and vectors into the chosen coordinate system. Note that rendering is disabled in coordinate systems other than those native to the file (i.e., anything non-cartesian for you -- part of the reason for this feature was that I was experimenting with SPH in cylindrical and spherical coordinates where the reverse transformation was necessary). For 3D SPH simulations, extra columns will appear in the menu in cylindrical or spherical coordinates allowing plots of azimuthally-averaged surface density and Toomre Q parameter. For more details see \S\ref{sec:surfdens}. Details of the coordinate transformations are given in \S\ref{sec:coordtransforms}. If you have a coordinate system you would like implemented, please email me the details! \subsubsection{ Plotting vector components in different coordinate systems} See \S\ref{sec:geom}. \subsubsection{ Plotting orbital velocities} See \S\ref{sec:geom}. \subsubsection{ Plotting against azimuthal angle/cylindrical radius/etc} See \S\ref{sec:geom}. \subsubsection{ Plotting the exact solution to common test problems} \label{sec:exactsolns} The following exact solutions are provided \begin{itemize} \item Any arbitrary function y = f(x,t) (can be plotted on any or all of the plots). The functions to be plotted can also be specified by creating a \verb+splash.func+ file with one function per line. \item Hydrodynamic shock tubes (Riemann problem) -- a full solution is provided for all types of waves propagating in either direction. \item Spherically-symmetric 3D sedov blast wave problem. \item Polytropes (with arbitrary $\gamma$) \item One and two dimensional toy stars. This is a particularly simple test problem for SPH codes described in \citet{mp04}. \item Linear wave. This simply plots a sine wave of a specified amplitude, period and wavelength on the plot specified. \item MHD shock tubes (tabulated). These are tabulated solutions for 7 specific MHD shock tube problems. \item h vs $\rho$. This is the exact solution relating smoothing length and density in the form $h \propto (m/\rho)^{1/\nu}$ where $\nu$ is the number of spatial dimensions. \item radial density profiles. For various models commonly used in $N-$body simulations. \item Exact solution from a file. This option reads in an exact solution from the filename input by the user, assuming the file contains two columns containing the $x-$ and $y-$ coordinates of an exact solution to be plotted as a line on the plot specified. \end{itemize} Details of the calculation of the exact solutions are given in Appendix~\ref{sec:exact}. An example plot using the Sedov blast wave exact solution is shown in Figure~\ref{fig:sedov}. \begin{figure}[h] \begin{center} \includegraphics[width=0.5\textwidth]{sedov_example.png} \caption{Example of a plot utilising the Sedov blast wave exact solution. Taken from Rosswog \& Price (2007).} \label{fig:sedov} \end{center} \end{figure} \subsubsection{ Plotting an exact solution from a file} See \S\ref{sec:exactsolns}. One of the options for exact solution plotting is to read the exact solution from either one or a sequence of ascii files, such that the results are plotted alongside the particle data. The filename(s) can be specified by the user and will be saved to the `splash.defaults' file so that the solution(s) will be read and plotted on subsequent invocations of \splash. \subsubsection{ Changing the exact solution line style \& colour} The line style and colour of the exact solution line can be changed via the ``exact solution plot options'' option in the o) submenu. This option can also be used to turn on/off calculation of various error norms together with an inset plot of the residual error on the particles. See Appendix~\ref{sec:exact} for details of the error norms calculated. \subsubsection{ Setting the number of points used in an exact solution calculation} The number of points used in an exact solution calculation can be changed via the ``exact solution plot options'' option in the o) submenu. \subsubsection{ Plotting an inset plot of residual errors from an exact solution} An inset plot of residual errors between the plotted points and an exact solution calculation can be turned on via the ``exact solution plot options'' option in the o) submenu. \subsection{plot (l)imits}%HEVEA\cutname{lmenu.html} \subsubsection{ Using plot limits which adapt automatically for each new plot} \label{sec:adapt} Adaptive plot limits can be set using option 1 of the l)imits menu (press 'l' from the main menu, then '1'). Different settings can be applied to coordinate axes and non-coordinate axes. Note that changing plot limits interactively and pressing 's' in interactive mode will change this option back to using fixed limits. \subsubsection{ Using adaptive plot limits for the colour bar but not for the coordinates} Adaptive plot limits can be set individually for coordinate axes and non-coordinate axes (e.g. the colour bar) via the ``use adaptive/fixed limits'' option in the l)imits submenu. See \S\ref{sec:adapt}. \subsubsection{ Setting plot limits manually} Plot limits can be set manually using option 2) of the l)imits menu (or simply ``l2'' from the main menu). Alternatively you can edit the `splash.limits' file created by a S)ave from the main menu prior to invoking \splash (this file simply contains the minimum and maximum limits for each column on consecutive lines). \subsubsection{ Making plot limits relative to a particular particle} \label{sec:track} Particle tracking limits (i.e., where a chosen particle is always at the centre of the plot and limits are set relative to that position) can be set via the ``make xy limits relative to particle'' option in the l)imits menu. Alternatively particle tracking limits can be set interactively by pressing 't' in interactive mode with the cursor over the particle you wish to track. Note that this option only works if particle identities are preserved between timesteps. Also note that, with particle tracking limits set, the radius calculated via the ``calculate extra quantities'' option in the d)ata submenu is calculated relative to the tracked particle. Centreing on a sink particle can also be achieved using the SPLASH\_CENTRE\_ON\_SINK environment variable. \subsubsection{ Plotting in a comoving reference frame} A co-moving reference frame can be set using the ``make xy limits relative to particle'' option in the l)imits menu. Coordinate limits are then centred on the selected particle for all timesteps, with offsets as input by the user. This effectively gives the `Lagrangian' perspective. See \S\ref{sec:track} for more details. Centreing on a sink particle can also be achieved using the SPLASH\_CENTRE\_ON\_SINK environment variable. \subsubsection{ Setting the origin to correspond to a particular particle} See \S\ref{sec:track}. \subsubsection{ Tracking a particle} See \S\ref{sec:track}. \subsubsection{ Setting the origin to the position of the $n$th sink particle} \label{sec:tracksink} This can be achieved using the ``make xy limits relative to particle'' option in the l)imits menu. For example, to track the first sink particle we would proceed as follows: \begin{verbatim} Please enter your selection now (y axis or option):l3 ------------------ limits options --------------------- To track particle 4923, enter 4923 To track the 43rd particle of type 3, enter 3:43 Enter particle to track: (default="0"): 3:1 \end{verbatim} where 3:1 indicates the first particle of type 3. The origin is set to the position of this particle and limits are relative to its position. See \S\ref{sec:track} for more details. \subsubsection{ Plotting radial plots around sink particles} First, set the origin to the location of the sink, as described above. Then simply change to spherical coordinates using the ``change coordinate systems'' option in the o) menu. Alternatively, compute the radius using the ``calculate extra quantities'' option in the d)ata menu. \subsubsection{ Automatically adapting plot limits to match aspect ratio of output device} An option to automatically adjust the plot limits to match the aspect ratio of the output device is given in the l)imits menu, and is also prompted for whenever the paper size is changed (via the ``change paper size'' option in the p)age menu, see \S\ref{sec:papersize}). \subsubsection{ Plotting with log axes.} Log axes can be set either interactively (by pressing 'l' with the cursor over the desired axis) or manually via the ``apply log or inverse transformations to columns'' option in the l)imits menu. To use logarithmic axes labels as well, see \S\ref{sec:loglabels}. \subsubsection{ Plotting the square root, inverse or square of a quantity} Columns can be logged, inverted, sqrt-ed, squared or any combination of the above via the ``apply log or inverse transformations to columns'' option in the l)imits menu. If you have any additional transformations you would find useful please let me know, as it is straightforward to add more. \subsubsection{ Resetting limits for all columns} \label{sec:resetlimits} Limits for all columns can be reset to their minimum and maximum values from the current dump file via the ``reset limits for all columns'' option in the l)imits menu. See \S\ref{sec:interactive} for details of resetting plot limits for a particular plot in interactive mode. \subsubsection{ Restoring all plot limits to their minimum and maximum values in the current dump file} See \S\ref{sec:resetlimits}. \subsubsection{ Using a subset of data restricted by parameter range} \label{sec:rangerestrict} As of version 1.11.0, it is possible to use only a subset of the particles in both particle plots and rendered plots, according to restrictions on any or all of the data columns (for example, using only particles with $\rho > 10$, in the 3D box $x,y,z \in [-0.1, 0.1]$). Whilst this has always been possible by selecting, colouring and/or hiding particles in interactive mode (see \S\ref{sec:rendersubset}), the difference here is that the selection is based, for each timestep, strictly on the parameter range, rather than being a selection based on particle identity. This means that the parameter range is also saved to the \verb+splash.limits+ (i.e., by pressing `S' from the main menu) and is shown when \splash launches via lines such as: \begin{verbatim} >> current range restrictions set: ( 1.693E-01 < x < 1.820E-01 ) ( 2.205E-01 < y < 2.265E-01 ) ( 7.580E-06 < density < 2.989E-05 ) >> only particles within this range will be plotted and/or used in interpolation routines \end{verbatim} or more usually: \begin{verbatim} >> no current parameter range restrictions set \end{verbatim} Parameter range restrictions can be set either manually via the l)imits menu (option 7) or interactively by selecting a region in the plot and pressing `x', `y' or `r' to restrict using the $x$, $y$ or both $x$ and $y$ limits of the selected area respectively (pressing `R' instead removes all currently set restrictions). Another way of setting manual range restrictions is simply to edit the \verb+splash.limits+ file directly (this simply contains the min and max limits for each column, followed optionally by a third and fourth column specifying, respectively, the min and max of the range restriction). \subsubsection{ Plotting only particles with $\rho > 10$, $u > 20$ and $-0.25 < x < 0.25$} Plotting a subset of the particles restricted by a parameter can be achieved by setting a parameter range restriction (which does not change between timesteps -- see \S\ref{sec:rangerestrict}), or alternatively by an interactive selection based on particle identity (see \S\ref{sec:rendersubset}). \subsection{(r)endering options}%HEVEA\cutname{rmenu.html} \subsubsection{ Changing the number of pixels in a rendered image} The number of pixels in a rendered image can be set manually using the r)ender menu, option 1 (or simply type ``r1'' from the main menu). The number set is the number of pixels along the $x-$axis. The number of pixels along the $y-$axis is determined by the aspect ratio of the plot. As of version 1.11.1, the number of pixels used in an image is, by default, automatically determined by the actual number of pixels available on the graphics device, which depends in turn on the size of the page (the page size can be set manually in the p)age menu -- see \S\ref{sec:papersize}). For pixel devices use of the automatic pixel number determination is \emph{highly} recommended (hence why it is the default) to avoid interpolation artefacts in the image. For vector (non-pixel) devices such as postscript, svg or pdf, the number of pixels is set to $1024/\textrm{n}$, where n is the number of panels across the page. \subsubsection{ Changing the colour scheme} The colour scheme used for rendered plots can be changed either by pressing `m' or `M' in interactive mode to cycle through the available schemes or manually by using the ``change colour scheme'' option in the r)ender menu. A demonstration of all the colour schemes can be also be invoked from this menu option. Setting the colour scheme to zero plots only the contours of the rendered quantity (assuming that plot contours is set to true). The colour schemes available are shown in Figure~\ref{fig:colourschemes}. \begin{figure} \begin{center} \includegraphics[angle=270,width=\textwidth]{colourschemes.pdf} %%\begin{turn}{270}\epsfig{file=colourschemes.ps,height=\textwidth}\end{turn} \caption{\splash colour schemes} \label{fig:colourschemes} \end{center} \end{figure} User contributed colour schemes are eagerly invited (just send me either: a table of r,g,b colour indices [if you know them] or just an image of a colour bar you wish to reproduce and I will add it). \subsubsection{ Plotting contours as well as the rendered image} Contours of either the rendered pixel array or of another (separate) quantity can be plotted on top of the rendered plot by setting the ``plot contours'' option from the r)ender menu. With this option set, an extra prompt will appear after the render prompt asking the user for a quantity to be contoured. The contoured quantity can also be set via the command line options (\S\ref{sec:commandline}). If the rendered and contoured quantities are the same, further prompts appear which enable the limits for the contour plot to be set separately to the render plot. These limits are also saved separately in the \verb+splash.limits+ file when written. To plot contours \emph{instead} of the rendered image, use the ``change colour scheme'' option from the r)ender menu and choose colour scheme 0 (contours only). \subsubsection{ Plotting contours instead of a rendered image} To plot contours instead of the rendered image, use the ``change colour scheme'' option from the r)ender menu and choose colour scheme 0 (contours only). \subsubsection{ Changing the number of contour levels} The number of contour levels used whenever contours are drawn can be set via the ``change number of contours'' option in the r)ender menu. The contour levels can also be manually specified (see \S\ref{sec:contoursmanual}). \subsubsection{ Setting the contour levels manually} \label{sec:contoursmanual} As of v1.15.0, contour levels can be set manually by creating a file called \verb+splash.contours+ in the current directory (or \verb+prefix.contours+ if the \verb+splash -p prefix+ is specified on the command line). This file should contain one contour level per line, optionally with a label for each contour, e.g. \begin{verbatim} 1.e-2 level 1 1.e-1 level 2 0.1 my really great contour 1.0 hi mum \end{verbatim} \subsubsection{ Adding numeric labels to contours} An option to write numeric labels on contours appears as part of the ``change number of contours'' option in the r)ender menu. \subsubsection{ Adding arbitrary contour labels} Contours can also be labelled manually by creating a \verb+splash.contours+ file. See \S\ref{sec:contoursmanual}. \subsubsection{ Turning the colour bar off/ moving the colour bar label} The colour bar can be turned on or off and the style chosen (e.g. horizontal vs vertical) and for the vertical bar, the label moved closer to the bar itself, via the ``colour bar options'' option in the r)ender menu. To change the text in the colour bar label, see \S\ref{sec:setprojlabel}. \subsubsection{ Changing the style of the colour bar} \label{sec:colourbarstyle} The colour bar style (i.e., vertical vs. horizontal, plot-hugging vs. non plot-hugging, one-sided vs. two-sided, floating vs. fixed) can be changed via the ``colour bar options'' option in the r)ender submenu. If you want a different style implemented, email me! \subsubsection{ Using a horizontal colour bar} An option to use a horizontal colour bar instead of the default vertical arrangement is given in the ``colour bar options'' option in the r)ender submenu. \subsubsection{ Using `plot-hugging' colour bars} See \S\ref{sec:colourbarstyle}. \subsubsection{ Using floating/inset colour bars} See \S\ref{sec:colourbarstyle}. \subsubsection{ Plotting ticks on only one side of the colour bar} See \S\ref{sec:colourbarstyle}. \subsubsection{ Changing the text in the colour bar label} See \S\ref{sec:setprojlabel}. \subsubsection{ Using coloured particles instead of rendering to pixels} \label{sec:colournotrender} As a simpler alternative to interpolating to a pixel array, particles can simply be coloured according to the value of a particular quantity by setting the ``use particle colours not pixels'' option in the r)ender menu. With this option set, rendered plots are simply plotted by colouring the particles according to the rendered field. This is somewhat cruder but can be a good indication of where individual particles might be affecting results. Note that any colouring of the particles set in interactive mode will be overwritten by use of this option. \subsubsection{ Using normalised interpolations} A normalised interpolation to pixels can be used by setting the ``normalise interpolations'' option from the r)ender menu. In general this leads to smoother rendering but also means that edges and surfaces appear more prominently (and a bit strange). The general rule-of-thumb I use is therefore to use this option whenever there are no free surfaces in the simulation. Note that in 3D this option only affects cross-section slices (as it is a bit meaningless to normalise a column-integrated or opacity-rendered plot). \subsubsection{ Speeding up the rendering on 3D column integrated plots} Interpolation on 3D column integrated plots can be made faster by setting the ``use accelerated rendering'' option in the r)ender menu. The reason this is an option is that it makes a small approximation by assuming that each particle lies exactly in the centre of a pixel. In general this works very well but is not set by default because it can produce funny looking results when the particles are aligned on a regular grid (e.g. as is often the case in initial conditions). Typical speed-ups range from $\times 2$ up to $\times 4$, so it is highly recommended for interactive work. \subsubsection{ Using density weighted interpolation} Density weighted interpolation (where a quantity is plotted times $\rho$) can be turned on in the r)ender menu. \subsubsection{ Selecting and rendering only a subset of the particles} An example of how to render using only a selected subset of the particles was given in \S\ref{sec:rendersubset}. \subsubsection{ Changing the label used for 3D projection plots} \label{sec:setprojlabel} The labelling scheme used to determine the colour bar label can be changed via the ``customize label on projection plots'' option in the r)ender menu. Information specific to the quantity being rendered can be incorporated via format codes as follows: \begin{verbatim} Example format strings: \(2268) %l d%z %uz : this is the default format "\int rho [g/cm^3] dz [cm]" column %l : would print "column density" for density surface %l : would print "surface density" %l integrated through %z : would print "density integrated through z" Format codes: %l : label for rendered quantity %z : label for 'z' %uz : units label for z (only if physical units applied) \end{verbatim} \subsubsection{ Changing ``column density'' to ``surface density'' on 3D plots} See \S\ref{sec:setprojlabel}. \subsubsection{ Changing the interpolation kernel} The kernel used for the interpolations is by default the M$_{4}$ cubic B-spline, which has been standard in SPH calculations since the mid-1980's. Other kernels can be selected via the ``change kernel'' option in the r)ender menu. The kernel can also be changed by setting the \verb+SPLASH_KERNEL+ environment variable to either the kernel name as listed in the render menu option, or something sensible resembling it. At present only a few kernels are implemented, with `cubic' , `quartic' and `quintic' referring to the M$_{4}$, M$_{5}$ and M$_{6}$ B-splines with support of 2h and 3h, respectively. See \citet{price12} for more details. \subsection{(v)ector plot options}%HEVEA\cutname{vmenu.html} \label{sec:vectorplots} \subsubsection{ Changing the number of arrows on vector plots} See \S\ref{sec:vecpix}. \subsubsection{ Changing the number of pixels in vector plots} \label{sec:vecpix} The number of pixels used on vector plots can be changed via the ``change number of pixels'' option in the v)ector menu. This controls the number and average size of the arrows which appear (i.e., one arrow is plotted at the centre of each pixel). \subsubsection{ Changing the size of arrows on vector plots} The size of the arrows on vector plots is proportional to the magnitude of the vector quantity at that pixel, where the maximum size is set from the maximum plot limit for the x, y and z components of the vector quantity being plotted such that the longest arrow fills one pixel. These limits can be changed manually via the l)imits menu options. Where these limits are nowhere near the actual values of the vector field, arrows can appear either very big (just a line across the screen) or extremely small (appearing as just dots). Pressing `w' in interactive mode automatically adjusts the arrows to sensible proportions (this is the equivalent of pressing `a' for non-vector quantities). Alternatively pressing `v' (to decrease) or `V' (to increase) can be used to adjust the arrow lengths (the change can be multiplied by 10 or more by first pressing `z' one or more times before pressing 'v' or 'V'). \subsubsection{ Plotting vector arrows in white instead of black or vice-versa} Vector arrows are by default plotted using the current foreground colour index (i.e., as used for plotting the axes). To plot in the background colour index instead set the ``use background colour for arrows'' option in the v) menu. \subsubsection{ Turning off the legend for vector plots} The legend which appears on vector plots can be turned on or off via the ``vector plot legend settings'' option in the v) menu. \subsubsection{ Moving the vector plot legend} The position of the vector plot legend can be set either interactively by positioning the mouse and pressing 'H' or manually via the ``vector plot legend settings'' option in the v) menu. \subsubsection{ Plotting stream/fieldlines instead of arrows} To plot a vector plot that uses stream/fieldlines instead of arrows, set the ``plot stream/field lines instead of arrows'' option in the v) menu. This option performs a simple integration of the interpolated vector field to get the stream function, the contours of which are then plotted (note that the number of contours can be changed via the ``change number of contours'' option in the r)ender menu). It is generally advantageous to use a larger number of pixels for the vector interpolation (See \S\ref{sec:vecpix}) to get smooth contours. At present this option works quite well for smooth vector fields but can perform poorly for vector fields with strong gradients. \subsubsection{ Turning arrow heads off for vector plots} Vector plots can be plotted using arrows without heads using the ``turn arrow heads on/off'' option in the v)ector plot options menu. \subsubsection{ Hiding vector arrows where there are no SPH particles} On rendered plots often arrows can appear where there are apparently no SPH particles because the interpolation is performed to all pixels within $2h$ of an SPH particle. Such arrows in regions of few or no particles can be hidden using the ``hide arrows where there are no particles'' option in the v) menu. A threshold number of particles for each pixel can be specified, below which no arrow will be plotted on that pixel. \subsubsection{ Plotting a vector plot in a cross section slice} Vector plots are either in a cross section slice or are column integrated projections depending on the setting of the ``switch between cross section/projection'' option in the x) menu. Setting this to cross section and plotting a vector plot produces a vector plot in a cross section slice. \subsubsection{ Making all arrow the same length (i.e., showing direction only, not magnitude)} An option to plot all vector arrows of the same length (instead of the default option where the length of the arrow is proportional to the vector magnitude) can be set from the v) menu. \subsection{(x) cross section/3D plotting options}%HEVEA\cutname{xmenu.html} \subsubsection{ Plotting a cross section slice through 3D data} When plotting a rendered plot of 3D data, the default option is to plot a column-integrated plot. To change this to a cross section slice, use option 1) in the x) menu (``switch between cross section/projection''). See \S\ref{sec:basic} for examples of how this works. An oblique cross section slice can be set interactively using the 'x' key, see \S\ref{sec:obliquexsec} which works by setting a combination of rotation and a cross section slice position. \subsubsection{ Plotting a cross section line through 2D data} In 2D, setting the ``switch between cross section/projection'' option in the x) menu to cross section means that rendered plots are in fact a 1D cross section (i.e., a line) through 2D data. The position of the line is completely arbitrary (i.e., can be set for oblique cross sections as well as straight lines) and is set interactively after the usual $y-$ and $x-$ axis prompts. \subsubsection{ Rotating the particles} An angle of rotation about may be set each axis may be set in the x)sec/rotate submenu using the ``rotation on/off/settings'' option or interactively (press 'h' in interactive mode to see the exact keystrokes). The position of the origin about which particles are rotated can be set from the ``rotation on/off/settings'' option in the x) menu. Rotated axes or boxes can be plotted using the ``set axes for rotated/3D plots'' option in the same menu. Rotations are performed in the order $z-y-x$. This means that the $y-$ rotation angle is an angle about the \emph{new} $y-$axis, defined by the $z$ rotation and similarly for the $x-$ rotation. If you think about it long enough, it makes sense. If in doubt, do it interactively and set the angles in the order $z-y-x$. \subsubsection{ Setting the origin about which particles are rotated} The origin about which particles are rotated and relative to which the radius is calculated when the ``calculate extra quantities'' option is set in the d)ata menu can be changed via the ``rotation on/off/settings'' option in the x) menu. \subsubsection{ Adding 3D perspective} \label{sec:3Dperspective} 3D perspective can be turned on via the ``3D perspective on/off'' option in the x) menu. Prompts for setting the perspective position then appear after the usual prompts for y and x axes, rendering and vector plots, i.e., something like the following: \begin{verbatim} Please enter your selection now (y axis or option):2 (x axis) (default=1): (render) (0=none) ([0:20], default=0): (vector plot) (0=none, 7=B, 10=v, 17=J) ([0:17], default=0): enter z coordinate of observer (default=1.800): enter distance between observer and projection screen ([0.000:], default=0.1800): Graphics device/type (? to see list, default /xwin): \end{verbatim} 3D perspective is defined by two parameters: a distance to the observer $zobs$ and a distance between the observer and a screen placed in front of the observer, $dscreen$. The transformation from usual $x$ and $y$ to screen $x'$ and $y'$ is then given by \begin{eqnarray} x' & = & x*dscreen/(zobs-z), \nonumber \\ y' & = & y*dscreen/(zobs-z). \end{eqnarray} This means that objects at the screen distance will have unit magnification, objects closer than the screen will appear larger (points diverge) and objects further away will appear smaller (points converge). The situation could be beautifully illustrated if I could be bothered drawing a figure. I have found reasonable results with something like a $1/10$ reduction at the typical distance of the object (i.e., observer is placed at a distance of $10\times$ object size with distance to screen of $1\times$ object size). \splash sets this as default using the z plot limit as the `object size'. The position of the 3D observer in $z$ can also be changed in interactive mode using 'u' or 'U' (to move 'up') and 'd' or 'D' (to move 'down'). \subsubsection{ Using 3D surface rendering} 3D surface rendering (turned on using the ``3D surface rendering on/off'' option in the x) menu) performs a ray-trace through the particle data, thus visualising the "last scattering surface". When set, the user is prompted for an "optical depth" before plotting which determines the position of the surface. Only applies to 3D data. When set with cross-section (instead of projection), particles at or below the z value of the slice are used. %The one caveat is that the rendering algorithm needs to use more than 256 colours (for example, if we have a colour map with 256 colours, we need to then have all 256 colours at varying levels of intensity), at present this is implemented by %(optionally) writing the pixel map directly to a .ppm file (called splash\_\#\#\#\#\#.ppm where the number is the number of the current dumpfile) and the approximate version (i.e., without the background fade) %is given to the graphics device (e.g. the /xw screen). The .ppm files are uncompressed and therefore somewhat large but can be converted easily to any %other format (e.g. png, ps) using netpbm tools (\url{http://netpbm.sourceforge.net/}) such as %ppmtopng, ppmtops. For examples of the 3D surface rendering in \splash, have a look at my movies of neutron star mergers: \begin{quote} \url{http://users.monash.edu.au/~dprice/research/nsmag}. \end{quote} \subsubsection{ Plotting 3D box / 3D axes} Rotated axes or boxes can be plotted using the ``set axes for rotated/3D plots'' option in the x) menu. \subsubsection{ Setting up animation sequences} \label{sec:animseq} Animation sequences can be set via the ``set animation sequence'' option in the x) menu. At present the possible sequences that can be added are: \begin{verbatim} 1 : steady zoom on x and y axes 2 : steady rotation 3 : steady change of limits (e.g. for colour bar) 4 : steady movement of 3D observer 5 : sequence of cross section slices through a 3D box 6 : steady change of opacity for 3D surface plots \end{verbatim} Up to one sequence of each type can be added (i.e., up to 6 in total) with different start and end points (specified in terms of dump file number), with the additional possibility of inserting extra frames between dump files (e.g. to plot a sequence of frames consisting of a changing view of the same dump file). Animation sequences can also be set using `e' in interactive mode. To set a sequence interactively first adjust the plot settings to correspond to the start of the sequence (pressing `s' to save if this is done in interactive mode). Then in interactive mode move to the dump file you want to be the end-point and also adjust the plot settings to correspond to the end-point of your desired sequence (i.e., adjust the colour bar limits and/or adjust the rotation angle and/or the x/y limits and/or the 3D observer position and/or the opacity). Then, rather than pressing `s' (which would make these become the default plot settings) press `e' instead, saving these settings as the end-point of the desired animation sequence. This can be done multiple times to set multiple sequences. Animation sequences set up in this manner are saved to a file called \verb+splash.anim+ either when prompted (if setting sequences non-interactively) or by pressing 'S' from the main menu which then saves both the \verb+splash.limits+ and \verb+splash.anim+ files in addition to the usual \verb+splash.defaults+ file. \textbf{Note:} As of version 1.11.1, animation sequences act on a `per page' basis rather than simply `per frame'. This means that you can produce a multi-panelled movie (e.g.) showing the evolution of different runs side by side, with the same animation sequence applied to each. \subsubsection{ Plotting a sequence of frames rotating a data set through 360 degrees} This can be achieved by setting an animation sequence with a steady change of rotation angle. See \S\ref{sec:animseq}. \subsubsection{ Plotting a `fly-around' of 3D data} This can be achieved by setting an animation sequence with a steady change of rotation angle. See \S\ref{sec:animseq}. \subsubsection{ Plotting a flythru of 3D data} A sequence of cross section slices progressively deeper into a 3D box or alternatively a steady movement of the 3D observer (on projection plots) can be plotted by setting up an animation sequence. See \S\ref{sec:animseq} for details. \subsubsection{ Adding a steady zoom sequence to a movie} A steady change of $x-$ and $y-$ limits can be added by setting up an animation sequence. See \S\ref{sec:animseq} for details. \subsubsection{ Adding a steady change of colour bar limits} A steady change of limits on the colour bar over one or more dump files for a movie can be implemented by setting up an animation sequence. See \S\ref{sec:animseq} for details. \subsubsection{ Adding steady movement of the 3D observer} \label{sec:move3Dobserver} The position of the 3D observer can be steadily changed over several dump files (or several frames produced of the same dump file) by setting up an animation sequence. See \S\ref{sec:animseq} for details. \subsection{Miscellaneous other useful things}%HEVEA\cutname{misc.html} \subsubsection{ Saving plot settings / plot limits to disk} The (s)ave option saves the default options to a file called `splash.defaults' in the current directory which is read automatically upon the next invocation of \splash. This file uses namelist formatting and may be edited manually prior to startup if so desired (this is quite useful for setting multiplots with many plots per page). The (S)ave option writes both the defaults file and also saves the current plot limits to a file called `splash.limits' which is also read automatically at startup. If animation sequences have been set (see \S\ref{sec:animseq}), this also saves the `splash.anim' file to disk. Typing ``sa'' or ``Sa'' gives a ``save-as'' option, whereby the prefix of files saved by \splash can be changed (e.g. using ``sa'' the defaults file can be renamed, whereas using ``Sa'' all files saved by \splash are given a new prefix). The prefix to the configuration files which are written by \splash can also be changed using the \verb+-p+ option on the command line (the default is ``splash'', i.e., ``splash.defaults'', ``splash.limits'' etc). \subsubsection{ My attempt at in-built help} The (h)elp option at the moment does nothing particularly useful apart from tell you about menu shortcuts (see \S\ref{sec:menushortcuts}). It seemed like a good idea at the time\ldots \subsubsection{ Keyboard shortcuts to menu options} \label{sec:menushortcuts} Menu options which normally require two keystrokes (e.g. x menu, option 1) can be shortcut to by simply typing the letter and number together at the main menu prompt (so e.g. ``x1'' for x menu, option 1, ``r2'' for render menu, option 2, etc.). This can be quite useful if you are playing around with one particular option a lot. \subsubsection{ Exiting \splash} (q)uit, unsurprisingly, quits. Typing a number greater than the number of data columns also exits the program (e.g. I often simply type 99 to exit). %HEVEA\cutend \section{Advanced plotting examples}%HEVEA\cutname{tutorials.html} \subsection{Rendered plot of star formation data} This is an example using data provided by Paul Clark. The data is from an SPH simulation of star formation in sphNG format. We read the dump file as follows: \begin{verbatim} dprice/dustfrag> ssplash omuk162 \end{verbatim} after which we get output along the lines of: \begin{verbatim} ... reading single dumpfile >>>>>>>>>>>>>>>>>>>>>>>>>> omuk162 <<<<<<<<<<<<<<<<<<<<<<<<<< double precision dump File ID: FHydroRTMHD1 npart = 11744854 ... \end{verbatim} and arrive at the main menu: \begin{verbatim} You may choose from a delectable sample of plots ------------------------------------------------------- 1) x 7) v\dx 2) y 8) v\dy 3) z 9) v\dz 4) particle mass 10) u 5) h 11) grad h 6) density ------------------------------------------------------- 12) multiplot [ 4 ] m) set multiplot ------------------------------------------------------- d(ata) p(age) o(pts) l(imits) le(g)end h(elp) r(ender) v(ector) x(sec/rotate) s,S(ave) q(uit) ------------------------------------------------------- Please enter your selection now (y axis or option): \end{verbatim} here we want to plot a rendered plot of column density (density is in column 6), so we type `2' for column 2 (y) as the y axis, `1' for column 1 (x) as the x-axis and at the render prompt `6', for density, ie: \begin{verbatim} Please enter your selection now (y axis or option):2 (x axis) (default=1): (render) (0=none) ([0:11], default=0):6 (vector plot) (0=none, 7=v) ([0:7], default=0):0 Graphics device/type (? to see list, default /xw): /xw \end{verbatim} producing the plot shown in Figure~\ref{fig:starpart1} -- somewhat black! The main thing to note is the limits on the colour bar (extending from $0$ to $10^{7}$ on a linear scale) which is the main source of all the blackness. Moving the cursor over the colour bar and pressing `l' for log produces the figure shown on the right hand side --- a vast improvement! \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{starpart1.png} & \includegraphics[width=0.5\textwidth]{starpart2.png} \end{tabular} \caption{First stage in the star formation figure tutorial: a simple render plot of density (left) and with a log axis after having placed cursor over colour bar and pressed `l' (right)} \label{fig:starpart1} \end{center} \end{figure} For this visualisation we will eventually want the data in physical units rather than code units. For the sphNG read these units are already specified in the read\_data routine, so all we have to do is turn physical units on. Pressing `q' from interactive mode (that is, with the cursor in the plot window) returns us to the main menu. Physical units are turned on from the d)ata menu, as follows: \begin{verbatim} Please enter your selection now (y axis or option):d ----------------- data read options ------------------- 0) exit 1) read new data /re-read data 2) change number of timesteps used ( 1 ) 3) plot selected steps only ( OFF ) 4) buffering of data on/off ( OFF ) 5) turn calculate extra quantities on/off ( OFF ) 6) use physical units ( OFF ) 7) change physical unit settings enter option ([0:7], default=0):6 current settings for conversion to physical units are: x [cm] = x x 1.000E+17 y [cm] = y x 1.000E+17 z [cm] = z x 1.000E+17 particle mass [g] = particle mass x 1.991E+33 h [cm] = h x 1.000E+17 density [g/cm\u3\d] = density x 1.991E-18 v\dx [cm/s] = v\dx x 3.645E+04 v\dy [cm/s] = v\dy x 3.645E+04 v\dz [cm/s] = v\dz x 3.645E+04 u [erg/g] = u x 1.328E+09 grad h = grad h x 1.000E+00 time = time x 1.69E+00 Use physical units? (default=yes): \end{verbatim} returning us to the main menu with labels changed as follows: \begin{verbatim} You may choose from a delectable sample of plots ------------------------------------------------------- 1) x [cm] 7) v\dx [cm/s] 2) y [cm] 8) v\dy [cm/s] 3) z [cm] 9) v\dz [cm/s] 4) particle mass [g] 10) u [erg/g] 5) h [cm] 11) grad h 6) log density [g/cm\u3 ------------------------------------------------------- 12) multiplot [ 4 ] m) set multiplot ------------------------------------------------------- d(ata) p(age) o(pts) l(imits) le(g)end h(elp) r(ender) v(ector) x(sec/rotate) s,S(ave) q(uit) ------------------------------------------------------- Please enter your selection now (y axis or option): \end{verbatim} at this stage we will save the current settings to file by pressing `s' from the main menu. \begin{verbatim} Please enter your selection now (y axis or option):s default options saved to file splash.defaults \end{verbatim} Actually we would prefer the column labels in AU, but we will come to that later. Replotting the same plot (that is 2, 1, 6, 0, /xw from the main menu) plots the same plot we had before, but with the axes in physical units. Zooming in (using the mouse) on the region of interest and adapting the colour bar limits by moving the mouse over the colour bar and pressing `a' produces the plot shown in the left panel of Figure~\ref{fig:starpart2}. \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{starpart3.png} & \includegraphics[width=0.5\textwidth]{starpart4.png} \end{tabular} \caption{Second stage in the star formation figure tutorial: having applied physical units, zooming in and pressing `a' on the colour bar (left) and having changed the colour scheme (right)} \label{fig:starpart2} \end{center} \end{figure} For this kind of plot, the Bate colour scheme looks better -- pressing `m' with the mouse in the plot window changes the colour scheme, producing the plot shown in the right hand panel of Figure~\ref{fig:starpart2}. Pressing `s' in interactive mode (that is, with the mouse in the plot window) saves the current zoom and colour bar settings (but not to disk until you also press `S' from the main menu). Pressing `q' from interactive mode returns to the main menu. Next we want to turn on the plotting of sink particles (all particle types other than gas are turned off by default). This is done in the o)ptions submenu as follows: \begin{verbatim} Please enter your selection now (y axis or option):o ------------- particle plot options ------------------- 0) exit 1) turn on/off particles by type ( ON, OFF, OFF, OFF ) 2) change graph markers for each type ( 1, 4, 17, 1 ) 3) set colour for each particle type ( -1, -1, -1, -1 ) 4) plot line joining particles ( OFF ) 5) plot smoothing circles ( 0 ) 6) use fast particle plotting ( ON ) 7) change coordinate systems ( 1 ) 8) plot exact solution ( 0 ) 9) exact solution plot options enter option ([0:9], default=0):1 Plot gas particles? (default=yes): Plot ghost particles? (default=no): Plot sink particles? (default=no):y >> Plot sink particles on top of rendered plots? (default=no):y Plot unknown/dead particles? (default=no): \end{verbatim} Repeating our previous plot (i.e., 2, 1, 6, 0, /xw) produces the plot shown in the left panel of Figure~\ref{fig:starpart3}. \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{starpart5.png} & \includegraphics[width=0.5\textwidth]{starpart6.png} \end{tabular} \caption{Third stage in the star formation figure tutorial: having turned sink particle plotting on (left) replacing the axes with a scale (right)} \label{fig:starpart3} \end{center} \end{figure} The axes in [cm] are kind of ugly, so we could either change this to a sensible unit or plot a scale instead. We will do the latter. The axes can be turned off in the p)age submenu, as follows: \begin{verbatim} Please enter your selection now (y axis or option):p ---------------- page setup options ------------------- ... 2) axes options ( 0) ... enter option ([0:8], default=0):2 -4 : draw box and major tick marks only; -3 : draw box and tick marks (major and minor) only; -2 : draw no box, axes or labels; -1 : draw box only; 0 : draw box and label it with coordinates; 1 : same as AXIS=0, but also draw the coordinate axes (X=0, Y=0); 2 : same as AXIS=1, but also draw grid lines at major increments of the coordinates; 10 : draw box and label X-axis logarithmically; 20 : draw box and label Y-axis logarithmically; 30 : draw box and label both axes logarithmically. enter axis option ([-4:30], default=0):-2 axis = -2 \end{verbatim} The option to plot a scale of a particular length is also to be found in the le(g)end menu. We will choose to plot a scale of length 0.1 pc. \begin{verbatim} Please enter your selection now (y axis or option):g ---------------- legend and title options ------------------- To set the plot titles, create a file called 'splash.titles' in the working directory, with one title per line 0) exit 1) time legend on/off/settings ( ON 0.87 1.87 0.00 "t=") 2) titles on/off/settings ( ON 0.20 -0.92 0.00) 3) legend for multiple steps per page on/off ( OFF ) 4) plot scale on co-ordinate plots ( OFF ) 5) legend only on nth panel/first row/column ( 0 ) Enter option ([0:5], default=0):4 Plot scale on co-ordinate plots? (default=no):y Enter length of scale in the current x,y,z units (default=1.000):3.0856e15 Enter text to appear below scale (e.g. '10 AU') (default=1 unit): 0.1 pc Enter horizontal position as fraction of viewport ([0.000:1.000], default=0.5000): Enter vertical position in character heights above bottom (default=1.000): \end{verbatim} Note that because the x axis units were already in cm, we simply entered the value for 0.1pc in these units. Before plotting again, we should save what we have done so far to disk: Pressing `S' from the main menu saves both the current plot settings \emph{and} the plot limits to disk: \begin{verbatim} Please enter your selection now (y axis or option):S default options saved to file splash.defaults saving plot limits to file splash.limits \end{verbatim} Plotting our figure again (2-1-6-0-/xw) produces the plot shown in the right hand panel of Figure~\ref{fig:starpart3}. Nearly there...! To add the finishing touches we want to increase the number of pixels substantially. This is done in the r)ender menu, option 1, for which we can use the shortcut `r1': \begin{verbatim} Please enter your selection now (y axis or option):r1 ----------------- rendering options ------------------- enter number of pixels along x axis ([1:10000], default=200):1000 \end{verbatim} then, to plot the figure to file instead of the screen, we simply choose a different PGPLOT device at the prompt: \begin{verbatim} Please enter your selection now (y axis or option):2 (x axis) (default=1): (render) (0=none) ([0:11], default=6): (vector plot) (0=none, 7=v) ([0:7], default=0): Graphics device/type (? to see list, default /xw): starpartfinal.gif/gif \end{verbatim} producing our final finished Figure shown in Figure~\ref{fig:starfinal}. \begin{figure}[h!] \begin{center} \includegraphics[width=0.5\textwidth]{starpartfinal.png} \caption{Finished star formation plot} \label{fig:starfinal} \end{center} \end{figure} Pressing `S' from the main menu saves all of the settings and plot limits to disk, so invoking \splash again will produce the same plot. To produce the same plot on a sequence of dumps, simply put more than one file on the command line and plot to a non-interactive device (see \ref{sec:movies}). Use the postscript devices /ps or /cps (for colour) to make figures suitable for inclusion in a paper. Other things you may want to do with this plot include: \begin{itemize} \item Turn the time legend off. See \S\ref{sec:legendoff}. \item Change the colour of sink particles. See \S\ref{sec:partcolours}. \item Change the foreground/background colour of the page. See \S\ref{sec:pagecolours}. \end{itemize} \subsection{Multi-panelled figure} The following is an example plot taken from \citet{pb07}. Here I will plot a sequence of plots tiled on the same page, so that columns correspond to dumps taken from different runs at the same time and rows correspond to an evolutionary sequence from a given run. The plot uses sphNG data which contains sink particles, so I also want these to appear on the plots and be plotted in white. Basically I want the plots to be plotted such that as much of the plot is taken up by data and very little by axes and the like but still conveying all of the necessary infomation. We proceed as follows: Firstly, each different run (corresponding in this case to a series of runs with different magnetic field strength) are in different subdirectories with names like \verb+mbossbod_f10.0/+, \verb+mbossbod_f5.0/+, etc. which all contain a sequence of dump files with names like \verb+mbos001+, \verb+mbos002+ etc. To begin the plot, I start by creating a new, empty subdirectory so that the \verb+splash.defaults+ and \verb+splash.limits+ files created by pressing 'S' from the main menu will be in this directory such that running \splash from that directory always produces this plot. So: \begin{verbatim} dprice% mkdir plot1 dprice% cd plot1 \end{verbatim} then having decided which dump files from each run to use, I create a text file listing these filenames (with the full relative pathname) in the order in which I will plot them. For example, calling this file (arbitrarily) \verb+filelistplot+, the contents should be something like the following: \begin{verbatim} dprice% more filelistplot ../mbossbod_f20.0/mbos259 ../mbossbod_f20.0/mbos263 ../mbossbod_f20.0/mbos268 ../mbossbod_f20.0/mbos275 ../mbossbod_f20.0/mbos294 ../mbossbod_f10.0/mbos259 ../mbossbod_f10.0/mbos263 ../mbossbod_f10.0/mbos268 ../mbossbod_f10.0/mbos275 ../mbossbod_f10.0/mbos294 ../mbossbod_f7.5/mbos259 ../mbossbod_f7.5/mbos263 ../mbossbod_f7.5/mbos268 ... \end{verbatim} Then invoke \splash (ssplash for sphNG) with these filenames on the command line: \begin{verbatim} ssplash `cat filelistplot` \end{verbatim} after which the first dump file should be read, indicated by output along the lines of: \begin{verbatim} reading single dumpfile >>>>>>>>>>>>>>>>>>>>>>>>>> ../mbossbod_f20.0/mbos259 <<<<<<<<<<<<<<<<<<<<<<<<<< double precision dump File ID: SHydroRTMHD1 npart = 491567 ... \end{verbatim} An alternative method is to rename the `filelistplot' file \verb+splash.filenames+, from which the filenames will be read if there are none specified on the command line (this feature was implemented as a workaround for a limit to the number of command line arguments on the some compilers). The first stage is to get a plot of a single panel looking good. So, from the main menu, we will plot a simple rendering of density and adjust the plot limits until we are happy: \begin{verbatim} You may choose from a delectable sample of plots ------------------------------------------------------- 1) x 6) density 2) y 7) B\dx 3) z 8) B\dy 4) particle mass 9) B\dz 5) h ------------------------------------------------------- 10) multiplot [ 4 ] m) set multiplot ------------------------------------------------------- d(ata) p(age) o(pts) l(imits) le(g)end h(elp) r(ender) v(ector) x(sec/rotate) s,S(ave) q(uit) ------------------------------------------------------- Please enter your selection now (y axis or option):2 (x axis) (default=1): (render) (0=none) ([0:9], default=0):6 (vector plot) (0=none, 7=B) ([0:7], default=0): Graphics device/type (? to see list, default /xw): /xw \end{verbatim} which should produce the plot shown in the left hand panel of Figure~\ref{fig:multipart1}. Not much can be seen at first -- just a few white dots. This is mainly a result of the density axis (i.e., the colour bar) not being logged. Moving the cursor over the colour bar and pressing `l' results in the plot shown in the right hand panel of Figure~\ref{fig:multipart1}. \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{multipart1.png} & \includegraphics[width=0.5\textwidth]{multipart2.png} \end{tabular} \caption{First stage in the multi-panelled figure tutorial: a simple render plot of density (left) and with a log axis after having placed cursor over colour bar and pressed `l' (right)} \label{fig:multipart1} \end{center} \end{figure} Before we proceed any further, we will first change the axes to be in physical units rather than code units. Pressing `q' in the plot window to exit interactive mode and return to the main menu, and from the d)ata menu, turn the ``use physical units option'' on: \begin{verbatim} Please enter your selection now (y axis or option):d ----------------- data read options ------------------- 0) exit 1) read new data /re-read data 2) change number of timesteps used ( 1 ) 3) plot selected steps only ( OFF ) 4) buffering of data on/off ( OFF ) 5) turn calculate extra quantities on/off ( OFF ) 6) use physical units ( OFF ) 7) change physical unit settings enter option ([0:7], default=0):6 current settings for conversion to physical units are: x [cm] = x x 1.000E+16 y [cm] = y x 1.000E+16 z [cm] = z x 1.000E+16 particle mass [g] = particle mass x 1.991E+33 h [cm] = h x 1.000E+16 density [g/cm\u3\d] = density x 1.991E-15 B\dx [G] = B\dx x 1.000E+00 B\dy [G] = B\dy x 1.000E+00 B\dz [G] = B\dz x 1.000E+00 time = time x 1.13E-01 Use physical units? (default=yes):yes \end{verbatim} The default transformations to physical units are in this case set in the data read. However it would be nicer in this case to set the x and y axis units to AU (Astronomical Units), rather than cm. From the d)ata menu we proceed as follows: \begin{verbatim} enter option ([0:7], default=0):7 enter column to change units (-2=reset all,-1=quit,0=time) ([-2:9], default=-1):1 enter x [cm] units (new=old*units) (default=0.1000E+17):668.3893 enter label amendment (default=[cm]): [AU] Apply these units to all coordinates and h? (default=yes): Enter unit for 'z' in 3D column integrated plots (default=0.1000E+17): Enter label for z integration unit (e.g. [cm]) (default=[cm]): enter column to change units (-2=reset all,-1=quit,0=time) ([-2:9], default=-1): save units to file? (default=yes): saving plot limits to file splash.units \end{verbatim} where in the above I set the multiplicative factor such that the x axis will be in AU and correspondingly changed the units label to `` [AU]'' (note the preceding space). I was also prompted to change the unit for 'z integration' -- this is the length unit added when integrating a quantity through z. Leaving this in cm means that, even though the coordinate axes are in AU, the density (in g/cm$^{3}$) is integrated through z in cm, giving column density in g/cm$^{2}$ (as opposed to g /cm$^{3}$ AU). To save what we have done so far, press `s' from the main menu to save the current settings to the \verb+splash.defaults+ file: \begin{verbatim} Please enter your selection now (y axis or option):s default options saved to file splash.defaults \end{verbatim} Having turned physical units on, we replot the same plot (i.e., answering 2, 1, 6, 0, /xw to the prompts, as previously). First of all we find simply a white screen. This is a result of the colour bar axis now being wrong. Moving the mouse over the colour bar and pressing `a' (to adapt) results in the plot shown in the left hand panel of Figure~\ref{fig:multipart3}. The plot looks basically identical to the previous plot, except that the axes are now in physical units (x and y are in AU and column density is in g/cm$^{2}$). Next, we zoom in to the central region of interest using the mouse -- selecting a region and clicking to zoom in. Pressing `o' centres the plot on the origin and as we zoom in it we also press `a' over the colour bar to readjust the colour bar limits to the max/min on the zoomed-in plot. Finishing with the adjustments (and pressing `s' in the plot window to save the current settings) results in the plot shown in the right hand panel of Figure~\ref{fig:multipart3}. \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{multipart3.png} & \includegraphics[width=0.5\textwidth]{multipart4.png} \end{tabular} \caption{Second stage in the multi-panelled figure tutorial: having changed the axes into physical units (left) and zooming in and adjusting the colour bar (right).} \label{fig:multipart3} \end{center} \end{figure} \subsection{Surface rendering} Here I will give an example of how to use the 3D surface rendering feature starting with a dump file kindly supplied by Giuseppe Lodato from an SPH simulation of a warped accretion disc. First we read the file (in sphNG format, so we use ssplash): \begin{verbatim} dprice$ ssplash warp001 \end{verbatim} after which we reach the main menu: \begin{verbatim} You may choose from a delectable sample of plots ------------------------------------------------------- 1) x 6) density 2) y 7) v\dx 3) z 8) v\dy 4) particle mass 9) v\dz 5) h ------------------------------------------------------- 10) multiplot [ 4 ] m) set multiplot ------------------------------------------------------- d(ata) p(age) o(pts) l(imits) le(g)end h(elp) r(ender) v(ector) x(sec/rotate) s,S(ave) q(uit) ------------------------------------------------------- Please enter your selection now (y axis or option): \end{verbatim} Firstly we want to plot just a simple render plot of density. Thus we choose: \begin{verbatim} Please enter your selection now (y axis or option):2 (x axis) (default=1): (render) (0=none) ([0:9], default=0):6 (vector plot) (0=none, 7=v) ([0:7], default=0): Graphics device/type (? to see list, default /xwin): /xw \end{verbatim} producing the plot shown in the left panel of Figure~\ref{fig:surfpart1} (I have used \verb+/png+ instead of \verb+/xw+ to produce the figures for the userguide). Moving the cursor over the colour bar and pressing `l' to log the colour bar axis produces the Figure in the right panel of Figure~\ref{fig:surfpart1}. \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{surfpart1.png} & \includegraphics[width=0.5\textwidth]{surfpart2.png} \end{tabular} \caption{First stage in the surface rendering tutorial: a simple render plot of density (left) and with a log axis after having placed cursor over colour bar and pressed `l' (right)} \label{fig:surfpart1} \end{center} \end{figure} The next step is to adjust the viewing angle. Pressing `h' in the plot window brings up the list of keystrokes which can be used to change the angle. Here we want to add a rotation about the $x-$ axis, so we press \verb+{+ three times to change the x angle by -90 degrees and then press \verb+[+ once to increment the angle by a further -15 degrees. The \splash output in the terminal reads, amongst other things: \begin{verbatim} rotating particles about z by 0.00 rotating particles about y by 0.00 rotating particles about x by 255.00 \end{verbatim} Then we have the Figure shown in the left panel of Figure~\ref{fig:surfpart2}. \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{surfpart3.png} & \includegraphics[width=0.5\textwidth]{surfpart4.png} \end{tabular} \caption{Second stage in the surface rendering tutorial: after adjusting the rotation angle (left) and with 3D surface rendering turned on (which also turns on 3D perspective) and having adjusted the colour bar limits (right)} \label{fig:surfpart2} \end{center} \end{figure} Next, we need to turn the 3D surface rendering on. This cannot be done in interactive mode so we need to exit -- pressing `s' first to save what we have done so far, then 'q' to quit interactive mode. Then, back at the \splash main menu, we type x4 for the x)sec/3D plotting options menu, option 4 which is ``3D surface rendering on/off'' with prompts appearing as follows: \begin{verbatim} Please enter your selection now (y axis or option):x4 ---------- cross section / 3D plotting options -------- Use 3D opacity rendering? (default=yes):y also turning on 3D perspective (which must be set for this to work) Warning: 3D opacity rendering sends only an approximate version to the PGPLOT device (not corrected for brightness) Do you want to write a ppm file in addition to PGPLOT output? (default=yes):y \end{verbatim} Now we replot the original plot with the new settings as follows: \begin{verbatim} Please enter your selection now (y axis or option):2 (x axis) (default=1): (render) (0=none) ([0:9], default=6): (vector plot) (0=none, 7=v) ([0:7], default=0): enter z coordinate of observer (default=53.58): enter distance between observer and projection screen ([0.000:], default=5.358): using current h and pmass limits to calculate kappa (cross section/unit mass) min h = 0.1197254 min particle mass = 3.812551E-11 [ kappa = pi*h_min**2/(particle_mass*n_smoothing_lengths) ] enter approximate surface depth (number of smoothing lengths): ([0.000:], default=2.000): kappa (particle cross section per unit mass) = 1.2369025E+9 Graphics device/type (? to see list, default /xwin): \end{verbatim} Note that several new prompts appear -- for the moment I have just used the default answers by pressing return. The first result is rather frightening : just a black image with a black colour bar! This is because the limits we set for column density are several orders of magnitude away from the limits on density. Moving the cursor over the colour bar and pressing `a' to adapt the limits produces the plot shown in the right panel of Figure~\ref{fig:surfpart2}. Note that the plot suddenly appears much smaller -- this is a consequence of the 3D perspective settings. Moving the cursor into the plot window and pressing `a' adapts the plot limits. After also clicking on the colour bar and adjusting the colour bar limits, we arrive at the plot shown in the left panel of Figure~\ref{fig:surfpart3}. \begin{figure}[h] \begin{center} \begin{tabular}{cc} \includegraphics[width=0.5\textwidth]{surfpart5.png} & \includegraphics[width=0.5\textwidth]{surfpart6.png} \end{tabular} \caption{Third stage in the surface rendering tutorial: after adjusting the xy and colour bar limits interactively (left) and increasing the number of pixels and having turned the axes off (right)} \label{fig:surfpart3} \end{center} \end{figure} Now that we are nearly there, to add the finishing touches we need to i) increase the number of pixels in the image and ii) turn the axes off, since they are no longer meaningful with 3D perspective set. The number of pixels can be increased by returning to the \splash main menu (pressing `s' in interactive mode before doing so to save what we have done so far), then typing `r1' for render menu, option 1: \begin{verbatim} Please enter your selection now (y axis or option):r1 ----------------- rendering options ------------------- enter number of pixels along x axis ([1:10000], default=200):1000 \end{verbatim} Next, we turn the axes off using the p)age submenu: \begin{verbatim} Please enter your selection now (y axis or option):p2 ---------------- page setup options ------------------- -4 : draw box and major tick marks only; -3 : draw box and tick marks (major and minor) only; -2 : draw no box, axes or labels; -1 : draw box only; 0 : draw box and label it with coordinates; 1 : same as AXIS=0, but also draw the coordinate axes (X=0, Y=0); 2 : same as AXIS=1, but also draw grid lines at major increments of the coordinates; 10 : draw box and label X-axis logarithmically; 20 : draw box and label Y-axis logarithmically; 30 : draw box and label both axes logarithmically. enter axis option ([-4:30], default=0):-2 axis = -2 \end{verbatim} Plotting the same plot again now results in the plot shown in the right panel of Figure~\ref{fig:surfpart3}. Finally we will also set the background colour to black, adjust the opacity and move the time legend. Notice that in the right panel of Figure~\ref{fig:surfpart3} the surface looks quite blotchy. This is an indication that the surface is too shallow (that is we are only seeing particles on the very top). Thus we will adjust the opacity for a slightly deeper plot. We proceed as follows: Exiting interactive mode (pressing `s' then `q' in the plot window), we first set the foreground and background colours in the p)age submenu: \begin{verbatim} Please enter your selection now (y axis or option):p8 ---------------- page setup options ------------------- Enter background colour (by name, e.g. "black") (default=):black Enter foreground colour (by name, e.g. "white") (default=):white Do you want to plot axes and overlaid text in background colour (default is foreground) ? (default=no): \end{verbatim} Now, replotting the same plot again, but this time adjusting the opacity at the prompt: \begin{verbatim} enter approximate surface depth (number of smoothing lengths): ([0.000:], default=2.000):200.0 \end{verbatim} Finally, moving the time legend by positioning the cursor and pressing 'G' and zooming out slightly by pressing `-' once, we arrive at our finished figure (or movie frame) shown in Figure~\ref{fig:surfpartfinal}. Pressing `s' in interactive mode saves the settings, then pressing `q' returns to the \splash main menu. To save the settings to disk, press `S' from the main menu to save both the \verb+splash.defaults+ file and the \verb+splash.limits+ file. \begin{figure}[h!] \begin{center} \includegraphics[width=0.5\textwidth]{surfpartfinal.png} \caption{Finished surface-rendered plot} \label{fig:surfpartfinal} \end{center} \end{figure} To create a sequence of images with these settings, then simply invoke \splash again with multiple files: \begin{verbatim} ssplash warp??? \end{verbatim} then plotting the same plot as previously to a non-interactive device will cycle through all dump files producing a sequence of plots with names like \verb+splash_0000.png+, \verb+splash_0001.png+ etc. These can be easily converted into an animation. \subsection{Using asplash to plot energy vs time plots} \label{sec:evsplash} asplash (that is, the compilation of \splash which reads ascii files) can also be used for non-SPH data. For example I often use it to plot the contents of the .ev file my SPH code dumps monitoring quantities like energy and angular momentum at every timestep. A shortcut way of setting options appropriate to reading such files (e.g. plotting lines instead of dots, plotting all files on the same page) is implemented by adding the ``-e'' option to the command line: e.g. \begin{verbatim} asplash -e file1.ev file2.ev file3.ev \end{verbatim} also, using the -e option on the command line means that any modification to the preset options /limits are saved to files called \verb+evsplash.defaults+ and \verb+evsplash.limits+ instead of the usual \verb+splash.defaults+ and \verb+splash.limits+. This means the defaults for this type of plot are saved separately to those for ``normal'' plots of SPH data. For other command line options, see \S\ref{sec:commandline}. \subsection{Powerspectrum of 1D data} In one dimension an extra plot item appears in the data menu which takes a power spectrum (in space) of a particular variable defined on the particles. Upon selection the user is prompted for various settings before plotting the power spectrum. For data defined on irregularly distributed particles, there are two methods for taking the power spectrum: Either to interpolate to an even grid and use a Fourier transform or to use a method for calculating a periodogram of irregularly sampled data which can have significant advantages over interpolation. Algorithms for both of these methods have been implemented. For the first, the SPH data is interpolated to a one dimensional grid using the kernel before calculating the (slow!) fourier transform. The second method computes a Lomb/Scargle periodogram as described in \citet{numericalrecipes}. It should be stressed, however, that \emph{neither} of the subroutines for calculating the power spectrum is particularly fast and have \emph{only} been included as a preliminary feature since I have used them once or twice in one dimensional simulations where speed is not an issue. The algorithms are fairly simple to extend to multidimensional data, although faster implementations would be needed (such as a Fast Fourier Transform routine). \subsection{Plotting azimuthally-averaged disc surface density and Toomre Q parameter} \label{sec:surfdens} For analysis of accretion disc simulations, it is useful to make azimuthally averaged plots of the disc properties such as the surface density and, for self-gravitating discs, the Toomre Q parameter. Extra columns appear to plot both of these quantities when the simulation is 3D and the coordinate system is changed to cylindrical or spherical co-ordinates (in the particle plot (o)ptions menu -- see \S\ref{sec:geom}). For the Toomre Q parameter to appear it is also necessary to have read the thermal energy from the dump file. For example, having read a dump file, change the coordinate system to cylindricals: \begin{verbatim} Please enter your selection now (y axis or option):o7 ------------- particle plot options ------------------- 0) reset (= 1) 1) cartesian x,y,z 2) cylindrical r,phi,z 3) spherical r,phi,theta 4) toroidal r,theta,phi Enter coordinate system to plot in: ([0:4], default=1):2 \end{verbatim} then extra columns appear in the menu: \begin{verbatim} You may choose from a delectable sample of plots ------------------------------------------------------- 1) r 13) u 2) phi 14) grad h 3) z 15) grad soft ... ... 11) v\dphi 23) Surface density 12) v\dz 24) Toomre Q parameter ------------------------------------------------------- \end{verbatim} Then (in this example), select column 23 to plot surface density, \begin{verbatim} Please enter your selection now (y axis or option):23 setting x axis to r for surface density plot \end{verbatim} ...and the plot will appear - an example surface density plot is shown in Figure~\ref{fig:surfdens}. \begin{figure}[h!] \begin{center} \includegraphics[width=0.5\textwidth]{surfdens.pdf} \caption{Plot of azimuthally averaged surface density in a 3D accretion disk simulation} \label{fig:surfdens} \end{center} \end{figure} Azimuthally averaged quantities are calculated by binning the particles into a fixed number of annuli in radius. The mean surface density is calculated using \begin{equation} \Sigma(r_{ann}) = \frac{M_{ann}}{\pi [(r_{ann} + 0.5\Delta r)^{2} - (r_{ann} - 0.5\Delta r)^{2}]}, \end{equation} that is, the total mass in the annulus (sum of the particle masses) divided by its area, where $r_{ann}$ is the radius (cylindrical or spherical) of the annulus. The Toomre Q parameter, defined as \begin{equation} Q_{Toomre}(r) = \frac{\bar{c}_{s}(r)\kappa(r)}{\pi \Sigma(r)}, \end{equation} where $\kappa$ is the epicyclic frequency and $\bar{c}_{s}$ is the RMS sound speed, is calculated using the above surface density, assuming a Keplerian rotation profile and a central star mass of unity (i.e., $\kappa(r) = \Omega(r)$, where $\Omega(r) = r^{-3/2}$). The sound speed for each particle $i$ is calculated from the stored thermal energy and $\gamma$ (ratio of specific heats) according to \begin{equation} c_{s,i}^{2} = \left\{ \begin{array}{ll} \frac23 u_{i}, & \gamma = 1; \\ (\gamma-1)\gamma u_i, & \gamma \neq 1; \end{array}\right. \end{equation} from which the RMS sound speed is calculated as the square root of the average of $c_{s}^{2}$ on the particles in the annulus. \section{Other useful information}%HEVEA\cutname{other.html} \subsection{Converting binary dump files to ascii using \splash} \label{sec:convert} \splash has a command line feature which can be used to convert binary SPH dump files into ascii format. The syntax is \begin{verbatim} splash to ascii dump001 dump002 dump??? \end{verbatim} which will convert all of the dump files listed on the command line into ascii format (called \verb+dump001.ascii+, \verb+dump002.ascii+ etc.), with columns as would be listed in the main menu if you opened the dump file in \splash. Note that the output \emph{includes} calculated extra quantities such as the radius if these have been turned on [in the d) menu] and the settings saved to the \verb+splash.defaults+ file. Similarly the data will be output in physical units if a \verb+splash.units+ file is present. For other command line options, see \S\ref{sec:commandline}. \subsection{Converting SPH data files to 3D gridded data using \splash} \label{sec:converttogrid} \splash has a command line feature which can be used to read binary SPH dump files and output 3D gridded data in a variety of formats. The syntax is \begin{verbatim} splash to grid dump001 dump002 dump??? \end{verbatim} which will interpolate the density, velocity (if present) and magnetic field (if present) onto a 3D grid and output the results to files (the default output format is ascii, with one file for each quantity interpolated). Other data columns in the SPH file can be interpolated using the ``allto'' option, which interpolates \emph{all} of the columns to the grid: \begin{verbatim} splash allto grid dump001 dump002 dump??? \end{verbatim} The grid interpolation uses the $x$, $y$, and $z$ limits --- as saved to the \verb+splash.limits+ file --- for the box, and the grid size is given by the ``set number of pixels'' option in the r)ender menu --- as saved to the \verb+splash.defaults+ file. Automatic pixel determination also works (if npixels = 0) but there is a sensible upper limit placed on the grid size determined in this manner to avoid ridiculous memory/disk usage. Various environment variable options are available (these are output at runtime) that can be used to change various aspects of the grid interpolation behaviour (e.g. setting \verb+SPLASH_TO_GRID_PERIODIC=yes+ enforces periodic boundary conditions). For all possible output formats, use \verb+splash --help+ or see the full list of command line options in \S\ref{sec:commandline}. \subsection{Using \splash to calculate global quantities as a function of time.} \label{sec:splashcalc} \splash has a command line feature that can be used to calculate global quantities on the particles as a function of time, for example kinetic, thermal, magnetic and total energy, total linear and angular momentum. An example to calculate the energies in a sequence of dump files is: \begin{verbatim} splash calc energies dump001 dump002 dump??? \end{verbatim} Other options are given by typing 'splash calc', which currently has the following options: \begin{verbatim} splash calc energies : calculate KE,PE,total energy vs time output to file called 'energy.out' calc massaboverho : mass above a series of density thresholds vs time output to file called 'massaboverho.out' calc max : maximum of each column vs. time output to file called 'maxvals.out' calc min : minimum of each column vs. time output to file called 'minvals.out' calc diff : (max - min) of each column vs. time output to file called 'diffvals.out' calc amp : 0.5*(max - min) of each column vs. time output to file called 'ampvals.out' calc delta : 0.5*(max - min)/mean of each column vs. time output to file called 'deltavals.out' calc mean : mean of each column vs. time output to file called 'meanvals.out' calc rms : (mass weighted) root mean square of each column vs. time output to file called 'rmsvals.out' calc timeaverage : time average of *all* entries for every particle output to file called 'time_average.out' calc ratio : ratio of *all* entries in each file compared to first output to file called 'ratio.out' \end{verbatim} For the `energies' and `massaboverho' options to be successful, \splash must be aware of the locations of the corresponding columns in the data (i.e., by the column identification given in the set\_labels routine corresponding to the data read). For the `massaboverho' option an input file is required specifying the density thresholds (a default version is written if the appropriate file is not already present). \subsection{Using \splash to time average a series of files} The `splash calc timeaverage' command line option (see \S\ref{sec:splashcalc}) can be used to produce a time average of a series of files from any splash-readable format. This computes the time-average of every individual entry in the file as represented in \splash as a table of rows (or `particles') and columns (or `quantities defined on particles'). The output is an ascii file with the same rows and columns, averaged over all the snapshots on the command line. The number of columns is doubled in the output, giving the standard deviation for each quantity in the corresponding column (e.g., the standard deviation for column 1 is output in column $N + 1$). Examples of how this could be use might be to produce the time-averaged power spectrum from a series of ascii files containing power spectra for individual output times, or the time averaged probability density function (PDF) from PDFs produced by \splash. The resulting ascii file, called \verb+time_average.out+ can be plotted using the ascii splash binary (asplash). For other command line options, see \S\ref{sec:commandline}. %\subsection{Fixing the PGPLOT file naming system} %\label{sec:fixpgplotnames} % %When you run \splash to make a series of plots it generate files with names.. %\begin{verbatim} %pgplot.gif %pgplot.gif_1 %pgplot.gif_2 %... %pgplot.gif_10 %pgplot.gif_11 %pgplot.gif_12 %.. %pgplot.gif_200 %pgplot.gif_201 %\end{verbatim} %The annoying thing is that the numeric indices on these files are out of order and, when using %standard programs to make animations, results in frames out of sequence. % % The problem is related to PGPLOT file naming conventions. The fix is that, included in the splash/scripts directory is a script called 'fixpgplotnames.bash'. If you run this in the directory where your files are located, i.e., %\begin{verbatim} %cd mydir %~/splash/scripts/fixpgplotnames.bash %\end{verbatim} %will rename all of the files sensibly. If you run it with a number as the argument %\begin{verbatim} %~/splash/scripts/fixpgplotnames.bash 10 %\end{verbatim} %it will rename filenames adding 10 to the number (actually 11 because it starts from 1). This is %useful if you run \splash multiple times to get different parts of an animation. \subsection{Reading/processing data into images without having to answer prompts} \label{sec:batchmode} Previously, the only way to run \splash non-interactively was to write a small shell script which runs \splash and answers the prompts appropriately. For example: \begin{verbatim} #!/usr/bin/tcsh cd plot splash myrun* << ENDINPUT 2 1 8 0 /png q ENDINPUT \end{verbatim} which would plot the data in columns 2 and 1 and render the data in column 8 with output to file \verb+mypostscript.ps+. However, in more recent versions \splash can be invoked with plot options on the command line. Thus to achieve the same as in the example given above we would simply use \begin{verbatim} splash myrun* -x 1 -y 2 -render 8 -dev /png \end{verbatim} or simply \begin{verbatim} splash myrun* -r 8 -dev /png \end{verbatim} which will assume sensible default values (2 and 1 respectively) for the y and x axes. Similarly a vector plot can be specified with \verb+-vec+ and a contour plot with \verb+-cont+. The full list of command-line flags is given in \S\ref{sec:commandline}. If plotting options have been only partially specified on the command line, then prompts will appear for only the remaining options. This can be used for example to specify the graphics device via the \verb+-dev+ command line option, which means that only the device selection prompt does not appear. \subsection{Making frames across multiple processors} Making identical plots of a series of dump files for a movie is a task which can inherently be done in parallel. Included in the splash/scripts directory is a perl wrapper for \splash (``\verb+splash_parallel.pl+'') which distributes multiple instances of \splash across multiple machines, either via ssh or using Apple's xgrid, with a common input file as described in \S\ref{sec:batchmode}. The limitation to this is that you need to have a disk which can be mounted from all client machines (i.e., they can read the data files) and preferably with password-less access (e.g. using an ssh key-exchange or Kerberos authentication). The script itself may need some slight adjustment for your particular system. However, with large datasets often the slowest part of the rendering process can be reading the data file. A good way of crippling a system is therefore to set 100 jobs going which all decide to read a large data file from disk at the same time. To avoid this the script allows the user to set a delay between launching jobs (preferably slightly longer than the length of time it takes to read a single dump file), but some care is needed to avoid disaster. You have been warned! \subsection{What about boundaries? How does the rendering work near a boundary?} Usual practise in SPH simulations near boundaries is to introduce ghost particles which mirror the real particles. \splash does not explicitly setup any ghost particles but will use any that are present in the data (see next question for how to specify multiple particle types). Additional particle types contribute to the rendering calculations but not to the determination of the plot limits. Note, however, that \splash does \emph{not} set up ghost particles itself, as this may depend on the type and location of the boundary. Thus if your simulation uses ghost particle boundaries, the ghost particles should be dumped alongside the gas particles in the output file so that their positions, masses, densities and smoothing lengths can be read into \splash and used to render the image appropriately. \subsection{How does \splash handle multiple particle types?} \splash can handle up to 6 different particle types. These can be turned on and off in the particle plot o)ptions menu (\S\ref{sec:opts}). These types are be specified in the set\_labels part of the read\_data routine, which contains some lines of code along the lines of: \begin{verbatim} ntypes = 3 labeltype(1) = 'gas' labeltype(2) = 'ghost' labeltype(3) = 'sink' UseTypeInRenderings(1) = .true. UseTypeInRenderings(2) = .true. UseTypeInRenderings(3) = .false. \end{verbatim} which says that there are 3 particle types, with names as given, and that types 1 and 2 are SPH particles and should be used in the rendering where appropriate (i.e., only when plotting of this type is turned on in the o)pts menu). Particle types which are to be used in renderings should have masses, densities and smoothing lengths read. Non-SPH particle types (e.g. sink particles) can be optionally plotted on top of rendered plots. \subsection{Using special characters in the plot labels} Several of the examples shown in this manual use special characters (such as the $\int$ character) in the plot labels. In \giza these can be specified using \TeX-like escape sequences, or with the escape sequences used in \textsc{pgplot}. For example to plot the greek letter $\rho$ we would use \begin{verbatim} label = 'this would print the greek letter \rho' \end{verbatim} or, in \textsc{pgplot}-style: \begin{verbatim} label = 'this would print the greek letter \gr' \end{verbatim} where \verb+\gr+ is the \textsc{pgplot} escape sequence for $\rho$. \begin{quote} In \giza, which uses real fonts rather than the bitmapped characters used in \textsc{pgplot}, special characters are implemented with unicode characters. Thus, you need to select a font that has the appropriate characters included. The font can be changed using the \verb+GIZA_FONT+ environment variable. \end{quote} For other characters the procedure is similar. For example for the integral \begin{equation} \int v_x \mathrm{dx} \end{equation} we would use the \TeX-like expression \begin{verbatim} label = '\int v_x dx' \end{verbatim} or equivalently, in \textsc{pgplot}-style \begin{verbatim} label = '\(2268) v\d x \u dx' \end{verbatim} where \verb+\(2268)+ is the \textsc{pgplot} escape sequence for the integral sign. The \verb+\d+ indicates that what follows should be printed as subscript and \verb+\u+ correspondingly indicates a return to normal script (or from normal script to superscript). All of the escape sequences for special characters are listed in the appendix to the \textsc{pgplot} user guide. \begin{quote} WARNING: Note that the use of escape characters can be compiler dependent and may not therefore work on all compilers (for example the intel compiler needs the -nbs flag). \end{quote} \subsection{Making movies} See \S\ref{sec:movies} and the online FAQ (\url{http://users.monash.edu.au/~dprice/splash/faqs.html}). \subsection{Outputting the raw pixel map to a file} \label{sec:writepixmap} The actual pixel map rendered to the graphics device (i.e., when a quantity is rendered to pixels, not for particle plots) can be output directly to a file, or series of files by using the \verb+-o+ command line option when you invoke \splash. Invoking \splash with \verb+-o+ produces a list of currently implemented formats (at the moment these are an ascii dump file and ppm format). This is useful if you need to compare the image to the output from another code (e.g. using a different visualisation tool) or if you wish to have a ``raw'' rendering, that is without annotation on the plots, but which (in the ppm case) uses more colours. The files are given default names such as ``splash\_00001.dat'' or ``splash\_00001.ppm'' where the number corresponds to the frame number as would be rendered to the graphics device. For other command line options, see \S\ref{sec:commandline}. \section{User contributions}%HEVEA\cutname{wishlist.html} Please contribute! All user contributions or suggestions are greatly appreciated. In particular, please send: \begin{itemize} \item Bugs! \item Feature requests/suggestions. \item Pretty pictures for the gallery. \end{itemize} If you are *really* keen, you may also like to consider: \begin{itemize} \item Exact solution routines for test problem(s). Even just an analytic description from which I can write the code. \item Suggestions/tips on possible visualisation techniques \item More colour schemes (simply email me a table of the rgb colour indices, or failing that simply an image of the colour scheme and I will add it). \end{itemize} I am also very open to allowing commit access to the repositories - just let me know. Otherwise, contributions, comments and inevitable bugs should be sent to: \begin{verbatim} splash-users@googlegroups.com \end{verbatim} or \begin{verbatim} daniel.price@monash.edu \end{verbatim} \section*{Acknowledgements}%HEVEA\cutname{thanks.html} Several of the routines were developed from ideas used by Matthew Bate and \splash has been refined by many useful discussions with Matthew. The polytrope exact solution is from a routine by Joe Monaghan. I am indebted to one Thomas S. Ullrich at the University of Heidelberg who wrote the prompting module which is used throughout the program and to Roland Schmehl who wrote the excellent function parser module (made available at \url{http://fparser.sourceforge.net}). Last but not least, a huge thanks especially to all the users who have given feedback which has helped to improve \splash including, but not limited to: Stefan Adami, Craig Agnor, Richard Alexander, Gabe Altay, Pau Amaro-Seoane, Alessandro Atrani, Sumedh Anathpindika, Ben Ayliffe, Andreas Bauswein, Mark Bennett, David Brown, Florian Buerzle, Paul Cornwall, Jared Coughlin, Carlos Cuesta, Daniel Cunnama, Alan Duffy, Clare Dobbs, Carrie Elliot, Claude-Andr\'e Faucher-Gigu\`ere, Stefano Facchini, Juan Pablo Farias, Christoph Federrath, Laure Fouchet, Sergio Gelato, Thomas Grief, Doron Grossman, Jean-Fran\c{c}ois Gonzalez, Johnny Hitti, Mark Hutchison, Vid Ir\v{s}i\v{c}, John Jones, Sky King, Laura Kreidberg, Guillaume Laibe, Ben Lewis, Giuseppe Lodato, David Madlener, John Mansour, Ruben Martin, Farzana Meru, Andrew McLeod, Nick Moeckel, Shazrene Mohamed, Rebecca Nealon, Chris Nixon, Alex Pettitt, Cody Raskin, John Regan, Dave Rundle, Alison Sills, Kevin Sooley, Phil Sutton, Robert Thompson, St\'even Toupin, Terry Tricco, Yusuke Tsukamoto, Sigfried Vanaverbeke, Enrique Vazquez-Semadeni Antonio Vazquez, Tim Waters, James Wurster, and Matt Young. And to everyone who has cited the \splash paper! \newpage \appendix \section{Source code overview}%HEVEA\cutname{sourcecode.html} Here is a brief and outdated description of various files making up the code: \begin{longtable}{|lp{0.7\textwidth}|} \hline Filename & Description \\ \hline \endhead \multicolumn{2}{|r|}{\emph{continued on next page}} \\ \hline \endfoot \hline \endlastfoot allocate.f90 & allocates memory for main arrays \\ calc\_quantities.f90 & calculates additional quantities from particle data \\ colours.f90 & colour schemes for rendering\\ colourparts.f90 & colours particles\\ defaults.f90 & writes/reads default options to/from file\\ exact.f90 & module handling exact solution settings\\ exact\_densityprofiles.f90 & various $N-$body density profiles \\ exact\_fromfile.f90 & reads an exact solution tabulated in a file\\ exact\_mhdshock.f90 & some tabulated solutions for mhd shocks\\ exact\_polytrope.f90 & exact solution for a polytrope\\ exact\_rhoh.f90 & exact relation between density and smoothing length\\ exact\_sedov.f90 & exact solution for sedov blast wave\\ exact\_shock.f90 & exact solution for hydrodynamic shocks\\ exact\_wave.f90 & exact solution for a propagating sine wave\\ exact\_toystar.f90 & exact solution for the toy star problem\\ exact\_toystar2D.f90 & exact solution for the 2D toy star problem\\ get\_data.f90 & wrapper for main data read\\ geometry.f90 & module handling different coordinate systems\\ globaldata.f90 & various modules containing "global" variables\\ interactive.f90 & drives interactive mode\\ interpolate1D.f90 & interpolation of 1D SPH data to grid using kernel\\ interpolate2D.f90 & interpolation of 2D SPH data to grid \\ interpolate3D\_xsec.f90 & 3D cross section interpolations\\ interpolate3D\_projection.f90 & 3D interpolation integrated through domain\\ legends.f90 & plots (time) legend on plot\\ limits.f90 & sets initial plot limits and writes to/reads from limits file\\ menu.f90 & main menu\\ options\_data.f90 & sets options relating to current data\\ options\_limits.f90 & sets options relating to plot limits\\ options\_page.f90 & sets options relating to page setup\\ options\_particleplots.f90 & sets options relating to particle plots\\ options\_powerspec.f90 & sets options for power spectrum plotting\\ options\_render.f90 & sets options for render plots\\ options\_vector.f90 & sets options for vector plots\\ options\_xsecrotate.f90 & sets options for cross sections and rotation\\ particleplot.f90 & subroutines for particle plotting\\ plotstep.f90 & main ``backbone'' of the code which drives plotting of a single timestep\\ powerspectrums.f90 & calculates power spectrum of 1D data (2 methods)\\ read\_data\_dansph.f90 & reads data from my format of data files\\ read\_data\_mbate.f90 & reads data from matthew bate's format of data files\\ read\_data\_xxx.f90 & reads data from \ldots \\ render.f90 & takes array of pixels and plots render map/contours etc\\ rotate.f90 & subroutines controlling rotation of particles\\ setpage.f90 & sets up the PGPLOT page (replaces call to PGENV/PGLAB)\\ splash.f90 & main program, handles startup/ command line reading\\ timestepping.f90 & controls stepping through timesteps\\ titles.f90 & reads a list of titles to be used to label each timestep\\ transform.f90 & applies various transformations to data (log10, 1/x, etc) \\ \end{longtable} \section{Coordinate transformation details}%HEVEA\cutname{geometry.html} \label{sec:coordtransforms} Particle positions and vectors defined on the particles can be plotted in non-cartesian coordinate systems. The coordinate system can be set via the particle plot o)ptions menu, via the ``change coordinate system'' option. The actual coordinate transformations are defined in a standalone Fortran module called \verb+geometry.f90+ and the precise details can be determined by looking in this file. For reference, however the transformations are given below. \subsection{ Cylindrical Polar Coordinates} For cylindrical coordinates the transformations are: \begin{displaymath} \begin{array}{lclp{1cm}lcl} r & = & \sqrt{x^2 + y^2} & & x & = & r\cos\phi \\ \phi & = & \tan^{-1}{(y/x)} &; & y & = & r\sin\phi \\ z & = & z & & z & = & z\\ \end{array} \end{displaymath} where vectors transform according to: \begin{displaymath} \begin{array}{lclp{1cm}lcl} v_r & = & v_x \frac{x}{r} + v_y \frac{y}{r} & & v_x & = & v_r \cos\phi - v_\phi \sin\phi \\ v_\phi & = & v_x \left(\frac{-y}{r}\right) + v_y \left(\frac{x}{r}\right) &; &v_y & = & v_r \sin\phi + v_\phi \cos\phi \\ v_z & = & v_z & & v_z & = & v_z. \\ \end{array} \end{displaymath} In the case where these vectors are velocities, the $v_{\phi}$ component corresponds to $v_{\phi} = r\dot{\phi}$. \subsection{ Spherical Polar Coordinates} For spherical coordinates the transformations are: \begin{displaymath} \begin{array}{lclp{1cm}lcl} r & = & \sqrt{x^2 + y^2 + z^{2}} & & x & = & r\cos\phi\sin\theta\\ \phi & = & \tan^{-1}{(y/x)} &; & y & = & r\sin\phi\sin\theta \\ \theta & = & \cos^{-1}(z/r) & & z & = & r\cos\theta \\ \end{array} \end{displaymath} where vectors transform according to: \begin{displaymath} \begin{array}{lclp{1cm}lcl} v_r & = & v_x \frac{x}{r} + v_y \frac{y}{r} + v_{z}\frac{z}{r} & & v_x & = & v_r \cos\phi\sin\theta- v_\phi \sin\phi + v_\theta \cos\phi\cos\theta \\ v_\phi & = & v_x \left(\frac{-y}{\sqrt{x^2 + y^{2}}}\right) + v_y \left(\frac{x}{\sqrt{x^2 + y^{2}}}\right) &; &v_y & = & v_r \sin\phi\sin\theta + v_\phi \cos\phi + v_{\theta} \sin\phi\cos\theta \\ v_\theta & = & v_{x}\frac{xz}{r \sqrt{x^{2} + y^{2}}} + v_{y}\frac{yz}{r \sqrt{x^{2} + y^{2}}} - v_{z}\frac{(x^{2} + y^{2})}{r\sqrt{x^{2} + y^{2}}} & & v_z & = & v_r \cos\theta - v_\theta \sin\theta. \\ \end{array} \end{displaymath} In the case where these vectors are velocities, the components $v_{\phi}$ and $v_{\theta}$ correspond to $v_{\phi} = r\sin{\theta}\dot{\phi}$ and $v_{\theta} = r\dot{\theta}$ respectively. \subsection{ Toroidal Coordinates} Toroidal coordinates represent a local frame of reference inside a torus. The coordinate transformations are given by \begin{displaymath} \begin{array}{lclp{1cm}lcl} r & = & \sqrt{[(x^2 + y^2)^{1/2} - R]^{2} + z^{2}} & & x & = & (r\cos\theta + R) \cos\phi \\ \theta & = & \tan^{-1} \left[\frac{z}{(\sqrt{x^{2} + y^{2}} - R)}\right] &; & y & = & (r\cos\theta + R)\sin\phi \\ \phi & = & \tan^{-1}(y/x) & & z & = & r\sin\theta \\ \end{array} \end{displaymath} where $R$ is the radius of the torus. The use of the inverse tangent in $\theta$ instead of $\theta = \sin^{-1}(z/r)$ is necessary to get the quadrant correct (via the \verb+atan2+ function). Vectors transform according to: \begin{displaymath} \begin{array}{lclp{2cm}lcl} v_r & = & v_x \frac{x(r_{cyl} - R)}{r r_{cyl}} + v_y \frac{y(r_{cyl} - R)}{r r_{cyl}} + v_{z} \frac{z}{r} & & v_x & = & v_r \cos\theta\cos\phi- v_\theta \sin\theta\cos\phi - v_\phi\sin\phi \\ v_\theta & = & v_x \frac{-zx}{r r_{cyl}} + v_y\frac{-zy}{r r_{cyl}} + v_{z}\frac{(r_{cyl} - R)}{r} &; &v_y & = & v_r \cos\theta\sin\phi - v_\theta \sin\theta\sin\phi + v_\phi\cos\phi \\ v_\phi & = & v_{x} \left(\frac{-y}{r_{cyl}}\right) + v_{y} \left(\frac{x}{r_{cyl}}\right) & & v_z & = & v_{r}\sin\theta + v_{\theta} \cos\theta \\ \end{array} \end{displaymath} where we have defined, for convenience, \begin{equation} r_{cyl} = \sqrt{x^{2} + y^{2}} = r\cos\theta + R. \nonumber \end{equation} The torus radius $R$ is a parameter in the \verb+geometry+ module and is set to $1$ by default. \section{Exact solution details}%HEVEA\cutname{exactsolutions.html} \label{sec:exact} \subsection{Errors} The error norms calculated when exact solutions are plotted are as follows: The error for each particle is given by \begin{equation} e_i = f_i - f_{exact}, \end{equation} where the exact solution $f_{exact}(x)$ is the solution returned from the exact solution subroutines (with resolution adjustable in the exact solution options menu option) interpolated to the position of the current particle $x_i$ via a simple linear interpolation. The absolute $L_1$ error norm is simply the average of the errors across the domain, calculated according to \begin{equation} \Vert e \Vert_{L_1} = \frac{1}{N f_{max}} \sum_{i=1}^N \vert e_i \vert, \end{equation} where $f_{max}$ is the maximum value of the exact solution in the region in which the particles lie (also only particles in the current plot are used) which is used to normalise the error estimate. A better error norm is the $L_2$ or \emph{Root Mean Square} (RMS) norm given by \begin{equation} \Vert e \Vert_{L_2} = \left[\frac{1}{N} \left( \frac{1}{f_{max}^2} \sum_{i=1}^N \vert e_i \vert^2 \right)\right]^{1/2}. \end{equation} Finally the maximum error, or $L_\infty$ norm is calculated according to \begin{equation} \Vert e \Vert_{L_\infty} = \frac{1}{f_{max}} {\rm max}_i \vert e_i \vert. \end{equation} which is the most stringent error norm. The inset plot of the individual particle errors shows the fractional deviation for each particle given by \begin{equation} e_{i,frac} = (f_i - f_{exact}) / f_{exact}. \end{equation} \subsection{Shock tubes (Riemann problem)} The subroutine \verb+exact_shock+ plots the exact solution for a one-dimensional shock tube (Riemann problem). The difficult bit of the problem is to determine the jump in pressure and velocity across the shock front given the initial left and right states. This is performed in a separate subroutine (riemannsolver) as there are many different methods by which this can be done (see e.g. \citealt{toro92}). The actual subroutine exact\_shock reconstructs the shock profile (consisting of a rarefaction fan, contact discontinuity and shock, summarised in Figure \ref{fig:shocktube}), given the post-shock values of pressure and velocity. \begin{figure} \begin{center} \includegraphics[width=0.8\textwidth]{figs/sodshock.pdf} \caption{Example of exact solution for one-dimensional shock tube problem (red line) compared to the SPH solution (black line/particles), utilising the exact solutions incorporated in \splash} \label{fig:shocktube} \end{center} \end{figure} The speed at which the shock travels into the `right' fluid can be computed from the post shock velocity using the relation \begin{equation} v_{shock} = v_{post}\frac{(\rho_{post}/\rho_R)}{(\rho_{post}/\rho_R)- 1}, \end{equation} where the jump conditions imply \begin{equation} \frac{\rho_{post}}{\rho_R} = \frac{(P_{post}/P_R) + \beta}{1 + \beta (P_{post}/P_R)} \end{equation} with \begin{equation} \beta = \frac{\gamma - 1}{\gamma + 1}. \end{equation} \subsubsection{ Riemann solver} The algorithm for determining the post-shock velocity and pressure is taken from \citet{toro92}. \subsection{Polytrope} The subroutine \verb+exact_polytrope+ computes the exact solution for a static polytrope with arbitrary $\gamma$. From Poisson's equation \begin{equation} \nabla^2 \phi = 4\pi G \rho, \end{equation} assuming only radial dependence this is given by \begin{equation} \frac{1}{r^{2}} \frac{d}{dr} \left(r^{2} \frac{d\phi}{dr} \right) = 4\pi G \rho(r). \label{eq:poissonsph} \end{equation} The momentum equation assuming an equilibrium state (${\bf v} = 0$) and a polytropic equation of state $P = K\rho^{\gamma}$ gives \begin{equation} \frac{d\phi}{dr} = - \frac{\gamma K}{\gamma-1}\frac{d}{dr} \left[\rho^{(\gamma -1)} \right] \label{eq:polyk} \end{equation} Combining (\ref{eq:poissonsph}) and (\ref{eq:polyk}) we obtain an equation for the density profile \begin{equation} \frac{\gamma K}{4\pi G (\gamma - 1)} \frac{1}{r^{2}} \frac{d}{dr} \left[r^{2} \frac{d}{dr}\left( \rho^{\gamma-1} \right) \right] + \rho(r) = 0. \label{eq:dens} \end{equation} This equation can be rearranged to give \begin{equation} \frac{\gamma K}{4\pi G (\gamma - 1)} \frac{d^2}{dr^2} \left[r\rho^{\gamma-1}\right] + r\rho = 0. \end{equation} The program solves this equation numerically by defining a variable \begin{equation} \mathcal{E} = r \rho^{\gamma-1} \end{equation} and finite differencing the equation according to \begin{equation} \frac{\mathcal{E}^{i+1} - \mathcal{E}^i + \mathcal{E}^{i-1}}{(\Delta r)^2} = \frac{4\pi G (\gamma - 1)}{\gamma K} r \left(\frac{\mathcal{E}}{r}\right)^{1/(\gamma-1)}. \end{equation} \subsection{Linear wave} The subroutine \verb+exact_wave+ simply plots a sine function on a given graph. The function is of the form \begin{equation} y = \sin{(k x - \omega t)} \end{equation} where $k$ is the wavenumber and $\omega$ is the angular frequency. These parameters are set via the input values of wavelength $\lambda = 2\pi/k$ and wave period $P = 2\pi/\omega$. \begin{table} \centering \begin{tabular}{|l|l|} \hline $\lambda$ & wavelength \\ $P$ & period \\ \hline \end{tabular} \caption{Input parameters for the linear wave exact solution} \end{table} \subsection{Sedov blast wave} The subroutine \verb+exact_sedov+ computes the self-similar Sedov solution for a blast wave. \subsection{Toy stars} The subroutine \verb+exact_toystar1D+ computes the exact solutions for the `Toy Stars' described in \citet{mp04}. The system is one dimensional with velocity $v$, density $\rho$, and pressure $P$. The acceleration equation is \begin{equation} \frac{dv}{dt} = - \frac{1}{\rho} \frac{\partial P}{\partial x} - \Omega^2 x, \end{equation} We assume the equation of state is \begin{equation} P = K \rho^\gamma, \end{equation} The exact solutions provided assume the equations are scaled such that $\Omega^2 = 1$. \subsubsection{ Static structure} The static structure is given by \begin{equation} \bar \rho = 1- x^2, \end{equation} \subsubsection{ Linear solutions} The linear solution for the velocity is given by \begin{equation} v = 0.05 C_s G_n(x) \cos{\omega t} ) \end{equation} density is \begin{equation} \rho = \bar{\rho} + \eta \end{equation} where \begin{equation} \eta = 0.1 C_s \omega P_{n+1}(x) \sin{(\omega t)}) \end{equation} \subsubsection{ Non-linear solution} In this case the velocity is given by \begin{equation} v = A(t) x, \end{equation} whilst the density solution is \begin{equation} \rho^{\gamma -1} = H(t) - C(t) x^2. \end{equation} where the parameters A, H and C are determined by solving the ordinary differential equations \begin{eqnarray} \dot{H} & = & -AH(\gamma -1), \\ \dot{A} & = & \frac{2K \gamma}{\gamma -1} C - 1 - A^2 \\ \dot{C} & = & -AC(1+ \gamma), \end{eqnarray} The relation \begin{equation} A^2 = -1 - \frac{2 \sigma C}{\gamma -1} + kC^{\frac{2}{\gamma +1}}, \label{eq:kconst} \end{equation} is used to check the quality of the solution of the differential equations by evaluating the constant $k$ (which should remain close to its initial value). \subsection{MHD shock tubes} These are some tabulated solutions for specific MHD shock tube problems at a given time taken from the tables given in \citet{dw94} and \citet{rj95}. \subsection{h vs $\rho$} The subroutine exact\_hrho simply plots the relation between smoothing length and density, i.e., \begin{equation} h = h_{fact} \left(\frac{m}{\rho}\right)^{1/\nu} \end{equation} where $\nu$ is the number of spatial dimensions. The parameter $h_{fact}$ is output by the code into the header of each timestep. For particles of different masses, a different curve is plotted for each different mass value. \newpage \section{Writing your own read\_data subroutine}%HEVEA\cutname{readdata.html} \label{sec:writeyourown} Essentially, this is not recommended. The best way is just to email me a sample data file and a copy of the routine that wrote it. I am very happy to do this, will mean that your read is officially supported, will appear in the development repository, and will be updated with new features as necessary. It doesn't matter if your code only has one user, I am still happy to do this as it makes \splash more widely useable and saves trouble later. The second best way is to attempt to modify one of the existing data reads. Even then, there are some things to note: Most important is that, for the rendering routines to work, the density, particle masses and smoothing lengths for \emph{all} of the (gas) particles \emph{must} be read in from the data file and their locations in the main data array labelled using the integer parameters \verb+irho+, \verb+ipmass+ and \verb+ih+. Labelling of the location of other particle quantities (e.g. \verb+iutherm+ for the thermal energy) is used in order to plot the exact solutions on the appropriate graphs and also for calculating additional quantities (e.g. calculation of the pressure uses \verb+iutherm+ and \verb+irho+). The positions of vector components in the data columns are indicated by setting the variable \verb+iamvec+ of that column equal to the first component of the vector of which this component is a part. So if column 4 is a vector quantity (say ${\bf v}$ in 3D), then \verb+iamvec(4) = 4+, \verb+iamvec(5) = 4+ and \verb+iamvec(6) = 4+. Similarly the string \verb+labelvec+ should be set, i.e., \verb+labelvec = 'v'+ for these columns. \bibliographystyle{bibstyle} \bibliography{sph,mhd}%HEVEA\cutname{refs.html} %\end{divstyle} \end{document} splash/docs/html/arrow-right.png000644 000766 000000 00000002234 13261626263 017634 0ustar00dpricewheel000000 000000 PNG  IHDR cIDATH[lTU93s:J' .A.Ubjۡ-D!1Q|B|`b4&'#/$($`XJivӹ9g@CR&v=}Z[gyu/L&zoddd7{t*/@@o_"~XSl`)9պw8f :Nx:i_{0[KId^S_yckl6}||WD! K‘v۝AY~|p|{R8BvmxvR?_v(VSSsq)HÙ;===XRW{gWa Q/7vtt՝}0*DڨZStPIENDB`splash/docs/html/arrow-left.png000644 000766 000000 00000002225 13261626263 017451 0ustar00dpricewheel000000 000000 PNG  IHDR \IDATHKlTU߽wf:}O[EZ "&ML(((1 H! O7]jt!&*4`E7(yՄ }0s?Zm,=*S)kJSb[ີ:4444^3d2=GM fol &|ec\}퇟>ZPAǝc}/;s ڋD&<솰vC2 ȕ%Eg>H"2M9N&BP8dS[W6F.SG|7ҧIHY?> ؠJN1>H}a˲n#G G 10 NeϴI%xsSPUPKHj@:yuP(tȑc9A' ! NjH?(+_*E>~zҘ A#س淧wU^ `A/@2H$`S(M ;V?y"AP$% #!r)! ȁkqېEXB1"T$\dP1)߀g{}?/3(F#aYJ:gmM`.@B>=J'wkϷ>Jd[r82gfvQ-SGf@1RV, $ z RJFl Vnٓ!{?KO12*BZ_sSH2gyǐ2 뀛ڴrϦS R?ﲜ [WC~:g] 4T^Ssyz}>%dG_{:sx r@=cYK^k%FGuuuoZJ~}U6aAe…#eY`pt444lW#8XHd|GDevw<'Q|o\úymkSy*z/& X5k 4n"gom[68o]G'8hQ˦}/ҥfvm/`y;x<^__?Uo#9uSIENDB`splash/docs/html/arrow-up.png000644 000766 000000 00000002176 13261626263 017150 0ustar00dpricewheel000000 000000 PNG  IHDR EIDATHOluttE(Ih@1H  ^L5ăcb"cQ '4BAQ<"Miii).ݙ<%e>ͼy*2#~@wׅ N}igTŋbLmmڿ1[ #}_ݴiNֺH8V3?|~~TؑS;7TpSZ8 Fwu+DU7׋%I ! Cw{w:'J-%I08` lݾq|ⳟ2b*J*,: !+oB$?uKQhc$ ;n>XF'r܇݊ǂZFI*y Ҫy90CWnG?Ryy];{@ 2n]iEsHS+}JhRq g,phϗ***nռ=*xٶfB%15Y*:f!q]BW5Py A(y@Ӷ- _ڼqO!76ӁMـe@ی[O6pBaք+79i?W1ͯ|sY]?cp{|>љd&YԪң46'ů\X,v; Q[[k $;;*wU /XdVlDUf8D@4BҶw0Eoj~$MF0J"3VL># Y .R*ԟ4{ S9 (-b1o:h': ÞV{N0J tpQ?m.\F/U0WqAFc$8z p"ħ! c0,E]O~( ?51(x!mIO s5nRb|0\)ZrPC\pB qttQȸe PԒ 8`B (Ő1*,QPLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5?IDATx݅w#gwa?6m61efnڔSfNSffffffԻu-[{e]if9'4w$dGG[ot 9`\:\keC^^++_\|t$F%5Xk,I$+_\|tOiM;jJRkEG:6vtmhE.8ķza-JjpU$_[|PRMRawdCo~U$A"IAb4I:(7w+:gDR^$$F.oHvp`vؕ;ٝHjUNw .x>@Re#บ+yg'~K5'ؑÒsԿ8~9wE /;ER+IЭOR)IŹkG? pO;NIm1$ݻ_ v?.KRAUIAbTIr6X˨ʧo` _ "($"/$ƕ';VRUR^鋤@R`pxWRbLIx_ VIH vRb$$"*$",$FIL>=-΂2] .oBBRJR*IABR*IABRJR*IABRJR*_jĢ@RWRO0yJj(O`z$"?($FL߸/YЛI~ !)h%)hI^ֿ nTIo/p~?.설hIl !)h%)hIL It !)h%)hIT !)h%)hId !)hID !)h%)hIx !)h%)hIX Ih !)h%)hIH !)huIMV 3p%5`H ZI ZER?T )HH ZER?X )HH ZI Z-΂+ "U9ͳroh')hI} I}_ !)h%)hI}o !)h%)h%)hI}O I}w !)h%)hI}W !)h%)hI}g !)hI}G !)h%)hI}{ !)h%)hI}[ I}k !)huIQV 3K%5`H ZI ZER\ )HH Z-N,M+X "U9ͳroh')hI}c !)h%)hI}C I}} !)h%)hI}] !)h%)hI}m !)hI}M !)h%)hI=V )HH ZI ZER_] )HH ZER_U )HH ZI ZER_Y )HH ZI ZER_Q )HDR_^ )HxR{I}Y 8 ʜ̂9R*IABR*IABRJR*IABRJR*IA"IABRJR*IABRJR*IABR*IABRJR*IABRJR*IA"IABRj1;Zrv$Ei?87;)"*XLe'Nhדԥ&hIR7<ERY$uk$EYo%dTI %,Ki]~N0)3jaF+%5t-Ko.lԅw.Inw81J]$iSIJR>@R>@R>@R>@R5wuЫ0 'HpIHRHHRd:3LC$ :DZHj-H]~ڵk*IERd$H $upKz%U')VԺ$JZW$:8%=#=w߭Hc $up$.I%)2/}ћ7oJ"i'u%U!PRQIBRkYXteNcyE/zOqIs}óGL9 L' -+Z",QjׯK*56jyRg &]HvWمCIZ"(ARW}&Kq3I%@x 'gBRuI+~-%U$^0W|wjIC=ÒZmIT"G $u$U$)jKy?~2jFREԇHpIHRԜ|v2jFREԇHpI(I㞎_EݸqA$E$E".2bTTQ$AU$U$U$D$~,+s,KIvU$U$>U$ā'e6yZ-(I ?{H VV{H V{H VVԻH VVԻH ԻH VVԻH VV#U$U$N,+s2 "5mNTwhޚjʓ-LDWi~XrIMqS59p:-\kl'ԉ);i>^0'1+)FksvLzL9IѝZ09Sh($)(Q ZI :9&=0]a IENDB`splash/docs/figs/multipart3.png000644 000766 000000 00000045230 13261626263 017462 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATx݉fU~8Tw"u(]Ko3( Ҳ8@a0(nD&1DbfO4Y>9uOݧs{Ϲ}>~뭪w|gٵ AAAg ӭuͳ%/yI't'r)zleeew={ri^3xY37r3ϜYe,gsVfraGL^ۘcc`P?c:,ѩcgq2t~/6\\zk@?cۤ!H y1TMa0|:Wbyy:g*ghF1S6&[_?!`F?QY|?t^)S!zE 2z HHee' Y:R_w r?\YfΑg7FPF,*ime+%A(>~YF:-t,wZY9fʩdR |*5TTWRǐ; 6af?tRf]0Zh`6ՃG*A ߳Ic"? RPlHjoԼ3mRZU@| 5̝,&5h1IB= U5pj ,l_bKiC~^=kʗS Cz} 5V@jrXRc撇+U@3od1~qiL?KTdR[' $l HafIvLzע8MjN@jY2IRѐ v{;wd%E2*J̗;9̂@{hH-dfTN ;*DY:(3_jkO2gǑH 5T#ʮ5; Gׄ'ҋԿ3dy$ԎHHTt&u I BL9&Dhs@*&pH$chzAj.h$0v Lm8 y"=9D3HYR&R7d^E!%c'z[NGT(m)Hw$Ҳ#*6 Y@Ҳ#*R=2RsC1xULYX}lԿ6d)|w R RYk'z=UTKSSLOFg & )G*H{j-HHe[r6r=1 H fњ.iҟ 6oHKC@ @L_2yR(AzEeY)RS;|4: '<5vatj6vC}r*lȱ ~/!)J!՜(f\#f4HTARܐi5_@ Z H$IJBRU62Rk!^3a돫WHH$ 2)a"U@j0)9JaISKEI%fGIEGVzr9Gr/ )Gx9JSCY{b-xl e;0KHj;A `T Ԍoj3?)*2!e@ax)&Til@2F-5hpn7A vylR 5R6QzEd4R-")7zc IFHcCb-=cUA &GP)ZxR5$l%xo('I*m +:d޶z Tt>e*T)*HNJ> RDR#Uwڏk w ~+ѵ4+8rvޒA D*i T?40ѓ2x62nӑN!r}кL;)}E)DjѣC'Rg\z3yr )#5^ RMjk4j)?M3PI˖ȉ GΪ! R `" DCr! e%5\kIdq6-T;)ZnR~a0$@jM:*(I`DEOn!o=c ; R 5 RI}ǐt KQEAFJ]@۲Jx\& QR UfTN_?@ ^&G*uLQ7g@^yazMoR RDjtS RMj{R'w4La (o scFIu/7 R R @ aHqRQg _!SoĤO(UW[X=<-3=EwMerH|)mHxCOQFz$xBeUZm%rY ,]X!UwR RY@nH׃TFLa%EКO +ᰆIX2B'͂H-=R RYNPDOS 5$0v~8p`w*}R ZTuRՏ>n R ՛_5dR R 7!IR2zx Kױ^sמ=յurU UU|+)y RHm(o9 -C@e,^D/'H [gWuU"Q[UE<ЉNN?ɑ5r?lxW)** YRRXpRѹTj8)֎sV(4P,@I򊪲zmڕo+ R RYR U_2dT*Eʟޤzx5e *}Qe+8,_*:-룤b R U_4@ @JITtT:fRBfBO\TNѤԝѣGCu-RXm1jUa:ԅ-J!Td)R U_0dyIu;TQOpOѿHPE'K( U@UYI R RLj>zA J!D7%H)R]=ʓ˚x}I>@Zk VXT)}~!)IyCtcQ)t R wޝ"{ezbo8;)Or8q>VWb+%^ZOByWo+~SzR)2ڌi'F_@j_F&{Nt k8MB\3#|k֪ѣGRºjk"R`ES^zW]U1R))% RNjTRCTDm{T|;vlZ`s )?fV$HjЖ+TYS(tUH6RƐ=R ܤ|A ʑ!;>XN8&= z@ԞBʁ󰢶$E:S*I_Z@ !HT9R_7d5'FJYo'OJqޓkzR7Te=mTU/+"U C,i`7+: G\T۵+=kIM@j9HH# R$e%%N(^ O\MH&B9rgSnu.L/zM/# )E"5)&5R=H"% )1VRj%L)laz\9zU`eQXz9յ7=E{r71DNk!;c$:HH HHeMTtlH@R'^HyO42`=6l<;v oxSu5QuQ^$LP"U~#)tRẘDj HT.R/ RIICiJc ʩ\D94Aj1˵j!)0Ϲ'ORb*BXz]UI)Aj@ @*=H#' YvR +oF&$P&D|U</{G#A '!4ꡪz'@jIbHR?Ri8٨'^e^nm* p=b•Po87x9RBJ,UZY)BlHAR3r"@jiI%)ulRqiکƷպZc=墟Y )H \j06UNt+=J @(?nȤI/F&5A2"b6-<㛼ooԺ": RIɑ /k9;(`]k[$꘺'1Aj{C^Aߺ2'Kʱ>Q`8/AYJS=H1mRuCV,@j~JMPTNt᤾`Hhf]㝭"ImBUѾ()>_ HV#)|"(dh=zs,=Y\$EV*!:fʩ(,Re',^TQ=oߨ\ p)*H{_*cʷ!HԔHe4R{N*, eIo?T,1Xk9 ODJ(/JׄTHovsUJ9՛ԙUJeoFHTAR7@ TA%E(RwRS})F?eMŋcu8P/ŠJӧpR݀=H# Y?@ @j$x}5{"U]SS8H=Փ = mV%L U*^Wr7)/9AOyt$'"H %D3A~jlQU~J͕B*{m! R e&! R RrR]Eo%E=芧ފTԖoo%$}bz~.F"U2 GΦʩUuRIwMt^0dD)ZBR#g/3HTAR)ZRl݉ #g{tRWXHqOAĘf~N}Wa)`VR` SbEoR|T|J.F HTAR'b5_" R 5[ܱG*:tʆh(RtRG¾'e=~)IU ;ˑrR4@ @J HHe6F]HC'x9 At8#()$KD=Q9/7` U HM((6(V8@K~! R e&{ ))R'l )=mV:G *JxfBLK:⯩`"S%~t1"p<I =!)~! R RJʑcfMH/'*D*'D~Jtc(Rz3TNEah-He#>aґ«H{)3 UT ^N+m$#Hfv RIQyzs-oӄŻyQJj-|u27@ @J HHeMRMUFR|Gwm^HE'ӡnBxRO,FTL*R:IQR~`$NEYݗI}))% RImF崼kOH"IIBꚓkBHzQ4\B`~UaQRe)BRqlT(ISIu1Ӥ>fHHI}))%R1J-[<kNޙԐB*K!OoTyk VXT骈TWUSHJLDWl%evHTH$Cv*`EZR0wg #4J(fy^5,_VT88QO=Jp?i % R 5 RI}j'eYWQDӤplS<)&ZXI8=oۯw}kܕ-*Tկ:2DWH!-Ӥ3@ @J HHeh\<)wf]=|<%IH:XI/ )w"<뮻gn}t+ݗ*XomS͝{ST m]S}&!C@ @ HT"ŗG IWTz^>W=U*] S'HqTH'*\?~o}t*XW7U.RgZj9z")1W~0SO&AC@ @ H$! ՓDC'w׫SQ^_#2dNZ} R -HT޷< )Zݝz6\k$</pzuLNk:%hXHyRb#ECeoW$R{}{}^x?<|};[oőri GRfM7Vا.0bH/]Ub6d'HTH$!"]A)PKl|͆O6 AH?я~/>O>'E#gOJꧫFO#g-8)>Εԓ̟z@ |_hRǠo.HNʛ1Ig\V`SkJ*%DP uhGso|G\Ac^IJ譵TZA I򉮉^,65@j,qMjP)[y0 CD?3kUNRO=?}[ /Gs=m(`Sz?RB?=j-*HuR2GRaQJ'kLhW_ R U4dn'g(M)J+N቗Sa!WCQR4Sp #<=쳟ԧo~_?OrꡇHj|j UoTP@3>Pe'U@j0)I8HR 5{ PYIv9yxD]z=0 . ) )K-I]}~YG{|g>~vuj,_!OO퇓?6NUT)R@eGR Ry3:r3d N*ZHu"ETATt %:R~{|Ggy_c+<)ok+\!E!D;х']Ai6 R-Wer~)ZRQ?h:RABJ!THs=<?DG??:dN]w徳Akf8KcF@%ACRF 6]!XzR4@ RR U{ ))RkiL;):\d)D GOB)D*t^-TzR~HUʝ A|,)*Hj3*)R U#,;Iꩄ|D*ZNPk~C1lV(>O_?욂n6Y߃.vA =EIC6G#վrW*NJRYR RYR UÆ$R/(e|;j-TEI)*ሉDUHZꓟ'>'x;NTS>UHRݣBRphDHv᡺}t_%zW!w J@ @ w2OR%{4R4\"8w*QNQ7ʋ,f.~3 |_IwuMz'u(_T˾< R^RR UԃL|@jxfx9"%T餄*DfrT/~8r*WQ{Uq|bA͏G˩j+*w` !:቏A{^4ՃRR U݆HH)Ij߾N'r*GQw9E[u| D6q.<=!)RZ?߼Vm#Lh%] C@ @ HT֌CJ))ѕ.R||TRTR&1H)5tH1Xk&u=OBJ!FC@ @ HTl:Hr*lJ&pr|艓C ?H;bC^H&Es 6s%^@)c/)ʚ)unRĩSʌy1i>:}Jts[$L\C7 L8ޞy7>uSx=I4wR R @ ),zUTTFJRycQIEgG&tRrG:HNHuHH)HR}IEuU~t##͹SѢ*5J姂4%]KjvB*R?Q>}.zA*{@ @*k@ z! Ձ(S0 "XVM#MỎIzu*b[):G{HHe HTARZTj'. += +:wJ khE%x]oHK{/=a?<“&e,J$u!&FR 5)R)+)`RrJ^NFhJp=axմJH!яNlg^uN*NwRo7@ @J HTARu4dELbUitR*NJWa|vVX*+%,u=} 2W4G(j=-A_GκwXtI$)~8MRWR R @ H%&K $*U.,c,a9UN"dh23~??ww-ˢ!@ EjWbR U[ Yd>&a81c>$ui()@E$|tJ SP" I8*ɦ<IuZvHTH$ Rm# RH'RфE(UL<3_RT^+-sw\Îswp_7@ @J HHeM!Rz9I'z {-„"&S Oe#0>u_uWQOCH1RfȤI7ɶ/F)Jf~đڻwB*6w_QoT_T &I5 ,ǣF&%x Cl{+t7~i^œTץTg$LH$CLG)a/NjQRZ.=H)B[R oRTEmeJ)%GOV B*i72yR`R 5HDM*RxY_[-TnRN U[ g*c#xRTY1z:|8f(VO=|{zjC R'j8{r.dsf R%q"VeM ˬyHH2/P2S-JيF) 9l[o[럨D?^-ޒSHR~ RbȤI c1ʟ)*G 2>)H1 R4W'-tU/+,+JE+GUOkܓz'>lo)+ݻA @7R R dTm`RUhu%l UX4aF3Ѳ'%1n_x8XEIyU ^)*H ))R"IY$N Ҋ~ھ'I"OA_/kn14WEC@ @ HTXI2Rg=hؐ\E{X#%ܬ`7[^ܗM1ꉫ$6.C@ @ HTl'`|ǹǔݓBJ@!I H! R RJ@ )ӓT_N*^N\ތJ/B%t%FE=GRsBREx$aN ҿރbK0)cz*A*:e @ :dHHImF崼˹t%EU*e,S+oĐXӐ*kb)R\yJH$l HHޤJUak )+,eVn !Kb`)J!o$)*HmC̀HV@*?)VX+9,MO{R$b HHe HTAREWw)>t )煥J 6-m>J9b8HQLsA@ @ )˪ ՓTmUKw$M'ZjكR]R RYR RYNOjUE+cRm؈lX;-)zzx ]GI )o')2ڌɰˢ@ )J1RѹTJ'mp6!m%wo%*)ˡ<^[#/ RIYNIՎ"))%3(/ S[N?t#'(;&K{+lX;W=<d8Q!e$Ѳ5_@ L)H!EpRF*@N T` $uԲj.#LH &%)wBF?@ J|LJZ3𤺌UNvҩ"lXZ[G_,E*5f֓r ( e9sAO':HHŢ@,v)RS?@+a R)S۳㧦h']@gn}yɐzĆN蓥RG4yRR RYR RYc"9+N8%lX&k(V3k#)2gHHݻSԑS3Vd6ęԹHuMHeHHe6z"74K '1 sIDAT@j O_‡ RR2aT mgĢ* )ZKH-t@tF 5F4~KRERRu:րTݺv6)i02E*R r,R/7N*H/$HF*BҾ$`.U#3,r. 0)c/ZԤOkbYĐq*qy"2C@j봁HT, ) Rm"EC'f|CtUkFMRgR[' '1RSMc3?RHFO= lyeƒH*~o6 R)N3dIIه͞/7B=[q(5*:t[I R RROj!KMe^5F*<FLd!pN@0,~<{&1@ @J HHeMr$1w}'COF.@C-{g.s]'|uJj! R RJ@*@j3*e\{={rRry.tIY0d1\?t~ ׌ R U( dIjґ #ET}AZ eAөb;-~>K)^LRU F«H3NCG]鯨Us~,F@ oҖ%eȣ˥"L9QOgq4))*HdC @E`ʢgbJJd|j)[yNԞ=읦K )*Z4RKwx9VTetӣߛk A-jK̨'^EՅ;M ))Zrʫj-YQǙ {Kg͎s>b·׀lVR읦ُ<-|fjٯ3_pe{S];\(6cϭ@7Dҙ3/OGI۷8k2wbϭy9=Ư׸wؼ;77`Imґھ#?۾x OY; iz0s>M)ƘPTdUq_ 5lk'x~WIeZzH͂W4H-3I'H+E[x]yFYL2M{U`;GCpԃrr=ѻ)8s~n`"              H3V{Èt4#=)"$)BKtH $H!Ȑd\S$" I5wRb* kw@DKo&?V!MQYYY }R-6 YR5/             L1l/\CIENDB`splash/docs/figs/multipart2.png000644 000766 000000 00000043463 13261626263 017467 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATx݉$GEH$Ch,.]HXml 6o|b8:c/_fFdUV~|F=GUew^]AAdxf=F<̞<}wꩧviWٽ{*+++{ݻo߾Wy ^pV^(3;<2{~}Af{2cIvEnC=>zTg-Q> 'NCBɏ7trS~gI?C ]SNhxT2F3:@Y嬭rYLxF,3v6^cCO%*osLD-"MV{T" U$cJ&$ Y_ԜRGx#d )_EqO􆞟H2qODU\?y NOrB:RuF=N9tgII/C tb?>rX[u6 xt:)C$|<,*v)B!K7R UV\/I -]X jLbzsOZ|TEgӛ4NĪ5H- UT)H-;O Y7~2xGfRR²k nb|=(d?Ԩ* H'멲%H2fcU[h:N6'h+IC*4YdLΫ˩f'Ut6gϢ:jHNH$YR'@դfԬ˫&MTu/B͢n. ,zs~iѩg7)XHRĘ IU]H^ԅ5d&AjG_餜(#It^! (, +bV7 IuS$NI_l R ՁԖ.!#l! ua3EUvdY;ß:!_ę3shcD‘m 5 #SoO2R|.ī*5PUvd˞!GWU?R 8YxR?2@jqR!>uQo~RyU{18?9?aRs_/R\gDRT;,@*T$"%<3 A ʑH-pvq7DS@vRBo~/R Hٱsk3+VUrvzgL_/w^ +]k2Bss0TaTC(FuCr<kHsgRu@|ʓ7H-MT~?,[c*'e0#џ]d<Cby!)?J `> K>FEJX`Ru~IcTA& 'W-: 8xZKys I)T*HM[Hm2!z= uHm?!xAjJ= "S+ IkCr"5HTle2lJh,-N*6 ))RR RYc%EJ c>lz2N E(8)*A_tח)*G_)Lw%uVhϬTc>T u\uJ Ra% %@nHHMԜ'֘ z 8/qRtTXP:][Xѣ>eD*djFO R[CqEfґ«H{)FJY%Rc#x:`Z9ܚ,^Cj! IʇSC@ @Lj+*3=R RDWH{fqt$H?aZ"ѕbHHv R b)5 zĨWhɂAm=o0RؐW_ )}%oNzgH)!7H1R LW^J! pHT;)*GSI38;0C0NR RfR[Q9-v. RH]CRHN\$:yujU?T.$դVV@ @ @ cHAR3r"Ræ$}`ۍ5 n G V!\>Rs HHe HTAR3dʤb) LaݡHҭI?a`YbRېq+7H-523lq#HH L緷0% LF:홋S)UoX7v"tOpRː)*YKH@R R Ry['7 Wb\9C(t^`&j iHpԗ{G*D_R#HFo2_RP̶F'WIa !/tX E~8 w؛J@ԝDTH$ CN֘b~Āb>t!M@_(`B^=`e!E{O47B@ «HTVR5dHDʩH)VIq }6GRZ`V$H=^ R RS"UrT@ }k]?SY0,Hڨu Ti%TS魰8+;*RwosHHiwR RYp+;.̴#DZ=#@YCTssIFj1r)^5)lW,oHTWE 5v@ @*kKӫYBO~Ҁ<ē:zFs y+|lJ':"OIpR)*L*K~ RHiC`u.@fwNR(,^ޜ>%O~FG恺ibjX%' #GiTСfzUkcű~%9HVP@ .)J!HjeeetRQOԫq 7 0'Rs_xUK/8.RRTU + RIՉRJŁHT7 mRCLz[1WN ˵tPV6**e+J*TMR))% R 5IRmk凐z )0Us~pBj(lR =p%C6^~ FުxDoR RIM; R 5ZxEDǍF?zi(缊r9zM/ +O *QNUTH$uC@ @LOR-tUv):};d"$1XH Oy֩x<Ét% Uч/BR售*ZN $))!;Tjwضr*QBE=Q1 hYh}XR4UT7MJ!H))*HkRM8RIsν'!H'GlS$BUVXZrʢ*5J] G{HTAR_5@ @JI ) vB{ ;&\PYvϏk)$Bۂ"ɐVXbr?j+ESvUr*E*/fX ( RIm+:5@ )/ܱkR';wq ?iQWlT]CO IN+ZT ^$G*EH E)*H+H3 =-PqɏzFu5hV'>}"uNxw+IkBXJ9T} D)f1A j\QtTMzB&1LPpO eQs4HmQU:֊CUR*UA @ @ /:Z'AH5{c}K%A*Hэ)OQL!I U ^oDE+,RRR U1@ @JIFRJ+XBO~F5_W_R: /JXK_;F֪J)RS"Kd**! R RJj]2ɝw7<_/qC\p"IYޞHp#>nKQEWF*\8r*:ޛT=F{HT1R_2@ ̤!;T&IwTM&SH*FgY #,:)8,rkR U7@ @J={pR1eΫRR)œ>?Pn!9QU)R\O]U K։殙QU-JN*4gHH)ʚT+)*4 /,©q!-¢*:M'R*tS *@JJ\ LR R @ J*GD*ɝ_NA!(UHnR%'+*X [!VUa9ΦW-R_oS(Y\@ ʑ! R RJWlpTɇFKJ(QHqIG;_V$P~fUgCU6ͳTXK-:)JoMqI*HTUC@ @ HRm)ɓrgYWïz"R;sD%,瘢~1VJx'EImZhSkF! RI=mHH-N*:=-vL0E=̤d9gDnޚN튫ZJ̣R5)X@wR R @ D*VkӁ*7lst( IQݓ6[N2vRRetR7@ @J HHeMvR K&Y':&hT@m©ClTTUVQR.~! R RJlN)!R'b)UHs),x$$ίkxQRLG몃l H S۹};~! R RJt")wyaHMQHEycr~&,RUwRYe|f= %ʩ YC@ @ HTִ%S]H)=49pc1~x!Ec/,)*]e = E$Ī)'F{M"OnY4*$0]z7oTq7z[Ͳ*TOG)h^=Ek)C^qHͦ@jH؟ HTARO#5eS#U~&/{(<"b)ZHz%=+na*z]IEkhpRn >iHHA j\Q~GRX^@*=' )?S~Z5BVObKh,R:&'zwUW&}\qUHSJ4P2D.$(Ru>M1 RI()@ f.<gR4Ž %Juˆcʡqz;v5\Wkuu7:[W^yW&U)1^`# C@ @ )i¢@ JH>G*KGhZ`sՔ JV+TjD _H9+Ǐ\ߓZHU 4()E{׫a+%LW))*HaC@Z2Ց;TRjs}}Cs :g$zj}()R$:Nt'QC@ @ HT$UM8JU*[2p;pSLT>R~#Lj\?ַOwxzR{BRHtITQIZgwaH &%/h=R_}#^@ܫo3dH};)޽JʍWQRb7_)uر뮻Γ;?<?'?ɯ}kw/~p:R~{6d'כ'_NYHqUb@5B*ZKUeHE'­_}#H%ߤwH$,v.=O`.r_&ENJxB_-En;{9q}g~ӟ+<oϠ({$:$D?R_:$ 'ųZ/5)aR UHH)hT0R4tR“ ]8NsoO}O}S7ӟ>vmlnWIz8 AU1},HHTAR2@ @JIiRZ.(Tр9L{B)|<˓zGx WN}{җްO9v7pC=NTHJrh|X~Z @ R RfR' ZIP6Iz)/f'R~khlO>nfnZ_RByQH6 :\m(/sNJHK Tk@ @*k@ ߐF*T'ԃZ[t{`HHXyADiRVRV>u'7Ϝ8qYO_]5N )w' 'EgV)~g}?=.RR UԽHVTNKF* T =M:KpzW-9U;0K^3OWTBʓ Iڌ{o>Y?N/&^ @ { ))3 ̤VYlWR:C TNTnݟcw˵՞Hl3L_ a!>!F Em3I HYR UHH)+RR&.(rO{UrtеkOnhm%O!cIL?GoDT;JH x:nHH83d"+XH0$HEUњ#*rtRytN*JL9u뼭N>UB O̠sR9eaԕTBMR7q@ @F`R@ WA֛BW9{Y3TJ"UѼPH)L)OD*0IHP:QHT"O Ѳ@ Vs@$e"M=HT [\NʅS^5 uQW0I$.z#& ):}iHqO M*2D 5@ @JIiR<:_1U2h_ ŦE{lXB]=BJx)7_V_{R U5LL[@ @*k$)a1RbDE%LqR\ͦ uϳ)L)@`sf[-)O:)eOiR ))RҸI 3LϠ뤺^' u! R RJ@ @*kF#P@@øV)LGYtȷ7 Ul?* tū(ޤZR R @ wtvޭm4Tf(<ÉUTV?h^ODSs\jD,֚Y.vZ>T֪x!˻/7I))% R 5HV[U84;"-xӄVrG`j􇇓IMꭆHH))*H 1UgTwBa*n+ZQKMbS*>SVïWT?QRf8)}$))*H nUb)*j!(TOۅ3DxP߄RH#$HHe HTARo1: ar*ZQtJMVt~ TQ!ktCv7Z??z9yu:KH! R RJ@ @*kHE/(Z' iuQXTɥK?` & &KҤlHH)ʚٞ={RN]U aBaBUV+.WtWm4gIL/5]=R.R{rR?gHHImE崌۹H#,/ sjJIT誢Vfa"O<# F O!&AJ4Tj$?njR{pR[)FqEI R UUM,;)*FRU`T`i-TՅ=H ^HEluk;!u*tR\U1AjHHeHTARo4dJ^NQDyhpFy-"QdmT]BO!S^ZHHNJ26j6`4\O#xz@ )))R$9+Α2lJzE[QF֞Y S-X)FqEy[:RxR e USىWoj<Þh ,G @ @J HHeIRJϣ/QFr'd]vƇA>`P!e!Qvhx;)sj=%. R RJpɔJ܀1Oe䣣 F<֞Y)*H|C@ @ DHYLm'e(?r)?왍.)ʑz! R;E9T !uAjaUwsCoxLHH|9*,CT#쐴nRK u! RS H%c=~Q H HU,C@ ,jtTR#c1z̢*3 )?yH HM!yHMvR%0{D/`VUHE# RR# R|&R Ȳ~|2>j\Q CTW)R -ƫHԘ $~UԐ4o[9S )>cȴ)ʚ@j!KG='~4Ϧjz=-;6N!sCEm'ܶYNje!))N7dII"T<!#!i8ņ7iK C #E H-A@jtN3dH&}Na;S ))Z~Rlj(cd̨+u>x )WIh~/a_Έ?yb0:@ @jh,k=ѿjAN?+d mts#      bT7}2S=%S=]~fwgi4Oee͒'5CX39Vtb;&3ǎgbw"~HK&vzA YLtp؟ˤ<Җ)ڃR˝NQ̤<                   2LA H!H@ ^$k@ Ar3~5                 jDh/IENDB`splash/docs/figs/rhoh.jpg000644 000766 000000 00000423766 13261626263 016330 0ustar00dpricewheel000000 000000 JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222V" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?x+Lvmmmi±0Esl,9d{*LO [ghz=15*́W8<Vb60&F#sc7{aklvpܴD@ D䯖bs@΍u@Mx/3;7'`{gVg%xȢl o)< 6c;qր=fVZ$Y?bv tkWq?yMF|kz_> }jw/ sgqm0N|/黏mmX`pxz.hmuO౸]MJZ;Rb#S}kK?.=޾t8ΗpȒ|ؾLʬ>vB+I$PeFXCKc]%ȏPFxwFx2<S.ak&}Z6#I&a#& ٍU_ ɧ:tOkvxa4]Eu o*Md@ppyq?p -? 56E<ƄFP{WmZS-6Qv p9][FkkHKũiVbXxZkA)m -)2oiJrQRզ7!#{ή €=+R Kۥg8r<0>ѤOa'NTbelpf{]99%|/XE+9=q?HP$WH +dIZel/v/~QΪjz\74Vљ%p mQrj\/ ŷ.Oڀ:+Z[(dd{ټ YN"O<#,~]y;TnN9_ ,-{WMDn[ph+1ހ=OH {jʼn8;n2k .(LN)Eۺ .qxmW: [8% lM78@:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?H渇Ϳ 'Q+C i|_ [bw|@?Zonc֌/n4}GU,ڌȞmي@qm ӿgg Y1xt.zZMfmTw ,k/@eiO_+ݸ 6q$Jy N7@w?ԿjgܮUiq57ZDK$yf<H+~\qD?{{k*&GG^Zs(0\=?~?g,V 4SNVOo;}$rmvr˹i+OOsvgΩog̲՞M4F2I4l x4 ^G- (^ؤ n# ;Wɫ۬h%ʤ xQ{U?|+T0IJ#$,Ûcb?pAu(6Cf-y ],# {0h7T*wAC@J<?6F-OݼvUuAG"?-y'%|X.?$iጶt&eQۺ6tiw].##G= PN[Ko5 m">&^ڬA!'CKo2I`gg%o: d/4n+n/*qk՘q8j˻{¶I-R@ѡ*1 aj0k%Dg"bR寘 X6'.sXg]IRiQB.,h ψuĐ8ڧF2#F)-IrTewUX} NXȹ(C% ǥpzN[voj6m>e(Ѳ3W6n-u;kZ1@Tؤpop P}F:d3@+RM:uV\۸jp35tYd@H3ʜ}"MRiiiκ)&H7,,җT]qleۻ=xf|.ϭy ۺq@tA`bbe]^@69OElpO)+h]I&V]m+]yAm+`N!T +Td3./HQnɆ|c @'׏ W<"qe4(zgPcV^p!$F'+h$*ȶdJprF=ꆓe 7z"j6zkOm{-W1-cEU鷌r[+^&f(UJ#Y8('Zߌo&yHm]؞4i45AKSyH$+Ek{q?y>YG|: n&,abA\τY> nM6P\,'wɸ (߷C+=V;d;K"#9Bxa+4K$<-)[3!4.pO֮&Fs:[I"79;f=+bf6A-@;_km$B?ʼ lMմۋ+kiVRكOϒ q"c/P /[;+f[/];5 Ӭ')HP$*]G$ה|1}wR$)bXbA,×]%y\ =3\})dU|Fy7CׇJL/ӓWF÷m4\Hj9jH׎5mCPӬ<(PY*qw\uxZgt;kHorގ{VΛ#KH,УWV@<_KGWk}do5S\yfv$>\[\k7V;™v2#gkATFdS Zy#:Oc60<JrSoLۏg\MRE{5<뛈]c/$}+PC/Vy 놙%^'oኗ {7;3?ʲSY[5[c6$XI?7ZGT7[^E.xUϣG7=m" Ptfe$늪6b`=?|/|/^"O%ՕʱTu @'x}8N3\wCo|eTo|eTo}P|/|/}?# ^[)hl8}o Vac63ypD)i#' 03z GWDmtK"1e匶'u^F7>2Mg?a>wѾt/ߏ|8<U.7n9 KGQ~.ߏ?#F7>27>27Ѿ<U._F7o~/=~x]ό?*o|/|/}?#?#FM^zTn9 Ko|eTo|eTo}UU@_Il2|U.78__Y\y402ssp0,e]o|/|/nd$@v m岐<{p3{s@UUWuXT+ 23+WڌGKbkt>B ӜPOGQGV[BH%>Un&,q I͂ 295?M]}hSH4I`?J>27>2ѼIk\]GjčXĊGEP Z[ &OD_9pN[o~ KGQGW1-bҥxcHdouZWܺw.uk ckhY-Ev 'px ]ό?*ό?*4cŇۼf\CRi#.fE-< ,g+uCdjJ*F= fiZIh&xXN?QhZ4ZD@Xj{['iUN#iUq>2d$<:F/ sW?|eToUZhh*OKMȻzN[iܻ>2"UZhh*OK[MHۺΥWqT$pO&UZhhGU7Ibi. {'s@i%  BjM/i٥R\A5|2||]/}#lp햸ns3xQd-Z. IFluj7P][$}@~t aES>t-n m0:#r_Ү[+5;X};DH&\||Ի?_V?|eTH&RA\h, `a* Lz"EZ'77$ Aab/oU?_O0&u :bDn0?ZM/۷}]wǟ 44Sgɻt/V?j.Q1Sό?*44W}#q>2||]//\Uo ?_FGU*twwό?*44W}#q>2||BӞU=l%8|Uo mO[G3$+:E'bK/pƲ<ӄa|9B)VTz_?DmCb$Ps5`L1S&ҹZX48P1i[Em^̫v"h$wƺG%.G#.tuUIla_ٓé&|F!5A$'zS1Z}dqѢz kg CZu={;+(-b?5~`*ش=Y-FA!yt1(u_ QCqSVC k*N:=}k{ ? 4cFEYy7#׿OG#׿OZC{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]C{{^F?]^F?]U8gi#Yc:?"ԣfO%WS$Ў@vFnjtXjP_MsnI3^:1/b[^jEl"\b05؏]MB?]/#׿O@d_֣'x /A<⪧.Z 3a|}ĺO]#׿OG#׿O@m|KwqgtmR0)#ңLJm-6If{IH%xdbJzڶ ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?  ? m3Ww2\V#׿O^ |Z5m8ih<\g(;vʏS5_6-o/}||ϟz hK?@A454^Bo/RzP| нg(Կ^G_>E_/Y ?/}|7iג7"@]$I==?/z €=rDuq`۱+_ d^*̶WkO(OX<:ie`I#{`fV)|pq5z hK?@A454^Bo/RzP| нg(Կ^G_>E_/Y ?/}||ϟz hK?@A454^Bo/RzP| нg(Կ^G_>E_/Y ?/}||ϟz hK?@A454^BoH'&g4xk@e]y5^1e?` ϠWTdhrJ%"j̓I-vdyI{{U~-QZQEQEQEQEQETp X@셑Xq0 RPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_xJ+uZ߯B꜃«8YEsx?M5N?Mf?4[[:-4qA}Q@ SCt?Max/«&|5LwXEe Hۂ@f}k9A<B8CkW[%2&J*>x=YEsx?M5OVq]GFg=żGsYst+9A<B?  k9=3Džlm^?];\O(gTX Q. 4?CAErv_<+mu/iKmEEl^r SCt?MtPu u֌ }21ae)#n qA<B(NDžnnZet:MsPq\*h.otk+s[w1nH0'=GN 4?CAEsx?M5OIq];Fy=qIs&T 3k9A<Be¶ZF.FtXDQ.xz(*h.;_Uu[ciN4Ap GwP?'T]T~xVN/%XND7:9YEsx?M5OSq[T֮JdM:,IU8U(}z  h*h.Ӵk+ד2in@.r #=O^ 4?CAErzgÏ ڼ3FvQ#d+Ω<*A죁\*h.|V֭nь Y:Fl"ڬ m$*{'T]]8֝4^ѠK[,'Snu~s;x?M5Q@·֩ ѭ]n ȚtY*p P17?  k9A<B8𮝣X\xF8Nt̪sNIz5Q@ SCt?MS>xV5ӵN!^VuNAUe9A<Bõ_[ZxkF0&djKt zWyEsx?M5NO}f|?, :-3e\񌨍Q@ SCt?MSxV!5N%RUvNJf<9A<B|8𮣣_Y[FHNt,@qA9:uQ@ SCt?Mx?M5Q@¶6 Ѯ'HtY r {(t?  k8= ~X:KM%kE%a~^cܓ޷?  k99>xUkCo4Ol4ѕs26#A<B(?vZoIಷ}2F$9+3N=sH'i%ğ!RͨM(xñq}ŷ? bu+#2g> R?J_ew45ظnZ T &(9J/q_kwhouS>xϗY~qMd|clx)HF0WqU|/K =ZcݼSE"rBB|Ҳ)';rOÆO'eۄb `15]3QQC2\C‚(Ug~wÕf@^||*? }uzgZ}ay^Igi=x rS 41hzG#}aȂI^uSE䝦DA)$#@)wqs6%@,-ovm8D(L\j4tekP||)>cŚoYx#[[/h#AE`:a@bMI#I^X̋$|/ÃV=֠}.C=BYU||둖 HU@}ay^Igi=x rS 4-{m'o6'OA vfM/n U,:IJ6棨g[o=9 yɼѢfWكm $ GoOpKfUv!HX$DZA4) &븮WnV5HK}3_ӗC0&3+Dav2G yoE#o7yh߻yc|}OW0naw=sn`00'=\!j:`e8rOH;yERhֵ=?NW1ዽ{eH챿}b5QcgoW ey M^2YXV)4L^Fr2] k蚗n$D# 1X5ZvwWiWki)fpl0yP z/|cGF.7{ve{%juq[TrR/5A5VI4HK38$񊹨XkR;`1}T};~c@MD֓Oҥ2o-//G4>Wel07]G캵6TK{K YK 7ܠo Cpiݹ[{xȥYAxh?.DԖ D\,( Xr{Pyl|Z!Ү:m984t V|5ag p1%|o"8"63(faX^iYOiu7o I'{vUaeX Wx-ɖs5扨&~w5HT@X_XKkc=Q,hI#WP8fH@; 8 _?ϵ?ng6ϲ7睸c_}M_<7v^w.jzN66v|/[Hpό/$oiWuOnnKu$|$p=(.xVⴁG4ڃ:(}F[+7Zeٴ;} oැ2]AṼ$Iu޸PCax4mcZ=L/(,}훶P 5N='RKTO}>-$n8g{Z ̚_ OX%Gt omaV%Wͨx~\ kse<߈pG;k˽9o MeY d$F|~P#wYeuwmC*ePr$`;Ϯ;K-gA^ Q[Yj.S*e@19Fp%k2]I0CmX9~l$NILwOfiWZRN%1@cT 3ЛYơKo@To݀A] wjgvO4Ȗ%+6Y}6DaMPhknoq>a4]Xڛ|)/>$ &H?F@@BXEwC fukw*>β 9;qk>L{K12AjzŢnP:Q [afwUNqhaA {Y-tn-F1d(nđstPEPEPEP^Bk,λ.>kk>o Ԭ-J.uѺ5^i]+:Hya/Ҿev|L_'**+}WEUQEgzޝy_oܑgګ6vF2*uoż9#`FApAu{b#${wX4WR6jU]6P=Zl-/pJcam@ԀvNU,nc\[*L$Cy O5ג5q͙cŔdB 6LpaiBL~_c7/|s^@ؾ 1ݭg$'"O3C6'k[0D=-ͼS$D0g` b;}B?259F7YʧV(o+lWởG-Oj5]GdrD6rul}shy_2'i9'Bͧh6Wr^OooR\ɝ2I9$g((((((((((((((((((((((((((((((((((((((*CZ##AnNeXGힽ*r,,|*_Aqkewm{00!di"dH$sQ\]w>KKq 2Zx mTVB1csz>2Kygb*+.d : ,6}m^l&?Ͽ^6t۽W`bNO.$)bI *bH@$8!]RÞ|OiwiAs[hZ+x-J.!wyhPmkֵ Q|Acuykźϝg*JTcO׍ޢ,.2iK yCb]>3c88oZ]Ķ>TV m@rPn }P[7 h:d,\%XJ&iHp1o?ykjֺth{e:hdXIGHbۣ\+X@EPE^# {1V# cCEu]qz$nm^Pyطx*Р=P_xJ+uZ߯]|ycQOwwe#rѳ~# iyү4RO,9YYr7n?p^ ~kooH=B\i#nm"F-W&1p+X'֭OgK,o3m(9bes7oTN=S.&#&=`>Y|wdd߉<7s^^,mbc&)no\F.1>G׿L&VPUTg6$9;6yjSV_[M-/FI#-:+c9s$ ~xZф q,rW0a@&MB5)yzח1[鶐n0u S&g1A"]Ėb2ĥw#h`j//t ăPЪKuGC&1)l{*T>$K'zW;$UụFAli$ӭ;}g^i*YYYr7n?p뺖kuݮ-F "abʜ+rg#[SRG\YMz$'%ZTwQ4" 캤rzhkӮA2lCKF= H_o?X]vl6 尊@,<]gkuvvy@!`+wvN8ܖ|>tTVZOui#g߈rfnw h9?*v6Qʂp=Ҹ$g8U]HY 7.)|nF=%ӟU(鱢JIF&Žх65ZQ/.#cJ lBieX*~1rlrY=VF 0P (=UL&hב=*L!TϜ. Nx3(Aq1Q5;o][jN$v` #"LrN[lm{{I$$.U b̧+?^YtX-'I-="F"GVyJna.iڮZXk(gV2 vPF@2d31~"a]hXZlP,Jbgxn P,|wN/4$iSIL"Hʧl1T@8 p4Gt{mAo,w]ZKyoi:HipDL1 YPcC4M }&/ʻQXTJm\#153V[إ=cJ4#8HAX'YK 3xN}#NQk;-a!Ő#`fjJkM>71D4v8rA 1IIxꈑ;˒Aی'9$W`5DwutYmN7e_3"p++19T_Y,1j]%֤#J5w , 0ʚϛAZ`] Z0s.#۸y[J (cX&ɪq*@T\THBL8ef=3XS[֮^)gHҢ%#%qq}s:7v<ȮbeVk T;XyN>̻ZRWPQc B0 [#r vK}R.K;%,m,)ǙvR8*\xG> /!VRH͌d OIE-F]P[V[yΒ8A).7@a–`Tezlڵ[bwG1T,p .mQݙGz-Cl[LҮߴ}yFָ7Fp$>$WvW*[HeYZR[. LSR\>y{R4>i1O)cF`Α4 ڥϑZ]]2ng) 6d<\:դ}&p\KC%F.XcT<7wIt[ X>lC]mR?5cb2hs>m6IR)bդfhd:r(kݤ1IuޠE4nA!\*3# C7lHnU(h|Eav^Lop3\bH'icw͑ y_c3HJ`"4}aF)A6$q ak˨.m;8_)70T|)xIltżL f%V)99*Zz ݬi,VRbC%* k;;//m>hŮy/x ۍa$9`p P(4OC:ꚕI(toL;ea͜0Cl[LҮߴ}yFָ7Fp9/MV[HZ4%GBB!wsNUq OKG_ѿ Dl-2$ya BF;O @$+BB( ODӵ+o0웕6Fr6U2\0B pFgDdI ʬA`P U$w=*J(1+p bwEG(ʤ bm֣h#%ܢiwEz|O\Պ(.icA>P!7ltsʠ6u( (9º]s u/- *]\sgp:0`AAr7m:t-d漙r'.I ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9sH?d+(h~noĺl:7}.ݤh,f Tr3(A|k-wZ^˨6ܓ#>leO;0#5_pGcie DX*rX$bzxC{ ];UpUGs>Jٓ8!]υFKVvfFdP D2dۜxR4f?!Db+$)P1q- hEصH54pܒ%n,l{H M[Vv~&N7wHbf8 L-x[)4k,c7d\!V-cuP B 6т(a^UmfFx7yl[`W.7jSk:p\H3bI85Z7|s%%V?$ڸ8Pvǰ7:I+RDеʧn1@##׌Ω5q5-"=(\NڢWRW./X|$KQ'j*;j`Qͦ;ʠo.[U=.L{K12AhѼWgzHnb.l-o28+>\<;*Pݹ0 pqn}ev/%c;i;8%nU\C5ے+.@l x?Ϯ=2MZq$mX2mPm@QK>Ŷ8><9=8^9.|-@6Zm46%2m!&XjԤ0C#Fcd "+,O>+<cHt1𾇡[Ǧr]^iaJ͇aE3.rpB[/f}uS86Gپڍ9EF;NR ^hpiri1Wrdvypm 1c9/extԻ{ٖ7-zdFI7`eLd`ؓjnե a%UM'mf] ػVumj}KfR\F@)ݼ>escxt7˲;E#hemLv[$k[iki<{x$2n*vʈ2v5;;Ko =wWc`0Dݕ |G f,:SY{)nVrlYOrf M~ }KNi6y 0% YǜrS&.&/cRϻclVz]'|/V1麜W}naĘQb9˜64;:eݽ6v2IHbyDm \r1^^A-~ǧiO1n'm6-mt𮭨Zvf١ )K`'1d#91~aCn.$Օ/e"K[yfR;eLSi7w # Y#t(pA+2Fr85xVҞ/(Fر샖9’Kn-Ol:\4.^RB v3`^ XiZ{LI紓r')[e!Dyo4e|@Ԯ/.^.-7F4a@ic8,W*y:5HӼ[gy #bTq-4+Vo N.sww2ܖ!XAwmb+`)bc<9Hƥr:Ŝ:dz n`hK f@Ňp!^r4{Mj)h$e݋uS$ఆ6@xd[; wnMn9 O"?#H1(+io[4n[4Lv]OAR5彝֓kuSL7@w?Կj((vk:os w\e9;9ln=PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP'0ţiq޳EH~q_<y鞲sH)}i?WחG\OH`r>W·_5ou:Q<B6F۷ S:jAjBj~EJG6ؖ@DFpC<@?\d-h~D2~D;I,J$Ysy'uu+3s%$^Լ.\Z^zI9BNTAėWjuwfrF@\0xV}ua# IJ;,̤H$ Wk䵵x^BR--O ZE\յ+ۋBy%Ē(*@rNX@?umJA]h]2fC% R{GJEXk䵵x^BR--O Z7 '}A9' FX8U,KdI'>ظFqhn-2#b9s+K;Y?\tUS][RTE.X9{56-ذbc%9f@^]^I=F vYpCĖwVrjkw)rHH%slryV}rVԵ {[{B Dm3+0(=(umKFk/PIk3Dr R2ǰtPmJ.m.-B4|38'^H=(p؝6I+!9pYnqtPBQ-2 y.F.ܟVfcXVԢiB5!1etP[R-mu -esLΰp o,|oMS̍6:<EI<]\Kqq,O+I;I-oKHf.[u uU.%p1YrjS>Vd0ANpI䪝8 3 9jtPO5W 8x䍊09 󚱦ږp^wc;!FfdaT jڔ\:\f1#sʦp܁:( }]<3O39JlJ H%՜g w\ xEX}?QfKyRU2T>d!NA5UodK,۽Gm4hI;-tPյ&KmBS3bN~rNh][RTE.X9{56-ذbc%9f@]y[ԋߠK7O T 9RFx8jS>Vd0ANpI䪝8 3 9jtP.,滞K[]g%"rT/ݔ?01=}:(+#ž#]NCB]I1/nͺ Q*2 *1<-p,ftEw0ܪą$u`}Ҥ((((((((((((((((((((((((((((((((((((((((((?d++u8%4O֒D}Wcpeψ<-|U WK#C#եmj+1ݑx$}_[/ڷk(a8PJB =* u6Ǵnm砪X^[ϴ/J |e|#g~SkfsSoNZaeg%#\XM%a0$*1ǏIԦ"OKv~eֺK+M5:ݙ FH]5Ҽ"mvţ8=G~*Şg]2c:OwksnR0Z̬KM@o3ӼQaỀ:}֟tmV(f"\ J0Pvc8l^L{K12AjAM`ݛ4EwQIl`ruf-b,kn @FsFs݉9l] oo!h,4{#DQc~N 5GT,LgIuIo(C3eX^iYOiu7hd"c+meR`2A9 9-b+ifү&5]H/HzTP]?KUϷv m+"@9blu?4tnbKݻZ_V3[/>XnʀqJy6gXx&)!')$r)VFy1]ƕm<98!koH6G{n(vÑSg'ku\M #r4T2@ khpJђ$ 1TAz+{}Rymqj 8J/7gQԵqjS,V##"0(bSj!Np8mWM4RRh/oc[Y/JXLXqg .B/=* u6Ǵnm砩&ֽl'5(-Wu&fP# AH4{&C;MN;o~kH5ȏ ^7Ykk JOGQD* VfD0#2ܹ,FG rOĐJKV9܏e #bc`sXASJRdVI2cWyPZ30mP>*Ku`5]pHе~Whc[}8Ǘϝ?xzօ}0i-> [FUܻHO_=a># K,yMlm YYXv-d7 aw+ \ yaw76Q#4 $vyJwSg`[}_X_8܍٠ 85MMŢjOB[W2&2̼zok^I_% C v@]fm<98!koH6G{n(vÑ's0xAQ̃,UGH((((((((((((((((((((((((((((((((((((((((((<'aY? C! VO" PKa[|A(~%j袊(((((((((((((((((((((((+߾ȯw_~"=O_3|Pt?u&}s}3k~)zҢo$Q=((((((o+lW''MTOMBmOQIn^>UҺ((*Xn-#wXFܮ `r r(pZP5h.R\iltQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQECEu]rz jiMKvW3$|=uW>&ӭ[⏉+N2LlLS61#;'z^[^\CurrGʼzV|M B"$ɾ&e :+GpGj 4%ɦj۶,K{KcRY79/9zw4վ-$X_Jp fOc&ĐחzZ`I$ BLxvozfɮ5[7opv3vy>t K:Gگͦq,#`yon _;Fe-{ *{{;{Kkψ(aCn?*Ěkgu}}ȈdLdyJٟS4\yWر G=f[A+Ě{G[&$19FڪT_ m;{NEկ,/BҮ-a`-2ĕ!>l;i#UcHW(eCTյ(t },"vLy# tQZfk~oN}%˳9v4E\IԦ"OKv~e֬^k^^45+W$tY] *.##=&2!k)RJʢ!UK1PI=&GSisjio%XXč ?2Oq@X^[ϴ/J |e|#g~Sj1n uG)ڬb}Ҁ#Hs^lM$>Ҭz`g%>̋&lA ր+[Oė6pUo!K) Ÿew(\Fz\Mkq-RC+AQ@Q@Q@Q@Q@sa]KKfº]QEQEQEQEWM7ŚG 2uF1vf2 *€ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9sH?d+(|dC\h&.n7{kYţ)Fᢺ(zI-2+^_RA<ַ\[$36*G hAoq6ZM:35Tb, :ZmƉW+CB3L2ܐ&si0 _jw^_wu&7(䷎ XGǃ.HJJۀ6-|>4ai% nZJD`neptP|Ankɴp3^¦_.C!ű+7φuK?K:>fc ] rc6$np@sMkSh/ڬH3xY1,O, )3|i١qW>9QeP_]ƌgo͜0wyu%Kś:\[eW60@fU'`:E {Y̗V#$ "Tev}˕ x^F+m2 9R`1G$GWV!Ioig{i6]^׆+ˍb~9]X$d7g&~wc5ݽŝ-EMu J sRi5[;͍SK522=zf;O^GNi^O44K|nɗiw ǵY|-]Z^c7sK@́.U]H\䏛ZvUQKI vΒa d6:hе ]*yf#َ2'+0PHPi7[/oK}6_4$@.mR*N9cRO $vf}.+uHZN7clJ ~j -o]|RI";ʤrywMg5KI|%ݬ^lA(K3H̘ۀP[Z\$ kv\3*FrTvg8d-x X 1;X{a9dܪc pI^ܻ;MBIxLZVlcKư2t"-]!$R[Ǟ` wo#ojXǪH^c0KF^` QA =X_Z/>lPX[1*\4m ePPjJqCEr,D?;F!x85[eKvIh2h%b ˂X蚎m?7,8-7&sFXy_f 2$> K +SyP[Ic,pI'Tq+:(5 %1iZ1/{ЋYt4Io#{`)ݼ3ZZJ,CT@%YRr E# B6 ' 'a$9`p P+o}m,v+6#YdHaqQP,qI&LnЕ j/Vؠ.XCd (W>b+kO(V=N10QeF Sw.B5@c>Ơ_ 5\NDUҮÓ $`8ݴ6YPh<A^B!F| ~mPXM'O1sMyr%H9|Uy4+&;,6O7cwjoymTE>"x#U$Rxe>bROB|Җ3H|V7drG f)w92]hjubdB6 yev(r>Bŕhbysp3@w$%/0"x#U$Rxe>L[˛۾!)炟gYmN~ox{bkJ_Ȑ( *#sWiZiz·^:kZD$L|ʑ?6݀u iˣM;j} 3;$_0^_g%/uSuP\TIg͖_|7էK{i[Xò{SF>\?xwMӮ5v AgM.YؙvHBR>\.!񞘷7 c= }BR><&c=WN]ikVWͻv8l)6">b2? WpA$mS {p%ׇfmNƗiۉIh*?C7&}ؽM[' Ar?RE'ݟ6Y~_?bXY0X(ɍpR!l:ۏnk .&R_췢Hdyض' nMv[¦-g[N/Z.ITI 1YݱGsƛ]6°rAvgK)-*.̚_f=7[*hd$n<xvnG*m7WZ]/-W%gH|.7l#N,\2֭Zl[̍(`Ɠ1:kk' U-6+nAiiJcuE, }&M :ѯ#7zU B8\g*Pv?i/KLV1y0n*nd$UaG_G G񮂧^ HW$--^Z`ry៑J5h>$еQԴ'$ekBE5-OGI^}}wdhS|, @Rsֺɾ$Y6u~>1nY/.ZFw;{21_3=OX\ij[HH幜.dpѴfBr$`$~!j=ԉwo>KFStHd6cNJ0N ;:O^h7ZNyM>nmX]Y$*f]A̡v9T=Yk& =>CsOi=C8.s{vI:`GT|{ݪiͣ=;슿4w}-沺O}$1^EwxLkkݲ! ag>au>o|_gouQu.x#PlWcʖ/o@%shgN".cdyyw/x׃boymp3Xܤ!,20|?3Ew;˜]Fį#Ǖ,_/j%shgN".cdyyw/x׃@3gwlHBXd`'Ϙ~gl1ysT]K"Hb8pPa f?ӾȫKwk+']K<؛zc^[\=.7)K  901|@>/7n@ s[ `cq\URLj0u%y?v|>a |=pPq㼹.ۏ$` J1~yRvz(((G.:+ݭiPFAc}?k~-05LXE@QEaxHuI,tMKS{tC<HٲB$t' Ԇ$ee|2B.u_PMco=RAIsxTf |6h=!V?"aZ#RKxR'ʐJbmC(Y7 pqrđHV='ہ?}x\9Lru"-cK+dE,+XOP@֚BDk[+m'u˴mSkym3^5R2@) BG;I8$zRQ@sa]KKf z}U]GADÂsגzq]6DI;id xdIEPEPEPEPEP{=2K--cFI^SӮl/#-nxfMnF0dҳ371/gY sxK9b' 6((((((((((((((((((((((((((((((((((((((([wg$e#c`ue 1C! VO"CJ'e!Qv\Hs:LvW*~[em{k6ppYQqr8> ?VFE'ev4G BCA;""Pvh4e]2) _1ʸ- @7d#H|2X[>{sDYA |6ۄm|Y:N-Lf*X+ r@  ;\iih--xm崂&25|ye,7۩}=5MNkGOiZ(-U^wI猸2A9,rcJ},ڎxc[dhMYZfR긂5D~KS_ώPbA.I9<zzh:Z~E7Y@ĭ;hiݭ0}n;8!XqqBB-em?GojZ/-ݵpZHjLN]![(u-KY:Z,j#rӴA|ss.|Cq,ZEAdI]]ݾf?{3@M"S@ebKm2[n- 2$37d fv8Ez_wZ/[j +hZ$9ͫf0^hUˇ(k4K l""1]v]y}4ywUޡra ?6_;W<} 1_]%ҮϘ;B 8`wyy+|1%kk-#{%X./ۍ`R!nOM7Oҧ]J+-F^ȱ =k2Hg_z]dž4%a:)LZK4L,~X{fP oU4w񥅐.UMq6<´[?aĥ±ܳ RU\~,|*ijTCW ї7{rUgg}ټۿoM=qU Kڅŭ\hݡl$NTAY5ķ4y$39$$󚎊w[+6ؖ@Ǟ02yojpq۴S:l; ulv<V-[wjjė][ڵrD9|c@2cuѮ!յ9nm"RMr[$O e0Ƕ'1>Qrz&R܏S6+8Msh*΃ ܊:MosJʆ$gefRO$z5} w{ MLy:憍Yȓ?}7 9-q+[$p&4òxwϠ յBLMCR4gvDC9SPAQnڷ߿w8@MWL jhv[j*pD_-|) Wf2mTSC Ë́E$DoD<ފ JGE+Eܺʏr &NUXB7R"Lp^lFkxhp# գ WUᘙvqtWAO}~vy?RCm6Ƕ=[ǨܥY\BۋnL%T1UkqżCdQѾ#x@5ȹH.$,rH$Of K&吜tU+F<5}'<ϟn:nך{TP?QCEu]W4WWӬo&Әi/8PN2G_jZޣeeűj(U,.8]YF>@9o #fjϲ\oɻ;:}Exztj~WH$T]Ja|kzVxMtIoujK;kfYV,nbLC*ʹrV$l:ޅow.bF[v*DL+Xd <;k {;w[i7B* @櫍'Rk]>٢+VrKcVP{#*AҼD$]IO5h$zſ1~S!r3X\1[G1TMp.@u1,Ap2uk:t}oc>&kwH7.# 1QXkOgZ_]$h@rUDw~ٮŗdԯ";^a ?iO)v\llvnc>#]Y~SZ햣إi* H@ 98;8kkt}qc:wx76sVMAjVW7`2K4W!rŘs'˿q^ZC|֚6h&ܔ*p7Y3UpCceR{z;{K5%-dkW ;883i5l+$ftܲɨ?{8]d:#O֫p"))pBHTU,GԀrqk^TKDԟP7heELdy#:\ڤZ}n1#q>0̼zX%4rK}F)a2LCE((ZMDcO[͢4[K{U[\K2wuv 6ӂP>IԴ{[> m$в,ˀr0=htKYk}/OPGk JrHPN2@ϸk[K}%Ԓgi,.d̨sݸZlz5-ߑ=j֡K;Dw a1v7е$үSn! ;c `Ȣm XY㺉wp%W{:>e 8k0ҵ3\+B[4k4ƛd`bDl.%@rv qI_cbT󝄬"fy`^@W1$Vw9YG"eџU1-m'Td2Gqt=4H5&Hd.=Ew#;y [YJT#lH|wv\wa͡kE6}R +ldPGR ֣ԴKF[}SO:u DrF@`2ϱ?j" ƹ ո]‚r)`B7(?_EӴo+y󛉤NVEl+6K1Rh>F?lIld@0IRkoo!h,4{#DQc~N8z%^ͯڡvqݕ Ds` :xh,ifn6BBWH dbOj֣͝أXf= Ia{Oq.Yh1*MR]B+dK:o3A-/E_[Ŕw0[~YilD6ڬu@kR妮ayk5cƮIw[IKJ}wfm;CFSVǜeAjlR Y@ #P~Y XQÞ!H[CzoZ9hRmBq (ĩEIcF@8`{6(tݠ]%2WB3//i)y=ODO .G`44"AY-cmAqq}WNP(`$+UfodaA# hb.|YiwW[=Ķe;U1\gw&x)ѮtXn;F*1[Ye0fn P++Rog{kmv$wbTMv6vvIO[xx5qgc-'1GVIH؛qoe# y8!#pT8ր$ԴKF[}SO:u DrF@`2ϱ,OI/㽂S HYɄ8I$&~Ϫ,#+uIcU+BQxj}l;r?dQ GPQ+6hkژxcQb8F<R5X.KmRkz(b S)XY zU[v\Jm* X{r6Dǂ9|uZ(+ky--.m!6/o(pgkH~#9-/ssػI淖 &/{(1pWt,l hlX/[H&a29 ;A 4^}v&E57GK#DF>lP.z6eoX4{iB@vm#4-c[t'g-]3gs5ĶI 9I#J08 񊹦k7ZGhdqv\K`ї?JDlf}$u 2JLleeK<;e8L~la{2mdsue+S%KHlId 3f5W>MmVK9^A)q#&K*ʪa݅bA ^״ku4MJp%իĥN`p DZ y;M{7}<""7r !+#bmkq]6ݳ9hI:z 𷇬=;P,)|n,C漆9>U YE ¶>bxS|;7ryvwc'qRiZm-&,${k _1pctl1| }q77yWR>7;Xp2I@袊((((((+߾ȯw_~"=O_*|]?u":O_)|`'t\QE((((( ; _[5t7@w?Կj((((((((({}{GMy]Ko)HX2:0ʬ= (B/} D'1 A gvQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEs!+']WA\9? WUP_NNj./6Zkg IY& QB`_xk#x,$F%3yW09@-!Cx5MF)-$H೎ .*Z SW}Kva_YCuvdu|iPrvx+\jgkDZ\+3>f)Ry9I0ǮP.*Y"a,X11r3@)lѧQQ#eVTV/u"5YԟPnbe% yw'zcPK{w=̃vi 'ՙ'$յ)tu }Z4bFLQ/m--uYf;>g ҷ"@mB ^ XiZ{LI紓r')[e!Dyo4wz桩D{gͺVCO 8 GjĚX귗QIGI$ѱi$BK)%BI'IH!׈m"R8P%`sĞ|1G{y.Qa B~c8kn.%irI#gbrI'I5U' ftK'Bn2U6Ű2%z{cO仾24dR3>Qdm^k {;kɭ'wxIv6<6+cf}uS86Gپڍ9EF;NR 5ŝskk𼄤[[j'&jZ5\iz݌KY&+pJq=Gm=Fl/*AGΙ;[W'EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW|^E^^#E{{¾T?kG_?|w#▶q?LtYE(((((-݉gkIV">/ݔ?01=}: ; _[5tQEQEQEQEQEQEQEQEQEQEWguw)~fnqsESҎ]#MCf'U,8M((((((((((((((((((((((((((((((((((N |a9cũb'Y\9? WUP_%xX?XgMR)R$_H~n I?Z;_ ֠ _߇isB,eͱvJghrx6bcK'F(Fr 12Dm MSmf \y >;N#!`ӎtPII7IůL'1n-+֓OԅΔa%Qm 1L]mYj LpG ֎x+X&-ݴ3y.@N~Rʂ9z(ӿ?W?kvf6g_J܊ ^xP~{:ͤSsk|ь)0aWVTS+= >Ik%][&YHjc Sxnuyc u;H2X9a@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{\u5?WqZ+i^X?s}Ck,J?ZR$LR{ UEB ( ( ( ( (9º]r~ մ٭ﴸ G!fS*/e9^H=k(((((((((((qYKu_i3[naP ?+R3n;QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQECEu]rz iqjCh)Wg |ǭuW;_ ֯"ϓ4O_r;]WEg[g׾Am ^tJɵ{ؑh^ rV~[V{^=ɻ <ŷ*Wi'"9+SKFgX"fC8)%(d+:3 KA9FRkbޢNdgBF Q&^xZX^7(9Ԑq)!$QEV爴*-/$K#<2lVq<cOR;E=3Du5tM?u3IȳLI2p(>(+qtK`TD,'ra#IE\ٵkBQy$$,qw8UY' O mwJ]6/,hng}SbeV>TypX'FeY"4N 2x8Hh@u s:7v<ȮbeVk T;XyN>̻0袷H> X[$L$I"~byg&x9$tU)u axG (0 ܅;zuL{K12AhETI%w@*a"Is90$)D6`1 kZzXLtXQE(((((zcdjI ; _[5t7@w?Կj((((((((((((>Dӯ;V}d{/]89աEQXzOumgP7vzm"h PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP?xO²tCEu](kf;{E6`>c<`U?xO41 6KHdJES5Ͷkjo-͆7@m2^YK aqM[=$c>lyq$P݀[q-6_ vE1:4otCu5YD@lU$b<44]YML^mvI#h\"mY;'I d}x+#`}k5ԼEk=\ 0͹,xw $7'KX껾e uzqk7%ݿv#InyhDDnBWFNU^i`>oL0{ngz`V}kGE󅅥GNUHH6UH|Ept9m,fY%h3e1Uٴyz(_Rj:bMٻ$}K!2|ZRXErxʦ) bo4jvQjUG*&|72nyYFG.8s¨GPEPI{N:vi\icӦIR23&數-+qqqUNJ).P1!7]4 U <::kv}--I{ĶlݳިK=Qu֣5e6(|9!6Ǐ(Pe5;//绺C#,y8«ֆbxS|;7ryvwc'q[$.:;w ͳ%ݜL}yIywgAy2jq;WIw(RgpAsFԴ謆cig#2R-y+E8\"mϿ}?QKy^&;2T~WN5;{wF;K!x㷒DgPVU%v@~-Eʓ.E~% nrxYӣ}Ϻ2e`!q=@?oyi[] rH*d+Bjuh|cWT7$u(<+2y x5x.m*Y7M7\qe-n]Kp@Ც zS&oFq p NYHBF7b濼Gw y vo[IٿGڼ3fWnμj5d_hzli[;b&U_5K,1-4R lC[}\FfGWKy~^N6g}Mk%m|7RƷn)ౚ"Λn &wX\iس6+ǨZn#i䔯؂v1z( ( ( ( ( ( ( ( +k߾ȯw_vSWC}Ok~.U־=֊(EPEPEPEPEP?WR٫WA@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@*VKtqoq;*6u=AHQ@xNS3os/BhmidljXOV@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@9? WUW?xO²tW^?H"dFF:F  c_n_>#hwQLr AA ^/ 6oybn.-esCHH/IWm"𕞚if19eF'tK@Q@Q@Q@Q@Q@Q@Q@iyΕ} Բ%S)-C:V]RO᭘[HIqiP0 GkNkpdP\1mcO)=6[[aD1G W9ʘp(RFo`-pT$]Q@Q@Q@Q@Q@Q@Q@Q@{\u5?WqZ+oDG_T+O|Uֿj(Q@Q@Q@Q@Q@lK{=EVɖ/*3~e!Ո1W?WR٫(((((((((((((((((((((((((((((((((((((((((((((O]x=Xes!+']WA@|KG"yɝJwd [Q؂+hG5;\~'$O E}lno->oXb!@`C29R` ZOjZ{DD(J$&/,(ݹ %n=mRWKYhI"|һn+5'ne#IFK{R (݀U*`6fv}ոwƬ8#Ut=묇Zd>x/Q mh|R";Ws1Qrz6\_αHNUTvթ'GM:\[ZKe|Vr&rS 7袊(((((5~_nbKB&6+ׅ',qBuu{ =#P]ϴ.6ǺOD| zu/Zow$(ne7]>} 5\/4Tu> $39 cb2n4F?.WdRC =*{F[SDԬ`g]ZJ[g{jSk:p\H3bI85cR5[u5\86mgrѢ9tcCνeyw ݤ:,y|RI<,p Ss[oollei%K0HU>ʣs #ė-흴pC-_C# s QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQECEu]rzz$Ѵ󩸊e[Wa >QWREq;2pYn>|gZ|O7Q*++2vt $rrGʼzU8o-lỞ;[h$!%rp< QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE?Wq׀׿|^E@k~.2}kpSWʟ g?Dt[QE((((( ; _[5t7@w?Կj((((((((((((((((((((((((((((((((((((((((((((((<'aY? C! VO" PKa[|Y#Si]uk,E>o|7N+BE {Y̗V#$ "Tev}˕ Et>&.:5ڴؒ[ck'7PCn*{V\zN66v|/[Hpό/NO Hew:HDRavֽuqM<8vg % wJˢ.!Yx}u2;?Ko#4<\(,?yuN] X>n}>źsy||ܽ3gZׇ/$- $ke'Ej/{$]R/~Zn(bcH9戼5L$Z&#"<*L̥x#F=eW#u)H}ݬ,bF|`x'MN)nn" NBd ]ѱ<譍Lj%lg K, O#6T Ar[*9Ut-bNQү,`\n{F``Nz Ϣ5j^ED.0v9an$ Ϩ_XK$S%O/}$1袊((((((((((((((+߾ȯw_~"=O_+|_>),$~:O_.Zd Qb3v@}[EUQEQEQEQEQE&MHG-SLS4mG98玲º]QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE_?kWo0y^vnxLՊK#^vZԴ)<Â03c&:J( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9=vMHihx:ܳJ+?,{:C! VO" KzqOUeHQ&yRˡ9ĞY׋ų;O HǾ@ lkڎy?lݻf-g7VG}W.c@$)#p3+#vk&ڍEԒhtLVRr`Ld!ZZ䗶Pcg9 ;R.)-Em݋mѷZ2!e,<qt N6vb:GO8Q$KâAOkJB`xmcMX'Ԯ`nUWϸhkb}}hi2Oӯ;Ajqiڈɾg-1Pɽ^X ⴼo`q:qӐ:zs@FM^2|[BUPyh`ߒO[+8Ry.Ml3Ed C9;NF_)͍ Lc\Rn kUi_w+(?}*K[4k/5oFIcYLLJDP9`$-6-K[dmY"@v󷑖rĖwt#oƠ u#+(_%xܲGkۙH-672\HPh~bF 9Pk_u%%nΛl$wk̑`NTӆRjw.&quci%¼`'3*bND =v_mbG(`6LAqcvy姉t$LmaRf$y>RB/M>(u^R}_SKkV3n.$}lˁZKk'xxu}AuIt#&3 Ps1gywj_uZOe1-/BN9*?>/iv'~sw~Ehk`}3~߳v/݌vgEQEQEQEQEQEQEQEQEQEQEQEQEQE?Wq׀׿|^E@k~.>>)kC1ֿ S]o#j((((((WA\u8%Ӗ;Rh((((((((((((((((((((((((<'aY? N |a9cũb'Y@|k~M׋භX5;pCH3LWU·_5Eŭ^/d`eS!61\ap}Fiy~$$k?)?(XPcx IuVtP>xI qCEs;`p71FT m=>O*yo;q<NqƺigyaF-stٔ^Cr }Ju ;K($Tdŝ$ E\R]Keȷ<qs׶:ydKZm@FLەa/a]j.մxE6,m; ',pۋ\]\^]Mc],Z(n $i€;pXTtPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^#E{x {\tֿ ևwq XC*~z;bO_4|H.4ϋ:ז!0cLm_S*(AEPEPEPEPEaİx鑵3j@Ti[E.8΋s u/-  ; _[5tQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQECEu]s!+']WA@|GeoxaWFphc+ ;[C_nW>2M|^U4/[ZRcYp] 63* 8$y8R^7![13y#viR^oy~jjtTW7Y웉!lp tWZɢEjN֚5Φnbnl DP$oh3a·mf#;3q!#wuX3csX^[^Mi'xd6E`.KkXmBT)ۇP8=o5ya9[-hV1ۼ;3HÐ7X*m/n]6ڋǖlH[0؁y|TO1#~oɏ1H`AFrURxO` ƍ>R]*[/[L$L.&"5U`  ) w<9sekCu[>#u%GpTd8CrHp?πC+FXc{!s6Si6. _j XA`_i1݂ P }0Hpye-y#v`lfgfEP!xM4OW͸% {s_MݬK[:UDF"nms$MligWoo7K6ۮVW/ U#Ҵ٬urVDņ^Z.okhIcv)T6e]C9nVS\j#" Zp QӰZ˷]66Bu0Ǎ]0]0u 6.Oľ"t{{h㕵 {ԔPxoL,AZ]G;8sy1Rr2?vO ռW4x䍃+so+lPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPU9//വA.H,x$ƬQ@^]Y'moMu9IX/FHRN2@ϸҹsH ?VF}_xS| ObW"W|NF{p09h#z_&[IG e]ӝeGʲbAH<`G8 tVtp DF[ͪA9aG096ht$$H<.@9$ܓ]ӝeGʲbAH<`G8 tp DF[ͪA9aG09â`lxgJIlK$~d]ӝeGʲbAH<`G8 tVtp DF[ͪA9aG096ht$$H<.@9$ܓ]ӝeGʲbAH<`G8 tp DF[ͪA9aG09â`lxgJIlK$~d]ӝeGʲbAH<`G8 tVtp DF[ͪA9aG096ht$$H<.@9$ܓ]ӝeGʲbAH<`G8 tp DF[ͪA9aG09é!kg/D #%YJRA"`lxgJIlK$~d]ӝeGʲbAH<`G8 tVtp DF[ͪA9aG096ht$$H<.@9$ܓ]ӝeGʲbAH<`G8 tp DF[ͪA9aG09é'kn.%irI#gbrI'I5ug4:PMg]oq$mvߗ Fw$ /h>U mlN2A*9Aâ OF(Ȫ"2mR ' >Q9gY6s@<3 $߶q%Fmr?tgq V /h>U mlN2A*9A OF(Ȫ"2mR ' >Q9ug4:PMg]oq$mvߗ Fw$ /h>U mlN2A*9Aâ OF(Ȫ"2mR ' >Q9<,ʐ;X,U?tn$tMGEPEPEPEPEPEPEP^#E{x {\tֿ S]k-?Du]¾`l$0'ʁLcQ7d&}UEU((((( ; _[5t7@w?Կj(((((((((((((((((((((((((? xH] wvcLr!r22285TtٵHuItG!M]*eE|d QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEs!+']WA\9? WUP_xJ+uZ߯w>ccgi,QM-۰HW ‚FAt?φ([FTY 70OB kZg+ * FKi.L5"6ޮ:n8jy^ y4?X<"*Hႆ۝JeHŚ.iQZʮ3B C Z=J-짹IbicYpBg#ʠ 04j(kii]$ CU$* ;FFI$)QtآKym#ĉ.HFx,m'gexY) r MMM0I%ޫe%́vM:P!ݴbr$T}ĐD[̌A33*Y! d0&ii:k@>n /61dMGO __QP\4 .H%sEW_X|:ddM|\x~b9mEU2rrVH8fEl}Ju ;K($Tdŝ$ شgG-3#ntg9|{š%Waul]2vcÑ1r .oDv1g$UDiV ^5 qK^sv7Sm]%nhCCcOJ^"[x|ddddь"煭%erc]I2{AQԓ709vƨ@ =v(((((((((((~"G.:k_?|VeO:Җ>O^ûⶰr3jLΊ(QEQEQEQEQE'Ia"PSR[Dm|dx'º]QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEa-4 RUK4F)y5 7In|dx'sH ?VF}_xJ+uZ9( ( ( ( ( (=L>uy^^m%7'vҸqd''$ܢ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9sHO]x=Xe·_5PKa[EQEQEQEQEQEjh/cX业Y[jAc|('@Ijx{횔8O9%k} pɹj_iO?WOs;_,Ai%ZbC#F̯.BAGgRJYwW!Nu'GALlpd}3qˀ ֭FlKmRZUKΧ͈ (OG:,_P滸tۜFZE,<lyUAi#SӮlt[YbLwcIkpÈMv`< \F.m8%],JDžG݃чPMcZN\-cb# 0g:( ( ( ( ( ( ( ( ( ( ( ( ( ( ( +k߾ȯw_vSW$;_?|tWX\e3J((((((WA\sa]KKf ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _M}GNk|ٟ,\9>օxHN}ݡr*èe` =J 6Guu$`t2DX1x8#熴^[似x!2ҼpI(<*/9,xC{ݔeM+,r <#%~ ϟJ ᡆ8Fy@8!cVYkXO-_{ $g?1<[JkM>71D4v8rA OxfJ#uTakn*r`@&>j҈Фd%!:P.[rR rz͵6wDKbpQ"P RjbH vkk]eKmޒz0O xOtmR)&{[xcj bIy (W5k%uIRHVI!hY(ܡ8:((((((((((((((+߾ȯw_~"=O_,|[>)eS'?>+{_?|s⦳9DGS-Ί((((((Lz[FΧ[fYU/-'CAs=es u/- (((((((((((((((((((((((((((((((*9hm!wD,FT3>@Ԋ/v"Y,5gMK['_$)V8 3TfZ.(茉!QU,*GI@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@c|O hwJxwJXn@;ǘ#+|V ۏ5^iry4w{0R&#a nia+z(SJ&ҭ-ŭ:KF]Rd #@bmmwb.uVюzҰ 2Hs͌bWmQâ45 fS|e>N[

H^*Ɠ 3NJmnfK0bI[{V=bSno$.y}nv$dҫEQEQEQEQEQEQEQEQEQEQEQEQEQEQE?Wq׀׿|^E@k>.~*k_XLu=¾[3OZc}_EUQEQEQEQEQE7@w?Կj+L[KEƧ[iY/ sqeQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE^DL#c ȫ_ Y.Pzh;(%$MIePiEr7&zXZ  3Bw)fcbqZ((((((((((<'aY? 5# G-Ssr+7خ8h}Cx(WrO[͢4[K{U[\K2wuv 6ӂPo<1<j3$:+nEreNIԵ$4Gh!g P 7w< u-7U%]@[ʌE ŕ9%V&Gϝuiqs6[QU7ʆ R?3d5{3&nxhͰRAY_;9 VwصۛuT-4ݍ$$Q(MsC9QznldPI MQ/p.% F M<k t[![z({a$eZ2omqm_RO7iy?g(0#, )bctxCA)M2yx#I$lNAw ýlA6[0D#5 `*J(޳iAu[Uq<Z|Lx295A#Mo ;fBNFGN sa]KKf ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( +<]֍nZ~YݚI7VDT*(wYj_uJfGP~WF%HUa#Т(((Y<6Sȸ p]x=xPj$WԴ%ݔ˕yLpyҹsH ?VF}_xZݗ?]cPJ1]:+^A&8+_}k?Soady%.pw$NzԲxS MM$I}˜ŘZǢ$񧊦x^_.%i\2;;gk3 TͿ̈́ɲMܹY\ z+rOxg. RQ##ƞ*[_"wEG_˹I* ݒf v}h>,#Yf|AY6I8;19듞$4T ]e5m+;YGbGzâ MM$I}˜ŘZǢ$񧊦x^_.%i\2;;%Q@Q@Q@Q@Q@{u3:"nUbB:JRPEPEPEPEPE{u3:"nUbB:J@QEQEQEQEQEQQ<3<%U`J6oC ((<'aY? [M-PSwfS*/خ9d@;Z(Z7^*vVZkX-# 2T7d)'#-}_|A  1Rs4bBRIY|[C\47_ٰiWZۛȝ@uBY0wH8xHMFXHrn{pɟ/tmxkkf=N8n) [piI8cn2U.dA ͺ D| PŖ^7Cqmaw("pcÜ8|-:dqjWOmфQ<${2[g Cr_ .yow|d'|7`aYA$w4>Igug&|rak)4]8f't0lѯݑp[̵CI6 F/SAU?i׺t֗[d*c$G 8=7*r tmG٪ê6--:|{,yL~νZMPHl L֯,gE`+ #|K`yc;Z췲]_\KnI<\f=ܬ `yv@/ja^Ϧ̱4++Ti m39Ǔ-,I{YheF2!#)`Lj+l:\4.^RB v3`^Meᴑ %d ryH1Clf9'nN6ۼ?yvc1hGl׭GmJ+Yhj|K l Z麍Z֧^jO$cF A:wey|C&u{/9C4l6FlBW ]bKiݻڭe$󯜠_8b8Fƹ~$KԬhL5J3!YN]k0KgEJf`[e ~BXFj埈!w,ڬ{dI$l8Rqʂ_]?wmݻwr8ɭHu[iyv[Rαw=a(P3\zN66v|/[Hpό/$TyLIfqm˻ }ͷ|Ezeђ#Y0C Yd /i{[%գܿluvSpkw"\夳nblqAKc B8mJ+qov(#Y(RQh94M?J>-zY5BgCXHnB vV\\]]jVz{}..k+e Z9E_z"vXQTx%FXA$~%ס&"$&MݭӉ]x9#^ =(SL|5p1Euqnn䶚23ΐwc%IcZk;gakb/}rlgs"$.rK$NQ1RU;$(Gl׭GmJ+Yhj|K l ZOi7MKR']m.u#ǔ!`,LT۸/]k^lR[opV\rF+)RNb u&t7z{kA*ba[c7-M7Q>̶M kX # {?Z˥jmƝs0b7 ɇp`*Ǵ?KFOk-q\Eh+06g+uî]=-nf.Re9/d.rWtOzO}[o7nd82k>(((((((((~"G.:k_?|cJD_Q+忋8}a1a(AEPEPEPEPE^L KXi2ǁ@h~q>u9HDRc!cc-/}u^+/R.\-3X&+2qF}c6]/OgFM +Sl*&Z_"5Q@&Z_"5OISh2~QWRRzdu?S]eeϮ+3Tmj+w1lt+,ߺ$|=4:i<ӟ; \Σ_L&Z_"5V?%Jw}fvۜmVv9Y_BDeQ+ˑ >tP? >S|m5=muzTޣGOYEsi=3Kj3U4+ BV7Ɍ!>:2\q#&=mh%攴bt "#'YEsi=ω-$~:ᵇOGü]*1g^I'%4ydJv[ v%si=OEjiӛ+FUȌpzc#VO|Оw3ӧ5@&Z_"5OVPz~>5RhW^Pb `uQ]eeϮ+3G&Z_"5Q@hvtfꪚ!+QrxL?E{k9;/DZp%[6] 2EFK7>avGL?E{k8{ZI-2lukw ylTc"7leϮ+3W&.m .}oMP.-R͍a99@K9m4fH6{!C0F;ǎ&Z_"5Q@hѯf}A-kXЯBN?>eϮ+3]eϮ+3TE6c.0jo]EzU%*76GS5Q@&Z_"5N=֢z~ p͗B̑yQϘdQsQ@&Z_"5mKHeߵ-f!>gI.K yYHo6ڠصe,6 sZeϮ+3T|m]ii2@o+! 1߆SmVv9Y_BDeQ+ˑ(-/}u^?2\E6c.0jo]EzU%*76GS5s-/}u^(OLRڻj:~ uUM McO<&Z_"5V\%mAϭj"Yl'99"9oZG-Nuk6i oy:IrXcDleϮ+3]e%u6uBJrHRN2@ϸ {DZri̐=[m 1HC/>`wL?E{k9=OEjiӛ+FUȌpziAEsi='MX˪iQG^IJ?tx MuP? >SiAErzgQghW"oGB}t?2\.s. oMPmP˝`94$|C4:Gw$;ý\|ßOLV&Z_"5TbxZXaU H*Orx!E=i:4~Zt*e'G#?|y\AwTT"] l19!N ,1PxU_KsI&fȯ pd08J-ֽ"(|EmLP6qp'j<\+74f@yY}_xJ+uZ&o.?Yi/6ɶU݀ Y(Хo쩚MyS'}C[ۋ/?ȓjC* +i͝޳d|ݦ[|30:)XBBg2m!6>Wk{)o{Ocv?y\xV } !kKYLKvqKŝ8qJ9*'!>y8o~mՇmq#/Q$jH9X؂z'kn.%irI#gbrI'I4Yv?>cwf/Ϸ+ M0iDpyW 1\]Xuw ʌ|`'qW9ڸY4HMIF-Q-̈́0bU,0V#+h~=,ɹI^uΗ3rG}@B~ ixfoPI5RI#!XDq@=o5ya9[-hV1ۼ;3HÐ7@u z-ΕgKo1l31FRW$ B7!մmTck;I4ok߿˹T.oÓ*X>GEzF+{RMN=[)6AXHIږn ͣk>)khC6We7wUp0 HG>wW'V>q&-Ls"3n76@.xp¾ i㴒0p,APs0q]f$.mAڧ  0Ep4CHx,@HLRۢey3 `F 5蕇oTMF(%Dk$Mb.P?E#[QEQEQEQEAs*?.6潺 OG3fޘWs~| !H%bBS[PY%XOrI ( ( ( (9}vN3GE)o!% 1 ]W> >r0qrՔP08 жl5gPK2s# +3 7r1@M?x}ұbeGz.8d}D.TcGX((({{.|3GҼ׷A#bPx,xدW7[d)H]kw 3cĽKII4O H##F8e ڹ:Ŧ:npFFdȌ0C T IKqwpREm9]ƥʀT\z1a#L0}V8vaػ1\m]]FF11@Q@Q@Q@Q@ݍsJl6wvWs,!1ʓf<.,Ni@H]P3PY*{ ܐ+-]f$.U51^$$иtVmo7O!VP7a9bz@wiyxi๵{MK;8r<0 ?'V=4 7LfP,+V#p0 QEQEQE˭z4 `8bdC~F,rZLv\v,R$4jOwVsL;€*gO4cJ[F[Ү-Sr àEE]ǝ︞K$Y2+2@BNPJ Kx<]Mp̈\N EPEP_xS| ObW"W|NF{p096<+},NIV #c<0'nܯ^W ŏ/̓~˭$z 2ɮOF#eYo1 F$0rd!qa}}i־\o$et[UI `J ̤rt6mZl1;*b6̣h wN)QTDeڤN@9$|sγ`lxgJIlK$~lC9͜wjE#IsJd4f H8۰.|Y1\]5!|q"nArks,ѣV[H698.S="yH',?rHF&Vn"(ZFfHì(6Cϯ}8餕%jm#ڼAe:́(I&.Ǹ6;oːA;IMwNwz4as*y6g'  E^ mI4F"q8;xrn&Ԧ妘$U@`&XPX19@ ]Ӆ|'dUo6I(39҄o8|{#hd3KjEăU#ĕ@#O/@>BsR|5ig}UkOͳګ<+P#,[~ҥvr( /h>U mlN2A*9A OF(Ȫ"2mR ' >Q9̏ᗒ]+io|h/X &ʨ|%Kn V? Yɨ>GVP,US ݍ`ug4:PMg]oq$mvߗ Fw$ /h>U mlN2A*9A˿5 Y^p;]I 288 iqg6y, feڒIߘl*>gwN)QTDeڤN@9$|sγ`lxgJIlK$~l]BN}BX/C uVPWs^ͣk7]F\Io#FIRI#P /h>U mlN2A*9A OF(Ȫ"2mR ' >Q9̓)~ ]f+x3țu%­MNK3;G\;Vf eǪp6ht$$H<.@9&I59_ ф}̫-$HٜdTs%Ӗyo4<0 B3)׺sX0vu#pKCv YTA>ъ2*Tr$`sl  BI7Iu=đy~\2rN!x^㸂"YVg>G*O.T9b;K(<İ9nQr+ RCpH=bMwNwz4as*y6g'  ]Ӆ|'dUo6I(B/˪A.M:.-.vȤ>_$nޠ, }mYc|P1YNFì9҄o8|{#hd3$txY|'F2m#kfrq9Q2 o{-.ݢO5pY16h#H)@/ Àr,xHu[coVs6 8v1&Ñ@c]Ӆ|'dUo6I(39҄o8|{#hd34 Z=J-짹IbicYpBg#ʠ 04[^KKwho-L_͚) PK0cMwNwz4as*y6g'  ]Ӆ|'dUo6I(Ʊ[ M%n ky2*>X9rcs6W6nAY6vڭn9qƸm$rEhTa@γ`lxgJIlK$~d]ӝeGʲbAH<`G8;Iln.coEf I]n wN)QTDeڤN@9$|syY%!w,FX~I'Ԛ(((((((+߾ȯw_~"=O_,|\}a1 _Mk[ YEB ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( +׋gMSWZKQ gH@l`I@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|ks==uͣjf@Y0呕|ձc ]&)J4V*Y ` ;%&9}84ǰʺ#ɀho,. 1Tj3wBh{F (evw;s60wmm/cD*4Uq ڭi wM𮟨 5ܳs nyj;~r#aX%yK{XZJ5j eG庲#*jy^ y4?X<"*Hႆ۝JЇn5͸tsW~h/o5wU9wVV~ McP-|*4|Cd¨kG^-,[n#2m+BwU^\hnn /61dMcq,I4s9U0\^@>Tt'B66/̱d6OT ]|Ept9m,fY%h3e1Uٴyz(r\JtyVr|%e6 j^(4ťxZ( D"?8RAkX$E\յ)fTX{ۉ.$X ر$d_Kꗦ2$!n$S stPQm;k{+H ",B0Yo[s uq-IJM<^I$bNI$I<棢:O[u;{ ,%>L݌X']HKSj<ϑ@z* $EPG`Tזrڭs4Ea&>i]c~N7ʚz6nZGN>"٣Hsv[;BtPH0Okwp.cAF NKۓ]5ij<]q($P`:(ױjj+ioSQ$KݲY #)-ʐ,ʐ;X,U?tn$tMGEn[KKAiho-͆ 7 )a.MOw 27YTw1<2~ KEn[ki:lQ%VADtpͿbD$p \iih--xm崂&25|ye,7ۇEn]k:ƧZ[&̥_lN n;1c<6 K 2춶H\6tVI%۸,$(nس׆4UwC3H\m2y|2}jǢ((((((((+߾ȯw_~"=O_+\}Qk𯕾.|T־eQT ((((((((((((((((((((((((((((fSt!'^kt# RA{1?޻((((((((((((((((((((((((%$2|B (Jd Wy8  e_xɣMk2P9#X<ɼE`+y!a{D\\IȸRyaG<қ'uHuIuISdWmtT^xW@{Z-~Ó[kCm]y[3K_)mFs`y]sQҢҭޣpј#t* *C)žy( n%HgIUAV>%׭Qi ]:Ul99& m /F>7Sa-Lefe&oIxm?zh[WNӴ:sio(EX]L$>@*umյ+Pjr(eӼ`` 4RBxKUfoeR GFFvvibbGH4aP#bL AEzeٲ펣u~L$t-8UA 9d\-?͠>Hw47I#aqDQ?hfOh "IGjYI'UDci){Ś1PW<+C¿?!???Ͷӵ6ѯ.= 2J`e 2Q98r1kipgbpInx7TE񮉩\iom{,{ pG WAk 1'UclE][oȷ?&[ yϘEhk \cw{ϭzCu0MUy@.83WCsM+JOcyAM0;=X6|{b1X{hM B"$ɾ&e :+GpGj'k[m"rG"d`pAAj]-oe"4ew;Hp 0ߚ!N] sh}$PWN R)' f .)!S@,H ۇG]֠}.C=BYU||둖 HU@7{e/YkI`^tH␴EC 0V' x&)!')$r)VFy1]'ӵ]*-LK I584Ğq *xUiP~a]o|5{~ 8f눜{ _-"c@Q@>} ǴX+;y.&Vڠ$o&{X 9;I+r;_,Ai%ZbC#F̯.BAGgRJYw뛝Z-֓F6|d-"cnp3Gkb/X[hvQO|`챢] Yp啕n_arz^6~hJ<$4B*6$] FWԠM4NRvyؖ#)cp8zƉO[Wpj>tQK QCd|#$ƙ>&Ċک[ȒG J6o[a!$!"FᾲL }=ov܋F7Aen:GON+PkpyOb#- rr#aԒO4 K,ȕnf8=P6ht$$H<.@9&I59_ ф}̫-$HٜdTsEn wN)QTDeڤN@9$|sγ`lxgJIlK$~@159_ ф}̫-$HٜdTs wN)QTDeڤN@9$|s :+`6ht$$H<.@9&I59_ ф}̫-$HٜdTsEn wN)QTDeڤN@9$|sγ`lxgJIlK$~@159_ ф}̫-$HٜdTs wN)QTDeڤN@9$|s \g}IS/Ȫb9l 9۹͚$,ʐ;X,U?tn$tMG@Q@Q@Q@Q@Q@Q@Q@Q@{\u5?WqZ+oU5?":O_+\}`a:+M]uKQ i?a/G0XZӮ_*Ӯ_*`X: %>5p?40(5x<[U!񷋿ia7G2 j_muKS_CN 9Roӭ_*5 :?!/G2)`dZ?/Qx\9Yyx]Ͼ/L>5w : % gtW_x?40k5KQtWţƾ.[U/&.[U9Oh5 : %|\i?a/G0}E|_ T|WЗŧ5KQ̂4Wſ4U8xZ?BOFoTi5KQ̃FOѭ_*?ѭ_*tW Oѭ|U?/ѭ|U9O(G|\?f>_*1sU9O诎G_*|[[9%%-$m.RU|I_TF}E|z,qmku.Qr3+owcMd}_*(__Iғ_ϊ5_S[ž)}o{D>F}E|)KS'PQzcPZ|\|UcA_纠o kY|Ui#>>+y[TZ^aQ_]smhKR?3kX=F}E|{ ?fֿ>_*x`Z?/GAϰ诏G_*h`Q_['ֿ>_*F}gPr3 +_YSv. =fϯ,h8>%ֿ>_*ͭ|U#>>ş3_/Bŧf]g9 g;Y|U._*jٳ+?JWK|U/%^+Z?K=fϮ译GWĚQ G_/GA0/S@U)NJ5ڠg4Wg^)_/G%^+|M|UgהWc~+ .Tx8Z=fϯhOYKP+`]g9xŸ$Ⱦ gT:_w?m ͎ =x8yRܻZW?4LI 8(:-Vq3a$ܟJ|DpE W|pAo99/RO)KV̚@^}b^|HjC5;:##@!>c # ;m^ݫK͑Ipwd a'k0` SƱO$i2LVX 7pz}x~AsiZlhRBƤ1²eD!Gbe'Ĝ}\՞L줶?xc7oF +;u8>T="p{q)-XaH`E|u%tV0+pWhg27$SEΠZ1/VVUicJ 3@Z䗶Pcg9 ;R.)tu \ eduWF uSq 'ail@Kf#=ėUaOX՟XI u$8B"ٝ8 }Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{\u5?WqZ+U5{#-?jkYDS-ˤd><2OjØo/ڀ;Fb/ !:p *\Ҁ !(sH*Ԫ8 #D; TbREb%1枨I$qM`g҃ٞm0c(2H3(϶isD?|eT@;vp)d ӞS}*rzVڔĽ?\U=)+rPRR}2=EH)@kjSXQCN}:U71N }i9)O [Y"g Ο3**-0cU\AF9.aU8WEgk.p+L+EN?xqRlqCJ}t t"i,q 11V8x#1"nr_O'Ń~fwݼMI4Qwbmce Mn劏\1Hm_:5ϩ852K8Tn>T&zp k5E'΅U4)90Q hҮdbڤHdz)8` dL*k2,9T o(Yv@himTPD3q8[{7 IMlgMem>ȀI+z3i-mo5nzEhsҡkQq kDC })sq8,Eil@cHd[N1KjۆzjdNF@3y?'hٕ ZB?4ԙ-ml8*X 6AG`'5;Żi8|ˡtlv`?4j8$dVM 6IAgM]Qʆ.۠G\KBFrvv,?{S9ÔΎTL>:Kfw;U1A ۈ{ԺQ2ř#zf+ OcQ1˂rӅl|jx$ 8|QFaHs? i$’]5 nAs+AmtbsڥX?tgi{FW~rx4XGlcagQrmQBion>!SR,O8rml.)fkS쫸7ҋU=2Ll; LcҵG)23Z^9QAlo^f5Rʹ'd@ nY/j=r#&K!\S$2scڝ8hf51y9ǭk݀XTFH{F:o=XԟDVE>,6 @Ǿ8;)y3D+hP4 ~oZ^яg1w+kB$qO0FOو$ah)MBOrh|wُ]m!o{ԀD?\S)mQ_)ҵJ3B1٣\`Oq@>iF )H84s"`lDZ&2|nQ"h[Z9B~m<5H3\?nk +ƴm =GQeg$\8Ce&'8$&360iZVKVe{7+ l9,K?agzl˂9B`3ڧ:֪dCޙ&ᑌ헍س9YbOZ9NU뺥g0p:*l9;9-|5嵉.]Mn11 pA;ҹj9c:i^iyJY$A ~#5~_k:\4bEg AnRs]6S K-n-|W?bL`:7Arb ONy\nQL[pF[;F?֫{?~{ywo~zqP-y{n\M bC۷ S:IrMҵ9`KjIJmU6۹g9o~'qYKpNWrHOAJ5/^jZEG0,J$FVX; L*_=Fk{K|1c E9 $ ;h\gb[hǙߎ7cZ;}?’mK S`T(b08 b5ŝskk𼄤[[j'#\k~1MvF"ȉw3=B/.ӏhVF+PKwؙʠYSm "HA22q=s>GvipIGeXbK=B_=ܩΞi͐7sހ:sInIYeg%Ѱ@k4gdCaڲ3;;JoĚ}4?&KK ]rF*S#+nFPI}{PӬ4u+ݝ1*+j}xS$J.y"Oo4c7 b9*HdGHS|HE-72$6g-'2 o6A(zElC?\^\VK]h,/ܠer9YX^[ϴ/J |e|#g~Sh$MuqRM<8RNrIh)<_)\bsqۚz*U'-8*@8b]ȥrӂ*U840is`2 ǚvB l3zfsjW)X`!O8;)8T zcLur +(BN.~u=T>)pxU┌/2-\܇ CR3-KqA҅qrUnޜ(QTzc__֜Ǘ|ҸWĘ=hdv=}*X~T砢M< c"Av =AV\UslUC5M#9&"nbN;zИ5qbh亁*Ns}BѴ+1*olՇ7RO~a‹by!Ui^ J,T\y(;A?4BA*QU~rYG,fO_Tc)oS&$Bcw=ɴBYY/z?S\rZO#]&{x#B=DTCAa !̗7pޒ?!Sm#ӑO!{F;8z.Ӎ֬Ȓ9ʪ?¢0̎; &2/(@,zUs S /r1Ԏ/vLZT?aSVw'vIɤ=b@eq$6I떦D$bjgTb~U#/cVI4)P <}wM۳6Bщꋍvzsɪ3"=Wb/h;IjR֌zU$(8|!6DSVkN9X Af 6܌UJlui'ޥݸH띃?BpG~) ԫŒ&G'j](TC(8݌v:sHpvǚU2P2TF#1=9LnE=BL Nf䟭!1H:Rlt@FrOaV-t-bQNү/[Cn${Nܠd`z3ְe Oyj2 [|6@d>YX5;94F1_Gq<ЌJU`3ņG.ףGw5%DI]ڸ 6I「pONS{3#2;`p (Ok^ҭ5-Nх˝BUR Tn,l>43Uk !VXb-Id8 IIԚO6hp!o-UH(5Cg12A&!ܣc6;\=hyeD\\ꭴ.)Q2喓jI#iwIƎB8nxswϳݓ_ɷ=6t$𭶝w}є_K.'iw1t-bNQү,`\n{F``Nz 5+imۻWV$կ㐢%ȕ]xrմ+TIm@6}]A`1|b0C_Ejl4yb+F;=]#xT_*WȞcbJWji FHHXD.p ª`N08c\Юthxn]VhUp2>eFAw^1u]3ƑewjokM'{2$)(rBfŚUޑ 7O܋ETeEcd|#?YBGke-$KLHd{hٕS( I\K.>bţ7Z޺l6Dv(H'>ܛ4/n[gHVYG읮Gs,Mqq bi:n]w5jq _[C#].Yrl U.Ok ;0Ij4R9a #EW,)6PHR-Z.{`Zc0HXXd=S_kpڮ%Ie,ʹeʀ̡HؽKh^ev+GSh $ēXof.Nq8ª~ 5-6#r V̈CaHf98WtY>A+ɼqpR2V" o(;~k:x ]䴞 ȉy7ڢ(JEڒ`)vjm~NcMovj*ͨE,WI4CXNN/ \iIa}DHt6<' m YA7K]FȆkI/3FdyC 6vV:CDX<2Ħ6x,r9+7DۂYǫ\I|u_i֒ɨ=+JI%ނqÿ]KRս^uJ XaQr2'B;_,Ai%ZbC#F̯.BAGgRJYww7tדBq2E2`iMGx2{kai3k{+>e,rn}#cdcjzlڵBѡ(<8 pNtj q+Kn+&b%H#.[ʗ@ 9:+okAx&0G}kcy 0G,LaU| +;v|{kk;B evSddn7c?"|CEta  m])8,G $o'MxF/A5z_MKViʩmpr{whunY$h]̿.23B6*s*ouCK:3qxP7| P?En7cDԮ6 GY̮2lXv+ ( ( ( ( ( ( ( ( ( ( ( t|1=KG`#&:qs7 zLP@s#M$Qk7|AԮӤl|T⤘3بPO&սJ/e|N=QIj.NsLNi\i 1IJܟZf4\ۚ4O"` pQx4H NoJ 1Z.4RSC(2dpR {TwZ`\jZLq$= 9=MMLU ܊wsH_@:X lT.O!C&BUS1Q=?ODTXeHN*֔GCԄ(W'FyziU'jQ c ENU l%v0< 0rI4&JШ-L$IG4zD5c#"BF_oVOH p$#͌Լomf@s4wxPqV% p!<<|zy{eqLwOpfxPpsMtӑ49e"`c#\Vf̋;fd_+yPH5}23䲀{qMDE;9iDi97D"ŎԋnUKy* |@m? R3mV6 本?Bh6cr4\ y7rƧFNBAT R)e;\1.H :RX`S\xӶM>mÔ0#S˴QRG֡x.r{iCa9 (* EG)~VV@}4ݻOII ϶E*TGQ r:֕4Xr@\m<{ Fy Ӟ}N~AnUA?ZC.")8Kz T})X}i(9$5KsN``4T j@B R>ϵ!JVђz?IIҋ#y{hh(8<0 >Y!ϾEzIxyP h2sTO fJ/?Aקy<06m}˻e4t7 )Lw"#)?\Uט !XXFءk܂4n،:8'/pz(a<HF=E'IȦa3E!'<`'iv|Ϲ5ʃ3FGeP;(P9\N2nM`Ȩۀ:ԪёLBB#i=@`)Uc"*YdOH5JTi$*=N{cfF9ҵ5w7+;6`davW` w.,l hlX/[H&a29 ;A 4j~4O&} ZjV 4.X.dn'.po[n Q5gı<^tA ;Ibin݆ BUVfVF26+$X̷$ZbRo xz4[KU7л"(p"+ݐw˜*[C$t}V9KYA6ps?7|sL ĺ~SNDM9lDIϗ"M M='moQtH%Ǖ!ibBHKʟHʆ sR񮃩k7,SQkIg (m6o^l=:`i"x#~IPizqJı{ ;@#Wf>YNFJlY< i$s01+70U|)~CWtϷy6|f3zs1pzQyQ+ȱFI |*\ִS&[K/m @|nj$E`CFݽ+.61nY/.ZFw;{21_3PŃܹUYaK#P-4a"prLl,t5H濿eK]2.MI-\}0U:n}rJP+C;y,QG;u;+.㵋ql#v G+).Z Y1$>VcEu,M 4PHcXU8w`Fg'u='w-nWv2qg4IagYYiګFk2#>"$2p.'yd-{U"(!m'O7Ҷ9"Xi\FK2l u=5紸= ed9drǵ,/.쌑R$Xe6Fq%˨HPBe,S`, ( Mg ?ZAg{o,6e^g1lɏF*@$:efM̼1Sin#"fm21ON.xZX^7(9Ԑq)!$]?ԭ/,ݦ%|̶RI792s X kE=n"Bvݷ77!Ycfc^G~0Vv=ceufžUNWp`P\*`௘خ< ,;'8c2#V &TIKy_XG4R"G!pQpWl4_ߙu{ֽ1N1or3aa%a#fu YI:/)eĸ+:;u=s:7v<ȮbeVk T;XyN>̻6 =yI/~T 6ȅT*1ڪUi ɬ Vw"k3<b[%^vwڛ\j\]sPN9Wm.wmiQM} #_{HZ<[6TgUɑ N9y\x{ 7ZJiR[#'n1@LY܊PEPEPEPEPEPEPEPEPEPEPEP^j;,nUTq^_G~Ο,tCjk u(+KD=XlFFJ@WӺ>mj׶`yM#S[cGsQ2^U낉 bA=P޹d7+O)z.i &2Pv`p)Ljy'&"JᨢE9c51+3=i>Ԫ$dҌ /L4 ڜI9ʐXS )邼K{ҸYd>:coQǭV+,@j5r|)oj_/8ٷJm@<Ӹ Ө'@})(0M<84A/*?ZFҚ] v␔۽)-Ϧ)К8~VJdvjb݅Lϯrsy<9=71) 6iDqPc"u>XW&9%:"q)ϭUPS'CB w- O8,11Enjtm!o(Pi2s捇WpߙZAg(;SMF5vcO>r1/RЉWϽBlcS?.;Su,*QzsLy_~)dɔ#yM( \}sKVLXg]ijM ބTylI<ȡ?0\yN974ƹ#rМI 2AaOxpz)_|{1Lv1H%dʅY64rߖhD.;) e3R>qQz9]OZ H~uQЬA)'R֗R|`+C܏T;@u$1Ɲ.#4ӰXЌ'鸞 ?ny>p>8`fhZX>z|ZFWiyy:'~4i<TqXZc4uO׊~iq֤ۦ1GR/ӛGg.N'D&u}Ԋ@}To^|]N/QaRL IrnA?9{,&5OBOBj_JDžeIQIJAҤdg)m\14("2sӰ 02)q@b@niVi#VXPQ4+10t9#@X3BY 9E]*[GQTJK" )#O-J@.e]@@j ;W#ޓiX g' `QVxPN+=m? 7@,ύf).UӜS7J͑yu֐03 s,2y qNGr(9dP>⍆AT\R4Wib~[o£'S`ҷpr+) )v5*{8|KA~p0|%='RFluKu5pAGV6oqn-m._.2 aknCVx_Iֵ.~j"i9"aFBkc,4enu+06P9*K  ELD1;͒Oےͷs1f5y'<)ԮVRLP aPd+z4{0Uy.tZ)cMgTIGwP\G =f|F+bo$X/`pܻrp{ jmUŐH9 W]ȹUR^^ ҬZ4%ԡJe"@'0UaFrCg^E y?1Dj!1$N!# = =7SO WVl>X ,G1 ysx{ +k"ՖG2nPK A` y[dy\Hř؜IyuihQ`PX.nrV`?_q CQVϢ=WTVcE-V` ºn,[ۅK1 !h$|*$em9m)%$Ia6K$ȲV`3np<4vkfn2-E,RGJCqׇmfSK:Y[!X#4Q@,3\K;GNxU(ڻ kޑZZY^q4l$-v%LKi1*,G!Jp^\YCi<~2FJEw0rx[VL8syf+\Xcqf6!OtaTa4nX9Sn.[.\FLk[I#$іkvUd(3TA h+xmNdc:RvnϲL0q8f~O xvO%῞KKbJo*eH |̆H䒘_\JtyVr|%e6 [ki:lQ%VADtpͿbD$p7.<1, L1Jb$YeFwYc2$N|¬VIcOh4 U$nI;{2e |@MIO$332^B/A @m#YLdaJ@8c M6P-Nm͈"X4>/luTD4tz}BIגȐ,y[&MFBM/tg..ba#{u%PfBFIpVkgcNNEro6dj+nG bD4{+;M+FoDL 2%m 6P]>i:ɪjtKeh`6p19%kxƒ8vUn2͗(((((?g}!ApF䎾sgbG'zݥqD'W[}Ws1;OLcW[cGsnf-;p)˼F91*9_)m;P3V[.?Eruu=6יȋK_!'yOIb]B._ED]o)ƚ-Wb BfPɊ9riCr_Z=T#)Eu8o]=dq4c'^s! ׈-aߝdX:z>2;b?=>I/O~/_xonqŬ iy>~vky n@=7- fi!ceIRGޔ{9/?/8*INGc]k`E ȋX,56ѫ I4#?& >=J,r}(jAǭ?x91ifqiwnOVuaaIϹ5)izll4^I`ZWC[7yR7`46+ƤWc9d"[h B=2#;W"zܻ>@U e&2wP$1#e<ⓔz )[VXH0Ur21sHsS[Qn7]JI}[9!Lڪ:VI{Li8ܒ+zė#sVЧ#ۆ#ޔIX;7Sf%~np;բQ=F*n; TfǹBŒMD dg*.U®pNȄTqsQ/qsOԸl?R֊Gبh~GO#ya =;ӜM/1 L#Koֻ^PyB=0,*_By^:EC\}BԸB؜#i_WNl~QO e1\%k 3j'v9",HHeZ~{9y ԨzȿLGpO-'Z:\C[HF#oCw 2)X`Im]X{x?嬟5;N%oΛ%JBYݱ%bcﶚ 9u >rWE[0ܯ߈(|A8L-M[ۊEnzzZ1⠤;b}E817TӶ)r@ I9hC58%[6 %O41#@B scYrbo@iD\vVoUajmԨR#@b ڕuH5 &egqrrrsMF|!#}ېz)5^S{hֺ\$J%FVX; $ W*[oTC=FЪ:`cW5ĺ=붷@n7 s󀤌qYtQ@-.v'{y͹RJNr\b}jOcc_VO~wקEl\xXm4?OmJ靬۟:*f袀 ( ( ( ( ( +z#>)7;#_:ҿOzfX$O1110$ =_ $6ɇJZ[m8ė2=Ye~(>'nKs ·#{T74tn6˴)0ނ 覨p+@RSF_׺ףz.&(fV~i?u='ypx8pzF=Wws]z>˧=>tK`8z_Q?O*MaIX&+#B\P=#+`Te?`bO@q)=8?ZxeVP>SkwC7 m%phsP{Ag\ SpJzCsN/!8+/,)nx9) cdsL4p|'VM ~Nqh=j^f01"bw ޜQ$~9BŞ}CN.͟ida=*6^Et?^cih+t9 8x*<\Zoӗw5גktzJR95*xgSnm?BRÅy)lz<_7ُi 2DG\ 6ؕjxex]FvkMGҵ96(CHimF=viMzh[)ó&﹚%>i!"4;~֧v:j)F?&iڟfs7 O΃pOdcixY?N0}k}wMlN}HZSű??Sʻ~#@G SF:-]Qs5&*`C LkFMſ=UIn~M8xfAݟ fu2 BYO _ɵ_? #x[R$>?"mcq9GT閤?ZaӬԟjST~wgi5@z[Oq>h1I-uM36}uKU'`4_4{Kt^'kC,G~}&|,i8KGOj@? > ճ?7K~߶?6j}s+7R~'Tg+o-Tw j5?KT?iߓi `MMG82[Os>x2 E+]q_hl?Vu{R=هN;Wk;5JN^=E$1z&5 Ja!:H5r洛:65wOjb灞;eQPKpd+U5 M0xjKCwjyf)J&PXݚk Џi;=&]iH9c''r*ALDMQZ×6n8_}sda R6>ӧiŏM7llzrJ:1$ #>t1x^5Ï)_1))1;:v"5ѿTd5e<3wW6߃Ktu݅(o&M5/3Ŧ}w7CxZg3Yo&]̌4u+܅ַdžS:o& ^>u}wG$29c2CF+tf|M0^̖`MRꃙѿ*|l#s j)z"k_?~K^BGMA:2d)ڢqd~M5? MW,̌0q<+֤5Ylyo xIie~w?/wl7V??Oھk_bݑ ?[?/v㴣/-?iIvE?1M ?[zsdM4.N]SOa'Qabey[}`Od <ϰy)c?$bF1(2Ѳ Y"{}?&}s??,uCγOBn@dzG}.}:OMק]`&|vND_4/diY>7 za= xq/~fiY>7+ҏ/YgiG/9şMdgi>7^ Oa sg?G}٣>/ 'H ., Ck,E]9D4LݮY;ON2z'/KK8 $\ug9i>1N--;쐙O^Q_?QGf+].Ykt}JD28c*{36QI?ɴp>M72"\+;ҏh63O]d|j!O?ͳ]-Q=yr"O&nE"OI'R=?&OW;mujȹa 2ц++29sRSQRlz6ph jq.覨F\HӿZtEU֘ޕƚ?*3,S.Vn緥de?:39/\$%5R36Β5靬u烃SuNo/YQM+˻/~?Z:_"'9Ls 5?Q*9.T_77i,|;gi?298]< ##Ac/Յ'ǩRՊSM(]XPn(Qr Lu`Svǽ9p=E9+J˱֚`s?('-[NNymPx CCFpyX<$kߙǣ*vRiRߨ+*=RgS)ϘrC8 #Uo(ʎH@0\$5|-5"y0n@fmx +6yyr.\teRѺ{gֵ ]n yS\RGj߇GS U㰕qk@J9q׊-_q\PT1wkZL?(<)W̑vBdP$/cs}~l6ʖO_9tŵQxĤ`AХՅu5n5(%2zj\橈4zisV-fe?^` %@y>f9q~V_Em7$ 2rG4mbQu$\UWj6ی~u#Z/k5eGNNH6Bg(yX #جK~1rN3pO|9.ss$;l#ïЭqz߀mR;`Gy˖OSqHo?-K49'vSܔuhxDYr`7$pd).ӆ^{*Rqudd<J9tg5)#G,G'#%4;{ٖ (۹ ڻ;\qsN6yzZ]gG)BW^7KVK;hocBXfLҩ&kY]E(iG`ťHvQ y\Ҫڧ-d֌i3r(. FצAkxҋR[i$ Tc[%G$ͷcLA^?mcY>0ѭtpq0V'." y% c?֢(F ڕIKEԥg*$LSj0.nqX~syonEqz*K_ tVڑOr=}Ma?i3Xj|qy\Tc0jk]:j\be$=Y-0$[n0?J'Rj ٳR$t\9#qI'kU}lWl84}+J흫n֏ioygD`L.I^aUGA9JKwG\bNq ede?[ ă0 *:PxglX9}}tW M7;.+m, y;tjr7W^pص^:[;?Tzcw1Srr+ B8f FTu];sIm]PހyPkcT]OH$y+X&l,Lk;,?r:-/"+ q*Hj }xKxn .arA_>ӻNzhnч̚GGI4l3$rYdi6ŽdP_j[P:t⛛I;&?%pVoOM/Uk˱mی玾:G4mrK}:&5z y7Q 5Zr^&iϒifN9 jw:µK4Q\Fxf'F ٢ԩkߧ,a ~5UWp2bY.}OR ŢFx~5S}Z+zc7{#5'kXIRyt4/fkG×[I{&8fp@*gya,.>kʭ$⑲9{.،|>S5MDn>{40 fVlG:KqZ]!f9dpyQ jHIr%˼3LBF [ 7bP5Eg,Qy5QF3*%3yKDQ]jRb減q0~k~"Z]C)n۠@;cj4'8um59;]AmH*uCE!O5_T6+is5E 63P^xXռeJH1\j|\h!xSHʨι鸞ֵ}:[w ow#SxlCĉ$ Xn})6WǒJG(ܟhW#8\=4GBۼ qZw:e]JF@=|UJЭ1 q=r}JxiNIOOk&Um˭C٫$0Ď<ٲ)O\z45b/[X<$݃(Lsu ׷9*`F.hwCUk)ݦSM >p??T=k,eΥ16,¨v&dXGo⼨]yi $;X *z(IJn\yNC /B*WK6 8Z@vgW끟Ƴm1[0]/e8VFwWd&!)/\t`I|Rs7i$֞[I( my]Ts5&ԭ捒Uw)sҪ \ș=4dBMpsӗ@[]݌3i -pS 2€vt=Љ\\uϕ9Ĺ=kܝ]o}5ō O8Pqn(]r-nzE[+=F|i 2 uwka4隭q!"7 uZxI%B,"0\RO0FC4b,4wgu)&Ad*f?SIؤ879Q#r9b TA"M8G^n'p I5F)a|dK_-Ph/'d`qN Kʿ)KsyRN9g*nV?NUs FPHYpx#FDVY yGOV,z6OH?(Z<6bx3PU[FRxPѣsT={yc4ֱ]41y (Yʮ9YJD4-'ym_s/ָi1ֵ:-/e7o]WX 1RW=#+G7n;^hv0;Ϸ-HB\C>C]E2ۺiM0r/xe\;F-n۾gWBkMu4nj.|(;3O=]%պ$EP^Xiwאjoz\$Hа,23F+Ռ$ތ⭺8XtMR&;$szgejKF/pCY9e8@}Ok[٣kn] lzךx*QmgU%%6D~o,ck^~er{֦{Zctk+Lwm8ޙiwbkPCL#y|z{z ջBK+$de'Esalu)URdp\cJłRD{2J49*,MF!I)-)eIÏ1~8n -VbNJ-s؆<Zc4Mm\FYA&r8~Xj"Ֆhʳr0635c/٢tJOk&&P"0䑐z~i6L-# ؄8-UJT_υ&<]]_mKV80:}rW#3Gt:>VByu---E7NܬhR8`\yRaRo氍ERdľ]Oq2X* ]2\yun ۸ki,ם#گkz\ 3{UМ*-{.u2g?3VzpH{0ԣ.[?I^L @2fo_l+dsrrr; ]}]l`S|?^Sӥ?OkMh$>>M L[i& dUڻRįU[j7"xHY ˫1gKa$" (`~5ڕt~K+ni鸎*;xo4akȽ15[}NxHљK,UM7Ml>f$sUb"7 "lceFA+!JЎNPq嬿N(>n2>N2rG=.¢4ԣxKDcf$)}eGHbN1p_iⶕQy++BOBΊ?5y),͵m `u+{QI[iN  EqnwX5>-bƽ̈́dkD3%XkJ5cBj.]z2$"MXbMfr#sUKfaZaa"*Okԩ *jSʧVƝJSWfj H&RۿW[=nBԙDI$M[##\r\19F3kJLuXnm)">s:XJN6ѕSiMTRmQ|.C 8G\⹭G~-/5_*3oJ:'QG5`hzA+iܜt5溊>gOxn; i.4&8FdYk[x%>-VԞ{X06bݘg*SsO5gO^Sۧ;e8Q xMs˖] kB/k׼"V5g}:!;‘Ա'tG`^'#$Qvjy͹\wˍWBSq(RCM't\u9ϯ5=|/ pƐ]Z?O^XCek AQ~I⹵>MRYۤc|W<{_ͧzIמff7|?0 uힲL<FfKH ) ɷ;7ak(b<|֬,2 FOd g+1h'ҫg [Z\ߴsd+ׅb=XlElȊ}B𳛝_Nr˟]{ qm)3~UqE{?륣@k$$fЫRHԦ$doGí5u=bV,nOgڰY偹a2005/iWF(nxЫc?m6HdRɐe'.EQޅR)RY8%ԄOtT$OP67&:R]xm'A3ڡ7{PkF^jv3湿P5{lm2w?+u9kAmRInZ9a +5,5q]I;S,վv 42N@TzW*dR7RksI Kj`;s].i%BWky$()^z׋ `eI15ԪV!0HviQYEh烈l`qz=/""|#SZn5XhK&o)p ڔdltFUDT +c}W:'dw,K1nr,dU8Ojmio]Dx}6[/tLiq$a@r57mWQǖCxw-+o? mθ8<}kg_ȣJQs^#_|Ku⋍KOB)瞹.\k\] 9V8wJN}WQ/-WBH;TGL? ɳgb1c׽c*V*0j~p͟>)6>S?TE||O>,'Lc+/cNW{4sqy<\H <~jZ%ˆDM&?S:q]-AuQm\RKCfYP;/_'5.Cv蒅+dJ\Z-W!@^kp[jޗac`6nTa2j8>c~LcrnWǵF;qj;6Gl>7I 6K&:jľ MmRimWĿ3\uj"=2M9>SŦEc+j[[@%kE)sTz 8,Ai3O޸riT1X\n[oFM890y`!'p9OZJ,rG<ڈIlm:B\%sn{~##'<{$s#<qbԅf3҃Uu$aE'F cU"gdu-gFbeO_҆ mI\qMVMUwv|+g"+9WLi,G UbxǯlhYcfI#!հTƒzGwir}oQ$ ,z؎ZÚzAmI5qasP #ҹ+GQR#1mn<zY5]ZbmZL9^9gΟWtlUt!Œ#Ԛv}* E zΖLɴH%1㤹%WeO6Pҷ-y%{tU\`9^Qg[xHxu4>Hnt ෝ1]j<9eeHnŜh6$Xr^_K-6ec9= ͬk3j+,;NWyY^+l-쭵 ?%IIYL5.j?E ~x-O[yuI +:\4Q^~weI$_#:ێOo:q[hq^y/2`_0ιۏ>qVrl"SǀTܱZ`W$N8yE )lc8QmIl]c,iDcRc: &&_o+irxŒ%GE^ZFC289oij D5FaJ `J(Œˀ(4 sIl~4QGPG)gorьRNQM;n3E<|nP rƀ~QJ W;]qEKq=SúbkJLcSئS1ۦGJ(jnD65*aU9(%($ UZ3{2aI VN)쁡 *ش (QW)*"O3ːK;,"cEJwآOKHs?SE\+,1\ }Fh+aǗҔGo#VCnϵ Sʀ!Ka,J(ƑF |q^axo\]1 -@>~N~h&?M(6Vv(L&#'xo$BN2>*&=#0Q C(ȦT1Q0#ɤ>yJ}袥 @ǒV'PidvC=(9iޯc-"{[hGeb?0Z5k]-m-?fًY~QW _81*F#w݌E86p.EI]ݵd9&GWwNońKMt1*S(EһEE4p$8QEeXCKƱ r敔EH\E)TeH(rAiVE(`mMqEԠ,EV'??ƥ6'vBfYpjq?Z(.LpyFኟŠ(cAPQKƇi7(c۴~msVäkm#)?ULsږZj_eYv%ϿzI;U6$n(ar>.K> stream xKʲeW?8e A%kf6ȸxxg5h$G____4e-WkWg:YTԒ(3uY6L7?KfV8}穊mhVccmg^o>}ݪ  )i-s.wGI_odII哱8Fϥt:Ϲ}[|I%cj E f82Rhm>{|[>2H}2v1.11Z_ʮӹy?ץ?VE 38߿E;P1TM^1Kcis0(>ɛX30Qm;EZt0S\sQ s]ʜ2^iz|]_>e&Tey=;czIfsD[?5j=NֲsPvYpƨB,"3/)kBDQ-n;{?=O'8OS IMXZ_4Og<n6Y7Mc<Qu8=j2otfwajbY[Tի7=tk\5_ԪKCq' NfM˟fXǐcT!h3FyX[)5ƪz|TUMU骇ez }*MV`HI;?+g H3~c53cY'VM\t-oR#?ۿ?ݗ< i\g/muoHU9+p]/ڐEWcݓP:wO=Vyn[Ew}3Q@3ZxN:# xح{w"AfS[*ߌoqsfc`9V ]IwJ(eمfSj{<5\q+ II鐳D/&ˮrq,Ƿ쭧s">]l {>Zә &V u "cL]= /q&|2;K;Cf3a F o"H)UG~H+_oֻUdCԬ,vNV}İDwп %s4äϚ`Y caɾ`KwC } &"eLxY>" 9P~g QA4Dk2.2荜u쯿k|8sFsM+ 2ή:Tl8^>44S-E$};cUڿSW(3dLl:Um ͦ[u@BiOAm gjk7~NQ Asd?9-V/[1>/>}n84]7,SW!2,(.H]f]*Z%vW=VIR߿Onr;nBݪq"AJ1!hևaゞGd`&* )buPEnqE=d*l};-YΨr\-n{l9gc˙$gxJ4h wAsSUDkȍ1z4Cص?D+qsxWs zNu?fLV'NIι^9 {ήz 5o$;C =ǘZ0/]agT3rY0}ų}Q)C9E-X@4"Z(רe\/z_Ep=1qK;c%XND:e)\Zx٫hwUq'*j>xsA/_gt= &hm+-ʐW~ͨT)cAc]AJ!רN8YH,S8u`3QRFNz1nc]7XwTQ4wc;  "ZCnsdyzn[yt<]hGDж)x+5QM5kKfjgO+(1enjϭʪnm`M6Fz.- 8߿ <`G*TQMA@*-t``1=@EsXa,`׉ #Ƙ1aߍU+fB26ϭ8T~sJ")*eTwc+d3B8szvy b8|.=H YGPQ&xp~Aƶjtg;D3^{K^j* P:F7/{YTLsLԺ[-a< 3Us.wṾV}6 P1]/X\eRsYTBtgºW<ASˁ Z ](;A}_Ej3Z+Uy 52Hܰ!hh|[3{LU^]4^ǨRvS愠mE/`*UJgV9'eOz5Hո.]hE)CMo;c8}PÝ܏R8fH\nLTBciJJtq{k@A/*U/^joh8U.̏[tſzi&ߵXXj 1,vA'd1vy5k:`[YkȖ8U[{Cr2Y+XYšgT|3ZܷhK9k޷dYS#5FTnU"E,*k>H5UH(QUF̐lAMZ1R&v$3uYQ ]/*(.N)c@(a, N 9=},RJӫ,ZsZ4H0Dow v|st΃? 5jrƙx:PZq'9Nj!@)CU߂$∀2Q-'33;r_9%m s*v/'+ETq%snGFCC܋3Vl hf&]xTEcFT5PC‡wpk<5[^Vd{uV7sk=XΦV7ZTV7zbP&BTff#т8`_{Zc0^T{ZT{e29aX3n&arƨJ3Dʘmzw-#z%U͛eXwY8_zK&s{t]79g'NO\60 :.j߽Ic^3?kol簐d!j,%h#ݪu}_iC i4mh:Z!T;Z9 YISOs!FFDZt? c2tP9ҍ*!>O&Qoj1?m ,znƨBϭjνrvʌ]gPO\Jvy",:"A8׷s($N1Z<3gL!9š~XQD}ӝCPsd &%iDDĜ0QDM%F1p#KZZkʮIx[Bs-](]5c:FF`P6zhi1Q6CuȣFQIz\syM1Bu#>*rtY+XEDx+oi 8EmaTwb``wٱԳOoR%5wAL2*FT17՗iotzn,Ò{)Z)ݚyx;gM5=h(kקwL5nR0dު=E=>βWZB` d{̒WglW~؍*aU/Ş 8F@V^v㣷ȞSG9b"EGoDaΧjKQ};9؜1k9,i9$giMYŐ^J.pIsVFkh& 8zM{oVHJB{[-b@]zy8-ﲦ|gfR5Rzt~m =/nj0tEsQD9j{+ ^>1Fa1F+! ʼK]˻Kd v+1X)ah"BKÊ<Dpp&n2bL 21^ jDAl(hӼ#TH"0vg;YΒu&mE-LPa1eL V`3$%(YQF+ac[#wV;;S;h<@^? 6.w3PQaط9µyٞsS!j WknWoA]3NvTFK̆2z ^5*eMu>ӬGDQϕ5U?ˆQP&-u~~pCOC߼ Fߟ-0LJ3č]/gm ʊ+I#)E]/cߟL3DLbw1'pvm{|hz'DŽ2<)lΚM% 5N,*Cf{e2 H4/'eٍϽ}'(*JWMa;ӝTfkA~*V֔W)UԴ,+cMxOsgʍK|5~_~*b[be㌪c|{}/=6Ę[>v>^>F>.ewWk \U2cFwf5*> TFZFB(-v:pwl6H g {gu1= zE7T Cjx W&*"r"^KUM{YX)ث]ꋭa9nH4ǫIHk kI}u۝JNʘ?zqGfI3Jffsf9BlnVf3[͎ѯZKk13QUV q5jc"uƹl*0*mc޼՝]VU5!kNY݋pv"ۨ[uUVX'2fPF!e^D?$*Z{p CSͅVh٥)CPgy3Qu7*{C]ޔQ fzLTJR[A+foR٢X2`^2QmJa$7V$d͌h;܎8*{ c8쫌lD3;HPS9MSd< /U `kS.c#G۔4hͰ<{gH >pvbx>_UPĢ2TP^* H4OA(?՗)X-٤$*aUPIe2֙5QNjN 3֭5zEQHT˜!ZTl{e72Q!"2/ޥXA*g:f]1-/Ta!~"k,կ{&?. 7d[f.k{…NdW5IOEJE~MA%k,XS}3wvbW͟T1p7}u삣3vb/Z ֜U*W@"Ts{v8SQ c~hQsMr1.Hݭ/JQ)wJD egRR)^ǒ,'^ BuВIIJ@cL0-?5ibet64[? 8gT=Oq0DKV{e}s<閧" $?hIvY=ͯRT[-*cmX3A,^ü2z!Q׌frKuU ׂpg1ø1քux]S=k.rz@2F17\(("A\ut{WK-U`q<׹D8E0^UYz.wF1nT<~YYscu\אַTbVWqDUDF2u&ճ1,c:tϪulixk8WI8{C>j(DJ<' Hsgmx>u`-uZ ^zD gWzPEeU1 MS13yemU>ޗ/{o[=={YЉtʍ}U+ˍ]/З.1QaZO1cwV4vlEQc + ߫ajRA֡/%>۷tKXSg]D$%'zy𨦾ڵWpR]/$V7 {/8כXS}.7vm0g)Jb|!jјƊ2Xj˝S//?.] R&*8+arIѭ94T[]DDOEPS7*x\ jFA|JY#S[×܁1X;jLTW3Z\s$pƴ/sVS!!a!u`A thYP2=5 25;NQ\cƘ@FQb/*Qs pes5Fs{C ڙ[8Upyu{2IIrRD;8k)x%VfjMt8`g?TQ5'xJugDzd =@+ۍxjv8,a@4MF{wVrQ!+^+CAL B)֘e3۷zbV,z$K8bsiW} Og:MYZ.삗dn/lKlV˂gmddЪ-,zZ@UZ.~f!PvtW4K\ւ FND˜SƌkeyWX@)Q>6t{ށ2Nފ2FQ̜(CEDX2Ĵ hYQ>#Qj{o)V&{+uPV v՚frZOojC70TqdkEXUER&^+cŚWUHhez噅O\|]=D-NR0Q' gaL2 ٦t(Cf/*CxтA (}]kD<-zr;{˸9fNQnҝ=u(`ցNrXWV?rX^2˚NdWuotJ%Ʈjj{@=}z1cYSMPZ=-c*6TU;  [ƺ[n+iSS}9at}1Q1ZnQ &EQTƚ9\UnUm.5M+fFv^Ծ\@ 53_Yc,]/gEJ ^$%'A˾ĩfQg~]CZ%`>RV U"Z}dyw;.a,{y#v aXSE\O#e-RT}aZTVG|Ks! FcP>˭qPj"7y"a0Fj=-zD4fiY`t_Q<|8?b(1m6/5(Eq8 U9x 㠰"yƄӫ\™daE(Ub 0z^/ږɯm>_`4Ĭ`b^RǃXTV]*FT179+BoΝۙWT=>xC3Mw}ٲ!;}cXJkUOZTwA͛Qe̐oɰ%D6YeF}vC;O:>R%5MfLT73iԪC#XUN2("An,&^HqDH*5DFn#X/G ?jPDS5Zq' ++&rʥ׎j'${_eP-"@}ɆW[M`o;&⍶vX}E2VZZTj@1Ì2Q->D™A\*BnIڭ_jax?Tu(YI8oDyNLTy`o=ؚxQjUhk13j0Qy!17>bgQ9n7ƐOCD}]/ճr,ϑ5v[c*AI0X+&1QUXW=r3Tk%ִ)jseнjBR@cO5z_[>h aV:op+Hn%1٠j@hW%dBfR#.rWoJ)p,'fM;Y^3|~;;|:9ZORr OaU!9 Q*5 Qj{N,EQU[b*BU0T1 IphU3D5T\ĉqs!T5,5cZk W"*Bn9fjɸV"'ר6O Tty~5ϭ)sp{XA@ kȈ2z!b-SCq_- A{/o4848 [dPW3Bch16Dcv6x/O14cW;nMhuqO}G}O^T۲pʿ8k*Ezv)KU^ ^HN{@: E.Ng;r{̟{s+לPuV*Ǥ39e+مO1"ei)izv[P ƚׯLT۫"Y8򢌾/)ze{ik]gSHvy%PTbR.t_U@T}bJCǝڇ+cW\F삗;D:hz./͑ J;m0^]}댪-.x ;'pR 7x+О2V]#8( hQF%C_3e v;Ɏ\ s>gyjOYA}WV1r%ghqDM2aBM#*"a)c黲5qPvꑪb߼eg͗C#JC9̫OwRo_:Agԋulج{Qt"c_ղ?R#}E#[4}=_s䬩YE8ݠ~Pm}BH}.kBsz䬩wʩgT43ZrsGsd٭93TMQ kĄEe-CLKzyd$QŨ*EfHYO25زQ8cIY'ꄰz_eU/e*R&e/eWp̬P qgLEBq}& bؘr.XvwtҭRI1cjugThT+tP0<x+ h9DxSa_.v姽Κj$,K.hHQ^1JUߊD]W[i&*N *弔3Q(Ll ]pjwk*#[Ckj>(UPǗ:5dLTFǏ0"| gE%-*E12Q!hRF%K_;%6|ę;sujQYEBWVQ1ƱXUgDcDQ0f1^مZH(^uIUUE/ռoU~X$8Ǐ>/RKTnc7=A<˷]NC|zSE6v"]c_նJ%]cˇGշz mrz#Wh8UK4QU8[|1Xd٘n7vUla`}rC3gM?w(B~_sGX-WLT9jN}~O!4P9߂&E$DRdE=5s-VlP=ch:0Uy=5-kWX,a, ^ ^ 5d},d1ċGUh}Ǜ2VLQRI߲쀤nc:UhO:vO+gMZ0>hQ'W3$C2Zd{f=Jz6OPOw=c_GEOd[X{S v}`:(ʙj-llxq ^mLIV5B-*ڥ+c eBT"3Wtثal/9r![nGZ;ֹãg<{˭cM 3bGݪڢ*OjVs}&Z~ji䬍.72AE#zX)xg)ʪxpJd,g~DS^ pPDM5֘j eQ-8U +(c QlR)+b [^Rabr|>j* QHn.,3F#V=,RRKX i4j!ogYP%#\ɧ+^Iei+sSIyM]#h+b aʅ{`V a>s2ks̬6b#2Tׅ|WĚ )-"DD`,kLTAi Ab"-YGrd,%,, kRY wAC{?kH4]ZhΫssåQ;KjL%e]6_h*ɖ"P7lÁg y|l{M91x'šjzb(*aҴ'(B)";onǯ?wclFr>p#$.hôoB'2`ߊX8zoob/x@8Xdi{5OQEPmTdzݚwU5niu-³YbO[iPPsrCLqn-OpFkL2 b WC%4Sn[q'WAj[m  agR^0ܕ2/f{֥ aX9|^C܌b\OV {Qua,(ܸ1±y1M!jr*jiCg h=%- SʐZ(Be jW鵗~&&AQ,gtrF%3$ jwF2/n[_dSkB@y쨴Xkc~]Ӄ*:hm[ml].ӏq;l k)(cwv.`eB~3}F|E\<䁻mQ[xU.]WiWCxb׺n$V6xAR[uZ46KcUR3ʪXWW!TF{̆2?/ںJM3%ΐڴ5ë^k1g֬-׿ISʘzL=\HP*EfE0E)iݶS)Y{<@F@}5L1V*1vϸLx_ܗ۫wQ,m5W]bfKb󺭍5gRƫUѭF&ܙj:pu:\2>L)j-:m i+֐cUtoIO7ED-==꒲QeeU݇ic׬ *4guĒ2x^ ^Q)I3F{ fLos [ιBz-sF[~hs >+r'#ph,%C}M(h1Yd*cLU)W4)孫\dH4+UWeWtF]0+Z4 z,k\^|U|E#nQ3 ~"OӷrkP$}GRSH4jfUVƘm{߇cʷ}OdWDD% bR蹲 >jbꟿ*7-0$y eGU0X5즈n\Pm/u*Zc56m}~r\]8Ka~9#QT1 .+pW LT@0Z\^-`{pƴ@QH1'u[?}~vJy@5M[guiT4A6v\r!g1Z\j|_-1X iUk!jJBmݚħnst7nU(mʈlf0l6|xkڍ+<^*aوZTTN2Fb1Qe^"zWP3 OjZS,oApW*<JE6cI{T zʉ$%eR0)t<`ܞ?cƐXrW1*a6G +k~x O T9a+seMexJjXT:'+cb$X-tu"Yk0$CZ3nOU L*beZ5ofzQ kiQH12Q!hQFxĖUmZVk1YfcUEՈ-'٫/yͺ?0U5POA\/DϏ.+ &~ʝ]6Jk-*c]X Athd:kf_\; 0ފET"?3U%*#B߂Px_QlF[Dx[WGm-wAKN}ۯ&7CA`^ُ F>dQjCI3(*ab1}`=u Kbo\q9"E?پU(EQELʪ{@ RT/l3&_B^Gvs(eZ'{zyiS b̧,L%jQY͏2Q!?hQUFu,u"Ve: FU 0CXj?Ag2pҢ2+ceч(QF6|1KEaxK<.*a1 t^# QHmюh 39&1 ;ڭQQ cvhQLNݼ_(Hyѿa ƚʊ,`"tk3mwuBT=b'!8DuH7GITK tL WQE٫q AԌ^Aч{F%t+&lͻ_<^/0 ^ڬE0S`7689Q0<Ģ2< ye;L3&c3==3߆9Ѳ^[O]UUG|ƚj1QJcb6T:QD+e^*xz^.cڼl>~YTXhQWz8(㷌i~-F$3G;9f ƇUK&DUr}^'~|}k•>K>` Ho#둨1;XkeBL"ӣW}E+J&W(U3xU3{96D㽶XdMãZ׶1Y1i;DXS=oBcM1` !ۊ; 0~,%jㆍ<ƽҡRcƤyQgZEe7^cH(L,"C·UDN&}HS04l #ub3Q:=fkv4vFMJCʘkzP&*DU-"?;|>,>!<쪖 θvr8c9Ung_{eM>i!fYxW,|ח]1:2u.LT5bL,V+SS-ɡw׃psQ#S)CE߂qF@MebQFߑ9cr:O4~P"Ou*3OFpOFl|2jѼd4KU.qv^pV//PSս<٠;Hp{[pv"nuzyp.Ӣ2 z>ߍxDb럿1ʢfHYyouN.2B_*U5^"z"o1EUGUT]_cuL1Q ܬxuTE bhk`{C2-zOQ8΂OHNq)B-.a0EY{8Nx=q<܃m T0<9%c uiF1 dLT wYĆzˇƮW~籫*exEe2^ H42cvwևK OdZa ;dUC>5,>#dzBUʪUCWV qDoկ?mXzYukxܝpj_JŁsgˇs"GlP˕W'D`Kr"hp,3؋g ׏ߝ5E% Ԣ[uıyH[u2U)>x7pm1VS+b0`LTW$NTNaV+b zcE%i="&y1/J5D 886+:-RjQe7[#.ʡW%ecpG)Iе7עcK5GM[ \8|c3(cҍ5އP:_BrTՃ9_q:ƪv~;cS*_25$LT1>,|Fםսn*auwgF7FPEgTw1ZĪxc/p]6Q k51@ :qPFc Aï?7 {MESߵJzܭUzft+.t"Mc_4ۮJ%Rcˇ|J v]D41aCMŝ-i~Z],m5$:uRS$r+_RCw˂XSiD3T1#ngl8jNu)Nr+R9v~ k~u bZ^0^}դc pfB}GNsD f"uLmR&~jhzdH~f$b)R Ҝ2&+PV hQFyb:FruTPT}ِܑ|^-kC7⪒gD|R˓I/q:>Ew _9@?<3L+}b-v{tX gQ^1:1Qγ}Pz~E㌗X}EԷle!nv.fa zDù.NrwssɦO(p#ʐ^@5?(YQQ"z/zD~^1? v1~4c>b!\$eyB`h a <_8O @M:c?yjkgYEO-SDZR'aB*$eI,}9MV%q:=P Ae1cW96s}?L,'jĚdߟ M!ixA h k֨Aj<,CWe(7dDՄ_ ɢM`O2`+!F0xW𪺍N vm HQ<֌Z+ʚ|ցx)Uo4JP<gTaDap!r=G1;tKUtK-/e|28f1ls)">2)@4 ZcV瀞~7#;?XϭNs|1UmG[&*ccˇG,ڻD54m]9Y+uM[690Κj}T\2Q\gGcb "02:3ck܊OzkgTq/[F҆GKCtu:ڗ͆Y&S,SY]w5٭wꇉ7ZjMaMYSegf)#n`'iTT,jeuNu,yu,A $ %/1Uj/ZUged[Ϲ=:׌o\3u}ί e|P]1F>FtH/x#o~nEDWendstream endobj 6 0 obj 20395 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 /Rotate 90>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 33 0 obj <> endobj 34 0 obj <> endobj 32 0 obj <>/Length 218>>stream xA Oe Jme_\/__edѓPDt([xwDD endstream endobj 31 0 obj <>/Length 425>>stream xAo0 *P5RǓܚVZ1Ό%c} zQdkrq: *KdIRs V^AAޫ}-EKFWسNc\c̡Cj[s߼%4[Ylm4qJ,e~TX٧"}\zL7{>pga/2za=ɯ7y~>^ڽUP#[VQQQk9k-z٧+KWr5>b]Bp41ma;{yʵ?>EZ endstream endobj 30 0 obj <>/Length 300>>stream xK EQd]HPj'$ivpBQZRfڴtq{b!Eú.f>p=#C؋niʬ22,Jnmf2YȿOlKmt+r:>_"puoo+|+qҲ6%^X޻Zg۫Zz K< endstream endobj 29 0 obj <>/Length 244>>stream x0Qw*I 83":=az%ʣN--?t-a^:k^pFMݦ&uM45ʘ_S%?mOu+J /l)] endstream endobj 28 0 obj <>/Length 245>>stream x B2n8ZJm/EY)"O{|tzҼTG͇ϧ9GNcOs͜l)M-9],fxVc\/ȱ`k#Awq_ܹ>߽bH7? gODJ endstream endobj 27 0 obj <>/Length 227>>stream x @F$*.%"nZ[_0źOGoevy=k/}]>w?{t >]ܧ@>d=d=4YY= @@i@@@>d=d=4Y') endstream endobj 26 0 obj <>/Length 279>>stream xr EMtbS*j^Ո~ ӌJ{ˆ+sȵCy|ewmwOsͱ S1^R!q )1)ՐǺ҄#nq{y_BfܜhNߕyB\FֶgWz;>E?Y]-Q endstream endobj 25 0 obj <>/Length 225>>stream x 0 T?XSM= h؏R5M1ϱ,Gǫηn~$k;QkvVm7z/IS|Vm7M;$5Y>[s?^d{nx|;Ko< endstream endobj 24 0 obj <>/Length 229>>stream x 0wY}9}&O#)ϴ Ӿ&-2Fݯ˩-_NmޯC}y0?Z?NDtα)3C~Rf endstream endobj 23 0 obj <>/Length 261>>stream x P芎YV`394PZ/+xRkZjԈZ[8zҝ0}5m̮&'w2O˿,~w89E3{)r8_>G}hy_t3W}]O:MbML6[W7\H&kZ endstream endobj 22 0 obj <>/Length 224>>stream xA0_C{sPdr.fҤy1I?a.n]dikd7.ܱW$T>2ޓҌx=~2%ZF<{\*K]\Sx^IA endstream endobj 21 0 obj <>/Length 215>>stream x @Ыa!YLrq`U悧%9O]4[[X-Ҷs¶ݪk;1f|%,WpQ endstream endobj 20 0 obj <>/Length 233>>stream xA0Щpkb-Xt̢m~v߽=6X}=ou뷴GC 1oS|+:Wr/ B@~<B@~<B@~<B@~<B@~<B_` endstream endobj 19 0 obj <>/Length 296>>stream xM0 -1\x6baeb'㔎:dkQ[L'ͩixkx_ K޾kLqW9GgwoJ.P*ug/e|?]NqFi\ iМru=m>~z޹|e&q[GZ9.75kif(t endstream endobj 18 0 obj <>/Length 294>>stream x[0t^wD,!4i3$뤋tנVj#'%hz5%yp|v̇uL,/S8 endstream endobj 17 0 obj <>/Length 293>>stream x Hr?&sƶ4TgPy\>uuWM\VGӔiois3W/nrϱ>w8b<},T!T|y!^;Gs[\w S׹y5-_]\$6=4n|)X3%1,%ϹRR-٢nϗ/gO7>/Length 258>>stream xݎ Z nVu$ 9龜HQF{:?4EyDXk]uk<_<#Hm~.G=ml;mnby~~j9O:?olN?:󵿃ˋ656Q V endstream endobj 15 0 obj <>/Length 230>>stream xC0_{l+ ci'8~ xzþrS8,RTV;rϹO_ 15ƔVorlZZImڑ[-S}ymy:o endstream endobj 14 0 obj <>/Length 235>>stream xA0@Qf聼P@B/i@ ,^4MoZ~s90߆Z_>{UWf7烟m|] +Ae endstream endobj 13 0 obj <>/Length 232>>stream x 0 ?pΥ. K "d菝@/9^7~/yJ=K~xzδ$ L$; gm#K]w[Dּ)q>_&o. endstream endobj 12 0 obj <>/Length 247>>stream xj@jUb4[pಌ1,$JO}9m,]ѹ͔nJ?~g-LmפȗN6 O~jK'!OWw,CpyOI{1dIvs,վ>B5 endstream endobj 11 0 obj <>/Length 193>>stream x @E3YYF 1R=,lJ2\>/Length 362>>stream xAj@ÒvZn,S90q|~?#|9/J endstream endobj 9 0 obj <>/Length 220>>stream xA r"T]LB&SԤ\&&#o3W_lTt\&2c?zmCguZ3Qǵ5L^2癤YE0f 7^ J endstream endobj 8 0 obj <>/Length 211>>stream xӱ 0At ^t9z;}߯OG~}R@o^ H `K z) 7A/0襀t}R@o^ H `K z) 7A/0襀t}R@o^ H `K z) 7A/0襀ty>endobj xref 0 35 0000000000 65535 f 0000020736 00000 n 0000032541 00000 n 0000020667 00000 n 0000020501 00000 n 0000000015 00000 n 0000020480 00000 n 0000020784 00000 n 0000032131 00000 n 0000031712 00000 n 0000031150 00000 n 0000030757 00000 n 0000030310 00000 n 0000029878 00000 n 0000029443 00000 n 0000029013 00000 n 0000028555 00000 n 0000028062 00000 n 0000027568 00000 n 0000027072 00000 n 0000026639 00000 n 0000026224 00000 n 0000025800 00000 n 0000025339 00000 n 0000024910 00000 n 0000024485 00000 n 0000024006 00000 n 0000023579 00000 n 0000023134 00000 n 0000022690 00000 n 0000022190 00000 n 0000021565 00000 n 0000021147 00000 n 0000020825 00000 n 0000020855 00000 n trailer << /Size 35 /Root 1 0 R /Info 2 0 R /ID [<43125A067176AF0353B2C170E83B137E><43125A067176AF0353B2C170E83B137E>] >> startxref 32729 %%EOF splash/docs/figs/rendersubset.pdf000644 000766 000000 00000270627 13261626263 020062 0ustar00dpricewheel000000 000000 %PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x˒ɑx\f"H3IL Q dP 34ӽ=r##2])D#񥧇5\}_~WCWWCjm|?nv[WMꇾy~zMMjǻU鬟qwx{{z8z}8\~forx8^A?s1|Xχ^ӏ6-=."x4ߧZnXF췫V۾]mȆlAnY27_ǧ;<0qv?-^mB 8j|ޡ.(SOVAr np3Rt1]r9Ha xs,I^u}y_Eqsb9gr|`PR&q_.r/*f6c!OOU50-jr^">}O}ܛw#l5 8G a3Tx7 ;*G{e}9^Y:{ +} .z{TOZV|o?_"vͫ_{(*N~ݯn#ռv/AC8=WQ)$~{0E<")" p!^封X~WżB\-MuFv[ à?{b٭U mٵowcxaS葉; |X~XCo*^Z~wIxYȏj!~ƆͰYuh۴fEdp^D[oV-B 4jZx?EGQFk|aTx$S00 ds#i3ԫa7|ꠁZ n߿**jцC%/xBcy |4ь$EZz|px $?O];>}U#mv7%iyR{-ԇ}Oᄌؠ<2Pe?2 7-ӘQoqb= ]1ocĿb Vf4l|/J 1OBck:E8m(j:Qѩ5v}jx eIВ.9' OęG4>g1,ԑaI(A|ẇ[a춰Hnk&*iP+kpZn^+h:P+D6*(67z J¿Bi!wT,S" 7Xk[ۀچA%&Ug Wdf2@HVDGR|g?5("Q?qsRi!H/7)}wf7??Ck im?l'[:_(z6җ[h#"|cUz|a]TRWzC]Sգ-br+w4xt蔅he~%Nj4(A?r7NA Ù].3RhtCڄle|@{%UiݪCOfL}M8k@,jĺ٭Bj~ Hn[nH~xfbv< %=#/ i3(dS,a˱]x)AU0a\,ş*iS^(oМSvyFwYr A-6~\U7OFjBJFrVH?6'?MvBNʨ4.lb\X,O>rg>(#WlP h u>1Q.6JHņ!󓡅P9 B揎O&f4zլ;=xFtJւf#3tJCC"ѳ[Gccb%|I]YIןtD")Uw iJD.\7xez c $W}:Q?CaGߗa|; ayh&|)M/b3kЗc#A>u^&rzut9Hse07RQh[&Bh~̝jeJ|=sL6 7z^ZiDj˜,(ȬA._,8^Cv nLV2jLnQ==ccH0aPd==R s=Dsa"GAfRfncqf2qN6&9r(b2'{*VuCKZ֖9mN#b5"pb6L"/AoCyvBkE^|@*sۺ/me/k[ҭʼm=8(U݅*e6HnidvQ%so'VTE#FLDNr_woyFtfƀHMȉ@*-k^"?OhKbD~U3/'-WO` **3DVIt}A?5gh91_M[IYjEg=#"~"Y =2c$2t_ow Jw.6[̙1%#7nn!Ғ EͪauSl!Ð-VoQj1%D)/9j#5(U,0$o1Ķ0BD)y!y`KopX"g)yNfS 5f0h*))FqfXXA2R(5@FVpd;uqgB0^[oUEH.!"A hXCq:dLjs9-#()䆶S@K]d-GجzQ2nPl,bKv 9E0+t\hwL!kh!Y"2y\.&,1`YX%z]5THI]m9H@!7bcf@ F!_=ŐˮM*)9aQn,rLhw0rܣLGw&sLzmټng2ݡ\=c'zL.I*h9n?Lo':uLKXXs@N2'kb&~'dD+A]D%> j>\fi9.T`6f60#WS>#|:mee#n8ʁ";|l }SIZ4d>5Z¶Ec}"'Kg芬ٛ-gm:*'ɀ;/kah名gt]3+_H=琽8c6ʖd!+-^B.Vbv,áC>0KUᆜ)^I&uCI`TqJdTW~= "\ve\5 2ki`)5t$+&BJ1X,)ҙS&Ro;;l|t`O8d#gb^FyMPȅ''Lg!\LAc2ÅddЄLɔC4'nqS&Gb= e(Ě{[j0UkgAЮW]?./<]g}Z$ }5yt?<տܱΛpuk ;cŒBY)!`.0v981tNh$E=Ȳκ0=Zbg2DtF+JOedB:' e]#-znr1vC9dGs+J`H.S= A35C6cں8㓆,.s4 3"XZ3֞q0U_.X?m.?rdJw@/e D)_ ˆXXSĸ8Х|ZgBag0$˶a7xL![1]Sp-Wp#P)Lpp Y4Nn$+5JEԔYéȊ^1$GL"fK0‡SȍQST 17#.Eѥ -1p ߟ G _f0Q1m"6 dQ "AMRM3DND8/> "( B7/^L C2xB >ˎ N}LS+g͹# s׽vbHnᚽVε-)FX .VԅCR @xꀍB.Wia'gc٦w2a Ɩ}RŖru.`7Y1z!H+CP|6 2氤o4s@/@1#26HX(5|d meXbSW|9GC:cHq3SPA/4rL)·b8眜]>Ddf@7șH,&_C]a?\Dfm`"- SC&YJA.4Pg -8Ð5,N'Rq _ 0m͆i d#!s6Ð RS̈BɭS3d!YMaIKyv;琭P Cr'NBk--Q#dwXX z 3"j vEϽFJg_^/6U )M)bȁjo3̈39_#lYA/?J(B.Tnf,i1$#oAc>ԍGRa^2NLg \$(9 Ma jo{Q<F4? JnP `Qb\`Ň3uN%Yפ_tyۄ 6ÌȞdF!sy!MqFtz@fu<+1 2ɕ)QڑF2%xDɹ(%b.cf.Doec 558YYq1EkDŸ6bR>A+#*l#xʔ;hXw yL)b_~dO)7Z5x052ÐQY$O0$JŘBn Įa)E<]CpG&SJVn!+~ŐNaH$O0$QM0 k ߄BnL]'SJuՏ OAdc'SxOj #k(yOfG`47|2\I ] {}'3 uƮN q797QnJQKC@NH8̀U9-s5؎)E!u;\IIF4)D⁎-΂7 <'8:k|[ĭe-\]i8'(7- LX 1.\ݮD~W":^8[ة&)|i}9 :IluꑞÁ0r0 clQޛL,r%Dd3<7=LaG{gBȞ3`j*><F|3(VzH(|P~\UYDfWoik`zyu"OQXQ2{H(| qҮXfDԕ)X&td [L0FcqV\"r2upp Ϟa5{糧pi4LhL_{wޙr0 xɎ53LHdW1c\)tL7tk WDEed휅`܏f 彳1Llc[Zkhi\XPC}GK"˗0 #kv{a+ψA#GU@cS feFX;Kj2wz!O^E;3̈,#P:~BɭԙY VcDƓ1Ȍ6``)%iDM8ԏ 4! Rc i޹lL<(-8 dWa@ VkpK6_rqU9aDLeX#Yw5: g VeECS Ȉ+`}15Sg 5ݙsxc;`lE ѭRz:FK.Wq= M8yS R=oaUzhcLo.BËn: 94eUc!YԔ׌N &l8^ 1&yo̳Ш!Nz6Rh)dl" HiR!yk}bF|ZN<+9`I8FzhQ)0#O 417XװddZx!_S#!9ofůYf4ꢀ 9"+[qcLq{u!BK9<)9=EyWd̀R%9 G_Ě=.rFk6'Y/R B㮛Іp_c)Dz7àx&l8CY <3QCl_1zֲ3IfWM4\.'jWh337Ð̣nljs1kp2`3pq,&}C/-3:!L+%I|: Tp-Xqx{ڃ-S]xl?UC,N@mB|]_#w\j_CUWq&= q1ynyS}bۤ۔d{,+NؕX@DݫFB(eNlld&1YL2`[|[We&sq'CK0z*tE^ ?v{Bšm`fP9@\ehTe8t*GG#~ڢq鬢2sXiOdMgZ|VS!ߴVtޓL5E|ݓ*qjl=#;=smuаHڃ q#ɜkjnwrL#c' b&p$F9ZENa3N&!\[X6N2'ʯijdtoKe63]m2']םl95ƯF]GZ.L`t1QNQd>vn+րsf*/u -@'kЮXI"[g'`1Nkh71#}|.ExgY㻕E|g4-ݰI >GָI#n%ML"3lșEcƎ/@&ie3 xW$3[.?0$5\ͼIPV)R"7HbaN|3=vڢ'`l%|9l19n]$(5_*%Z|,t Ik,~XMQ/4Ōk`y-~`cĠܔ0޼*c19bV8dsȰJhSL4c lĨ\9~E03 t 3d!Yffz7 PAX2z',ۯk0#d1k@k_H-A3$I fI`ab@ N`pb@fR^s8`3-DYŐ̷'նZhj`T86ũAbGZƠMn$&8$zk.9yK *ILVj:2<㨱=2قgkX.V5k:G)3ɳTBK,[(-CM"`KQEưF9dknvD0a9KABCLXC'8 ՞qlƦ=GxqִּI9`[Ɍ&=Zb)QΈ8;r",nSBcWF"q'mi :RT~:i-7ʡl:5wɧyP7ؕ e325oQjvŷʼnӄB8).nCUa@3`v Z@!WQ+ }DL4X>Ǵ%Nq<.o,3ڊ{ڊ+lk}}b@UJʺ.5vL)f Z) }ohaP zk+xZo'Zom z aͷr֡Wx&³Iۈ7 l)@5v7CQC3a j=GܩĝM<h0Qo7"/g~#c͇Ǐ/ÛmYW_mw?|x;O,ނX-_.#NO?~WB]#|ޟۇepHCR-u&Y4R~Np#WЦ~2T?nWwX>7x6VoIfB]xz: h? O)g)xfgKWس)[pQt8dxġ|'Z^;=ٗG^qt8++ӿEpJ8.}N>x{~ʫ-ڦ]!C)+w?xfǘH]wop92FiZ,oIG0ٌ ՓJbGFxd^\6Omg+:;q?~}/~R>[m->B[=,>& )F˫$s{gqkhrz8>(ƛo?X98!k@F$Ls ˁvb$>j?j|TI*eIcj ZƼA u| /YzOq"2##YHL) 0|B٣Q !Y Sn$Ĝ$IvH&NvMyQYū^WUY%#[~|E*>?"aj5^;>Py+~Lm#:hDT+72BEMJbA~ȞwoT^[:e1 S[3}D2s-IsMhpCX)+ ?#kB }tM~><Pb"*/!H3BjhpAln+u}5jCU^R_ϻu1eUK(1le;# Y׭0z{ǫ]A;~JSjK-O/)|Dn{0z` z5p'o_y~+?1}\M@0F)B;׾A(\1 endstream endobj 5 0 obj 12099 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 595.276 841.89] /CropBox [175.3405 252.9492 420.959 451.1328] /BleedBox [0 0 595.276 841.89] /TrimBox [0 0 595.276 841.89] /ArtBox [0 0 595.276 841.89] /Annots 16 0 R >> endobj 6 0 obj << /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /ExtGState << /Gs2 27 0 R /Gs3 28 0 R /Gs4 29 0 R /Gs1 30 0 R >> /Font << /F3.0 9 0 R /F5.0 15 0 R /F4.0 10 0 R /F2.0 8 0 R /F1.0 7 0 R >> /XObject << /Im2 13 0 R /Im1 11 0 R >> >> endobj 16 0 obj [ 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R ] endobj 13 0 obj << /Length 14 0 R /Type /XObject /Subtype /Image /Width 201 /Height 207 /ColorSpace 31 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream x}gjM!/]ecc8`-,+Gmm klYbŤla[WFmCҚjƔFiDURQ)USRhB)T^>s9ﻻsfFF3g9Sx{45Ӕ4i2iz4n^?Mooo7Nӛ雧[izdǦiz4}4}1MwMӻ=y|4~os|5?4d?o:o7|aO=1<.0 ף6< SadZ,}-k2AS iX)2&v@WfrcۇG;6^c3:`*CX&d L0Ź7Ǖhem%W8,ykUrMqUbE^@j韙&y|4-G}y{з# faO6JĶ{SS+J^^\a.+U W.\s`ꝳmD x@=Nix4Q3?X2v߀y,1]⏫P5 % ~fd7#n*ztqP$WTn\+c,`Gwǚ@ץhe.ݰKVYs`~+b+bu{T*DELӡ;g+`͋)g0ck =c&KkJU䃬duM ZVtGWo/鲖 G)0үǯ9j[V3?AĖu.]7?`&xZ}?% M2|," b-rҟ@Bd*PMO#gW-.ubAmߦ7_ Edxȡʲ C`Yyq >=.U5HVo/)p*\%V(,}<>z6|҂!`nQtBWWې.`5>N=B+Ӳt WU*JEyQDacgӯgW-!--#6" . :`v-_Ks9u Z+khqQޚ$[ W=zU\x_d*DaןM_>VGnil` +dvzt~tM9?Ү#HN22AvX =_ԃ>8pEB>~6}bFn7-kX*` ``Eɗ)*oN) ZђqhX(U K9gCgS1w@]a;c(.XWRbHh Z Mܛ: +p;W?gőst2VF$,ݰTiZK?#x/dvc7GKt]CLZ-s>wSV$+V:JnW.= wFAㅳskg:c%bIb`=o嫜^Vh&ZLD\a+IYp";@N/Mo:#_r1:<$'H,| 8[[wZ]*Cnh ̪7ܐ,W3\.wqݢT K_8y)GS_uÝD.b~ |%+{tH~yiB҂Yg(ĖFE?#xoi U+c]G5UW-ZW2o&g ZYD/2,C"b\*,}ly|lFnw `ұ20WM̟X_K(R'.Һi8.oJƲ`tZ{j ZPATj/7Ln(LOӻK隺d*b *,޳'Ϧavw0i cDg0ħ!c̱G%'3X/G0K[xq`W&}ٸjFɁv P*[<*k t=+k- !hwsU塤abJ0*+$).=<"S!OݚӇw?FnUx+33xq `叟KĢu)]f]2Ze&72'2ђ.ČtL~0V3|e!BKp3s #7:w. 0& %L#%_=i%&w.]Rq-^4^jUSzau{H $CHVPn',| ? A f_ul7/Aiagd &`+ q5zĚP;A"5U,C[hJzzngȒ"Y $P T1nw|v` X\2"Fރr)]fum2V'E,!_7_j~:ZO3|k'A9P}LdQK+rϿj؍]WC]g ` ~WcK9]1G᰺^"p*[P. tꆤ߇-xՂ֭}TYd;\#mܐ%Y$է3 T3_ۯ=;,L!IF! _8qKrWѥ>#܍7t=zEj7h1ONwj! XYdB _KsRb1tU ]w֎JoG<3,>+{@]BW"Vmp ZwLU9 Z/jø!3{\eq(56G0 K?}"c]*?bW̱]_;cXХ="2t^D٢,[0͕<;}oY qP- !~iRY|<[/WAGK9/PJMd qϹ ~Mw~~|z[n`=|usץt#n*G (3#ˣ-ŷϽA"k'փ _J rZrrI SEu\HVp*PߘMw2C`~Kҗ\'ۧsLJK\.1UtxtdtY>lуH_*C9(ϛa"]lB+,.DKvU[KZ~q/Ξ"h1.7t5jf6;W.*8=77/5*WĖ|9.`́A+zv+q,g."zXMCm[<*7Yr=thJSKv}K hxO KrEr ߚ NWXKEBt47c^tI^G.]\^BW]lQ?dT+x(G'ZjɢV5 0ڰ%da`'*J=t7ne= 6WWc袖ʇ.]Үꦆ.]_7+[A ]-. W׵EKu-&۵`(:Z J@"Y;+~?x>7A.n/} 2G ]^t]M &[Lzv^V [j}pQ*;=2y ]W!>iTM΃VF&$h!7tY]bA\@vU TpoލF>0 c ԏo~;]|]Cp"T &yEsj#RYk@JG"C\[\tJE踡 KY$ d TpmƿO 0Xd jǽ9Vڠj'(=Wz/"H,1 _2?k֣sLPNޡeުezbB$'h) ]"Y` ;' TG>u{g,$9VJٸԮl]iGӎ>u#\e.JpMi^[ăU,<(27K}ZjCn(Zߕ,0*P!{nO|W嫊t%ՓwUͮ! ]9:AQcTelQ&•y P(Jآ:'OB!ޚw=_Ʉ+խ Z冻_Evv*P!wo7_{c,REvپ'weiגJ-jQ}*+ʑ6WQeVKZo+zb^AKlMu- *lؒjÝ*$"ы3`%_=`]jG$/CW"W٢u$Z,-f90$GySX댖tvyҊ7ϷCdx^#hes7, '+?8̖㭛t v ͮ!te(n߲EMTy>MT3PypUypݠeAC -yHyhVGnX \q:zc.%`%_iPT嘾B=kfWEU[[$\|_HKb^߫p]LjAAs~yZCJ-k\GtRO^"YSBNO>5;`=D4%.%]=WJCꑪ r5Qy.\iDDK׵EK9Y"ܚNS+>Ԣ A5ݵN^"Y^<K[tWڥvT#}Q'-zբ$Zd],+U!ؾzi⺆h麈AKk嘖. F[7 x9 'DOFaW% cm[&j-괬 Y ׆pq]K7z|-2-[f&yZij ҈4:Z6dR4{hsM0^heQIS+K~wq%Zm7d~B1+s\jWҫ/zhs:C~蠖p U< ߊamƧ-AVe -M-1XAu^vJOjC0H9 W߹Kw;|c}JO]vеi\D\yЈC.8m`*{NDS0VTKc.ɺNS5:/9 HV QXt `]%ȅ.W麘xF\ ]FDm,FpItBܠ5T D!D%%"B+u^:ZpHEW;}XWEn*2t-mjg[R18y>U.Dy[W-RB'Cjef'hy>$WLʊdq=!~jWRiAĆ.CW+my@9$\zVޥB,%Z2ٶ-Өxg+*lר 6$8RV$ 6;GG>ś-FЅXt=t-Z\ 'wP՟O#bUT|)AµRO;WՉ-exZ!4 -[$闲adxVEX` &>E3wj1U+tq燮ehsU#b)\O •#KO4]z'6/ jAlBB,|E`%êS?{ ug ]V8jtpUs^c¥QlY)_ W=_ZfK'.Ĵ wzVGˤEjoZ؀V-'uؐ0>A+nH]wYD `HZU5TtP#\EjWQke 0|[k?1-B9QVhAKANO!nHl"P?}a%"~8TmsWJޜ]EB`'~f DZ RHV%r@K rivRg΃0>A ҹ+?wJE…Ts5wڗiMvsJp0?xom/kS{˸uR9MCn+ޙ!Ha 1F0XR:"]C#'}+V'm{g>8ԉ=nm'U"_{%]+~@KƖa Yx_sw@ڒ"J-Zls-k8?Jl jC#સOuhyEW2ݮWb TKe_*<󣔇ЂAwCT\_̢c*[]%\~,=<p =>0'1UuS i7huC,ղZ52ITTA'v#YAQ_|:EWBWlA%(W;.kK1'YwO܈[}z$h9 ZsV=@|'h->ҋF Z*RNFJB6WuPMo^3OVB,OA$Vky#T+ihvyHK(Las%YX*7nIW:]G Ca^<1:Q-nUt#ߨրZhV=hij&JZd0?]%_]e+ϗp k WMuG7{ܪss?^&pMҬZa- = Di9Rh󠦓Hѫp;tpU^@rJEz)\3 q:1sKJTE۫$ EQ-ObW-OBLlS-bDKV, ~!rVq W%a~;Z7wzYZvz"l@}?K!5nTkZ-[h)T~@؅+Q'ZVʩO:'UI"1hɫt?Tk+lMoЂ 9Zʉi@U;]=Kh'ub6}*ޖ36sܪ:s9=y"1@LjxVCG+ڕFybuWVG+q[ߪ'Iu,*kILk+hjuVkQB+x:CE"V/ȧK*O\։wzL;>|%4XՓ|3LMkJ5_!j$3 5EF ZAj[ĭ*ŭ:8}ꎖ$9XEb?55 ZZ[TK״5dA9VʉXh_ Uvŭ:!nMЪ8Ѳi_?c~f>ZtM-9y@Zк3p'ƭ$8zZz_ TY5gkg07oz4my%ѪVWp}5W54z"UQ-r- l#ި1␵.bVjTh =n I~h*힣lRGHGkn1G_!V+ryƢk*kuV1ľU^OC]uhim]C ! ZqCQ2@k˳RB`^!zs9|iZyXGZ[E"YGOOE.NP.6iKZyb>Ctby+S\;Z/b RwxX2~im O>XkZd'k̠U/tjW Z[ZV@Cӽ6d-GOBx΅5/\<7/'=o-{hmEJ;dA.-՟&x.OZuZn^P_mv< EGKUk$ yj㖮B N52puHRMemP_v j%o^Qu+3DO.ZjN'}򓫝u wu :OS!X{-_S!C/_WJ吉 Z.W`V^&zL`R'ӕO݇ɼC:Kɺ4.>GL\J-{!^&h /_ם+Q-Z;:S1!"Cm࡝.;A <(v*;W^\Jr߰hp4'YCZgi*h'\תgfj0$kWl{% *hר'V;7Mb5%ug1h9P{=U$&np jdF^1Ni /a_R7s=hi=\A%6t-geIWQA^:ށ"\.ta[ rϊXu~&U/0=e)+W=^?qzM^rjChq-q&^!zݞ{z@{Wx-TP \UJUj8sX}]԰|:9w>3FƲya%YI:52CnҬAk-S)j+ y~DVO,b(*6W=bW WlJVEKϡ^oy\i-rnh%-m }Wpi۪e{"%F**η5HԒ|V<"iXn9WzpXHVVg9W=zajO_|ZZ]-nxWG+}lYMnZ[ҎDͪhK<+tTcK=A?"p?mz掵LY՟3x\Tm톏O7A+Y+G -3pxۦpjp \b2"c R-.)*w1 *bVKuO@t Έڟo!~L(B$hi;B+uސOk+E$/oa' ] c5b/Z\. DݫP2+k,#ڌNYyNǚ/ /Ynޫ֢<=]hyTKjZd?h7^:U;?p$yq<+tA(,QTTnjµF,~4+j:R,zM%Yyr(ZZ6ZB:ZҬNC>]]Q(*W0@d ?Epc1\U}PT=agg,^6Ԇ-9 Z̝@ Z9\M?($n'&.B(w"-IV%VhT > W~~qU{: k[' _x=IM|ujCК}颥jZВ.%r\}]ٞyH1y…N\*`*W~Qva{XWW=1{=ٰ!YnOh;ڐdqCOo1><ډhivϋl|B.H`EԪXUAf[rU:D@VL9j|k3 vvGjsxn(o\%7?TyZѐ5U-J҂'섮hWX Ċʹ6W96S] - #?IѠ1)x)N YW=Voq4Z}eߐwQb4$O,*BQJV3|UzU\X*+3Z!,g)YjdٳIlBme掔E]W2+H kZzA-s<qap䵾*ux"W ]9)D%P1pKڟOfideOGQ¡P4T[>~tgg4\9,nk֣Th2ET+h9hj ?Hiȧ"QN%y/>ɕ8I)";8A-*0V#PRUMn^T!b U ޫkGxW9jZ({5JrOz|{a3 Uղ"A;Z&>vZ[<$3]%\`gl00ܘ^PEҼggH=UEAJ] E,n~(GJVecdM*eXn}%Z-m@@he*NTUUHԁŭGPAvBg `a,8XBJ"\UJѫnuޯJ.ѶS6i9XIYǮ Z_NZj j-jxjJIA]'BI\]|,)DQ*vbԿgV/JQtz,NmIȜ$+;\le"Yz|t ZJiZS|7K`k:@qՉ|#ta9 K> Tj,ŪBq\8nCIUFVwV(Rz’j8yQde9U ,uiiZHFv<2h\5k5MAMŌ=0PKBNXFUP,b)4 UCWs%nCE*,+T$+ vN^-& ,|.k;ZZ8&f|n5M+%[9KZFjېiUe}cWX_#dx #eNh}4EVѲ|Z[$埸%'3ԉN0[,rư|UOl&(+N5lsգAj;ɒ޿{q.KʪCRխERUtcrдezYl$[DWY R1HsBDR>L%VIh@ϋ&X3䫮WFsl&D,j5a*[qI{[áR+Zh)a˭в$U@OFP:YD$쒉1ZFj|*$e>-ҜЅrLT_ `J=x* b^\񞈵 l8 =A5E/|zxnwwMBT'D2AK>_'HL{ttx[…"s.06Ȕ;@ªXyp~Ւ+WCʖ ]B vx@pEnЂyOCZ[ Sh$&niAFO B]W3"ǀPq*BU*bp%WcAR ='YkZ1~i_;MfTK<LH0E4>*nQx_p).T6gg0G>u{s(o/LX WĪmvf?#V L“NCVx(,\x0DOIhYSZZ[Jǭt xLaF.`v]h p.J/I{X jJ! &W3 o+\Vz~eOR^?MZU+hkj>9ݢaQK*)WV'ZFj{")tEBQG]A@U8!b&g}so.Ja2 +zԞ++?HuCˣjAXG#H%-(- *.W-vd$ƆnT*^H*Wʍ*]Vzn. X2biύ4H+ĕ'vjB˪<,X3Jk%?.ˤ~g4 wS+ mk ZoWbz|>"Ap[.+bx\E@\tW蒻T-|LR‵+e*U꣸R.}s%aWlժJYO->%Au-jUZocNhٚ$y]Dݭ %\<HBX0CZɧQVP%Si,Tq@8`%޹Lkwʐ*}^yuWVg$K;JVzpoh!/E?L#IǭgD

5'Fή*Wu _ a"پhiZimgلu-J&N/ <%w$P!Aa ?!-hT'Te*UCj`i^Q݁+^oz3"JyYJ# -pIY$K97bUCZfR-`B#@jJ%!nY*QS< 0̐f88FQ!K%VT *u6WHz,4 5j61?Vo|[剮\'ql⌜ (` RO_% eQUdW++E:WfrUd]+*5Bt $[[<2'*.=KsvWn,nUD JCPu7d=YUjiObCZЫDwӅx?$YlզI-%53\wKa|]. nHȧd,dRח bUMpTvdUܷ"+Vk $_q+-x$\¥_ $]3J9Ln(ȧdFQ~BtbC}UqWz -DCѽ[!?WKџ0/(!Uoܨ)d5TLЃjbJWzEnfTKg5P $y.PqC\-?p sU,(1>h?]|q⃱`>r;a7v3TPXu-@ ɪjK,m*3Zj-=Q剮B. *t}h.8#1S0|I_I ̆ưT8hTg* 3ݪ*XU5`w@\*%܆ʄt4Q1py7qʿy}DdE!r]+E\;w@AFp`[w @pqe_Pr@JRU ]K̛94A+ i>5r;]wJ+F1e F|^W2u t*^Y $*a*:]& Z'&?b8$b1QY,A(#x OFg$^W**JKU* r84'*zL7 5[h[s%8|1ϳ%]/=ۊu` r cyF oCq ZU'ZZZg< 'w]l]1Xg,F Q'82{[k<Ȓ1c1hm|Y[ѹ4HkL5xHBWlB#{z~QE%jaKQQ~Fzx-[:FJ|y|0Li]`x>  ZK,ztNKwP0VaNw@ۉz˾^@g*!JzeД.nY}i endstream endobj 14 0 obj 21298 endobj 11 0 obj << /Length 12 0 R /Type /XObject /Subtype /Image /Width 1 /Height 400 /ColorSpace 31 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream x tm'7P[^ʘI$;/U7FI!Cu;( U7of M+1"Őe|ĪC&ԊkÃP/6047&"mv=)vnC;fg@x.pÏÏ5 ?ޘ0F^dƔ8ӌ 2މ ?ǘ=ヘoƇ#/2 dž_l%_j,+ X?5Zc]\o q7_-࿩w58C?LU[w?DI]؀GtO#ۘ}Mus=ЂZǴ #G;/]'9]+zo=9KOf>gl \ы/\._֫ø6\ԛrgޛi")/N")q: ͊8'W0SIZ$-yTmӪ [ZٺinI[suP-tig~KKfjl3/ `A:TeTVnc_[H/NS%z4WerxL/U+'WqAo][zRS`- endstream endobj 12 0 obj 641 endobj 27 0 obj << /Type /ExtGState /OPM 1 >> endobj 28 0 obj << /Type /ExtGState /BG 32 0 R >> endobj 29 0 obj << /Type /ExtGState /UCR 33 0 R >> endobj 30 0 obj << /Type /ExtGState /SM 0.02000000 >> endobj 34 0 obj << /Length 35 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xMHaї$T& R+SeL b}wg-E"u.VDNC:DuE^";cT03y|URcE4`λޘvztLUF\)s:k-iYj6|vP4*wd>,y vڴ=S԰79 ڸ@`ӋmvUl5`P=Gj)kP*}6~^/~.~a2 nײ0%f|U 9l7?j`l7"tiNf]?uhgM Zʲ4i[&LY_x {xO$̥߬S]%֧&7g̞>r=g8`候 8rʶ<dWT'<eL~.u"A=9뗚]>313X3-$e}u,gmg664$ыEzL*LZ_j_]Xy[?Xs N/ ]|msϚƫk_WfȸA2)oz-di2|m٣j|5ԥej8ɮeE7[Q|IM%ײxf)|6\ k`Ҳ䍐.> endobj 36 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 33 0 obj << /Length 37 0 R /FunctionType 0 /Size [ 256 ] /BitsPerSample 8 /Order 1 /Domain [ 0 1 ] /Range [ -1 1 ] /Encode [ 0 255 ] /Decode [ -1 1 ] /Filter /FlateDecode >> stream xkhD endstream endobj 37 0 obj 12 endobj 32 0 obj << /Length 38 0 R /FunctionType 0 /Size [ 256 ] /BitsPerSample 8 /Order 1 /Domain [ 0 1 ] /Range [ 0 1 ] /Encode [ 0 255 ] /Decode [ 0 1 ] /Filter /FlateDecode >> stream xc` endstream endobj 38 0 obj 12 endobj 26 0 obj << /Subtype /Link /Type /Annot /Rect [ 192.571 471.399 199.545 482.466 ] /C [ 1 0 0 ] >> endobj 25 0 obj << /Subtype /Link /Type /Annot /Rect [ 451.712 483.354 458.686 494.298 ] /C [ 1 0 0 ] >> endobj 24 0 obj << /Subtype /Link /Type /Annot /Rect [ 283.636 495.309 305.554 506.377 ] /C [ 1 0 0 ] >> endobj 23 0 obj << /Subtype /Link /Type /Annot /Rect [ 245.914 540.514 267.832 551.582 ] /C [ 1 0 0 ] >> endobj 22 0 obj << /Subtype /Link /Type /Annot /Rect [ 284.481 552.4691 306.399 563.537 ] /C [ 1 0 0 ] >> endobj 21 0 obj << /Subtype /Link /Type /Annot /Rect [ 129.362 597.895 151.28 608.743 ] /C [ 1 0 0 ] >> endobj 20 0 obj << /Subtype /Link /Type /Annot /Rect [ 368.892 702.656 395.791 713.7241 ] /C [ 1 0 0 ] >> endobj 19 0 obj << /Subtype /Link /Type /Annot /Rect [ 85.33301 714.611 92.30701 725.5551 ] /C [ 1 0 0 ] >> endobj 18 0 obj << /Subtype /Link /Type /Annot /Rect [ 78.135 72.561 100.053 83.41 ] /C [ 1 0 0 ] >> endobj 17 0 obj << /Subtype /Link /Type /Annot /Rect [ 99.415 141.457 126.314 152.525 ] /C [ 1 0 0 ] >> endobj 42 0 obj << /Length 43 0 R /Length1 39 0 R /Length2 40 0 R /Length3 41 0 R /Filter /FlateDecode >> stream xzUT\k-n=pw!h(B A`A.{}p~s}\{یP3G /bĮvt@4j: [7eW#EQy+H #:{vb>~# q!V\ vV  spxy`+W% Oe  /`n,h 8zA6qn@'_Ϥ3//b@/r` t;zO7s]Y_UCb, ' Ͽ k-ˮK(sgrpӁ/< 7lVK :]?<|Φt=&<\<<<Ϟ?f j @ ޼3I:7\,( Y|n?  DQ7LDMx̲P7'(O_ O> cY` \sb,—yqݾj]h@%R(&͌r`7;*Ezr7ELx)*ʬȻi2'_P1&osg !XbmW_IJ֣ᤳGW3I799\pP \ - VmF(L-3)Iރ YUKI1SǽKͻ5֣ ~x)RSe ̆9sL3.Hpآ$KtǍzƶWgϟQi072B% kHbIhk:=#jݕ#ujF:2!c;'*q-9{Qf A_MI]V0/*g]4'>\P*(`iSJBfɱBm&I)eR Nz*f"Kz!=A풘N5KX}vtkl\iׯP~V &<@:q@8_4,ښNwrs2*D<ag친.lmG|-mߏIAlbGrX{(TJlV ;Cѹ}E'*$2hOQ(_GrO"c8=5Lp۫S-A:)\v"9AVm_004W*JH5!XFOl1s߮ hDȤ0gB0*J1eF( OH3z3lΊCw'̡\dahci7d4,qo.xkh=9$,YuLC7C5ŏywLrˮl[S;`{**Fz/ U'fLHSqGTpQ񠌅Qz{v JC@-5r|Ef4e[kkPpi`Rh(;9bOk'3+l2}֢^]:<†ۀ1:[͓_R+6(:G.~9lc1{νֶG$ud4Pn)WΫ8UKHZ%jZ\e`B3$p6>ja((C^bW#ݭLNc-ˀy(02NK};SsOO[Nh>:4AkzսA7.&m,"(wiWMo@',Ϻ,wt pqsU=A43V\riVct/"Z1B] ޫ7̋о00T`죕q2HZΓՇ(:u= laݻI|eEAI0[f}Fe-$ET]زYV:G/ Lv.W1,O,oY\$K~ |)8Nm+I`=-cR W2r0B omC-SA xlUb Ӛ)RA,rEfRϏ#՜+K!jmS<ɯ*h+z#iwn+~8x)ar2%)\D,OFLLͻ= |iy5Vk9!cZz)µ˷L@#pZ)i46HziƝ<' /q_u#l\<͝=H? }7Qwlh@Z(X.BRuηxoD'֥3'Ԃ9bFYD.kkV޳@Esb!ŋhgD:G$2BMcL_# HM1>O}9jVѩzq[P^]*⣡D)Ω)-6[lfr:*`Zm. &Y#eGG}'!Yt-#ھtl nԛ)JJ>dNu&&XQiz iۨ魂bsν%hD(!BPhr%rӫZzd½yM3#nixgF" Yf N8Tjtn)1Æf/$hN7~E1_5q&Uhj-Ph 8ksࢦ#Ƨ r;Dh+'磨9KL8e㲁~B.DI^/{D㊝:Cs\t]E>6)'2ҨO{d F4h/҉j.NT&A&G"0yFjj.?zD rܣ¢31Wr0L I$ JYQ9t(f͒ $%:o4($05lĮχa'RP^=Wg[,seVzn$5g@ڸRzJ8clB5fyؘi|f$V*`FSM40ۆa.)Οn$&'6ݨR>hL)O8)P hL#o?J**N%w:Ά %i<$/cL mg(kh?j&&(Vho lbPyYPV'JR[R@Glopv5wqe冕{Rkuԙ%&5aMf h\" 1I}jnY4ye6ރ^aV. 9q>0Q\' Չ|m8g+.fȎW8)ߤ_2Nrb\ѯ#!eUTd9I?z"=oGAXﻷvH #O>ȣC ;I{exΆ4쓮 GVg3ޒf 69RTEa~`? /=hW&]6rNF5+S!t%sq{«k( ۡ2/}9))ꂃ/0n WG39'8(@({gq.Xڔc%k0BJzJ1 \oϳKv[CAj2WvYF;(s:L("ЬJ M |~ &wc^Q܍!CXu(Qy\Uǂw-OTz&f q/_J:̅.Mv8.,$(4cCzi|LV~wCYӖl@K2gqdD$G/{Y>x6Hvu'I bR:@dib1\‡2nI_y@掣1cZ 9,n"({FիOb ʅ$7,Aw{e oGܓ`+ayܟ)M+r/ƙO^I4Z1Rt\epZ*4:ݹyN9VcԭyR K8x0$w~nfᙼ].ƍx 4IXяM\IbKsXe}@s۽!hH nUOh2b95ݟ7ajj]߫"we[BD7qVY }w7waߎN<_Jja܇%vJl ad- {wLzA5|ᕝ }7j|f 9&k-hS~i4}#%-HCoI0,r1rяsgW^97MkPR!.oPoOP#Sv4)H,O K5'/6ŰU$BPFbܹ߄۾EH.HSo鑠5!awڹQpu%D8&~=:iyMo*Ʌ#Qu:4ɢv+e>I$1#G2H S)*I'-6*gT('򃑋;A.`ǜ̕"0ozf`D)'!ڇ w'Τ|vR{ 묄]Us"TZ B hNip]3+zy7A+@3P 6ҷQ9L|V (d 'f/X~mJv枸S4?⦑;89`AY׵D KaXfRʕ_*韹sZRF)_TiVߠOoYy( )(p]wي>'MNBؐH%+<M 4UHVgw±ffۚ%`q#as<#k$lWֵE33ͰK텭PP_~ZHW9-] x6Q&" ? }ġ%͓%xo|$io&P 1~<yb,a7x/rȂK==]͚ қag';D)Z̜ޑBPi9aV(/Vp\ϐon8&b¬$Mae So d^PN /:omNمX2ͲX^DB?7}"0 A*9K]u)  /ۗ#OdEn'7쬋; ΙGcQ(I^7{H]ܿtζ_2U+ӇYL'IR |}XRn¸1G8HNl`;@V.=-S[l&5K~p`uv)D(A2CK/)}3Do#¦VR8zcKzZD\i ٠ء Y>ѵdsu6fe8SO!d lS#ym7ybǾ!Ɩiȉ2.Tk !eZnVx|gHHCmb騮Sʲ(Pj7% *Y+W0) D'DǼ;x%ڜL!j 0NKMmP Op~]x,Z$E۟-T;6eJKFcw$o8&^5i#k<7tG"G53YMԫ}%MBlP*DѤAӫ"M~KIq#AHT l9S+1; BbD~ȑ:&CNRJ+cy18K)Ebd h&z@IF)C#tB]"mբ7>p>كTmDIYHx_،+ț'JC &d:3r`a;Ĝrs GɆBeWղ:{[CM\&{69S|CZÏɁ",Hq ;߰~jq+?<1+vdaĦz `. O@jj\R}vêcV|&28]0?zob1ai$g Da4Y=L9wEb1ov MD>Bo%:7*pȎ2p-aS~Uh#25LlHMp@2+PuN t.dinRGT cŃup,g5̹U0[.PɜXgE t(jOunK=FmiliԨJ8XRL/uֹ]|1-> 4Qx qDኾH]ȤahfgK\ ecGއ.)G{6m+^#wVWWփ+t!}g{c7zKaaXq|/#k(t)cn444د2 USS r8%2!mw#ܞoSu$B&a؟5q cXJJ o >=MR7A.64_2Ѭ`ǕȥKߣjŐ氁IA2yu{^MYx+~3NȩLH^Y{] cp *aeԅx+G x_>s?*6} endstream endobj 43 0 obj 8872 endobj 39 0 obj 950 endobj 40 0 obj 8163 endobj 41 0 obj 532 endobj 44 0 obj << /Type /FontDescriptor /Ascent 924 /CapHeight 821 /Descent -270 /Flags 96 /FontBBox [-200 -301 1041 955] /FontName /LUDOJY+NimbusRomNo9L-ReguItal /ItalicAngle -15.5 /StemV 78 /MaxWidth 1241 /StemH 23 /XHeight 616 /FontFile 42 0 R >> endobj 45 0 obj [ 500 0 444 500 444 0 0 0 278 0 0 278 0 500 0 500 0 389 389 278 0 0 0 0 444 ] endobj 9 0 obj << /Type /Font /Subtype /Type1 /BaseFont /LUDOJY+NimbusRomNo9L-ReguItal /FontDescriptor 44 0 R /Widths 45 0 R /FirstChar 97 /LastChar 121 /Encoding /MacRomanEncoding >> endobj 49 0 obj << /Length 50 0 R /Length1 46 0 R /Length2 47 0 R /Length3 48 0 R /Filter /FlateDecode >> stream xuUT\5`n;$hpwn5B XAr9g;rZ/kV}U_9kH' sCx8y@@#E5iCvU102 0YKW(P 0e^{[;W`/| ;BNp7WZ@{W$C:#@KGG_uH  !`.lo Lf gw@>Ndk>`g y,tstTtY-u_U?C"Ǣ k9g)o kػZ]nN {lhh8# ;{kWx׌%Q԰knoc+h z4˿޿?M:93f Z"^9>|h 7w#Wm7r  @7zl7r#"@n׿WLZ A WmݴD& \ZG[6 xBb Oyc*͹8p ۏqW`<%:lE"PgV٘:NVi ~߶-! 6^.b}g|9UNu+zHkZ^&x- 1Z겑q btSQov+y#w]r񫵣x񙸐RviGiB4hM6jW/=6wő4BS"_D#6gM,n()( o9ܗԑuPוɞ2ˍ]/6wGk"lf^9KDe/mT4(fiBK5^kA= }P=^& zv?Yo$1x j :Dt>$>K̓ Rﶵ{4Z޲)FA]%j' wtt eioXLy ~B^ ʡV \{śfj)A4ذD6Jpw|̂\,ӡ |B[@D2צJy' ҷtݏ?o u9$!=Db$ L|pƣ2EG+QSk{!kzXUUAjz}lƘUĤA]aEOaK%XV_ﰵp8"#)MZt,9(Ě=:Jڢc@g*95K48n1{=L7אVb`*LJW0o_ǕNY BXr>c5{RBjB-{$ 籍A%U4[f%|Eo e՗$J_7 #IO=:BZ/0Dd,P%L{$:y Y">Q@RDÃIb.Wූ឵ cniRXZ9{x-h8roHWjpTP0;lnGEѝ-\~<I`OSۿ¯Wu`?60ϼ_XTF1&瑏&fS:սsO[w#rM`QcbnN:/0@Bc';'0ף~wK%0i_#bṪ,哷 vld*vIh"5fDMͪoG> 2[gܦP_ЪO{W['`L!$`%,xYչvdR&uX<껑h6"l;-'xvI5%3(~T?YuCW^y2ijM1UnA۞@ܥ'=Bqf%t%}GE7fB$ F 9*U] ʹ妟|M>\QfY$pI7֙B9@z4 P5 Ry2h9ǚi!फ(k-`FYfqP~lhm%2/fJϘ`b1ȯ7x;ph21Df`b/AEZ !Sm3>#0^fT|/FHZ?wT |&D˲W ;ռ:J%~Gv21tDٮ6ܫdV5_e¸rd_{= y GF:OE&z2YNcp2q_y2r$uL-'lB=I˙<%:oȁ E'Qʭ$W->y៞!|5Toh8 ճJ3I5?DSY7uʛ'~}!"_cvƃgs' |z-Bm;o*\&MV9Yslٸ5Zg8rf҄3 PBw݃BJ!oU]/b:Mq_hiMpZ^3(6edN\Xk^>ۙj۫Wy'aVӳɀ$4sK[M -I՞I"+GZNu mzƇ$Co;1ܛ~e>3x{Zk5fhl9}vBa7VG+62zMvO} $#J+ U+=ʏ LY?]/fc2KF+HE )֧l3<{V5.9SF6\ZAoyDzg^9K}]uԫiOHb(АXtv.vgQWr'][Zʻ`Tկ3t/+|@,yQGݳ}5'HO>T!2_j&ur 3"Ԇ0kKB@ /f Ďƫ͝$#:7 mi[4w;\rZX\>.̜Z ê|a `X{c|t@<ʄ A&Ye!"m0 ޲呥p}qWͺCzZT"7ڤe]Y7__껻peJ|;AJ,k>]j+ ̣6聖Þ!WQ PS˦<2B4k}Y)?}X^Uhvh?/_PcMf{PrL6B{ .cso+ɟ Wxcl90EWH1cV$3Q?Ңm5}"WZU H)%>hQuJsU#i&êg n]5ݡB{Ifɶ+TƯ>tb>aҵn(@Pu2-b`[wu7TERiٓy--IdgOgN[fj'bYjJM᮵Sq@(=Y~7Z˅!=U4oW!jƫAŷXHeبnsG@'qI|b3%)(|$C^$^KwWy!E},8h$Yn-zl[1UsZMLk;Bɥ4 P4ݚNԓ"KlCv]` C}EW,IaXohV˱2h2><̉*JZϥ=j1sv0k4J M` IZy8TM$VҖU LEb:B]rHb3ÌV"OZCΛ_x ם&XToy3ő]x~ HJr#r߸Rn 8E)YzR5E;ȏNe?WfDމПʨ-jf0rG\*^=@%l0H ]{g5-8i(T P_[vǤ+@Klro]BiW5¾aɱ3s㲸V:K 6DD-cν۝,?UEk:qڶpb[ԜBM۪T=q?Ф \`%1ix{KKm%WmÕ 3nEMѨ[tys҅fz5ꧮTzyf[5t& !e4sJQ{%t:ĘC[Zٕ`s|?B~cOshE^Bx':' & "%+Bh]^mq{W{Ei]>{/XnԈFų I<Ưߚ V]$RzNu nbw%ab8~pi뺻W)qΰ0K.*}k/goZyлCL,\/%,gLB = Q! Q[K*:碲)zd~x I%VMK"/C//edrL6͏IaZ^V ߘڇ懋(TLSW$QQoX\aOL H,  [ 9(*pUM'N2T:h`I.5Eacu|d2%gBrVmH `~ 9$$9;Yn{}:!;zLIwafS#9I(@7w<6FN!P{bXp &LIg*%\Gϐ}R6azL!jn8zg6팊#h7MGE|&m0`nch]+E~yh[(3,uqeV|Yc+iU;wXqFpryxc M9'L!wƎE$A,XPYi`~S(L MaۊOuag{;6ʠt2Ŭflpr`0:,iNԆvc8%'QӐdD$n)A-]Hԝ+K_ jpt؁>f?;ޒ hGȌE[5*tE 5Nɔ W'G(8gRJP{eNUF@C6~AV.<=P9j`՚L7Y؇L*Xx n3Mb,ăvoH\V&azC*Z mE9gE'(0X bg7cuՊZDᦫ?V3B]Yo:NM{[Ŗb>A*jaYF< ̮Btq9#|-8L{r?T =č)r! Q׎L(Vq~9ɗ-^vNu%_ eT%ueξfQ3 ߟQ|ҋgER7lV%i>=*P] g ];9ᡮ1QλWe)hJ~ I*@olV177>!hU<;Q\q f= c'YT4v}ũJMm_,`9Sec 4Du]Hw<HD ԰BI3|m.1ғ$El6I}4[clMtT30ճħv?_t?uN(6ɑ/ơbQO52_=s#f8C \t_+$;?\i4yC^( "1G>w fU,8?/X[M+n^ZtlwIKaȌRquۖe-X*[t^ѨN+)*b" [@-9|*\+۟Q\AB-g}'k\A=tTS*/hHfX X;B,N endstream endobj 50 0 obj 8049 endobj 46 0 obj 821 endobj 47 0 obj 7409 endobj 48 0 obj 532 endobj 51 0 obj << /Type /FontDescriptor /Ascent 800 /CapHeight 711 /Descent -235 /Flags 33 /FontBBox [-35 -266 762 831] /FontName /ZTINBY+CMTT10 /ItalicAngle 0 /StemV 69 /MaxWidth 797 /StemH 61 /XHeight 533 /FontFile 49 0 R >> endobj 52 0 obj [ 525 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 525 0 0 0 0 0 0 525 525 0 0 525 525 0 0 525 0 0 525 525 ] endobj 15 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ZTINBY+CMTT10 /FontDescriptor 51 0 R /Widths 52 0 R /FirstChar 46 /LastChar 116 /Encoding /MacRomanEncoding >> endobj 56 0 obj << /Length 57 0 R /Length1 53 0 R /Length2 54 0 R /Length3 55 0 R /Filter /FlateDecode >> stream xUTͶ-A޸N㮍=hpw]Zg>c_sqoyf3gQWoSJ:ۃ<1uy%iu:9 #'%;9;nY}Bdlag+f p3(Q;{wG 3sg@  ?wq9l& g'1щ`hm wrt003L,F 3 [9Jٚ\AN6Ѽ/ahbgk0ǹX[ڀ𞐡-=' _Io!6Swʶv}I 2Qp66Z;#jkr)9Yf 𿍩[[قW%^2J(Zk:v{]d -m # |Z 0tt4tNOf{`6F9L zo`0)&  ? `2L&7 'o(;{0K]/{XwnbUlһ?b]һ_zwqK../wgsGY}L\+Znu2^N W,XAvޏ""v`'3@7j ek䟎zoDcGGoG_lj~@`1y;c xb2Ydfq!,&VsKXRPyW!v4&q_T1ٝ7#%9{&.)eQ9B:") yUeGs6wO'(0s7Wҩ*6F)%O1{%Ⱥ~-b8:L[;d߰0\wcǻ/s@Q3ptd">#uUd؃KHAb;B &x(O m 3LyYn 龥zDZUHކ}J"̅*ʑz ;#I3eSʒtr4@ElJQsg{DK֟1'X+9\WwE\> :aϝ*bc/=ԐYh"E(+cPl r x&,ZŐa?HW5?JB2׺PS]F# rk񖗼?*Րg_Gŋ+oJ;V QS S 7X܊Tf"0 8_6WNFktlL"TEAzPBW/5X!. +יxmORY+K0 7?kP9'sD%\C{;ڴoPw_ ږQ{TO A$d%KL6 S! ⑰U9'X¿o=V̅  Yb!۔t|43hKkv:s .sYo5幯 0*M4;Oo)VOuH11: H :7} ^RЎ!t 940}Eٱz4̷"{h)Q4x̡3eMVA#QyA[Žo /狎 UD=6>LbGpט>i & u9 WWUX5գfBdё+cjHH\뢚vf҂!"Ȟ E,}GdnPJz^L[4E p}AqRyۅY{/SYoN~SY}HQ]43ׅ :MvEW8?imGYdç-R6J#) Ge̢D~oQ{q՝>,w4;ȿ_ɪ9*X\Щ&Dl֌绚ܑ|8s$wPF.O͆2rI.|XpxKudlՉL{j 66N $Rh7%=^Mc> UK3yPKū/G]1eu8Ɇ=f,yĤ-FR0(Z ca0ha9q/&kz? cJ)s!EEjTh7u+a:8 0e(+)O^BuLCsR bVyձñV&l>rALDS@xuBh91Ӟ>fYc$?REfIя-$I ߠ#i%#[~&ʯ:g B^% 4OyФ4ؙf)Ӟad4g7!Ŧ^=܁Y&ɒlZ:i$YZEuX_\/P "U]NHF/kr_d)J1GBIRQ]9S@a`ͫ9;9/=?'4ڲ+xEvq9|cGµ>#ư:ۑxs3{EڰQV嗠54~ʒ9?^=E)%X*ÞF?߁^Iꘈ*̪.pPC쥄徠orKX]Y" kX˰ F/ ey'fef$n XОVE(`8¦iaGJ%w/J/S~V_##LVn+/8jk;]4)?ߔNдwV7 -Sԉ[Brj8NQ]}4\ 8{GIURV-n>LT&/g̎AHg0XbU,h6H6Vl*iCH+[mS!Ҧy}=ZrXj l֨sb!x/NM=[[eo%K'^,vqcߋbWxbu$  3tFn= 薚'9pֲ @,$(]~lHHk:P}M[M>&cb]-*VW(޴;mvqHTD+gHK)mnꌤGv@V1Erm$ES .8!q˂c< $~IhL*Z}IU \Tv "Ncv0k[L3VŸy&:,jZBPhSv*NlA#sWdtq+CdR!~8of[цV]ɐ;Gtt]CHc6}Lp5AϾQYYda5g_4VY]/ WqM6 w#oT~ǍDrfxt,pl3-ZM;K%a^B㟛~UdLVmaV%%٨XyA7HLV,=C!Te~FM?38b },ʩ?V.uej,-oh Û3&]bvܗ&_O1A̲"T\5">Ak5SVADT] пh;emIFmÉD46,Z.ϋQ].[yLвK5YO9zh3 ݇1*TIB1%ʩ쳡eOҢszk "&fYWGѝk)5#aC¡Q?̜ (|aM}ťȩ t}4r'?V tB@ڝ qnsJAh<׉<3p#K!{ VWrzHu&ϔW&$I1:r6"&;'+|t֒;Kt) 5X%z㬚W3xM=]Um'=Wl P88RZvy'֙3 Ʀ]Jg0U&J.EØZ|A9vQ~x rx+KrY Rxw]w J< ֟`ڌf䃩j=";dD·Oa~Ë=`nԷX6iB#Wi2yo.m*'QJ7Sng]Z hK̇rz٣yJT5SJEg{L6dy޹rH-^:-'j>79v#Z6x' oRERka*Ml]q-hn+szsV46IY:Pg 'wOL_r=7] UYNlա8S?#O:ROSqގɞ~sS:J`r*e,˶21xJz>pO3@R>@JӇ[p0Т/:e-?!9h+cP_K߃fӤ&X)]|Ѹ5>g44)%A ]vDɎ18|U4w°M˒HbH; εK3 F:~a]Ѓ %*?-4.Ҍ:M\MM`J42Z5@,m_u{gCw$IJCy/r5cl)ΧHlOe]P-9wO~n',1_SѾGlY;Z)p ~רu0 y:svB2~͟h 嫈d ZdB>vcGz'v cOR .qQnHLUJfYR$(Ոk3c:ucy'A1Kgv&++ol2%Na!-jAUbYVc\QՊyݷz0^ qFHmwD6q\s6 r ة?X/7g*-z7qDQ{n=j^: *ji HHk]3\m[ h)3mBزЮ4VRcYLaJP0?wM$%9|3^ö{̍!܎7r|jj6ژb`sw+6%[>F8EKNv _*݄9`H[Y~wgDўqhۖ5ca^6d(~NBV_&a5ŝMr¿H- n7 Fy\Jir $m~1ί)~CҤOh{ .S2ʮ#cdjM  ]RlcC<_5dk uI#?(So l- ceɌ:Pa7dE ,Rb"=h"H&Ix*4H{"4eKLD>-?#g)2K _8@DYpƩ$%V@Σ'oI{d0EN.pOn'e6}j#ߎdŧ 9`m"'鎪'_#ߨ>&w=Q_/KkZPEnY{I!_T>5% & nfʜ\ߏщW41m'﹔>O,FD7? e*fHE?g]"oSJܠ #b dE^'w۵dE +TӞ5u Ub_0s6L:aK6(T-Ø!-1-.#~\ek*Ԉ5-L@6q࠵p8Տ^iQ˃9C (&ʼn tΰНJpSKy 6vX[%b>i


:ۉa@.\m`ԋ~ه!o!„qyv$G>(KL(QB[[7!IX >WVC nZ %ğ(K 9>(̷&:GQȡgI 0 Nz?Ɏ^YI_ZW̊5>*0s8yxa>\FOYK6p g<挪yZ_n3Hu'%547NGB!ˠro,{@Fx;ˮ#Zޅ#AǾ_H,gA}؛*^h^{Dԏ ONUJ7#AB 4Y9?6V0eEZ"l=6s|12@MG?z—5SL* :iuGjVZoAwKև9o]'4Sto_o` <,d) ^_&[C@o^X!:I94E)m=)n\^xL6W[g^H[(FPև`D31N]jL=ϽU/|@zUN/1f;p}RX͉^ u&ABgrY+`6$)+6]P8<0/e? ͒˵FDeYS*p~3.:=Jrl(g&)+xˤW'Wf穴|)(u]UL#B_.yYWy+oMtKjmKm5vr y1&`PWuR|p> #(rvq.%^*6Pђ.G=(!K?dRܳ!t!n1h<).LI~Y/l7PdEÀ3ȼ8ֱ,iOp] %V mֱn& ܇2mT @rg$ up` 2"E ̊>|)[XO_NH7)Ŕiw;~:yj'j3Е-$2lѤ6[8Kzh[-X%P}ZcSH1 Ρ@lg $E9=22/R$n:G bޏm&_A4A/eȟ]+nuJu(|m~M8ktjk&{x/YJzBԀ ['o~V ZoTca"6]Ul qSt'Ba\\!E-7EPBjO\77m[GIݝgj;w,QE#Bځil9 IGrnŴ9i7]dL$HGA&ZNvTHR,|_'?sӺ< <|y\m !uq]F{)%zpw? vx#p0 XKzEs:]sN^&-4uK`m#|_\k iF*15MHI<.PؒL9n=E5p6%`>y~16*~YCa-7HYm_|^)$8g=&U'a 870uZ)88fR6ep38ot0LϤ ώWH桕PO .MICLAs`-F(< %'5Ph7)MoF9:IC"l QSuu@VGx6[ ,6uժyYo7";Y{QHW=[}5,iY*!vMߐl4e1qiIxVǠZ^ņbDTvK_ WFҊ,E-,=o'A"AI#¾?NRqIVQA?~ _QckP?x: endstream endobj 57 0 obj 11821 endobj 53 0 obj 1211 endobj 54 0 obj 11003 endobj 55 0 obj 532 endobj 58 0 obj << /Type /FontDescriptor /Ascent 960 /CapHeight 853 /Descent -341 /Flags 32 /FontBBox [-199 -372 1031 991] /FontName /DWORKW+NimbusRomNo9L-Medi /ItalicAngle 0 /StemV 140 /MaxWidth 1230 /StemH 33 /XHeight 640 /FontFile 56 0 R >> endobj 59 0 obj [ 250 0 0 0 0 500 500 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 611 0 722 0 667 0 0 1000 0 0 0 0 0 0 0 0 0 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 0 444 389 333 556 0 722 0 500 ] endobj 10 0 obj << /Type /Font /Subtype /Type1 /BaseFont /DWORKW+NimbusRomNo9L-Medi /FontDescriptor 58 0 R /Widths 59 0 R /FirstChar 46 /LastChar 121 /Encoding /MacRomanEncoding >> endobj 63 0 obj << /Length 64 0 R /Length1 60 0 R /Length2 61 0 R /Length3 62 0 R /Filter /FlateDecode >> stream xxUX۶.`!@`A醦q]Cp' =8!|]O>ܪcP؞*#(=O'P "'gȭk$<00W`q~A ?P pDZ۠@@utrE<U8C(-wA ]x`_u.$tAP[ `  * ; uuO ty@|@;x02WM# 7;:xh 0$Ʃ ; P̫yCuQAlQw\0BKN =[= A{P/OqmZPF!m=&@> ~48%8[ED`$P x ly`4 +_~ ]`>ĉ'yyWP+.Y@@ &&zM`$q=$"08V`0<"bU4lU-b]vg % %[zAʈQ}xݛqP3aѿ&q d ^OJrw "ԩ๲"oS 9p;Ѯorzk./AizT:c>edHK7>+BY}җBqEc?+z(~{~`{碶}o@K+frrƛuZ}G6=A<n`^ӨgJ"ߚF5SM+GJ3ZԷ}K[ /X6y%bW79bC+3Nj8PK{*:U  q/0?pfgT75mdxi*cNVw B:Z]fbƒ`N KU&'IĤ*SĔϜz_na`sF5F"NlDnJU88t6*߲ww1|z¢SMODP=뇹/$2(924jZ3t˦_~<^oX5ɟp1; )x?moO<2ZhDp1Mq-D^n G tsB|yO^{1:~զ!0Sn-ѷ9^hqX$HFG>]0*g~%p˭~ďc!ymg_5<fpu11 by2o{`H{~ư{CWp o9U֗j)Ma" E*N^ԉhꛏ -dVD/cjBwO?&)>+Vz:Bz#$0j4^Q3qVll?_ULA!atg/#g;7-s__+ I21f񶣚eA"&d|*ShmwdSsf p788ShIgy/a)t>B} `aNt֒I/ІP4uMjct|I9b" ^k%'+u"Gcwi8yQL /dqv_,m}z'G *G>\cIRq&qPJs8 M\@P'oSLl,GU&Y Ȉ $Qxuu9 D}?Xvhq߾T{,o86t ә{~߀տtÃd?2$կiI޼L7ӆZLw$,eʂ4, R"w{˕No M_لsU9Z`ڞƥbrԻLY_[Pπ^a3OjQnsZΎAfLFҋ2esK^fSls)Gγn26DZ?wyxbcW"AOjH$§\KʘK J,A([Yͨ6|˸:f9:k>}ݲ:;OZ8 hk+3|Oښپ\nuȳzL"7.#Xnu/:ZP(褙vY~ EI"H3Tց}]1h?#]!]d-d>7הΚ8dbm6-4eKg#FGfT땘V{O(x~Wz:7BJ[o~ DQSN]K1 bg8*S.A#-qtU"6o,"0%zx+;5&mB"qxm #3\)d2d5iI6d%s8Rgn :fJ~vSqC_%ѷq 'K!@tmkDlRaf+3r@YG3I4?<\ZI?A /uup<oPg/XQ>NM|q18)/wW/Wri+2R. RU_%o#r$\Nܨ-m'MdCtNL~U g]^4ѕHbv9"ڕa_rWY ,n/-co.l 3Ue0-=@~m/|)Br"TXpD͇Nߙ鹎dź^4 OVGb9dc]_a7<R}ZdKWdľ)cL9uYmoj]i Ԑ|uTwq?OT,"Vc5 \Gt|‚DOT P> 0Xh(XڦVgݎWY$A#c|QK~έ ウ/ J,ꕭS{iJDZzh6-k_]F̿VKeꡗ %7,qνwj=/ J,f;L u\q6EYnQ߁7L|^Xѧ6rSFgּhǰ38ʉwkP[BpDzEdFyjl478cSAUc A/6B}èFIRY 7B {^|Y/q<_]5]7#Hugi+Z7EZljϧg gϿ㧹҅'jl?WVt`**2]A Bz}F\4oF0u[ 䦙w](BM bn lՈBA]֟:D2^G6%–i]T\و4V}bD^6Y%lGcoUL3骼L'*{"Kfx"-&*oHtBj $&Ws3AvC{ĝ6hdp0 ­4B\qKgwR O!=c >4 ɭ>"ѴqQv1Δ'y᫹o,=jHׂ˖ݝWȡDŽ8EB,umAsmXhF=in,(=cUΧM;f:,TPGE7鵓nk MPVrSHH>{[aVb*j i6@c*Ja~= OLXI"2O++#1O{=s7c>osւ #Mրr>cSVpA&ɳ+nTըq.nL2q쮌'CdP8ߟ `{Tvm|<4hzx/%CfAE88 vGejʄk=խN*@tp CE#g]?v\;>ť?B4;#POZ#~nL|+C=)g@C>xM" LI:=. u VϺV8i.Q1Ocܛ֯՘Jov_4}~+ cNGD#A]喲s KtTJAQ;[dӻ7I5wY{FHSun4q[#:݃ROFeBߘtRy͂(VKIkAF";Ŷy> endobj 66 0 obj [ 444 ] endobj 8 0 obj << /Type /Font /Subtype /Type1 /BaseFont /BPGAAX+CMSY10 /FontDescriptor 65 0 R /Widths 66 0 R /FirstChar 164 /LastChar 164 /Encoding /MacRomanEncoding >> endobj 70 0 obj << /Length 71 0 R /Length1 67 0 R /Length2 68 0 R /Length3 69 0 R /Filter /FlateDecode >> stream xUT\`MpNPݡ9W}_U75=gdDb6FLtLeUE!a!Y3+}G 4#೚ LHZDOu731u332mm->Kli=kC`if`d ,-음 ᙘf}#3kx9JXSlh_UNFO>/ghcm 042hi)geO<π1 Rgef[;dJk=[S^o3s123s00Ycdmhdoifm$g4W:tLN3}-ە>pN?_Lsy%={3#=##g??ۭE l ͬMl={{=Lpg}0r ϙ 01Uo`0,`0H p.?_tK/}jO)֧ߺOӿb1Ob>]ҧ_tK./}O?>]ҧ_tqK../+/e6O,BF[~J8uf26s_Z|?'M#O9~ gSx~X~.}XYv6FF3)RC}l~uOօ3SW[E3ϥ;U>#ճ7~Fvϐm%l\tL:fN&vbf-kZua ?9*O66덌\ l xS~rQmn`u#1)yQ 6xb2_!EM u 2<\̴!QzDaoY$JU 0K#i* ]'/VRCm[u<dlV\K+Z;${t ;!]xXbƥozPZHc]WAɔ}1L;{Up 84.|woko{ E0&DSo;v}ߔTI ^ISu5K(B{|徛M>mO6~F';WHJT&%7 qRTe :q cī?b3hӳn 0d4Qb7E[ӯdF:H!\51?ŢPgeЫ,{B&6!w'sꔈNפȬg&,E^O jo)>tH%*qA&9fLBa (=6ן*Li}?}n)t,m.w &c.u@cVO + !㒧-J(i2c΢"ߗk*3a1QzbSmRu*ػ=TK]F9hDl:5~'X^=ޢȒx>@*kǖWyϾ>ܖ0*HK RW&{uмK7TN єр5ag68lDx,{%U<,*cm"K.$fv@._1y[k5hp+JjL#Y;Ғ|S'֐%}6_EO$`'t-OڦaFG]Yi A*lG:}(\KVY4:sxW6Uݠ]u5*L8*JV 38~i0JR}`jcQpH-*p`R $ŻfK&7`4k$S݆#辋B 52ca 8᪇oy,҄Q/Fk]3էhO hBg{_QJ箳Q.sq ϯQGHc3D|=]] t暝3_ez(ܤه&t_GQRZBq~WKc m}!c!ܞa6ê]4bxN _Hb. 2PG.ܾlz`YQ':sh3 ^aףW0ߕ @?!y&剔Tw [zv2,O2~.oŵ,]F+J)_ddǜffݭA2%R'PƘ/ R !ϨY:~Ҡ{TAVpPiLK<l\S3ՓU/"=R`LeOU{m`9W$oʓ8 $ c/mq_Aa?Fɰs͙ ֈ8ٷVpyT{+x\,{\bSu*"Pl@09 jÇeuf\Ir+,bOAVnf螪;(W%8^/*i Orz_O1hGհD9@b2v7hqV vl$KL[n/׹p61Bo#:s8"%DS1"7 R4Kz);jpIL8>;XgCPNt," ' <QӮݝ|TU$ )Pq<,w]q ^<f$^+LDG֡`dL   iR.}y9L"f:A5/&0;$9 M=O`'͉=CΗy&Gt!tߓҝʜObur͕'2:L; \3 vӔp iq<\ &/O.~4aFXt1ZqX82qt zLW;𽽉Q$̓w/-+YvV(]~P,A']y=֋ҵ\,CIg}ptq,7ʙ3"[gloc?Hڃݷ>q#@h{lq|# 1svhDÚ)^R~,N(= a\ $e}[HnLSsa:9u7Q\}|Gvi"[\puwa,*4Z.cT0oн^ͼ&+⚭BH+ϯ{˚zvmRK:wR4 NpPJ hW a;i& kӓ+v?|)ZJ^iE~ٷK&2#eRگ/#C'd]ڬ*WD:ٲ8!61•7 *ZA78TEk& ?J=QsWPF*N$9%|e/g`ɦF5Ś7[! -Y^KbMʻ-}mvڂ q/Pzۚg-m2"F1sH8I8iDp6n/ǫ20@Ee4nQgγr{nbOI8|44N?N1=Hw!2`.M6Y*z:,1,I>,![QDaa1319uiD'(U~e8x#xzK#iiRQ211@)ڵ"$|sOStncC4sb9 u1Hx5W% rתd4I B'"`IGd%,fOq0s+I3%WpUKy^:afiJI0 W- \PVxK0׍1Ҧ_8t+GLI c|dz+Obe ^;3}jv G4Э,MZX4vUE6$MjH"-$8ۿc/R#DZI`P3loX2XϯȤҋU5qH%,?8Т]38p**$~2a-H,Fv2 H= ] ,QgGC"b,F՚kþ)A JЍwq1vOng4qjߡ52\262xЮ4u43NJӄD*4g䘎4_6Зb N1;n)ykWhP:ah«x闓)?lx dSs);qb6KP}%@~|mz3VLi7ԔZ(Qcj<.Ôz>9?AO,ЍuKHƚ0Fuߚf#=s}\bC(0Ѐdg$_~uЕڽdO&KUx3!FK|æ(iƅdXQa)hp&&aesDb :{-~ȣ}n6y!(a ~gkX+/5Q|Z>@J2'OZq60K `4x|vnyRSƦ˗۸oF^,1Wb+YzIzb0Ո՛n%ͮfQm"k*{E&PW\ z^2<ݱ5EZh#t .D6HE :iV2%wsѼGH,}w ,Zr(Jw+3T2PdZi3el0+l2GmA,!^/[#HǼ׋[Rk!_*w36 5,^#BͷF`؜-1KҦo9\D"wWR=#tIޛ;4V@1l=[TM&.?1V }ĨcHW siHyG%MݑrV ~nOT:Z[-9l1VlstVR6Ozll$'jlNQK"pS@|@ISZ* v##1 6Ue֎dQV #E,{ExRIs bXbx(Y^rSҀ/30ZpɨN#{j6ֻ~BU_ҐNM~oWy+ ,sH_eyDQL^YIo:}iWRܽu,Boo6"}\~o+jP{ -©>R27mKoڣ\#/4Vc$Q)?BRA=HGiRAX Gy[bx#1~3F{qa¥P!{2ŋ :ѓې-ow*+]U H/x+`CELRqge q;m`PQrhvK}<,ԆdB:Vs;jRU߀M;|˨G̯f;pł]ͣF]n*zD1 YOܹ__xh_JIvy9WCΪhk}?0:Λeج}OL8][*X5DhuʮOh8ٓ-5BTӗ=u\f$c1`cFb9~!Ăn $V;4n5c9]3柙 kO|EcUYY}ށpPiyX0b൱ h/ԩ(RXg͡IvX/F\=G)2 Tey8Jls.Fi5!b(", jӞnΣy"/蚕pǦxʆoMG( EDU)mM޺ۿ{7'r:LLx,@~]3ezkh|sۢFdGgY2fNVJD{7gJZz1QBJJk77{/>ۛsاu~o9w˿Dld$f |DbxkK{I(t E+&ә ]BjÏ|9eV+F뮡 [Uq!OZg8&a/ccrxz'^Ѐ ^X+7ie@)&u =I{@zh6}uX%3ZކCBUǪVRVVDx۪1TW@( qG w6?J%'[VVtZ1b|^8_!v`-"U6lƏW D9x#yӼv8ґ)sP5 qt(EP˱eL++ߢ4d282% dJҶu6_sj:yi6`YCs#E _qI:Y|Tq%DAJ SCax,d6NSHK]2`<)K0AT[RiƸ pQgpywu~-߅*#"|?jGB3sRM=bA8!Q죀XQV;g w.C?x&f, #äZ;N0yH5ݍ֭Y_/ O^[ڄd(P7K HM)P(c`Eo6 gE\5gisj}xxIe m*fyE1%'tb l Z9Mȓ=Zaʚ~&Ľ.M–Z30xՆ!yb۹5GX k;]k8DE.Uz'XZ2ԝnX/hc8T7NH!e?rATri>`vʍ.Edw-{YRvcC^%x DS}Kgl~)xI'A2Yy}y=T']eІ2YꝛHo}~.)%=,><)$T UL~Y9q3mTxem:o&?$ud&a+-?0BavgF+- l( n7ŅBG#9;RRQ@,c E<ٕArE] (BeSt\(c%W!J;pV0Rh:y\78GV\+1Ak3+}ε> >agU^96'P6& (Da3C<~u$@)juΕt^[gN>ø ~\{haSsRefʑk?lѹKY3cRƩl,u<$*(4Hy0Nun*8,{IDױƈ9c@TeTĔV2yD_B>Zi)h6/@p[Vȑ~h/2clF3 j՗QbO؜-V ;:9V,#ٜI>۬q+p?!26=}Ջ+ 3~xu]}8 `gzT${]┵B"H4Ao?łJ!GvH1e(ڳWT"+'\؏Brؙ~{EXzh*`mאa(@E+>($` UV|go N9.ͣiTarH-aBAR־ [8_$v?AOwud{xhf7a, [&_]k/7X@Y"OsK%ߗg-os;KTpcf=}TI c!Whp֎وq~WEma"t1oD@9#!<0.!H0~'dChm~M=szl*dڵr&HJ:~vFyֺȃ}?},,c.F&.ʫpr }}9/jW5Ek!Ҙ;f N; ypAEs\^؝tBy߷[?Ppli`TeJ [%{D=8*Ni\'š`TQ(O-ykm_Ыvx)& 叽qk*y 0oSM xq;n'xt)e˝/Ԝ}Xb+˷6"L kT&XU8Ggb^PɊT͢gb4ݷ2cmu4qFoai1y/BA\(C7o_bt"Pwld-bkKİx^,xQ[G㫆niy扞Ph$2Q z'?CwgCKOjx祇h"?duK-;RIa v?Km؂iK[q_/p_ʣoh׊gLٳs48dAo_ ɽ"V0 FIl.Dڪ-)(` h[|Ld/Wl7MSn/Z}Kj4KtJWsߚA  9OW! Ii<24bp/a="fpo·ǖ7ORtQU%z:ml=^Ɨߡ/nᰱ-'}gԖtf?Gw9blQBweOD7oU-x<)SAMRd݅Ae*hQN9plB7<6!Z!Ww%q͌-+Lے_6e3Ot1K(ֿ?{a6؂A؉aDˍ]p'?ƹ/^!~/|q\t :M\BBHZysH@=g>;J8+|VOQ!" izrҭ'TdA/t% ! I H hyqE@3WٌH?U?'||= ȵ%piJeuL:(MHZR"ׁnҵ)kspuJuK@cƗ8M _Q=٫lZ $8TĮHIlB[f'3\Xm^`U7!PȰGw9\mkV$1Ft@݊^2bP¡n~8p*";P9Ȋ+$=I0\҇f|>RXȝ.P8GU/HDŽKx?+8sQqy9zRW=}sKsW3ɱtħ@͡΂@pc 8ďUX9վjV4ZXJCMCTx^n$q>8m$Pjv%-e'0G_.I &,Q}'/8&8إM gQd+ m<첞2a{C ߮yڞúayu 0b?nT5jqVW\B~'sS?eVwV6tiejdtO E]S䚱\ηAJ<3{1ơ9o6 ~]ps_['e_C^YB=a@Vy` hA?y*S/3P3Gkhש5=g>Y] *EIvhEVH,#/EhR]nR}FؖB Bx=&SNÖzr#DN8a'2_`eC-8:(VK0vZpv`VASL2nf~2H(O'Hd  8&WL;;B#-/dλ%>qo@/qG;Tc-%]Ėʛ$V2+ ̍/ "\vc0@_,څdEg ՟ل#lZKWA%RrOpRgK䳘aNGbW]EXɾ-HRRϏ]Fسy;fCPAUo8q6iϮ7/q,8Ħӵۼ7*dNkHMNﻺyحW {,\tiwYyN%&Slթwӗʅb,1!5d]f?D~wt"J/yAv[vv4,ϣ3k|$#Xe tCDi>&QfRj%9U^A#ա,A>lIxՖ=YK˫X ,d@Ƙq36oU AV۩K~f8V ]|;C+NuBEKK^ xC뤹|pw#Dl Wkz̗-c6p?t)r1#FZY;<8B.i('D;lkeAV[P(̘&e1l߲kwDuBiUdc›Hs_}1?ájEAie\MB94NمC=usxVWa@6c3oc> endobj 73 0 obj [ 333 333 0 0 250 333 250 0 0 500 500 500 500 500 500 500 500 0 278 0 0 0 0 0 0 722 0 0 0 611 556 0 722 333 0 0 611 0 722 0 556 0 0 556 611 0 0 0 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0 444 444 333 333 0 0 0 0 0 0 0 0 556 ] endobj 7 0 obj << /Type /Font /Subtype /Type1 /BaseFont /VXTBCB+NimbusRomNo9L-Regu /FontDescriptor 72 0 R /Widths 73 0 R /FirstChar 40 /LastChar 222 /Encoding /MacRomanEncoding >> endobj 1 0 obj << /Producer (Mac OS X 10.5.8 Quartz PDFContext) /CreationDate (D:20090920233725Z00'00') /ModDate (D:20090920233725Z00'00') >> endobj xref 0 74 0000000000 65535 f 0000092835 00000 n 0000012216 00000 n 0000036285 00000 n 0000000022 00000 n 0000012195 00000 n 0000012478 00000 n 0000092655 00000 n 0000076381 00000 n 0000047348 00000 n 0000068875 00000 n 0000034304 00000 n 0000035112 00000 n 0000012815 00000 n 0000034282 00000 n 0000056195 00000 n 0000012725 00000 n 0000037821 00000 n 0000037720 00000 n 0000037612 00000 n 0000037506 00000 n 0000037402 00000 n 0000037296 00000 n 0000037191 00000 n 0000037086 00000 n 0000036981 00000 n 0000036876 00000 n 0000035132 00000 n 0000035178 00000 n 0000035228 00000 n 0000035279 00000 n 0000036248 00000 n 0000036648 00000 n 0000036418 00000 n 0000035333 00000 n 0000036228 00000 n 0000036368 00000 n 0000036629 00000 n 0000036857 00000 n 0000046942 00000 n 0000046962 00000 n 0000046983 00000 n 0000037925 00000 n 0000046921 00000 n 0000047003 00000 n 0000047254 00000 n 0000055726 00000 n 0000055746 00000 n 0000055767 00000 n 0000047532 00000 n 0000055705 00000 n 0000055787 00000 n 0000056015 00000 n 0000068331 00000 n 0000068352 00000 n 0000068374 00000 n 0000056364 00000 n 0000068309 00000 n 0000068394 00000 n 0000068638 00000 n 0000076058 00000 n 0000076078 00000 n 0000076099 00000 n 0000069056 00000 n 0000076037 00000 n 0000076119 00000 n 0000076357 00000 n 0000091853 00000 n 0000091874 00000 n 0000091896 00000 n 0000076550 00000 n 0000091831 00000 n 0000091916 00000 n 0000092159 00000 n trailer << /Size 74 /Root 36 0 R /Info 1 0 R /ID [ ] >> startxref 92977 %%EOF splash/docs/figs/multipart4.png000644 000766 000000 00000065733 13261626263 017475 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATx݉Uŝ?/Ѹ ("%jAD@vwlvh]P茉8&3dLL|yd2SVoU}:}]y%M{UԄ HLA*HQ=5K_!|SPa29\r9e/rNcK9NxիpB)'ʜT2*ӻ>&>}9Mt7ط&F|ycE $)ꩣǕr,I ߑr.󥧁xZIODFHuR)i MeO=rz/)Q$;$^rׁ*ޯ7\qe2Uε_Rk?R YH5)\H*< 4O穧Q<)R)NJ(x :9$º]3{y?oSUUrJ<'Kk T HuyRJJz*HR*D@ ,RXD 5>R9zr{WqʿK0qsG`OwlU~9uJ' NAU]WDH.CJiw/*U XRڞNJSls UڀV~TD*ɓ!e{jZ'E*SɔP\.Ia;$Y |KK>__`U]UO[U⅒*T7SQ9*R *t'R_F@3 ]uPRgCo;UCTR {&% )<>[ |`*:NaORpt'ZTUoS['{ϔPr&%n.pʊk'zpa?NUw-TK*\H5YMR R))3>)aOv!e<[HR} ^֓WBJ];\9`,:aUСvJ+1 ]M唯2T&A RyCDjZ B|HT~-O=K*=uܓ$ybne-.d;2d.KM"0]SUtWtJJT\9uJ;)*}[ޮDT`HHE~v<"5Υ $H )Tĭ[j {VHOf^x2Z#f]>ۑo9{;tfeT'`2ut]J/SYUtڃ%IU4)*ă%e/I7|Y?R~@ r!ۈHHeNG()RH͚)25y Oess<=äzx2uu]/sI,SVJʩKʩlI?=lU1?l)ʅg)hRlR…+6kgei!&OIP']@ْє's#ʡ+JT\9u _N>:YkyTGρKQx~f HT!@ r!^MT `7t٬7%xIaRtnѿ4*ΕSȪrRb( =?ѓ#DB?@ r!UL" ,p[8 RuD3fWHOp )\Hyg>WsD{ͥ&8τ"TN:-}t&~~oRN9BJj(RY]ǀHBW3 RrShJ-|RfHRO"zj)(&Ћ|3UݠU٭tV e){2Ul}$UoQ R4"U|7~cBIe3'Bz:9R@'.Ӟ }G1)G?*Sen~WM?K*u ժ:,)\nHuR \H}D)S} )$:+xux2=>$Zm2hXTUJjs%5!e2A%+jR @*RDz{ٕhHT' H)4YsS;۬&EVt'ݷEM޹dT4e(^TEk޴1:UT.)ʅ/#R5xHT ͭ)zyO13u¤l썜;)4@*RG@ I]D#umx7){E5::^<RBn=-{&Gʬ CtO~5,!LȞ¤!sg)wʖVeJU.B; Ty3*r(MSU1ҽ<ˍ @n|Eu R "ReR 2\ )S⡼'l698!ZxEYC<TCIUET9ħ)2@Q[RZUcOz|@ !U/$͓TÌZB N؅Tٴ'bfHB"<5A="k(3daÆ /gxQ%I)Zwt LOp!I%R%)ʅԇ")r60]Hu )>z;ys졼K"Ԅ\u7SOj{ĈȌ(FAR*8RsRlxT6R%SuH9k$7DꃈXw kDJϻ#:W}^ Jo37y]^3'$(O߰aŽ`4rQ:uJ\٤1(Ri3~u'em)OU:m@ @*׀HBtARēM*"wyduXy))-٧'UC!={##^-*J՛/[_xa/%#R1I`Q/HT>~٦L5ER ]kRM `*S*JRv!Ş%կttA?{ܶR}~ūnvIPG Fcǎ'3VF52oHYiR R"R RTEE_[%E:'('xR}Cyj$j(agO0ax]ȊHT⎺wTTS#NHD꽈^HBo#uHщDR^!u|z=\HR܄"M\fSY+& $` :x&N4idJS餢6pV d8בT,WK)H) ${ HT&"58fA:~)F.Rf)/A?/e&:)SH)Rjԭ “@"rIZZȴ477 ^}tAج(ĸR:WyHR#vxGTRs))5 Rz7"]TL! R!u CJu/'=\ HjР;C{T%< JӦM6mP% v.R^!UkR܎.)):6 G*&S SKH@Ꝉt NB1G*~byARHN]tnr\V.6F1zhIM̙3g#~0CJ6))AdBFDT_w+I(@*= RiD)KU_O$OW}}>$e}Ç5jԸqIP;wyٳg̘1uTM DE祤HÓ0R@ r HT.~ChtR΢Bkمԙv!E}WvO~]wBJ`4i$-X`… ̟?Μ9;Q?u(*B*)r#QE<9ގHHTHT)4zw{9=%tRHI )A?_Oe=ݧ:躐ѣ'L *&I`jmm]xE('Z^Eʾ{[RdIoHc~)q*). Rz+" R ձH5ȪRAOBdRl]Nx:ԺUH5E#F;y֬YLfKRӧOhA'>))˕Wq;셽iwx%%QQO@=\K5cR)ʅԛzz@*RoDK_̤*,xt*L]tCJTBǨQpϟdɒ+VZJ\T6m RI;RޙqJHsDR^=LOTR@ : 1R #RD#u(S6)Nz"& ̪m\J)r\頋翠 XhQ+ ;Zr6IJWSNT}TqvIt!UZNHUtrk^!ո$ R k@ r!ZD6)g\)zCJwO;> Jt!G'^uЅ{gxQH >~Zn݆ ֯_z%K,X0sL3:!9bόNؤщ1RRt;IuY6)ZNe XNUT1jQ4t-R ~J HT.^H׹/BYHs)) ooާIB#EiSK脐!IfϞdɒknڴi˖-7nԒ%Srs$o% ҨISRQR: Rz%" գ|hl#;kIR>/%>|"VZ%e#Dm"~U6JALHuR 4=R U1#U.,OR\/L*>6.}ށ}k4)sw\1mڢE֭[w?< v}>ofR,IJ#%QTHT)ʅK钤.>o)A?ݻ 7nVt>LT<B?k7o,<޽{׮][n…3gΤ;.ݜDH3~we)#\*w4Rޭnʤ^HWHuR )$,}s >PvН?I'(ZJ8qܹr#"ܽV{UoIwpH9R\H)H tI+;lR\RyAAN=A>^sT͞=uI&7Np4T> >!%B_/bIIT)"H)UQ}toTZ=3$NU HT.F}aR;r7~I%zIoE-*L3eʔm۶=ί?_|!^|B?.ީT-X Fǎ;j(uwk1_VHd)'eoR)\R #ҵI_b!uJy/RSRW_}GJ<;ШQpjժݻwK/?_?Wz}ѭ[ϟ9sfKK˄ Tor>qVK$E}ɤNvv%TF9nG*noRZ!u|ygVEI=)H~T_@2/jФHI0)|RRr'DR&%5jf̘H2SO?o?~ذaŊsQc£# |r&RN٤1;2xD9R j?G#R% R sYR R<@Mٽ9@J<Pp]sϘ1cDi4k֬%Kl߾B_W(ٳ}ڵk-[pBJrJϊt}RCJrHiU"EUeH=@jU R9x %R3'H$)GE95n8!DZt<'?o~/=ܮ]7onkk[xyDZrsSN7}Yo@9%UM}t>ͨVFRR- pK)HՍ-(M@@*RG>%('3D`&E;)I2y{^Jm+Rw9l0QNHESL3gy}xO>?ݻwǎׯ's)Ȋ>:GqʩDRJԥ&2yjrIU3a)j R /fgHH2[2<+ $2Svڶm*԰f]NHSv,O˩UWNc) +NuFJrT2)))5K!-M"EWvԄTqΡr꣫3frj<EEBݎ;6m$ʩ˗w}gO2Ea^9E%Rwrr#Sr*)NÑ[ŽtR?@ @HH .́TxU*RR}1c>#iT-[6QkrJlʩ;Kݢ贜Hr껁"wyTPp}tTEISur$ETQRG@ @*s@ r!="])3)gf#K=W<%):c; ƍ4ITG3gΜ?ҥK]v=<#^R<꣯^zٲejXNy3>::}t ^l/ G?+>G*<9[) HT)ʅԶtIRS{HщI$u^2H 6'LPVQN猪o޽{w-Hrn޼yڵ# IC YVҪ )~3C *rY2őfA*= R k@ r!5" œ2 zxRʓG*fK?l̖S}JHD`6Kk\HmHI{3#%U)noүt'O."mISNT+Vlܸql[W;-޶f͚+.ŭ]`#UU*~W?ڏ,pH_)*3){qHeHH\HmHW%u)̒zRq;.%%Wx8lޑk|]].hشwk׮ZJ--Fx$[# wxRŝwмG*GϗQU))\HH"u,%zY!HIUMJMkrJ&;d+YZh*_N֭[z 9<;ӏlWOPx^+Uy;:_Ly3>!T%Ҫ()$~+?g%S=p R R@*Rk#R)\IS)ē.nw(ķYRu>ҥKjżobI[d锯<&ETyج]NY/孕 )\R 5HR )dR=@aH٫I9NIUf$RoΜ9˗/okk3֯7YblW^1OsUG7){"Rd|,R#R RѤI rDRf}G)rw ww'~7kY =5f3ĵŋWX!֭7)%'^y9p,Ug{ؓ~޳컽7^z4nSHT."R R 9ERkFH"E)k|8=ߩSF.]j~Ԛ5kV}]fry(¸r,ʩW\m˩޳tc~H9** HH\HH'e9:Eժ)JSfiKUлl7rj޼yju6Gד~B\;._Jw mrʩF.)_5HTTN}eD@ @*HTc8RRչ|b+*B2W9r\k6wQ唠I^Z6s.-/唞sTK=*g4~>Gj)è:%~68^Ԋ4WA @ @ *LI.)73Iщz;Ҫ.]*=>QFsSѣǑrjѢEtGW̙3Gս#r,U{u)ΒIU"T76<" Ej)\R ei0RJ#eF'CH])RN]Sfi1[*2F1f̘ Ś59S-J1.zn1_rGE ,t_!eR R IU`)ʅ҈4,v\HtdM,I9|9u9,w,u:MSrBgn#~rkAnRZ=T1 zG*0@2tz#u AwHSH?VpR R1*Z%Uu_~EtУHNRجOCJSjZE{^=T=ŘI& }[ )RHyz)C*Vo]N}RkyUfYSe95vXUN͝;uٲ6Cl,ޭN6lX/~ZsҨ^ᓒH%Iyy (>u R]2׳ RjH*zoR UI~Oڽ9 唺مSjYo{H#R R9 @ rLwbHxbfRJUxz"0@aH]x@*-PHUzQE1 9B!*"Q3^u*]d9jSQETwq^a{)Go)3guj)QKjaDT%-)ڤFR IdR'ؕK."$wM^H7ró_[{,"{fK?Ej]y9BPnWO9?+;C*5B+z:ۧy!E[AHog)ۓM[1eR "Pj5&HHA r 5?" DV~R6:@!U饽 %eZW:`MЖu.VSr5RZS{ȑz8F~ėS}tߥ#OTϞ~=@THupR?@+Z@ r!5/"]?6KF'jB+p9+*CV,)9Bq7<+>"~zYN\R)Q|?3z(Ũ*5FN]Ȟ~~=Y#I+>zx}wR~CG]I$%~aRAJ}*&Sgt HT.fG@ @*sr e6H;HM?kW?#|谟+]jyS{9/]VhXUPϜ9S4I9B GO09sʾAlGI)U_B'9c Tn HT.fEHUoR ՅIJ)VoQEI)QE6u9;:#E]kn]I?W6%u)fEjS1ZjjϘ1EuZ@RN^^J'HT%ER RDU{Hy}tTu6G+*r6u&:s9h^2 ϊI8ڼy˖-[eDQ%T1 pir>ЃdmLRGp#j?ihR@ r HT.E\RTM*&՟[ۛTN]RRDUb9eT ];ydy}]T}fix)*%K̟?sLtJ,O!˩t9eѯ(lRΩmH nf'щnG R~@ @*׀HBjjD )Dkr6TGrtN]NUi Y`~fxv<;w{-3g)ƌgeY M&\,EI%R_H@3")Nt@*RS"*^d'TiRZv]6飓r|Ҫ ZgSg *E,>_Nyƌf+W\nݦM۶m۱cC=Pq)Y;mgo)G7KIRRzڠM%")̱:~Lʻ"()?z@^ysWMI5kHRѮ]ٳo߾G}T\YhѢ =n=k7$w{kJoiO.HBf.#@ @*@b~/ιҨ"HIUd[=RSwJSfK?:'{{WZ~-[D- Ox'EEiӲezc7. _L#jBY$,@ HT.&G@ @*ssxp*CVuGwHiUq63:sTQsY+ruүTNXb͛7Iz'{ U;w^ڜ7j)G7^}T7ǒR_f{eG`I/Kt3:2I)*)\S4RޤQEIȗS4Q_^_Zti[ۆ o߾k׮ /g&RNFGR RwygIq;(3J~ E:'UIQR#R R 9 R kIy;2("ʩrJ"[a|9nݺ[>{z7|?ohkkǸّ#G YE&IG2)oIooNHog)IMH!U HTvR^a|D)<~JKNx>:%P&V"ޛXN)!-©%KQ7رcϞ=zw{7͛Eh9lHKzHIFR@ HH\HHW!EWB}t:)wWlR,>SwrJ-[ԶmvϾ}ˏ=Y]x IDAT[3YEJO$%wtBIq0){tGB!uHq)\R  SRpf(.˩K3o袜óyŪz|ɷ~O>?W^yEڰaxOmRj<%U:9)]HRD)A-D@ @*s}zTiREy{Ϟ$ ~ ~rK'N-\pٲeoBp믿G-Yd֬Y--OCTOE3?JB~ S@ r!uoD@ @*T1}:cb6pJ[kǫ &I9/1o7n:pJ2?I2hѢ+WnܸQS<Oӿ˿Wz뭷߿eQQ͚,yRM? =RdHv!ϛHkR'#SHT.FG@ @*sIybIqT")Oy餼>Ҩ,!}]d95+䰟[f͚[rJP͗_~E~<RhQ\s=.DsL*}/#)IHHTHT )*Lʞ`Iq'z{9K<>,n)wʔ)d+}֭۱cSO=_??.TjӦӧO0AS~mdDRHB*LC*XHE))\_Z]IGErTEl`}飧}tpS+|ǎijsJߵk //|駢zqNT^---cǎ(U]A)of"R#){"R RѤFD@ @*sIj)d~GJ*>@# φgrGórʬrTiԖ-[{^_?>8xPp[b(&N(DO"NRS.̷N{@*RwG@ @*s)[U")Jѳ P83}MJS9͐x;唜3f̄ Du4sL5Fzmo U*`ڟ[neה)SD9%.wNIlK.Ζ_T2ySR\!NzHT)ʅtmR@ݐ)VuF>:9G~95b[jBf/T}'~gK/=#đ#G:t[) I:Ѥ* đ:԰HHeHH8RTU"r*LWe?ʤ7nɓM6{ljVo|G裷z멧ڲe… G-K|Rr$?:"HHTԛsHw{cH^"E~ U իW+U?k׮˗ QAjaoSEM!ŵ¤i$jƌ9RoΝ<̛o7#jW_}U۴iAyW5[HiδO%耟$,=BυGI))\SmR*JʗBJLr95fQM!)hR#R R 9,)OO9RM]¤E,)2@MJ HrJ-eرcԩSgΜ9wo۶m{_y7xW8cǎŋKNΆIyKz DR%ѤB)6 R=" R RREj)nG$˩0).)AS>qb3QhѢu=B꥗^Ν;E5e$))HKqVD]#@ HT.E" [@ @*"ҎT|YTQUDdJ>-T`TIiU)=<+ggrJ^=p?/T~%%W7sK L"TaRb̤lO>r!e-"yYhr_u>@ Ñ*w?@ bSKRDUT`l6GRH P>Z0s&Ԅ &Oʩy-_\ z{gݷo/j)~ÇIyHy3'7w/S{~[#I J6R R| 7 tIe[KIuvtFJ g1?Cn]gKk{uOF)o~S䰟"}}yٳg֭/Vͭ^HQ=R>@}7R|/@ȩ3l:V /+)b~J~x- / R UA˼r"S>Rٶ;[jR\AΘ;w۳'ܿݻ7n*o5*uHE0`HMR}r:u mQD:6)"eU^)BGRF2%"HH۷ookkD5rHY<\q#TCJw9Rı^R~)&HHeNhzH)U聭Ij̙ .\r|p׮]ڴiӲe{Cғ;lRl")nf4~trVvpʎ#). R9" R RS R)F97DϕСC%SjzB5kVkk(6o޼s;DMf6rVLx0U_KщJTZ!ubB R "R R 9 MtW9 e&O,̙3gѢEWaÆ+V̟?_/.I|VԕR̬OJK;B*GUIE1" R RR RL*i%wqOv`Rdl,WMt^J.onn>}y,Y"H J7oܸQxbQbZK|.~ڻ7UW3Ԁ`!AwH%yRB*8:a^D@ @ 2'VU)RJK$!}lR†S۹~K.mkk[W̚5k/_`qI( }ޡ߮M )w>W}dϻ;9vw)ʅT17$S\H%q2Ļ+.yyM^C^Rq1 t3R>"R R 9 R kXREI{8KQqv2gOO mr3'IM)ylX#H-^x̢Eop'*/A]N\N:ZqT`!Շ w3 )I[ŔI])&uMD@ @ 2mדԀS )LOHq+;nk3g΂ Z[[E %^Ο?k,me}щ+J;x:Ηso`V RA DRG/@ HT.HHT8|U餔vߟ%%Qe#^#eN/tx}J %2͛5kԩ'N )ub_w'vpsBJf8R^!Hq:Hq)ʅU))P U՜==Eʛ0y"5{ljܹ̙3N4iرcE%/UHRl{+)o3ٵ9Kݤ2wI!哺2" R UMRBB'Y R U'R$TlUѤMH].CK%ffbv3Yf(&O7n =0;G'2 NAO!u}RWD!E HT% /pgHyHVK;I]S:V4iɍf}^JjҤah&'apȐ!P)5"ysvy32>M*'U6UPHB$bR RxJ:3mL}7s-t/ARƓ$"Ij?AJmnn$R.= Pxw? ..vR}(QB7_H/8dn&:\'R.!)ϣF@ Gmuđrn6)o;YRtAAB(b55ixEWͣć9JT:藒eRƓM-웼_HEk,"L٤v|Q Ru#dN @~:gZKIUYH~ I .m3xhU1Bxtrz>^!u _HsBĠ'oY!%RQR߉HHTHT)4sLWU,DHRҝ_A-R{4xèQe%<Å/.&Y` ~y:5B.H!U HTvRֈQᒈtR)Ry)F&(>Rb)Mʌ ;e-F* wejf=7q!M=-; NPOTB*A)6 R8" R R')ymQntZ[")UN U#S%edv ˬ:LkunVۓY!{}M^u4Ү.⪏a6RHBˆHT4b9u5):*GRJU;I%m&EoBJSwhU $#>BLQH!u.'B_}zt, Rf>݇Rl@ r!uAD@ @*¤9REH)UC*Y?wJQQ;tuVWHR}3R>)2RG&t). R k@ r!u^D~۹PRF4%Y?%j/IM!zR,TfR:7" R M꜈HHeNHH]"Y P|DeҰnQ{4&]B(=pu“R{I!u$%'ڼeRfp+UyR HT.ΎHHTd! PNdJ:PESHʩ@FEGVQns~]WHOZ'0:I'WHTR oG@ @*s*%8@RLLβrJYWTt%57 5RO "TTD!EHyYh^ ^=oI) JlUH*j}CJ bwt+<1?ESe|#೮ۻtto'_oh>bR ))ʜBSdۛ ) [}W"w{ϋ]!URv9uFhIEJ ^Ret{dyRRʞIIȩ R 3#R RѤG#w{S:U)&ETQRޘG)kmO=E2 )bR R@*RgD˓UIy!eSpŗS}U)  L'1H1tx.ޓtx:ĵR.(R R@*R"HSJŕS>EEy??K5A[iqQ&%`'S,dϝB*I׽l\!u t8_HT8 R:=" R Rc"()s")zʳrщ*JUM]]RD0w1}GQ'SS:ԄWHR>$鄘Cy# )GC)? R:-" R RLJ P"lQe)Ur"{\adzLB}nɬߟ=xI牧txsy58O \HHH/(UIugTE6r>#aSH)O K*;=[5QLRlOiO)S_ۓ"lSO1t|lٗI_)PO=3y .F"臁H@wD:6)S @ rH~AI٦JQrOT urU]TX2/‡&wBseR=|O= Rԓ&B9ZM)xRFG&JO))ȤT2RJHq7|M's9o Y]0˾cRB:'#:?\': )WQ17@ r!urD: &Rb[DR)()I)ÁVV.W'0".֓|ΰL9ԛ>x JdRNtK HH\HI)GGVŒyPUz?Qe`뒀4fK7lLsnf-,?$੯u#eϏ#Mly}?N!PE5tZnĦu pw>a)ۏYQ#=9G ^VtW} @*RG@ @*sR˩ʝsUT|p&2Rb0}`"s$)2 yS8O;/=}HB7HT.HW#VB'?(T94)JR@ r HT.zD:->[M)[\U)y?W96##[\;IDAT,J6&D*Nbq*Ddu扐 '=-w<[L!} HB[D@ @ 2tQ.*gWTŋ7G[.[^W=+Pj|\F3ט={ mݒ)))fHR #R RѤH!uT*,bUq;,/0Et{"6#<%n̴υ*tFd!Ri4,ڏ:!iU>~V3  a)yt=LI"n {fn剒*'U MHBȈ^ HT}H_:QJT2Xtϋ Kp"eOɀj'.a_=٤<}HBꈈp:)\Hr8ήB#*Jb[dS^/*Vr:(zj'HB갈tB?Lʤ*TXدDw9}x8^ݱ?_? SBP{%xb&&"rp͚!%UR C#R R 9ERGGoq2_T%*_n 7t1Oj(RBFedH=ˣ)} ؀HB")NꁃStä*TXWQykg_T$: M$ۼ]BE zzp4 ?\H)&tRGqdmTU*Vo7[ 8J}%Tڨ(Oi(Owjz Hq)\S&'r.xX~/ˣ&^f mJuS;<EoG}>)yO>G@ @*s@ @*IS*RMuP  pd\^`VӇ)(fncz -)L+_@HCPR*ԇT!'R_HHE. tR~DYQB :.j0,I]NeC|$)>Cnr(7wÞ׫!O-TVOb9uUL@ r!R:U'U )E-HCPRJ+:OSU>8ڣMTzɶ *+".)OC n zmrhuT!S"y? @ rMԿ/E>U}Tlϣz>AAAAAA_cU{!Y9ϝެ"t̟ת/XB-FZoN̂,4u]hrP4x[YE5~Z ~kB{*oYhr5ww_}\S V7P~CHa ί5KZ˿jjkqկs[Sc_uZR?&H2ykK$B-UWjFFb?ZSMM+| kguj5wwBвy*>*y?J$>,              $,rXɞd3 q)Cc ])5N dI$*.19\$.R%H!H~ԑե)IL=QpޭBfLXc~R-E\Rk@ Ar "          XzNIENDB`splash/docs/figs/surfpart4.png000644 000766 000000 00000014624 13261626263 017313 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5?IDATxew^`PTQ@QTDvMve0 HC0C','!&Ng2t (" *~]unUStq9u:[\X^eW] #+8֬XIY8ݽ;>ԩzݻc IWTp{w|nq9b;7~V5K]Ul񱜤JRRWLRjZ$5,uV/83~9}C!a+IoÈIN: #)E%I@R}"Iv5PRIjg6IHOR#*X1$E0Gy,ut?H`9ImSG(JRIj I`$ $5$E?:9<"If]jkIx$6MIKxNJI Ժ%I]u/+'AN.srh?:g]WmJR}:|,%!K?Zf6'wN69p/Y $5 wԵ ۗTFRȧl;$Eߪ7K֌FRV FRի8]@<84"I]-ݬxnyqnYIy "ko$+I@R}"X`RRHFR\pWO8ǚ[VRƘ"Iς5@Rb^p%E0zu)JR$ Ljm$E0I$E0f"XTRku'X{#VFH@R}"T$g;s/Hj&'/Hj&)II@R}"I|f"T$')RkrXW#Zp|/A\$E >I,0u)JR$#RU3I,+)e-,")ԳI`$G~,.!)E%{$E֧)I~8$E >I,0u)JR$#7zb&)e% H hIHORIʌ #*X~<$E >IHOR#,d8w2I]+9sf"T$')*LR$E0I`$ $5$E >I}f"T$')ԛ $5$E0wI@R}"TFRo,LR$E0IPZw3?"I&\PՔ/i$')3$aUI>ڬ"I]S6)3wޙ;~cJj%I=] $')K8"IxՋ⤓a&i$E >IP $')Կ.LR$E0z}f"T$')ԏHj&)SI@R}"T$')ԅI$E >IHOR#' $5HꉂM*3H$U"T(I`$E )Kq0I]}3zD#HjWz#%RHRBRh$x&å;WX"IlLzD#lnR.IR$$E0z@R3IHORH|f"I=Z $')II@R}"I+LR$E0zuf"I=R $')II`$pf"T$')CI@R}"II$E >IHϬw $5$E >I~@R3I(LR$E0@R3IHORHUI@R}"I-LR$E0@R3IHORHI$E0zUs_V)I)LR$E0:S $')II`$uwf"IU $')II`$ugf&aHZɉ@R}"IQ $')+ $5$E >In/LR$E0@R3IHOR#[ $5$E >INHj&)II@R}"IHf"I@R3IHORHI$E >IHOR#Y $')Hj&)II@R}f`3w\ $')II`$CI@R}"I`f"T$')Hj&)II@R}"I}f"I}_f"T$')Hj&)II@R}"I}Of"TFR] $')II`$]I@R}"TFR7Hj&)Կ(LR$E0IL>{'ÑԵ$E >In,zRnFH\-6)IbRf$E0v6`JRԩ="I]Sdښ/F$E0gIВFԕlМ9swu]ݶ9nԩM;'+A$r=sWj{;oVRJR} $%>II}[fn>|zs={fǣzH[ $5HR}CRIOR|ܳc=O> ^ӧO?/{٪W@R3I]".;;|<m<̃>x]ϟV~OREz.7M*LR/6%:{I=#O?O޴Խ>Zޠe >nU.'lڊ]MnnR7tO<;|mw};{{wy_zVjS[ˇE%%` o,Xߤ&y L'&u̙.\pܹ׼ٳO喇z/oFR//II*T$Ho#z+yW;1qOII*I}]f}w?C7pêg$'U"Ioҗ%/YJ,Hk $5$E0I62)">IHOR#.LR$E0I$E >I/LR6x"|efr"T$')?.LR#(LR$E0I$E >IHOR#T $')ԗHj&)II@R}"I@R3IHORHHj&)?(LR$E >I~f"T$')+LR#/-LRH9WiSf2{gɉ@R}"TFR@R3IHOR# $5$E >Ivf"T$')ԗHj&)Hj&)II@R}"I}Qf"T$')II`$ $5H $5$E >I@R3IHORH $5$E0I$E >IHd8w2-LN$E0I$E0I@R}"TFR@R3IHORH $5$E0I$E >IHOR#,LR$E0I$E >I>@R3IHORH $5$E >I>@R3Izf"T$g;sǧHj&'II@R}"I}rf"TFRT $')II`$I@R}"TFRP $')_+LR&')Lj>ـ$E0v i1ݖ/N0IHOR#Zp-I$E0oR[RJu?$E">I>@R3IER;OXQPRR$GԩFKI >अR')$UH 68/ž?uzDܗŪ7"KpxMJ*,Ł$u-$Ł$UH# 6619$UtH"TFRQ $')K $5$E >I>@R3IHORH $5HHj&)II@R}"I}hf"T$')_.LRHC $5$E >IRf"T$')_,̬w掿P $')II`$I@R}"T$')ԟ/LR#?W $')II`$AI@R}"TFR@R3IHOR#,LR$E0zIf"T$')Hj&)ԟ)LR$E0zI$E >IHOR#+wI]+9޷@R3IHORHOHj&)II@R}"II$E >Iާ@R3Is՜k-8%)"R}"II$E >IHOR#.LR#?Q $')II`$I$E >IHOR#*LRH= $5$E >Iޣ@R3IHORH $53 GRJN#w+LR$E0I@R}"TFR@R3Ihf"T$')ԻHj&)II@R}"IKf"TFR\ $')II`$NI@R}"TFRX FRP $')II@R}"I}f"T$g;sHj&'Hj&)II@R}"II$E >IHOR#?\ $')*LR$E0zI$E >IHOR#.LR#*LR$E0zI$E >IHOR[ARx+m;>`fb+MHj&a+I IjXꊭyRm_\W\Wߌld #(j&OM[I&) w Nj"Iv,|I/N+=P [|'>6>4cq j8ٶ@V h3[>/ȥ\pM[չd)hCdkQ-pX ^'5b7Ph˰5b'˴3J4R~S,قFbh W,g,<˲")BO/7"-oo,~S,Y]ఘsזP) 0/}/2/ZIENDB`splash/docs/figs/surfpartfinal.png000644 000766 000000 00000231414 13261626263 020237 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼cX+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATx콇׶-zI !@! ! ;l܋Ozeޛ${s9{_{o7\އ`mIe ϲ/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBa%$*] RC cJ/ĝs9]^G[3HJIHUt(u{ZKTOIJI<\k;~HBPg[@wI) .p;$$bsÂytW\sxDOO%K,z< nx^z}?u{O6$$_~a_}+~O>_d2HJI<.x{ɧeG}z媵_oܴi;vvڶq~'\c_HJI ~ģB[")`3 po=p؉Ϟrupp``xLC! 4dIȤ04tEG$FiсS\TϤs9]@RJAĂ_|yW}<{KGFG'&D=Q d2hR)37t0F.>~Owl:`J*z,_d7gΣ?~٠6%u4G35n]jOA-1szS-|{ي5o:0446N 4)B2pd0&;q_bJxjK( Sꨣ0Fphǒg{^J]-GjK<:xE~|݆;{ӛ@!hQBM (hVVDKOZqTO'?&+C'F.mYs{(%ᙗ/dϿݱ=W `H4bἉ2@EХf -'NR(:":uwϮC)࿺ ۬X0%%~Ͽo.v?rR(elFH^o0v#\bl5Ӈ@Yp3HI@! IVgLqf&~;W伔ogZ~nνOay]In! LhD\bd2ӠUa83)A( Lͤ@qDd/~UORJSo.͛7nq!y#rQ Ze&OZŤM|-?6q̪Ո;>ѧQj8t뷻xRƲO6طo}N^Jj$٨C%DV9wCAgBDcLF-vs$-cT_qK qd -0?Rڱ?yJJI<xŅK>Za'=zQ(DI;0Y@-K!"Aצ8f}QKڄ@Ft57uL%$x<{?^~΃G^70:gТcihQЏr&V4!( I045Jz0 ;ɢu kt?w+$ Oh_mܾЩs sψ؂+_OOk!UU 2>-R8( zUcHE%HiX4!r}?\J>E~=~žB1:ae9Q<9:(67ñGd;*#a+0M+4N b0 G%$P,x_h;=}ŋ $ocL)q(IUff6Q#+`Tq#78EM J FRFm>. )%q_K Y短f~*4CC#1z4(`dcYȔ"ʘLF#DI柁u܇!+++kYLiYo'P ٞx.]m;vwI0ܵRѳ-=I)!PL %%4EDaG4+1́~{|`)Œ tQDGb:j %t3/%)8WhM[v;}RooOOOrdApB:l j'CEQJ} Y MB9Mt!ӹӵ"q˂e/4_ӯs9]@RJ⹗Y[w~wȹ}CWQuH+NJ!nTE%( )VEP'BPצ79qP:IgVTk$$<[V})1>bGu:YX@qBg$Ω6bh?Y"N}'Y.OIG>wW+"z񬤔ă--;?d|tOn׀R|S-q1Z氫S(H+pg>/m #,ǎlgs9G]Dx ]jƭ: MĄ(1D\R:8)aSTˆEp>7A+!+)Z؏=ql"Z1L1fw&Nz?QR~n|~e_nwq#Mfڶ> Ҏ5*CaP:0TîB160d `q-U2K'D}% XQ0zj{=Ju2IJ=xk}W'7¦\ bICXlĴ$̷z;T894NCUǧ(T+2Eh`(vgƹq- y JnoMRJ//Yj#g v\c46-;g]\=n+V 0n(D \V JTTHL'q ԚѳhpE)#wu9,ctT2|/0Խ1%O ObS|vHԘ+lMރX(-o vڈR1A VdrKFv@}UR,mJGRf!^Xe6:wk';K/+/5_lھԅ!$ҋc в%`O :{hq/‚ <+Q BgWRY" I7'C(qs sBii.`Nj)N(R-wR _ZחZgl+cQ,``93B^؋ł+ N 5- g~VOyqkG4!VciA~ gZf3dN!tdS4ss{7/xGRswn[O2jIv$nvΝ03U:ϭn5~vǔe0?/-ͻ];8۝V )E :H&D܊75z^s!ʬ8=^/w[.^lwR̸SJͽT[}׻NL5c<+ȭPKg: I(d؈Yt#8YTl6|w( T{9\.$'A>eR&0)(vHjOJ*~RG"Qi\9yJJI},x흏>9wwhXbOF'֥`_*kg\Fpyv>fb͑MiN0")ܣiD}r֫s^'EQy`D-Ϣah;OгwB?h3\8yRix.g^]onlޫcf( (gh*ؠZ> fm|0t]eO%R.bAB>n+ {h)Gr2F Q\!C0wB=HqυPMWXyp.pkMpHUdŚI@P4NfE+ ٞYAq\ =e6J$u)r9(!%s<.'x|sC]H| 1[򈈔$jv?'ʊJtylamkNpeo..:Q[.,^LFdV (|gk#`Lkstm Ah",z}>oW~ѹ. )%qkڵӗ'GG&&y8={\(Zr<\;7+g6n96#4$1v:[b1(lfǩJEuR`8LsBQGjݶ~m'Rw v:v,w ^],EkgtC#hr GNTQv4qFyQ6ٙRN(}Y)c*yj*'Jy=6~ACrYO@j΋NF+x):=Y4|,]:LI(ّc\!`@cQ8rlvϋ#+p`XƆC_6cXUX8!Vwm{Mc\[]zŋjÖ]O\<8<22"VȬvkf:ɌnɁFnZ)e$@'@ǹCVv>UR@*v߶;#<^m`] :=[,}⁢ԭpϯ_^ٷێ:?4 )B: ؏z X2e(ơt TWumUw(@ܞ@PB v;k`hJCTzHlI J)q$L)6&`I ņY{S2:‚_]⫍`+ IDATS, 𔓘'9э+.!&9Q aE Ʃ#'DI^(rMr]\ppM0qVXs֦3ӠzBOz֣sg[v݉ك_ysRJ]@RJܲO޾ɋC#lZ̪>](uI) 7v>vPcjY,4ẗ́, & OmKtK`*DT # NOX gL~iΏmۇ(iЋX,I2u>ely:C,x~O80<&]XU7v7}%]pDσS[².KD_?5 ADL${0H٠?\|>unp̡QqB"Jq\5<^(,":с+'I);sV=GO;8I nImZXPUqn^*^"s "LOMLME`ZsN =vROȘf|l\,`g'qMki^qtS3iD ?RIb}tt86Ei  M|=")No&Q)m5f23T9)sINSjs)&H;]uw. )X Oj'5 Ce%Y y@xfEp4306MG0U42|($q ܂i?4F(i s73o>mĽs-dD)aġm>}vo?$cӯw;tqLER7*9\݇xjVN͈7ƃӑt,FIt4ˤstCEDqB" BX*Jel6ǢdމELRS~JN\laQiI_69;c+;A< ͑m-"qKTE&媅,3HJI̋Ǯ&VZ3$a zQ WP{>%mS@2ġ4(ϧId"H&"33h,6 )X >Cdc%(iz;{9n¯Ŧ|W0]EAnUoEHXHdj]4eBRJbpɲO=qnuya^+1)U0]?zfALx&KSĨt*Fd:)d<2Oe)DcCt0iEP^j:N(9=Rs>}\Lq2+Vt)9^ܿ}wК:'W|uv;s*VCO$ x2ý~|[!vX-11±ŧp8@:)˦qX%ST4OPH%d4%Q(5I$(2T)E|>:VEZ节jz*p*(iMzؼg=PUUmEG5 ?F)c=|;}:ǻㄧ.Z]1D)oguE , #"3h"B%133 Q<O2$+bU"d4q-Ie$}h<*-eVNw9Qoϖ~H,Hk[zے9(eNO qZMO۾fiw7ARJVxOmvރg{ƱjI3>6 K>յ",6p gC)]ĐG{aQŋl0!FĂRvo :>Y?xikzJ#]@RQ[z^ɀEMB3X<<6iJ抅DtK%)UJNT+FtV&n)ZeD~ׁ=9N6+<Э)XQd2kM]Xmqv rVc*s&$$:xj_|sً=~gOc^NAȉ֞E1V^P CީH"Pd.6El:.sq"d*U(R!.D*Ε (JP\1!VJ4ӡ4s St'|R+0UX"o^ ;vj>>}ZV|~A?P],ÛС?R4_閝GN^Bcwj:0bnێt SS31Rdq EHr\T)'(`ųR6/Rq'r|>( \DE\l>!3HFcp& E_,i&vppMMT͹ZL:5Rl=Mf=~mӌ_>qOyshw>vߑ "aJgnUfufϾrA `lp&)T4F!RJ^/I UOx,Y,E(e6OZ(+R*yTDL: aE&)xP=偘H佇WwPx%YH͆I>T=65m_2'J. )g^}s_Rm!:!%Z0=cM-6/@\Q4ER6eE)'({ˠQ^ JEoB\)RXB6W(2bV%Sb&O&RYF#1 &bZʁGJ^^D9vxfLE,X$(b7Եz=8+Dt[я]>ypÚ6n.yeٸ^PM6%|5rև ԉOT8]p").XhE1P*4Ibr BT*eJRRL\.R Q*FţT҅*6>K #Q*-82N[),`-f <]/F/=s퇿'СԡԣE+fۮg/^wpc{-Jv4ecm*\$|d:%(ϣ0 jL LlZB4RC+JxU@'BU3v۬&PݛV-1&J. )W=2Nh p2 \P*ңY lJ,эwT2Bk.AѲ(EYM ik/Ul|+Vt(6Z7`F2TA{_,e6$k,|wv;~bMb pQ7R< ~],nBRDp:̤\.DP?|2daz%]Vl(e %u:R9C^X(J\kbZJ%"g2}%)^"N ^' &&*`6 G1GT0M㠰yx,춛:ݖUiRPzܒKCcYL5h4&5I x8El⮄ݞp%ӆq^V*8U"~kTL)q77n_08Ux߅(FaMPo#[$6㗺 T@9a,v}:{#UT$P)In'Q)+dOsr6+bV+JXZGjb:IT"lR'S?%wRTp41eѷ#Y TPR+QTKrʔ* T{Q*:~)\HRh]6P Sܓ|{t3M\.v l$%R»vO|is+Gv_v(]@R__-{;4ʎrؤ5wGƔyXT`=r 馒R9HpD!&C(7IPD!&[ |V%4xW(m9t H("yg髧D.椢bc _:kuMBjx=f`Y^_/ŊF`\(ۿwzpѲ6to?|yʌ.'F/hbÚ)L$2Ţ3̣x,G& GX2MݑQ#FGlVժfHLkbQ$N.R|5~iMŢT@mrhnY#m mM)JY! >`8{ybozzuC zlhlYŢdMS- ,fSRhL&9&3\C!'d D6N\V)UZT":ΖlE _XJ8J\S+쳘V=tKq@?vUx6zp4ƉދjᯭnRO6wLo0/fK$~:2U" <iml ǹD!"QGO@|Gj> LR,d T/:FT-Z2ŬjRlR XeщG#NS L]Cl.TjGa{!H1{6S Ӆ]SaΜN[H"}X LQm5?ݶO :. )㉗7{={ip^j^ W!@آ;5p50 _*K),.S4LP@0_a֒JW*% fR*+JU55|.)TQFdySh,)$}N)qȆS'JQ76paWߩU!)9ywhdbrR, JYT8 &ȍ +]" pG4aD83hR~fyVP/AwErQ&]5V!(o1TTvU/j(JĵjJ)SpLOy,fN޽g1s&w gx%:]tQ( o2jzu+~uOC$fx/_llpjc0cjf:/VT*]mITyp*E?q=?~.ΒR G]EoFbZIȳ  X2CP p4`>#7xبJz=WRT(1dxOsQчRe FRk(XrJ@T,V+Z♪x<[>K*e1AO7bndEс#)'b;=pm ͈LF%;gN^ϛp*񱡫'|v9t(. )7ذ\0Gi&tb H1A{]O( PH$%tUf+FΕ?*2MwD/RW,S̮j 12jUg)S*"N3Kd\oUr.KU&MۨaJqUH TPRC'Pb?#P ha🥲X0=3|vvdЎOlm!)w>Z/j8U)̶|*!:6IK4沐OЕ(+J0EJ*BP7|*]oRfVY°!Hz1)k2J&b'Uqt2S35ՊT,6[nҼ> ?l񊊟bV(%Ô!;xS$>G-s'}kxJ HJ=xz |wR?E(-P E)-FUT D_0 G'1[uG͗CC_Zz/NxLg#C 72 (/ MMX* JE#TFr&_Tg!]%f]VW qR5fMzh52">S4+R7;[+UM*1d oVTg(|qL,rU1 d@~;5(Ŧ*OBAvF._:wd7|%Y,xwٗ[rh||bbR˽0N,UW=UJV`;Gv+TP՚:*)($xZH԰(6:c+GU RTTn*z@!OYbJwӽTTɤy68=6,~QEQ52spl'6)越}=P>8x6rL$]IMR]j']F\Z]w(^S$hTuHPWL2B}NU)N0@SNCA/( aifcid[YQ7ɔ_hV`8bO^>{k6ERt}`g[Ռa(::acgAPzٌ *9 H<X$M0Y $82RY $ШP*B[.Wj5P|+ךj"ֵ<ƵFNhժTH˹,Tl\y RH BDEa(az=p>ga\l3+C9xq60nlkV,zheoBRSVR Yi߉KG&ظf5:X\'vbn`xf&R$(5p \ )P A($z\6ۤxC+KzNԩoJjn\ojGJ6/QJjERPz!OŴW\>&Q$ŲQܶ糈!dZCL=Yb.z~rȮ53wPFz8䋣g ku㸸`(YُO]lʜN @$E9E Z&3lgsRl r"f 9"~Rӣآu)`әRB/W7? WJjRfg1p?GÏpJKPJS\Đ?"QɭN"=~bٝ_/zη[ϋwI/B40:'ܗ MNj޴R>p"`=JЙ ׳R`[:S-*D(gelS,W JQR%RڍkzIV+⬘rE9b /HY-?%hMPJۡ6b=zXDA!usG7}嗔z+oXPfbtdtlv&C;AYenGBqéLd"_J# E+sD.ON> =r*.F (q,JER\xRQ٢lT&TQ:4JsL;&q2\)TܡÅQxxɺ]hb˃>WX9dA A%ŷ 5/  v# ]8g%Q?QZz~oF&ڔsO;eA*"Hrpt:A*X.OL:o>d2_(󣚩jK " LP*׮a?(xR \Zizj4RA] [7VZ+fl\< 儵jKbtFR!#>'6cP$o1r%5(<+sQ7x±E͐z+}{SWG4J9v6U9TBk6vtTbJRDjlR~Һz^o6FT.ʍֵJ+v-B.uN7"\0b"9T W)A)$FÃo~:V~ʹ㻷_zt[>tށ 4$ VH;v&v g7~p=00ΣD:%< {\2AUNcF&EB:PtE\/UDQlB/UjJ:3;jPU*3qEP>"lbnpblbPK@^jгne"TN[c{7\Yxs;t(5$d<[syp8Fn2ZDܬcxs/n菂0dWx 38Y`-U9܋*Eyl;f%t"?umJրQi_kziϝկbƍbH8U'}Ddqbݐ"r` R88\V'i&ۇ|h*<9x6{{P$\h(pETA^PAMu9x+W4*K¹L;LR)D?TkSM'~f=z4»nsȸ.4 g1sέ`JMOPD:DKgb[K,#1+()L kPg3J5[ʨ5ۄoƏpTkͶ}VWo^}]毊"Z .0JߜqC^+k:'6"dY¨Y ATsVv?~St(R*z#g'5Z@e΋51@Ch !3bh<-WrJ2R!GLm(kT1lẔN:,b Tƍ̙ Z}FzYZ**M*Qֵ[Vu]fѪg=yb݅7Nxyoo#ٱQΊ?IM&L8"|g`׮Xt=lY؋R=^|oޱKCPMRM]B8@4 ;77 XlRal݈FbQe}XaZԬQVyR*7Pchh6lv@`ۼ>K"JՈ_8byjiYϪSLUsqpyCd`gu F Lx-*Vǣ\4 =P0tg{ZB}mfŻ5t(R^{o8wq`xphBG !զ (7q'?ДDfhUH, b2I'Z^&\>W.`RT*ׯ5,/SB[0^BvQRyhԺBeUj^k4Z(jjX@V(+Y ^tvBCI |RXއ&GPь)}o uѫGv}o:wI.Z.LjGG&&a5oYEU\KXF /$s+H"CaX!Jp*EECj)+uaUQD~?hGj_׫b 勭FcvvכMz|=nP1š]t"Nl@UĈ?Lƌ4ɋI’h Fﱫf=~ݝsHJ=xo۵X8z6 aͦXy~[{|A삟3J) Cݎ\L,](+U|z--UgrY)r KJwr^(+qr[~GllZ%K%)TZ*j12rcBib j"Ql1h}8q(EiS14cÃNm\+DRłg.Zeً}hJS55PXXJjz.@nQIpP$Il(rbJq%jh znn|# xx~KP"kS'aƌ747nЉϣG{{Νڻ_p>t(URx|ufB(p4Y'O凅E ?g>DlBS.]h(NJi Y%HܸѪ袯 zGFTw+TEͶD0ajxՀD?^oMz̵f tb$U*U 2aznf}NrmVу@gܪ*&ރ*Fvl1klORUFM'۰ވnÊח|~W΂*߹ɬ%QO D 8F#H?qKxGtAJD%j֌|DfՇ՝݀& "Sb4;fF4Q] qvgdx7 y<[I,Wn(Gb(7N^AJc8Q9ԤU AF`\ٰeP5CZ|IH5:[5KּoŨQMaKIPuݻ}׿M/OۈH] qo>(ݜW ըfRUS[SY-=nq@Ҍ4FS#zh4@Q4ؿ270DqXI)hP2gYYdE<͉<*OC](2 Co% 첣ʼnoR9~8#(PH6cGvY AUK:p^F} uH+ <#o}b-v !c?➽o]Qm7fڀEO]Mm@Z_ksK[gWg'Yвf('@Y)<@ \.uvN8^q8 #M#Q hp<˩> csdOcycm-NM/X EjIq )/!ȕEꊃsriʾH}⪯/{9WYU] UUC7/dj- IDAT$ $K5utwu`vdv<B"5|'Yvx[%%$~qyL4+ #)P6HС H,ғW_Zx!B&44%2l! nm7T] QMښL~Kc_{=}8pͣ]Y]A^+I/h/ TBՈ}6: HfP;n!C6QƵu ]4"q7 Qy-eK ˢ2ʳ\,GKG.^WpT{TEƔҐ(k(]P\^)jO'̢1W%'NTUՖdžr\8|W0;j?U||7Uqī6@:\eMͭ'Nt`4˃ghGoXmv7vEorOp}pb(I*G.7B9x]E k,$,@ft:YɫۿE /H.jR1߽?KCYEEE8`QG!h0`'up<ЏұkTexU6#r@EY9:Iɪ$p\P%$~DTñ8U;hXD(Ym6| x[OlC uīn!]F-ʭ/"2{IH |L4P&j`G^ eF@2ǯx^}ؑ2 k=Mxu"dJHЭmV@ i"9\G)8 D^y%:D9P#8XEJ=UXio.@rDqQ`jkq >P˄5[C {X%B nQI®=|ÄvX.^u(Ib#\Ndvw 4P~Xn7dX(up>hc vHAZbYÎ-txҎDK@I)2@E҅$p.sOPWK ]ͨ }Tm-Dvpk?1r%梦m-m'N6ĊCwk.mHY;Lq ?7OCon%e- aaD.*B+żz#AXw`p``洣^,A P [*YU'H~թpE%#Ot)9r'h72p3%QR j1EcDx.fjs6^r-3/ ?Cͭm-MnHxTD@ jw"\w˟}K>G@ꊏ{~=w9~b eF@Rǧѝ=˯!颎#}wWP߂HwEȅC=GvHB(D~$Y(L>.Fsؑ#rS,CF|n^AfR@ċ 뢜 Ԕǰq+JW PAVnCEʷu]X!Po3g?W.O F@R>O{= =w`kYa͆h^3X4cS# H<+JlVtu`$@=G9.@Š ;Ys)/v u%GnǿyWYkl<5O Y(6WdveI ,PMJ7yIbU`OV!pvSYd8YSQE\C@q͗ќ*<9B"{4EQ.ċsς;7 Vh2$ʋL #2E* ȣ  x%UVWSUo(4E=)BDMT U$PAM9mыEA2[P6;8\uw nq *4-UfwYN4A]#4!f:"*]T>:ӭ#u e{w=z$.6]|ٽǪZOJ"aH_"z8jx=2J9HI2a9ţUƢS@$JP|Ť&H(%@*yj*`$_rr?oQYR$@*1'Z KFNu ֎xN+u8s <^x"v65R Dtq>a ( "}bKryh764@s͗Hz E]WƣTD"x,7}>IYHhE; PRɅr8Xq'G;ۛpBDLFNQJ\\]\>Dd!z.Я8I cv16vt6T;qѱ؁>C[;ɬ,٭=D$4eOB. $"~ >h4_$Z.G]4fhR!gK#lR0\(x8)\:K T&GSD Ñ@8KH~)}p  y@L ^Uiڅfd y%;P7QJFt`ܲD2$ ljM@Rnjl|sߞ]/>?˳WN7ڛ|5CӐulF@v:PNtR%IbJeq!VTȪUE Ƣ0\>*t*>%9dn8FCD:3<)FJ<::1: gґB!Kb|1-d&LgSd('"X \ HEP4W C `SIEXF&)2. z@QVmDq2ЯOHB]J>M&DPbVv,Ah"j1س빧soʥ֓}؁%O|So"f́Ȕ 71fEQQDWWh T[Ox:td*BB.76K> B~|dx8epXJGrc\an~)b(et]<@1Oc 呇MQNY!O`%D##6Kw+[(0Vy'!PB:EEB2Ry%LΛDLyt &ۈ(n9Ƶ7ġB:.2P=&NxY7QR^|@ a/5 IDATv {|qLMwH"a#tvd$ɗ&KX1-NMf&H>?=:\g&2l$Rt.p$h1$D,|$Ȋ7 Be^#P"f5w@aDĄZ-hAunʏols{-ݭF"5g ڗ#.@}<_7*:zCv'ubw:n M? I4Π}ΰp(L8 kIx_LJsD.?: jbjffiajfqjv~T^=yryj~afb46=312 @J g3`D6 e8/B~HL\诈!bX;1+/nK94vlUn@{9T;v┏ f¿?v'[˾qRmۈHkn󱪦4ܠH{ MH|Z4-/EBD,ʧP L2U.J|4 F'旗'N..,\X^]?23uryqXijzeabjbTȍO'L* GR.NdX*30@IU~k  @YYfbh@wIWZk Dt.E΢$Dx9eSSSK8xgo~rJL؁%}guv?9_ϝ4'Hp$ A/%^ zU}>䓉lK)scSSc#åřѹ哧4N_xk'WN-,;zvqznf~qyyqbn~~zaj~j T6LO.dSIHQy9%bD6 ?A.Jb6Y(nrٹ险٩řSV6z{}s&[@ķ{o<#b֎Fnr'yIAjO Bˢ ( &3t~x$AMd"_LY3뫧̝ @IxhF#zccVL=(…a>4G߀*Kwa6k!d&cmן}[o ?T6bRP=W7*C8n^4MSX7i,P ݿ *JG"(ip*/GK芦ffNyksye~aceywoS~{镕3>͵3g6&&ff6WfF La|bX,7&H4 R-BOӶ#zuxtAkxZT8VvamlR3WAHsmm&Y$g?^65ٿ;z֡A ɂ4s-ȅCr),Q栳bE Q!=>^ D3t8SP8Ca6%SʼnDntr~S5@͙{[ow]=3;ڜ]9;o^]?87381>^Y-fL&7<8]X]_9ΙoonљѱRi.UXXX[^;>? hVΟ^],@ƇGVONJh1+3T@"*?"n\Ow K(iڂ@ zBHm!j+M_ւq]=˫&bR3nաHM]'NU?YxSfD= BOx.Lbxadb_,dfWY|g}̻~[g3'FAkO}.xFmk_{YiUΜzsS+ c kS# ##H46VH&R?+L!$ ф$HÓ9@[S}I! @>6lu^P M{Y FR-Mʲ_~G+O^ ո؁>|m[łb'J߉sډvqGaubi֓_(A_  ҩT*LGFFRaj|jjP(L.M, 'NX]<6Ϟ>{zsbj|K46zvmem?KűO,L F&Kdnd8ʤ!OcBH"BG*Uko e$NQ"QS(k5S )D9BBkmkkCs z]s)6bRW?;h@HPʼn4Z@Sv]r EX|gq3B5zh( D4r<F'GNjùs3sV7Ϝ}?G[Jk7N?{ɷO-@! jt|jG\4J%B6H$Ec(C~RsbqnNF"٧S'jt&D\VQ1]e^:qUo?/^mH]ׯ=j q{p![ g^$*ʴHqzI!K±86*jX(ʎt">ãcãsk3kkVW6Z]<1syT7ȣ6ο?l“$NMONΎeRp(fd:_*34z1H=vOUO4a"fK5465,W]t-"_Eѐ-wĮTY/?=w7^KcR+'WY6aGL9Uu34uxU[7j=B&b}"!A. '3H1ȏ󣣹dirq~o?qnc-u9Xmraά-mNOώOM.N Ws\6Ɔ`Q}$AYGa0` Hm:`('$’ #r$UA03utt*: =xǏo?vW}O eF@j{^i4v((n)ʆi4BN 줩Z8* +9!d"]%ȣ.eL07;pjS +Ξ?sݳ~un }fMċ +gϼϽunڻoMM,-O&fgf&\:Y,$T2N1Gߣʲ~{@&rm% ([$!Q$d"C#HL#uzTb,?r>~kA߅؁ʼnknyw {v<|fq21D&O<ǫD@ T(@0G#-U\D*JDl!+槦+ OάO/.͝<}ƙ3Kg?&\!쥔Љ꩓?橅'l.-C˗fGG'&bzx$(|(PEP cd*.\*ff"TjS3奩婹͓+N,\Hn#.@~m=f nw8n*b 2eYaeE!Gރ{P@KHѐ7"#=M&P-,) /CS(LN/˛s sS Kg֗ {^;tvqiIԻgo^=}DfյSScss#|)˗FPD)369RH' Bd<1{ DI얁fcm.jMAVu5oHy` S9Q_ّ7*_j474*ӿy?mHa/.Z5桡!'(f'BcynRHE P'h^ynGw"0.(} y[-T8E@Qcdfzvb)2%H1əɹɓKKΜ^Xxwmt|n~f~ٹũ٩bgh6O$ҹL$ˤRрLJ{ZKA4UȌ@V3^#v7xK䱾;~ÇL龻[QXW#뼽 0egj>~}]QJ} U؁{V7[CN78Jf$q"gWBPiVr${= kH<DbAzES8,N&L>l0+NϤɱٹYx/M-¯ŵcSKK'痗&J#TirMC2冇q$ˤX: @:A#yPC`iDCmm!&:ԓ-}h O[H+^CCKkۉqk2Tyݏn?IR;qMmflu2)]NsrЋsy@OPuHߍ*+UPvr"B8|_<[Ȧ\8:^2xffj025Z(MOMNN'[ {|vu|zt|rbe~4:78W= y-?:3YzR.ɏ3b*FBP'B~FUEJ9]uk u &Qb1$4G`:rd*A:^/ގ؍uUGSH;o7w3vxYdS%witQ/Ͱ,˫&sv;tNVx\H4KU i^#h i(XtPEbDZ|!ͣe:OLJ\> +@M5$4cD(d hԧb$X,-N8 |*続;ZL(Sruy1Γ&ӾzbY=ed:MRxELm UP Šx"tAG9^hXPbP<,ê &>AO0ͤQcL6LCcIRT*eGƋx"DcX./'`"N$ 8P}h4 *(ʪUԄĕhljFsxb[^YMjvn07}w,Fl5V| xxsKK >^_wӏ>p}'>!m>sbb%.7| IDATm9N )9H61Eђ2NpTq'd^UQ y ΚOQd@Mp"C H,IrbB!芠jE J<-Lp BqDG%0)pXL-͐L[pyɈ \ByUeeumc#@ 9pʧBj3^UMmc d8 y}n[_QmV\}QypmN<{htiP9wZmĬ ~NZRe /ː8P887#{" GRD`DS0g(ZA,[YjpدT-U_"B>IFH0 ,IŃb)Cˠח(/ 4 5 2QdCVӂ-T92}f=Eu)U:PWݭexI 恮J;~twn7|G?\ =.@jO|| i* #PpX0Lg2X4~xNg8/$+ ()ͲьZ75[%JI*DEZMU DZ8(֛:{;[LUǏ~ox᛿vW_ُ}f}z@k|J>ˠv;]V fi QuZ#ܕgFxehYS6r,M2C1WpyIq.hԣ}*A(^ME٣,Ca4Rd<>MRA(* r76xrzd< -Cqn2 z(S66Ad#B踦LP 8*/!՛Tk0F|p^~?tS? F@}~u27b1.Վ>ޅ$YPW!OUYj>? ) Tns9"jnQEJd }tx峢GR>Y}WV<Q4G1T8^ (KZ( (QdRWESCrM"kDb΀UP\P<'}+&4Q3 :FCeّ~'{W ;▇^kه̖!+C7@xм}3!ʼn2dEBuYmwh2dwq(*~? goWo R?PQnPwyV]\8YU4|yX ģ`:z-Ӆ<2wCDt#Tv7 CaU Tt+wЂIwz(CacXIbIQƊ"2SXKqPBgD&HW5#Qd:Q/*CRUV.U}Km0=U١}{_|꾻o?mZ|oDukb3ڠ}ShApЗ@`ѳC t?ad -v\dH1z\_10a1 .M4PjGq \# Аp%XP1hxVPJyxN<3W`1qXNatBwN7Xo뢹!ѕ  yD>2%7nyUM 7{el5^y~ 4cRD|WlAန$C ^Ynl|j+@҅C0ajCwDV@K?+J'I( ]a"ısQr tQ"(]nYS0Kn()pT -qbl6lNFA[(%+.j{U6zp^^QTDJQT E@^!=3{c1{M@|u~yl+."^BIqϧw!+ Pr+:YXnGN"U,b> ,yw{`xw7G&rUw(F,\RO1uvtttڡ[6<K9Hp69r#VUUA`|IUCϮ]eo&;@No-Pi`̀+jZ0mTq+\o}TnX PVitPU [$UPOjR*Ua3 /IprÃ{n\'[7 ls2XCH7ݺsSHPjQbDRһj-<F$|^eDB&C_. M;6K^=[n_ezu+΅NVJe/e3ʊZ)MH}a@ic;m*iRtm\aCA/6!Ɣe~p܉Gڰ^GRsK8CHyv"-`35(p A Hi}ׇٺW9/ov '欞T$рA; 4K{[o4z]i- NEMsQYuTve^B/֨(5*`In@\_CT}Hj6ZoJaR!W(\Vj2ad2T~L|`0\T=.s }̐bmnY!bBYo6noHNԟ9}[7w3Gmޱk7L띁t^gSBjjMw-yD-e<'wDBYQՏ @zϷYM|m7ʵF EcU-Ax :Nl턡\6 B\#8Rx< {6Ao}w8 ^6;)Rh i/A¤En,JT4R~`c:=܉/6oڼw,]8HBΟ_ܸ&fff p\&`4xLSYY,BEX1<|o|@EߢU`oNߧ =\u}8ߖ E*^^QUNN.7z*U =5rje #HQʗȦL2ƣc=‚TPj(}]npix,_m4Mv@upSHy<.n^~r g<]gܸ~{tffLgKzpBW.<unw4Luy?<9:`Q4k4[QlP JIޣ:e&kkµQTUTRQש(ႡAՊ,˒, \&B6Jbѐ_˭m*1IljYi7!r@h9cvvΈlmYl6M޽{sG7]N @jf gybզݹ72:1{JRYmPl۩:=j5zj{ᅢd^o ?Rj4>25P @N\jT#1D*dT UV@թ St:n/Lģ@k0jPUew`9ds)\:19[Ɋ`x3~&0,YXMi5&ݽySGg5RyNu:{@AنƗ5EըoTjԚz3dTMYF!ߴz ij?ͷ)^!wN(NZMCjl%jj )p4My&KT ٪^BJA * B1>XSe>N6; Rk4h^`Nhè?rKgOkzRK8CH7߶٫chnz?Qi*QK%zx%zԯU 6 ؛w}UjZkZj]jRB{MPi^c=:0LUw*hGj}eH"˄2'V p$KvB@Tʦtd,3l~|a3K `G55;p܉z4/9Sׯ^>}6?rZBS7o?t[ӳsҴD32X ֱfkTY鷫50۠A6Խ_ &tuCZ"·g ~zI}Bb,&TV^ݢZ@D/$@4 b>"Jbqȱ7𸙵  %?o-N67ӌ G>pΛ FElV#gZͼ~7( ,iuWv|uk_}1J6)g%!M /aH酰p ݑqj@3TorГ/HPJsʲ " oA~Q-07: acu)A ~H pF7E=/TY. aʃ~J&ӹ C*Y*LhpY`~y 6^-ׅI dW3K2* xAI?{ٓ~3oszb!Ϭz˾#g/ݹ76> c98=]V:Hr\*kf_SFV._!F?\Əm0@UVqk>6<J?m!xPvԺ]\©DPm*CQŵnEKl fEhxt&B%!Q>x|>LQnG*O&)|Gt{C絃@a1ܻ~ȗ_Y?tZy5>~SS3"#LXX.r𐚱2j۠Bz06b5r $o ^ͷm<5U(3h܂=D/6+0 cf-KXW Ud%US.8L:*DU u S]~1j`Ó [`mfǢy\(L[l˺מ3Ίk><|qP:=cb"9FO$/R0hw ]) 2L[}7S%"(TʜQEu7)ҝ !>.*ZuaO,7)U|.ϗ1q U@J*f3`[ۥ0AփJp灶p\Q( :BC \N/bϤtopZyO_aנ&S0H yKRYm zY_Ο&Vavњ58D_~=αV8+IPQlJiC/C*j<ȳr4}& < ۟֖( Lߵkq|YOzi)kH0;9>vzny_@Ίӫ֭IlX,<=l*]UJު-(om( =./< H|Tn1to#M+_Z S׮)"1vYY.!>R*7L(t"Ase}4;#zTy+^6&yNA-Tܽr|vkxOwG}YÛ8f~Y (BžSob:8;MHttvE7=v_WȢop!>lFg S35GOVwH|&Q@ɫL'p*nĦŐE폰V4˗ncMta+&/e<:4Ftnx*QK(pQlR|.FqơX]pÇ!ʈ{(L%, yB7ya1>~7w?H,8Hٷ>sI`7 b!'piZR^֜pvHaC8N*F\ lWv w"-a%Ԣs2h\8Ewٯ;|BGӯ١WoNLOԜ`1S|yV%H͚T,sn5dv~GUdJQ7?uF/}RXH^E}MNu*R4U$RUDU }_M&ɢ:> }R~LV&Rƻ=4~&,aӧy^fC| C*Nc3Lܼr/>۲n4g)s};xmdt|>DS203ǓR(U*Ο*HN Bڨӡd48UE6OskX _OljUvۀ<$SkYQf7'jKEc:%ұh4x,,,81PjLV—b1IFKRTƓi{pSmtϭF?H]YYz8wQXt4"{l:0 P&@hj]?dZc&^>f}^GZTTZ2;ht%ORIB)~4h2JĨ8XODB䁜""ڢAf*>n ex#.50 i3޿}7tזRRy/tYhYXm°Uː(UP)X =\tTuY~o(Tv$ z§|+\(C Iy\yEAZCKJXӉ ըp8Ce-eip;s;RV8MP2tH 0 $r K!* rtSwj[?|×3ԝ%'7|vw&4v2m pJL3`BSC'V!PuNkՙbR"H iEr" PCKY43I`Ka&y;'cTA°mF*C> Rvs^@t:m IAkHtB؃/ڳ}zaK3%4',JjV!+:@N(b/IJHp,'N\$ >υ]&"TGVc! EK:xáGꍺ?ܱ>ݸ~[WRyoiAs: );\&[ ,j*TvZ]8H +S'EVꝅ^K-ך=a j 3>n۪VVW|ΐKhe9eFѯ)ٍy^-^ Tnr{@uE*D--(|ƿ@P"e3=z;:@ n;vbf[/dz+ &YzJ$e*%l^jPLB\ZRj$:~WPw;.djPA㧪Շ9h ,YlAW:|IEoK iBo @'BTf=nDornF.<."x2y) X,C84Nݿw]Szw:HXY>qµS:!/:^-6YlD:04͚IfzrF-*:0\}OPA$@aNVo,@#$Yqх=|HB!85P*U+4tStsl6MT,Q %_$F̂snb@rs`qyA:ew9D2A/&\&&ȭmߺݵϯ5ߟ_\ƦfhNe^!21A#ʦrEܽ*r²S*&A(ʧ Kv el%j^PŢ`W^ZM\V`UF !|96E^ vl"^ʹd BUX0,0t{q;E-c9:@F8-T1Cō _j[H,I'+!23~/wqݛ3%z/N\169\5/01E2d4CP|njbWYXLsjr-~$#}ݾ99x9t68w~Mּ³+|1!R}ק/޾`jF/,H͉N*RV%anuxBRjCwOl87Q#UEavqVa%YExF XFw`7n΃۩{h<͡~l'Z}pe"בD'$놋6,P9gg@ ;P3,/ }Qi1˜{zo um gC-{OrP ) Uale{-zȼьTr6WDdZ`ZR¦] 8 35+yʕnWXSlT+\҄FCGVyA,ec'3iQ$HRRKIyaH뵚 3Sc9~x'k-RWp7Vk7{s/[sz%mV@n;*R>- N=21;1qQtlB]X_D/Qot@TU;RC+TH Z*+5OpH,ËB6+awƕ3';+*g;??zB 25:nV Z!䴻É,czQx쵫JYE'tRUkTJn_@ܔ50v2DBG+M1 \P9xທ)~~_18CHi6o{ҝ{zӢ+Tg+K8RO_5:Ίx``A a @ r2i!OJ*vBhױU@TsW+V+\C"B5Uq L@Vא@9Os%%$A`ҸE&C3c# E^hp UPwJ^75qƕ 'ٿwW,B ܼ37R7%hyE&=)Kf2bQ*sbrBRG\| HZT[*  6vf=F"U% _L~DT6T>*LAW}(0@ Βd]cE qN Q[I?;>:rKgO>}˛ uy gBj՛}+ 'ش\\%-<6K($ryMfXvcP!K9ruwneYWeE Jބ^UbJkBBTLSee(I"(e2; B vb 9wڜ}tfФ07;~ڥsΞ>r`ϧ[֭^)? ui gBu~ܛc&mڭVr<淹q7c5MC^ق@%EKLj0 qbFTU0 ֘9A:>R_!;aNx2GRbf9(NK{e HYYCi6,s)Ce/M?v[7jg?n^Ջ`vYZY9홞P7=hn-ORQj\5\*PFVf0ȕ~Qz&1&!T.22]2E- $XMgKE$d-w06^{|>{-TzpDnP+y8 n\9qdϮ>~wͪ|:H]\Yzv͇_?ͥ[zj7q6 yCn=KS'F& vz%hqiDj|JeVE*Q(7!wJ>/17zA9NX02V6%."dz9tyAεa^\xdf;*^}Pm`X=f݃;W/=y!~ !۾>q삤1yX׳w ua>CB)8ɪɸ/N)F76`.V(RʵN@h4C0dC!؅YDD Of8D\ŦFt*A Tg&ܸrmケ8~Y[vxia013=95^|!euR6X v%Õ7UT/UU)I5X\& R*\ZjT\b9oXgsL\cN$W(,w SL,goK?攑 3f7# څ,T>7f'͙Fȹs(6:kM$`NB~',êMܿ~ܱ۷yyR8~,S?>oݿ?6>5H͠J'Kx Vm9±ni6yd"UY IDAT>DY%iB>5: YH.cSTQTʹlUUˠar2 (qZ!1*dra\23!ωQa:g %=$;_LC6ŅEsƏAaldF~?=l&KO*@βԿ?v΃'>39=;DY(Kf:Zz.NUh+4,)a[T)I F^ATy,W @(d"BJ5`AFsZE_DKg JU& m<^ .%V6bw>^q>kq 5'8ͱ`d$w9ɱo\<՞?x_?+RϮpW'.3I7*ve_N7<,,'sbʐJeiɦƐ+(*fSJR!cTR"AnVUJ% %\mÖ?*R0^؂5H0MRN0%vbt.- O ƔI.5lr.dQvQ,;h$[MzW/?㓏֭Ya l? un gA꩷?sҍ<pq7I-eNx KQd+U9-қYՊȢF f:zU)X`T%xR&S(1~ټ|TTDdRD$@g D%'{!.4O{,dي,5+13%p4Sf: Lܺv?^^!>;|"m[  ɐf DBwh9|D&[$0dƆ[U`"*W )`DTqh:)Z&_a6 >{ڻb&j+EJ\0RDM̤QH$W)N&g;ͣ.HIRd+*i8k*K؟?`LOh)g3K8 R|pl 2(ۄ~X N FR )E\S)AsXXR9LM]ރ5:^!a `K2 _fXFV+0(T2µ%c仐sAeHic4$+%.L!BDEn5D쮃ٙ{w^<~;6Rs~Yomؙ#Szc8Opyq^JœO};Շp6WmOp%)1L qDfA. eu gj'ᄍaӷԳ"![' LMzݜ8 /4p@.`* G±tJ|]aW*+2MR֔$ ~{/f @9HBTM^19_9Qi( ,M a3 ;("^XbV-,E 0GBm؏vm{s'GoB3ԩ%e'po.]y0953CC lƱ3!di'8p^ ñ Rb9gB)JL!b<ҪrDK)A:_. ,!o#/5Kx怣0^6RBt)y4| H(k/;z`׭yO9H\YZx G/&q{Q#f)fh K(QiqS㣗l|yHgK8R;Ƨ,/"A"5ôA3+E#,/OEjWP),WKrQQʄ rpaI%-*2{K%2 ^G$KG84(Ђ_Jp"!х,}gjlrAs$Q\y͎\>roYj(2Ί;;F'f lβf<,3,@DnDp<d Obχm{I$ T㺊,*Ue*Po2L#B`vētϮtc{Kl19]v0>JG ) ӏ̂^:j`Pۭ+'*8 U;wȃq9Bg7+Ho!+a@,!+Db)yYj2\Aq$(oRĂw~07d nES4 O GPFC!1.`f9\\kBU,Qj5~|FUW]Xڬ9mUCk u| kwrA?3=5d~N(om=|cwnzQ*~&h2˔$漂ϤوSk"Xu9|)i@ Dbl!IFGFk,6X|~ɹ6r4HAO#ި%W-O#Mɻ.?vhy}gcK8=~}o}@5jrRg0P4Uz`aL#&*KXqQJ3*PȦR\If:,FFxV T0ue'ûL/?w3# zB%ÙV5HY}aQa+\|fz}n\;cYg%AOϼqQ3TtIM:!2/i+ ߢC_ R9I%sE. !qІ*)Y2i(6$ J`"G_:Du Ar4\+3d6wnyGfvMA1?:ʠ<80s֕o|G<3Ygop~Rxe!g^ycӎiojfvz9AE31tpp%xyn)!)#]H*ȕj1 9K |, ,f|+Zq(Kt"M$(T+Dw(HnXAZ֤@".Ԅ1-VVb OqШD qv3wnٸ_FKAj҈ ?ӓHƪzj&qDfg4Fh$%pr1%RRYEAr1 RVݩkLBTq z,HsX(PxH.VFv7s>p3_ ^/3LӡsZ #W}g\ëyX8+t_w<Os3&0o酡 :ᄍP̕uWu{#8 ^ʐwr؄%ed(!+t*Ñ%KA*i(\.N'Ooh>y{/ in{5:ʗld\۳ibc]_ :"0b=aj|[Бggl}<[!MN2 Ϝ[$T.zDهwk4FD"e,J=4&bTWKt2+9@|U I"xK,S6Xq8[(ˠLI(!t^MY-rmS.u)=,PB`0H5q x&~ލgdꗆ̿%_c} =_z{3WF'u3xLcE#9̊#VKG^OOO$[cz0lKxyT,+IYbt*ѼP墺$9 Ulǣa7p| t elyz ]H w:l>c 6VKXEJP$*ldcu?h݋ý/rhXusdb>}s¿NMgVq<u3|'G !B@VT2K@2{ 1T)%Ru7,P!J9[b}9=q ; Ym ΐt'tӤٗ*$Q|kE^/Ld̈́HBHe;/W_l{p- ,-djʎ[>ʖ'/N"?iN 9;7M0s+K/6Vbgw{ B~PR.{6c,j ]`M |`:?j8Zϡp6rp~`=XBybw|e# %LF`2/dPx,赋ŮO7pπ;zeݦǮC~ BAk0kL}\bn b܏j@`nB@HrT"<7b !a 5XaH`1O5 xIC#\.DE p~D0Y8F A]-^׌߂_)dz%J}hV[W.;ֿvSgK8?_";>ߜytRbǣɌSe  pRqy}nYl4XN ӱDH5)ƁO(8Mq4B1r%P}dfYG]ͽ^vf 9]R.z9pqq*ds8f͜?«V"gAA=6@ z};ouL9{о6/P˝_R[g \#oP%hxB"poAg1ajn6gp/e'_ڰЩK#c3s 1# cs.z<ü Lqu=7#O${$AlCőx T&I<r6ߕ"ƳRZ$0`G~~:gK8}r՛#㓓S`4UP`g \Cdw|]rs^LWnP#u:x, #RYr9a@_=n,n\/Yvmt|0w9of-Jo,|d &aaGF/^"I,a%Mݻv7Gںyk/2@c?~;cQgo f+M1xPiBUV yMAȔfd6$DbH %79..oSx#pPJ'!vq%1& /lY2v M?<:8ϓ )6kŒNf9L'rN EG cJ2'ܸF|x:*A ~&Ag z 'q΋#RA4Yar9#X.rJ&,R ԵyĮQMaܠA1Rρ4qwgu)$t!4)KPAS!\Nl%{‘`0 4<~t\`304ykͺSX''~*.-..R؜peҡƗ&Se-vt,Tap4=v߾zGlÞ>H[y k`a67Ee/C?4XPA0 t#ӎzB^]9RX HinGWv[ hPs惑D^ )QlfiHX4v IDAT N<|xQ$', HCDf͌\9wx7|Yz齽G.^3>ŋs4P$B~]<[)m' a_ow 8}$/ۡtzT}bF莵}Ц@o0 h: 1l~jok_<RO΃"wgb,56ivZ5as&\/Nc.v ~XXxKu "7.$ל0SX8&#!RT;͜WmȲXfRL<ϋ/BB/#oK?4cg9B՛>ㄨY\I$ܸ f;;H?b4 Y8nh@{D!i4%a}w * YL7w 0;xHv x(Vh kE9vvq[L4db )êpٯ?pmR_,<zfG^1:>Z @#/ؕ/ D5qN0p4&uxpᝄ[`{x."~x_h LxfV#*nmf~1- ~rn߸r̉/}'pRpH'_uo|I),fb1M`Z! Ŧͼ'p.)):bZ[`{Xj]!\s#Np#xuS"l:٬ NnȌEYXΰ0jNw0aCls.>qh|vgBj/ݟ,mgԤx EFEx7 ^q3^DVЫ376OO^b„C,7~/vM҆vFq=d*#'_3&ȑ4Y?oY5L}R8~*I7/i9+Ө]b5!i +n?fafͭQflaS{  0í-mN#]lFl] f'05 "%8/ujƸH"21Ő|Hyu6QX.ZVVsh 3*0%An R?/;8WpPY9y<6eZݴ /sfqr!"8 Ha{ C0GtMɱ_t_ C_ c _RϯLLNMOsX<{=Rf)Jp!E=FZɉĹsib qqv\NĢ#AmQ"QzFg6/ubr :}dؖX:De>ҮS̺/9͑_޹}0w= Ro|tؕ['&S3sp>) Rb23blgY{-QbW0ӵ [YBJ RXYƜ5/ H1*6#=;;,"8  **8("(L2 2>x<Ǩ&Uk?wܛTwu| q%|9oGq7dKDvb'Q9"ז&Gz޹|,K_:X>8;[6#TtcIPr$V#Űr/&j뒤 $#77bxhQV<iQ`1G<4A9a a0C]T] _>ֵKu5'Hn}: =8|RpcP|jWȋίwP@/CvepL wJ F<u$R/q$"Dra80uB"i01l$ZLj`͍<|#Y\X\ZBͩ?IUf_It}9JRn 8.U#8,:wg v`[q! N'j9>)aߠuAR8;/H\a*B|‚lI*𬾛}n||J1AKdž;tC6X߸UUL>j(l8tJStG;1X\\r)\*SpQBvX\5"吤hj5(WF43B?y+؊4x@PCȧh9Պ41A|6A Q\de0[gUa굆$˰ʯ c'J= !@M4Eic >U':n~YTi_$ñ7̸m)DB!*T1b9<jo/j I.qà aywnL) jf]9aqѓI H,N9AG=tRiQyahf߼|yΝÑԭ"(aIUT]3:$W*]cEu_%N(P.yʼ udXN8O: h{> >0B@ȗ |<,G 4t$̌gQb/A\lLmκ4V :ȨJ+lz]++˓ܾR{Taq&ڊd%өc3.M88U9ck]/`T}Q"| )E}Su9&ҍP;Lact*xWBƝ({Sp6[a.m| /VyJh1ѫvh\{ayD:*`VIڧR6K'R204U|.s78j$K VBae0Ts!6H p'7wwvwdfvx=rWR nAiJ7,=G¾ hs{z˚HRג2)к jr)Fxekd25,͐r\r9-Uw->|Z'eݫ+Ksӯ_<}~Q+8"%6[Q5>6HA^dNPWLO蚮LQ>WdsQjɀe)g΃5/90=ҹŗ?GjQGdM[αO?d4&^>wFSÕڳpHȪ%ukӬ!b{=.o_Y 9@ 1:htH2D1i<(8D?9rtg(#:PQsaxP`GfusPfgTavRXXZ^|7VKS2q$T'O\hzbhrБk,4MMεO|#:Lᘐ&hK,G4!lLZ!dƦѰ %R8k-B!df]* ǵ2?;5U$5vOHj6=찥U'|lo:% 0UTrL,bS."3hj;LDHTED@#8b`Xƚll:@uB:T4wۑg/U<<({\R/É%eG~}!}qި*\6r! %O_]Ȉ1t5;bBeŭ0oɴL6 r.!&-)vAo][*8WHBb&$𖲷$~grкaKJD¯rjCˑf2[_>Zo(LdST <+ /?}qAyBu.[q{ɡ0/>\09H!vT_q ƒ$bmHY14 g]Hl/ga|ns\󬯮./Nt޸vҹ*1IAIҝyKR ڣt:tbbJ X2Cd늶,i}5VR41~eFh=3^Savb'^EF}ua~fz[Oe AIǫ/ܰ\niɀ4Qv?QTD!U͠q2:P_S49fM1Qr0>5xߚsSIH,G$Y66Sͳ~zrldxhzŸޖTyA; 7t5'E}_*PZ=DGSMKP C$pW>El{ E 3[w\nkk֬mllofS`򭼛y;:[ 52p$uJNR-Cno\54ߡAl Ii ST:%5tHj aT 1U[W[`#ƁʹN'*,I )JFp,j)j|{ۮ^>%M#KEPr:fei])C7{<>븜0BO*5u@W ME%;N^"ED"{ xT_])E}# EPz>H^:ȍxuJ :JZxE!ºuȯ!I+P khXCT&KqҜI*v'q#!zDMd>ۚG@cH!fJ&ӅO?orf>&=3w=\wZ  )}m+|qADSH-yMW1x0'C̤f皳} =om͝f!|ϳ~ayГmMΝ>yPj$Uvmx&8\jɃ)c4<m# jc(l1J E1|흝F> k̛Wj5I/ꆗTnP GX"W\H&K{vuCs!,'KPe( \:$ jϞ|J&=N< jC˙^4`D #]AR^=CbqȾgw&hK+!=槸e-T-1K?YZt&c*Yo\&Mz\sSï_r¹Ӈ$[đ"(II]Y>edS6$Zadý]Xucǎ7~Td2R]YŽ\6MF 2?FX/ B%p3winb|tt$Ƒ"(UI~Sq"N+I>AHvŏ%<A)域)Ge ,~Gʪ˵4񦿯ӮWI d%UV;'29륃,kkɎB C `V0hz}-d }Ш{*vϼzvƋ aO$ñh:gt8tQGxNO$x M gfr[[ J{I s8$f' |ƆƦrGRt%UVґu.Oi'( BʼnR=IYdʴl~{gEC^7p4»OmTwd㕕rERj'WBԞEA&K*cxI乌l>p0H@; U)OW|:B4O^ :;o55_<ÁߋABIUι43A*1~ၔ$,%%sVy4liMDFoB6C?>Cd<$oeia~Ыɱ/U9T\GR'%UVwz(a4Ņl&=2B&YWAKAwZgRIoiW/=~;07OJI(ҖTEgcSF$"e6w6]3V>O_ q_<ǬS+b~k,gGz{:lk|x*9ϱ$UVvO߸Zn"f.".6Ľh8.5Yy3Q;mU$#"(uIU~#cy(j=elaD˭v4{fG_uv[[kk WjO?tPs"g*+CSKn7#٭O70(LեŅ3c#_=ypTdNVIBHxIp=3 K+kkk.kmmݯ;Yz>gݵv,YI xy߳λ͍O8\q`yEEE2$I+=!o?zbܼu -~{}ݽ~aa7/w={Zc--ׯ֝;* \#)wTi'7ݼ?0z|rjrbS33S/uuܸzS'Ԟ>V )ETrGwwutwt |b͛Olkkq f_GRG`IՑ]|v?pR]Ʌ/ukj/]9SuںhnnRw11C_GRG`IsOV64^Up$uT>qDz |MI* | |QDREq$UQ")Aʋ@$%C$%_GR@$%C$%_GR?HJ>HJ(V")A")A8DR9S("]I[("I I I;-(@`Rv_H"JW~k]ao]QoHJ(")A[J,նAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA58 +IENDB`splash/docs/figs/surfpart5.png000644 000766 000000 00000060467 13261626263 017322 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATxUym]lc&cf̀cb$$$F10c̎㸉8CIiIszz w> X{a}mt?z>c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1 ;L (wn?z9E 0lذQFTTT͚5ʩS JKK}iӦ3>"bnʊkd6aĈ&L(..F@ٳgϜ9sܹsa矯.Xxg>̘1cԩEEESOFV)vTV\#_#OV*+aR*"udw@~棏>J4r)S-ar,Zh#O>}:9ҰM{ h#;e<7J"%(JJ~fz#`ǨIe_Ħ49eG9tP&&Br&x.]-[< ] 6m-`9 .\ ׬Yn=ƭ$@;o3Yjt*),?*vT"JHϟzGI-$a Acuu5IRyyٳbiŋQMp /a k0ŤU8N=3/*DJ\! N:v_# o9UoRK(G `ŊK,Qo:`RJc+H Ix#BsΝp\BRyG6)E zn zalR@)[ЋTV*0`1c0دD "3.\8D#|R ipzӁ1INw68-r:9W)Y">[#O@*JVsd\OMϰRqsq|cРANCCTTT6N`{8f!/f'bRUUh\Jܪil%VdYbn}T2 bK>9'|WM2~z52a h;O5AS +V,_\c%J]tQ=$/[RGϘQP0{+|`[4ݸ&"zq5qҬTAYtH{#J={vAATX) L2E99Mqq1ꠔ P߶|(${45fRSp?hL"h9q+//l>wMFݻC\T^0ӱa+)JQV*GIRጣPF . +k@>*;ۋ/PD8 mφ"j#)yw ( p+ 4`l R&8j˖={BK,9@ʕJ V*HR7◓J}+ hr&uQ]%@GFjTbF@hc֭`B#uZH`Xn"cc#1,wP\邙,S`゙ )(5/ 2R Y2=@1cL:RŋaM0Nt+eP꿉2!/HyDA|z#]/ӑc>ŰaÔ(}!mR ~8b[cfُ Du,Y"hVYbL+`YjfM/ ` N \6oެxX}Bboݺu˖-l)+*ABS9= !ΥQ$_W}&|BME*UWxq|J='n{vo޹++vR)"Di*+. 2jԨ &hAU$I&3ǚ %ꑉ(1ɉĈ`'̑~ǎJiƽQWWTmb8\ь xͨ;KhԫLK^^1cm۶wSN544,X*wRJ.}޺8RJݹRiוֹ+y晢Ll$ZtPIИ Z y S] %P mC4۴i{8\)T%$e'jSbVQ>F4pn4;wܵkWgg/+W>Xo#Ч1i+]*X_X)AiBⱸxΜ9FF<*hi:8˶t_1֭[yMBب!X+*%r9ռ*2<019FT(]Ӷ6p6T޿޽{q-L<#T/"z;?JŁbKV6J]W}Az465Il69ij"<B ZXcƶD3O<.4)YWW)Fܹ0׬) _$1UNR&li$`^-tJyֽѴx[@_d}Kog X)s+VJ|c~`nԟEJL81AD`&jjTuCklkINbl푉E{K̙34PfRGƃsʕ+uWeΆ5S^^^\\Lڇ[B䓆>|N:EJYEEEߟeJ&zaRO#w5ѽd۽anQǒmk3|@ Bq|q3أ~&$HPHx$Ty$l ގ_|WyK*>~8ΣG{hl̀Wm$:.]k=/++ I)?ӧkyCUǼpbjq<%͆ ~8V9V*R+ecrA)( < 3f̝[UU8Ug*Yj"h ^jb:aƐ۷cOI(+dEJ8@.@.C"E'OQ摳q9.X G9rX;P#`;VJΘ1c_'O& q dш#*n݊:*J^B֒$uuu /#d= ;vӼı?w)ⴼʵHvn(h2H 3D)HKk.Դ/r'ђo/R"TR+e"`cX?WGnnu4)*V†3g$K׼QM'fGӭ7+X~RѰRRw"`#"A8'@d};o^'/!4(#fO'4ĉ_\hizUZ^u5J MJzM0"AWW瑃$ՕTiI Istx9)v2ʫm| "r9$Dy j6|#phN6OX z;{Ƹ=ε U>ѣG}iJ')U&?XRO"`Ç/))A: Hi̗,јY섌GlP' %!IDΝ;GbҰE/Ѐu%?™ciaEvR.^e4Ks'RJwԊܪ^eN(*Yx Okk+eĈV*~+/R(aTL&M"4~@sHj͎?_8Dm۶mҥz%*&DTM./_$`-@}wi O%R%BF{<$4pјf' "SmmkUQv-x[l,`?5+Qʝ/ťyԌ|p{$rX)sV*N~+X)+uoX#G8qɓ, +WX2Z7TSCCzӺ:MB]:A`*hFbۉ'4 bt ]S8!~07ȢaeVۚa[_lh?J_ynXM|B?PFJ-V*~~+X)+V x 2bh$ ɖ4A&֭[gk֬!g! d# Fn.]z1ؤ; @\a|~^Rܢ=qA͕W*N3'ON %%Ȣ4 hiy):`!GBD8ziB&XD"Z>b@G4)P9hYi0ٳstW1L"W$JS lBIdRR5k,1_|p,-Z#TMEٯq÷nP#`r+>+X)+JR'KCxHTSZ!Y=*[`{.< B3rOqBzɓ'&NU\㨃d*ϟ߾};Wf' ^+ v|B2U @B Um$ 0rhfϣYJț{͝kÅ[(*XJYcT z/V*!mB&2 Uis ' x~bMp'HE{ 3ի>DI?@!C.yh"(:\|:DY8O]ŭ!b0<\hCޜ-5iik%"Ad4J%0D_&^(MOGv.]+ADR(jgbeπwdW^R x7ܧʥ?J}5V*RV*sX)+Bފv @8q= zk;;v$1 #1 IDAT`V"ȢON;wƅ{fV -k@ţr&.x,ZtZՉ4G ÐFΝImK^H|)_S*!Cx-܎/. S),,=zC=t? X,R/Ze2SR)TXD&ۊ+ThٲeyqݭD5{^bBҥ.Gv] TzGǫJJDΤ p2Y.S.,7JW^%bgU,Aށ+ԠWr~xGGH 5xQ%T@DR(|zbAX|JeR_^T)RyddJxԆH#U]0" _Fb4(py%'5txgeWZ0: @HFƾkoj) p͋/`]i ox:uDHyl9-W3*QN.-)(oԨQz,/x5 gQ-sNO4iBވ G}A#jYJɸ.WG~!ΏHȺϱ1+_L\0ڵk ­ux$OZsRI/2̙3ghY^mrj9 oHYΝ;U^~Rg#`RRR!T;9}7e-HdT& ms˖-fDtvvO bmӜ_|O Xx/}4h̙ -p?>?}qI~ӟb'½ʘ8/qsfBltҒ~ ,[L%fhVXLU쬨pZ uCL5D~40aBjMԙRRV*vzVHS+ӗ+eb'TtTrٳikkC ⇝~Z͎;4n6_;GD:L 4w%"|;y嗵ĹJ'?`W7Il|_Eo|*B?ȋhNX555؃+VB,2͞=g%,!2jCOET85m۶&ި8"d=RR*u*_*vZ)+;R]/~K%J=; R"Kx D=zQe:%r-sN4/AC.S%iCk .ʁxM$X؆|@ !d!9̈p:Ud6P G),!"m•ӧ6}&x5ѳKZCc%pE#&GkBH"i hPvĉiRaRV*vBNF *+eb'*m6(5|pD!/_ʐ>tht@sɷP`C$"c9!J.i54(fhN.A3-Kl1,=[Jwnmm->mXfIr&̙3QfuVFN[&Q;!&HN:+uX~TGԽb"Ou7ʜ]p(\yB(b?c1H`?ϒ9JqBG>?q^Uq-LU,\TC*gsq \ },кR$:h2v0Odyeڴi$Kd zoq) Ҵ*O +uOX)+Jw6+JXm_Nt}Ç/))y?!oذAC<@aDz5~6l(/_%G%xXZ:9dC%33D鉠dwIV{~̚yO*aO~zLRe^xaj-`Ȣz/Hyo? %{U4x|=q 1TUUM2%]?{ Tjo]b++u 0Ai(@2ɞ|ŋɟtGr Œ"^!!,=ov^*p 9 $4r=|1Xa[AL׬v-Wd mU#<ե (TSX̠r4q( nMeMx+oTlԩeee|O>dj~H)#T5V*VJgJYѵ3V*ZOH) " Nfbu>}TO٩E>/$XD:a !&1R PGSpE3`ӦMZFW5լzOT&Tn$R3U*X" 1{iPѐX񞴾 &>ns ӤIƌ% PXHX)+u'\E"cKV _ 3~(  'z*;wtG/U4 Kј3W\QIK$*nDzBXjXJq yG ǛDRɱRVR1PZZZQQ:Z^L#yTCŋɢist!+٦%zA-^.{4^zޘٔΣq5'kK*dTUUNٳg%{pG]+Vh,λI[Q y+ܯhl'4բ24R#``+eB#`nˠAF=uTbCīMSv Ђ=7X\ &GyҘCȇ1x%w5/9J"q f<$[4f &Р S_|1ZPʐzփKPjZqq%ٳg %4B?<2GTCmRVJXRVJJmQFTVVʧիW`$"CBШ]2i+f {PM @$A,¿CBA8H:EF♦:p&b)abǒhM^*ɭw_ % ~ny %٩z,YH:BOLr3;⮑I5jЪ|5Ǐ#X8RV*AT}TX)+ T.VHOM5n`$7ҊDH"{ G̫:@&DC|>S$g=VAS?B+en!TjsԭX)+u V ""}P'4 Ar@*$%(Eȫ%aki/^hYUϫbփdKUNz*Ge :b`#4ĉXpɱ?pJq5ܔ )qf(z5TVӡh|&d#&;R:t:4Čy "APk1gϾKXŗnxZ9H2 +uWX)+u;Bj"`>JY*>V[V s!!5A3TɓD=*fNPI(*9'!&UMW%uIBA]s6{WDָŸ#xSȤ\0b0Ic]I؏IG,uhJ Ss ^~H* Ieԩ5-(^պ=z##Cͼ?| PuNNisׄJ-RV* VsB@R,CQTpbPKIQm$RAZZAkyC)5eE[:/ْzQ H48Ci9 ijel*sε( gp\{0 ŻG^z+lhWɢ4힔lI-/_|ҥ_~\ 7)鏅"* VJY$X^RVn TIIIUUD / &ȫ zE9E)%XIبEd$)Q|HӰɣjXkĄ3-^lL/%or`ssI;v̙*ѡ{q 4(sJ͋RV* VX)+u,:l\ԨQ5JWa3VT" АU #9N^*´*2$vb3%-O)cGG\eQhMq}k1nC(S(>U{nD'KWO<2E$Tjn2I+eL͌=!W=I6J(%7T2 Tj* iDƤMf,)'IHwA6`^@J駢:-@J7w_555q6lxܹϣ644N8رc QBfGJY)+u3VJ3R"J1T׷ғŰGn &F+ chKΤ\%ɣN1\:PJm޼Y5rJ Crc3޽{P#G*5zhT QbFSr%s(gRgF4 V ĒU'OwDTQQKΦ9أ:3Haii봉g<ٳuV۷۷ȑ#(uر{Ҁ[RqMO0aȐ!n;VJX *R& R;xOO2yB6C>SN8qҤIyIJdS.*<*ZSɍPJ94REsɮhΫ 5okk!Lz1ٳ/@$F1rL]>cPX)+eϕ"hՏ /YCdXERahY8MW"U94IY; |r#TpS"*aSM3Mks&577Ϟ={ҥttt߿Ј ɓ'e *R& RS"`IB?WJ!T4m~ŧc.];4 >H4 I8BIEZ$Gu}YKlcgX-O;v a:|0ѡCȑ#| 6B&GJY)+eLJM@)EjC'r&žc5C$l v2D_Y ؉@;:&W)v j^Һ;ЀZ?كO;<)ԩSN8Ow&BDu9;Gt#B&FJY)+eLJM@)e̸@kԔ(MoW `fk@M4UBqaa!Es!fSڣ+i%<\eTS -`dTuuuhDED uAڹsֲz4g6leJ?VJX +eLBE R:.ZST(1؞L ɤ.p <ZYP*)Iֹ );(..@57j¦4B&uҀL:dȐLV%LA/hPg"KJ$us+egѱ>uʱc7=w;52 xVvjzI&z jCu!{xʇxJnrlɓ1O\ pn)GFMr f<8p@C$N#j,B?Nrc#CJJtezͥ|W{ֈjP2 d)-Ld|3ҏVKid*IUǏ2WZtRNaÆ-[ˎ;ZZZȖ𦻻;F"аk׮zUDѣGg:LBD *R>MR)7^ڸq#W!yBt[ R6VʤU*G-J z*9}LOH!XJU&ʖ HoNOz x 2+W_HZZZvI.,;vrKۺM6-Zh*F3g,//7nȑ# УQA_6PSs+*`Rɔ*HT+E:NۼSX4h E6fD7nDJkג!iCYYYII8p F1ν, L_#n3T)L{RYq͞ӫJ73>˵n{IF/m)n]qNYPpj91sgGt1枰RK.gLWCc1c1E➝,,MsGQp}f=;5֟}B n[h߮IY<[c!|_,Qr>TT~&ehRAjED)S//TY 9}?7+ Vܠ ,W1{oפjIENDB`splash/docs/figs/xsec3D.fig000755 000766 000000 00000003126 13261626263 016471 0ustar00dpricewheel000000 000000 #FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 2175 1575 75 75 2175 1575 2175 1650 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3750 3075 75 75 3750 3075 3750 3150 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4800 1575 75 75 4800 1575 4800 1650 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4800 4950 75 75 4800 4950 4800 5025 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 1950 2775 75 75 1950 2775 1950 2850 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3472 1822 75 75 3472 1822 3472 1897 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 5625 3525 75 75 5625 3525 5625 3600 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 600 2325 75 75 600 2325 600 2400 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3300 675 75 75 3300 675 3300 750 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 878 772 75 75 878 772 878 847 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4972 2603 75 75 4972 2603 4972 2678 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4725 3600 75 75 4725 3600 4725 3675 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 5700 675 75 75 5700 675 5700 750 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3075 5550 75 75 3075 5550 3075 5625 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 2925 3900 75 75 2925 3900 2925 3975 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 450 5175 75 75 450 5175 450 5250 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 1200 3675 75 75 1200 3675 1200 3750 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3750 4425 75 75 3750 4425 3750 4500 1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 1950 2775 1969 1969 1950 2775 3225 4275 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 750 1200 1950 2775 4 0 0 100 0 0 20 0.0000 4 210 300 1425 1950 2h\001 4 0 0 100 0 1 20 0.0000 4 210 90 1725 2025 i\001 4 0 0 100 0 1 20 0.0000 4 210 90 1725 2925 i\001 splash/docs/figs/colourparts.pdf000644 000766 000000 00000477722 13261626263 017737 0ustar00dpricewheel000000 000000 %PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream xˎKΖuuԟØ3Ā آ3T)Zjw9|__p}_z>>O?}|<~n??}~_￾y?-~}?x:o"t>oϟ|Pf\^1>]W;~?~'t97yʧszJ_? Ү?p֓Ÿ[/|>~k/|\5Ծ7|Z ^݇΅ڸog`38`}:o!Us|:6ƋxG}r>pץߏwRB>{:N?ח^ЇOsSA2}qe*Nq*3gcyB>Jt /? >!ɺ4[\g/[_[ЇOt>0T_XƘ[Ї/e0_ p|,JMwM>xGS<9K3$ߵyAƚ'珦s =oM݂k^u}2dZ^3dd9lߵlߜ>t^jۯ k3~g3~s^PyA͹9zd\oky ݏg+A ֿ*~5ㆫ|FJ576j8.0@B{X.?_l^8+ &Y O_G.|/:q;9O?b+r~c#muE!G'&8?~X8Pv:yٜKm~>}٨ɰ,;T-N9Bmp>:߈`!Ǭp֤ \?ȹVO9j+㝍k4vlsI~+֘[l^8_LEśsy bH6r;8^wC=F>9ysN޸7kcl>_øfq('_~sfY9oaSf#_-=FӏP&r-orao 8tu7d3kcg]EMny[\4Y^b#cK9⍋|k\?jw69PBq ۺ(wxyY j/ǯU-FM -xMe&8K c|y8 y@1׼GO>i|a>Oލ}Hs}o+j7֗@d؇2O A9OADgmx:>^T.3ϻ9G/l>MSa,/=؇Ia}lv|ºOLA=ѱ7lH=x%u*S IPZ > ׂyCܳ>Mw3}ĹցZ> }qMq#]滑nMpCݜ?;OgyMyCfx=s[}s>81˙ܕ_~cM@Ē%w{-z#oYs~|#[/C΋ͬI'ЯYX|}?nǾ?~C_O~c#gtOral"-+6r&b=]Ov?M"'=֯lFKs^lf}|6w}`Dxfc^ȿgZe`9]aC"zI c W5odrɇ$ {|8OF ;~^|}C׏k*Gșfw.glJ]lUO>.9~)w>QiV 9Nk֯lbx;A3K?X&LK qK W6gݣ9/6>>Ի>yG".| ڏw.7>aFDflf#cO>;2?tafp^/'yzeN5OnS76jm݋9iť#lI`aq|Qvc$,ry}[Y7"//W\E%%HrEeUĦ8DԪysz|8빞 i|u ^tfK yo.Ov/T\z(NHroǗ܋)˵VM5)N j0%vo@jz:x Pbe p9Q1" WpZyN6<>)a(S*5Uew48}ߘ1Hcj FH/OIl_9on v~P/gx)\Qߎ>ќY77BI^!NFOF|:oN҈lyY#[ yӠwkUH[^E[;}1Nlt(3^0]9  n8x%URTt4BealqHrME' 5(yՠjkӻ}脌x6R߼"l(b1м;Y?"8 r}v`ee1hKA: !~[h3H={ZqX\y܂0a8/1HAc&>sBe1Ǝ܆(ղ,H?0]<@ܰrRPD2 $ Z)l]Pr 42̥*] OB8 ,5mNeW_NM ).hsc]~^P;㒍t.OGuԀ zyl,\Yʼn:E\k|HGKv"-6$ fw%sh^Jc~u9 pLT,z~ܾ O=8\ `t]$pԦJ&HDDBV+*nx>j0"l|ҐVR%x52-݄\sw؂`^V*Řue|pzAGGcH*HL UaC4V[͞<1!OE WO.#kqU;xQ4RT~Qxs`#4TbIy1TvyND !/(Z64'$9-e_uj(NiYVXcFrqy-g87{k6urfo&-Tߘ4PFJ^ ^kIHi =\kTin{ra=ܪWF'wvұߜd1,S܋S"Q[!/5Ch͉88eRB0c %bzR+v!w}3q6 z2:v-NDT,ΥvٿSƲy2_wRX4TSYaѸb\XlsWWF_h)OEWQQ ;@v;.w6xsӼE!ZXJ b=]EЁxGG LbƸ}a#їDBhTP9qIr3|.Gn\&A48Ƹb@p -?k( UbJH b׳KݍoiXUjaJqHSñF+W3x@@+/!jqVojcc3LjyqbXƒFmp§ӃDJ^!kCܶ qa *d)cqvrtcTrd)Iyג nRF OJylaY1N5#x%R.X4ڔ Vd7^M=ֆ./q|o9/3E 5>؁)ND qN5tESv8eW& bЩ)h؆0 jxq-4Յ'ZE.>QLj@t8;g<ԫit,,T'D%B,Z * yݾAl_7}qn,Df9k5$ pBFX7ɩ夂cU>9nJ^ZA5Sَ+{K&KNj,=$3_\dP az9"A8ⴐ%rv ZsX7Z^!Ά`զ}1 uzQC~R%H0&8y:~^ɅDr=gl:؋HH+ęZ|s}a5*ư@Oաi~IΩGqPdS/\ƭHrEEbQیT8-&8@AKۧ?,vXC"6 7bk Mild-̺?/h(.ݹea"'VHqj$b40e2fVX)WZ-okSRC{z8Y)76f> pOë{uB#=x`FvɓS#%r;=MhNaB-gh5i a_xxOEB-A盱O8/1@ЂlAb6FěRړD+&[8oyXZHk&Ņa9Ho ф9rŒBl% @EHȸ19BP,' ݸoN y =ISI5ӻ}i>mejm"7@{X[[$rr$6âIҍ\ﺺiY8gd܌LqpCVjP-ݺ>r:ب쁅=lG<0у@b -NNt(wX'`N-/(Zyq}amnpN1.İY$>ɠ"q S8 $e,ydB,Z Ҽ9 [J%W^Q> 3#p SѕFxwR7{2Vjg D$iQeWs@+!J,JKѴ#?ݗ#U~) zG6rRkrEwܥ kNym*HWZAq!]p` hKzHe:`̩zEEi !r5JJkn]:t򳮷Kx[O&`@OeBg7pݺ8 $e9p{TewGzۈZA5Ӱ鵮^`/2OΞ䌻zx pqۥ(O0,A,&P/YnPj7mNLH+ šfXR|Ra$DvpdPE仔mm+q$93x,pt,-ߩ /857/fpb%"t{㒗Hk 7wb#NM1 1YrYlQz)-ĥ @^,70B -FS\!)jICr<:m9DhFa;ۼtLT[@)yY1>[1H iyXj&8ۗ KWdB/#(.h#,l4H54k`~;'3kwSVd3²TQU|cfD;FXiaL * *M!%ܒ"-؜c% f_n PnPH7i\b) ,K5}җ6ٖUH S`UTt3H ㎘#=WqS>ݣء6'WWkUHYSBоlIJrs:8s(j8nbdmYZrK)6R*NHS8qܰ1P-W',L'Y 3,:-64Q,P]$&3s:觓pezD1Jv!X Gͩ\alЪ917B,Z5}85&19Lka8eK# pqx8oubÀ!)K0©J|;R5Cp)B̠҈ݬ#+(#q+͉qq5lON4"cy͜*BpL_mHqb N!/ˉAE1{pb5!˵V4oNMiJ-zItq%-"}):udz\a܁'oN^_,‘r"֜h myX_1jyqz/oMٯד+1coI ##Ydݪ ga̷aa 6H[A4U^"[7ݕX2 Ts1! 'QG7O6K_~!\'FJ^+*D6'WxV!1yV)Nܼ^\xcTKnaYEyU{qz$ pQ)N1,R'{] IyUbh5)N jfKW,t% Յsr ;tbtoHJ`ŅM|,VT FUB,*kPݸv"ŠZ~ ,@ݱL>AAg+)RĻDNND-,Vtms 7d@," I[ʸǛ^#XXu%+5M-ŀi"%rE/B)j|rj[Zma& QUgs/`vBF=9ꀝM~:Hq Oe8aڜ>Z^"nVM5)N l/a$^t")0n0!v3 $v->71ǚPpDc(ͅBёaˊֲq+ J/N Qc4<=l Xi9CGV&> +N q" < lTͺ0 $pRKfrQ^F #(-FR)iGb뎣Ӎ7{*o5zcnL"ma"W܃ <Ƥx97!*Ъ N4Da^Z}4āqWQ[Blg}j&FGcXHɳ|MHqžxUZCZShA:\b- Ŝ<=kdi(aHqJ$.MiNbs#YA&'uYD @i Y3d w$4*lRey3b0R&JKʊϻmh1bLKHmHto8UfT3/Fµ.BQ&۾)yYpئMNΜ%4TmMqz/ZcqhbI;%Gǁ!@ЂD(p*8 ri"ʣ *SV5Fʾ/@rbnȈlbW٥}ByLh HqjebcjNA*A+rՠZ(HHMoi8ds/Lvg= *ƒhsWDc `|ifanwߊHM03%ҦZ`DB妣W{3( ^۲yd q4 q6i͘(W'Tv eQI.&Zf ^rЖVP@;9 C¸0]ňݝՐy|Q!abXX#~fIMc"X.N,WT(VMm)Fb՚!c;wU4+[@fL 9Xq<-*t =Di!b1#Ƒ4%EjjK+Ni](300yB8D$ˤ3kMXg.0:2/NQƑ]-$N {FKh-UN$J"{NG|ZOwZ+^rd^S,wn7Lk4*npf}]e+bR(.08 $:s rhE6Np;`pE{BXn!Wl ;MD͑'|+*=V3t'аh6Ê F؀hZici:ǞeZK`[50NJHN yYL^hwS^"vUS5ͩzD~~7E8 #{ၠ1㠞9n扬fjͩpZ& 8TNfL6\l®A:T쑸Nxlg30Rk'<5F8aC^!)j95%r̬[_ rE?#KG݌GΌL3d`gD ̪-> 4+b2[z $]}|d{ |63bBVIٳn/`\7~}"<9 $Y1H$ҜmyXWnSjkS#e_6:p'W[!.\#XDJ6e (Fdx(XS#4 +T+ՐkL*l m@iTYE2gxHVt;xM}<',Hp`c5OzBx慊 P<%/<ܜX Q<4($VMONÖx[Ǔ#%$*p7FM ŵ`z-INS!(pMܹ IyUSi هhtwޘ IFF)'BX}hMh7.$VXk,7#.B+kFРi;ͻe_\TV[e/hvvTQ`@"AovĻO_Y+MEjdVw!XcH?@r %˵RM5l)ND'+ygd>d@mR$9O0Bpިx(8;UHrb 5|bA!kj"#""(i3b!j3"Ov7=R61;| b 1;sHTl1J$ qYdMWRm)FݻY\c|aabq66n (9C0''@IԆbp65B|}TÔ4Sl﵌ǣT$Ǭvۻ>$Ivc qK|fS^u@Xf6|ZMwlf{Tbwk54&*&O ?瀻9:}€߈HEDR )A հ QZTmK2&h7}bPY#EL v96{&C񾢞uLfh:e?0#)2/FYnPqOas"J±ܔWHKh]ijY׆i\G #:ߌ z큅D &:`0iw!ъ!/5ܑ'&&˕B,Z5հ8 䱨Nhŵ] KƮCzh>u#h~9Megr$P)-rN͉cK IyUS k@>Is[ȍΰzba.xPAM9:;v%/<)6 akՃę͈ÇJ*RESv ~HY2ލs+iL% i6NyhSa_)qY`?81-yXTdіb,IiL?s/ʮ]+.jAL7fE;'<0.f#Ģc !R#c^[WS@Jmf!J%eV :+vYū%`xd=S9p=/;$sܣ.۪=Hq\6[0q59v.'M=_(m rUboen7-NuE7goz@tp825p5{΅ۮxda"<`'8N/HrMEq y裷VJ N/Hɛ+X8\dUAR _-,E1HPbeDHbBPYJWSe hĶm!!/%OOóXni0|oesJdW=h' k;DJt뾒8M:gP0|{?@B\*"mHEbjP-p͸ؐMqtecBb\wLȞ0y# nS Lcg:d'(:x{ݜ0PBWZB8 [¾׹ o'h/q&khW ק7Ep#h8h S"[^*'LQMyp*rՠjkӻ}i>G+Ȗ|1w`]X)*g 7ƓIǭ;aUNP'٘4bpac,QLBI eqZlI+faZb_j4$Ľjb}3/CC":Q(Wax$K8>&02A -\GJ6zěVH S%x'צ4Fʸhv>3;#tQAž4 EF$Z=7xSKY\cz9A1ϖUS樒{ qq/WqpsTen hg 6@et͉)&P\c|化mNP&x f+$-9 ˚n_Li^lb"P"f7HEXŕAXn#LXX !i$eОjC@xl>5Hy alV Ik,j\}%0Rc+k]cdK-$kL8BET ^;Ti J6z),w[HU~`jYX?$Rxj58#"@ՓH0% tJ":\qkhńB}p!&SŖN z$#z@[cL.:p%B+ :Ǜ77 AHɿE* #\< ,K܌-,~[BFUX*ʧ_Nu^ 9{*t0 S8-3NɦŊې͈r}c%-TWRvsJん1`2!>m̎6m7FD!"Dmc5FQỳƤ"3,HF Ķ7ECv+#kdE! [^(6o%;71fagzFk9^}c?^07M^p/N\cTPӤl-MZ ٳ6agmY,|xJ;1Ƕtiҷ P8%j)bMnf!\!V:50% Ҍʗ.8.Rw!4VH4`q$s0ʼnEWX7'#"%ƛT<5 rKոxw5u qߘuoD3oX'#dDJ, <( 7s-bKmASJNt :[Yd+иf61υ9$oTDi,yYN,4iNP[yg [Zmfrz/b0L88jՍ+rteuA{"Lg A BYՀ\/B18Rf!hc Nٌ B Z:t俐lR- _OGΘYC˖-|=˜mDӝIE=fTj@q  w2ђpbT(lװ# 0}Vw[ 2Dn@ `/d&R/QN:{&V<\:6> ON8 3Gߓ5BG)-lTwNFж¿#^_4 /./6üϚY@Lt^^P4k81-$a ;/h+rUSme$Gq1 )t銟,giaʱӍpy4n+ BJX7UnZZ(J!-hiUv@Im&w@ R1(D: Z;,g*y`TV|SP}O`vEK/ ~BUx=¥hO(Jf4)ҟL9<~,]ZJ1,>+ -~(tA&&QAT+*b>A*F1(q#`$)N6 |2""4HbMDXU3EVˀTjP=-ʼnBJ^F6s&gHRa #6k;i^bE>rJx7Yér2m `+"\ufɈU8J)-AR[EJ/6ی[|µ4N *uA̲?sVNZ+`TDC@?/W->GunDh ,{HkriJKFa|a*g,6fce&HSOxOR[ tH?fȋ˗  $uQX >.fSMmTmJsjh-hwO)e"/sL1<D`Y6S"q6MtC,!Bq6PSc )!/K#isrc5T thTÚ4o BB:^:X-8C#*¼0fgRxwך7/39 $e\7msrߴbjPy>9q!_i̍z=b}1"oŒpƽ7*yBU)yY1.߲mNnyX(ThTÚ4/6FZ}|6h#G#9lHR"h# H& XT   4 -x/=x1tf)JJhih1p{W0kS}7qr3S2bA0R*s+Rmr]Y<8yfMhKjaMqH-N`Fd._l pSRFy 7 U 7rEŚhhp"<*.KULNIj7h\q#3|N9{#ϳ۲a#F678m U,ɉM@rfzRq-#g}+rUS k@>[<^ptތT :ds`9 <0b{E8"$381.Dq>cΐA Q\+ZHZvtgMHy(k'_/9fo:G S61 񟳁DHя)-/Io,mNgZ^ @,GCVM5)Na Mp iuah}>B2 7YG{ZD,0x˧@Bc`>9>ձJJkל[3[:/$;`{Le)7%IB*>fe Km*3eۘ KNQp}tpb%FʐWVVe4m |"!퉴f t`'L geFV|w!(F@ NsIK$YnPuYll'B" M,As'xÜNyR+c/,tc3p;C#&慠 DDXyȖ 6̲8T (/ rp:*aMqH#"-l.wb_?4v @ƅQ1o6u,Z {<A"{c>ՊD,ц֌f<,*iӴ!ݴ0KE|.眚Y1ĵ잫DRv#(A,FšD5UfQe}6vg-F4V?, {M6 e3H鉑.z!.[GSAH(* ,*(ƎYbJNQaSm]tYrfV ͛ӻ}ZC-bX&ɩEj<:JqJCGr'O[8I2]4Rpl Ҋ p6Ƒz=hip"U7ΦC6iFc\IAK&&gDLC+/X(T Ґc~Yw/p*sCѤ-K>pn ͛]G>Ѝ:KDxb-/c/3 eHS8 $e"OiNNĖUpUS5ͩeBM?/^ͷt Oa;y>|=\XJj ~kLv,WTxPPRja% ;/Qm5w6' BwpVJiS@p4κpDJz+5lA"8Le\Z0ʫT̢ԍ:9򡲖 CW=nqliN} 9~nz ITs'b\W)]=<$͜ruracfҦbA8М6R(W5 NT/<8JJ'a $I<.PUW'ˍ,yYIZ`ɔ%-yY1.IQ *LU% jaK^x.ɋi!x0Gc5qzi:SbFp-<i!,4afÁj"t&e❤ňrD|MuHK$1J-S\qJQ^tVg ge0#eCi|8OH#ps $ZbGE,WTI%G8W}kTyqzN&Q58@ԍw%! s8 sȐ R9= Ks^Q_E^.su{Y=&.@Ѡa*C,@+^k`Q!ZԨ9x$煶uz!"N*t~ ]\[DR /oBLVLiQIO9ET +LLeU[HQ)1tig0ЄMo| D8 HmM8%WyqSg !-5/`DB,W: ֺ ;Yw}=l^8e ķyXBAxke"6l lğ'2.]4Y,!Xa1Z 4b֩Z`/ ,CR+7 3//aD_ujcmĬk$fyx4R,6'2k-40B6(80+iyp·R~tc_?c4g;%5OMY1/5 )1J Kq҃ qI*$2MFLNfd<[SM!"c}"vyq`"dd% ,P ~opE %eFFREByҜn]YB%2"6 NuXjcx a[iCU\Q1y?wpbj^qZ5ղ-`ƅ@hśK~}ncg5D};[X:3R8J7@+M/oFs[ZW9rCjQ#-V6Ù}Epp8`e=΁EC^!k֠gsz/r/$B3\ٗsDt?aKy *DἨtI6D)a,X7'!>hhU/`nYtqo|ՙYA% 7p3b^M @oc G]? DaMsWU7#A'ŗB,*=xnNÐ0N/BF9qx0ާ$ O@ H<(*|(жE,<s ,V9ՓkV[Z/m6MlM^N'ȓ&sĕ!sDj1B=Au0ɍ~bN}r"1.`5 WunjԼ8VsAsЋہ1 O-c;X#()>K @ S?DPL"%,5x}$#H6-~Sh3h+!)/>RCYZS"n?^["&:*> iK,b͢R\45'x-Z8zD2#=/?H\mal&2 0O`gi- s1#$39%PZXNRiSഭZ 8gdv~s`IbxqM`5t>gXT NN yY<= g9׀WVJksq*[}a 4 :(7mT3T@y -Lt}bg'}@rbю7/-NLV~'a =\kTin_x)V#'s݉sL:\J**98 V05U;SMC^icZ 8 [J]DZBʌ9f Sq#)Zk'%(FT686HK$WV ͛ӻ}6kdeb7AXx)OOnBŤFֱ^h!RQ%G: IY¶ɈbiZX[eYh}A6\@(h!hexsAKfb )P+e␁&'tB,Z 8#w=Q0ўo83>i4PJ -3y1u,}v3-%/j+WZ5U[Ӝ)jbf qUXhFBCS.kLlYX"8ebKp yY4Bޟaڜ8%`iѴ)T^if_Y-' E`{K;1N$A3c7 #&yMi MޜʮMieiѴ)ͦa*[Q5 c 6v#S'Hr Ś{^WЭ@jͦF:uTašjf#]5/DZj ^ i$CG{Sr7/UqHrEIL[%S^!kUYӜ-%:8G6=¸{cT/d) ) d#C 0Z}#k9w%[)W-x D#W,VF }Q[[Gqe.$V(VPZOG} ik՚:5U5+qԉT#Ǜ IN{`qBy$Nj+ȳ G*6&cb2 ޛ-iY*i8zҹٰe fJBP@aQlڊ)zȭ Da-H epuo (0'cu`"Wn?F4劊:z2KDkUiޜ-% ̑9nHY&3%@7" Ay7FldU2j o".6qkG(rԠj[ӛuǻ/8Wcb7# 9  5RTAS#%/%^\i)[^!{i5ښn_1El| BnkcQ#GuL `X ̼> @c|89"teW"'#ec2*qXi#jV|1@XK(S4% Y}hL #axvn>Զ bS"[U`&X0'GQ^U kjXSզı n@uX44IN d؀6`n ĦjPVCq'0G\k(1[K LjrTaCq6p|%G_M/px$-xKn ɴCqHM7u7BR^iTmMsjArU%I4pE4(KbJD ^DCDkDefrw NצV)NUP>wj7ʳ}1'99R 0B)\>$+{|P Jlɪ×#Vڗ*6nF[߂&%Pɛ*G'!,nJ1nA=G,5WҘiRh qYN*x'f&'د<8b *[QSZ7=eT:+#}adqe^u8dyb歙I1p/NBB^kt6'$]LyYljh^RtA=Gd`|<dB~6P 9WZEr(cIpON4<$5v!@ ۊ)bja/'?~ \>50ޮcS=ЮݵqJoTD*UqHrRL59DIbDfr FSHl}/ )#, /a!!3wYZ#lF1/FPSi /*0:)k$況0-{FM~s宰0d5D#Ь ܭ2jH@:iUBHKD6L"Ņg18P'uQ,%*i,VXtuhg8> :;|x/jIND冼,'U`nV7}WT+1f~( qTQSID G:#X1A_-YUQəkga kJ\4i cǪ2DHRS#٭01ʘB//)6@$mgc(68z aKF,& imMiNqaYy86f=P1> ?+3yTIY8 Dyf5F|\i͇7Hi4B!JNl)>Î]Hϋ/hʔum1&I8[|DS ;e{I"o0e MŠw!4ny+N&` nyXj*.z+N)yy,7)@w*>b?QxeŒc䍬T2TʼnwÂSrE$+ONfƖWZj͛S#e_ߪkѓZVXhƠ5FWRhθfBh@oHg\0 Y|sB{_gz/rՠjkӻ}a5OEq^)Vڳ0Ԇh^*WtbZ RDO`Xx"%"j 1x &XQT+TiҚS#eV˷bqe 0+٭@XI ! -]KM ˔4m ebbC)*X™Y*&)Q851y(;14?r~` Z7]Vkl,vrgHBMB(=Pm_ \4ڎ┖,S,OHsGnʆB ~78<؈%r&*i sT<|lN}myXJ,ZmIWh#>DG7hN#KY7S܋@Bhİs5Z^!Xji %o/n"SQ<:b(r:)MZ{~ $ s `*w@pHTFA^臵Dh_B" i ',M%ˍVj4oNe˵/ 4lO0lп/FmO;p̹Ӡs.A (I8ohB}A^ ^8^&MY}#.j рi\1E6I-HT dlc4cF,7J ղ9-XqLNk,~)G4_r`oCFn$]S!W8u=K^k5T˚洐TX͸7БǛj?gGr)@zu`wIDk17Q+/`DfF5L- 3`˽9-DyG2rxK^!)7Z5ysZ>?eF)cdDt"qk6cAW2|@2*ZRRc/j2DeN6#335ƩHpAKF ;5SZVzAZs/D#e pad9Әќev5'=M]- =ۧLތ,uގ?JUҠjNdF%yU1F7Rn3 'BykrP5ikðiOFQ#6daG$`Br ~lR(VꃉT7Yhw^,ݓT4b!'#ҷ6F5+N֨09ȐDivT_ʰTA NU{Z˸>4BީC\O]/YX4ua4.Bbh$i,t\6{[Cxqq:ɗd@3=nIԋ6l\=TM͢!_.4RV!ќ^>UC40~0_Af-6Qf/RJ8 Wɳ"Ņ#g>7`I|Ba ( < #Q\!{d?:xHRW_*:yI7Еd_\N˖Ǣ.@|4MbrpG5>F4o`.VGOHFbdMJsL`l'ieMASr޶ ug03~L1=H>ZfE8hZ#B\&+Œ#O? M,-sq{ijQ%ղ4pbL4 >DˈAc R)G|mTbj> F"@ĦA" FTp ̌Xhu0Ojmu`7ueaBh؄BH [.Ign8B?6 3i"%0ˆ) M@ZX#%URNː횵YpF_"faH1G^T`q0%!HBMHDwªXS1pb12)5`PAѰ6OR}"R]'\7sTztEP³>ٵܜu ҜiyU';ɒW z+oQ5ily4un#ii'B(ㄞ [g9 რC[7>7(Ќ8<c!2჻xD2)QP-3B" c9m@/ҤHɖdqi-ط/8kT9<yU1ߜ2AF,7Z UYi_pBQRf [A?0v؉ aTqhl0B"%p&qk'WfXrP][d41.٨/ dɵ.XU}06o^a[7^hw!P#[/X<;/&-IXjcszZ{e0ʷ#d^oȜL@i%aP.+15uŴS!+\S90v#|5b9բkӾ 3K̹x/=јCXS8pL._]4fSFkͻPR MwJ`t^ Љ5ft6aF, s[mLN^rc+a̺6[~'35Q)RmZXk^>,nDqkrբkӾ4oʰ?Vo,7v8\hQq8F|[*sh~s*N)"BQaaX[T#%h?TpWh!-wgoS8YbSAXiY~}ObFb0r>bA4'"! pjyU+9j81z&+RӡY48 -6'c_RyN9A= w  #bf8 ¾HMJX)443,x#aF̚6EY "# f)B3d8J_" MfvY9-$,7a'X+Z UYVi_F9BctҞ/s a0ժ1VLXZBc~Sf9 gS^| #sXlZTc z{Z2A1H)Yb$z1^3q4;[@O8b{1bzp̼Tt-N-,yXHiUXkӾXSf&H4.5Ǝ썥:qtElbƷyJ ȋ#ء]MiN IW~Uǁkv9a]#/"HZ ղ9-$i~̤r+` YUp)E*WA 0=T2DGnd[Xjq)yPq=5ͩk_&ĥ; /~ u}1:0yL+Bm:6MM5i-bE/aDq2HklٜUC?k.BMLEW@8\ hhs& (clLħ2 @),C+l6ޢQ(TXTl3ӫaiȀ{̽ic.Q} `D b]QG('pZyUn\.^'c7K^!ᔻƚtmy͜ iq*r~W爖ʼnuI[i ݃l,P5'D\k w:狯5bj-($G6NJ3E doC*FQؕѣD鸣4.d k@RHt0-*^k2P9~fHKkĩjݜ}pyVEq;}Y|pNyBQ`mA0/N\q؛ ='ai}"%kK3@K{'ƮuiuLn,z c3=`Yl!9@`aeJ-Sبt j72n>paC.:2;:o7%Sc<́{U@ =Ee0gRrzA"7;t%Nf"h\ZY P"7jX(z @]1BJHPT H*6Tdݧy #OKX cGV0#|񕽮.ia)_At f02F*mD VZ$\h?x5bR ^jliNO2;z,- 60~c$k q6m68AYň>07(/~`̎1r⃲xc+$FƖiVYiJ+xIvs}Iȍ/X֯$*UԤL&umZ˜E(vUjlr8!ia-'{Խ1kay|ѐF%X7e>Th!Ǧإ",gq0QitI4DimS /h۳P4/>Ex2?JpRBCE>~9hU>u/h{A|ueJ1ZUuXwemmmUs6ĨpIT2-a!%XmjIS8-Dqx2A3ñ_5HkrP-[S!i%N8sTua7TRt(Jpl%6rj8bDVDzJC5jA7@@,6q2\F2dҺ pftϣ/!"UYcc`lB\i/#`7duDfD ,%@S)UqNn6|˗ QiƔ4Ȉgz`'* * p(=o9BbTI3'} |iN4-aCJ{cFJX4T׎?MɊBfa_,e=L)_y!=ւ}h`AziNz D^k/Wso91yh5TcpDx^hpo%.mgvq*;]EC {7 DEM1jQ ֈF*Sh^ q5adn@{߆U\}!ڸ Dl "cH̵`"Df=B"58!8W-jZliyu1l?{rc+yT㎆*38RPs\aQ|Jlq@ȫrMJ͑r"\ickrPuMͩk_Fs"g;{*L"~U1 %"g"VA 0%J*W7'"n*'͸{t91=гˍVCUoN˖f+ݜ.ڂ8 \ 7y*gh%oHDnޜ R\"^;_FP׈FZ78LvPX,$z.e3U1*x,\4~c&QAiBs#Uz k_vZ4mǰyZC/Gjv4uS.NĂ Y=%J"X'V.F ,jN<+RM5 WHʍV-͇Ӿ_Yc;)lVr:2E1iJY J#'z^6 ,n*V^9-$TFu'D˃S#{kfsZ<5zot-چJ@|PC!%LW_`"$`mҜ 'U&ɰ+[X#*`]X%aMp}yk1'/+T$҇DroN <ȏv9ģ@-Njʚi_*{j6JY!+ 'B(pc N9 M_@ωȴ4ːU/0r3;FE?t4#3 AڶX̍>C:7–(cUf1P-=W*+[Oq7yU1Ufˉo,-jƚ4HIE1zY!b><)f G$(%8f=/4 .zZ]>I#FZ %u10 ! 9H ѫ܃}XE) rMq0{\:qcۄ[W^#;J [FB.s^"Bl.9j(*>iYe`;J13B,I91$XqpsůypjrP5>{!,Imbn/4gLY%>TF9n ~DZTk*4ň,,944cFyVv)5k˂I?1fdw!<"ك艬xXy4/3S9-DyӰIK^!)7Z5mp|2q='r1juBVh.Z / 妋{h4ߡKfF]/'EʖHkjYӜ\gꟾ`t=dBE/ﳀAJch0iN<ˢ8KˉW©rUcK?{p{3̪pɝOo X.*&x~h'0DiCjGAJ\)ZNZGG8?Nz#p:^r Bd!AYma+lF+Ԧ%ôtyryq3ۼYNN BЁi$ܻz Uʥ/d 68u_/kBҧ!DK?ը?0 @;lӍ Hg۟q`'fbR P#KG|QhѴņ~hamԜ:/$˂'ƴGBI.E㞘s'6]UŔw1bFTO`tc #4758|R08]KbZ$=ލdRg?+Dd!"Csyuj;9`DFCDLv6f!Bq/X -KFE6yXm +҂6|A`PWndÇ健U*½)b9l"25#Z{XG#7vnF!5Q?rt=xguAd6ǁDpwq֝5 ^n,$P彞ȷ牑SIk4*lk݌bY4_a7H织ɲ"ɸ.f\=\!N$YX{(U8IH˫r}+hNn{kĮ=Z-&$s33Xͧr1,숻l1!"S0,L$q\s_*9)'qiyXU* 5i!8iLbҿ/8\ci|G%maKjUkyY(%5͉ʫrqjN6\#8%~h~Q8׺GIO%$Ƀ(=ɃXjy8L{a4PfqVtPϢ=݌RDh rDX>]Jf βu`Q u40k/v15z$ث^;dD0kS+a怃6Z_*D{p1B6#R + # kY{, ˽rOhyќ2x#%}\6 =FPTBEa\6XE-p']eʆ0*V@LSRXE_ya tS0_8C^؅u1Uk2a=(vUh̞N\iypjrWZ՞ jnص[X4cCEHKHD~h6TxJV9=`$U s~ŎJєkNA!1`xr { hø5~1Dhp$l ;5#A"`RɨPW2S(_N./prށVCUlNORl8vzclЪA BWgs&9ZpmLnCޜ S˫rCe.ňveDwkbԢc:m~vG O# E '(ٛrq=pbT-KNFyjVl!1.=NÝ5Mg䍑DoFܭ%*R賃9gEŃd ܖFJ^k5T˚洐~9Xl$Ln[Lܛ BڅETbHs*$!T8UA"rA$? bѪ19-12|z0Xg#83a 9hO Ŗ-2dR1|i+N6e\TXOl}[bPm]:D;U>$.Ҁcc~3M;vpj`<AUj0. `/HR?ԈF*($,[&g{p9r0,DZ7\,Bu;)szטT[w 0nMv# mתVxqZEEKrVYiyXn*,!f8=~%y@7Ao)Y =ӥy_g!]hAK9|( ha*k>K]QXf KqLAoS|k ?+,GlӀ$Af ME{ ִ9yUN`u^NypjrբkӾ4hehFmf$C}܄N3=Ί'b" fnF$qUn0nOaT6Ē׈RCTD\3Z2mkpX->3Mm"ʝA#/3T|rĦ\sZHm ^m-Nɽ#N(oZTZ9=Ǽ21@9lj"1F *CDt TM_ aU1";..#'y5AQhhƎa3H[H{g'7X%2IqVϰbfZH Ao :Z^Ɔw1Zm7e$q|FJ\+5TWftt#+J"NjcQ]Ox5 H vOx-*n 4':z!- Z6'nh5Te hi_j[N7ƕ} SNA mD^˗ga]Nhd="ٜ/#F`yo.Zh 椹HeȰ܃ yq"h k%PiO'F x⍭^< 06DL/ɋ(:---Uo45bjQ[øiӿhF2Q`rc׻AžtL7I8݋S#W^Mqc&zÉ:~dsBաNiZG?";3He5'xqE@Q 97BáaT8;]4y[?7EToY^y`/4} \w!(MwA\ªGamԯnXN#/ ߦ҉˩+I32?4G4RV;oNO9n4~p |#9Q|MG 0&A`䵪9ȳª'FZ`P i#1G_FJ) KEMFwkOڋ$h%c>@U=iqUTO G Y4HD6:L>6mժDݾwvr۶ LByo,W-k0(Hʦb^H3HU~ dsyrK^## SZ CH+^XS 8Oձ}=dC"=bDÌa;H%A)0zМ S˳P1X`Jgy#FލjmMsZH3!F} \n,x{*7Cu<J+,Ua( Ҫ`qGxy'!aW mٛheBZb25 pZqVU@|޺<Ú6VO.HgWuQNMBFhI.F (avqdenxO6w /$Q=oOW8t$ρp-%Mؼ ňF7* Ѻ3R-# VǰX3#\7fs>m.N h$db!h)D,*Ryq rVŚI5TsZTcKszZ>OjV.z1rHR #,{phi 8Vcl}~zZdND^-zg{Jrbieʕ׈FE54}Z8`7: J@ai0 pItMvHLxajKj*YV,5 (͏4l0bXV-JBR%jeHK_ϗǨT} ?bte yMr0   Fza"e;rtqHb1HhbĘ+9-XͣƖ4H[rPs8|1ִzT}0kbN`9x0{B3 /N Q3ԥB$Q7o+$FƖi_攠pU#tY$΋cv=_>!Hi7洐Li 'tZ7oHk"#qz:&oy x E*fOÎ@\*] R5D^* 4<85bjʚiV|tF*f~1;>{aP? IƳu/_H<É ]!- cˉ9tY[^!6P-kBb_*MMP b md8cV9ބMt `nT 280'(.J*8ApyhVcBT: qs/nz8޵SʞK_ӃpFf4HrR kQx(%7fhX(|Zg"p"u;|NB^U8Me3BP EDxi!bCm|0zg]KALd}:(ޜR@ѭQVd+HuCiԃ#fǩ!Ct#K6Yܽ̋FybMNaν_* F(5*l]|%neGǵY1]cI$ ޯm_K腁Kńü4H˳H3깵)yPgi!/:$flcDj q}.& 3+0@gШSv|3H kLQ qӚ]kK@FҟXdȁAA&RҤ\!tȝb2 @lUAcF,V*ҦS m\\5Vv%L>WmDu#c_MA\8@nu69EZ-#.d 8* «q1J\pe\7{!y0yA#PXr#`48ޜhQ=r1>YXX*u+$FƖib5 N cV`cFپވOP'[ZWV:$_Dj.Y;b^nNe^A͇iS?q/z~{ʜ,w׼<q hi{‹IW9 TW=/'B8f";gˍVCUlNOnt{Ħ̚fZ>1w 2Fȹk^ JB'}̐eH[ziy3-A^iǖk5W&DZI0amcߺr-L7 Iޙm @Bw>DB" `l{^%o߂pl OF;xfi<-ks鍛ۂ?̈&3Ne6WEh覸~J ='G^DmCCN-/h5T˚洐ؗtYh얖X'Y16U8|fA $>>s^iF aڥϊgB8#{թ0*bYp坈YyDƮrM$8 ".VDYKnAl`$r_ ߅b)N Qܦ=wNPc7$EA|>\[:lk=wrUcRiO_z_7#UJKgq %0n@ڶ4L+k9' mDX ^L ^Niߖ: -C6ˊʅ' ;tyA;36=/Kؽy|6SkNTeaid K>DFcCx }sfB6DR~Msj\>~'w=T賨csZV z8A |tg/,K*b2yZ{OJ\#P-[څ8Lfz`6lp5W |қP1I>0W}<ߠ31U -r u 86'L=+:jƒ3c4M _s˰| Yl }raTqHPzP9!# DZ*Mn$8P ƼUZ'='^hL͆b_Ǚf ;ZؑmPmYЎIsF1|cCWT-G= w6kN QR2W,NLҿ#W^#%* ƚ4HԽFTgxs=-G b}ML ax\U-xi.̱ JЮ L|2uSZd-LD_csZHU2뇾RUxiW^#jb j?2ds7M=# !Et0F! KɝF" yˉ97^ٕ׈FE54}VE fApk2|o'C^c U:( %b.~f|&F0n ,*!y/FitiA$qyzecV,eCBZK#nf%Ṳ6qK5LFL (h}..0 poN *'U4õdָt29NfLMqqXgs4'Xv _|Փ6T(GpRy !\] ahfDˤWZ#*V1cJqոXi)덅zc_KZ_7u(8;<@93 S FRTH<jKV#cBx-c8=-L5nO35ȂI3 Th&Bх i#n$Bs[4Hea\Ğjah#jҜ\"9Cln U٠qyuta6(ꅠO6@TCũpc$\c/x9o5bj }V97"QSa 0S{pw1OOu`ŀ݈s.ŹT̛B{8cI+kAJAJ^jh!?TRfŴv /0a38ir(0w'Ϸ 'T!%ϯGG8rUc/4}*aBe|N'&P1o)8_Jk=ؑb^D^k*ܶʼnO5b9VݥP5i/Vn[l!72U./D+:u^}%S" H\ayڳ }hkĵRilNuyW D~=,sbcƁ&Z Xje!AyI)\֌,4(ϯwxg8Q pyh՘Tmp3v8BU97O쪬qcFԴ+h Qk,H.c*Th@8l&`rQC:` yGZݷ7_1=dwE&tK@z.fJUsZHJZ"FJ^kUXkNٖt㢲TV[i0yc,=?b8LutKEVsA 5LA3N_6,:ײ (QPDbd\Ai' ֭C]@Ҽ+¨CژcImj[; lΊxp48Ȉj'YFP&wUsZy.{ɝz9hUZŇ8-#>ʃ9?FqXCHM@Cj J%/SrS^yh^|8 e= E(@qAo@|gJ=nݭełghF-J۞ņ$0+MKX#jTH8ŸL.GbUCSތNl*Rt'-!H $8ZqiANlʫrbьlNwq0fszڧՆ?iwwhɄL@p+pc#9:h pc0Ttyho8ڸ8zXc+Nfl0!C&7IQW. zl}bHp4ũ+I| s\:yTA,7Z-9=K6)Lw잨tuXH6\Etȱ뫚:ҷ2O&ٜJ)[C)N]A<*<6 ~m;E&lpPc"Q2C yDEii 9 }H_`/o0 { Ys6Hv 3w@B"5;n_^%8./y®VNO2"dMo%'Ƹ\GyrZP_8ǩͩ+rv?#d-Njƚ4Z}9&Ve3e@+͸  7ı ȽvAЂ(" ރEຑҪTѨW YvX *5Ne\8i#@zDL!P!C8 9:ecgԙ:iW LsV}9"TNK"Xjt4o$N9HKux=m7b˨w03cȉlcRǬ6SA| =zze^nyUnp[0]%RC/F uoPSLx P}X6Ǚj٩],-%9_?p0TbSdPTͲ,d*;b/Z7 ԁU=!@vޝFoAniTCpGia[>ˈ^XV9X4Tv9-$ƥ]Q16Nr1ྀ`+e" PUAЂ xcT]@i/Cø6fðDPi/بڔ0.#AyFٮ9ƁEj*HI"..'u1*"bDgz|+S6F-7GZ#:-ۓ3m[nˢSf$CSg79#]|1`1?\ςZ l"qnf#b.*&bI2h,Fש- FX̰bteKRqk]Fq zb>)$ < bkf0BQ΢d :Y(*aHHct9qTw䗸wTZ4ZB]zRyڦcJw[g1"ƙ *y@~ ~Yu/ QBD!9.F BUbP)i6.nD<ס0.;P]8|wI /%Iwr&$C\A2l-Uu)8hrPJ=%-}܂%V+(0WCgE|DqS 1EfX`T@8954K]ܠfyaCXFLsaǔTH8Ÿec%nDjsxadCFa'fAkޒ`}Ek8 MU6uFli<85RJ#lN˖Wg;GGTF o0_@jvAHNj$M|%ߜBH0rCEqu%jSiΈ ^Lev0: ̀MȚA"5pϕ2{t%ˍVNObilLˁ67;tC߾G30rk#U2Mע`s؜`Д)3Sli 7탠B][}qo-)I׀ pEHr1XLF! CKREZ#2 `aXoJ1e8 2ⰝIj!Kg31'Ġ-շ'q"g¢軜[K^#%ysZH3tMH"9) +frh80a\T dߜ *'U&՗CuF^VˍVa)NOb5I FaӯYp7V+.pe^CAP1VGZ+""]È`TW\#~Q`"ڒri!ZW ?/SKO\D.qo o e4'BJScw81dP0HkrUah!9-$Zʰ =τBp0&$>3v&KA-?Z 1kHdU 6S?rb#N-{Ѽ9-[b.;?ٮ= x2Pq9<, B{v)&,8-$T"X{܋=W -5 thƔbSI_`Dzg19j7!~c}%䒝 Hօ44 VE*̛$L 0'Q质%ЌY&s],"`0e@ܸ!4 D3:5#f%!!tbeo7XũڄBb\8m {c^bnI OգP Kɐq}ȫ1 ܹҜ)y(FPD8VؖWHʍV9P5i!P#}ᙪ K72Xf th ~QfȲL#tiHIj}ʱ.v BԖ;x r6B/2ŸCL$ax [{jqUpF\rbD6'Gh5TGqZ[Dr6N= 1+x,B"6h@F$EZef! {S1ˈQ,9tGl6 -X1 $aź+p!0ҧyPH<]Dђ4#H,2zMn~tFEFI^--:'N`g~׌h'ӿ4^5̃p9x: +REC )/:\6ǂJjLiNOL$/#;!bɊi PV+CWu>ǣk s80T' rigTG9ɎM[)yP-kBb r4173>9/ v5JEC/PzpšOP\HkQn0bFzS" W^# VcMs[ھtx?!SKOT*-+4\|'4"XqaipzA" %PHBѳZTcM8'<1`15<=ְS`UTsmnN Fm*g'o/] IѪ͛Ӿt36U?_79o~Og f~`FW8DO4H˳\a+p{FȋAJ^k5Te/Mq=8wX3XF"l cDjhA C5&Bϰv8Ͱv`9ͰZTcMs[F'!Nґ8K9mkufAũPz`\ء4gXX4ЌPlCǿn^6`-oH kjҜqeyz:ZblX􌬸l1  07#44oKvi@av#AZ|=r?)qP-SBb{E"d cv*u bg.‡cgla$Gc}W\qڈ:D,_>ynJkNCUz0ZD{#Ȳ# bC&067:p#&kġjNSUrg860LS!%ˍVNORjZkYhm*Q4׬lrfSqX%z=_FyUn0FWÉvkh5T˚n:*]$inD/_P+{F+}YU Yʱ&0.ͤv0u9-$\Sr8RHkrP5i_6ʶuFQ>#(t"\`F;JpT8AIf4HrR Ntjy1'RhlQxĺ>ć[loa0NPR8ؕID">|>5vgzN0'hqzH5RZj4N}3i_E]Z M10Qdx(Ȁg@ J-vp c!VŚ瘽5J+o*-6e8=KMYGit$Cc5}h vZ: CC"8]DX {,80;h9 IѪ0z5i߆-xH ƘY>5 3d@H|@FE$ۦ[9BԌ.i:c;.׏[|)aPDt89 ƥ\,3ittL;pqw r |-DiLkN<}KŶCmDZ^#3 CeMsZHܸ!=wC*X}1>ēhE›A6UƉ FX#( 6XMBOjˮuUO_uRD QRHD,#19ɽFvwϗp8|R] |tyZXމ8c T&O $bOL|zɈXZo0F8L k〣"w:؃_3م^Б0E28G*}FnD[Ky&f(֔b ˽E &MmRe<-G3f0fW җ#RSR^(KQcՎ/(ؔ @a.iVLM"y8&iWaG baf ua;lb|ǴfsKQh/T,h]F,>1Aap}Ty%UcH$oD/X:\UӾk@ת׳fXAda"4 ]^T,y~3sG+ںRMM3-$eĞqIPh"NXXUT A405hUrI\ zeVl(W]Ði*[b0؅E:M҃†f!,.zM1RB $kxN%|˝߻L,ҫY,V|SC@-c|\aoc 3K@ 0>hqa#E)S${3ku@,Q/vHXmY1*sI7_@b:!Vci:RZ,f]9^P/ʹ>+$buɂYy߻NPj L 43RȣX4$$8`2DP-w3e 0P4Zםȕ*[cx.|ݔQkA!VX_&@nyo0+&c:ќ6b#aogٱbsibkx|?Ht(!BU4s&\bZHʫ|JOf]f.u[\nU)RerǍà#?IE;`bCx2p0RuNkJbqNdT2Pi Vid0̊# X|:l JPV3M1\ی8`tATc{DgXce1FRv"Lo%Bc?/HPpMK3-Ө1k2|cw"\pDE01qh!Hqya"\|+T-*qĆsKp `24KYH|Q*[.]6fh,$PfRBk^,ZpK43N3o,OqIjR-]i!ǂf>X=3 fGc 4q*ikg<}M4%벢Mn+:̈6B]K!18,-5o߬ԓR!6MK!_cIү#FR^T>GvhEFȇ-J5֥ˣ]iư{c,vaPu"VZ/b$Pz I8|GW& ǯ]\#&ҥRŕ{Q(X}P(2hLyD@F2'hupi|FΖkh5#<s& G, {ZŔG5GV^"%Yp" -=96\;sfZH/שz j};k%MÐSIjX蘄kg`$.*I?/^ isSa=UH*['b8 k|#JrҤGV>b-2XĂTMJ#T`tƅvHXLV٨` $GjO&3W(:nFbg!)|1rr8nk+&H=OSlXG*5Fǡq!ȯR6z=2UL|<ƃeLVF:[|#Uc7ӳ~jVUJi7A*0:XHc`tD#=™L Ai!)*WYSV]\1٘4Zf14XHu(zy\th#D]$*:ϵRARw y-V1(vZsl^He UL0 630:k4+]lz)UH}EIc !.S!g*0b\L:,ugp#L'ͨ24rѝ3@ad1w)AmU܋ XNT8K 8[A,O >erfa[^!7R5 y3=G?d!`T7EmXgDv^hvY8Q46.f. 9lT,$W&bK*HJRD$Mi{F3NGJ %"9c1o ~i|TABD|n 4?@s5ךx@2uaX؈44ꢘ IUhI-5s0ŞpU80J T4jh GU: F@.ƖTFaHA&&h2Lnr-RD룵= `j,a%bޖ$$a7d p*I{`mUcbb:АWMbXi-V4~zt}.Z2ҷ9`a7cYc{1y .*_B2^LtSi"SW+UcKa]Bg@,hq՛^ +Ƕ #s|' ũKa!WL4AR^7\ac}?Mo1H؃_ښ҇ZzcQ3!8*ӤN2u(q`"LjM7(XuS1ebxk|#Uag2k,m7.v\r&,3&A&ˌǰ̖TG6$bi]aeAX/̘YX "J4D/ TXdyTKfZH; "BF|BeaB R?׀  ici@_9`D!cΔS#;"LdTi}`AF/Chޚ2FV N! |W>CƂGzS n7XYU^#&Ւ1}Y%\+ˮ0֟ jMuǶ 鴎`H L$VT1T8)+RaUiU4HA6΅C |,/i •sARᅾ0$L7à5+ M"S#U^K5FaJTܼۖ<\XcE2.t g/:E|\XN8qazE`:H l@y 4iM. }eH|leIip%qD᜗Mn*l?luf:)U*Gvh6 Hu֥j\ѮVwJj=sPq%W"F6.Qih`&.s t kFj4ɼe`ƅf"#[q8g_(񛯊NJ E>HYL \+"bmzf+A=F. P]=4>fFvW4:G1~z!9T.%I A=ҼR18yI7KM/&,QWyLRMfifN4l>@`p+6IM7BRXeTsi<$.LlKnR.X^>k8eC4z.gck Ӧ"({OqO6u/ͶRReCf2]#)uβLhH)F7RMf3= G@ wvx]ѐ1[P$ Sgr-.`8JRܠˀ,M"B澉h1=WqoTKfZHR{"f$un #e&ߺ  X`\Y]U A9.S#aJy BփTy-դmi/ ͈U#lk/,ܬZ];ޅ}A:2RqҰqvM֩,L~]}ϻ]НojA@J M;H+xł $LH3ߤ:aB?j2qএ2Y^=RJJm6ӳ~it_s.n fJnHt`a|HRcRn_k&R*_c N~İ+$H5Zy,̍2$S. ??1p}cF ;Ǖi&'), }2hRSLTq-Rc~ԣJp쾑=V"$ar/}b 33s"=Jx1}ʃivjR6Y s>mI>&=`ijD&gxA\H'| D*[b**1ݥ5%R"wQ0}R.M9#D-T4֔oBBzY 8-T-| K Ċ!.]LTy-դZ4B_ъ\?w!9n6_2s0AB >NU>\&z˫|0!`צHu+9H3=.FC.{`X\Yyz*swʙ0RuaHqR H #[0|5IŇ&&ʹgqR!V #U^#U^K56ʹ. q[f!ZeC^3|0rPQ#V}v|'Uzfb眇rȰzֈLT!jR'2 TsJz1lyVmaxa?DhD;0R^Tt5@,&v|KIityٵESucѱa'pfFD &2sh-*Xz> :Z_&U6M3-j$ H=3X9u5)~j̴<Ƒ4/҃xU⣍`318$o0)#bfUVK)JJAJ6BP(? jLjc 0`اⓊoYs! Di/@cTS!-7!SDXt,z!]=[%׈F8O32~'X~91Q53!U.H4lAS0Mtf1S]4LZIUPs{h >Qջq' yC>^T`/q^(:'HK $ڥ,ӥK%-*LTKfZHlMgOԥ֔`sy=e/R E;C2k7@R^kS&/_S^*+FI5 Sځ>^-uzeq7!$>X.~ f\8dX|4SHT'`i53ѲcC>fM5pE\iH$›F+iA<-x]&sYx+JkN4*$ҵHwFp;QqҸNm- -)X/K?CXT^zL 򬳛g5RT!H>L ~YaI\MqN Me!FIB.L-T(I=4|e:.-  KazV.*c]T2-vc2j<њ1eTƏ {SS#䣡-*1M/:z)4!ߕjR-m~ g4Ӫ!)2`$ΌyC #Hl)YȨ {83 ==`ST"]^#7RM%y3=W_9!| "`A@xc(VK;p G5A^_5cTAX5`a K0a8t.s@jPq'~\4AUwcb13g`d+`Q#JO ,3b”wc,3`/>k|#Uajigwq%?%KAM+PX}u-kT#[0e,O˄oʣ7&l \jk$TM3=j v6 b/11'$8X, ebb rʫ|)kgKM[ab#5LEk2pAU)8yTHX!SEA,+|Xz8{`j|#դ*7ҥ+Cǫ]< eׅD H{D2W<\ƣU]j|vaEb2bD9C$ď&9USu 5DyUN_&lK4ډdT48lB"řmA-]"f 5zCIP=hi$XaaEAqXIDUT!]VXM%0UŌa@5~VZJ!u"YHn 5 ;&^ y- UIYmFnZFfz.:ka(59VscIT895!Z]}t򬬍?f!cl!Ʀx2!TiHI;kWavwt#;)-Mt&H5Co ښ|x@'% @V*C1Ǣ|쫰F̆!VvNTitq~gن=ǚ#޷P6ơ z4$HMF 8tFUأ5 d4p]e8pB_WƖ-a§WkESe}wWX 2ާ˳qA%BZF hN 3gAi.|ɰ E U^K5J4Ly\k<^>83ߓ ؇`/uebk>P< " &$l*[cS/3l 02|lnVm%"2NDxA녤֩o*\"{fFtƪ28_Iպ ѳvi%^A~?vVjF~p \Z+rM*7}.A{HU^t) ؒ4E-TjiL ~Z:OFXaKfiBaaA~=4ͳʪ\ɤEŃ5!րَ@+h!OD$enzP"tdU<Fc^O{(8&|fM^ǹ`|4M dm؍8wŘJkƼBF*c4H+g;z ϘX YPg)& MF- jB`69uX'D-*TGoRյ߮315 MvE4s>Ldȃ=5[^9  tSLu_bLHGaz֯gObLq0w9f5_ v>Aq8X?ٓ2 *`\ta~i>kd뇲wg1-$e@#WBFʢےa|LF{TznMm#O`zRPٜ[[Ŵ俬>2 ZTIhg2w Ym{W_0R<fAn兾I=`h#~ -rHi+S1-U³?c0丛@5Z9@"pȤ…M"Bpz0Rz^5bq & Ff&սq Ȅ \aI]rϚ* \a#**DdVL8-<>vz\9q;kWHT-ɇiTtWĐòaYP+`n6w`zw/ A`1L$` Bg*(X3b`͐"9WNu$Ldh u<Bsnϛ=5KV7kUTa|mf J$MDV:{tP:RX: G-Z|cv#iQ\Kzit"!>  `?|zn0% @:IJ\NhFV":Ո=LrikfH222$,0-56_BH1*_ĮK/Z0BR^TL.uNH5bjl`y_ZeJ@OW/=/+LQh -\oxm0LfZU6AE;a_=A#RIl#ӤChM5kU]mNX\mLW9Ӻ~3/Dxqg!N7 "]%u,=fTyޖ X{cab׫|`Bs2(ڝ"}-f!)l!bWԅS*9)Fղhs kr0g+fV׎"hd(!pEOᦢp7ˤo PL'͈=4rt3ɘ?~coos(DBp_ š`vȜt v -|dVY-p9D޲O8E )K3-NFHcbt98q4qX(3cأ@DY} 5,_0r<&V\ÔA+Uɼgz,m򃥷0UEt3]OF,{(>SSA/%{:A"K |&hJ[{Bñ"a%? -P8sgG>~*B~TWL>x\QBL+ըRDO(~:m.ƬAm[l4h^\gؔ2h[A48BB Ό(L!U; Y%ab'+$FƖv}._ƺzǵ4V卼0ZUod0bE\U Aǰ p r0Hʫ|3L2MF7RT0-]<U݉RN.E٠fem;QXRDPXi*/¸st`^5by#UajigI~u*ʫ޽8=Sn%R1h4W2&´agVL IySabb;EU^!~WI5 _ք)Z͋u iֱsK^RQ(+ 2>0y|ߤ*F`*_bwѾ\Vk|Wj)˔G" >C2Ջ:VO[D UIA@Tjo3r3`t6[)^RAjR64Hޮ^Bo6'DE2l̇N4ߛ'Ei @o4\ocbAVc(nǦ\SB.HŝUR^3rA,?p{za̟Ӗc0.`ыnW q4ebgTtߩ|8evR,LW= I2\9obKEe<"0B!<} E)7Һ \)| 9L#.*lDZ4Bgrt-3q$ѯRa+ fZUJl90"6%@wvLOX6GsE Vpd .լM0 -"EK.!=ۼpqfZHʫ|*a_(o? qRT#y3-]6`֡ eln91 iס'^B`lb5lMĖ Y +EBLQi\#ФDC4@,1rNDV"2*FTx.6?F5_d8Mb;JfZHʫ| eBYd[5bjR_qY:SN+@TєDPW~ȀMJ[qy#G҆ejmNyH0:/VTNIL!,7V:Ys]FO%0hZ3D o4LU ;jZ(g1e#TA9m]Yڜ>_Ii4fO,Z p-P(ڪI'^VDgoj[Z!.#–TEKw3̮bԣM!Zl8 ?wDW&4U؄kF/lī0x1MY"TJ3"S[1QP*s,ٜ635 qOi6*v@p'W~TA {3:9:u&̷,@F7RFfzqfkpYodM AXfnR;Ⰻ&C*BTL]^3KuLS#VfYlVPn>G tmMDDԻO6سq~HH3|| 3.l{+ - -`r Q`JHG oK2b[#rM*L4fҘiMyʃi|I5J_c+=SRJ+ˆg M~Q3 Ud0G2Aw[$EwRq@"bR@Dx) K-PjfQL)ը6VEy:?ў%_>_ Ԉf_:/VL9T9DFh+Ua9lMcY$xTp\h?{TGK*]C H] [r+ NUuVЄ UZ!WIL Xg5T聾ƅDa aQ,@ mh PA)d :or PhҌC3HiFavȂ e]Y .ߘL_ j(sE`_Rylm^1-$oa|2]^#U^K5ʹ.au4oתm0c>eɔ+$M%l"Ʃ/w0Sa RD Hi>75*D,mkġ( itAZLf#bmV`Ѕa9f\$7_bA`u`" .4BR^TnOc&fBWI5  +_ܶ\ ɇ\6L_F Jq[Ęi&vF4qW~i\3 V\-RcS6Ep|a5ZZa-g=aq(wnTa^R5&rti Ti9b1,@g4>һH 0JeP b`84=b<BRV䗉uU^#U^K56ʹ.1"D[w\"k#6RV*,0I]QEN*BziXuSQm8ڤ&tyo*,f.ѯ1z &f9njcɲ+Jx`&B50//SVUNM"mXqc/Pp5&r7B0-]_:6gpMler fX2p0D]ĚxNDLZ-f 3 =<`y.QN–L+-&wc7# -^=26VÇt|wJ0^ Mu>Ow2RT2L05 >NNU-/gl[~5*znPZ]d;u^FbZxG0*sa.aYh)MZHCĮ ?nqTq-Ta0.4HkYƖ\!ƸZ~Are*Wu/;R6et0(M2wV<1+jR]]$b.ն_w6*>਼16LM I}>+i.C{ L4T"Z@J%Aln e һFJR 5D[*̱݊1bg6xf 402r5j]i* 2P̶RD532Vi{ΓjL ԝjJ{31G:6Z3ՉqO#[Dv xaaF3;ΓJJDHiҰ~h0*+Rc"-0 R12RK &~Uec,Pq\RqAo_qMZ|n*V4_3-$o0^LcU^#U^K5FalXHK=@?|\Sy<{-}&!kqT**Sz5t3ik"8=GwLڃ2k ӂx֭K \l@M00eD T-*XV2C`j|T#`ٴ.M1$`$˕;y.ZyZ$tf 2hu6Nu;P/f/Zt-}l,xXbXzJ+eiQ>i f h,0/U*7dzzty05B_RTjsLu&/@Q4K|CLDC(Z/r=ⸯ"HdR}/&0Iyo0zL(@+qTIZ4B<46a9 dp Itk_ΚA->;97][o&:Wy$_eH54ӳ~ꚨxk0C8l~ V1L+Z.Mj7BR^Tl$9LF7RTM3=wz YbSϼ8KZv7L`kbzhH^&Iv JYCX.`QWWY^ lF߱nbVz4KA=k^hPL3@k= l%U̐"L,k4w8Cg0M[^aR56Lk߀<1\`"0 e\a% cϸbץ-aP2?ޅeWccÇ}8tq'2U#52ПUuF+[U`Q#UZ[[?ޘlXSsDNĒ*H-$U6SE4%=EF家Αhir(:ʮ aEb!weA^0f)o[邱Gt8vWjEćKtO{54ӓvÚ#ztx^ ʻ,xޏ)s(:Axia"7S!ʗ"Y2fԈXՓÓDr{`+c9]JO݊c>/^'w[;0xAdً|HkJOrˮ =?*>VmW?F .L*. THj+UNE`LWyoZFfz4zx~]3Lqg56:ugzNi])nblu2vyrʂ!FɳR4ZfGs2s?gm% h"X׬=sm4TSʞ*_º0R\=kIm6KM㧨|DAA^}/f2j;C&m@[vB$=Y̶08 QBqe*ę$K YTH1zٸ40r,n- jf`~V`Rf#ym uŜk(1U.暪5U>1!Ʃ>+u@v 9̲#V-xN D j~7O>eGsMw ݂jrXA`c3-HRnV9_n9hvJNK ?p ֭np? eqf;Ӕˣ2tUA`wUIs|uu臓%L,GR WҺu0f*:U>Sc0晧)/5|GhL ~˂ӱkL{2f3ayyip xp\Hm"THxrCI-T/BUq <244I"RKWs|dq ᘂ S)w' ; uS]tS\3Ue"oau ;RL"Qr~ďuLHՔ31:o"#`h[;V̰7-S^T|p11&EK׈FI5  7S H"\3s yjQ\%8 }G`N&pR4TyFZ-53,Vyd{&LH;? C1D,znY0Q,zNl Xnx~#Ir8I2gXʴ!-}6BriC\jW{~WI%$}%Y@_?B"u7V"豒B!];@??L|Xp5O4yxƛԧR1B0kXȘY$} ?q|d3Om7_/[e25O q}ެ7;c7[e~j+Cެ3/_ Z]\(DWyL+.=܃M{v܃S</Yu |:{Ձޔ{''R&7.5z@1Л:{s&F!~9.&wL}xu1or^C#fcors饤]kv$yk]=Zg۪{K$~z]=c?r^;ƴ  ޳1'\/3t6K^[=\4^]|?#"[y "o` pKcq.zQ~1M/g7{2~e^;2~yAo1c W;ދ 'Gϧڎ #'7p&3u,_$~629T2:^ qꪓo;_dpAjWq`&Q"'J"zUO<g˼.G/Vy<89)/.~ydXϩ_=.hd eGxU85 gy=Q޷h%Y5wfFw?]#;ʢͩIؓST|~kwr{{QO+ggӃLcgf}|9ӭwN5{]3jAiq!k>6/ooE~4Y+9*˹|*'77)ߙ gpxNwvŅ+y&+)i_O4W|oT+\9T~qg}Yi//LDdQ,홴v&g}i`{~#9t=u3Şދ40TLwD)s(O~w~tzۅx3˩'j'Z4􊙨F"o DƁK^'$7K0_?7_[+Iܢ=W}#W.GNttL̨:`fGkTYby k_"&Xi"IpbrѪGeɟh\Y|QgjRDxި]LN/ȼij8jntk˿Mr.A1Y3RmxA@:.zG^b>,uӂżEX``o1@݋ݽ:{mSŪU 'ws CLv_WUvſP whs˨~ޓxn[\r?%Ŋ#s?Yv]^vn^%Q*@>`/1@{u>s`Q|tzxFC;qKO.Kmj*^U~$>Лg8"MZݿT|gH_"=gHgz.QOe5O;w C|E|bow+ ܩ}Uā^K||mbX~rd~/Uk7)N׋$% @KQ/b>f]T_>R5.c+b<<c؋0P ?|`CHlt~$Xl:ƼW_}b}$IbyY$ƭGxh'ЖoAx|>hOˮ⩅pxmsƛ'2Lx᫇{:ȏ&Jы_b@Я?2v{~a$ o(5Hz8~X$/6{" m}skV;,9 {C\by3rA [i<@<jw3H8uM!D f֍μrjuӫS+TT庰Ooc5?\r=q{M$9?|X+byZ}IE*FZE*#2%٫O4R]'xffJ,{YB\s%慜-ΔvXD.qxc8P/Rb-r(kt˞.؇ &'7Okݏ ,G-@oskM<sO@O//̄gZyrqdzokݳ%3=F=8rh c=G NǛ@(p#o5o %DyboI7_uq·-9/eޢ!X^~i:C"y=̠xyG3h#y W߫ ^1_-{_ă{{x{mwFjxF%c2/蝺8"M{\_%oԯ zy˼wgH z,; x&779k?,y,X_xYtx|/mp>8(0'"<4R{A+o<@ݩ9"@oǮr4Uqh&Tμ1Ib=pHq?~\q_gs ]GisۀUĹqjŊx$ m]xu&ٴo~܊xSKc3||X0^u+^C\(Msr9RuQp_P!U''0!mf-|q]gܪ{2o|Fjnkg/H.đLFSo iXv3m3K|[g&3 ~6Hux(BY;  xfO,<ŷ1Kg^#%4{Ka$|otK|zƭzg4a|b-y =3a]]Lx>"OfF7X$>8,d_jxoJe~PKb3xl?(4a}Z=?|Ko'9x_cy`>|޻k]\H[]ؿzi.ܫF,k$ZTŵy6o9OԸSWえ7OTSX|ԎA;b߂~@ϝ~ ATqm"ya]\#TKfZڝ%vz%r~ZKl_kn민||U;ֈuis[&a H%-M|՘|V} p4S:wFQuTiziJsǴ>M3~rD&?:A|}%`Ŝ~x`a:/npsHH 1"g572qwE+3`ݨ֣<~p3|*![aR Iե8T]|{1kCtN4r ]tfW ΍E0@n8v 7E~ыA,VyT Ig蘎 Ty-դmiu? @1NE?ݮGyΫ(?`={܆ VPK4EI3WdWZyވ/עJ%5uPZefOddd|)Dzy 9fwT0 5MiJd.DvU >9x.OB $9Xo CҙvkqV z ٤VfHirkQ@YgUᘥ mHع.LIx6l3 ;LydZxkꌉWڎ{mxA4 a27ueTb^4igAHmjGBǪPU*.crP:׮zsV2[aѕސKhJлr8V^a^c褴LA¬wD7B 4n[#qHGAi!K7TqiA /vj4!w=cɭL*94Ჩ=iJ"?kJ!˗PH] FRoICҹ~j}rL~\"rS0rS.cWHxE j]ҹv5ܹ0Ciuy@Otb- A >q A ;:(mHU.e44)PQ"ar_#%yK5 JtJ(+ FBsO$.d≍qG!lmoBv=W)P*B.p=Y5[("Q#]мj|LaGa`w:rR)zա"5MK Ҕx_sQ7%x/z#Ք*wJ.~KM8K7<0w1 JfZP#9O &`x^h#~UK5ƯT[f( ?ћHc垊DU$7/| n!ί+l]^d70֎u cAl)^k]MM&!l_b_hCщYXt7v+c1]=y@t`lY *~;"҄ivV۰yX*s qJ25/BW|,ʒQl B`G[%/=G)NL<91eŔ j+5% >urXCܰ<=c,^:Y rGĶ7LY݁kQޔ6_Z{)gͽ1f8\yHL .9 &B`7UeHx`?0=h :XY4F4H+GzyT̜rRca֘hBFH_`6>0"zڳ)m옟G!$yg"ٍͮPJgEeO{:ɴ*o_"s5 ),=OBDKKKhR(|\HQdwJ(cxWN!ՔZ rVo'Gț xPU/VtS=@8̺,l|PZjAyj+6Ju^ W8 r]#ϻ*& ⁎U^"#mNJi{pdCEb׊[36-bĖfWKwtG +Dj7r7xՇ3'72Ad"*O@CSZ@Ʊݍ@1g 5ʴBj"xshQy>警Kez8Y(u\BGlDJnU~!CwvfXDVUJʝo=}#)9 1b'7c楞HF/Ygߍ%$X M.A7GmC۝,/ Y>jI՘Z4HWgB>B?,)Y(CڐcҞ7^9/J]MJ5%,DnvTHѕbyA _=dw)LjPڐކ9D+JEo!ůZ؉M\m;KnA0Nl,W]EHŃݓAI3{A ͯY ZX&vJyp"gT[))sAz8:&~Mtyn!Zc |J'îM)X&skjՐ RSʐZ҄6$ڭ.y"9,+4t^dz|4ƴ<Y@xRO|*f={R;E bdB%w mHάf^!نݳrf^=%",hߕ@!ѸeK[t@j( ljRHShvuɉ'dy\sqMe1JCԛ1v֯ZCIVbS 3FJ~UoJ]/K5`%Vfn2᥈p+p~HѳG{Ad7+~b /^$WCen b%A7_j_W((uo!cT m҆Dl|Xx0 ӡ(sO {mıEyРjJX"7#G BrC])-ؕP%rMBDo#։a/T('gRs|VXD+cp`"uҎ_իRJyAIemFV(RCiץy9C`Hly$U)SA j[}o)95TpZ)CYLN/H{Q)):|Zj\DŽXp,.ns(q-%?8͎$Z0K^*އ҆H2ߢq"ADBZHޔ 9 㲏c;cZ !׃O>Aӧ~nҠ8y!?ϝSݔ6DnV+R{]17B q+CF] 5M$dWaz1^b]QX7`qYSLT<Qt[t$]0"44U62nq~7f IJҹra6v*a"Y9OVӍ HQQj.PK5BB)^؛XFCnZv׈FTk36]~[' CCyr|K!> {̻_#Ti&mJŘUdqACnk Yd)(v)8j((SpP&BOWkadʯY*- mLl(1Y{ԋL>ARmft2E[/> Xu7]**v(-)#Qr*8^L80N[4Hz]zL(ѝǝ_!n2I]jӦ)mHKmw|11skJbehM2;Mrc=b?Aҁ]WBh#F٥3j+5CĠ>no^ov` H^ʳG`G #!B8 LiB4;m% G62MM qv?zSr< #kx즤0 ^}[%2mU:MffV,HȕB+\ ݈>KbFg Z)Y4x>RkR` |xw\{)¯M)MIS$KA5bj+54s2_صdU$R'nZ73νg @\'"A`-r%4i:v_u^l-P*;r|/5v AsJp&AѥV/J¥DF߰8%.Hk!x:a|p\pܯ`rkMBԏL`KcW0Xk٣f:4A6fӢ.,]Z.qh! HGF-uݶ#LҹS!T'kCk,ǘ# N/{'TnJtV!eLm*QjDoXKUXjMΩfv_# Y {6Db^RTExA0/4;@bDivUm|"8xQ?5R;iJm4 v*r6س",E(cq(Hвr™I5Y2a#B¬ Ff̊`BviNE[!aG0Dj,g K즴)"#AƔd$,{O)p[rG5m {)v~n!g E$wJl)~-Ք*mvJŴ3"7 ί [ R])SL(FZW^39;%\;CH!Ք3ۦ(4? :}OU%Ʒ7@qHKd@̢KUNhF"n)FfWVj+543ќ" @gk`Ʈ@%?Rْ/tQ1ȴZt1tŬER*MiCƬ9E:-+bxh=V s P\יSZ:@`AH2i(1K i~Uo0^tĖv~-ՔڴiJά:>ٔIDabnvȂ{„"Bll2M)5s?kVAҌMhS#+t5'lt1:^Zy~ ^pjS]VZg//}VKhIn:ovp+!B\`r7 t!v" `|),*L|:(yV؈ =Po0^45BE(5BRojL9G!/ Omcxf8wy?5Hsac [Д{z>(2Q5b=J6Mit~pL0QYX:&y$F! N_L%ްJW#N OC_#֣KTK>6]Y©ߞ+ɔ /k8T$IpsjARqbNytn#s? (UJzJ_'\]СIq~;fW*>iW|)Eng&L-5$33J ?5-0i׆'v*L̨Rd0ruB;{ڝa\pd-]Z]ֈV-̡aEiC䧍KZm]斿WK%JtA8,.Nय-4H2쾫\ }]Aie-oJt>ҢH ץvJǖF<)5/Jɇ°>cJ2\r.ځ8-!0,]څt=yI!4FKv֛R(dF],ݰkЦKڐfW1sfj񭌖Rq":`!NJO؍4wiJzK:X0ɠi~.hJBkKE|(mD?{Z7hceyy'Z;%_x6ҞfJx ԛ҆QғE'CȜ ,DFJ2MHm?mOv?ꉈ_ӄix[8 Ks8^#P@E&4H0VrlƭL[)uM\;;|/箶ivz;̪)ȇ7I>mq0⅚s&_,r.o q8R6MiC_VF=>Rނԭ MzM@A KUX8q/D~5uJ)M!C\f'A96~S1]WF'rer"QN>QYœRA҆5O$6J~5bjJ6Ja#-"p sâ {IL>`x{%V:KZZhinㅉ8Pl&tVVRo2Y)#b5)%ҔnJvC9l^N`y0)eG4e !T °3#-4*qQ*#"ޔ%ٍ9n ȴl4 ifgcwʍ`mʌ<~6J-~c%A9-+߱ƀͲRdvPW7;>l s iDZkVwz-hJx R0磮C k3H-vH~P:ׯ'7piY/#Nフи|RM9jDZdv#Ҕ6dߥ2\K45tgu/~%Q)Nǩ@fkځ2#5Ԓ 0Dۤ(v)b*ޯ*P*KfjGB*%;-1X}})ng ͧw'#; >QiKO J CKwX1M 'a!_|ָ _lA SzMeMS:/3}(X7GV&;>:A:D(mK~l<|Z+v 9 _Sti~;硞a$*| 5Aeã&Z3:R}QR_0Ӆ`U,q%1׃_!7R5Ɣ\3rͱ;AzY\KVN>d}^ӽ=d)~Mf}%v5߻r9و%O~k Z׾ùQ7?(5RZ)U6]Όe0Y Ƥr7iՃ"=*P߄6z9N*hNo zRNmBDyzֈ"QĶP.)Eйj+}+1'F  'ߝE+ԃ۴ARS*O2 !wFtГάܞC)i҄6Db֩ ňch9QNE9Q@9_#Do rV`lX3A,vZU=Yq.PናlY1g,-ʼnAkDI[p;HSRc6JL\985RZ2Gt_-hB.g06 q[;Aֻ5A JmRt0ZҎ uBHk;-tJ2ݚd6L{IPh_50W6A2A4hH^khMoWcxQ"wA;kzk%Քb]3:/3BT_ߏɳ60sy9A,mn\[@[TKS!дjb%񊍐 k]X> V6e떒efpQ96/Ƞh$v]憧y[ АWUT~BK 95`5wh mHjRc y+ <- 5pWو !b!l:,EkW+4AQ>4G)Go"M) HUXѦ).9K}$ԤcdNN%F9dkWm#iwN| YURnFέHJCiS$e*/ETn| Odن2_za%B]@^`r0ä,!.|5pQ*W BJ)Ҕ6iȉ[4 |;Su@?_7ƘRhPSw;*B 3i zoyX +/M viJbΡ]wrmAߛ kvY,;87K@%rC.8˻ l 4@9\1V6!VaGʹE}ÌVp~К,;4Kq!.d ]Uk˕Mp5bjJ.Ci%DJgF7,rgiù}'} ݘj&!a"yFGb׀F)Trs(:6M]ua^cE+~\Xq [EbЩ Nk+ف l|̽S*W,=XqЃR#s&E)iӔ6$e-b;ls +_C kGR[rB%eW39%KӃ]Vr!ބ6DvL4{ǣm,]fLx8Gvvs'r/0H[ o݋5VUSb?89)/GrGAif4e_# Cf( egcI'" zϷa/9OHג x%PDk$Q5V)wZĞf!W)7uYEcu*,hӔ6]_D沯UfE6L.ywP=D`u19l B Tjr 9a|auE!t?c{hmJ|A)nLqY%l42iD^ BiG¯u)DE%4\5FwHR\>tMlIOm7eBv{Mm+٦ ajJS{+N97~!UaXCi%+aA;59H-SiARcC5{v V( HeiiV#yu"O&UHKm d{m]<\Ҕ\=P0@7Q"˛?](~H՘RCiOc8,ٱ3Fy Ohf0rє6$^y(c{ivZZn{CIɰ+X!}a;(c*uZ<Mz/tŲd.CPv C%ofXDJ[X%"XZDs<[^cUS0aWCwR{I>if"(ݱ.fq(By4_#FΖ),Nz]V%FIovN\& I=xttiBrsbܘ}~-Oo0sX#) 嵼GsmB4/Pj+E ~MFO1+273)fܫH ;8֣E*OK_^]?.#„k"Vbq)l)B~ҬMiC@RH] ?RJr_6]_-^kC7jg~\QuKG,)AqfZٽHeC(-[U+LٹnT} 'LJ.C32qb9aL}<}c qm8#h^YC&Pt<ǫ{BL&ds "%^Xt65tK:3=eġ0Y=aBt9.DzF)wp.L"t*UK1jwJ=6~Xo 5F!!c`3}Sϧ c6f\}gyQnWj\,? H+/4A]UT4 !C!biJmH!tjĜ) 1v(3>Ђ{YTmF 0z+R7,ޔDzSʧ6J&rkz,%ՔڴiJ~Ӓ\8fE@KRڊd/tҹʘˀLLEAUn|Zx#Dƭسi+54sVxy+Nq$H_xLH '%J~U% yzd,[$G_A2 w0G .H4!4bW,EkYC/S_,IaJ`MI4v\k>2s{0DV|\$#WmDbJ`qG>iviY.NKNBy]o!tYL.Z:.:{_Egf\!#0yAXuH5D,}eFb)kJtܪV((x-l!TCE .J)M`ۇτn,ÖeVv~porˎ!&6 Hvm"`:|S*g c)~EX )~%’iJeB;Iv\qO)Z-f88_%Kg& Bk+ AWcOpPdS+Z|Y22 +2rXټKŚ $QV4(f2Dʮ."ӛ``7ŎiJ%u![ӤƮP[|(*~ gcRLJXWqH]z(I7Jrz)~; ~,/2"3-Qʠ}!V8|inXDJ+ZH%P:WL,iz81.IazURBz A 0Y)2תe+}!!7X^<(cm_!SK56m҆4%Jgf)g=΃ˋ:A0PKg|feY}e:nj .i.iYR>VԳ_9#AX|CIeyqGS$r4A6L;A6vjJHj8c_*;!tFdaX~j<.yYL+L}ߪ k8?O)2N jJl i~UO,兰!9XoR%Nit~ 5ԝcQ1,Q',OL7 tB"jSȡCl7vjJѴǠ.MiC[hXO Qq̎>~ Rُ2dnd !7_qaK8>SNL؂cFcڿL,G{'%Jl2Ny^+\*9>J EM)fsCw[ə.=FIcC0/Lз\v/f W RDҁ4?혁Ȝ6%9YvkTS SkӔ6$u/k_h}ʘ$$"!I7 Egvd6x<˗$N}hC'E8mfUm t8_VY*ΦE4t^UD )4X5f#]ԭj,y5Ema"i3`S I_ճT0>PB?ZJAT[Ѧ)sG5+o_1B`>QZE{~1Pe4)3zjz Y%S[a׀F)TT^K>]om W3県 oRC+ЍdSWOIͬ^+3ޠ -kIiT9M~y :mdRNTt6s.Lt]cmYk@]Fv ,@ 2+k2WCgTW !tbòskz%RT}ڴTiJHCZ.&<'A2GBGU5a@5eQKgŐ(qoIe),M71ﬠcFJ8"Y7z>U_.2;fhc,)@H-YD g*:#*K঱!r}LnLS4v[ڱ<٨Xӄ2 \F_%E`J1Oo? ag5\UZЊK ҍiJmH(AS28Reɺ: ͳAQgC9RAMV%}ׅ9cמImЇ;"SkzT}mҮK8<_wnj\E:GJgdе4$-SpIIga"#9%;]^UkN9eHSj)BE_Q堊e K &G]VV 1ԳzMnU=KY %UTSjӦ)mʯٺH[A\^!?B]FBܳlf]>TK`P-~R/=Jy`\]ը2i7Fǚ\RM%8 B)&A7U_ zf9)"Zw8\޺bĹ=4Fy&.mҹ~eh$>L9Ȝc]1Q'BaJ0^#B)"P] օxH7_iVJ]jn:ѬFScp6~D c ȵߋp'W*Ak,ox^_2BM;zv|͎sJiTi2|hDޏ8N< $@ | Ap5,CjJez, [UB Fʢ{F0ӗ{!N=[^}pʼnRSt>@z`k'!mUee R6`{j}N3*V-)125vHZ+YyMo70E#ƥZ(ByMR9yFb 5) i#<0&q6%4շkA=RM|( 9?wJʏtFF)%F>rJXj[! ҍІNN)HȆvm)nqJfѮ7M_H!HeaW6! W1kI"DSBiGa*'|qPB;Bl*J]iJ-]ZhyQټ zy}?^ Vhw/}ZDK^nT{9=CܻPB\: HM!t]lZ\rcs"x~rչ9Bf* &?I us>(l]|PZ:hCB;aĮ ܥi (1R9u۰ a9}Btk#܃hl='b0< -6qaHJ P3[`[J(U >(-di3aQ{ae,3$|"mAQ뒝 7HSڐzU Y᠋(CeF#QjisPZԢ7zXT,*(qiXxJj0Wdi?F4z*j7!%QtIj]h8izz;=C}xmƠvx~ؼ{آ‚Sc>aC;uz}ȋHhI"%Jgک 1Q0n+ځYT@#b╕ʏcG82XkB]Jᧁ*RFf֍TD_#ůRP;W|Ҷcy-A"y≅ UB u(XȒrG&!Hc?>2 ŮdB^&/V,:n0k>Gr=sh*a$≥rÔdxiّHQҁYH_BTEvJ%F_K5FTȡ_]!Y<1):J} - EB#_u[mZ~֫RbprSb`G[˴ʌ*Ejav219ˊ ն{0r=j!Ht/4r9S'DygP ^j%T_#èTSjJɩgQʻܐc/rA-o+v24-‹5J ;,FfkLdS~$Єp趷VAAŌkDf#VUJgʕIH?0 ߡrcrC^! Ry <فPڐz]-)5d61=F#׶ȹR M\;8縐slBl܎6bs+B;"ڭo$RypAz)E0VQ9W'ܘr.gav|!*),5O) #cwnZٲ >Oc `9Xd7V}Q^J!K9M^T%ilh .5ۺ ]F[Yp4K tħA."ˠ7$`yBXH47]FK /n"}$讎[edŃ]^]Fك6N&>pD»s|B",& .2|)dWq&p{^f]DCW.eqN/Q͎oe}H/KQ^e2<)>E{Xiew/˚o(c)n-FB˜"/Bc/}ˤo"/2Ǥ/2Ӂ˨/1!&}mwC1!L&ew {i. `a2&0{>h얈 7>nM}fls:taJA|M[ tg+Z+SHL`]F!']ԁ$a.`%/t]F#awО0S /2<[9iڢHb?ٖ."q`Oxo7H2^.qVFwK]+ :.~ M3pS"Pr3_\T ̱5Kn;Ә|d|hQ9%|)c%;qE$O-3wN™|xP!jQp|f FY-BT&ō|\FKY|xo#ֿ٨@hL09G|ʚKcxJ|!mq{dcöV2VިI['x?%s*^%)8A7$>v0T$\ .>U~\3StxI/'5uK %6E4$< ^ 0y=K4S";PYsٴghٻ"S4=T <̲{@zHG@z| !Zmhr.TNsHyG6HfS ]ET]f3󢯓bThYq{BH6 sbHzi≯\1ΈFpI𕏇Xd =q \_\=c|wOpq;(O;J.zHួE$ٿO^78lO=7g~߯"?7o_I6>{拧d쟿x^E᧏o}}x?}_={b{ o_CoAؿZr!^#*ջ6|= Oϧw܂qg |}Xܡz #bN ؂O^kQ)ViO${[ɳ?f|Q7rzV޾ֺ{>TFɣճW_ +?xWFhW1p\'6HE?9G5G5go׿?dO_|VIF E~f*?3Zw=y),wƀ_c)73 A_=|1tiY䊂Gd#k~bn?}x6ǖԿ1){ED.&w0O5tݮ`+i ?$o`/^m/F_bKJW-鿪ۇ򓲷/e[+M[29寯I^haMŪ67i7Vkkz0f}K"T0Hl5߿|c)kiz &y{Vbe!lO?-A<{u>wr;a9/KOxwY?~z#,/za9ȫ׵:?8i\|UӖKB}+5WUI-~?`~5~Jva触D@?42K7>ciJ'Klͻ<ϝ\?f![3w_Bpŷ[Me@O%?_x չț\O+L'?^ϟ].ysE /'Єݞ]j -{j;?M\ݶc (<{>;W5ku߿ZSg^?eI9}FOo3EO4g_|JeB;ɬ͟kɊA3h õ=/Z(RT 5~qD1Ǽ9Q.9ĭƐUw"$훏]Ч1-=Woڣ*"6D?{,Z,?Eu+M(־?Q?}Q\磢8~,Ebc&Ë<{?n=(zE|F!WO53t)?iUjkGZĥ}l5~ZѩNnEc-b獭=$?%eM|5=),\n>oYIR[86gy[CջZ-{s7;\%?fXw$EfQ`{oaB7Kp}e jϔ]h~7-a,]>273c햇e8^}[E/^ /#ߵv'iKxyn Ow69fG+Y:h ]i0˴ݴzyl9cڸYDW߽qc]fk-hktC7,ػœ_pL%~.5rs%2ͷwK;|v/< U<< v`||gz#^'3]O2XzF3aQiq o޽l׿ QDdO?zu;o`XcJbփ4.7s%?'W鴻WfsdGH)_7ݜ^py1a?$?fab!wK>?v߈ZvcW~&5k   ~S zߴ!_I~" r_<17|?:mZJ4,mBɆ.~աc1ehM5 UJak}[ ^^:**4MpƊJ||M_+)Mcc/u>}:krޟyg1/oP ߽eaN?^Wi_k:sVG/LDl KNO׎ڠׯ7 katYrp5JrRIUl_XX&Q%;4eoאC_k›˷9>^_wü{s͠{ɳ=pEN6V7#^5ȯ8/U%!J%08U?Ki3ǝUV)-ӿ/W?OZJRr^(>xVU uo޾ur_Wu_4DV|^ߩEZ53'S645u }xk%5.o]=<ʬYyWK_,"XG߭O,? F$Zrcͽ}kY[ 9c$/*ĵGpQ3Nem%rGD^3%߱C{KYڛ(ΘWeLCʒChO"c-sM :>{%,fAb䭛iпqhZ~B>+?'`ڭ{Na`t nͪ):W4AC}Iu@ |  *5)Oxd;*&ڞkNH`KLs1rHuԿsF/_q&o*Ok²oxX@ >t U \Nh}6mZg3tg 9K˹;vfn{1=?'? )_֖5p9#g+/oǵ?*[bݴz쉄)hg.}8YAGPlƫm-u(TSsSMٲGYC lVxnKYF}'v5Ϙ,o?kשj%iuX.sx%xJ-oп]`ϚPҬ8A+W dSxv-8ęCOBCFP ZޯSKe[pOCjTT:LWMksԮXCXuS|dV-.u?VU>ƿ|D/7v.Y:Fc.k/N+zW OMճRO: endstream endobj 5 0 obj 113453 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 595.276 841.89] /CropBox [94.76723 575.168 503.4817 770.1055] /BleedBox [0 0 595.276 841.89] /TrimBox [0 0 595.276 841.89] /ArtBox [0 0 595.276 841.89] /Annots 13 0 R >> endobj 6 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs2 8 0 R /Cs1 7 0 R >> /ExtGState << /Gs4 18 0 R /Gs5 19 0 R /Gs6 20 0 R /Gs2 21 0 R /Gs1 22 0 R /Gs3 23 0 R >> /Font << /F2.0 10 0 R /F3.0 11 0 R /F4.0 12 0 R /F1.0 9 0 R >> >> endobj 13 0 obj [ 14 0 R 15 0 R 16 0 R 17 0 R ] endobj 18 0 obj << /Type /ExtGState /UCR 24 0 R >> endobj 19 0 obj << /Type /ExtGState /BG 25 0 R >> endobj 20 0 obj << /Type /ExtGState /UCR 26 0 R >> endobj 21 0 obj << /Type /ExtGState /OPM 1 >> endobj 22 0 obj << /Type /ExtGState /SM 0.02000000 >> endobj 23 0 obj << /Type /ExtGState /BG 27 0 R >> endobj 28 0 obj << /Length 29 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >> stream xROHQ6Axw )vuYm[Ңgߺ3ӛ5œ]`鲙}v*b{a[QÓ'a?dy֭S{=5ڊ^-CT#hsM9s1F9 1w7;aYf ]%{w;ћ9 \Ir< X}I<>Uw(gRVzWOelπ~v{|u׶>UEP>,l%KTn)=J+vp,ZSk9xw"zmMWzmʨ)(ͳDf[xf8:罊ZIE?9Z*UVPog~~\?A< =ѯ tIsQIi!3NTc)[d@f endstream endobj 29 0 obj 704 endobj 8 0 obj [ /ICCBased 28 0 R ] endobj 30 0 obj << /Length 31 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xMHaї$T& R+SeL b}wg-E"u.VDNC:DuE^";cT03y|URcE4`λޘvztLUF\)s:k-iYj6|vP4*wd>,y vڴ=S԰79 ڸ@`ӋmvUl5`P=Gj)kP*}6~^/~.~a2 nײ0%f|U 9l7?j`l7"tiNf]?uhgM Zʲ4i[&LY_x {xO$̥߬S]%֧&7g̞>r=g8`候 8rʶ<dWT'<eL~.u"A=9뗚]>313X3-$e}u,gmg664$ыEzL*LZ_j_]Xy[?Xs N/ ]|msϚƫk_WfȸA2)oz-di2|m٣j|5ԥej8ɮeE7[Q|IM%ײxf)|6\ k`Ҳ䍐.> endobj 32 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 27 0 obj << /Length 33 0 R /FunctionType 0 /Size [ 256 ] /BitsPerSample 8 /Order 1 /Domain [ 0 1 ] /Range [ 0 1 ] /Encode [ 0 255 ] /Decode [ 0 1 ] /Filter /FlateDecode >> stream xc` endstream endobj 33 0 obj 12 endobj 26 0 obj << /Length 34 0 R /FunctionType 0 /Size [ 256 ] /BitsPerSample 8 /Order 1 /Domain [ 0 1 ] /Range [ -1 1 ] /Encode [ 0 255 ] /Decode [ -1 1 ] /Filter /FlateDecode >> stream xkhD endstream endobj 34 0 obj 12 endobj 25 0 obj << /Length 35 0 R /FunctionType 0 /Size [ 256 ] /BitsPerSample 8 /Order 1 /Domain [ 0 1 ] /Range [ 0 1 ] /Encode [ 0 255 ] /Decode [ 0 1 ] /Filter /FlateDecode >> stream xc` endstream endobj 35 0 obj 12 endobj 24 0 obj << /Length 36 0 R /FunctionType 0 /Size [ 256 ] /BitsPerSample 8 /Order 1 /Domain [ 0 1 ] /Range [ -1 1 ] /Encode [ 0 255 ] /Decode [ -1 1 ] /Filter /FlateDecode >> stream xkhD endstream endobj 36 0 obj 12 endobj 17 0 obj << /Subtype /Link /Type /Annot /Rect [ 441.498 201.31 463.416 212.378 ] /C [ 1 0 0 ] >> endobj 16 0 obj << /Subtype /Link /Type /Annot /Rect [ 183.14 258.471 205.058 269.539 ] /C [ 1 0 0 ] >> endobj 15 0 obj << /Subtype /Link /Type /Annot /Rect [ 387.409 84.374 394.383 95.318 ] /C [ 1 0 0 ] >> endobj 14 0 obj << /Subtype /Link /Type /Annot /Rect [ 256.515 120.239 263.488 131.183 ] /C [ 1 0 0 ] >> endobj 40 0 obj << /Length 41 0 R /Length1 37 0 R /Length2 38 0 R /Length3 39 0 R /Filter /FlateDecode >> stream xET[q E NUCp[QO_5;u(Hml L<YiuMqMZY3+C'E+Yniz0 ͎BA!b6p45pYQ("6nf&R&&V%lO6bXfK3#<=l 103@fFC5 _65llfKm 6~di)k`P+[@րҀrS ,I>Z wJJ6o n ɛ9 ,S-ͬ6fe@o}ʦfF`o+~ngB_ ]o;Uo+8ڛF}o[Yـ̬M,{{o` `m6(3Q诖(e6?`48F'p7)䟮"c4CofiX7?&bLlЛzsX\ЛzsqCo.X ˛=xs2qo"oZ`{3?d}s5[[X9(,l gг1ʚ KO= ު?PmxS3krGEտKds ҹj=-I%°2% sj9+n2ŧnL!m<|K m&/=LyQaᶥrD+S[sy;)X2:8W7߯3tۓiamӦ9娿Dg{̆N]3"h "9Oh0z&BMY8Nk:CI+Ε1'11jwd,4U(HVEEx 4Qd|zR-OAu(}F*+cdCcW.^rzPer먼xQRAaCG(ZX9UPxV5gI&@SdwMM)۞ȱ58H~JzeiB%qliB%a dg|a)z-ݝ𺱡M.ᆸ<3/[{MOGycS?bd.C0~ n~).+7KQu}T%곪+dw#TQ^p v+`u.=5"%gCJ*Io&ˍNDph7hH7?Sheю R:un%' 1A`/({8; Bas4%i׽3 @ #ˑC{JYehH]D梟r2>479=F/C&xߡElO%IIDiKS>qub ;;6L4r&#KM˜wX ;%5wFcXN֣\ 4x4xTf3?9s@%r8{1 'X\])* E }r()*=7wgj^DdnG+^᠄^+'z =,\?6١Z>yZ$j$zł]g6/t-t6u/04KL)*20 q:::M}~-zO2FbefLc#]*N 5yzBk;l5HTx,檱/rB+"#1Ʃuҍ06Ir5&c '4{U|]V:3p-Q hKv9T2]UAدB,bqԘˎ9?lB,V"_jYPϋji4{'vcok~jTvbpP⇮zHk')`{]|ؠRCmePp9qLo?.|!<|N*&)dh<.-h~ 1qQ$Rdai5r7$+b֛fǭy酬ڢr-΃oʋ\6jd!Q~3ʨeàaAWEڝQcVKl-dJW>6A>RG?47׀uw~dO%X4UwCmqtI*_<r7JcJ+sHcAa5~Qqr2;TqH2:˜c5ҚJ˥< N,$#ƧKиL|! n61/d|ur*#X2Gеl"n>kI+qs\z+#׸iJ 1샊x˽1O k oXF[tӌ0*_ '3 Թns%D+2Lț P{ԥA Q˵o6&X[:u3sz ŕUMO/^-?0g-YGqPdk 1kU*Ȇ8m½->xU;ntds3UT׀klE'SFO\خfavĨ6s?52xn $NԨO=e|0{=Ǐ:qPRTE5%x007HWCw_3k8EBD gQd պ2à F!9IxѸt51^1 t68y 6ɂ/wTA ~g[bI'?RFKҍ-'3^#y}mՒ,PLW-74鱁KX T"T8mJ)rxȽa*&ϗZזG Qhscݑҡ ՛ɨl2FP'բ2Fp>iۂjShΝp|aW&k&5/Y=/䋌[Wk;W.u'+6MEą ojzQsFDLaD*b#.N+d:שZj;DIĨ$Hl,x4nzqm•7u~d&o4.B!#/{1Næ5Ar7/W ^}$f+psh%Z8o9:;AXBH9Ak#MDk(cpA/fYS> B_rPpu}&yx|wtƙ!""$!a vJT+P;6ԥw vUdyqrm֜%GB&e,54[-Q @j@/W=DJj>Jn rWU,!plƿaS/b[;b8_TF-eX2]&3 |yC: k=8#.$55}ReGj&oCI7EX-`'!eE犃^㺞V4> V78jJyT0}JW^ @H՛‹)JCRC!^r>6e8fJM#}Y5 k+1a"cp6˶63>q@?֋E.%B,iXfڷ; =*Fjmndp'f]gKAsEC. n>"tvJ](.THލl6ZmdM){eчƉ?sd* W \66Tda>'6L4X;yDvs%%D{\+P_H8 "XϨd KњOe>dKBLuBGS /CY=7D7߉VEj*ĪZn4G 侠kv\Be! )(Ga"m\4#TOȂ2! ²ew1\Lde`KA$@|5 5-i\^REfY^Yh֢˞ ]8zxT*wD PQP!nP׮LE|9{V}== ~p[ !"I1~b jf}IG~$s4cΒI׃^,8pTiY8uI>tf~=|KMNr0k  B_480Cn|HPs& Hm$&Eb浶YKl1bךrS;{st\STKO_T6B*: ^$!46Y.rRX8^2MUv 2P O_s:q) >Ty ?cf^ސaD^:K9BM8{UJ⤬η4J9FDZ8 "Oŵ8LzH8"hc{<qWɨξ/%xw!V8Os?A._j%pj%Ǥf8&6EUj>;&_)£7*i2׹M˘w`ZFT|HQG$]ڵl ,Rk,TV TX&ȧOc,wY˟[;. }TeLI+tf;ut#űhp'k/ T(} 1T8J΀\ɒ[ع +EA߸om!T?:vƐN|*^Xs>C:puKUHOk }ƽ.nQֺzFݳ4l'q>+/( xмfƊ2c9[)TI3".Sf腪%㨉B6ze^E^sOM`޴3W$#4j؜\&ceh(+At$Ƣy)zsBb|jZο[ipPo1*m 3{%Y. 䨭\íCGCbrMWd]NܚgNE@EWyU_G5}?UǑ*t+WXNvajJf|<3z.jr#ɰV=Sb9< 2c0 R#^kHحxɽQ& h+ Uʓi\LoxsZK:f6")ҥb`f[o"^T[N.tqKxܕMwEğܬDE'Őz^F|ܵi: (>״ު-48m{eenn#lWK`(EK&S|?SFֈZDǵssdM{%4 =wsWby7a(c^;p3twps~'$X*NM3 Kb-GYM+j3r3-9%%v~z֔hܴq!9LxڍDέ+# zڔݹ4xw*ge酀aq \c_ʇDnJGL^ )`N_=D=OYyHOS #vzL3m()pmM%J 򐱊 5Ce&z_mIXuP J-#PqQ*hp\PHr*JV#RCSsSTW闳G Ft'}ilǰ7Nݵx>bҨS< Tb̒_·g)2L!c+ 1S꧙|RŸ6O;4j?ųii]7"i> G[BxzJ%VUV^{Yj#΍[eޭ?RXj6ͽO%&? ,|6&f 7 "_0}pV;SakN\R܂mK# 0#^_e%Fsze\D?snN4$cp^7iWIn d铔vz'$JpVŘ~v4xf!*(ꩈ/ZǙgkY0v0p2hG.\rl1:3usP-~Dni^|Ș_`/n_o굷@Ҹ\HsnnP 75RV;UiIƼ>p`L6=,'IxˡYՠWXaA D~q+~Ap uIBQPܷ=켄'̇l5f1J;yu4=M ug׏)< ߢyJVQ +EƷYд51ΛΡg7QJJ0f r+y1d*YO1dGFOs:5(?͗ ]X<)]*@iOeJM,7@1!iZ~/(.&|TNL{cD.CgL9nTc(#vLXQ[$^;# }>N3# $OC)FЖ }]jq3 ᧢!њ`8vc FRn0]Ժ?1䜷׸'F *JIRRCWgT2{վLC)R^`˘[u"x@};9 !>rO6߬~7XSv9f9!au2/]SpheLSV3}.).r_$m{r4<eڲ`$%YZw[?'ʰFPDT9u$+u><1`c %htIG%,$u ՜ÚIx NSk=ˇ=Ц8L]E>{k᩠vܼk|= gjř#&@U S l2=v炴aa?m^؇yf IGd<- #·! K0& dTvlOvēr"?]@.aWi!#BZ.!w7PrkPɾ9m~Ġ)sv)2F$+L_OցR#fiper/_<%hs2Y5]|aE@Rf~.l UVD(QbME"|ùЛKKݸ46O;a"w!; `w7Fd`kg2]iϟ~O,JDjnk\RTliHC @ oTᵰcv{;q`ioIiLbYv8F,E7ȣ髯?^j@nN+Y%ٶ]aI0T<@ gfmc7wrLPZ<AϊGih ~ -ʙA(K^"ꃅuYm28;Ebf_RP#WEԑUbLL*toe/%$yn?!65ɩhm }0ERhUDžpҞVcNMVEkFhzAُs)b6ZM KtJZV/wz6FT+L ,#;<2[&# X|e ;cX0h%8EF<&I -i3'[k%Lwy%=eݤ~L|=geė|>=Vc68vl:@o '^-md9/˪o|K>.˛剛a>iif6hmYObDߩLHc\LɆda8ݤ~>}BRL^1/)"k/ĒA3%ْ# l!T}9fFvPcZ BF|8XgKƔq6vIꜧ";16}A18sXu ]gwϔEx-Ӻ\W^Iw<5fDh}}-?S$Zu+R|.*#4!y$D8|#h2Î~]K|$l/i 4ԭ%&]cXa]jjkskICFDHUe94gyh<>o7=\7+©Pj#sه"5< ̃Oƒ+k-BlZ sqmAB8dAOBxs~mξD:} ?MA(\ ܛZVe 8!i$9f {gh (Ы]uFQ8Q0!ۗ jһme4Ɵ.$1*%8ZoCrAxPz#F 屐] K~#s2dHjE5ï0juH22cAY9ǥdYor};v7탦`%t:T9Z6 ӜAvlGR(*kx/T_D$FoW9`w{v줜1՘7Gxo?O'r`d 6w|~oo5= endstream endobj 41 0 obj 11483 endobj 37 0 obj 1209 endobj 38 0 obj 10664 endobj 39 0 obj 532 endobj 42 0 obj << /Type /FontDescriptor /Ascent 960 /CapHeight 853 /Descent -341 /Flags 32 /FontBBox [-199 -372 1031 991] /FontName /NLXZFZ+NimbusRomNo9L-Medi /ItalicAngle 0 /StemV 140 /MaxWidth 1230 /StemH 33 /XHeight 640 /FontFile 40 0 R >> endobj 43 0 obj [ 333 333 0 0 0 0 250 0 0 500 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 722 0 722 0 0 0 0 0 0 0 0 0 944 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 556 444 556 444 333 500 556 278 0 556 278 833 556 500 556 0 444 389 333 556 500 0 500 ] endobj 10 0 obj << /Type /Font /Subtype /Type1 /BaseFont /NLXZFZ+NimbusRomNo9L-Medi /FontDescriptor 42 0 R /Widths 43 0 R /FirstChar 40 /LastChar 120 /Encoding /MacRomanEncoding >> endobj 47 0 obj << /Length 48 0 R /Length1 44 0 R /Length2 45 0 R /Length3 46 0 R /Filter /FlateDecode >> stream xYUTP@fKS:`d`pnAD:F:DJ iNiS:Y\7s2?UD8=]`n0H bdLCC y 33$!!(P %`>Cx"a(` uqCN.n(( l8a(W fuvE ]8BP[0h A0g޿8!m\r"]c?bp{mvnp& dC`pTtVCrlz=y䞕=knHo0WEVqAп]0g6@?0Gg2{A^?yWkC`m/^?Wk@PHݛOSpAœ|B@ /߯` 자qOh@ }Ϛk0AB |'Xz m=|7$3N km |G؈HIOEU=MިE)Kׅ#Eqh%qrR :=JR](`譕aWnԿXGݤ ߜ6cc3(64aQ XV8qcŗ !gz1b;Q1"KMkDC>:F'R^ZhzHZ[ (o=ɼLP s\M!<ŧ\\PAB VAĒvdCN}nI5c)&Əc6C(֟PZ |M,NW# '"\k}jN;M''CGL;l 545̯8v/" E1j>%t "(KFG`5|>VX5721;N(?)\\|G.Q#" IV܇7S|DEuh(O I/ aʋ*BF pA!T%YEP2@ѯ_rr}5ok'Sn (c/>XfX~G'g qo*#,#[ a2c-Q!zU2ۏF:-{ `ѽk2QK /qr%(^_0ĩ}?R鶞M lnX.C`EWDZdZm,V+2.%S~wm=Z/sBk)!>R+ʖ{|DeȄ> eR%}w N@ g]cLd_z/+\#8>Dk鷶%侑T1 fJ) >C΁nlµks0kWh%T^"\ ]EҸeՍ<Qs߭v6vLdQzh2̮2XfQ&h)>`=Rv:[Ƭ1{؄)i]J'ڼjv"8pWZx.!fyA+&tZ Lh,zPZ 1U'R$Re X"=>YzŲS7Gӥ)/ngJX᫚2Ð 7:OПH:x=z/O܂RH>@h=_wz3*wR( 9?Mқ7%epjIP30VReDx41WXަ.1/䘝緌gGGQgI߱L irծjAW `/n_A<O&o_1aٵyoKꎧi_N g #v7] 3 &+1Qµj5eU(7mv3f_ P8&{]|;`1LٗH !^BfT h zYwQج5a@#$O@61"%CZ["'ӽ &\څ<=̺2ioBs A<5׉s ;2%S3u]y"sr6;aBpbwIF2G`Wp_$8yNJ$յ4Y+u}+pɕ&~ ]~ؤyЌCTռbN:9 /Age"e6x9PtW@8⟝6&Sg(/kHFGwí{,)R hDAYX>{l`k*\"cדY¨2rցՎ3ȶLWm p֎"鸢=ŀM$4!QG!huB_, 6a\DzR[Xa[,EmD7ҪR->JtmVsc G.HٴG4^Dk`ޢ-/.޿1ӕFO G*;2Łl{ _yv@ xuYl+T}Bj 47Gժ4^s$4e[1vZ~-|:,PA3pMiIhGlİm^`VHPLCPRn#iZf4M4PUM\.=haQe׏6VG˶q|C@8@U09:~1 qQ x)pPS4b+#j י7o}"MB 22Z -,|q(iɍI.ѡ-zBw KW:cE=]QIvϤՄ=4dMS]1 ϽPBb6|cJ=pO h˒a4W \B3}gUEP%3ɞ߼%?>`scŐ@M ]SBOCU>IrzC՝ ^{w3D^8?~B9dTD!^r?6=G\s|&34n KU,"8.VgNw=?DIĶemYv? aӬƠk4uS{RJӐɉ,u͐D~5JTL*g٭<1K6 7R߮A`Z@`璽7d⣦)ග_3j<2'Ǭf|߂ F_Ier7MR%/{tc't l.~ve© nMxȬ`O" v%{8n2 #z@VE&L{9щ1zݹ &4j왵vZ ><ghtWݯ.2 -dE. )9MFBQ0T7zlMғϪIm>\9|ۻ h[Fq8j2.fަl~dno_9kN]-0e흼g$)hgF=~F1H4u3u|hIC:E/ߴfpEeYp__Ά? C~f'Sh#-g܈!\SmЊYxzm^~\Q)gejTVMfIHxWS*oDr l eck ͙x6bWt7ocy.Xfy>60NanUeYҏ ]#n^ 1+`k)\C+)oDF2xE$UOv7WSdۖLƴ LlPb㎦\;{ a/B}H!m2B"^4ihZ/JH i䌹4~N=^y>sd߮fxkxc 0_Z{mu>b`$,lE6_;/nY~4$FYEeqlAEon\&@J8?e.IqG|_6W.k2wbZKڍMpB}fU#f S\'0,i0q ϓ^rps!AS.b]vO^lz4;k+b|6P>m s^pVyڶUZAC9G*{"9{;_Cx8%avZ#U/g'JM"6Iggh(н΄$Q.ZE^ɚ壻Y9*!ܕkR30Hg^Z?FV#zzR*MK9^<->T~e7+njb 5Z͟ qbU{2lD!'~--eE+T9BM7&{u)"AB. _&SsgAˇYsߋhB(H~ j endstream endobj 48 0 obj 6910 endobj 44 0 obj 761 endobj 45 0 obj 6286 endobj 46 0 obj 532 endobj 49 0 obj << /Type /FontDescriptor /Ascent 750 /CapHeight 667 /Descent -250 /Flags 96 /FontBBox [-63 -281 1079 781] /FontName /HIYRGY+CMMI10 /ItalicAngle -14.03999 /StemV 72 /MaxWidth 1142 /StemH 31 /XHeight 500 /FontFile 47 0 R >> endobj 50 0 obj [ 778 0 778 ] endobj 11 0 obj << /Type /Font /Subtype /Type1 /BaseFont /HIYRGY+CMMI10 /FontDescriptor 49 0 R /Widths 50 0 R /FirstChar 60 /LastChar 62 /Encoding /MacRomanEncoding >> endobj 54 0 obj << /Length 55 0 R /Length1 51 0 R /Length2 52 0 R /Length3 53 0 R /Filter /FlateDecode >> stream xXUXTݻAB.e%n%$aIinAR@J)n:|~y\sq}׻_6ce$q+h w!66E"!h8@_#a( g$jR\ 8mיx 2lcL^jDp=[X_BywGU6Z@&P^-E{y}tt ECL;XMn"}㧊Ro#s[Ђ㖱p "C `T ҅ zv˟ Z,$>^q磟Syymu3F-^ډ ;AX)\7i}8L۫ld䞂%="==I}i1RܓI#TB[N-/(tC0#c)'/ZwoKt\Jz>z?lo oSxqۖef)jA#ƃ )|6^X_ȟ / ÂŲ:loӪTgg,à'=aUF嫿qijPl%[cҙleQc4wtjgT >]00 ϩdv̻q<'s!69Ek[CXkkLxLUTn#9 ̲?OS$yRD:Hv)"}1ڲa$,g|7Mo9Q;@3^^J{YO6nV-I ry ll7 co;sڗ8F<7GHRT9HFz{T_yP#MՑakgO*宽)'RmJ;O!(8Ȅ%~ g9HʒC{4_']c M$x M`.}3Qb,Vc^G(C|ǟµkU0G|3+eѕlCUZ ]䝂X0I-* 7%m~'U:w` t AP ЕWI}2[I^ <Yu˅(u|j/TkL 7SD'h&^V c/W"?(>~si]{Ig1= AR}ϐ̠-q,^f1֑CJ) 5IbO tztxehH&̭`3ߨќĤ 4.m唣mdf܄~ w-zC`>qUͮesC o^G:~RB15OZ* SkzmvBYb[Iu+}^܉U`U&$X?x\BYDZWXK|V R0kt!S[{8GP:bOT8/=<;3 `䇍JGvU!-br[gJ 3 q=zq+ȁ*=Tю7qM 1|x_$utQB fsy^ D5L܊3X= rgX6FYj8ju 8ddor=ok *`{.x;MlH ss%cx낭|3 JS\L&{K N,i$3?)X1dfnEq`J I~\[1ʥܪ`dS *o5&(m=OI_]Zcm 9ĴyIb&ˏTwΓX1%HS{)X`'KMDX.ib:`N!$fg5|Ѷ`A!z/R8işhfigČG@T|C@&=^EvN'qŧc6v!TK6uq Gd2飲*T$%mǓxGYn f J~avqD߾'1p 'H"G+ݧzi:֞g` f f&3wfB_1$O<\~'?Z0ǜd*Ghk}OYhP sc}^A5'GǺ_$ʗ6:X5x2<qyQN~ yٚ>ܞF1WȧI/+?Wxe9;CEm[35s3 @n F,㵷oCK{[ZR6O2zF +r7;p=w|aIOY9N!}Y0;ao%d{q_ۆ$AҚTk0l!7Ұ4a&ewdKc{~t&< z޹kI0GЊLR{5:wl1*魵/b%V8onq#KqqUV,YoC`ͅ %L-#㋠40ԃ '\mG{ăZ^gf:0m}+ KP @1\$ȲY{;b-=}o>^Uo<#Ozo'I~ *:[%G4knhF%Yšid^h\~jǃa6uW#־:({MOqv!D]G#ֻa69G:dٚ51e'7צ<ɋw0Bڷ<!N>Ϛ67VQo&^ؗ״M̟`y,eO+=fUhΣlM8f8LSGG7跒jƲS,T,Mlg˜Bf}ZogֺG"Tj|AkDbE-N-'Oбj7ˠ0 _(; U7.j>cDst^%{x{j(<vFh4[n਍ݵ?KP; :4'9CGwVtFXD,(eiF&oCr=K,[ZxgzՉ93k81%yX48l1yNjge[X`r#25-ʕf k5XMP62&3R6K|~>w57[)Tg<>OMU*8Z9֌B(d^HLrq b+jp R*Ja~B }MXaՁIy=2?A  ]RsVc>pV }ֆv QvuBcbվVs@r_)+ Y=$Yuv>dP\+1T{_]D.M4ݽ')̀aAN+zKȳ}n> ]kȀ=;+o|2Ȳśm^>^ Bĝ'RQ:'ߟǩdep&ީɶk8 ?9J%ϕi}XҞsl1k};~gei\P[IӢDjo`Oa mP@%K7]UTi>?τn lkTo꓁P^Qv#&aWȗ wk!BĚ~#{Z[RSD#NeG3($gS{@CXyK*LL:/ uo#QϼPlA|.T6Oeܕԣ!U/boƝdžv3/4}N]ڄ^ B"HQfvwzutց%IRݺ0l*%۪SMT:mIH,DUTOGOo PHG[&7w&0 !b=ƃe HKU uiۡ&bivuǹTf,ep I) o a#c R> ڥIYz%M_/zMD#qE^ҹ:ť;fI}::HA\:oMRfgKq vُ[(E-JD/̊.[46\:iN)㏕ur<ws(PO)/5Ȼ@4GQ?G'.+q~ > hYD^Ay7ӐѾǤvrN7UUhErnFRvw }zl|R>!_ p tId endstream endobj 55 0 obj 6962 endobj 51 0 obj 762 endobj 52 0 obj 6346 endobj 53 0 obj 532 endobj 56 0 obj << /Type /FontDescriptor /Ascent 775 /CapHeight 689 /Descent -960 /Flags 96 /FontBBox [-60 -991 1147 806] /FontName /HIYRGY+CMSY10 /ItalicAngle -14.03999 /StemV 85 /MaxWidth 1207 /StemH 40 /XHeight 517 /FontFile 54 0 R >> endobj 57 0 obj [ 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 444 ] endobj 12 0 obj << /Type /Font /Subtype /Type1 /BaseFont /HIYRGY+CMSY10 /FontDescriptor 56 0 R /Widths 57 0 R /FirstChar 92 /LastChar 164 /Encoding /MacRomanEncoding >> endobj 61 0 obj << /Length 62 0 R /Length1 58 0 R /Length2 59 0 R /Length3 60 0 R /Filter /FlateDecode >> stream xUT$ͺ4P84CӸ;4ҸKaιg=վ9nx"229 'vTr213rDdhdͬ ldmd&Nn6Dro@}G3k}G 7 P~Z:9iֆ}k#h`c@ з{w#21 @3kD9JZld_]@{Oտ?Oodcm0sշH W(K}miUҧ btwMSO}./}XO˿b>]ҧ_tX>]ҧ_tqK./}8O>]ҧ?)ccl:.~ 8etr:&g\k/f 7;77υ_IOGC+32}M;'G%oe>jd`mz$ R +b0u5\~ @~@nWLe`ohtf_}oA6:&vN3's/cp1z EϷ}{63wϭ  4D\Y1 0OIOuQkibu#5){Q6|f6XX%Eː$ 6:^̶%U~Bes%߶H ;9a&GXi) &/WSǟBm/Xuc bv~= VW g\c!1ZߍG4"W%ܮ\0L7fVV菃]l&=;YpȻ_3Ex1e 4Fz-E`QiG4a* 4YF &R=a:k]@^s9ҩj'Ci .HE3ICVB~D͎3.RҙI9HCˬdBL&7ߩRt˻CGZzcܢ@jƖF**Q]t65sU\- ֫Z m(f0lד~T\ M~J1B((ӊ*}cۂ8I9xob{my ~}vPlYXjusۦ)&.UټOt^e2_8dUDD kӯ֎fCWp/&S]'ߜ/'9IQх "˪$aZ*yIR('NZP̧Tk}cte;=^ߘ? Axng(+Ʀ~_*_mpD9v9 i8ݜcԙhƲ0V)5=ZgDSlz90^ =DX \J;yv׸fVڰLuh^_{p 7O?x a!@縔 :-@M䒷v0s()}:$)Ѡ{,aF%&ĔgXVUbM!&gԬ9Tq$lBN %`:muU[VPX.,! r?C{ưPO$f*xKuۮ7jgkdl 3rA-+)`Jd~s h:1$[H>SQD; z%;@1qi͚wXrXum ,i1a.b<_̜Olz4OuFSȉdM% D2/z\s>]pf+]ˆ,~{8ٌʘx.져uwՉ.eDxVzgO VŃuCV(F8 BE|= %A2T# *~h*]a̖hz5S7vJy[$oRW}rH{>1èŘ5 )|[cq HK2I]V},1cYo UId(5)PRݦ8 =fn-}Ȫt`->[fh ڀiv*K#ޢؒd>H*gW-jR}߯p[ nF6fr3+ܵ!Vlt>. ƱͲuR$6ܯ-Ȉ*9"Kї v Kӗ]9t4K$py kWՙT<Gv$;r|ve(ĭEmE^!θ',ڧaX i _@Nêm;q~R#ͰhvSy7=alUJտ[˻WIs8S5b(+МbSrJ-)aS ĻOfoܧVh1KiD.k;1|}doD4R mm5Z ^åahTnQ%VTS(yN@+ ?'f!S}TnM:8z1{PLMKHυ|ƍf0" - b l1᥶[*dz#TvψkK)*Z~_NqL[N$V:+yp/(zw-I8`&Q_d0q@_wS[P0 rҢTweaWl\q9Òo>4I-AiaTpzU]QT('6X3a>,inŮ!.~Gn4>&/M3ʪ$uh.839P,SƢfҕbeZ"2iGo7_LAtbsW8P&tDZm l~W&Ee`!Db: p1 R[V2|SSƾ/9_5cO=^8.@QdԵU=Ve~(Xcю9E3 1RK@/[:gûC.d4k pۑ\1f hO由dNA~)\,c|\TWn0p& ;dІ,2af`|>7r"y_tI9J>-c3]<+8R f֋Nch7=/6T"]cMWΦjcB TWt\S UUMھ60}^**%~ 5pe&ՓnҦi%@'/H"hUgPf4u~-}M\!tz7{PL`nځT"Λ/bog?L:;=q#@jjqr' 1{~lDǚ-^Ұq,N$3!Qv PFį KW}75(LUhtO=mƷ&kVjOq)m' 7ԶJ|6Ϡ2}U'i"xk*6P %vy?c'Xik2N_HXrXx"MIጇ1-{tt^1{fr(X Sǘ\r{^];:_j]dWh=,m@[GX\oȞYlMCrmI׋eqcl.c̕o vMٛZh-5oW^*6!MoJN0lp`M4H2>'ɷO?t ^l+,+n)^V;9'*_I bW.CGU{(5.I KE%*\ˀYtd1zjB//wCpH[!3ƿv/)|汒!6ZYSTh#MJ)Z@,6/؇6 sdDkV*]J\5=RwKԘ!XcdB1+ڰTKU(oʃ]k\dAtUs-tFJCI?eGGfOx2|O8oCFiƹS߷Xy˛$țsY B] dЯ3ai_lvqµҐ"*h7..1y4E>2\>DAӡA&>W4e d!sZPG(`UѶ6;d]DLFr޳d {ø(Bߞ[:/^Κ94BF-@\F&%TbFOjx} 7Gwb=j΁]T{ #^VHKU3pS-[z꼅ծυ[qb3H}qv^Os~MژQ3*Ux9蹆Kf)V.GI_V~bkItFFg2 8V )qEȤۚuo;tX oFPԦ> -?M23GV-l|[w(Z^V~ϻK8xI.04)p&,dc.[Y9^T;@SU8KnhU+P9FsXy\Y.~lc=1Y$s]@c38nˢ`qTL:S4$c|GА@"ibCoCյ8בKN{Juxʒ"5^4 - ֽfi s6 7#MJbø_dܬ#2Hy䏬SZLlzEck+q9==ȑ\% N`f#`UlP$ʂit_p 5 NA mQcb.ސh]3'],4 (>?h3}BrbDCtύ2E7D۲,ҵkaZ-㛶5 =\J- g凤 k#&WIJ%;#e.v-Y7!kc%Wh>5E6Wږ׌- \Y1z 4F T"@[HvmGw!wKQiu^7tF~ IϽL/ѸZ ,:|$I.!*+qiND1[وzJ ! Uni;XuV@GSPK[h*fU>ę}KD8fYZf MlnSoVηn}']"%Cl_^wުOlҞ$MI8#f'930CW P)uNdc3?NA2ƕ)Ntd@Smb{AQ[yUl1ְFhI+,pTId}xѩ}Pn|ے hǬ +qGc d m>a ^,Ϳ0Q0ea#W~/viPvp},k8K;YsÙnDsYB#d«}њ̰/Q\ѷKn9?DbN8j4G__G8icmHO$~x )TFI_t%\7s*J').,ZA%uJY.wuǾGy‹X:X8Gt%$ lKJyVG>8_ǮjD)q=zGW}yv xb VL7JRGD[{,wPK7P9uٽUوlS i}a挓R<{pMkK^^ܴ6߶,e|ĩBVxȑ?3tICDX hzmF?mE*Em?(]фA:IiY3k?aBc&$5~V KT &qLBXkSB9KFbye+g=N*WE\!XU,t/P#>}}Q.mkfd%%Z]Nes|of>8 @^J NJ,LRG*'M7jm$xfkQ*q +W:x[&Qne0Σ/r1P4 |R C= fԃ'b҃"j2:F]͹}=d/Q|e@3)0yʙYFaGW5s~WGo業@me;qkuL%R 5ˀa"%ϸYS3NB]G@(&:&;h;OdN6xj |\/.i"wY hN&&!0ђ}%4]?; q(yV k0*=S\wCQqXSmW_Rꖫ6A!/= ?cB l% -.^XjYލO |;P},<شT4c?:!Pwzq±=oqzYlx|8zYs?Tu,0l6mYe.};oA-Z=\q J'3'ܿMJ.5㋕h쟩6л, aNv717i^aQ`A_5sU8d u -sB$0"WXlnƼ(]EޗdEWRrEĬOH#dk_l´ehn_{x?*sf=NؐjӢLJdәU]cys˿𪿼0Ow[}U&n "WM)PW8@iSk|z7t;|yBf? ' .c|>CƊJ[D"L'xknc߆9l-6Ml$ڀC'w*@MS|}6lHxgP_[=J7L/aNf+O eYk63\Ty%/h8&uW)+5RoRLo:B\j(܄꧟Ph]H2OH./6~d^щ"c :(< H\{k@h'F)ϱ]l}S9mNP"$e QJ m'fˋ9Mt }reD/'ͱ"N:]=x'l0_٫ lI3&%b1*ԳdjiZUxn&LQ.0sbP*DLw!K玢js]N'd'HR2ZZs2&虡&%(~9d+WA{wS;VR.o-.(}4_nrnK4D ԜIKEh|'K9Wm3!$y@(ͽx,o'߬ySV/r}] /fK/ڶ}r ZB8FS!Y, q9)J݆`Pq@W$ATÏ.JBdu/PI=N26y4Fd@?0r:ծ<=ڛL>t4AmΡ%[h8aZФ9/la,ښb麖bPF(Qg|śj""^]G)~&a4))ta~Eg檅ZC[ۛGw3{Oxlc]¶29$Xt[;r/ 20#5|@IyVC/ŴL(Hs`6b]w[CU'r3 Gڣ &6k~@qiB(IZS֟6,M\l0Z 7.FohnɒOc?6S6$;R`% 6 ,b/dtFSk[9yqv. 0]tcSrv$3I ;P2l9dt4YMk92iN&y/[WA9"1iFz5o^ji_fƵcYp$яڠOT‰0cЊ3_NSG#HF.I„\˩8rT=DzL-x4]8],|2UQu] B{,׈|#[MiPݱ(`G\vPِ,[7[CF{z5޻*gCtIW n?V`xh@}!9_ٹ -hK18 h#G-jWM% 碽'Lf &/M.D %kd P[I2\AoGx K5xe3v%mp wi+#Um8E=?K \}uYӻ&'[cvw|Jy_GQ>IY$$$嫳tI` Fc \3F۰m,'sx7^; KWfۡI3ڍ%A+ DY o|aD;[EpUzS]aC 6_])^6j % Ȉ+t4÷_*2a˷ԕA3Ǿ; Wu\;OЗ`  5ȠڄR>Zyq}P7 n0X?"=ᢍICרӣս[dFj VVkVPLOb+?Gקq~Z"H:gUt]( kZ<?,{R=t#5ܗq]tyaaq*yb |#-҆~YqLU[:2|-gt7E]; h^O7#,huf#qmEWԜ"] ,1йm!BFZ[C.|0`j.q!`nVq#ppY)pЩ2큫GK O4ӺފOr#\LYiGۗw#Vl'0rcS!(G` -MW1mA7eW`m"˔ <1|L #ziC-sb𚌋s3ɅzW\yI6Fe7gmۂ߭N6J+ѩ% 8e2 8۪"AP ȯ] z"iȲ|JR5:mms 5Sb|naRl k=\ =ž*mSkO_ B?#Bm2%(Y듞JJAM[joI >I cGH[Y퀦v&̱7ʜ}]A֗A$64 }z톉7&tx]j8yiT%b]B'C/ w5ԉZZhtؕW|RDePT~%+q-X;Ho(ǤrYZ؛ X EZ\a۪f#\Ȍ5;3Wm 3ŽOO][{mUwmǤR M߹3k|ԋL.Sڰtr.0b3yi&MZ̅L+0bD^R%L9,8ABϘr{^xjphhWp6~SD&1EN*s VbKryT,=8=1s8k7TIclTY" ?QCK?@qM endstream endobj 62 0 obj 15683 endobj 58 0 obj 1711 endobj 59 0 obj 14642 endobj 60 0 obj 532 endobj 63 0 obj << /Type /FontDescriptor /Ascent 924 /CapHeight 821 /Descent -281 /Flags 32 /FontBBox [-199 -312 1031 955] /FontName /PEGSAL+NimbusRomNo9L-Regu /ItalicAngle 0 /StemV 85 /MaxWidth 1230 /StemH 30 /XHeight 616 /FontFile 61 0 R >> endobj 64 0 obj [ 333 0 0 0 0 0 0 333 333 0 0 250 333 250 278 0 500 500 0 500 0 0 500 0 500 278 278 0 0 0 0 0 722 0 0 0 611 556 722 0 333 0 0 611 889 722 722 556 0 0 556 611 0 0 944 722 0 0 333 0 333 0 0 0 444 500 444 500 444 333 500 500 278 0 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 444 444 333 333 0 0 0 0 0 0 0 0 556 ] endobj 9 0 obj << /Type /Font /Subtype /Type1 /BaseFont /PEGSAL+NimbusRomNo9L-Regu /FontDescriptor 63 0 R /Widths 64 0 R /FirstChar 33 /LastChar 222 /Encoding /MacRomanEncoding >> endobj 1 0 obj << /Producer (Mac OS X 10.5.8 Quartz PDFContext) /CreationDate (D:20090920233545Z00'00') /ModDate (D:20090920233545Z00'00') >> endobj xref 0 65 0000000000 65535 f 0000162193 00000 n 0000113571 00000 n 0000116235 00000 n 0000000022 00000 n 0000113549 00000 n 0000113833 00000 n 0000116199 00000 n 0000115248 00000 n 0000162013 00000 n 0000129880 00000 n 0000137445 00000 n 0000145189 00000 n 0000114070 00000 n 0000117595 00000 n 0000117492 00000 n 0000117388 00000 n 0000117284 00000 n 0000114118 00000 n 0000114169 00000 n 0000114219 00000 n 0000114270 00000 n 0000114316 00000 n 0000114370 00000 n 0000117054 00000 n 0000116826 00000 n 0000116596 00000 n 0000116368 00000 n 0000114420 00000 n 0000115228 00000 n 0000115284 00000 n 0000116179 00000 n 0000116318 00000 n 0000116577 00000 n 0000116807 00000 n 0000117035 00000 n 0000117265 00000 n 0000129329 00000 n 0000129350 00000 n 0000129372 00000 n 0000117700 00000 n 0000129307 00000 n 0000129392 00000 n 0000129636 00000 n 0000137116 00000 n 0000137136 00000 n 0000137157 00000 n 0000130061 00000 n 0000137095 00000 n 0000137177 00000 n 0000137415 00000 n 0000144720 00000 n 0000144740 00000 n 0000144761 00000 n 0000137613 00000 n 0000144699 00000 n 0000144781 00000 n 0000145019 00000 n 0000161187 00000 n 0000161208 00000 n 0000161230 00000 n 0000145358 00000 n 0000161165 00000 n 0000161250 00000 n 0000161493 00000 n trailer << /Size 65 /Root 32 0 R /Info 1 0 R /ID [ <0b177faf389b3ed854b859409e9c6d24> <0b177faf389b3ed854b859409e9c6d24> ] >> startxref 162335 %%EOF splash/docs/figs/surfpart6.png000644 000766 000000 00000330151 13261626263 017311 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATx{׵5|8,˒II>3;AN,Mr}{<^2ľ{cKJlID54 K88{{|'^vnݾ{8P8Pu^z\N/"bh$]YzW\ໞ+¹)w'+Wݸq֭ħ>]XZ D1o(*ɲD =No^w g΁R??~?{GOWVN yB@4 SQQQ COv,/={^.w7s8ܼᓹEDzciiqa !JReB!JRyUzǏܻuU*J}gû/}|;=]t=>E)7BTTAy$(RLPDAHu+ebEǟ/_>_X\^uEIĨUDTY]u}Փ4t]THw |WP7ɣn\&ٕ? ~?T }ÏoܾwGsD( Q^TMH$U N,//Sad(S5WUEDzN/ <+sssOR+>~/*8~^yƍ[+hB<(+$DQA"xU*+*D1(I~܁P}vOA6Q9ׯ:8qʭ;Y m18b-PJyTSBL(B+X8w/ƫ]9ϥ8O(7s!-}p{#xMW( av FbJυ7(kTAQ”7"Am>d|Ƨo!x{wqXs %O^q!󊲪 Gih$?AJHW=aA^t\z+K 8vyqHDPLJCcK_(ήaz98tK??c NoR,Zhey?1(Y^vñU|U/ HF<U-8 kf:[̛1Ϫcj;Ǐ?qk|ͧu2rAT)J1 K R*8 .t.8I)s9]LfiǏRA5L.Ĩ2-諾#V!{;^paXx:a?7/ AFE(iJP'FS^sv44$J`8J(caG/BL7ɤ)K `_]N߿lk8pʍGOWQ+9}Asb|vbzD); 0Fp+b b'Sސ.Qq {;_fG8\hLR)ęsB'"ӻgorx'=[qLDh\x>riJ$,Q N)"3J~lh)0QeE%e++(J19Ƣ'zN4pJq%^r2D !6 %ĈK_ ѓ"rL ]U52s4$^pNUQr,-e[\t,ESh$*($+hS0 L~F^k)N).^|EJLD!`D(|M5- X ۶ }PS^RN[^f'UN'+*bAQURK ??bo_ρR o-Ż[-8HBh&.P0)x|8RdtʃјlmKENm۠ae a׊cy]ָpbtC!&}8тB*rR+ۉ_ro#d!J "2( ]@7C1ղ"H Th}奅JԲ*_0"u5 QIDa{98Oo{2<Թ=lW!cb0BVL\D](ebѠA-DELL"CbUPtX !Kb䭰WV\!ɸE0hq;Lv2͝Q9~N) >^XtgOn?$aIQ舂(D e ,\YD#QhBIf-TU,dE#P(qBL:ee&w  QJD2S\mԅ?=x/~:w88yG_|7>q܁`]'˓pbIB4IR0q*I$Y7nBģNe|lreES93"r- *,²OdBޜ7(??^SGٍ{]L,-/>}P,# ƨn@+ )RFw !?wUq(**D(J2W/az%ݤJ*Wlur Rx+Wٍ[w/| !`ֱ4p.%P(GٴqTL6 L-A%n"ü&AƘ|A2DzV׽z ! NQ`V2[7(?;.w(>ݸqֽ U Um (ӬIM\wxҖq`4so Pdx E1&}  >/?=z8RPiکbm(]:ͻwܾ}F޽i7H$*$Feۉk:51S D-p?+6ώ.DBDG#׼$rzTQcMDQ3[E›xxҝ{ݻ{ѼZRh%9RT)@pS|M ٹ&2pDñX Y0s#Fc),^נw!䀔-zQH@(D;?SS0R9)+o}Q` XZq: FH1!e|n_HqӲtUdI3R0dA1`6̋KN V/zpx";B _ Ͷ`EyYC+#3͵qF)e/!]vBA'O}E)p 8@x"n&7Y6,11/qKA?Ҿ%Dz/,1}@BBďʯ !("k;p :^7,)iR9Z9j $(hRpxDTgl 3aYYqDÊ[$碈)CCWxe] .}QbtV]yqg=DPJR9-J.)/S8E-7&;T/Ϟx2b4őĈR8`*b&Fi M2* Oej%k2$閥ɔ7* rP=S&&,_At8GY,H5h2xo?򆱀FTBܐT#WfQu`#R K\f8߅VC"NEY@T(fc,404kJ=u3Y8įXD!(qۄ?7tF)T )(ShcbNd`0d2N*11B؅ nPQy6U_*_Qj1WB-'Jh<:sSyxc")<°Sx#R8`̈3 y0Ŵi] &sQ^;}숊dTbGȁ\q,9G[=$9ͷR|P'w.N \ץtLF;`X6鄉̈́,1ƄPL2 Ud *$n_(:癭hV*Kk2>?hK,0qeB3Uk ┨'ݷR߉7wC⣛+X 143=bSP*@SMs6A8kQ5%cRB1<"HEC l BJAS}F;S,dtE !"!-~BmE-*@Ke aJgN)ҥ+(nI*.ZX-Č!QH%q;U,+^53G$% pw'%/ >0\?k  鸼>z{HGsgBxm})?~7-CBgCpT3l% -wG!"6BJOSd&_.*k '&+00zBml.M%`~䇒tx@8 xW1)D )nQ /E\ӰR3J6;[0e?1ҧ%+ TeDňFlЅCm@Bavx2GP:_T+Ō" 1U'"RHfO)_AA3( k$)y#f}-h,100#Lq0>sKqGW>v>fvRAJ*۩ K?$AַJnF"NRZ-哒1{hI] ٰ%tO$ή 3S V'pO{"EF#1450 6{߸y㳫W?uo~qe.o:jވq ]X?egU RBareB!O`-uͲmUR0X"UETUQ RLe[з =.|&(FW3E"tp`o"H5[_wsSφ=5]<)Lٗs 7BW{$e:?zl(Ύ4;S FIH"1-nGjGb$)w+z774(Ġ _f?;0 11MQ3mˢJ7pJqyPwP"dEalKf'VD*.b1MSQn)&(eZT:[^fRL v2fsK?R~+H6-KE)bt6LJxu{S{z|ʽz6L{3[J6@==,bL*eFAil,Ub) KBTL-Dd*)l,-r[F򇣲g]OͰFkg~?K>Z2` BeL uȽ1źqĪ*3aĈ'P.|l$YǑ:4qC4CRPpfi-ifRVp4-kÍ\π_^}8cNH VhM Vh`V13ib7NڦzL gY,Ii*݇Y)%E.SҗjHGJXJl6y]l2-!C0kH[VܘlLOƃ:O,p\ʖk=M`Yj$1tA^7)')EQl1IP'aAx"k0`b Y:Hl؃̈ELE;oeQ$îl־_9^C^IK&Mz>y&nb'Ϫ]&ۇ/3J?{/yLuKㆣVRO#,&fxM3,;˦t9Gjv:MQ\,o$sP6Lgj2F(& ]YJؾAi_?]p # 40t;□k"FfBZbjWRX?N/}t'7W!Au2+Lz"0 sM(LP71ГB&. }8SSt;ϲDD(T6Soг&v"ct{}D~?]*Q[h];sKWo޺}GKN/) V?CLq yk8C1+[7d4鬡!j)[RcYAsbƠ"aR ]K抅l&U IAOOmOPerl1*l>Nub .!A &J|6_6O^l./؄4*'AVc3U%|&aT9RL&[*lDJ% !A6rrUĀED64l\eIrBkfډl>M;I- xIJ\1D2nj8򊰅,cDJM:1 2lR+ӯR>z8*,H64B3 jĢxQ.%4#+ T$SJGH臒3],+;[( UqEᩘfE, &񊦝/Ul43r), Ya$)+dHE ">ÌR)ЎwR2[[ϞooqJq|S( DUk6^f "=, 墅r%&Ӆ\kBm ׽I|#F$V(/TL#KD -3-U^( 0YP#14,U'FO%3alUi;;^>{v8R9=9pފFKnhlJ"&4+MR UNv-{r!Vb`1&zR%JeBXg2fB9 /,laT2Se7^(B>7Sb tYbc і.paq(ho;f;/}돖 6fqcb`HP t3,Ar:lgL2'&F ;;iHOd{g IDATttx>~N)?ɢMTHǢL-H]) @1S j4*7ʅr% K$lbD 1hVjXSq]M;HFVT Z.Pgb>l5Q$증Nl ϨbMC/YR!c UG?_pR*^1?jR ezVg ̤!ƼhaѮS\4-PaBZIWK#.GQ5JVWV^)DBc<3?^ֲ̓6J)a==NIcQ6G噖8<~͇D9ۻۓ^:}Fc.OWnAk+JۥrQ2 cOB>N #RY|9`з6X 骷 vT}%T@URXwZ&%:"lGE;/PKr;J"fcX f28 CQ>uRAI;y~)6}5,/:g3O ,(z0ٝӭ)/>}ʌ_Pr)ڇm&=Ћո;ȊǁlfIV4"K)lδ R鄥@''Y &*dszP bʊE.r?]J 3z 91夊uבYΊ|2gHQOIa?ůzB{[d xƈ~L5 F%IY+1RDVs?n`Xa=7 7N#|O*^aydbC:)ϡYd!v$lT.j^)Jи Tawo(CH앍dҔH}&<0!bNc 7?g ɊOG'vX#sR7}p .QuEExHQ1M#&afuBQ Z*R]EB=fL!chj(J옷WjF bP"A7#T ET+N)1jUf)` LY8W9?{x\N}30 %v4=zl6 G o*87܊ӍR؟"Dg*aPQu{ln ^hБ 59,RTTR1͚Qҝl"$z&@J䘔(Su!*jw0l/mjfq8{R> V"ij1 &X!d2ezب6_>;Z) J}EvN xË_#NJ 6GPf#zlUl1KnA )-lMe|\&X9I%0 5R95[Zlֳqtl~ ,'sJkj%sBBS!jds ųD6_ڍRAeJߡW(RES)/$՛^88޹r杻2H3-1cD*I1?/6̔<`LT= wTR!kU Eba2F)kegk Uԥl}  SDkmA >H*C& рwX VM 1;)z]۞>;mU:s_ Z~DoxM's .T5".BfYMYY.<4źm{l!e[>"d1`)m\ʪKfF4M:UrZiqIc0W` RDLmt=p^>"ȊDbNl괛`w?;8nQ֗I[G6J}`ԏM +QIl;D"Mf#wpdIl#v:_ VUįL.͕J\Rsh`dVPH2RQ/#drj9ljIvP+2KUR$E;N*&v)M$laxH(3ܠ׽LqQa&:%)3zó<~^N ̿/.B1.Ij0I bA_Z*TR$TrIʕjnsNy]:Ngobl(JMjR6M/'QXNGƢ*N : $4~a׀9_#0p {$ƦR`m8lw͵Z>eۑş}M_ᝏo9\k(͔Ƙ2R!Ku>l#@(j E"6Sl*H*ez<&lYՓ4T*{N@y |$j5tjfd勔yA`E\-$ uhD tH*iU C/[ijBLU)3 F 7׻^L[7(\=1d9\+ OV+A- P@y*|`6ŮQ) t I0ȗ+GɀRt2BXRG"S4jDn'Q*+傅F<@{y,HlTɧ tvZP%`r>J$Xp_R$YO3 S#Q5k.%}h}4?v;~1pJqx+nܟs (8n_D!vŞQu#Qa:6̎n)%-8HITǓUk4MS\&e▙8= ZX?vQD+/$X\elRJt~&}Eٖ7p0\R.wm 9g &e.KjVkv룍ͭ㽝nw)J)K?{(᛾8~tƝOlwᘬBj_P3 38>o:DmH#&HeKzKa(WY{vIjk~gߠԿv|ԷI/!Wo{0}KʲFGOb+(2q`i[Q~E(2RCt\5KVLyEJm4jW8'Ajq]5+]rqCl +8 lO1hqqA"ʬsn"1zsh*Ke#tZ`}Yu3F<2)FV(f)[Jrpdv2;LJ13 *T\*ۍ[ e7h<趪hHA)T+d\H!RغR A5N@u㳑X6lTbdEo%thSZժq^f7ܻQf\J( "ݏ@DShaf4vUF3PV%J:5ôRuQAi`F3a=g u(%: xpcp fh5 MKaxy[$TD:-v(5X,LT:+6b.AŲ%ĐZ鴛bAw'fUf94*y[)F)_5\.6z 2~lDWfKIB><&?1[soN~ }U)"43넹hG/? ŨDwOR9)Sûo?4#R0McbHSzQ͢tm3=X 'O'RCUۃndte<ՊX:.(Uw~^۵Zx5g:Fx2)UjZ61*to!0D)Led[`T BB&ނ#(%63"f_ry1ՉR[{xwԫg5c~Q_<8~Rx烋7 yvI"CQ0 n%2Hw=/DM.Aq)L T+`Ю(6ZdjyVmcw[FѤkz%O P"W7Zj>K"eɬh kv0%03@;U* SǂxhAEIQZ1`>XWeR\N/oLw~v'V.gB)QÇܛsy Rgh4D!c[TR61m" KƱM#2Qhexlrr4%NFi?G9VjkpFC XtnwVZ,dOʼnbn5j,Dz L(4&@ ܊39b}aM@DS9 Vf&ݝ B&aWo]>.Q'[ ~֋@!l!{i# OFj'x -T5MRf+~Q&ps8;*Rۖz>;ww6J)˗7ֻ^WBF(Iᓉ(%lփN";A60C(m4}P*KPít:ޚA'.}pRߍ>ep0%M\u 9A%0;a0 @2H pLl4S8 `,X"Ƴ~~:dTJqGpm>;lvKqJq|%l..atib' @Uo`ftΖ6v2]%> l^mRW󦑭t֡&o4VyF8|~lWKןlO'^[rx{sXHdPk)6ReLuw>bD14fcDlg!:}XBPiMNًZ6C'.sqJq>r歧؋Rv$3n %PmF(‡b7QA520"MROj`CKz >f1uG!{Fk G`bp:h(5~/xsms5kZ2LJz)1ۤeڲֶ̀.xN^O ,=X}5˺.Q%<6;USEשvFѳNOw{L*j7;?{pJ[w?vQE5 GTX%R@CA 8~'7yñ01"PTQA1*n0+7/Nv<+RstcsUWZhs:o&^YO&Iݽ:'FV*n<}qbtV,z;'/8t-fL]E6N0: %JM`[Mb6vFO?~p#pC2zm9e>0Ao>>|{o\ɥmE ,<}Rqӻ]n'Qloc2%I0Y&E's3b1+t 4dd[[fg0XX6ǣptwh8N*T֎9j3{QūӓLJJ`{Ӫ3A+b3Zfbn"!dd :ci~ѣNj^q, df`&]5Z^m8P{|9<;oݻ}gN)~ѓY")ХG_l;3 NE2(eG@&{õNmoF\ rX!Oxw_xbF^L؜oU+p"SZ^UQ$È^9k8!zQ]u J͡z\kֆZmrBM-[Lw&.=Wxʣť%[48FeC"3FQ6Hgͣ M6+NM&CD)6ޙlOz>Z_v֦v7AV>buq7nwvvO㗧{;%aMUKXRun:R? 6bs3lh"Ϲ0d)IEԲcqiyՍ d&HZZ.RmJӗcؘlW>NN)VVWW^_XZfh ~Dq}a{U89x}dM+& 4ʔ*%:$b>4IRLLYʴtVqmckmmkh5?;6{`gUj`{k|z{xM`{/wEe/O 66,&2Llgr\r =LhĔ{6V[Qҿhfnd{aslb 6X8~ūts}vX#^'zFݏ?~S ވZLZXT $F2͗آVʥbhT\cVnm}G [lL8ߝ ƽzc{wSԻv\xoϟ9zqWml'~>b4m$m sلe( !a6`,uf :(ᔕe"Jla-oZVFAۓ-tZzKP9~xK%Cӹ's sE MX^+&ֱ3+QRZTۅLckλ,:Ŕiٵ`ۛGG{{/~ۓAg8ٚT`ةW{;''wvǓ//??ի_gᨛIسMl6mc ʌ~56&6 S8pu])eN6Rv7[ͭC+kKs7.R9)c•OЍs<_XqE)r;W1=Vr)J2$Yg9}\ި4FLn.fJ{ӓý~8:>R?ު*a;{G;/_:9:u/=K\_;9QT*2yI4)DC®~P}0^key(n_jm =t;x<=<ݛlPZ.Wqǹwwww&g{a- 5tѽO>'{FtXuJ:}؜.G !*֭$0 YĖhCSx0? 6z^=NM6hI cڻ(ޜ|~rx/W_OTJZ3?ߞnM|>w:d6R!6Ylo pM0mKP'a!l!~(CD\t(58YE-DzhshMR{dP{uS9~=?zpJxpg.#fѦ Aki pqJ|nmc" +pcJY[[kv7L 1b69z|{2!wOO/jܙ~t2ܝVOwwϷR3tCO iیK  m%FE%&HVSJhl#a3+``ifo 7w'ۇ{@=sOd9~xx2%:b8]qxòavJ0p)g rIK" +ʴlKASv:hw5a(6l jrqd5m<˿ͯON<}5*Ơpk:\[×ihިwG'ǣb4-ZQ)a m3JQKBӌ'Ʊ:.z o(*F0+ꈩFTMF[/:ۛq1s:=w}R?E A.6j-4䖮}++._(}!ID6U#^nwڽZ7l[FjOZ?UZWsrt߼bg^-7{0q^ע!ٖeٖ+%J, zpzϜϜ~pPRNyI^(Ln"THضdh5k{-  UUB"Ui ^cF!x88rP)&cJݬV+b>e^^xÖst-hc,'K$pxAH-LtWTl@]ӑ9J"]NP4h P)RnI(n|BV*dR=:A#BRDQ+UÑ\9lm=wϾ[H{Na%@c,BIqp.pЉLDJu:NӹlCqeLE) d!*@ZJ># e ˅Nd<鲢jm"4EPDYWV_ќ zІ9ӉJt Ct 5?RTop"޿_N ZwЍфb͍,'P]F2LFER4Yɥ;+w뗿hR^^}w F`ws6H2S(Hз:8 bh/:N? 춫BޕZdwl02 5`,%fTCM Dirggg!@bÑY=WH ,ϱ$K3Nj=XC Hj>qT(1y4[ -VʅMeBhR1f(l&ooBˇ}I}589ZNF4Lu]o!k!w}?] d:K1a)TdFӅM4gYIedv(rlSd "7SG,)H5U]>9bt4 1%a`8drj;x$dzj;3 <3uMRXHN_Tu GL] jp Grn<+Ɔeo';2e eCK Qi&#PvLg.5*ZQD,fm༟/{hzq 0t GO, Z=[+K:B% n! =/ǿN`u c_6fOG vzhk4Ausڸ@4N! fJC#1&! Pf`i}[0s#$JѨw*[@ʵE`n=m+/}-RPGLVOw4x$PaPk]rdj6ASĀDP9|&8Q564ohH"` s&;[Ks g9LIɢ|8}Tхє|" TNV LJLyMRF@hMdTJ\:cGhJml7W@\nX^x&@ hJ`YJ#wWL!z9R߿ ?ůF"Q8FD}@7oFGn ҥn#)FXN\ yxD"p>>4dA0g:Z2?Awj$bftb(aIʼȳڀӈvMx'SR&lիrPe.xYYm'#s|R|)J%R@dT,o? ƣ}{BX$qFް@;l"zKvB^ٯàQ_8&C$#N.xV,W ;mBT<2W4U$2 d9tMq=IӉ0ӒiM/L{Z-Fu:t( DAʶXEGð jgCI-#b>Zٷ_Oއ6oHxCWHGX$uMU$rylOijat%v#f٬Sm;39`N//Fbʰ5~n1MuL‘|+tr9>>f~B6c!ߩ m|ñ͵Շ<\0s$oUc kl P3e[2R?g?n!*h^y.c:u8z'SR6&1+c|zQ ծ`t:6.[n`x bU\UQ/{rlPuLȪiȲPl&cSV ovF%Gq$/./ c8e“Uwz>3gBrs޳Td⁃— n=Do''(?8|sUDstG.zTp!_C?/; =D(vDaf6_.Ѽ3AGpJa HjMG8iO"IǗ5{<%]F8^A꫃~N&cn(dbΦ΀uvq>B&hl eOk<l?"}U1گ AhdwB#D&OB]{vdA$K,$ Pp(Y\]ETIh[Coƹs?8M媭x#9nwۥ'~- /շ}·{{]Il& |!I&h$x/@*qpd+-r@[rXk5nA#@SrPD0xvCP SlW]ԇĘ.K gS{ Wy6Ɠ(th32l<O,͓jөCli@ IDAT\.d~r&NonlnSxX{~KQ{>vob)9OOҬ39نNnӪ}{0qS?ٯ_>·=J*/*1B"%R~T j#:FG2 pIWI ³85:YƱ,EtttX繆MGs9 b明jkds8[gꞷM=M{.jt>zڨB8O:&CύՕs.@k_gV6'˩E8)36J_a2;[H}/o;w|?,X(蘾øz`<5OL̿Jަhg;\^+ IJdl>Td],H0Vp- z}>: i];(pCSWe^+.a5Z25`lO6FϜm 5L-kv}߭F^4lO83q*P蛻>G0JW7z;)|Ѷb :>qX<Mq9mR<7;-t-7*|0.ne[,[B._@U:w{eVlc*qlq }J69כ/lЖFv[ n&<u tG?{ׯW0哕)gpgw9 =|*zZJ^$Z."rXtϣz) M! r3U%avHhU4bY+//<.@c~vrc Fcy5ɚ9YJ5 iO{؆TV.7;'-bpԀ H)E]We5dnLfkd;{ Kͩ#3is3T z>Yyvt$7k:FGR~$Ov1A(Dލ.QGYG8~|cP-X8ILUʠML!x纐n!S?'vOtt{}0%dRi6U;dǰB!"znj< tdr _7=JZ|QW |P۠th.EW9hرE8v p1246Mq<Y sj"odzxtx6qK7h;rСV*QN-ЁfKw?Pj׿”{3fNOq@S<|f!&bݭM ?=GB;\?ӗ^wKy O%յmh‘E; O#t4Z' ] ( (f\&,H*,C تŒZuМdpk99ThY^tGpFS[t4h([:#'WyCgtqhx>-EIL\ё< T/պ^ ||E!fax><|sAjK-ۖHX``gѽ~5kA|wO_|şnmoxec{AeŎt"8>9hB(<|z%ESN`nd&(2g';j#IbsRPR(8S4v0_]>W ,]rCjh;vO:BE#-7z^Q-"KY3KCh$:N+Z]YyGkO/(nZ@Z*8e~WqB^o=z{RǸZX4K'D*Ǚl7FF%?=4p64[v:jP0m{@ <2`^Pe$ITEZw c6yC(N~tL3KwJ㋧NjH0ZBYx:29z:'RKP 7yMfwhJ^nP .UD^%4>w5EG@l:p6ߞMCt:+DtЭUX2s/.艉+k{GG'vOhl;p.ʚL:xp+_B\?o֦:^c4.H2N"S0*0S0kTjEx ˍF߭,4u$3=ӬVkcdn0 e"lXّwN]Cۃj2+&Gϵ4h4g95KA3-1 ղFO/,+諑p<_^hmδ bjbUEL^1`K '}wcMg qbFW|@=]?oMy)kmU?|?K|pmsk_c x#h˜Unwq^Z.춊b E~lMUwPF>0%E1r>:TWZOu+0g:ϱJI/.1ItϗCEeo2>o8?&gO >jr:D0!ʘsZȧFAH1t/~wpI 1@. :mH''S R_gw~+plm}}c{X2p(J"A?4͔chתT=7o;-*eQ4SN5GtFJ= Ű4sٓR@kacߜo5^a4]SyG-e.K&&WˑkWxrá#tX`g|;p,k4\.c2qczOB>9ݿI=8U!uN0+Vn:s`u 56ϴ = ;8q=䃗~W3HQԋ#PL+>·w{Հ=|FR$ZOÈ'AVrnaL@5Tz=xٰA\4zA8+KЮB"c!/.(c9^4g@p1Dա^[gK2u*ϳ4 l|dY$/.*(:~2MSzrhԫf\N3F}+z)trr8Zo2,w Rs-{;k{q"KEqH@De2D*6}eNhw,tV^䵟S8Rm8&tVqѨ_*拥Jyx"4T![E䧴b)8 91vL+q?MKVV)ef B1ں<0@+.Qv? nmB'7/HXo:^d$IDIѡK)Mkr)][o~xu ]zgf0$p}sgS8C+HSB38}+}M n5A߰6F7H*nd $6A ܑ)mòAr_<]Φ3AN Y%وH.O^^L+]L[U_9*GçkW8Qp," h*˟6n"pNn1r8b\x}P,[@"࿢fAT"WW3HQ׭?~i`uu GIdROh\5ZMyz@ IpJv]i6 ZEY85]YđI8EV_fZ:a˫3C3tW' QN*~ ν~|9d9+c_,uU˱,pxzy1od>eITEKaЬm9XGo1*'#AL3óC؅AG#P2Aw(*$sp)TY~˜CVlOFyt EG[6OC=+ :z\az,_DzĈ=ӣ.tyՀV듴CC蒬)q}lU MLmo<[,?ɺjrk~5L2ӄq3=^*W 1b;Cxwxt'q1 #p·8yqeĒjE{@%A;G1H s z9R*_ywܽ i`sgq.WAtʙH4_kur! R0ҹ].jntC)FP4HUzZor Ȑ^I L_:.+ ✝A6ٖ+d]ggO@fQZ I2{tZkYhtv±Нh­T rx((dP*AtO;n4ބG?rmb #^Rrc*ʖ"4"hrkl<8^o>x[HKGoF#+<\=¼hj~!,{}|H*eTLTW{Ӫs&AfTY+1R*ZDW] AetdC d WEdz~F1M J"jd瀥̓Rмuvvv}5%QQ_,%U3zШ䳅JVW@M~~)j-@'_3!vfc&I ~uX,|Td h{29O=T掆4/0VW]e9X;>id~<_(t2րYѝly˧SYh/>tRb;bRr7sG?6ɍ!! *>7yX:-/fuWoQ<ܤ#TJ`{oQ_~*ido 7=xFeK1D$OI@P4fr/fZ ׁ(ab1NkS0'ΤЪ%X|C2Z>xzG2_uD<8H~̦C}ܒzYݶUIƉ>ZPӻk+z[B;]?{Gk7Aǡ#4յ STr)lL\ʦbD QAse:* DT၌ :[m$p1EVWϑm Rs-e'/F${>XJk$0>\.AlI-Wz)H:Jݣ Ȕ|2VnR>eZз+xIzjdÏO _\}k616zT{xFǛ,ëxrlY|H].gˋtqϮF@~ Ndž"qx7+52|iEO:(R(㡽^;ݨ_hR }j>@vM Kb:꣇w7Bz9Rr??݂:0R(݄x/D3f.壘FBVJ8Souj\,}oU +"(x:lGh dYP"F?4НP_El*HJ2=ځf3]y?"Ge,?+]_}vuhZLƋdI#U?zk Ct&1rVYƣaO9J\:xr%Va MTq+Txo*3fvjc?vsn /❏ֶv7D6w'SP;Q{ȔT,n1')rKCbN)0 C1dI R9<"dȖehMLbMB8K|7z9RB^ͼ1>mO2b6AhĜˤne vӏ"p蓂 JGer\GW$sbd2 Obi#H MPRL,K ϩT^\.Πq}y\sR)]i iSCxd$|UE$ p%$д) :}M[ڊj""# 2! m}g(KkR_yO6vД_ӓ0JGg)SSh+,UN[Ќ++VᇥJh680N;A IRt0Es]\PTݚ] m7RF 5Y ĀeDŘ&Ј MQeiLݾէ< (F.l7WtD&8=a!0(xsdI9so8_4?P4M[w?^#RW۸W@CPa0> ST!?=dl2z]zh8u2'46Ni" 4 N3ЉL(Jw)"rK G~{G/oL4I3vFWO8`dݙN'w2d XQ'#X4 c&~衑t 8ͩԗR ^,pT4ˣl&W5oY*g6Lĺ(Eb+zW/;z9R"/޻ `09a<)IڻL D8 Dz5MTWX]^.(5XT0mǐT]34a8I(tםO|3Og#fu{ ZV[]y$JrYC~HL32MK, d63;b: @w8=7#?So:1J~ wNcoB ]f][:2nwy~Y>Z~_|MݺK}ɅHW~øpC*I̍{]2](t^ݦxUFD \n5&kA CѢjG M0S H7Zxή\.'S̖@e 4_'8Id M5, PNQf)r =vA#ámY;_&:e# c0W`?X+LU;ftl\_VaU*0 ؇Ԁ7Nyѝ|/}[*ꦞA V'wN'bj7Ŭݓ0J̴:vd;@;0ݑm eUM&z 7LIO**8 ;~,δ8JDNTndd:|<CUD h˝ ibv{ :a;h(4PhƮe;TU˅( 70f҇1bhnl*sh M%3NwZzi2xZ.\UtYN}ݷ^O]D9R~!?Oa9'SOéLX>21[$","tQw,dA쐌r؆*XVlh&˪ Ru jk$„p"MD@ :f1V p=5sZ4q<Hᵌ!vd2 3x<3UTk 7?vYUEQ'tY ZL7$CJw;=jXB3m6={cCʅRq`C7`q,XluS>x O 72v^9 .-9Z뒜2HJN6kXw_{_oif[HsOcT`gq>wՑczOa[h^1r|-sDZ\u:O1agly60RdIT".*@G[!PKGcp:jӝaY~T&|%gş=y|L<Ah$x11IcV0<|Vo$pxplԿ,̦M.xDx4wv{x*H`(#PhcB|{#1J4DJ{+[?x Kr,  9s>Os@# `,u_?}@Ⱦ|$WmYg94&W %c4  [߾?3q۷@ScUJJ;@ 8&`1Fo 26-i42q*hqz*\`թVkU2Xp\UƩGIb!_Paa%trT,A02288j0y$,D#J6&mhpdyl:pݦ{"A9{!1 cZWN.U1HUW޽[TUS]C"<N.]+.-++xUN 3"csBL.XMݐvwTO~;Cqg?x``/11626m1nqTc!lWi m¥R u:ڬqFZu8&,R Hq3pŦS5ԡޞ&Ǘ2BXC}=#4Ni:@c2ybf&Z+ЋIEBlpҠ`LBPX,VkTml.+ʽ&0U#e<*YGWs޽x&@Yr5N`)*x\6 % #M `HV_g8_E| kkkۺtG D܃322Jc&08g DńI&|01\K:AUtW*p\IP'Bt2l(r^>TWNè 3PV)fV\Z__n(sT$B.j,v^R+ (P"7iU ^AӍ/W`rPu5iVW uŋHF*=ew/\8KW8zkB\/-LpnlnG\? G@0!s[@!R'3H=OvTXWgʗq*K(!ʐrذ#(SG(<NF ='Fo1ɀ oa2:BCR"l6 ip\)PcF+Jl67FYqzp C ]zBRHUB*_KBb9LӡGP" D c"PegK.pmv˫,/=X҂aEd/Tn6S0<*I#T0œ}5CqgWwwuuS >>J BK &\VLf#ˆ`,6 @B0V%B032 (Pr(W*dqR4\*@DnD&B&N\3M96*TA!H j+r"DBT/G,ƦW0kJʪZZ[몊\Fr @ă 7_Y9a*ʊr )6F}C}}'QgKymB#gFXu|ltd  a4(o`jU?F-6AJ:u`JMe[M"JgQmZR7MzaG$t `k=>BA!hB1RIy,NPvqLrl.mtpdB2C1~l\SZ[uLnnԡzerz/Dz˧ZTA |zRNpLP(]uՕׯ]8?[OcH T}g#&?Q0\$TZד20=~F1Yv9Orke9l&Vh4zU0|z20Y W&BccB(4&i6,:i$"^Pު*DEY2t8R)Sp'jt$CpB=w}tdc@] bq, IDAT`һۡl.ޞzPr&jom(-.i@T3P2@|99뫧E:6Աg(qx|6Bx_gMY^ _|:b,Cqo_+().h \-:2ޠSTe:=.CVf֫eX>Ef1괨q^ѩl}&a2fQs Ȩrt2 ǣzB&VWk5j \vnL 72SѠf6:9UZdqa1 xvIҨZJe;< &օf*&bD$@ɝLT*RgYj٪Uq8\Qi蔨՘6<%o|*0uU<57*![ u`P]PUOu)$aQIyckO0ŠvQ}hwgPW[w~!iAy+竛Z]ulB~]ݽT6 0Y,P"ڌ8 FɀAS\ifä́AW*JuA;qڭZQ!bqya/t:+{ éytKvp;aP n3J١O*S[0@eTǡU(J娈B@yP T*9Tg wQ FinmeyyxV}R_5M%y f!TsoNr*z 9;02XVK\ޡso~ݯ?1ߍǐrU l..@TV^VJzi&p,)-ZkBFtxoW{[+ 6^}t[^xz&5xNёnr0Ǡ R!V% tLGnI$4j=a6H:ǍNHEHI GRd0mFG8I]nA)AJ'a a2FIJ4SA_(l1;h4EJ*UC=VZ*m0 ܨbx$"LcCOkTr14jLI$RpU/ |(j1Ct /9dzeVZȩ ;+B*$MKJK_t޼b@p;J#[{GBʕEh'֦+o'?z[!O2g"_n$5'vO  R1_cJ  #֊x\d2=4^YB_DJ!)*+,y"ʁ @ U]e P/,-&9jJKIH0WǠht@l;,r~o>?cD؞ 4~ cz\*BrE\0DLVYSd&`2G& 4"$If&6Ned2Ev;W :lylf[04;=5G险drjv:v{B sY'b%_jڨQȭ`$KV &0,z&)rqH"d^0%6`qll1\AEvAU5e%@Vދ ܶSQRTPXڊR_]rDRuu MmolipZRYfKn`su|Aι7~ʿ>ӄY-{\igOo0+$" }RuR>z#b$x:-z`fͪU`2az@ihtf3D$¾@bvf:`حVG< qL, ,NƓS;0 }Źl"1MgRT6ZDBXe^ZVixj}y*w/DU!~)L*;s=ý R] NJcPL@S%@0}|_{NLj>AQH~'^=7ډ2N0`=s,OOPK=uL=W߽k?r9l&%rXgt*VB%H1N{րnc8R#J0nyp*l|> SL<>577;>t*Cbufvfjvae2+[Q33sSPBK_ RO?zjQqYU08q .f3itPT%8a4bS t4JTav:F9z=J*5J̈́NhGsjr2Mfۍ\O&JM"J[\^^_[^Z_J$d&_X=>Nөŕx$F#`4 Tfc:M-f>/9I#R7+ NO 2vBDْ 8u 0^N)MMk N-a$(_ `9]79B!ܰ1.6%W/]8O_ηg-)C,#o)jhDb %J\6ؤlfVzX"Ӻn AV)ӳ)%R8w24P(hZɐ͛N}@e%sK+k (n,/m-!I$ 7vwn"@x%M}J"ln'il, |A;ʪt$IB!v:>݄X t{1\75֒B( Kr2C*@h)(̯)ȍݾ䓐rǼXښ:Ǚ\p&ø&pGk]yBۯk7g/oIFu7*'fPH-8M*L(0ͬ>p}Aq%B!\ xKKs SمÃݝ[Gkk+++S;ˋ>vVolmnnXbw|U7O$ҙiDaVCA.uyj7IrPf Kat4zNNAIeU]Ssc]M]M4'PW %e@QЪr y|1z}50Y E!+:@h*/ȹ|}ŧm"1"LObz♸<iW_Xo&fEi(e -mmM c1@-':J+brW /Ep#vt{c|c>d`/ +/ uU^ѷ\ gy qyT (٣NP &qj^K$AA>:,2!}|1A`HR>O$ӪE ɓ#2mDW *Pkt:9!HEn,EBP DC0 VO<ޥ ŭɹťŅ7wgnܾthc'Gn޺[Tzfei*J$0 Lvv6Gam-zpz"InMNZdQdR> >v4ɹnYuueՋ.\9b$/Tocux7 H syl.dDpi`VUo=ߊǐ" $r^GQj*(8Îq*YBTRb܍zzUF JL+Iag2q!/bPzz6HP:Y^Y]XH֏?`{kލյ>ݓO>'z>z?_>y9Y]ٹuo{a&32 F2i: nēhv: l@$ⰺ^^\8q}Dknfg6fV76W# d4ET&MO"ũl~Naс.J]jXOO':{:m X˫IDiw:G(쵘 :f9fAo0vb CXX'#^ΜzX(Uzvfag}{osqf`cmgʝ;?|?s7>_|{o߹[ݿG7Onܻw5NOObI]#H84==]YNxإauSI$r2hg;ZAliijokokR ʲ<8ؽtjAIM}cc}=͵vtQc=T 2֖?w_$8S+e'1&'PN2<<26~lq6'0A@FcpdJ* ds9`[}>gB-6lƗL⩹X,>xJf7еr{x{h{kKGn<|=::}G_ټ>7072%60;7;x]PtyB~(m IDATݮ7,WJ> M]m]C#RQ\R%POGC}٥(MzYuSsSC}}c"60G474x*K,SqXl)/o}^cH ׷ b&F`; MlTb8t* a] H(ych`w;@42dJ "g&59Oegwvn./8Z]^h?G7n:ݣť7V/|˟߿w'޺{յ 饥 mR陙G1XNQlZr.+j@::ZKH*,GhQƦƺY 4DQ8\G( PZFVJI@VY%wѨl1n L¨wzpLK`vXff6݁p(J,-,Ngxfk./9\]^?_[G. H`w;;Kn[88sigwm?~'ͥOn-o/.nLK{[3ssԬӠh~w49].*+Z[;:ZKPz~qY%*_|>jR0 }EeU}GiVRmClTd}Ues%yW~ s8ӋMCc# :8W@GDy2ҸBRɕ*\ DnNǥ"I'aO$djnqgkojzjjjfmu`1Llڟ]\\]>پq'{vn~x||px}p+K7>8o_CZۿu{s~u;^NMMƽd$2*3ģuX,d%"HwmU584C.\ST v%T:9`_TZ =Syb͘RM]SkGggg);:ƨA36P_wWK}! Qk:8~y!x )X3g(ycC=4S ը8OBJ\(aJL. NJL.W!D Y,66|@ݡH$=Rfreuqp> f ;;;7n}v|>zxw 䗾_,^]]qkvw9>{񝥥=3SD" '7JFÁZD4@e?jCuc4DCKkcm}/*j(<ᩌ_ey!nRAdv?H~.>Tps׾~/13_qtwsTmG5wsb +:F-!Z.Ӻ6Pd0 -&KUJ`<`(=xt66fg`owsoifa/>wtp7IWnn?XY{|û'?tt{3kۓшj:?DɄ@X"!dBg1U56:RdyY޵w#νs)DNQ!i "eY&]ρkE MP>ZچY>Acq$C^0Ĥ tV_xx )p1JcX4>i(K8Ɔ1E Z$D2ov_נPJQRk8g \$p<^Gf=v;=ӫ3Db:(6kbavznvi{cGo}_򳓓â?9+++7=ޭtzUp( ʀ?OfBaMZ5fB/=] |(s`Z[_--(Z6<e:oa+`ЖWX2ΟC 0:T,.q:J,ɢ u6 \zկ:_ǐR?AA7* mFv644SX!FereJ&q,U 8L&36TkT \ĸZoH Fɴӓ̱Tr} $VF1}x^<~٧7?=mO>8ypG'Ovwwmf{s+3LvJmF1Y.H8àт UYQUSSQzq[oƛ_+B8ȅ'!ѻ~@+"]_VYDP0OW4BT.\JFI5TN{ RO%R1b p8LPc" &d2pzGil rWJ(|* Hn9]6l1hjvku۟HuZcH8 MocSt:=ʢJ+{pq{zsuuhsye{[~ͱEtG?doG{_onLRj6;0 l"5dSQ/zۦq{ښPT_SY][]Q @Qo{݋%եE$@<хsK00{՜2Fle~ (2&cbQT_Os]IE7_[G=QAi˗aѩ4PaBEJ.f   Sp/˥+X _cf#aXpx*%Q;0!+ 7NOhi2;]Vo zsy9t҉;'/{~O77>]"rx/9sno\[\YXYHf3ip8p2s}x"Zhk[ں[ʋs^8[P?rn^Qqiueqa>B&hEun.QQ>R)`=lo"HiN @_o҅Q7CJq/ ЅqKI$FOd"6F x P(|\*JN"PRݱh(Kg& ^K%-d& Hr6HF-傖ɬ'ݾqc/3?}=qwcуGG~xvv?_-,|ol}xg~z~xk;_݈F"եjt$'fK˙t8 B 7rX,Jnw+<&ǫJ _;o#:0ԭ(t0ʩQ9KW Jk;:;;:{zzǘZJ 5Xy;OdtAϏ58HP%`e:ʼh)7*H8>b&#a2yBɌl2[AB|$~sV&35y`2[ܼv66m޼}ttn~rnc2L* EpzcìTdJ6xv*̑fL5_S 0A.B2GZAeM]m58YC ]x]^|; NYF82dHL*IF0n(+vܛ37~x )~_|xJ1# Tn,9T |.W ٿT b'|!il&K&b[@~V 6Nx} x>.yZ5:Xa*KхPxjE-0uxoo&up[~ o$@zlv:K$RzFe8=#"L"jNFءQ\>NJeO#r^ŖRXڗ:ͪ$\6K0fZ4*곫Jh4 v?qckuf*;>)幩ٕ8񹥕L٩Z f Gt&_9GBD4.+J9NT TdTWW\zqy\/m(&)蜓B}QrG5jhjMή1#2\FFj߷~_I q?3*G"BX=X3E%cPaJgxB(p>`2hctRB E2\7p.hSNQL1 0 AǍ&H.G$=37J'\&+9.:啥jӳ +K(t'粳d2zCT$At2NF^`۝>׮pjKY1۔UT 9,Jas}(%m\Xa`_5HgxP硹~ @ݹ!gF`B@hhokEz/%}!%8wk(b 0) `8L6Ja0c#CWC1>O(p\!B'pu2>2LaB .c&Q!4X.ݗJ* =65l+5j-27;q, ZbHΣii^\fr~qwwe-!J'Q8md:J'зksliG/-L&~o2Aɞ/J0k*(8p*(,+/-&.m!Q +Q uFvϓK@iyeM-YSKk CN%S**;?0GNUWxR'3HN}\jU#l:/@>' Ņ^9n)D+bv`|#ErB&Csbhx*X>^P߄LlU*Z$" Q)UVv2h24 Yd~Fvwhv*K,g2L:NOP8DHT:%Bh{,6w(t,Šv{#Ǖ#Vi3jQ.5M͍`YXZGj繠b9y 6+̹z7%M/[]] a:P] uv @!Tx7^{ RF7FF'lXC;*)}&!DB!lwc").H|^ Q 9BX,6Nk8lTѵKJp@Bo#pm(6V E-d&ŲShtjiq&u=`ǽfB>_$.u`DC^0b@o9JQCdTt:IOC%(^ @'rT]s=t"W} rl?A\CEY\%johjBX w _qߊƫкqJF!sx< zL$u)DeQa¬=62s}ͧXrDxA,2DTQP`h0bQǡ!LQ(Tހtd4rNd`ZL& ^GBv^H8DIZi`Rq8#riEcsK34kˊa(?eDE^=u\/(,|soox% kkU֎ޞζ呐b2 P",?Uhomn*+Ȼv|G8q^HFDb1g|l4Lm(p ].DeFHc2XZ*rT(DJ&cZo6p6>.C\c2a&FGgNи_"7]vFQ9zXui-sx9]lו51A0iYKdYM9Ӌ'|z=:4Ê=|!4w49?{׏o~᷿{|90~~u955ڹhx|ŗI89%ji@L p>BP!{ouMN*ç $j^Uf oU I%]W*qpo{e?x#RWiz ah=:wLUAfA 2$f^7LGr^Vk mjJ(hX ŝ"qfڮhbݱڂD %?~xO|爜.mz:\=w*:~xƣ94cg6M/g]'`sʢJ6)1u=]j4**dd=Ϣ*D&W,䠽AX8a2cWBnޮ,=x0l'p$*UbF%U3ʳC,!pKq,l.|o]bY<Əsw#^nU]!})fV]°kh١Kj F7T1:ϗE8U5H4 Xe]!ݔjd2~xu9BGf Mrb扂ZfPvgH yM0mǎNQWoГ)jkB@Ix2\><;CdȰ$$I,;AJ!!|H]۲<Þ W˲f:a T$nxjRɣGߵM &H%PQvb.~ KBH`uvȴok{Jݫwm艂H4HbJVe( -Ȭ-2{v PYw~O O{]\^g4 V F'd$^kXuW>WEChƖѭL_Zx*Ui8@8IRW]oNfWFp$BJRdP.2}/[()Z 9Ν$u8 {pˆ{snKб0DM7 bfK47,0r]My"NJJ$6 =#=GћT `Y rL %B\6˹B%"b2{^ÌT-CFN]U$Ll -*2- JMH~VZš- !~lY Xs nP%>зZh5(B?`8OgQp u $+|6qzz'_|g ?GݥdR߹]^%erSSL]c%ViKZWǓmׯ^V)Ñ95t۵t%($Gó\WƓ;EONSJ%BcC@-xH88 >xǨD6;ÚSHH58qBSCQT˄NQ*dRǁ?yWI<RCo| Y (x!@7?vq#C;? SxW ۬)zυt҇Ӂ63*˚ʳG)h(͋b{~G/.oF x5K}瓁]ݜCY'ۆQKڂuo7AzoqFD1m kғQJ }=8P //CXAƑu:Ђ[)ܚA7crPguAsU/srSuK%Ihah< д  Zұ]2 !Hݿp;&6O}R#qk᠏v<[{|r)K㺆(HpH>ͧ:][k_~ֿ||?@ǯ޻; ldK᭞sx:;ճ*Ǹ-n'_= /4}HtX/j7( dhkd#]dF 2l\gA2 ; j|1'CO8˵B5=yG>Q"Su &AOsKGP!ms"1pF;\L"|O@no^ DRjNYRGЇ&Jۄ1CGD<~/ xyf=[@[UEbNgîs ?N'}g|l/]ԌSD9notq~9ADS(aH783TdbNACٵtb6ۀ6'$݂Iot6u=[ci]hr$THiʼnR* dho? ~ |rr ηnDrg/xasY0ĩ|89My}8UtIҡ+ y\Lj< V/#^]wȶ 6dը;ץJBzYz_x&WYpH;jR:EmHs5,"ӱ8'Dڠudv~葫*.exBhr~LլVJ_>@UکRh!-=/ӪeLgP4'a0B9HC "xK\pCr9Rfﰵ89=È3M괢jTCu8{mAy$)'g<RBO6!qZ9_FȵtM=ʛOLF=g~Eӫ74m{z>A;\ts5cR8-EWPzNq=EQf5uZUtjVu\4P6Qes<B꓅0NQ -fχ@Wb=] TEk|.[=>Q's` '>G>4r9̶L:+4[H9tAt:l*MM@#hfɣ᠏sN~n t\mt]Ue,PJFMVP4 lTrP4'?@Ξ0rLu:|HrCǧ8@ 4aDyy ( ;NQ-RZd*T:nP4;$i:WFڦ(NXz(+, tۑa($Իķ( Z",//>!q„ƪ5$oC2nT˨ɩe*nۜn"8hPq鸇;dN+6s=EZ݁ODŒR03h@'B: g#tz:=F{[;Ț@p8rO Ϸ7DhԊ;ۀ H'g1Iɵ8H;>lCeRs*B?>^1Axy W/Vrs,pt/6CZ%cY՛ tbM0ъe5"rPbvGL^OpKF$j1ƙ׳[ǽ-Xs mU++MhD& 3\%C& ,hGZP܅"P:;;SRVp츞 66oH8V`w?W0~?{Ͼw ,mtu rI@r.EYrf(4,Cejk͹Au8^ Xo|6009\7X^6Ȗ9Hќh=MVtMQ2]jpl%۾T9'k"k:O$bj5JJ\"H`/%Ύ ~B7qT$ a(x(ʇk0}t: &@~( \01\\LKT:SМ[:0 F'A@<=;=ZQg#^Hg,28z VȵY1Z {͜OuB s%mefz3Xz1^]u1pq|{wڨp/s4jYv ȫ *7v\d% ƙm l:H&']T49HɆ4Wi԰h$ `0rI* ,P;[kkDucwD%&-܈m\`绻dJg]irSFY9Nm#@vk{(r m.|W/y<r@ǿŅmmG@8$-J-Qh[SOe(ֲ=?XP OUޓ/ME'~??<jD:{4bpQT\wצ((|T!j*+4Og[vwD /y*Q(zl8@TB}'A@8MdTjx4!$ɡ]YAr6|g$]S܃ X&W,7[Y£W%D&_|w]PyHOOn/!}cڈIR:kTaY Cˎ3}vaZ1yu>;"ϋqOf?j`xdҕvfbCHcX8mbfE55)UQv<k;p *-Wx ksp p@Um8jv3Ǻ H`oggt?HCrqNGZnQ#J0sC?yBkGNlzY5ۼ(A0>S JA0ts3H1/~G PS 7/!=vs(N[kpڠ! k8BQJDcRDIl'CO(FZehg/xO\Z]]_o-nnt<}Ozj*6}l]|?D%K,R;kw@,bk Cj>@E"]Ayh4Bt@; {>}͙c<"C'?}o}~}2yD ˫ۻi-l JP\c1Ypz}92Yy|9c&.B2M4EUk 4g.Q8QAe*0B-7Zzi;@ӹálK|R"Q T,UBj,ww\(xLT,ΦSIsd*ARȕD]ʃ޹YjڲRRx+>-_m2kobN_|ޛ˿?@ďk~_ܹ{(w|~ T}JZ(MJ!g:$NV f3K8Tg ``)Hm\잧0dZeh`SP:aN64O\A (:CM~}gFQ(@ "툘%pTG0_YFеi4Ivf!@ OI,' J+ih67f)*?xW/LR$~;}'|%twnG2`_iЪ5iFtCΆ±mbRHt2UEfQNb4h\#~D㨸GLU`y ( MTTnY@% JX69(q c|Ci4AJE|^HqB ?H@&i'tTʟCYY݀D?<:QʿN;GadچCY.XkIE5j"nهGaz_oΟ'AzxQ!8[xyiu|k}qi{D$w]Vk2nM.^WYfw`2T&~5_Fn-Aht!'vWAAwXeSAڎҵ\6W7+ESUJReIV{tpZ8Ko69EFF MS`Q$k`(zK$rt4 oTZ%@=Ek "aOBt&B{\)(^jApl4uݴNZ.#[d};o=[H?[KH`CѬmay;[+8z`yg?d+AdjQo6^\]JNGq\ʰIj;Z-갫5ҩTMf/C#M=,󬮥[0A)FWd2S(`$^l" 9כNpT6嘖-3b.G[S/g'тa7»ӳB*O$"Z?:.člݽ`|"q? ЌKL:[hUGV@on2 Sz1 Wܻ?w?w_ ª_Y&55|#6K]յ$V&$ u.hsp4-Mwld3l2LG}&6j4g6ˑLi0l"g`$W,4[ A)cj F!_*݁/YA :"~<>8crIp;5-Iђyd:hh<,%5lhҊ*$ hE2ܪӑoc[_~z@jumc>RW p%2%8'O!ň7E2nCY[NuQ֯_+?4C=|/ވ/3~8׶|C>P4EE(N:$N ӂ7˥ p\,QͳDS^мhfpΰojjibYg{~)hKSPJ$. x?_^ll5/dM'JӬgJRT38yAf^*SQb:qJ(-Q4+6<9]~?"MvvɌXnHS % kBbs#+ w+UfknjvZgѾou7^|@ /~ӟ{޽,k|@VBx9>|hKgעfRp=.n*JuȊΧ2vDJ$tڴ-:/-$2P6Z%>ex Q3] AOtUr Fb%|nsQȋv4[oWsLJp7$AnѴ,h]h6PL}Ċwc{ ַSbh}tw88 fSћKADD3dq϶UZހ[MLпx7/*_g;GNGx7sb 'n+(B 2j \N{Qx !7zo]Z#vaCOvGa{>8tzP(m[: j[1Uز(l UPyNI(К2X^5ϝvɸ/vxYC]-8ċ!׸T2qO)|gSVdG4ūU 6uXvޞo/? N/{zmIg : ! Cd΢ _Q-*'ɐz58xFA]2P`wsw^RRo~~=LO9ҩm͇길#zSE1tU8y*/pch *Lڒfg!рUzW7ɀg4KPJ3kb IDATvL-g? ӳht]^@2|Eg~xsċ_/Ox n`<|+ɓwOgRX*֫jhM݁g:sw|p"KHKg@J|59:@ZK1x wo["1H`4_ #?qw7VhX&{;DsL=8DTHܽs}CπI SgH[eB')ThѢHye&s 4+v"Y_^4# H_.oRG߻swavNcjBt*_+t%_ zOÞX*]PJqVg/43ɳdk(WKUA`8i陊20Kю uRZmK*bxEQDv{.ע36Q9NOv:!?ݝեeqZ[]'zP4ƷFXဦ_޺u*o|Dee{_B n߹`gdccsgo?H>|Y,]Whn@$#+;w,e!E;KwO~~ N {Co]>wv8(;lwJ2^e;FstRT\SB,Dl]xBa=8,g~ŒdʢE1H tιCuu9 ʲ=ؿs ̷Jpm{s*h2rZ)C[2_jt/|nh9YQ;ҩSyޑ b9T3IEzg#!prwRZOF[ laBRXt ,Qҕao#P)}&f}CM'jCTA*V\AhcС&|:);?_׈R,.xifA^Gj1I5Y-TZ\\6WŸK¥g8{v&ӳ4zRGGճ[k |BDtVRSV'''Gc*JلWi#+&HQ7ۭd0 xawYMp;|H<*:-?v֡?~xtv/m[\-fbڥ'EVN5fsYκ(E+>s듏[ou|Ao 963.= n3P%HH\AbըAqy֚j?@ij|z<MRoWCa^~wgG#x5k-vRcf֯->W٬V1Lzb:}w\L~xFbV^.*z\nRX10Y~jtJXNm"-"A.40;>!?r `:/EB}<>>Zvʇz9 O;Z1J8{%nZ C+9\|^擹klҗL/|VP/fVTUNR)M'~ZfS͗_hR~Qjr>&aFa?w(Τ$M_R>S /!E5GQOϐL_X$^|=}Dr6;1U~6JLPJ%#A[{_XU\A ;~G^f7NVOͱoq'HwV5\7{^Q$>{\nRPx{&:XTFSMOOs6…c^ {+x?|?_M+H%/7p6-rsr6e[4gX=@X{܁H{r:ZlGdߚh8<[}P^g8Cr>TzbH6eYNokA>dJ|2ӍFΞR:>c@ nM44(sAJx47S̝Q`;< T1;JaSh,-x>:|!j~v(Cjs|zƱmf,ޤ[-#s? z{{qh2QA8ODuXym ap",!y]N1Q)I.NֳrZ{j>lѣtGkxtoOϨvA$ZFPeJJMjRe]>;'gGİ3 nRN#F.⅘?ʉdJMD$:aF\HcOl3exELtCR*),x~ # U9f'b.i3_bY9(j&+4:Sq:i;LlA)h^[UJ8TV%b-IRrR>C،/ǒ? *BR4*|NY4O"9K:fbdz-$0/1/ J*͖)ϞoFMFZΧ,v!V;>~Oo᷿Q_>fJ Hiy|JF)H9]~Ap$fT?@\qzJxw'`0ڜQaU>lB nTrA*VZzT&^(Zټ[Gj&y=,T=M&tʥ`.VjTTjqPaK[CejjW@I6~^0quD]~1d*/w%/^<=;=^S,:JJf9N~ן;oEiq5{(@o]g:]Š9<^7HC$f3ߤ @Px1jGd9;]{i,-T[Y5_{ѰU4:t5n7iJl杜$FJVRUE*4ǃ * hBF *A*, a{;9١r4,9KnKS{6KuĘukf!tKg3zӛW/x~~v.v)+ @~뽌q)ZB_ݣGF<o (|T)dӪP\M䘪pzz/U՞mFZ?7.ߩJ?upܬWV9ɖhBb+#}TݨT T00RNʙD5i0ÄN''-egm, v*0q62i|Psџ\>7)x8/7'_|{Az$5Omʇp8ެ=w~][z˝]ڇ'F妌$IbrYl狪$p4J&bx\|A)lJb3˽٠Ӫ5Gn5 x_fr\N'f{lֳnV.5zh9jWs HR,[(Ht?SͷF(|";s{,Ҡ&;)TXKE>M$ҙ,F|>:y|=iQE*Wz~&O>~k`DW-`ɠR("qgD4A(UٸVڍjhwGl^Nk ڪWF*6ˣ頇?!HN|+*qZ7S`&*r[ʩɣU¥"_Ŵ&tҗ<۽ARkCrUHJ~9T`8ٛ.HnXJ,szy0>LO ӿn=w)gg2_٫ G`9)u|\+HVa v6vycpD2TQ9D>pOԓ'{v k1vw1AjgOovRֳj8K;7n J7XúK]DΒUD3q R5ⵁԇ,.( &ͬNJϋς &gAba6+T!jT[:]S:poFj~v7[r.W&&Vtb$F2;Fw0 Jt&fwIE@a} W _#^H} Kz.ȋQϦ輫&upD„8\s:Yh9R8{P"[2˅(gsNRoԨiQIRg^:4jnޜc<ɨ+TѠ^Gt\Ȩu^&*54y69T6 e1FƔkޟ!Е\7P$VP`HZc>j +kd4 RA!(T[ ՟.f3z,{l9߆',9U!ALVG7KWfw/HAC%,R"2lɴ`LzZX9K:#RvtPusblnQ*6g!=ԛbMW* 9jjt2 :Tr$Fj;r=T rV3@S}<4; 8үnwKS 7;E >v07=xTڤV*+H5'juhst~dh45d(1ŧ|w$1q)w /[[%`uufZe1t"u;[X`-泥zO)T߃̫H$3urZΕ:錒fCW-,Et6פ5SmG3W*VRL\kDNtЬV +$GirD`!%f`;;>;O }xGEmv=p˽wh;e0QB-KWϟ=v4C]bкDTE)™p_J;~(H6?4H}cH3VŧǹX$ȅ^Ṕ(AZ^j&U4jk B2XR,6ʹ/SJCY\H4t!5PbTojT2ҙ\R}8*t, w12fDc,zc]vQg8T&]Š@5k&:crNΟ8[6gOf^NkT3+Li;@-yOoX' ^#*[=Իb ki4;͠()aQ(rN /# FRTsЬ8Z!~"$Ċ|3ZPQtT)םJ&&xͻ^UpoS:UJ\`/!PuB>J=WNEM3C)JllK66dл(w?,v n FE/q4sP )ڬ2^?ۧr:yhZHq֋0I/2>W_޺IQ7$H} k8B$9dCڕLJQi%م .NQ^ ,WekJ!JHR4Qj"lou;pR͠VjTtONL&WʥRR*fSM%R-xXjY tN+UpWf.X{?xc0`A}ZtH@+ ?%6`ȽxgG zYLzBMbV/ ٰ'7K׈+0׈b~սk-(`xSX'2`P+XYkTİ@*gsZ\Mٔ %RvO:V{I6)Jhulʤb½Fc|˂HN -55\^A#<´ *R;A j9VcBήR=hSGo&b/ iX6;o_DX..uXrN1֌JM{o{3N\Ut {;G&1yu@zAK 'EQVRIo5,䪏d,\Xl5e^TPI^(CŮ3];N3ɤ lZb8vI.1IF=e]OA˫8&S+5Rbt&!/JTV*.&bLDkn;UKbSm/5Fv76YT҅\*﷋tn5nUʪR@BPzÞppP!/dA[Q Ga=8hKpye5tbv>7ܽw 6geqxjR/l`GZf*QJQLpR~\!xq.>>Σm^ҫH:?$> q]ڨ>+ZHb˿R'uBd|\hjXkz>6^gu-$2Boר4MIfK,um1(}X" 7'\:j~X \ŽH($Vp65s@2-6jlݿ{⛻>7\/Z~XڍJ6 4b^Aj1bf^Fߋz5`ןWx\#~w>\ތw`=.JR79T0qzc&AO!ϡ)dTϦrRo_)hjRmQ%Od+v]/2\ %|9K(X18E#XJHHL-RV&  bryY#$7_Rؽ.\eS*,L~d|=w1(pA)Mg7׍R?[Z]\PXMvb1i7&{0\ "I(Mr,"GS\2Ջbbg8lA,jw`mc`j~6-$j?faި*}EJm.K(L2EQl+q1’QS: !GCu5[ؐץI90Juݿwy &V*,Jf  <|خj|ǸueOoù;~u\ޠ$*y@5x7.d+TYtA@O0S 7(Fx)IJܨ`a(;^gkT*A{e/Jɤ_fXMTEA TYEj0QRjT1 rw%`Aƽh~i/PW[ OR_s_ݡەB> <^!Vm:ZR.d2FԤ׬W zo=~x[7 ׈_*m7ޢ&3\p@x@LAT "p *|Uҩh2ju+wzR[:ԇP:4JL6LGrXm R\,TZut/e1{gvDQt.&b\GH,L'  CtC r荼(ƃ'CQ{:dW"aPZ{+' L\P)B!1뾾}r֭?EF@~yCX]-&~y 68w8yL B#Jx{|2&%Ke)* KT*5yh0uZNɸ[eZo2˥Rۮ粹%V;  P P0$%Rl&"s#VfhVp41 T löu.Ҙ(I^'Aܽd~X<*C G:{<)37gzf!%R?佷|^\AwB|};{(bVڐvmkunFh8V E\eJ<%jAMRjP*l,j1+Bm7kFM&A*[zn<"_EjRx,,1c'"_G=)R)^F""z]v,[v%^G>W03XFB1Yj8cC78ųtNɳn}nn{7?W^#~z0Fgc$ V5k6)ty}8k4>n)L w@t5R +NM2eLϏN֣`"*S /ڭ* ]? ^Q NÇ3x0.h"KGdI!jd,+=|ھlC$2|ozu`ijM -+b2-}Pt|wϧz>^ _u4H!oRac(։Y,,c\f݂y(5%'lWRTv#pj*hh<NƣɴUR*\SB7*bM%bK~nP/e(d.:&ݯf AJ ~ᬦ >xtfq[}ݍ=*(@bq7=-6_o+i5i,D 韈+HyHH 8ۏ. )ejrzC='ЁS1G"~QyB> He%$=D:ll2ǣl*U+HRe Rf[)s0/X&Fuv,č/HriEgmV,6x4)&@2;0؜)x${m0G  Taaj8Y?za]vV*d8+~'7zLmŭ~I*8b1j]R"2[j6RAqa5=PXi\WF۟LUtB-"Z71BXis|ɫq jL^BAGVȫ 4oK,%`L[f9AOFDMOC>Ndf;t4p,{u=s%n )b8Fim=sCw90_Za*尗gF=0j$":v-'UTY59D22u Q%Z6q^TZv-k7) }2N ,>0* @T,;$4V*C^Z,c-H<*Z¥Swxݟ_d=vn^{2fػ?ٙ{5mAxE!,nvlN@ʪۣ]yW Y%%F2{/A`8WҊ{!NuL-ۭb2| =[, G/E(EC~sm{Ӷ?5>jv^A-t$zlP#^;EO 2 "+HlFMdr ?nw8?O7&n*\~ IDAT2`Mf:ܺRxofަ=Š (`TjNl juy/$ڝZ"y9+\FmV`RelXaQ6 Dx](€#1%.>!'Z@_ W2e'J$#RՇ;l P8S(QEpWGp0,No+EZjhT#P*[L7?\\A~%A`H}0,сsr7+m& n*HT P4"jY+țlxqFt,g!:B4PaX]slOm|..C1W:KO6>:ye&RZ$c300vR5 ,aT*3 j֒6]xa(ҔibDd:bo{2T JB7>q)5O䀷,bb'7"]Ymt(~Z!'aƤ9(D\j[DJrNf/fULKnY2BbIUHSI\{=>!,j3{g6Zʸh1=M88P7BT =`IA! tjyC!%)zEҀl W㓓o~9wQ~BE{w>7s2^WH}o[;4]~úx(Sc<@yrTqtI!,+CEJ;pF`ͲJqP.QMF^-TƱ;I)؊\0TKR(8xV ;D$ &zMCVo[a#a39cvtRnm#gD`y0 H56L/_\'GlخO/q_~;7[?t\A a;8ÐzÇ:=ox DO@q# 8h S8nԡO-C(=R7*UlB4c8Š#PB i!^):P.GMz\^/~MBa ˃y?vS2qJ ?tlNϟ>}Oώgb1Gj>Wf_}W{p/RG;{ve8rIVM -=n*͞O1 p$ʤRcֱ[gL  .P6Rj2[Č:5ZPeDRPc 3`E*3LSN0f*mLY `]rRgQc*R-S)m>{gKwZ9F5ֽ;S_G\Ar@7IQфC)L-M+P H^,CR4.Nƀ,lxX/h4% QeD2&Bi]I^e-nb z(h'KH᪗͚e *VM³YתA<>mc$Da\M 盳OOd:_*T<r;LݭGoG+H H=܂7ɉps}Dog1Rf)LDw͑Q b9V ExcBRP-jMt*cw5'D&jLd] J;HQ@[^n@v%L dG j ]..ma vtV`,f9M dX.ł^è{;_}q׀)^?HϾִ< @,oYbcHHc+Qc@p)_ePpGkC!`++ժ0'T"R2I/7QE 4K$A2[?\DYCFجNw"!6r Q [Ysr*ry M/}zS4Vݪ*|Qg7eɝ/?~x ?}=FN~\HAc 'F?WJqvF:"pʒ,PNWޢ^HyqSkz= L. Ҕ`l0@?;q5RcQD,F +D@Ds9Vz hJtxmz,?JTiFTRޞx-!;쵛F@7!ʰuGn|?Z\At@-:PvzO^o%:I,`i 1/적RuA9 zaBI|\]-I+1 G0)P0ףAVgx6[p;0Z.G EO+ Kgn IÐGBHI bRat>uH5gϟ?o5bWc"qe?nk}Q䂝DO t06(a6^7UQE> g~Fw =lO$ ק᠛WD?4U vHztRВ'R fJ\*e29\!*3C d 7VS~`4+iPJEśQpcXjNd2n׳Z2G}oG?z^Ç訲#xC؝@`Os_]R*\B`$It?B~^x*dq6xjҖ>?FݘL%KXUrtRrf9>BF;oz}hl,pNtTdBIP{h4^o4/|Ѡ[+J*b8{8΃onvO?Oo Ǐ+H?;.#=bc0f"x47;hUVW+j b 1P! @E ñj;~ٔFc0Ս!@!n AD@JJ)I%K+?r R7Xg9)̡}/g)tsBW3bE 1-ZpѬfJ4ƙCwYzխޣӻZA7}KA9Fx Bӓlڣ.hJor` ۨ \QYl p]Ƚyr2. 9}˔s$:bš3Vé\/uj:+9*ϮGhapȐBxQ%[n6YUjLyJ>d3Qv.4*wΓ{_΍O䎎NkhL{?4kt{)0Uh.8E#ο_DO  15L)U0y<@ Or)0bNľ"v@Pil>Bnj*2f(J"Y[0'ѳgGsb1lHXwݯ޻d) R׈R~Po4Y*}6@Gxzft .<3# &unA<% p,&E$NRuR07cB,+,@5+y0`[]ޘ` %qQKxq~8#}2iLE`[b&Yd2.:|6LrutdNbo2Pz";[7?e\ABm.V~[v7; I-.vvґ %JOr5(ö9ؒ11!x%1vۣp1P2R`28a$jT;݆i^eIކBAM,Řrö6Kst6_6aj\-ǣj2P-84+d1EzS{}_e6ٖ}~0l-Q&7$X" au%.TDY'l5u)e(~a@( MbtQ,O k6-9nVM$\M+Q!ʥCID7/]cc7YYYLWyb&^xEZfr.){~4-6ggd<ӛ**ZR)sw G~k+H_#~>H;_?09pNDM0]V,G>Co=݇I3ۡ䞛<,i93P3)x H<d\f?9\hNBw96 :Эi&Sa (LVJ]ڐPbw+B˨lXͻ.fz<[}OWڻF|{Qyİ,nfe~#_-PD&b[-YĘ+RQI{DdcW'51Ps!%I\ЁxZ&؇JP!TɮUzF͒{f`_4c ;|ﭛݟ>^Hd*ǂj)A]$F|㡸F-C"NePYLtv©XsNh5`jEQ l>A/>igzoTf祗+2qZ1m.\س+"Rtqt4ۄ/pdofU!(06{wn >gb'٨Z-o«;df /˨AUsfDMiLXHDp)Ei,k.MN7_ǣT92$V K zMU A 2i%HM~~*/5Zh9Zm_׬f J=9%'|Q7Y R׈ R9jGԴ*{>ƥAC*D7-lẤ&d`Rr0kI9 7`. Gg&Prd<݇`XMDT!,)Sbqh4^v}|ӏPXQa?Θ^$H/%3BTr}|aļV*Wd\˕r1!3\:_IⵀԿ v|ZSaNeUN8S6Vs8aXƛ49f#+:2,6NxԠ.nP&iNe˯PXԬ2(#:YR8͆CxR7RfbL6_ۏ&_|.uT'^GzJ4 s8Ps'R":>===,BXTkJ)˦#yxǵ_B\Aj3A_lw8OREl%BKsBw1Ey#A@r>d?x=g@cS\WXp5%/ vv6zQhx!^osVѭ~bq)sdZ31K+|zx"Eʖ<}l,lv3*,!n6׷޹Y%'~>{:+%Iq?I {\!e/4/IӌE"ESPԱ~o~fpXJ Nyy)GYpwCHҨfoWtΓ';{:fy !LYO`p4 j1^Vwu@@U(TLzM7Ӥ~f(K+H=8]Կu芘3aIQOϫ! KAs2j@>(҆D Ifex,,`6@7$#h$̐+MTaNXٻt6X6qlq&_QB~9:P{Y=  %Dz9cCӫ\)ajbA2aѢx_B\A5gԻ{;CAn`4񹬰9<"wapjIcAC1[\^1ަe`+ʾK,eCoqY,&yp}W $L@ &ϛs9sΓJl۷c٫ή$DD˪WQjDJYsEv.I2JFW^:?;;Y/ T6`"ۨWv?H ?>@o+ffv+LEN !\ibrB&kݗj4? ,I*IeS5me%uP-# CD&"4q)BcёH"_+ٚ dALX5l3_^2E,gj)*NDńkvӪć/}g}@ENp%{t} ~cV 2p8ViXyplmVw4 mjhooXJs Wy8X7H!(#BDZ9_>F%bUa~V*ن<ܪf). z+$R5&XNj?!]^TD3̧jZ.fdT/1LlɱH*GC\K܆>)U{n{  VLt{(RxV N~&>?Ĥɧ+X峹ᅁÍ"n%8;†RɱXPҌ5{kV;59{[5 %ٜp4t9[-&%&RѨ2QLRbMt{R: m[Uh/?uW I=:3I`‷k98 `16 L3vF~ vC.L0 dQԊ ;X *HXc/&BNa.s k2/JiaG搜.IRd2rnY6O(N">߮WrXb^| a9Z /`LPG_Jw;/gR% DShH,6N&~bP>9|iZduPӦˊby IDATŐF5 }<+keR(-Š&BB}bJ=M>aJ16рC`<9YLǭFR=&^-x41nC|ӹ*t!q Ic w Ã?CpwwwH֛ j/HEj#ɶB҅_:0db+J/wes,BR;*oa~>+WKp<fng8=ByHPsarعaDq#>F_aǦuO&tjyPXf}8 oΧ `p aJ&Y]xEHk4oң4~t7 I)maĎSr7!l?Kb~`{G$3e8D WK _d\5|~?B( H)o,qpIA'Nߍ]+]>ԙ@(g1Ċ?^.\bT]z?~~ZC<7IExT"{>jwGyIIFABLEz| uJt,ߐ+f'O%}+0ڑuMp#ͅd#Lb/J.;ZjU<̆UL(𓓎Tc ēD,NP_P+rz,2\./^yo߼~bNq^J'7e Id=z_nZR76$%6 w#R~o6S{R\Gϫ_u;a|:~K&#TIߺFgؼ_~OࡗBB2D2q _4U+5*|wRPkv)Sp8%_]ˠLASLVyݧ̝ 3su"D#Dt%4/R.>ncaLXೱ'PfVkx⃫2gH@`"\_+J+t)W&a/YvbJ'"X6g.lrN\ 'DCMF| );IF>S`ЭpJ%c0Fx*S}yIʄ-#fLRRH`owgŋ pݲYnrk#b8*y=x(_ތ8)?qpݤᏐ¾ay>œI?~;+d,M2iDBt>$~g'j1[M[fPĎaX$ <v%}nERL#td0$D/E) h6H|h#)\E{1umw%@!n^^wUj| r+y8!|GQPw$3c_QP*ӉX4lZ#J[Ѡ7?]秗LS'|4;nKƓ3L"4|`oCڃԋpKU Gb)Sұ`H,vc2`4{t Q\> a;/h`}P8VgOe hw3uQ~B֭cwwfbN8S}GU3ff,U,3I%x*I'^R]v:}|}vuyVjLQp8A=r.JzJC;ȋ >h">@+Sl.?uyr`8\y/>D")~^W^H$8tf p{ LYLP6#mNaz,)sptxxd.SLlx$eJL&[Gv6?Oo..Nfd;Iʥcm(%JV+X) V_(M AI*t|?O?|:TenRbbm*b\ gO=Sʭ~G I#[ɲS}H[XA r_^-_0,QNpw~} á%o2>Ku͎ Ʊ}6]=& Xa`舩]58޷bi&)dB"A6_NNO1[>-֫zDd-eˋ$Sxćd88bpɣo}wr7WR"l.L:ع-Fb l>py|nAY 7ǘ;o^Rl-bʍb&%ȣAW$gbDT,Y,J|.H|>_,Nd5/%Ga4V-xg|-CC\K I9xHϺ燠"NqCU YrHoFN'k y oÃ=ܿ*%MrBa1Qk2lsv3iZ;߉hD|dqlѨ՛byquuuk{^5{N&J|GwkI}wnERܿƜJI8DǍ-ffl VyP;7gg򔪰i;érT,Q*_̖I5}9;D1M=6<*-`0ɈUJP ʵ_-ͪt:.)~XqQV{e>]3~F8/J^pI*:*iq zONuLmRFL-9ܞXR9.%^vg0dgSVC&.T5ՍD*˦>f_=vY%uWc%j<m䋣}fZ&-f-s>l_ra ^r٨xrti³.X.1vpR Q&=gfp"Wqw_/./Nij9ǃڍ2W㑀 ;BݧwkI=2=VqTd2 bav2B& $-zBQ|h},Qhmv]Sb?B.)\gfh2;/ap;Y2X.f8_DηtCBis02[[/v0y|&B:xB_bp[Ϸ^)HJк`NkfޑFPT1Ztӟ^o$ծVkUiLaǢ;{~Gdj^p-76$uxg%B.b V~-!,d\L|DpᎴD8&cR&)lh &Z?xRgY)gZbX%z{|v`iɖz`ZVxsjjݬWұVN<p yI(>?ds[%eqx6F":<Z I3SE%‚\ttp(Q +uڅkb5[˺BiQ+PjPXk(HED*'fVfx,2‰(~jX,d4"pd1Ԋd&W(d1mw\K -I R8}^Uh(+AY;<)F x\ܨ܎\Fõf\èɀդ2M\!BQGBvt6QJrsN ;X2 VQju{VZO~˫?jԫSP,ΤS1Ѳ'q%o ;(xHح@";_bJ]f$ws~ )VOvt[a̔ 7y\Q|HXlءK:! xP=L%¡`Q GnEwuzŪ lڮfaW7XKJ"UmNk?nԪx:qTb ܱH"UiU%;@q3#^{ō(dP =#f\-Nz|,f(74뙠|Izp+)dk5:vۑS$7kZ=7T+$bV L<6 1;gwO3+ 8k3 nZF򛐣Xs!E`u!1M%0LJl*-f|uXۗh:[yȍݧtr-onmHa!\ҹv,@h:bC*5 AN߱H_L7:Tj5Vr-,~D!h4^(7ZV7 >MӫWgh4].vըch<.|uDv [C.-./|h.,ó` h(n&~C .$qƒx$<^!A㿱\bt:Al2N)tn^+NLyJz76$ź=l#vimi0=[,vz P|u*V(XشW: Y)~atspFS~O&b>MՒ}o6Ȱ~2<Ha]By)Ӭ{̝f .V_!s TpyXAx~ Bh6Yɑ(ܠ~`ƌuhj+Ǚ( X:l&׹\a6E;8Ѡi^hbU-wQ&;~+hyzݸ%RhIra}>Ea$V,& 6Z ~V@a;;f{t2KőCHSx ƫxz:2$5nvjnJMpie{>z/|9]\K -Ip'avabJgر Y`8ǤFнFH÷v3-L3Zmn%WOwyCQxĖixe |Fh4X8ΧVTkkrRSq(Zf~};>䓏GH;ג܊@͊Ag%#.tl#Iˤ ~5/$i&8>gM >veux/pZIDݲW OBRH%SZ1_'Woftɤ,["J ~a}IWm7c:B w?>2E$X.0W<7PF9I%)Ht,ѐnf {BNE-s1I \RDbl1n8րD_> d `<l3Ys&7_zx|9Z\,7ZBƍ&;Y0MعpOtD(bq:|!ERHZCè`m+T<7”)obٙ.g볓|LfJDR|pJGT~\Kp+OnVLD~Z:I%b u؝P$@++@ҹQd@V׼d4a;y{įx`(^uz9X)<;=Y͆l5vOGءY֓G_~rt[ZR߀[Baj`B`.nºdE:;ON_7j;υ` '$ӱH\ͥ^ ٩L>dkl<9;M&7߿YM:rm7AR,faeo&J|ןSr-)WtKns&+,DHsWcg =FlNXNO03lqL`XkZbl"JTnƥa$*UTn4Gr}~NN{<_ΧN^IkA!zGtWZRwJZ <|.ZlFa!C$#Fd6]mTP*dnQVӲs. t*],fj٪B^7H1;_<"|5_N&|:l:___ZZE V\6;Z7J? }I}HXLV vqtљ̸9ƳZZEX5cfl QIDAT\*4ەRR{|*gt>cԴן.Vgb:f!W(ҡ@8 xx Si f;-:VQ/kI}znERiᒲn ǢAj24 Nr2B(sD/ F`IH$_zџZ}8<4L/Or8̖gg'l< Ty[5؅Z<{1 ϵ>$-o%v{!Ô pOr9`-:ukJ$JEn$L>éTjwڍVg䇣x4*j6/_<[l}zh]LFM\$%]p_}JGĻ >H4cu:lH"J$jX|RN8L9=V\mʅl&N{h|v1X:a*j7j1phXad5c)D">Gt)AkI}|nIR< ? H2NYN?>tV$d xBq2mJRVknkgXj b0NV`8MŘJVg)}Xal60%:{S -$ I̝o*+;L&|.G `o :VU|&W*cdTӓi{v5^~M挖b|\(T.+OR!Ii|RΥ3B.L2l2q(vͨ?c#kIܞ+b6ΆZ* ʝfT.&\H,f߈&s&*_.&;;f;9\gt2L櫷?}9 &gHX_bl1\&EarQt1xyvF~߽'?߸G7$uy8;,dc8l+\,xcLLdb?*wNjӨ mN_z}^_/ӓ|z:.b9 z|6\DX_8FrZ >}wO})']Ghk8NXVMbPi &RSJ1˴mcnV+d<yd/1eeSn1,VObWVɥG[ϟ={(!ג mIݻٮXc &d&+vN;|>:t:`PRi4ZvY29<a 'PVt)7&_N_(5v,M\ pjO18hNpujiUJR3VQժ"vriGYrDm57PJŢg?$E7ZUR8ITǦ{8Jr1kZZ:Z5FV$UTKlT,Y̤3IrxS&wXa&8(ɥ{;߱u~oEp_߀; 9ʵ&T2J>DTwXhpAԪy8yO?$ wMR~šH[mP<GP$˥X<U|ǩ\* RMU!f5rh ZXEbC,:zt~c%uw>|bH1D\d&kd2D<{jXttppݝ;{{/?}|9#׊W _'_<|HTLV fg;BxD"Oٱ) nY'>~ v{O=}ï>M oE)/WƏ M~u#ʯo%o$EWHRRr                                                                                         O/bQxYIENDB`splash/docs/figs/surfpart2.png000644 000766 000000 00000146202 13261626263 017307 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATx]u Y˽ܝ^,w6mi4`7A $,1$H $ϻw+(.[BCݺc]=Gߞ/$KscÒ%>,W~gokyy>[:ᄓN:#mڴ{k~ӟq)N?Ç˿Ϳy0߼_!`B*!ýc@jhAJHpm />:k9c9?I'wq#FoI&Mfޒ9JMMECCCSrPɈ̨w_[ZGXBaX#Ք %+f{4ه,X0gΜ3naܸq#Fطq_UB*!ן]RNӐ`*!O`_ 1'l(|NF*[̆ٶ.]ÛA&'Ђ.|>piXƝ@]yޜ2r}4?#[|7tgp'{~TrgiAEi?E3 R`Ca;JH.K.ۧMccUk 5#`-^swl t>Uظp¹sN~7~I'z?wo|1+q[B*!Շ%n TVAl,$JH}_>|ǵ^{}͙c%ø(JxIp´H>qZZa(@!@S^،OGO֧76.Zhԩ'?+G9r}??X7Hm T%R}X_`ClH}+_9#9rgQnև3gNޱ|A j/}iH^|!BP{z|ٲy/c VAl0}TBV Q _H}s)b( ~vm'?K,innW6~775ٖ6~IyZVzl=^N@_Uݟ(⣸bK|a@Θ1cʔ)g?;~TB ~ TBKH ;:;np[v#GOzY/gmmmEL9 rsŭP.rYW=U!c_n ]x9kl!r]a}ƌ< .t_!YB*!ՇUz԰!KH}Α 5d38ˮ[n{;\!X,Vw(zc岽TRa+)1rHbRP0|%Y, VȞk+[Zt s/~s9o~}U:PXѻ>ޭRՖ:H c=tx/K[yd [v[,ω @-]\r٫)vIOEe9$S4yv;$E1GB.Eʘ'Z=Ejn.3gɓ /4?ގ/c_H)>1~\#E=X׻>ީj'\#O>/S !r'xywcƌ1fR9Q8׌E0~ GS*V )uTGGhyOe (瞛|&MtSCXRxW($ o|2lƌǏ;KN:cۿۺ܏Rc 7~ߗRe}m%>6~{? 5_oq́:SoQм(-eDY y_#{u]wgqw:_,!Um ZS Ok1RGi+hȑW^yM7M4i"/[&#%1l:eTEȻjQ#I k;Ge,şw0ޟ˲[ʠTvhBjϟ c\xᅧvZEWs/}'RYjA6ȟ__KH% LܟE]4vػ5kҥK]eeXt3plK6"m4@5wvp@A4ܬ V`W77yN/P[D1\tvv3cW蒓Hjb3f}OS;&gOOyٯbJHaެRՖJHa2{'[l`gAtaG}#4f)SWYϑmuQ0%WYcs[pٶZ\07pp_*[ŭ#dSC=ϞǷ[G˂P}ߟ;wĉ\s͕Wy晟q NX ;>R OFS b?8: .9rM7͜9ؘT@mt^}a5=j ~'fn- B˓ڰ)UKAO-048JK3z/C˙ b[\\;WԴhѢ~;.;WQ5RY%-!RTB*D{=س>ǍwwϟXZĬTWlB*fdd+ b$]X3JX|v"/% ߫ 6ϣT~r¼׽_{b../+s_F_Knui'Q={Tv{ R`_="~XB*!W)BB*!էUzB}>Fc/被[x ܖJg}{nTP8Í!ݥ)D5&J$rJ-1"q>PbVf ϓ X>uf(Kz%뛳2eSL?~?яN:餿}wRTBo=TgHygy楗چo„ 3f,^!xE^NbN+V#ɕkU-p0!;! ]\)QbZv%OD:O)P>.{|j|Ջ#{mocMpUW9չFlK R l?Dj0JHa >B/O+o~ɧ!D7U-2^=oG84A[E! uuŐG> o(A!U=W)xջVccK̷7oҤI^{vi'x R` jKH% R/`ɗTBۯR~ C=sɓ͛G 2{Ė>dWCp煄[Ο*xUofz\( ^цυ yWǴ3*ts@WD-s䞯T&7#{KtM.\~Q0xڿJ}XBj"B} #Wuqg=vةS+&ec1<뛏^">IvfGڨPסQmm*shzv8+gI%!WSa%54(5x_ \kZ|nHl\h#\W\q?^XB*!Շ%n T o~gqE]tӦM[t)TVhqp-U Tz~ )spb1G>5)z/ֿZV=VJENʭ [G+%J벲lJvmR|k_;^R6K%$RUzKHU[B*!Շ%n9P?/'?ɍ78{2<)bQ[,3Ś^VQkQG^֢%[vAq{oyE\ B\ ^h[ NAE[ћ@)3:|o٣[Sj$;ׄ;e4j肻#qǏC=t\f [B*!ՇUzKHU !ugutӌ3XH+3/Z|xubWU\yA7m"_[ PII|P̺J¼]\%:>Kl2Ǟ]!2=F98gN%R}XBjJHa|j 򕯜z꥗^| Ċb(r_WwC->Eezr"[kpxtC뾳3^:qxxIݤ{fs٧9ϻ-ӳ>DnY{m,V`^"IVXA]Z@T.|CC98.;y0_H R >s5XBR >,!5p :=zw裋/nu]˂_sީ"R;JX[)F8z-BV'_x1{K=P]CE(0zѻ IDj[p\ .@4a _͛8qĈ?8ȺS%R}Xgk:# 'p9}wϞ=P[9i[Z\ a tE\ mDXE/$y6Woe%8%ocC]A ! ٟ颵5k1,N)dQ_mg=m$I{0];.I: N0\^~qBjYB*!Շ%n TVA,!UmDC;(Eŝ:\EΦ0_k<ȡ꒻PhE>6T)@KkU}g7׭s&_'^.LJw>ƼwCPRQyrtAQյ$էƕlR>wI&s{uމ+S,:5[q+֕+ٓ pg'G~09(zR%S,tQlG '?%eޫR>]tY&N7n]Ɏ9~Ԯ,!Um TN8o& yW~yxd{N‚Yga[$Isc16 jEôdH7b+1 fLoG6٫+W6T+LC#>8^٩!_="{D #Q].`ʇD׆BoD/&U>zw^~姜RS%R}X5ؠkTBjPPIVON=ԛo~G*=I5Xy9d(ѩ+x-؜eb.`>8Ze.RG^>\>ƒ+{1})9|)HU+K\ IG1| 1tGS(on1c]wu;ƯR >,!5pKH%nHtIW\qC-Y}~ 5~ea}#ؙJP-uD4>&bŊ 0ofƪ_f aHDKx-'Q,xŠj8TJ,(W>Q䣫:E[1u4Ycc㣏>z]tQf5ؾ6t,!+~TB*YfA=zI/&Eׁ-V.Zs!Zje?J!R;Ђ/8`0Բ*.}Tg&xʕ+*"!J?@b/*շ y uЗF9!^V:@J@x)hfμ?z RkTgNBsԞpRw'.\qc:c V͂oh[[|*{\>bd,Z*[A6\R⇇<_[BD(G< n>r+d`D#"y]ի/ 47C.KBE//[zǓ5X-!ߑ>JH S{ԗN:766VŊ>yLhA +4q jX8^;U *`9<9T\6~:/Sm~%\"V.Z+cU0ޅgR= 8v0[έeܹFUWy晇vX}V>TJH%RuTBjh 5TK_~7cI[=+"}ERנ]:5XYr)* ?XvЖୄ)#zpy[uJ FPH*NOM!tbI4ZW{; ߨ%R":}_ uQwލ7CM7zG! ^JlVXB/g: /-_zmkk)mm>F*  9%Y ^JhNgACW/`i#?++FT=n{B6D$X-:v`40s^80yW/F&;_1>fE[[mtGG7- [B*!%R"DUڀ:ꨣ.pԨQssuCLѨ+BKUFzO:!F {\B,jP +p@E<ge/z'Q UtQ[*+`n!*/Uv!ywzy@ӛ!5eOwm|PGjZB*!%R"KHU>|)S,Z(԰J ^ʦq'#La풫2hEbSq`b1 $fXw[cW-T<~n/'A,P[hGc9F ✫͞=ۨ5SO<JTB*! Hmːaj`H}{߻nqƌŕ(C'ө _GG΃xJA_ _XŠUp~cF7֞C_jUkk<E+="{.v (X..z)Fm"gem }ɌP&QՂSD !A>cs!TB*!%R"KHUۧCꠃ:#> .M[`oɆNru 2eP5} 8]^Ɛn:|">Es,^C }Ho##T"khTo{QDE J"Bm +:&5WA\`ڴi#5x%R" OW}se?lYծMeIHUVx~>#mK>B$_Eϱb%bP{Q΢&9uGe:OS{E,Dvg*G===ݶq\vm<23G^$ N(OȻ 6(HұdhhvSo喣,MCʄTBjGsKHH`9Kv)r%\)S.]>T.R>|%EܪXFhW1+4REH_-e!O+."21ܩD+!H,xrЦu…]"ۯt'* ̟$>R>Fn'cH #5>)Շ[*󐹶;5~yo~3B|O$R'D~=pkjO1#!u@ u5'R{Ԑ!S/qcǎ8qhIĹeMtx`ʹOW&A%retx*j(.Q=\L\(}ߧ[)[܉Dm7mSgXO;u8'Kj'tq[e Мvרp>?[S4 q ƕܩ)S윝uYLW/l>ބTBAjH٧AOϾ馛~v}%/7h;Z+*>y"3ph·#[d {Awѳ ](& ǮS uf>%hك*LFDs IPqqr}{Bl=T )r^^r NEYyeBU*h-l& 5ʬҢa Uecz뭗^zg۵R ~ZSO̐ڵR AF@O1\r%/kll~4oΌVh >>Ґ7Vri{}]8*y^F^Bҗ#ҷEOO+:"|WgVȢvx]"}Ww-(u\B\>rAgֵ{1c0{}:>-Wes@Ѫ(iz>2޿t)SʳeRTB_V` jKH%Ru׿>bs(˹6da#8&IK"Xχ0͛5"\\[ĦݓrlbҶa]̞]C3GhگJvڵه±"~+1VdVo#âǻ60.\bEut|bqG$F9`p񿡙do⊳:>RjT%R ZH}]#,YxN[F4*6%UѼ B,F65%nE8 ie]Cdzc室#^vvCauaҟ⭬{qjw8rvGNCT]d;}DHg)T7Cux` iVӒ.9Ƌ /U'xӧ_{#;ZB*!/R+kT%R;RClTmHvawބ .JRCCU-_=k[N;=>fG)_ж=*##c G +Ӆ! 5b]ɶC!8ewJV'yateJuR> )NPH SYQR~)Z,GJv:Mϟ2eܸqz귿!TτTBj#];١TB*!/R+jTՂ_Lj fSS577{qZ rf=cMDEZG֓Pk zܭ3llY/8Xw.Ë]u鴭h}& *k0e_׮KiV>Wƥݽ~Y~RynP˷CV]>Pp*ϣÊRY8QcɁTpSvn0w>Q(=!5pKH%RCOF_ڎklTj7*_\6%fEJ3{@ِdwb+%_AõbwTubf}+ϚgO[ ٯзʕfK d [}*bEiN PAhK` .X%GݾJEFi5LZuTS<`g'?)tACĄԁTW 6tTB*!UWD8QΙ3g66GDZ}l7B'FӖs#=3"%SA G>ƁZ(t!h=LJ;8:z]TUVV"ZDx0֬QY9۞P6{ZTI)ʞJ}NoeoB1="~%?ÁëL;݂7.hεjV͛gΜ)rqZo+,v*H4='Xh)DIR呺(Vں6~p+(T-ZM| Wh;Hu&@{݆ (1bj v&l,Wt~+\ JVx/GRW9/ɐDZSY;•I 1y^A8ƏAq|mH`2R8IH%R#Q |*_*!'`rIHq?~xŜkC FWue1?|ѻdMQ]MO>+Wj-vH孠{ɺB@b5D l?{ \LZQOpOR. G eADP5<+^JP]ŧRe5 xMK{-8ss=pa _UJF&T+{W]o|6jy a@ cNB-/JKjGCN;[ou޼e˖Q/sBY[GX%VJg>㥊ud?[#®Lh. .W#3PH"EB绽lo}Um TtRGW긿nƍTzGh{A%~}nMLle_C9wޒ*`[;][QFՄ vfkC\ "RU!}"R ܆zꗾϾ+ٳ|l|c|| UȓndSgEUjmJ;ȋ ʗjzieGm"9*8b]Un6l@)t7GCWrl"xj#13"W)۽g`O{ӎg}IT)<8v <[^Т XكulCcƌw[RE }JH%j TBHz#GN0a֬YK26Q--7No+:v%I[Ʋ mv;cr+5ђMN Ū-FoȊɐU#iӦ_/:ADƍ"|8Dm*|/z3ol-YeWدOGu+Pj';#BP2/TQU\X2\8JdG͝ h544̘qw5>b TB*!UWKH%RuE[E=vlMSN'٭#{K%.w^QŌ/AsBˇOpzt5wiy=ckIooOmܸMowGjmZ>R_G˷"Z0{)Zu{֐זQ`Sف*zBmerUr.̜[hCsN2k9ODP%-!| :Gq3gdw)!-ŘzҢs Ee>6vUdDJbQ+P !cTU DPO62XcWk{67nٲV$kv+]rE ;",E˭#_Q eWU=8:!eTBFmZ'55J%9WL\ciӮ3HHR T]-!JHB_?sƍ7iƎ<>jB`8xTF%ϓe lxJ HGUd 0W0uMNo,n O+qY~5ݺu+DІ :7qba+ tLvK^ |"~zKa-u38\W/NC#}mvqP1R9~͛ww_qw` jKH%RuTB*!UW #Kn%0k8qC ;ýx)>Nw;rRkmx_w8PceR@| 'a>X@۴ /guP_=U9~}G 퉕CV<_H~4Gj[FY#u,fr\U3J){]YHgg ! }kSӲe˦O~ugq׿~jRՖJHTK TB*!UWQȠϝkHVN9jH 'rh@8B@ . Ųp^JRnB>"JV*mU76nrF8B xxڨzȸQqr{n|(dԹD0>* DЏ֔hALWe#$r#M [܅G[n_ t1'ܩ_rĈ}MH R ~Z^%/;vĉ.9='m#\;g"RhmfJJiΥ rEFŹ2آ|e.R7ڭ-K:[-l=yv{Ss"C87v=oU}X1G+:mTGaXU&x[ΛEM6f̘;.!UGKH%RuTB_kTRGuevm3gd&o'E˻8У>~E&^DԹіy%KHfQ:kP'|\eleT^^nݺyBWH@7oVo۶mO<'xOM>i @:B6{UyxThD?4u8/+i89&8WѲs\ ,Y2o]wu9$h TH5` jKH%Ru?B{7oI[6J65EPAENvW&]PonR,.,.Ku\Ѩ?$m.wŮ ~5*nȯ7_xZWmE;XmD3'%¾~}Dz71ޥ6%:BmŐf{h<ˤ&ϩ#[ʼH766.]:ygVBnGS;R:R{NXV%w3nm[>Zߵpx^ϕ-z"WD/18="|nQNE@u<5FktTl_m>X1dq^zhz5ti\ЈJg^K7ƠqQDk=#Cluy'2䁣dm?}(ڼy۶mpf7'T @5 oFmpoj_u"ūxkR)m'!^B ?54( }8M<wJ_QUM:Q6L}W_H-RՖJH%jH_}cǎ}&:%5+Tz9+RʵPJH+ǔD]uje^E _2u>iC?:+*X粆HuՐ6mM7%f TH-RՖJH%j{FN>;?gi5sU-ãB>n-pBR1 vj;Ȭ9jk\;Q6Z#bQF,`loX,v{QKٲPۖ|*&^oEjX>/ ̟}G_eu61[5ɲSAJR2$WvQn(#hnj9:ܞ.z QNO(G+Yfq\7RkT%R ZB*!U#uW\qJs '.S5hIkBC]+lrϓUTęWc\$D46l"Y CءxUO=>^Jp1 wlʼnV54"NX$ZHPɸD3z6Zzŧ*{r~"cq<0z辑ZT%-!JHժ:ȫzԩ˖{ERY $Pn^ qR"f )_ ҥmV˫'bHiQV uEo{fʮo =p^ T#<ر#l{ eB L_&4롨wپBoMbXB"F ans\pIqV2_x+VϻUU(t5{n3fLB>JH,!Um THe/Nmɾ'RÇ|s!9m9޶ꕱs}N9[voJsP # z$GPTǑϥB\vi ­wu)3es"۲c^sg gfҪHd] 㓇uܱJٟZE$JS135qQgRL;|CEO~,ކ\3n;@jA TB*!UWKH%Ru^8#&N@Jg|AW$Ek /#yRѶp#bnHG]]¾ՙDyߏ jЖPѳ MIOfn|+{hLOQBv߶ڵr@8#6F IDATnE|^WեHbYh :|QShX /8Req޵hJP6Pn[]ED8cв46^z~GHͯRՖJH%jHs9w} .tE*lKT*Hдg ݵ5¯nu,Ar(Zɗn,lQj]RV-= .z⩧vɳ޲mn1~i^6z"lIlQ9]]l7esBM.qu6E9J8x$s>UJT"۝4i/0!זJHԼ,!Um THͭR&> /~ar˖- 8%"Mx eB+TR!|. DdZ/Svt#x5*lOLO}n (mƏd6C^k=+A"eۡzbGQTxH(v{, mET07D-n^, ^GUicm@ ['8@ 2x3^l/kqZ !R]Km*GVTo`7Q?ǡrYYKP@*J,1t}Iy#AS1+_R "yլ0 ecΜ9&1bORՖJH%j THͪR6SNҫ[n7Ouɭ8,C53BGM|)fBGqAm_ϩgŴ j6]{ P[-.|JTaQ~.߉𪈟s4IZ "5D쎹I=_dEvN{ZpmC`Jv9wxDC[q Q/ϵn!5O ʕB)MWj ř%&V2ub (V?Ny/>R{a TBJH,!Um<+&Nh|)2bum%+X *]Qݠe+sh H|4@zr[*⇬+!nu18rl7TTO<+Z?3ыc%Y[OU8E-Y b+gO޽n_tcg?gy/sN] 1 N?m* I⃖Eh6Ioz!HMe-*.XcKn2=:H9sz馛.*f` jKH%E,!Um TB6vW]uw̚5b/A )fE1|'F t>&ǣã3Z] \0>) Ex]Mdѱu7h|dw[qTVN-Rr tR/O/= ]/@ݢ2- 8mh''"[! &"xz[+TvJsGu(gIrTA52\ѤEU!.Z"@aә3. [B*!/RkT%R ڰC= @.Z*99ldJ:2"R6}>T!uDaC7R$hN}  .055͕%nasE/d+peRq;e 5Dԟg5*LJj,)}ě*הTe}PfJq% PFE+b|6N xĄ-!kT ;C~ɓ'744TFuW&E`BFQ]՝&D-x۷PD"}-NzJI 6}$嫘BDK4W*ωEEyT{3{rc.z_-߫jOTXUqlVqb{ A x*{>mHBI""䠏nBtyuW&#\rRxU'$"9W,8ˑFh c0!5pKH%E,!Um TB67~߿dɒNlnD&CŊ2B\1f]:ATQݦBm_KR 0bf e>؁%|c XFKx!2Rff݆>+fWzGE 8.jxG d'\7I~cPd5֐n%1>Pc*ھb&RvTE=PFӧp 1!! i5XBR ~JH}Zvt#<b T`zZZtB =$xӨMz!lFC? /櫅c@=o+m!(A4E!>yE}^Rgf"в:g26\'"x=Y` ַ 鴸vlQDnQo8 0p6G~+QqDE-DI0~5/2?|#RTB_` jnqK.mn.|q|׫Xánh&AUIo5`+c 7љGo+DF!ǡ潈!Dٷ{(]YQx+G LiXxCyTٔU:hqZY̩ZhS!WrXZy\IN % ;۲)_ RZ U^kw*O$z2j_ x ^%HjX^b EI!vU yd7% /'Z%yQUvZ_|J! 1fhjCn"流UX)d+dҠ`oOeYawbø H1Va^j"L6 $W!D&kE毽F2w[o@Əff@dNq̳z(W=WTjXh-Qy^ [}H[ oͦ?ǵ#AuEAJ.-:K~J;lT2cБOE9ә]|…se [B*!/RSjT%R"u acǎ2eɒ%PunV:hlՋq2?J+B42E>RqTMU;'T-9c̓aپ؈H9Ho6oWzw|2ޛAoRe*̠B2Rĸhߖz\;%k1TjDߒVuMYMT塙ǞB(Se |*=󥌑1 [B*! wR/6v^f!jjoqEޣ#Nmrل.t.US) 'n٢2tF*f*O4 *GXb=ywwޱ;[o٭WS oGвgPNgl֣oɿȟfzE㆘%5~YhHQ]JOݬȞOmN2zmlkd<OPQJ2zRՖJH%j TB6l̘1Fey\^\&p@L,_#͹ΝRy12°D<6Ww<.hOo;S{N>5G8_L5|"jaBJm˖ |-ٛ<-1"|¹lBU-^64': .(qv]f2`+aʔ)UHMRՖJH%j THMR6lܸqo;haՂW"˩9{N7ҲQvuw۹_U?شɖ.\O ;|ɇb!=K~ՐڒFXѬ||&P_?xz?~׿zgq!{WBX>wUQG|l7E К fo}X\5gab!?o2TX.8HrY`δ(3:u5 [B*! ?~c54445ibgWf ^"^PS}ZrI0 $喹HIÄV^MU?[fNJ- aya>Y^upeL<ЧNyɛLs~Oq.]:sq&` jKH%RuTB*!UWvm͟mQQ~NND(jGz 7^Y@ #S]]mvʆP#j m`6̆ dȌRm+q1)QNA|))EM{ވ{; ?]G}d?^{#He_{MD4oAiP ]rԯ+ïZ#<ѽܸ}L<΄fV*щڶDXU5WT1Zd6ڭ!uy{kT%R",!Um TB6{7/S466F%Js*tA(-̏57=a+VPCM==LI1JGtvđ"ū5s_6$e4 ˇ<(C?#[%ZQeF!kDCy9|Pj,C6*jѓlIYa#R:?rm>iYG,똣@FhH =v6^bϝ;-!{jT1q3񹊴:Ӊgʕ1AЋȡwwx"{#5xhK,@\R~3(K( G )[F>xd@7#~ >2H}ァJ#nx}V6 %H:iC~b^qDC ]zuvWnt2lQw"O W8lR'R.\8%!5pKH%Rukl8\ ՟]k"tvzCIqϧ]Le=6=zb<ē/ GegsL|WPx5Kᡓ7꽈aPM~;C*" }7,tC]MO?`{%+q_H6JzxˏdPBTQbN8Re˖Reaw`#U< ,! 5XTB*!5HU<0,]erhNf8(#hS%NuvnUGYLkϚV^1 hdmJ1/J<% Oe~L'FKԻᆱr>P~}Eq=+ˠz`zfEQ1A 5y`3Yͮ-p2Cv!8?E*q)zoU"b^1tUsR"Λ7-!jOXTyxUC}("[t9Ri#$+e!J̢Xm55鵍߃C {KH%E*O\Rê?7~ THY̰=nˤou7Jo҉#( =ҁ'w[U  Iiǘ{W^-$*7V&!5bܙTGLt1"v=ۃ~5!RQ^s?hM@4_?ÛR"<~|zUOe;!< $z%%#E=z!)ǩ;4(6dO=ŁꦦyQ@74IT>O;w-!;jT%R`:YwWŔ|rUO\=o;΍PC"3d5tgD2:9H!j*VZJܘLj(1YwD1l)z4u<ͨ#U/EاW|%𥤑O Vy!/D/$Ã#DQij0G5qpm1Ժz@a*x<;XR!UO&r"JzqP>mA"r?=qv-XH}@ %R#u{ v`!IZ,ъ(FJ2ZP0vDY+VP+5 IDATRxyoP($A{m1}K}[d{wY5ҫw3l_n2GPOŖ-__O2eޒ2ޑQ!D3YgT%=C!ifB+ܢ'SI.3Fźd٩@KJU@w@rqEW-=ݢ7R SRTB*!UWKH%E,!UmRRN>U#ۢ~,}y!f&#=E9qն:ďH2,\{|J_"! ~",$?8TEMrED>DmjA#"|8R ;O.Ծ{YP"oy0̱H-)iO FBդqJt~.V6jDǞs_ɭ]#K%`Ԑ^A,!Um TBJH%j55~MMMiʯ+gRrS؜{R;#m|΍Vk1k! (OSow4"K EQ%z2WC*>;a„? {衇 P4 )ͷ݁{LoS =Q>.4QԨ,7ZZ+"K6ǨT ->vOs]#9CMYe_pK.]p[jT%R ?cg˖Rˈmm;xHV,ŹfmPԉI DlPxD~4ȯj4)K2@Oy ;C0:^'KmhpWw|٭1eKT6E-b۶J]NJ8SG4^znѫ*;Eou ՞-AoNXzWs_;x dџ5 ]oz7,\N{W^ܪ͞cB@Qv}T"%8y6bZT#eeߧ=6tT>R~zKTG&>--ܨJze^/D1:#4 zWSď\ ?q<}߷Ɠ|q$od˜P1YEB)T1wԬ^nc#I؛AT/AtR<>W{LO=jtWnk|5kz=Lx".xOpuAD9lhe*"OQ4yN/Wŋ?RA,!Um TB6{mhHdWUď"H ^LGq 줋*җ_ J#S SGMnrHI#=` 4J2{%MMQNC,koM_Wr9!g3HQ~ U#"OK/lH[ʧv1j_$IgūX" U%:I}=o2+=ʌ4+JڽFShU=q+2*Qʶ4JH R ~KHU[B*!/RkTe-H`-[&_ 8,)+\=o Gqf+fa|.N-F ʁWXA,V+"9sLLH R T]-!kjTec oHԕljBJ>Ư!™CYZ6% .?G֯ߓ#aatjNp~/H%mB=򳊣j~;Z #k9EƤ H\uFHqr۷i'PB8&Re˺1Wj\[J(sr v5 oȎ5>g$s%L'SR@XzѢE TB*!UWv=̟&h9PVD~-窐3J]+C.IF~S*Ə b"~jfwlO+_PtwS<[.bnfB$Jz3ۧ%vjZDPD$yDyDkב4B鹐wR1Ᾰ0PbwVi/P0!kp W:4ՎɁÈ,(?\\Y+"ut!V `ܹ*HKHU[B*!%R"5KHU[6cΜ'@G*|, pzqf1PYE׬1( P*BH~ҞF\|xl\nw&BcR<:Q x"}G =Li:lD4g[LqG.&]8*b*J>mWS@څ|"sk+WbT[AY#}zΜ9sKH R T]-!5XB2i̹s%=|y>a_iN$hcLG/j5H_HELm&E멧x:OϻkA 7HGP3LQ$7p;7} ~qHfSzRXme U !e'²1 +@VA{!RE#U .dƙ._hQBj/,!JHՆM0GS`f4kqKu>^ٙHPS4"/m# luwkcG5"IQd&b=䓛6El$Q$&"L| Iw)&׆VD6|B'JII>h *hm4wVTZ}vZT P*Lbnؘ׶LZc:W:^.S=h?{~-FqdZ.Ʀߐ7o^U5XBR T]-!kTefIs"8V:UdQ"ҧ[UyI! fM6ŨF-zIUV5";BoT/h10W3A YXf.л7UyQzLsin*Sӯu'#w h#AuT?G}[23ۣ!!'XK6K1@Jq&N xmUFǚvH@pRMrI+#I}{{,!JH%j&R3KHU[$}&fuhvLnlu"ؑ}Xz +Wr}@^njvo5Vx c{o6:|'ݶoPl({!vglMd?"ˋ$!H~:^/zK Hq)^x!4'vI~ɥ5MkHm9N7qш68+rQREB霮pnL\KƜ9s= [B*!%R"5KHU[vfƵ;X GI\,F^9Llu"SϯTuy).)J[YĆLt𸨢83]]Aa /Rh:/VFqo c|CKV@Bx`7JY_NBCGv"v*Q3hmqzgRhLb: in ZD<F{?+&ݹIwڦ5N45 *_ ""b@TA& ((PEP Nm;۷տg?k$p6H1#hu@]JCk=#Ph^3Zo裩}R>\ :{ R!uL*ڴV旮E~]{z4Bn8 y%~h QR)]Y,Y2=@jP`T@*j ;,شie|Y{U)^Z j8RKѺʕRts|%+HTIc{M B\4y(-#i8 &xN]mg-ݽUʖ/_ %KH>ڿzp6]kf}fF m\>4sq&&! Z6 #BvYNX^]VW=yDF?owgN8ɗz!lRT P6eʔ߲e ވaIB1i!)~N/CzIaM *IkJk'x:oYW|mE"-e 4 э(LsC W PpZ ƟϘdoH]ClszHZTLAQF6vNPT&SstвKF(CNd&Q*ҥ-A+&z 3LUt~p &`ѢEs- Ejj H- Z /I/ %w9 ǯ&7Aد}I6@ZLUF[<aqQH)8H Ř1_gLD9w3J<$95K$y]Ck#}ro-vÁɷqp AfHN U~8镐w:P)\ ^i~pbmm'񴺺6o /<HoT 5(Rw`T@jP`TцM2wM} E j\:u%Dx1i22=ppū d4]_ϬT2zU|( (ܨ!9y Rzv8;(bC{Bf"zWa[>1-_0utb$4~wo~#z2A*(˚ yɴ&V;)eտAYzhM2ѳ[) RJpP yl'˭ hL<-~z+_rQT'9}ukZ > THR"٠8i+Vؼy3)^VS+%QnލS!&՝%vsIjя8Jl0\ΘT$EbkiӍ P)#EAE43 0|7l1PFR;]/LڷO>)DPď}89:zX uR\;}٣'wL ;묳RTT_jB URT P߾p„T&AB&:AjmcSwuLl."&‡ox[EG Ď8u~J!B6m bA聱>у|~('~kZƹ"hV*œy#t$,ZLg4qKaz uomSIaoz1MjB(۷/;"~*gҘhY{tebZ3}$f.\J6kjV"ҙ WoAbqHCKKPuɫ A!-P"/ܘG"QpDaOzrRP\K0Шx#H~Đ#Sr-Ti .qYm'v^~yܹ~9Rj@hT 5(R`T@*j iӦ-WbsBPE=HrvFB'J̸)naST ?E^Nʧ?JXx睴B]<Ot`-P&Ñ ~ )h*#%ܞRD:(;[JR"}JܧtqH{C2P>un]]Z/$f{w6HCqJJoi 6,]:yK.@~ A !O4iѢE;VpD֣nՊG]3lo2V/k&$ $΃ (`Z=6'˓DbxHnUDwJ)&)o]h_{@*j@hT H5ԄTn皚hPq%+k|cՁdf@T_bҍeh4XX.{(2}fBlŘ$g[8,YZS>A/AJW;xW! nUϘKMH6CEQ" s pi$r\zyΛ:W>U\RDG.9[_y{@O@*Ga8`T5j̙+Wlnn:gYfrB w::Ԝ͹NF!S̴(9u MT@ʵ=(H 9}a/RnX$ ')M~衻Oȋl:{hB;2Bpz!% ;5#wm+q6վ_y7t{SWmK.5kĉg}KRjT H5@jPg H;oŋ'yV/K O2HMhBEzm%Q+=(>Rʂ^N2?$:lڛ,N$_UYMWP2,( Lď|$AdIIp5 AD݂wI[<7a>a"'ytz3{cw,+jt0Ԓhreo/ͦ&Ib}h`T@*jRT PGO4i޼y-^P+Ra3ιtaqɧ0牘Ӌ+%̲ؓ5$#}FAi$>?b#<ׂວH跷]V ~)5LrMCij'HOG 1W_@Eo^,(ѐ L^ǐ|.GO19ӲɊueIb- zkkճgϾkO9VCu5X U@* jH}k_G}tMMbek+#o(O8UI0N8P_HZQ,~8IˇŚ; f%+vAdzEa!x$&ߨF"ydu :o}yřx6|PsPNadg#|U} Sr &Ew5fLq_;lGj7lɉ핽~~_s5 HoT 5(R`T@*j ++;FoϭdbT|mޅO|޶MʴsM7M*Ħ𞄄iT-2*6o:vy/Aab?c/ʱ7Pj=LAF&)6|_ ,Ca[<Ϧ&}v59E;e== كkuX'uX(*m/tm۶%Kn馁 HRTC- Zo}+ lެ^VVf&M:8jsf @/;rnl> B1gGL)]q^#Crhx<62ޙ(VZ=q0|%yR뢉C6S2t S ET‘ActAw?4.o뤯VmNg =91]qꬣ$נ(=wBw NtB O$.BBwQ$KM>ymu r6ڻWSşN~&z%{Aȃ", [Zjl}RT P @V?ܸqӧOuءH:n(c^FqbIR%f6$GAuғ4*HO9iuw3kPZǠq)!Ps;-(46 ͊=+ 7Ю:>u9"$l7s\Gnhڿ_&3;4`Ȋ'Kp&z!M>цMeڒ#s=Oo("uU H- Z HR "R'4rȉ'Μ+x=٫0+ |, ˥{&54\ KS$hw3$CV}@8OKdъVu8"-Dc{\]×Ry.(#ü|(>2TO/;חF~MTbrpܡk>UgvzfU6q۷w rZZ֮]Owߍ7x,*Z H Ԩ,*Z/r̘1'>3 йBԯ-3ÑN$P/r&#- Ƌ[#םM}|,7x_pXU2 @oI<|(]o=o󔭲}}?1#C$ζPV=l(^XDPHr˷Wb([Bha=J9Ǟm v&T͎4ʹD=lwp+F]gn˖_~yΜ9w9vO<1jRT PHRBW`Tю_ K/}G֯_]83>Tb[zz\=ܪZ&R 8 KgvZ? mQ:t鶀mA1+D0$+=hYGLB (ӥPMΡ"z|d`! Q) %m/&WThP ݽM܄:4\SHC݆Zz+o3yzq]rW@1HR"uy Hm .t-Z&!E)*mqiL:#~]*T81 prKh5"ȧ{~H[#6,^"nܬ%?E!ۏ#t3YsÓ9}mAim ntkb.(^VR4;:ReJ =!!fcuQlnnnjjzx;YTHRTC- E,*@HٟcXҒjkZ!bbG2? bdۭL} Lї477*ɏ:j}Eُt(=5b@ ԭS^q. dzK QSW$_է#[^ hyS|#҇TX#%!:---r}Pc]vS^{G;R@* jT 5(R`TB*#,Y" f wzQ mhLJ |yP'i9rPsj uϦشD%A/=y`IE G6ZX^et ']ؔMUˆ+kJ6mwvlsM6mOCgiX@*jRԠH]\ RFJvKhd2*أ LCr;fY-^2Psy_.OgR׳ی!<`j>JUt^ BM VJ֒} K]vr醀PܽY1Eټao8r<@qtq |=&3!cX9*:H",][b6ry;&WD0ЩU }.=+B{m2"}U)Г&](NYv.G>'uȟf:1jU\4i+tPB .R[:%\Dž!Tɾ\%W~D(ԭZ}$6Wrsֆ .;R{#o}@*5 - ڰ*G*رc-ڶm[guΑW{ O!Xm3|&)cȈxC,K}oGK~vxPތMH;br|j[Ľ?O[։ 8j Z`oRAWiz_(lf bpL~7b=]HC¿.r4osMӓa';-[6k֕W^YR?T HOT{!5zs׭[W(imF;q%N(*yZ%W]&$lA-H5ڽ[MytkUF2t%l0Y'QADB[;Z/ɳ1t:vq&"|y5\Hc[c?# lQp.@ ێ /p֬+V CтN=oEB@3JgyhT¤< Yz(SEVY.J\3a5! 0:ducSF=|O{v& Q FX0c _9!O8.(NJkUtEW6PUM?5/ɓ/@}ۀT 5(R߯hL @#7R{=,ZhڵH\sDQf,/g˔ԫ&^"JA8q"J|EG4it~|#9 W;eYS}_q=w&ܗ"N$h%ܗPРhQ=8UKwKr7{Vch:OI+4I}PdӳZa^~@ꃱ@* k@hi6q⣏>xfJ;8ADNToR8dmaDzMךa $UEceVw@ v IDATeBfC|-U"é=ޒ2FWM B5[),n>"׆}^V{@bylKm[}VmNk&sMR-vlu"GR&ͧ#mׯ|K/Nz >, E,*Z HR D*yM<_3)d'sa3yT ! G>s"z^Aʈz9 oB/ΒV VB we " 4{ !fbzv"ڨҁja3,ync3VU?Kg cmU~W~xĉ#F86ΫRT P @VR]wuw1wܗ^zi6 ڎQbf(M]m{Qp-O'&ΫvY? n'$O-Yv ~SBPby ybOnOH{pܵA6RG;rLM *`DNǾl=4 MoܸrO6nܸ~džwk@hT H5jCSN9r1^{߿zuKKK:WtOot&5Fv12>;n9淊˴hfɷpeH )-a 6 Ճ.fpgä!Ee9D8)*7:l.'Qm<$VZ`3&NLNwsk@hT H5@*jՆT}s_ɓ'?j˦TS!TV\0x qɂ}}mS<2 enET:>E؅"^Nxo'/tKYT\(LbR~ *vtq< V6]ZZҏg9s)Soo`T@*jRT P)+rW^y&cNpI)hN!vƪZ7xb= :]6v~<5淋EړyJxS ?|![nS 5p5yS`%kK*_s\-t@zz8Yʡ Z1}ye@he}n۶^5r95X UAvRH=,*1//|aاzjkf 5kmm_oFuk*Gs|#e6~zrOu媚>eo'Xh͕jx̅Dž[?yr:ܕ^U9+Tq937o;w]w?~Ĉlg9@*jRԠH} vH {ҥKq#nNADOskkJ,x(E+K9J Cm7a@xHAv[b___wD1 2(5Yâx "nދiYI *HЦGЕ N.;~m(*hq:sa#\E/6W6p9Xj5^|~`x էU->&8wGI蕬cY6UF׳ j^yeٷz')ΑV H- Eok@h o5ꮻzɍ72[TFلZz Nj|[WWB~#;"VXӂz*HbE y,M1VO6d%_iEd08`uRj][mFh1p )L[je[W]& j.W .~q?t>JDM AASC=eJjɒ%=.{]vY u|- Z H 7j@h u٣G[k޼yK.͠טp;:Л&HUQ4Y -V:yg]ZdSQV2| f,8dd'r&W1=&MW Ogtr4`Ogw(x];6*y"Nk)['06n\bŜ9s禛nqM 8 A: HRTC#F;okָ㤮^O'x)+HcY۷WLGAE^-OQUԥwY-H9TU{$Tov=a*&B޷lS#$< *p6[-{y,b{?_C. ܟB0%neLqO).`X .˲T^rfte-,6oMc}%Ӓ贌9uk0O[.g~X&Rg`T@*jRT P裏._| mZwDd ѨXSq(Ew\@j],bӑu 5V4jy7ME&xܮ WnX_.VeliS(n宫E'/JiѶ@D=K"=l+V̞={Ŀ~4G,*Z HR F uewuܹsUo߾.[O񖭪kJ6hЏt%+DXd^.-!˜VٍLATFWw 'ճ'Tj:w~Wsڒûmb.']Vrw YVlFe~?^c-lٲbEM<) @*rN@ѓ;cF/7.'x⥗:=}~%wKuk^x_fyOY_ֱ`=vYs_M.1:޵VBܣ-YgҺvZe: CoX=e\,w#u;lq7N"@+ 4P%pwn5l/tEՀz}u~'S9IݖXNy7RƥGuAt4ܮΦ 7/|'4G,*Z H `Tx≣Gg}vݺuUiS?I,bEɦNTl *ZJo}.jKʔ:-l䄖Ou'|Zz6=)}K^%S8hӳޥT̼lc_07~-s̙0aXgHoT 5H_5X U@*aHƎ>}O>f =qx%i0N|(>_}wfr+SϛXUS@(ꋕNR~|RλM\o6)\x#ZYy]tP/GƐ6۷_~믿SOmy̑j H- ũXk'T 5R[:眛oyƌ>u2хǘ?NP },2MɄV]3Mo[IVBj]]6?@'}]w*'_޼ht\ҳ]k Π=~Kɘ.e--7o^p~tYg54J H- @~k0Rȑ#'LK.]n۴ڶm/3?zD }mj^j:Κ K^.^/= ݗ6d]֓_}+(Nw^ķƏByUhrHЖhljZx7fHoT 5HZRE ,G5X U#A̙3_|uGlcMhQ1gb|=Y1TݽZ:J.dRVKd*Hz W`FKYSd, 6N+6N9.ӒGlIvӻ,hآh/O_~es?~g>SHoT 5R[ H `9R_}H%;唋.hڴi^z֭%RcI3 ¥X8F lyۧy$«œubY(=[|zL, R{_']{{{۲U’dt]8El >_01]5o54a*Ct}{SSӢEfͺ[N8R[ H `T!}߽gy&mRR-3:Xsvs{.\%+^.vVxL]$(!Xܰ%;ͺm^}I*lrDBST̬r1)$ǷiB$)tYڴ)G/`T@j @*r A"+_:us=aCBsի VMH^:/T W#SqUL`%oQn%ץdwܺpTU-ϵʩxya=?[Bk*nmmuϭ9K&Q)ne37)6xӦM $n3<:oTHRX UR#,*6iҤgݰa$լB>P+Yag Pi<-}V͚4 1JŽ(9 ]6(jdM?kӣHTz>tR>|+,=ߧHߩW_MnԤIw=ܯ|;eTHRX UF9qc=v;J%tU)ƢcUՐEzqN-7iTm]O \B GC{B/ftW>,ĺ#Ɠ*AOdžqS!o3 fQ&TNvO}ݺyy睗\Sj@h^H}:zN uKRX5X UԈ#nz'֬:ޒ)#%<ic*6Bβ}EɩrMwUր^u`BV.<lI%SH gú \t- sNGŌج\nO?8@}X H `THRX_`TюR7FN3祗^ڸqc&3"0bp)|pRu#Ǩa›+Y^:]]N]>O Fm^^G)Y>Ile}v(\n,[DIƝ^Ì͛WX1gΜI9p2 @*x _?j(y晍5|0} }r|VOHoT 5HXRE;H%;ǎ|njjj)ʶ"ZV`^MVyg#AWN*{7v~TS%3Χ)dY{˧lcY]&m7|Ӌ<tF!-  HRX U}H|ɷ޺`kע%2Xfy҂/aBT26?v:3;˕.ܧ>E0JVUҥׯo򜅘r -ЃXD;OT"|eqp_Ҷ. fNfz\wUTr~P3(4RY\ m 璘6x۶mMݙ3gvmc?<݂R[ H `9R}Ha_|w?C+VؼYL( ei6يI\\I Z̠X=J9H,+wWs$>JT2'd6pRt'#/]>m[r?gy j9RQRE ,@j 귡T/~qԨQɋX0fcIu(} 8xlybh-+Eyaɦ¹ܺ|{׼}1t_ypܱrDakk֭[ ۏ7˾og#z?HRXԟ`T@j !'pBjYf-]W!/ XB c~M󨼬ԘfIDAT6>OZi(d٨JJnO{v0R6-Z(}'Lp5\|Yga@QRC'w| 6$p*_~=̟bŊM6Qbe,t8%Fa;mq+SyߨdV֤ݚ8.r+D$@4ܲ%}˗?w}?f̘ݿ/~9(/,*Z H `THRX U 9]zS<[reS+-ba=uM͛J*S*rvES=-\gr%:l݌xf;,Hq) hEV].˗/\p7#`T@j @*m("ug\}mvwN6'W7o  b{F5W(\֗GΟ';*p-rO7mt"dP޿iٲeO<ĭ^ve~_җN;3׾v',G5X U@*-  6Jv?Fu5dҴ.X`ժ-[$$U>Z3xbń<2 Dxa^ɴEs*3KWL6]M7pç?aA-@j !SO= . vs}gVV"}l􄒯sGLVWC6%\ҳ *wRGd:ml0z?%4۶m[v/̙3caO`T@j @*m(#uuUW]5qćZhѪU6d5u_# tg}'?hHY H- ?RHoGۧM6gB*nuJNtߩ#ouIX:>QRE dj@hT 5П_ @*a }D*_gK=믿Νp—^JVk;<K[>R4=a>'E4T֖*Qc͛W\⋉9s{ɓoQFwE]>08R +:]8G!5lP ԰AJ_HP IM ??G_g]w݄ L8qwc ,x,yuث֭KTdeQޞU+D_kڪ akּҥK-Z4o^~WNT?ETxq ՐH pci7T^<=~_a8aacǶ>В<:,mgXXX6lm昅 ۰e(vL6C #6m.aC!† 2T뤇' 6hCÆ' 6l5Maؑ *J+e"5ԎTP!R,R>Df謔!`aCˆB >6 #6$m-a? #6$mV O$+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Q3gkÂZP i kEx",6TK5У5gIENDB`splash/docs/figs/xsec2D.fig000755 000766 000000 00000005142 13261626263 016470 0ustar00dpricewheel000000 000000 #FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 2175 1575 75 75 2175 1575 2175 1650 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3750 3075 75 75 3750 3075 3750 3150 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4800 1575 75 75 4800 1575 4800 1650 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4800 4950 75 75 4800 4950 4800 5025 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 1950 2775 75 75 1950 2775 1950 2850 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3472 1822 75 75 3472 1822 3472 1897 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 5625 3525 75 75 5625 3525 5625 3600 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 600 2325 75 75 600 2325 600 2400 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3300 675 75 75 3300 675 3300 750 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 878 772 75 75 878 772 878 847 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4972 2603 75 75 4972 2603 4972 2678 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 4725 3600 75 75 4725 3600 4725 3675 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 5700 675 75 75 5700 675 5700 750 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3075 5550 75 75 3075 5550 3075 5625 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 2925 3900 75 75 2925 3900 2925 3975 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 450 5175 75 75 450 5175 450 5250 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 1200 3675 75 75 1200 3675 1200 3750 1 3 0 1 0 7 100 0 0 0.000 1 0.0000 3750 4425 75 75 3750 4425 3750 4500 1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 1950 2775 1969 1969 1950 2775 3225 4275 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 750 1200 1950 2775 2 1 0 1 0 7 100 0 0 0.000 0 0 -1 0 0 2 4527 543 1002 6018 2 3 0 5 0 7 100 0 50 0.000 0 0 -1 0 0 3 1830 4744 3705 1819 1830 4744 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 1800 4800 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 3675 1875 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 3450 2175 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 2025 4425 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 2250 4125 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 3300 2475 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 3150 2775 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 2400 3750 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 2925 3075 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 2550 3525 1950 2775 2 3 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 3 1950 2775 2775 3375 1950 2775 4 0 0 100 0 0 20 0.0000 4 270 885 1725 5100 (x1,y1)\001 4 0 0 100 0 0 20 0.0000 4 210 300 1425 1950 2h\001 4 0 0 100 0 1 20 0.0000 4 210 90 1725 2025 i\001 4 0 0 100 0 0 20 0.0000 4 270 885 3825 1875 (x2,y2)\001 4 0 0 100 0 1 20 0.0000 4 210 90 1725 2925 i\001 splash/docs/figs/surfpart3.png000644 000766 000000 00000061747 13261626263 017322 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATxcUU0:GQGpTˈH!4B%$)$$$HOXQwڻg{ܛ۸ٟk m|4v8\~nS<l( 4RNf,ƒU7׍PoJƇ#(\RA):@b(O 8@o:PPJԻpxPCgFNXHQC>}C(e(Jb'  aƌ (8R.F^AR@(@)Pg @)3P (RR@C)S"  ~@K8QJpԿ`ퟯ8Z8_&]:Vԍ-@lwJ9QPJ7XJDHZ4\uM_qup$@Ou7)(e(X J5PshXʩfUmr͉oJ574ЩsKP jRVzԽZ8ygP>P (0~d[#?R2~C)d@)P; @)3P (04ɔL;_R@ԝtӪǨ 6wgDZDJ}c(eJP>P (p@((TJ}mƯ@8935ƄR@Wp@)JJkgJR,$((T8RN }P`((J}aRfPPJ9UJK)TOPh7P (RR@ }pr'w|n(e:RYJR@ }P`((@)@)JJR7,@)jܧohP`(uP  }PCk@z  J)'JP>P (0j(eJRW,\KơrP (p,(J]2(e(@)@)P':2~q(F& .Z?3P (RR@ j-G9P (p(_ 8?C Rfp@{:R-X  J)F5(8R YiF8(e(8Rb* ZI2~  Xy{S(e(@)@)PQ)*  F)'JP>P (02Rʙ")(JU[Iڹ8SP (p@)(@)@)PP  P)g1 J4FA)PMqucDUʙ|hP`(Ua;* ժR@A)P69P (p(ΏTʀu)XovQ\3_=X>P 8Drm} 0ksNhܫRO(coiR-{?0`0'#Q>}uֵk^z%# J-Ja8-F)J9=CΝ{G4=D}1|pWWgϞ=Wg֬YSLqӘ0a¨Qa!O=Ts6RN6kkӸJX8լRN+ڀRNo]׮#F4i+)4`!C芻ܹ ,Yhɒ%~~~W\r1K7ΟpBĉ|7JJ9M) @)3PR r"~a zӧO{piذacΘ1ۛKOOoo֭ ڴis.ɶm۷o߱c[n{yxPl׽I @)3PʉRr|NzE 7a„OBS6y{,nժU$G```P6VfCG&&&9r$66}mBΛ7o_堔}slJ[JRAkVɎR;3xĉ4-[QHHHX؞={"##cb0ǎ%''?~<--S'9qDn.}Ƞ{9=t @lJY:SP9h6 -@)GgئGmڴӧĉ_nݦM(f:p **"8C剼\QX+tI3|?jmٲ`~AJ9 P7hWg0 J9?ө/z'ڵk7|iݽȣׯ[yfdg4IVԩҲryI˓'/0&fڵ4C)SJXy{S(ՒRYԷG=bt#/ f͚M¦;wqGipؔ[P@_*m%%,WRQQA?eolrDO͛єryrY| 4~,$ J9TZ)2)(<=zMnnnbc̟?),,TILL)-ٹB I"Vb$ZDWo?Y(!\y9m۶ C:zOPR-xW;q"MfϞ͵TTڻwoTT\\۔UDxl땕UUUIodrE|[U%+a8gRONJZ5+ jnǭ b)JY]=q%=Z9/XCl ^P (RMj @⥗^Gyz\rժUܹl32(6)rT_r=Z(^:!B=sYVɻK ʲ2 (JRMj%@Ouuu?ߛ{s/,çt)WDQW`+$q. :+#W=Eg9"^999RRfTJeXJRş{!C;Xxƍ|nQRJ ' ٦rNd yi?ߕ.DY:_Jķf*U`YNRRj@JRTХ 4zɓ=<<.]bE@@Ν;'⸆/;;PqD2ƃ\KŤ(HOі JY%GT*/)!?nR*P jTJ5P:M3gΤwݺu[na ;|].;b=-222<}2|0JS2tݿ*=Y9 &{QK\P*P jhZR-@JrTC "(Zv͡Z$§< XBފ+.eC=ʰ?28Eb7Q޵^lUʲ bP9^Z\LST(u<#ݺ ;+K.D8u`,Kzyz>ѬimݕBtl۶mӧwP>P*T @)3P*P JYJY੧h{{{[.$d߾} \f.{3uMZ^5߅) JY!({F!+KKɧdQpx1Eݺu3)uP RցRRP:P{GϚ5kڢ{&$$%%M"c΅|;PW6 t߼UPFztK%q?-,1vjU-"z QNSyyyGpɒ%s5C&-@;Խ̨QKlذ!Dp/77#tJ^5C.m7ȥ\)!] Q.&<KrL\B_G,Ht! 6$wҤIFC}(e(g(eJ)RRwɼ<~o=|̙3.\jmJN>vXvvvAIWt+dm LQ#Z)N5)))::00p9@)@z@)a @)3PPJ`(uPLUm۶޾}G}T4d'P!!!Ӱ&\W&b d*g((V4]ʫ'yXoD#CpD%\BzUZI /Ļ7.µm|||JJA)(ՠ@)(uG-̴p(fӧ`~9bwww_ߍ7޽;""&&&%%%++)*:6j䢪a8"1 ?, )nR;.VC8f`w96^8#7,=(yt[ ~H4mCo8|̙3? J+P' (Ji@)J^yo߾cǎus>}: *wŋM;wF|ZNNNA vɒ+ jznʴҷ`'ɓZ+I kx%V,g^x .܏'ْW{d3$}WJϚvZv)SzOP(T2jӦMF4wҥKWڶm[xx8K-/ RC~[Gc%S|gOԵ˘%(_PsQW^ jFu[eOJ z^^^jjC6l@Ff>|_}HP>P JAJA;*i(e(զMvt2tɓΝtMvlӳi;^!G(gy`6 "s.t˵R "Vq: ϧ< PAX9y.b/zӧEKׯ_vL;gzJMs.E^_-/+"#G޽{Yf}o}Rz'HAF#6oFÇ BE\#πe㬒,Z\#reN{2^KO'qW;zU%W?+W m)_~^q RVFѣG3yGf(a(eJA)P>P J)0 2zG1uy󼽽,Yf ۷G1Tf&o6d-$['UkVU|YZ*tP'Y9ǫh7]ݲ'ElU\LO-3\QHWC-:}ҥ?'R0& GdyłO@-[6myg#RRPJR XJiJb~(t?חO߱btҜ뫜~;x6o8*d\%z%E;IEu8/^͂ _*J}M^O?uڵk6YY?&CS4>ͥ(k_~NR }ZR|acǎ1c׊+i$$ Cgi\h$L˨\ILȮRSXeEG"ӧOx]'ؽP 0]B3g.\p[xw8F?CEix,II9x]6m?(Y(6Pj(J2(es=owܧO77 }||V^iSHHHtttZZqMd1U˜HKb vrr^[ºSu"Oɳ>^XףGooo}/hCRRPJT(@)<_p߆ [lٱcGddLL o1[*kk9|2&?V\rR.[ O "?JwBYyiXT%jk׮}RN?_c++yut$W rC_OĬYY{!B);}*UP @)3-K' (ՠKA{)էOwwFAAAڙ6ڡ6s]e IDAT4(kN; Ԙ9}'ys""mdL}F =[RJ^•1+Dr!_9Ņ}+_zkH7n~]&+ dy P| /CC׬Y3suiv}R`(Pʌ+}QׯmF:ccSRR-'y%jeKn< MHFKeZRRI?.$08aUa.:jlLOӗ/_H4.\ 7"/PZ0ȑ#|P>P J)0e(eJA)P>ίT߾&MalOOUVq#Gp&N=QG5\fO\Zd~F~殈%Ȥɳ{9W/~UR=Nr; BV:)ƭ[ =XɯGZPMFڼy3}nnnJJA)R!RfRJoѹs=<ݼy&CU=&d>Ix'G``5T?P>P J)Rq>yF8N^:޽;&&)S_-gFNەg9r,bȪKp='tȉ (&l<Đuuy`EP-Wqńw3 O'~7׮]!&~'˿ |ˢ =z4<|˖-K,={Сwy~7 [JRPJR }OG1w\5۶mۻ7|Cq31p+[˽53sɱE)/_ֿP/^k5tp/i͢98Iv ʚ>-w{x苴~Y)3rJq/77799...""b+VPp!J[JRPJR YJq&|swH#)!!==l*.Jh.Åym,Ksʕ\DWUdR-\guNXˈjUXdz_.ɲ t;pC/*`8]f9[L︞pIu5R }I^z͙3gr_|III4;v0|b"M.Ŭ//gA|t]/bju [%yBțOˎg bǧr)jd LVr y6T7>//+miYLbyyy K,4>jCRfR(eQWiӦZ*8844t޽Gj튂o8/حR y~~KCtO^]*kk )t K2ܢk.piJ̗MeZ?Ů]#RPR\.RO9ҿ=!Os2>lC-RfR`(P R }Yg}Ck=Fp!PSrDvvvCtm7><"qI@Q )7~;G+O?qvuhDxe-~A_%bg]"i-.bOr6}ʯ~C}/;'NN}(3gCRRPJT= mݻ6yyy^:(&|111|5]yû]>k"O?uR|<ŗ_Z|.oޤ9rݕߊB&-GxUsEM 0Ov|6|N]5HzQ%y+60P J)RqD^{CN:o<+6nH6EFR>]+/^"ot~2`D"¨vq7'uܺ%ޑx7“UydY_X)cqƐ䑨o{^TלHB~%z .:;rr?o>cƌ7nܠA5Pji*RPJF IU9TuJwtoo\k׮ÇhM|.w G|;ǟt;_wLwk],$]!+'td;[wf/Nߔ"GX1^ce{v>z2$yyyǏӧc zz뭷}^h_R -p_ I{GxaÆ͝lٲumݺuޘ)/L׮шUM"]yAj|'A%ʙAQ\|7Ob_Ja:$+F螐('yb'Tfbl-ݙvMI;}7tͫW{xxМCU4R[j @)(PJTIRHoį9R AzQ:wܫWçM7iӧO_JGtuqO?._J6__t~Y\ʻ'~iUczüj=].V^ϸ\#==111::z.LVX1y޽{?x>}R`(P R L)!SO8R?xn@釯۶-<<<999??o4t9p"E"p`$}g_:zy/׿_ q__a ~/_e7}ɸn5׋RP˼"YZ&D̙"G߿ʀ˗{zzOlȭz%Ra(KJM2ڵ7oq9tzGcD+'vRaO?)]' /Erг_lF !'I@}xӁ䭏_p;D1[z˗9rRRR &yѼxРcuP>P J)RRPJTGPw?|HIee4h,;X˙Q%)*..LMM=xݻ7n$Ǎ9rd ^J[JRPJR }]6m0YpڵYYY/^۟9^/:5z.U⤸̐7.˯ȏ 9.*BF][*++ȧ;vp|̙Ç۶mDCRfRufVmQFyyym]P ue͂g䄏 z\mBoa3kβ]_Ӳr]խ[bi}괴Р+V̝;wѣG)/P" P J)0Zc֫j7R ZRΕuͦ+2lذٳ)XزeKxx|||qq_Jo]Fr)m/ܛOrڵD 6mڴu辙C%֕QWJ Z$?tݻ/^|xӖ CRfRJGHYpU[lٷo_l,MҊΟ?sYWKy7z7R999&vIn/Vn?|'HQ~1%jl=kJK333ؾ]N>}#Gؿ;(e(J-2Jۛ|ڸqcddBBȧkDHΜE|^R,xq٧̈́j|rcB ^=]ƍ<듧m#9h|M(NQ_i_{F 9qy)oR=y9WcU&~6ަO~Hoi>y#ҵkgΜG Yfǔ)4ݻwnڵk=b(P R }R`(PL)SO3f˗)l񩩩C*)@\ F L7+W.^xK|L2Y(օ93(;+-?iAB ʚ;l8Ӓl7uM1FHppʹs(|*,Φ 2"b;w\jʔ)ΟLxw`(e(@)@)(PPL)5bo~ݧ̂ *+Ϟ=K"S.H+ t܅ y~iR_׮]zzb,ˍtϵT$TyK/B. ?D|˫ʼ޻{=8LdjO-;4???777=bӟi:wX(eW:t ;ݼMRUU[.ݓo{\D~ДoyVv%~cQYY٩ST^މ'cc###wpI&/ 7(R,@)(JJA)R>RfX77nٴrePPО={L" xEJ9R2K5g???𸸸"hӰAx* "y6}W Nk?4NDZbt;!}w.!F_Ԩe\֥KW6KůfZrCTR3tGx [UU_ dz\RSS(ڴi+W5cOjϼ1RRPJR ,4R+uӀ88ρmoqdAAmBB(ZbŢE͛8q{5JyZJRPJOC(ս1cONfٲZyVVVnnɓN*/>ď΍.벜dx=BJV܊`Tq^ 8d"IC߲l Μey^-7OO8H9~>B;J9d$۔??Ϟ=y!CtxC) @)3P J)Rx⡇j߾ȑ4fV\~;>|Ѵ4^EEE, :;_>(/y `e"l Vvi?$ ~JwqOҰjsDŁY^ {q᠈dFn~< 4KT!Ubɧb_\xxΝW3qÆ ޽O4ذoL}R`(P R })EuڵW=z 4hڴ˗FF&%%P řeq&8-&>qah ]%ܞBtJCjwqM-: gU9^ŁWV!Xj0]K@𪱑bueXNTPw]vΘ1c^zJ={voXJ-2(e;J2 #FOm޼FRLLѣ4&H">҇> ZN><b*lѷŬd9+FG}CRfRcGuذaSLqw}OǎshebҠ*s/^0ZN|VUqi胺@bpn{N pL$JHy5idFM1)zB%*%|d(bz %,mIv_E3],eҜSWjm##wر|riLWWɓ'?p~{(e;J1a>4"""E<9'.ٞXqz=[ix挼 Z3qT9%:J\MxX,YɓrZ-kU:;Y%. &1WO5rs333:imIY&MO߫gJJA)RRfRs/J=pNz1qg@@@h=q#iЉhH <38MqfD^tWD^q#$soGZ;"r >t4_)cH=og* IDATL V\i.}kZ\x.d={lٲvZ|)9[n۷o۶ц{SC9RfR`(5Pʌeڵkקȑ#)Z`͚5۷oxwrw<1r9|VɨoLT9'"^ҳ2[Xȭe; MbN::$"|ᘞX*}1%#Θ׭Oj ^M.!!!k.Zh :tРAݻ;ٻ R 4PՓ;^}ՁΚ5kKѼGvg[I)EUud^[[-1iW[+X*qOD:MMb\jнurBXXș2ѳҍ>Q`$E[ \Ksy+&|ǎ%$$DGG7>ǻSjժHeJs@#q{etUnz8s$2wlSQHkulǩSE|\.k6KL7A~Vn,Q-߿Ν6ѧ>thϞ=_{F̀L @)3P J)RRPJ @)3֔ݻԩS#"222dYa Nq<&nܸwŋ[7oȄXc*IRKxtQI.Cdy!+6qn1|w]w儜*%СCۺ500pʕsΥמ~FR }(#G\pa@@XXXLLLrrVV\ex{]ͷvY^W=_~yE>,skZL12\NHYD*I ޛEQ@Ts;+'`6Ŕc$qWf?eNb—'߿u֍7ZEرc C=#0n(eJA)P>P J)RRm۶7{Æ Oɲ ̭&DZ8V g]v֭O?=Vы$?.9:%%n~~~nnni)]ryeZ);tJn&iN5ʥ]Yk ~EĄnذlٲ+VRD4Šn^ YJRPJT @)3P J)RRڀI7%&&fggkb,*-j4ϵ7nܠXeBX SyC Emdxřo6R-\V[Kd!zZs>|84t۶m^^3f̘9s)nӦMfJJA)RS,Y>޽QQQǎpY)e]yy Xjk?ׯ_ A9!v" &8kB)NCJ=r+lkTsFMu>bJuX'hf{qҥKɦQ۽{.]}& R }Rs'}vԨQ4m;x`RRRff!Rc;}yhR$w^zUigtT˕V2r{-qŹ.lR?#|IiWo.2zd.7G!![nݰaȑ#_}^h JYJRPJd @)3P J)RM:v8`… 7o-&z C`}"jdmmU =ň<^\pQ/vj;#h觀;UhJ*:UBqZ `yY%e)y9--->>kW\ICr;P>P J)0r2VsSLY?P4)q.Oʃ9kVm-YnjL+W._[C*{.xNJ1xrks1a\$'zۓ?TWIl~>wcNך]ٳ'(hժUsN:uȐ!-t}RCIRf+SOu?~|"ٳ]v4(e(JM2(eRwĈczyymٲ%<|B%թ_!pX Kԕɝ+%JPD1TNNVVֱc"#8cǎ D|ÇwҥFc> JA~ @)3zIϼy||֯_OOxV'ĥ\5dwɋ۹sb gjN2rܵދYd2~jWsKUǿWGIY%ٰS8q{nn.r/ 6 w|www77ilCّRRPJR [JSW_1| KKCDM>Uȝ}VM3]ad!]_(bp9* @N0M!aS=b+cnnzzzjjjRRҡCaaa,^x3g6t>}R3bPj(@)@)(J٧N.]LwC(:qB97Hs1kjy OΟ7eB駈JK2i;\)|7%\z̩y K'NdffrH)88 `ɒ% Ξ={ԨQ?#8CRfRSԇ~8u˃?·ŗKڈ}Qo:%bgX%J[a#׊bKx(gywۓ<&_p>#hzxJJJ|||Tݻƚ5k,1cMX 0`@Ns:&P>P J)0c(eJA)P>B_|>%K6mڴ~R&I9=W.ǥM21h?QGMh!b*~=cɹ<ٳ, 5#ԅ*'^:>*qϣ䘘={u]wws=X3\Pj(@)@)JGSW'@6bccO2jጷhs&drQwMɾCD9 `|jO"Ȏaz(w%Nk@$&&-[/]ӧO;>jQJ2(evC5ksر\ I\eTWf>^*ɴ@)ҽ8-svRJgW ]٫3r.ӧO@TG*[TZ2r;5BMßd8vN;^R^NvRr&|ZhlI`BBBKzzzΟ?mڴ!Co 1f(eJRC-̸̛7ߟf}4(O *99ѫԏ}ASj=jRo%X5z˚ry*(ъ̫ge\_%\9got/00{O}<3= [P>P (RR@ @)3.۶myU^9%r|{2~TH.V֯,7/YY)op'Od(JMM=x f3fATs }P`(5PʌKhhhRR~~>:$ǽ *P2Xʤ'Ԫ~Mq U0o.9@Qer%PS >|Ȑ!={+=[P>P (0`(e%2225$7Eҏ-g}u;eLۙfq2"ɟT]*$%fz|FnnnFƱc:wΝ;̙3&O2eѣty;vs=Z c  R:oZSP (0o;zRĸРzzO[2kd󩽕6q>ͭBo7|zJEEERNNZZ4 ߱cG@ ܹsv{ԵhP:\WZ0P (RR@T? ܋R-)MSNI8Oz_'} z9i)(qH|n$ASla !lٲvZiӦQ =ZR ~XHv;RPJүԺ3_xp7Jen JEU3yiBoo$xԒIBmWaVVVFFFJJ|||DDDh]nݺnʕ+-Z4o޼iFݽ{jq֊RR@RfPJe 3JJ>WfzmXZc*yh3|>Iq}"mA7'''333%%!!!..ARiӦM7_luwwjԩ& :>xgFQ_>GR@aA 4QE5\.aaaz]B_ݕTk"$F\Nee@|ӓJ!6˖-?1#G3f̸qFś8^I1mV˯PۖRSt}aიjRt[#n5F2Y(3{|F1'<]#%%ѣٳgǎ7^z"EF?O?v֍ׯ_Ϟo֣5)P그4oSJ z@kRJwJ )Eʾ}:a<]؉+{SM2c(b'..*kSvvvJJbb"72߻wݻ#uWXt4xpH'T_lαJRPVC)PZRB3~P (e_#=ұcŋh Qo0S3RL |'222HG1RR@Tw @)3P (Rєz &АJLLLO& K~~>GIdON)Wrx^čطoΝ6lXv^y4gΜ3L2bĈΝ{+@)@)PC @)3RO>ٷo_777;vő*II4#uRR9[cbbH={H={i,oݺu˖yzzJB'N;v ۷gϞ:uja(P  }PŸOgg ,[lɒ%˗StD,_Xoro ̛7}֬Y8pyW^dӻ6h RfPR|W?qON̟h"//iӦL2mڴ \]]gkL4} a :tG}?Fh` XJR@ }\n0;uԷaøO1cFѭ믿;2JÇ9r:ty_|iq1zP  }P+ɟ{c?O}QRcǶ:ز1l(eiR'wg(eFh@)@)JJR2(e(@)@)JJRZJR@;RfP(e(Jm(eJP>P (RR@[RfPCNRfP(e(Ju2(e(@)@)P/RfP`((@)@)JJR2(e(@)@)PUS(u@'P @)3P (RR@ ^2(e(@)@)P5 @)3P (RR@ԟ,@)PJ:k-P (RR@ԫRfP(e(J2CvRfP(e(Jb(eJP>P (RR@YJR@ ^2(e(@)@)P% @)3P (RR@ h(eۨWP`((@)@)JJR/XJR@ }P`(P  2(e(@)@)P9 @)3P (RR@ 2(e(J2(e(@)@)P @)3P (RR@ 2C2(e(@)@)JJROYJR@ }pr'wP (RR@RfP(e(@)4RkՍ[JRƚU,@)G}cήd.A>(eoɕ|[;KƃRZbڒRxqJY;̓C)ŕo 2=x<.?-(qqo㷆~M1hٸ(E6mBf)T =j{;C1r(Z'x8$7Ph#8ڦiV>)(qJ9')QhFkg8Rƀc8J& `/}R!q*{GG}_34,.0'ٲGIDAT FР@)LhHAq@YIENDB`splash/docs/figs/surfpart1.png000644 000766 000000 00000104432 13261626263 017305 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATxՕIAВZRrl%"Y ! !#@ Y&[l@`l-్0=Yl=Rt]W| ܓN!;7P H#S!e3G8RwrXmOϟ5ʌgCR~ZIUw%ꨳ"H R RcLuPEGݳRT@ RW EeHw"8l5w]7C{;u s R޽|E"Hu_)A* N!L{Y#Rit`c:pȱ~`]޴zҽ%TFKA.>RK?C*A %w:h1tIjz}~( B)[)A* T d|=l2J\$>s2}ݖcSJBJS%Aҏ#~\Z(T# \~ S}8Y9?Hrꆏk[exlK)CQjN*VTO!-ԉT6H4R4a=j4_,e!٥*UU[ޭ=Sawv"HHAcΫBk*T))AgR'#Qm'e`[:(׹DW#xhPUݖzGL!oKPg/ U4}޽|/TM-]e4J @M0n,N`zUOK)DE TP UcH H@zuԬW v# ^*ԯW/Hu_)A?RHo|bJr } ;֍g|:.f @A*o~j`sΠ/l+YG]G3SSH3=RRT@ RM!T]]?RZ /זJz)apէR@  AT&uuu{uνR:CϹG-C=Y RK lRAJ HDulR@xk6Nc`Y\yXIn6ԿϞ_j<ZoR'N\ )UȱD*[IuEA”:) Kp=ئ2H%TXjpAZX RcTW͵YK_*?$x^&HHV( cGAN!GeT!J 'HE STۨu/~QG*A"H R/ Ar4gn^@}e.(tfݤʓy>? b09ZP3w9RRT@*ԟR e Um)HA?QCʩ{igh5PWISY@YLTKő]5ȉtjrPU( }B)[RAVP l+{LypBqtTI@_T쐣/TF[{"~4 b"KH 𢻖z4)Q;[\d 'ΥT9Grz`-qJc q qk R{Z %H "H"H R/Gjeq_jc3RR*(CG\9/`--QbݽsekH3}*V}P{g] 9\ĖhI @F-z^G* .Q€|dKҩwe:}W{1% %H 5Ox %HTG LJz SVKTXCKhZ\_絴ۺ=|K f[[<g8?Mh"?[)A* TE֞Hĩj V.Fzlߖ1 #T(,}%W+Ub:VC3 7 R1e[AJ HBD/%H R')g.RJd*Zzh]}V{ơΙBǶAD]g5K3qw^> ՘*?AʖO#HpJ-K9u٘p0+S넟li3)UJu!Af3hՂV.vP]҉}\)A* PS*=,鑢=R>OA9x5u|xVQf^Wr,ejjH=|Rq O΃ kU W?^o/DғTAJ Ag)$Rgׅ_zTڴ ƚh cTr:r_mA)y-9m;,r*-m*ǦübI6XBZjKVNlSX?s%%H R Y {u:ar>1dxY_/k\~'L1/BA,x#cyWλK&v`=dgcTuN7L RT͑ p¶ %H H4S j>˚.9'kP2VNqqYVrs;T\voC3vrqqx:}= )gaEkCg}^)AHԏd~Bz]GHi j0 k{ qH/"lŊ<+JLPbK=v5ߠyg,C>ϳ%2\OcH+D~B)[҅zjCI^QrWB|E+[SDZX,\ϠN9oG&*H{T4!ԉN ! S=A4`Z, ֔r+)Cm~c,.4$pC9Oڭӣ,2g^tI٘Rj RG])A _;sǏRzm)PN )[0JRSpP :fNpDFJǹ^[suki! ՐsQ rNfK!Ms i P- WmV@τtR\ :s.X8/a~rJT0J֩ ;Y*^:AX %H%")DE$~UhHw2mq[/̨xcwDMa!.pcdO\T<8+Tw3[?Z<)cUˡ('H %H%"I l)Ae6 -551lfbi-gfn=%hGV -(Spv9񳫟2f@L1#qwǠ2H0RR USRb`>?L{xn!'KV꥙>wk#wܜYd!04sF;~ֺu#KZgPUyp3t=5/Hu_)A*AAJj*]C*O w+z^56wvֱ,5MJ^[=JaCT*᣺ v6,]ym6Yg`ni*hˋEqq:q q]JRT"RO!- R eKPD_9mA4Q'5+Cå6wN?YUbQyqt8f΀Y.  mkG*^kyspU })ARNCjn !V6*6:_-gu찕{m 93z0'إ>&&.wx>R&rѲKlAꨉ %H%""H"H RTMkHUE&FX]jSzeM9Ne.xh _axrLu r&@OI%|%Q]Mu7m?ʼNxsr() \yS%Hu_)A*S e %H%"Q 9q Mή1, n(㫶l%g K=&!1}o- $_J-1(b˦+h.HY5nc2]ֳ3Dž%aU$DpTAJJBJK%IJH||5=pCXN%FtЕ8)]v Zyv5wE˚JK*of+2d835}0ZqԪїbAr"#-H RT%{ %H RUAʏ-lC,]Ȇ$ `Mc}k_EU5BEm1$scJ:/Cs&Rcn,uez̉zL+l0ecwe>H!"H Rz"g2AsRtՆVC&H>@i-=nF Ѳ;,0FL!9~ Q>2 k a1QrsZ$l R e %H%"n l) n;sNK㭲93d!@9=NХIu.{klld _CMK+whY!\җbTW\юzp92E}DҼ8Ah %H R5AJJD*DN!ʠX k-ͮZ es 29%=hB8@/-&E:KX&u3J+>P{7\Ax'RR US)A) t9Z:D^ce-8CV>F QJ=uPy78NqBǗ4<%xέK(̖] f%xPF˘ɋg忹r\rd>B<ȮtZ v lDB)[)AJ$!U}4ˑuAzm(hγ(UʋC.L0RXr>CA'B!2ŤeC9V5*r|[iEml~YSBh -%̕ "H RHB)['nJ@nQgE88+4={L[Q[S6βC:S8{{K}BQ]>; DjKz|b!uyd h*j%{cȨL$~PE,I)A"H RHB)[W'3l3[Ya)>B)]+.<&.?jP8~Fn0jIg777acA)%[H^)Q5x.b'_S%H\)A*7R e %H R5HYir59l`VV3`Fb0mf' [k]T: A>h:+",(3dʤ[sCuٜl)~r)o@/I RGG!HH3wI!-\dJtfiPoIW:OYqǨF-^o & M56*p>O܄zh@ r9)P^-~9I;~uWRTWE %H%""HF*gB.ZV9|͚k&ezX>2Sա%,M0.*$G6 bWsܬG[WKK '@yDV;:ɝ]U13>U1ô)U4S8Oi "'|Dp`u,5ω %H R5 6kL& zdepHg(}šY|IIJϡ#|e( TP6ah 4A_Z+4y!_lu0ǥ؟XeՋ--^ :t\)ifJY(tzr_cml*T^O!- %HT"*n3yWcP]C{ԊFUe.u6Pрc7970fGIu@ 2?\N5O A,Fk֙~$4Ŝj=PRRL k"H RHB)[)AJH5# bG)#dn[LI?`)o9'(D@l$ښM J]ѩ U(_{ʺfEu Fr];a^O3Agge<_z5RRT_@8 RT@+Bʛ&Cs`feӁ "3] E=gi (bFt@!H@ى&1q܁C*8PS6lB+&^W!@+bF0u/ofK.Y&k2&?w SWR e %H _*__-:< a W4+!x(:CxB 2d`كo8! IDAT&ףA@j |_СC| @x({*Ь6q%f>g!ҜiVgLjş8#LjH(_}T SHwYRY:Z#UW)RO㊩9a--Vޔ`0T68;zcٖCl-J0>#ZBUG_QWiP6aNL֛!I%ƻ$%ؠʱ=Gwv kZ q73FUN"H R5Fn~R'VJXǡ$#MX~QVȶc6)qEdA F%A "gGZC np#8H6"nhU[[[w3 UȘ5&(E6@&6?GS` [TLsɁ'L@R.ry %Hu'FG\\tX R,;Y(A/ z]֨+.B㸫)cqvQaNj܁]--h%xFuaÔ/^pՠ?tUM:/';_kubo(/-+t|||UX(^pVO %H~BB'/%H R6R/ oKetGG﷕Z,笆̑LJ8RbfȡrA&A4Xˈ#Z"0!\d+ Bkm_NMʙQ tT\zW1hg zG8Kj^YjMTǑRT,_YD RT2R/@6Xp:3ި@l+KLG˔Lf4QJbduasu\cE8aOgy<2]DHFDB5 9~tԨH`K#Bm-6+WHdPշbW9Prv]̬nu< x.=t-m%]GD %Hԉ.1wx w<~y3/y:/+oBڨsj!}\^۰aF$>>~vxƎ ETx!ІC <>\uI `--ʙh/q]ՠs/&!u޼s+Nø-sVER e %H R5AJJD"HB*Z 6.&` +<{qRyC 5O9vkzN*;<2mÆᰡqB\h;Zlc;jT)t ʯs*ah | )P7'r)[$HV)AJRT"RϥAʖN۳^s W٥y6W`_1}QХ1'BjLi.7 J9A60@HC l\2z4BU 'G8.1bQ&M\8/i7e*=,yȡ x Ǻy30zLUTEJݶ q>;/].bc~N)k*3q!H7'Uu ".VC \LL@3fL[Eǎ%^ƏJ-Z;:Ћ"L<Hc4Ա tyz]|aUNJ*Q՚ 8gv ;fUM!- %HT)A*gR eK)B` 0e"yS21`Pa!%5G H܃֣&9 MƑ\ kA"m 4։~aF7'OFŸFM:$>|8'@9 C!4r"dJc($emF1RJxC$)f9U7EǕ U D"I)W#DB)[HeM' t,ol'0=X؄PۡtP94 b6y?Fh5uT<4?t'L%!7nҤISLvxi'QNKBlh>Eu045[?H;oju1F:J3R<@ZjΔ0K~~(em)A"H RTM%r.ی~5g#A fjM>L#_ 3&DVx #18!0HCPom֬YQnٳ'M"tM65BjdBG>S3'B4|ƌh-ab3<~} swdkMJ; Y6eJ[[mom'qm&].U' 7)߸T& Ro-Y:^ֳiy dB9tl-" d RAJJDp l)AR) YEb(YG9JcErh՘mT .Mǡ]eCiϹG P!h8G$'JpOǙ3̚;w3g.\xi. EO<&c[ޮJmb^) &E~欑_$ x,2՜myNjPvZERT"ROAAJj*!onC|9樃a,lxm z\*>3LF*?CE|Qk0D{[Z2GDd(d8j޼y3f,\pKrٲegZdc-X@|M͙3g,lz]11cTy6dA=&x25Qg$f!U/g;)>f0nsC ?cS R= rgx<R)A* UԖbtb&nfiQF7̧F] U%!ʋaj0` 9s#gӦ-YdժU^Z &BjHג%` %K"̡%!YWӧN:~q1ceH(0O>*pp3DZ&,*:f3V7-a!7d0A"H RH}%R^{wJqC_%\G3U.``U!^3O*yuB 3dA)긡G\roذPo{Rnժ͛7~z.](ZH7+(6rO4iP=P*lbHA ?[ς,~S@ym7^5<~$$'<DK!- %HTCJ=jh1msP-PUE"Kfy9ٖ*[jnE4PxFDI #Ud =GVL؜HsɖZbglܸY6ܹsZ+W-EtE.&S )ļyϬ(?%Ĵ5tB*d z67]U Bȯ/- 8+TDz4RR US3]rLem E6$YD~ts߼'-5Xlr4ԁ.> VUaO="fĉd6!(-!EH,_l2>s׬Ynݮ]"e;v sih;L2֭[xܹs YC`PѥQbbT!^*S_Ĝcr_L!`B"/L-tU Ddf!~3Z0Т$HX)A*GR e %H%"p l eEY[K>:ԢY5UblIz|e]a!r^剆]bUwd$0*U6mڵd>-_N,Xtʕ+7lذo߾3 C̥3θKn;/lٲeϞs=w۶m;wnUVǭ[?ϟ7oh,7uʢ'[iw98GXeOd88JSvz}:gwea j %HTl_gSn=^&i1Je$%)ukeRAWvP>+C2СC<ڤh D*?od|DBx1u;wqywnN+*ٳ/|޽{er_|饗R+VPp֭6tETr,`RwCUߜ0a>u=>v$$\\3ů,׊B JQ HYqވ֛/$C)DE %H R57ԛ,rV5ި< +EUO |`P<#T}ɁIY=Gfd5 wD#f̘hѢYq]vΎz>uy\s;n駟&3ĦWdYCa4ܹsb]l,F%R;Ғn} n,>[5>+`D#CGE1pY$~`x2)4spVw?K06مXH!- T$8I O)AJKTNˈT[M|qH۪ljz->BPI6P$tb!|j(J6v,ʀMA6*Yrƍ7mڵkgq]w}K_xô7_~g}]B3C!UlYlق$ټyN9iȰo]ZzkdyZžTP^wN]?٢%Lt]g5b 5RS e %H%"u_ l)A"eq֧.pD%Ʋ0x.U` 0!BjèLԄ ='[gvVZz5quyK*=_:uȑޣ?/O{wu[o}!Z;+$/hÆ ܽ[դ041XB@G(э.qj21JEv!zbYWhcYqjb52-†3GT RAJJD"H穆j r3CC jby8A  oj$EO T D޵v͜9wɓ'kFC㣾߮]]v޽{/@^{|3o|;ooW^y#s"ك5k֜qƬYG+B N]F+5/(R'kn84y:_]NI@yQ++mץ|ĝ Gl)ADGJKL)AJP/?KǓìxiLk7&=JJPhiC9p0B(RP;51. Q Ǜ8:u*PJ3g.\pv$PyXN۸K.袋.x~Gy衇o<C6ӳ~~G_w>_W믿Kﺋ":[hoəAtpWƷ I0 $2 l0;^,B+9n;E)DE:B)[)AJTlq[zsYBd)1&}9zI`AQIi'N8r$Y1'?XGۣ}[n!>ґ#Go֯~_O>_z>ӟwW_{cwq~y!gpu$7nܲe )wf͂_ʙoEz"&Ł @<+9(yqJd3*@ R=AJJDK)D%\s⼴V֥.S6רyfO!P.Tk(?~QHN-1> :zew8p`Ϟ=s-M}z}??O=E_|嗩?7xO_y啫{gϞkמs97\KR=i:M%jΘq3 P/RRPXz\j"q>͛0Yu,A'"H RTME'RTEj+ Nb1Z{뭷;|wݻo޻W],]4!f&ǏсQ#ڐ8d(Useݑ_jp7)J2SNwX2]L(+HX)A*S e %H%"I"NR{“t?iz)%h 8%]X*7ԕRƑGK4LT s8 ΝKzh눂/[o}G_W_y?&x6Q4t7tzgOt믿S}o[to}Ѷlٲfׯ_p7$=ȓ+>x{=:'{1jK/t_ɧJYt3gƌ#@!j~ z]B%F "vtF),9m;O"u{ qY R86R1:n+&15< 'ZK(x`2j8ӧRyV4p G`~4Dw޼y-]6mtO]/˿??x饗~׿ݻ"}ݺW^!os=w}M7uo߾muٳc "Q>6#E>67)CILGQJ2Z0B5|}_\ֳxNX9mBC%H RHݖBz[&H R}:-+BW {Cp#sK3kKtW QUz=T( qd@E)HI1;! ,Xxҥ֭[~֭dP]~]FqC>OT/O>Ν;g/'?!0Z.uw^wݞ={.䒥KWZaÆK-[7%`af.u:zs40s?;vd߱~O_W[o2};w. #!n`B9rB 7^ GlT .nTu9Yzc:=X}}Y3'L!t[}q=~+{__J*O)RQD$Щn7+ڸ|,fE9d4dJԦY8|+pIESayۨQj̙'O&6^kٰ֬V];}]v#{'"Eʻ{Ç׿ ߾.X:mܸuV@.[6o޼+WӧO:7NA_b6{djHMP wNVR{e6jSQ_-Kuގtܭ RT"R,$MT-.pQK3GEl({{0SCndϑ 3p#[jBjƍ۷s9W\qUW~cLdK$Į oݾ"m^zA.첛#!.ݼyڵk'N;w.f6nĉ( "3<JAiTTC*SQ}z62=NM &~]nI!]@ITH)Aw)AJ)L=~QsVm.^&Jbr Qha/QaL5 qFcB SUe  &rwMWJ}'?O߉oq| Ot;vxgy:ɫ_җڳgE]{7PK.]Zd bSLI[T>0 jiDiÆX6<ځ__ơ_4*ܠb@K=N/Ky@B;q9TGJTeɞcB"H Rԉ,iJc !scƺeuy?p*=]F:H gIyч]Ҏ:|S̞={f$+VXzׯ+/n=[o;~)z?OKdϟM7w߯~Gﯿo>,utv۶mwg[nٲeFSO=SHjmmBFO!JB6>A0 ByND~+.<<~TWE)RRT"R7A)^5UD<꫚_/T`j8Ʌ o`#*[.IرcɊAIL֭֬[=9}W|ÇULH}ǯz͊+j9sHO?obTL6ڕWE{.l-ڴiΜ9D'<{h:1c%Z'L8NF$CU9(Se'T؛[ :=.UVKWR USDnH!-ɶT6 Yr#ZV FMBoɁ>FoW E5&b)S"jI$Gy.@ %\;x-yw//ߊ{9Q/|ni zɢ4iF%;ϛ\ٝ {9z/z!(/ Tsf8u&TDJx.ZN^'2^)xcP'h ͛L= 9RK ?x鬷"!ᡇn;vl߾vm۶YiӦKΘq)ttte;ۇ#M!e :x!^SqQkgeף0VK;ۭz& %HT)A*HMldF E*]2GEqƧ}fb^MQH2OW>f6lXs3QU]\DN 5rȺ!-[d <۶Rĝw>O~|o~>O=E?O򓟼?O<÷Nڽ{-[n]fժU;ٳg ` uDt 0k[&Rϓ"V7֬.j.RI8U:DJR=}ŋoٲeBjV]z饗_~}_o_|[^z{/G}ˣo~ȑ#?(}kzoh:\eˆ UN=TwL5)}4fP)j{*%!P/6 B$J A_'y=v]w=_M7@f6m"-#ۍ3RM& j$&g# ۉ=TL/#2NR:DqzY*w })A %HpJ!-Tb]̪ @f-@^<^5CcJQT,\Rl djQ Bmm?$>9˗vڜ9sO߱c9眳ge]FHeu-7p×{w1Y C~=ܣJ{!uYgm|gyKR0bҷѷϞ&MR5'Pc'[ZZa.aȡ*ɣY;^#zǔB?S_|}7|w靈ϊd޽Ϟ={޼yd@utt̚EKǠDdMbFL3 ':FuZ vR'8ǯd Yq^;tnqZ,̌H.RR e %H R5Aon^tNr:~ q-Bs rAT$ \}HT0ip͚5 T[.>oժU_iӦ%K-~ԛky[nygzQ:z_җېxWPo:~Ϟ=gEHtɒ UR.Z3S Ȕ0HUv1OHʨF=_ U͡E<~t2eqhIz>u*-I/_z5PVuYDeПm۶]u!:bۯb߾}>7o+8㮽Ф\IT%Snj4i=gh};rr2\ jxFzǡ^eoƒ12q$ۯ R\ R{AJJD* VRt4VLSnd8k:~ZeIJ;/Ijs7$U1iL+?%JԎjҴi::hIŋ-ZD?{6:n7*gqԱ m%ٻwSpǎW_Mw޸qݺu[xɓ'])Sơ&m?^UlLN+Ѵs*e=c<Ŵ:|pr#D?pHG\k+)Hc.ߨmƌQz>sD5k+V8tN;4Bfٲ!kגMEtM+D֭d3s9瞻{no.o޼y6OҴiʆ蘆KrTr$2BᨓBB eá`uJ5[䋏?ne-<3 %H%"uI lDAJs$`^œb~˖ćl(`P]Le=m/2 {pKBJш>N;F;SO9b&"jbB͛wXjՖHȖ۶_k֬E6t9s-#lٲeӧ:}D{uuBs$-Y6-_Nyϟ|Sϼh/qEtMh4tU|}+Fū#F-alA<cwU׌TN@<έֳvZ$sKLb Hu_)A*S e %H R5 RCyiJl4c Ȅͤr`4X(taȻz8N&|d|j#G$ETIT|F$Yt)!fR䧯Y+0恵Vr7nda$Vd͝K|Z 7dC Osvl21c֬Yt)q$̜I\utt[oysCzpR!* s>XB6𧆾S}]yj0M]R USQ3wTm7 a)QY29/Gu CPsZ NzÆ9܁Q#.NON`yZ:ԩ+WgϦz9sLڅR:HmO.|=*'E8^WS)Z^Wu朗qވq|ܙ;.L!-ɶ u"* %HTlpX l R]YDh=|P.90!8CxڅB dH$x<**C ۧ@E 6PYd[ut,Z(0"&Ot)Zd3utرkp2a0x{Fj +bAŶSP$9EܖIhPHErAH9_)A*$*XK s"H RHB)[*HG{y-eoAg <&qf FTFJZh8$b*5r8|c0C!Fwtt>m @h4IE1"Ti1rCx>NsLKw ;6AG<_Ig7)"gJ9cwF U[)A"H RTM%T<[#(gQ~ 'XYSA \VM Ð954ﴆ߉6-C~p! 1 n? T jV&OKA UiuӭˇZCm|N[4Թ)DEڝB)[)AJH >YOvrкv 8 JuU&z+"+^P0qW)Fs鰒iSk+EvPè5چa<FRtOeN` KV<څɢ]-smF3Q_g DX6OTDH%, NOmNh_YP#]P.e=')*" yj׉*qA*CopHKmRjH!'5FUo}@U罞*}?щqOĴw*=:[6!;chr zty !VAT. A>}QCӂ\d ;!Q#5 S!\@*ބNTYO8Ljg1wIzU'}y\x8R#Usc"H RTME:;HϖHGEBQN4$3s˦XRٔ(FUnf̋/X޳MըP%-%BOȌA5Hi(&]STߩf]F\&i;?X )f8`n^[!.)AgHձzosAJ*ThHŝU:n5CVޛ:0xWH3H`?e81ФFb@J! '9C .WBeF@5['gh:c{Ӽ7F3iwb@HB|@*U}MǙRT0Sg='/ۿ e;X )AJF4D f.R 2:^s+#gjj&=7);T GÇ+@#Qk*I0P%JGz 'qǯ 9<+4''5Y[YZU{Z5/t\AJJDj[ l)A{(lX:/ozV[ISਮ4Y )p55]0R_ *0d=E!*G% 9-qƹeQSMzQU 5YҌ)M9mb$; )]ZY)AJ C+V_AJH|N:9mK7r%FA`נڲR BT/s]@hPNqðG1DU %D=HQ{"kXѼs 9<{6C_2RgސAJ=F$As^CN(N)b̽=d)'6 <~^2JȬ.U=v n?7Ә%Tn(QW|-&o/|qfL~x5+c|IyX7$ ԖrQ( %HTBHq:zZtв4F8-Ya6>tGJDH'd2D rv )5 ";#E{i2249|V[W@9Xyl\ xjUCZ')DE %H R5$m#kU5EȐ>fŎfU,ŕL\zζV=ꦋMsd&G+bGLFsPBjdH!- %HT)AJ:ʄfQ2Tf?)8 82SJutv_tm]\?@D`{ޤ|=J,3nn*j?߽,Hr)?^xlU RR e %H%"uz lTW㽁fZt>Rq`%{zqR4:'qWu"X>}pEފbQyRIYUJ3| _tuiDչտV՞0ns7Z ?A"H RTMŝC;6AʖRqn&Ar01Lk%hS}iE3Z[/CYk*5i~q48< 09E]n3 s Kjê shţ}y=7* ?~a\q^翔 UD"Ix/eD6AʖHEs[0jeLkXјM;>Qe&`Q*g9(8p }W(t9Qwΐ܊g9럣搀As:Qάb;ԘYE}U{VJRT"RS eKR~bRe-`)-hTA¸WW"J(Y6!k[^ fF5-쨬3z% PpŻ\t|Du9C¯#:)=,!HTE %H%".Rtm*FB%ЌzP,3-Y1*5?K\2VKA[VlV'3G8@ tDuo[ wh9O}|ǩ87C'5 })A"H RHM!-m)>Z|{Ό0A4Z}33Vl=_9$*>74= ?ng]dg "\Mn_+Bc忹gn*䬺!%HH)A*5)D%]/R493NL'/yRAXAVQrTD"uIO_x f ,z[1 |?t/r/S5Sb R=AJj* "Hْɞ9K(8{D-3`y|h9 Λ )srF4y%]+a zi|)FL}{c#_"?2O\0LI/A )AG"H RTMEZB)[:EdS̉ NYz9#l#̸@cc4ME]W5ҋ?U:.n&\TוP'8\N:&\p(184 uI V# ^ HL!- %HT i@[9_|Qj y#VL\IR],3#xAq$vM8{(NlRTas9&! dLpF`&HD*bm !H Rz"g2)DB*$_J)AsRL7t:qT^hT,=uZ|H^tb /+eֵT3fYҵ N9$S`2[&5yڳO.)HuCo$)S<~ %H$4~6ƑuV6 ;3#oaD1ߌVvaK͕3W됢l3rEMMΤb@Y`LqxgVod%} u4D:-Rt |6 l4Q _X&W 2ko8 a<ΙbKΒ⯮ JVwD NәwDAL (9xM`˥)5OTDZB)[)AJt :\F)kl) 7Ȳ:gFXsVv K9#L_dĈ 1ggCɲys;f~Lq'U6r3Hu %H%"ɢRT"RI8U:+Hu>#ܬjYr4+cyd8K)Kř&K:#rE=\\}o`Kt03FV FHAꨉ %H%"0Rt M΄(Yci߉'jK_FSesY=Ttp42`V_1:Chp!OX2 J ڢlZB)[)AJR US2RzJOR:G:蛅PSEj:~4<*~úA,w^ Jށ9s=v[3Sq|XL&Nwx\@b:~+,/g쁿7(PʂR(RR(RrRGMpLچFSxAG;scԨ𢌴~N5oo\Ӎr(WDY8ٮ_xM51K}rJJԮlQJҎڇGxH|pA(q=+wCwN酣/#ؓBsc8^_gQT:#]d[i+JJԮJԮܣTc _h_tydBJ\v s7I%b LN8rL)(WHe*̺Rn,(R(+(R(+۔:_s.=7&L0ufƥk1L_1DR}CkXz/~,?'4.g­H'Կ@)vSʿ e_ߛ 7EQ8P<`u >U!$Ғ(ژ8-WMmu}PN%0U;}zRB.>Ri?JV]+J駉Tes?琖'448\qH˙-7:7@,ѧPs+Nl3Qnw*u1MEJT*TŸhv(3PʆT-dM7~Bi!/j 6C-av1<٩.F}CDwIGYNBdפRK4_IOJN<@wԟJ-Bߤ}*F\L#G",Xش[ 5}Re)PZ>@c6GP($g^HN>TĹjJRV)!U4uALCP*R;JmO6h[Ts:s2epb ecl1E1@4]P (ōvP M⿡y"o{?J%TTYMc9 Q/`Ah>5cОv AUԊ$r^~I56O.PhtJl@Rl2 P RꃳRelRdL>jzgBOFNf QCtxb&uk~|3(PD](ezJ7wԭW#̰<(^ v_D zK2GaJJ U䓽hs}<] .wraiAm(eA)J@J%vvWX^G&-|I|i|W[tu9iZDJ͉f J3n $F^S4RPJڤID NMR)z@ŷ#%s}pgcQɖRvRR(JJ%v^CiT<&4Ӹ >~M>+L<&XXx r^S~JYP Pj;(R ~U[xM ZVl]]K5I4ωLVWWhe(eA)J@Ryy6!]pAs8مLՊXTjo7zBRR(JJ%R?o,1LGh81W%Uɡ*r0%+~2c)uҠ (PRJR?k,Ԍ:'4/vʪBSg,GUN(@¼OGB΃*j>̭R#/OrxtG>=Dh<ѻz㗫nokȖ>Ai(eA)J@J%R?i,P(#;Fˢ/.SipV8Mb Q)O5j( 5sRRJvPj@U AJ逿"B(.::%du)LJPQćuͣ F͠vP Pj;SL:*)&ՂeyPEl٩#q= 88 k[)pxhQ(ee8@/A[ԀR%~:t.+UGJ*ELąR1˺Wձ\RRk jӌ*EgFpwJ%vP T4R+uԇ=E^[ ImfMHJs鈔]5nIDATRReJ3:) I&qV-q7~.,UMaYZi;^@) JT*PʂR(RyR3UcF+yg@#]Պtm|äR:J}ΠvUWBxYԂ^_=.6uI =RzRAt(%Pd*w6UWAVMnTn0Nl3vP*9v(eA &7JTӕ:fqWkCD`uGuvI톖Uz JmbRj,( (RЃR3/b-ƃ&X"qѓBM*z~u(=sOP To6Rn:R^Q?'꛹K$nR3FbG,w#U P*R(RL#1H]({3\Wd.{e y5vPjPʂR+JNJ>`^\*k"c}ޠN)T3(z_k,tEB˟Vk 0jPvIIӦ W@) JTJmP*ӳRgbo=;"EzNkPU+ nP:(u(R_)zSOhX޽LgT/7RRs|ԭTK (5Vʠ^iÖ_6PIJ+۽fPj;(R ~U{"ZV7!^nPUM)U P*R(RyJ1'[]{U*oK*PʂRN%:J@) J9P Jm*%кS=+],oW%R(JM +%oW$Zv:}JTJmP*^/> `Y8Բ(]cl,(R!/QrJe&>RIV^vZ[SPة׵5^+&Js{lJ46ͼs/e|fi^FY췩 P*KJJׯȕo}Õk;9ܰ{p[|rp'`gP `WP `_a ؋tV+a"A(W"ہi[EJuvOToW zHV3 HtM?ҹR @A)xt6wxK 埾JAt8ʦ\s`_++(' `z"IENDB`splash/docs/figs/surfdens.pdf000644 000766 000000 00000016064 13261626263 017177 0ustar00dpricewheel000000 000000 %PDF-1.3 %쏢 5 0 obj <> stream xKe1R93ߏ @ 6m+RRÏ~'$?~O?W_}?|Y2ZӇUY?9Ě>I[ȳA̪F)u|$)?eOOQ-3R[Rt%"y^KE>: TnUhUe.?Vɹĩڟ::~ĪؔyVC`|J3ozĪZڋkZ|.S,}X(iq=W=TT"t~lJYʖx㺳L{/~[EZOeJ[4ZEZOdHKliW+-li{%.QIFU9*2EM6A?_ |VLufU{^/F 4%$M#0n"\#UKG;GɅ+K/0Vs6 gbXTq2wH y$z1x*ϑɃjx1]hq!5ϴIm/Iˣ.Q5h xuVZ80Sy)Kť虱qY}g&L+3W.33"3˫{m&gz([< 7gUC1v`R(9Z6{L_ƥx^iR Lv`SM3_YdSݴ`lW̖ZizfKӪ<1)&5YT%[}7P3 2GhuI*3"n,##%#J4mDyH"!FZ6BEeWt*bDJFYU#FY;IrbbD5F`Hozĕs2~տ9>bPjOV@}5Ш[,u렪XթVBGBU [ ШS5ͫ&!VukQ=bU*#Fꢪիy!Om4 ktG+׻Ltt3Vpp+0 xtJ>VTKu1$}U9fTtuye&E^ۧxy*cwC2DKI yd,isWkԯ637Źk۷*:PRWw%T4X1+r+-WjcpXDn|#pmb˰fW䘪fU=Mս\\OJa[ _>idX (0K,0u`Z\ UgsB|GZ<)f״U l4_UṳlUkЫS^c\6 Ve4< ϹgP <3aZC&=L;/e&צ.g;rq1SV^jVM]۸{Ue!q%IY3wgiXgJ߆O_rUl ίOYd>xZYRdXQU^(^uIKn={E-0ܧȠPP9ƪ$gb9HYgOY:Ȍj̺ދIS;[:^ADc4T}&58 5uꑱjEA38 y/rsNΏ!ByeVoL Lӳ82ɹuI AB@Z^%Z<0SVfH;x{RIDFĽfG#3)6[< #cU92wNNZ5`@}ױQ 2_ɻ둡:Ό"3eUfьM3c{Ϡ+ƕg&=gLޝ=cUv޲|c8ˀHC㹙7s-ȌH5?~72VV`PU-P=!(V+k3JiEF|F)vFWG0^g3o[@፵Fd􊡃=wd#;x*z<zuc4n=;HKO"bɸoOE; L+PDΏKo-lRdZL2ncd62M4v ~; "0M)2t@RrZ4Wu:j;ЈUفFdFeBd&E"#FgA@vYm݁G\T =sפioһ̨Եxi r"ϸ6cG\`"6ぱՖGFc205K`&}B9tg:@sPNop'ES/T+2ޓ*/SoC= P1w@4򟔱H5!9 i*iΥКHU*`έV`1ϙܒwv]6q=c EBzo#>F‚"cբ.C|sd>hl-f z *Z%92kpE/' ޙXuJrdjhf+]NX[baH6q`:+Wo#RA5Qn6ƬjK"^62RUebMx!^62ViHic52VSms?2WץW ir1xHSVk*˅c]cr;^ -:ɴ/DQ.2# Qek3l`NekqcUE[I+Ezx@{uVTڲU'jgPq1W:Q:*X36jr=2VMi+# ңܑA%z#gZ|lYLl +6gPž'-n52Vex^u F!ly WSjXbMŜ)~52uTǘ/AIUZ񪑱:sM l?Z3QA?gZ ;,uJVaFƪOh9oC٘3y'g:eW4;MհAlä36{2`-5gb_64#dTVAsHI|jd*2qAT\4ijFƪ /ӨE%m@РŴVd7 2VaIlXt)20lm^@dP^d; 2V KR{g[Šyjt[-V_ͥv/}=qPTy`KJM7>% *#bW ϫSVT5yO U %>#hi=cY%/gPw9fsSO5Ciĕ)Ae˺dWEfYhw2O^d4%es= d) *<5do=2KMϸ\_[g4?%"ǠΥ1L=k:񥁱G;UwA>"N/>70@A5sk}b9Qׇ^T\}v>ZW:Ě3W,gPa /KAÎs䙘!h5{T5gPaVԾ }'#hҹX!hN4u=fu9A_Ui`&f_]GWÝ"GJ-(40x q,XdkVL40ѰP$ ;_}v,ZHa=#*ԫ3O4ֳbנո SS.{VZȠ:ay/ \r u. *~3 룆:S&-UMLE\-OzQ o:ԇ:6%kz yݽXղcՂ`)V *TjUϠ:Yq(VUgOjPKsn{U=&Sz}FKj?oiGS-ThOA5ѷ5eǺ kcTuAwՇչ`F}G:Ϭ~40Vt{E`PaCguA:86FT4NjgW|X3V0{ 16biϬj躿c[k= 4Au2Ξfnzn`PF6<5jjaT5SRT~Tĥz͹i&5AӨTXs"fZR;9?{3']^(ajϮ]*DaPJu#h>EOrTCL\\lTXK`ډ~.xznf*u=*>Wb^ϠbZվeUTh˓/$A9):X2Xx$MAU4N+=T߆g_5BȬ7}޻$p.2% X}. eU wyxgl?\e{&M@ZWJ|@F#xeL+IaGeE[u72IoȌ. 7jL@\w1Nz"&%Lv#Sh1"cչrÚ52,a\endstream endobj 6 0 obj 6268 endobj 4 0 obj <
> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 /Rotate 90>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 8 0 obj <> endobj 2 0 obj <>endobj xref 0 9 0000000000 65535 f 0000006583 00000 n 0000006701 00000 n 0000006514 00000 n 0000006373 00000 n 0000000015 00000 n 0000006353 00000 n 0000006631 00000 n 0000006672 00000 n trailer << /Size 9 /Root 1 0 R /Info 2 0 R /ID [<8DC7A90EB5E28E3259AA9B7920A18244><8DC7A90EB5E28E3259AA9B7920A18244>] >> startxref 6889 %%EOF splash/docs/figs/renderplot.jpg000644 000766 000000 00000445121 13261626263 017533 0ustar00dpricewheel000000 000000 JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222V" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?x+Lvmmmi±0Esl,9d{*LO [ghz=15*́W8<Vb60&F#sc7{aklvpܴD@ D䯖bs@΍u@Mx/3;7'`{gVg%xȢl o)< 6c;qր=fVZ$Y?bv tkWq?yMF|kz_> }jw/ sgqm0N|/黏mmX`pxz.hmuO౸]MJZ;Rb#S}kK?.=޾t8ΗpȒ|ؾLʬ>vB+I$PeFXCKc]%ȏPFxwFx2<S.ak&}Z6#I&a#& ٍU_ ɧ:tOkvxa4]Eu o*Md@ppyq?p -? 56E<ƄFP{WmZS-6Qv p9][FkkHKũiVbXxZkA)m -)2oiJrQRզ7!#{ή €=+R Kۥg8r<0>ѤOa'NTbelpf{]99%|/XE+9=q?HP$WH +dIZel/v/~QΪjz\74Vљ%p mQrj\/ ŷ.Oڀ:+Z[(dd{ټ YN"O<#,~]y;TnN9_ ,-{WMDn[ph+1ހ=OH {jʼn8;n2k .(LN)Eۺ .qxmW: [8% lM78@:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?H渇Ϳ 'Q+C i|_ [bw|@?Zonc֌E-7YSX/O++5ĊYܲ(tC??h.Ƕ?4*i+w<C??h>Zjt {wnjd \IAj}ӥzs u/-+vs?"ױ]I QCe-㱛on6䟡mc'ȜeX7Zq?Sg co8=@a'wy8a[C.=kCK.Esomt#u$y+[~"G]$,"0\Dø]k"Ep^qB@K幷B^  P1ҽ?ok:͠ыSo/_P`HIz*$)Ixc-1,+ɲ=G|vy>M]K{tA|2H>$i}[i F_qV:tcss{9 st4t $uh.nkk9UAEx_gBYf˙ppLnدX~xt;FdSrҀ7CMKSICNB d ?L~v\G|L23ߞPyҕ7R`m/i-wt2nnmyLko q!dc3FG5kHk>"EN!iYeH>dK:3cQ oo^fsWV ʣ%<b<:$+,RF#yf%fqdBS$ޗdթSI}7Ψ!s)䊱Zō.e,Tү|wO˱r1{fpf ?ڗ."qۡ5ω<7HI? 쌒xInZ jp.-EY8)FcȮTZjwsI?>'j#Nvv2B81@_Z+("4m*ҩl_7i•]m8޽ Xtcy'g:m& u;$=>2@Q @8OjYX/e<҇&bv|mH'#3V mo$ƅ2kY|m\oe6vL#dX#Þ{ĚwlUHyV(!glpjk~0O!"Ywbx$UxD.[M Y>ug{4u;#s>md8m4{Aso ߃&0?~}TXo/-kG,l ᇸ,@~xl̆v @H0'e}o'xh߷8kЮ[(i\ HjY},"?4y *+O3m7Vn,-[!Kf?>H-G\R|{@4/l쯦՚Mo,B`v3(k/NX!B쐩w?E^Q2:KT {cHY\q牢_^Owb?7;Us2{qxUVDڤ}^+2xNO]ݴq#,Mo׶=#7^8m BNkwxġAfs(wC-i1m#gMz8s[:l.i#ȳBe^߭Z?robiL,Sƺ2r?o?*<+gE0MSݙy\Fu/^iu{mZ;BrJpĸ 1Ҕ4Ju_,Q{if܆G#7Z_\s-qCHlo4.C̒Dp9sֻXЮ(ؙHUt1%MFu,[5[c6$XI?7ZGWGs}eGW~5E@5vQQmvNˌyzAW5<]*Ҵ%vm<-JqZ#?#?#⫉m;=W2sՋjwZqi#n 9Wb.YJu"=C|/|/,o"ϗqʟFSוֹ ό?*ό?*o|//!{+Il򧍢}F`Ѿ+[ĥ(*M]Im,n27Ѿ ό?*ό?*o|/|/}73yQl2|U.7GQGQo|eTo|eTo}U&鳟˟]T@o4}MeqHöX#! jywѾ ό?*ό?*o|/|/G [dͱ|z~5q,j$nOP8(_F_Yz4K8fs$1G0RYhk-27>25,ⶒAoQbǀcp'b&Գtٴ0 WWN1@_F_X^Lj-&[[GW,TU[UŖk76ĮmpY`4o|eTo|eU?{h27>2|UcPOo5'd$rp78 u: e-ce-e7s@>27>2e6\S 4| Նҥ)yRbè>ό?*ό?*tx:`3et;I}sj@_F_F7GQGQo|eTo|eTo}UU@_F_F7GQGQo|eTo|eTo}UU@_F_FFl/um/5< I4R) Q֪^4+gsxx ?+Ɵ]W?JY((ּ#]=֒J-m aŷ 8Wz%8zVFZFJ:ik S?G?>g--c`$$ƽ3xxվ ۩v_n/nWU[)2|`<uQTW-+~W?JY<uQg)sznnb+0ia - U+Ɵ]noo ?+Ɵ]W?JYFҴbWԵ30Cnmg$): ơ%̗Ȓg\2v2}FYxO<]z5 ZcCE"FU^0 o|/W]T7e>62N*77" :[iۭi?g~qyHbF7>2[/X]WEkJrU!Ҵ@ҴN 02)bsZIKq0 w܌c* @ q>2d$<:F/ sW?|eToUZhh*OKMȻzN[iܻ>2"UZhh*OK[MHۺΥWqT$pO&UZhhGU7Ibi. {'s@i%  BjM/i٥R\A5|2||]/}#lp햸ns3xQd-Z. IFluj7P][$}@~t aES>t-n m0:#r_Ү[+5;X};DH&\||Ի?_V?|eTH&RA\h, `a* Lz"r%#f(d@4o}#q>2[kn`r#\%Hن0 2*--`q#F9 $N8h}/}#⩒]^-Nqy\6}Ny?JOR]0ހ}#q>2||Bf,Kzt/V?j.Q1Sό?*44W}#q>2||]//\Uo ?_FGU*twwό?*44W}#q>2||BӞUEqs%FY4UI5|ď2۠9VpF=r8NW diXd%W1KJH5+D7rP',P%!ڜn+7;;sڴ;7l7 N^]I.-n3n l, qZ=(Sg$w3Z_^J?]9MYP:2æݛY-men 3)'ҺmVu=2kBK=) @/G%.G#.iGu5*NOTdC sݬ`.03i=܈#󒨹 'G%.G#.p}*"Msjg+4kH+OVfIm2O F; kz0)zP)iUyoMA}?=hkhK?G4^Bo?/RzQ нg(?|Կ^E_/Y oRӯ$oE/q 2NH z{אE_/Y ?/z=7!bW%@㒽qUlVQo%ƞyuy;x4^Bo/RzPZ-ccSNr^Mgk91̖`JP<+/RzQ нg(ܿw5no;sҐOx4^Bo/RzP4>kY3+ gA&|Gy=U~ڲhP7+<ơ0In`gyG4^Bo'PfV>,t#zzFXhC/cdp䎙; m_f4Ab69NOA^M нg(Կ^Z朶 LBܼfE˹I@9[V6/k,)@x:x4^Bo/RzPO`L BE-A0\HAԿ^E_/Y f-E6J>@d@^;QD׮v<1:E+1zqS^' нg(Կ^6szpq1k̀0\mPsZ$,Ќ D d1a{k/z €=zH4=6-̌o%T*e= {lUR[_U|ׂE_/Y ?/}||ϟz hK?@A454^Bo/RzP| нg(Կ^G_>E_/Y ?/}||ϟz hK?@A454^Bo/RzP| нg(Կ^G_>E_/Y ?/}||ה=}rOL٤+"rWsڻw}yǐ WqVFۂ6r:9o.cgx䷏hrYbW.Ȫ5UFAP~@6?ֽcxDZ ?]oZٮ0(((((((((((((((((((((((((((((((((((((((((((((((((((((>:[Ϣr\4vm>fR?6:׉ZfuG;[͢]Ѭo"?i$wa sv#տi[tC7;Xp2I@?3y46p=2Hy&# #1mIpr GOoI ldIRLc!6@㜃p؝6I+!9pYnqo,|oMS̍6:<gur<5xฒtdp۱4%rruU;X t{"(v2('R)8U/5 Ds7a搹r}Ybz,{N4Tqj@RlMB.Z2p~4 WwK}>hxD}[~ ;tw)ݞxjPԴ칎ܤ8HQ\dfvy[H,^h--t^;[;]&8,]:pnJN^m4 g?vmIc;M-$˪ AxQӠϓV׮yu JkK$,L;$ "ל(dFF2jrl쓠@P36F7;'&; 3Ldi''5))Y~۶G!`q9x_/-mSY֖KeE04sqǟ˪Mq盙Iv.wU@1!R -Ffv*ÎX︎s@`Ym-4׷vanZH|`1/+]M'MSFlF[tnLf,@V~FYxa,WT,,zˆXC~2Fp|~z}&Zע=k焊'BFB3Þ5ԯl,۹@O/,(H630O99l5:v'h-2#Q_]ū]ItүڒG.dsAed5/iaԝ $J{;RGI Cy4mw >gZk)[MZ8ȮSBX8Ca|h2!o6A'oc6}w˸u>z؆gNվͪci,~WڣILn|^9|wGHUڙ.<"Be}o1ma9hubQQo\p$Nz|!#}ېz)4隇MRUq)p2;6619K .Y[J= dJ„moc;}wXU[%%d H˱MGyjZڅ43䪒= Pa M4t4ZXԭMN: ,!w{d薶wwg},$r2;*\ m[RdF._{36.1`d!99hl]W=ōLT4+.3Oxo@!5Ynu 2%7>IBA@>nHdɦGqwpzR,uss y =KwX,ŝ}sj%i3\;,IY=N@/e|v {,}$X9]|@zgƥsjGw3?oh6I-eVԕ,u ^D͋v, `F9,%Km V%Xȥf~H+{NK>7` ]YbL(pNKrp[w:̰20<Ŝ&n]1^Bl4_[L:nݧ '7dO/et-kq-.#W*d3^.^۰-7@<>|^=hM𑻹Hn N~qsx=/7͹|ڝ*<,t}7^L#=. \|?j$t]̐~aa[fJwY#m84˓>cZ- Obِ,dm;B-bjM<*y/66OIyrA>6{n>_!osp;8W/6[m,UqDۻ۸ƫI2J}ay^Igi=x rS 4}Gsu? GEx\WaE^[l҆۷鉨4 04ۛ>_9~o{}Z63z혬zm- >Y=WrZ#HPy,Wm:oMCqhlj| mp?yUcﹺaw Q#<.d+0Դٴ˅FXA$c2HqA*0 IԖ/Onpao-\#TŀhNЅ}(j~OmNhdk9$~DCqʌ/r{$OoncMТ캅%AʀH9 wga<ڌlxDN C0+z-*q,5hZ}<ͳnz?z$p+>=rK?V7^ϸa[İ+Z-gxFemd^?i~i?qGeGORWsN26.]n(>m.o̒.˒I#0c؏h}_kW qiq0ksѼT@8ids\xzB̻zZsF<}GmuBbyVDFxlI6>YOEwi5iw-Wܦv Qo]vP1v _~chv<Ϻ8z(΄ Lؒl/|i5iw-Wܦv QpPq˝Qn18mϗ;k|<,4}^t'> ^['jdDmdžēa~c埔Pq6yl\Aw}{>o=okpQ˝Qn18mϗ;k|;xXhO}@O*Ȉۏ &?)6yl\Aw}{>o=okpWEwo]vP1v _~chv<Ϻ+GmuBbyVDFxlI6>YOEwi5iw-Wܦv Qo]vP1v _~chv<Ϻ8z(΄ Lؒl/|i5iw-Wܦv QpPq˝Qn18mϗ;k|>BÒKq0\]!NykO |q\QEQE?ko\̫_SG^ 2g1o =k>42:MLc3I` F^?^GC~ Z(0Z` Z W) (Rj"b+&)qJ;h MFN8 M ;Rvp^jnj0/4KW4P-?i\3hJicL%%/"QGj(RR@ $_ K_WyZ襦#CB-4{6+tI$3n`2r@'=Mus~ #/s0o+SGB}֣(巸&T)$r(eu#x ̏QFG;HoXZBmvЬJ[̺!@ +?~ֿan맮;VaEUQEQEQEG<[oqsA*92<GO[WC^6+aۼI#\;~QP2],knmJRYioceswf ;6.#(wexyBSLhTD)f#!OZتzU:]h^>n`(BY۠@Q@Q@'B);C{*Ń,jx,8Gk%դڇݛUHacbe @r?)52(D5&IDeQՎ0 d ĺ$,}]Ns32YQ}vr[j$Mz Ǒ})26$VPN?Ś%u`yh '&#*埊ᶷ-ɻ-{)\my7pB2y|ߠP:6}{C<~Guդi&Jq Dp.Vnghff[M$_ ¬H>C|lAgI"!w i02=F]6°rAvgK)-*.̚_g]WEgaxGNkyBygef'<ʔݸè5TAj{C"_,T83)\7@xA&A=D $+\,8t5zOV|]snݳ{ۜ 8g@8{2Z,)w5qCfV$QnIme-ݘ+OԢus&넚(YZ$1d*;#i wLwnv߱N3& ~ꉩS\ZK,7[.p:G,3 2vl T^snVbE†ބVݴW&JhnHv*ے0zDA2+stWX|{š%Waul]2vcÑ1r .oDv1g$UDiV ^5 ^ԏDfծPb}5ݮpYnDqA0Fk. ( (CSwIJ W۴)fi.[ ؞!; ܶnݻ9]qd}QEQEQEQEQEQEQEQEzG?nduW˟uёzo@gCO.2?\W:dmi2 y#z-HZG[Wt὏jH=[Ls!j|fDx;zfm'q?6EmJɅQL((((((?h(v F^A^GCT6j CAE (KAI-RSr)qK\R-!1K\RJ (J"J8-8 cSJMcͣiiRSNN J汀yʹiivҹ0-jLRTRR^f6Rd{hRmmfG&LQpc6vԸmEmKBKDVLVtȊ+M+NnDSvijdDVV"̥)TSJ&c(RM"3)@LTSHs,Rb7W2qLS"n#qM?fB)Si)HMB' *E-|C_oxI熿kw)kwpCZ>e=Lm26+tY$jN'$s޺޼KVw:nL8n` IaJI]H#.qSk ѷu=3OҼgYB}vj[̺€39ڽ_8j|L(Ѐ(((Ri4}.m;Ry15۫"r lL46ʐ;X*tn dEy*k 7OmiȓZ]Y hYGt+$O$K5=ܰ9^7ĎK:VNpc^Al%dI]$.sV%I(([xEVi4p^FFVt⾈, VaoMjq! d.sJsú~S,vk\*7-RT (AǸ=ƍ_:T\ț"]+ ؼYpFn$qjxOúnj}EU@U`pr[m }66OzkwiuI&{,\47ؖ3.ĐBe0%r\jVOd62eܬy0Bյge [{h{h7l73 1˻``j4{;˖#ўH(`+ʹ(|dLJm>S5XN)qb1yٟ&9%bz}ƟTvy|/k^>7 n2ka!kq*b\IiTe?9 Vz&sɭE4MLnvUܐ+?Qy ןc] 9l=(BoO=śP]}0ökoEYWl!gNRK>yR"Dy_Ic#)}ҴR)qv#Y]. אPr*_,cnmvξL."$|铵rpxɮĞ|1G{y.Qa B~c8袊Mx$VJkpf`R*%W-tx{ +k"ՖG2nPK A` :(I[dy\Hř؜IyuW kXlV[QX+sÂ#;89ZEᗆ+ۜL $ rͅ&R H[!&%sOgXg_ٿ|7ۻߜ-XOj!oc嘤g.© . Ys^IV$AiBMy!mBd$9 hMo/uh7 A*}HcM# #p9\?s˸m/Ju[䛩ȮKdn !!1%]Q/[HXD-_S2L 2@̄chv^ů4YK18 U@m{ m3vӯ?qw7qFZ)L'vr>bw@'aZZܫg` V]w &Èڕ֗7IjR]/*h,06Xʛ2I\'cP5;kYu_@ҡYpƨ-3L.m[$F14BXY7$Wz[r2BNu Xo,ww#jiZm-&,${k _1pctl1| ˢ,_\jzeԯ4ϴ Ė8 zUz(<2j]Gu+闭 XJZ}ʘ$yWq7*(Q-wѣ"CC?T19>^;-MMZ jݲвCt.Cj u\.,=. VKND&(C2~s啈4b 6ٶ6ɖ^X#vS@Vlj<9uF;+#%$}٣F?t=cEX9n~ dLnq$d[iq|GiPH-A;a7 7PQEQEQEzG?nduW˟uёzo@]uy{r ^i:ʩ6[sCȻe c'quN/;9#?ʼ }4’@~I#QbO <i-kb"s:ElbB(EPEPEPEPEPEPw;Gf _*?-y "P: Z)TKE()@W5Q pSlD@)RNh@)@N Rٴ` )Sl08)S⥳hh)SݴӱNeLhSbK W4PP)ivҹj(IƩmmK1JrmmK1EÐmj\Q9ِTi6ӹ>̋m!Z&)ܗLm&*]N8␎*]ݴfM"3)@i1ZiZ̥)TSHLP!#a1*1qHEJE4%")TSHLQ"4i2HIRM"3u%QC)MfJqKBWyZ襯k<R! |x6E-no{9mZ}kÜzяQڠ}h}jy ѷuO56F]a^ i˭FQEldQEQEQE_2jFik<ηvmpci 22rN~\c;xOҮ9@;K)Ya1PH` c;[. S7#K-IJIdijw;`HЪ7jqBo x\`8 :wV(((iid.H[%XU@knuG[b݃ J4; eǘT 2玘:n%(]} I##:oikB\,\?;Ɋ8aťRߛysg>в B),8%)=, kiwچ&qpLbg'8.|ry;3ں#PI.|8ږ#A YRxarBvvZհM{MNHfYb+29Y] Qɠ u%Y_UiQ:rV@ $\ԍ]y[ԋߠK7O T 9RFx8w>ϡ,Z:‘n+Ǥؓ!1l| 7iL-q&qD 1BPAېX`~MJgӑc̐F3 &ѣGk\[ڤPrQpmb| 8xF]032 #($e,z/uKm[TḹwGEMȠ-0=NƝ$Vw/9B$6N@s u*tur~ /d5o͐aHwS,N r/\_|LJ]l+a0X%U˨bŞ$> Vȕgν'r+ЛL_F [6|/8ZNbS" LJy3<8epЕupD\ޫ2BgŞ$wW[~2^] `p3ҲhRdY&Mpl7ܪp{{WyZ隌ZwB8N˾2VU5CR0 rpO0؅*唆tf-~}LBvѽHTcwXվߴ?vuuri[9y4cB1y[ 8f8! \iM^).Ȏ15D,0VDdϐ=[RKKPM>g-'n9d W;JݞOT[M~7)fVWۓ'iU'rU~G$BH2oH}Faڮi:U3.3I'Ӽ?wy} mo1'u@9ڂ6@60X[H߇^Su+ϛ􃃅*A .k yiD!݄l} ay_%] ό3r=ľ4 ^`l y R3D<Ɠ[[k4 CG^]Ə[L[+ !<'~]y:3gFq{hhn_hϙdߞwg99@u5:;7*D$73)m:9˻Ԥ]RKD^..&"Fk(`յ-mu IgҟesLH@Nc RZ '5Xbi]76*b; 'RGyjZOp0h5,*ubd-ʌEswjÿa^o<'{[v~y;mGw|Cʿh$qHy]>]?'W wx2*n|dq8ܹ5]H:iѾ5E!c6dRːQT$p>Y/:U6 6P'<ZKoq9#<k^I_% C v@]eGkPkTxaYMRGe8%c*vSTkU;K2!s5ki&͖sp{+Mosm6QWSe8 pxKp ]dN w2%A;@,ۏI4'+?@O;>lW5:2>_^Ezwk Hc;י[ۻ|ʭeErVj>q}!m'|c%-knaES((((((7JѳWׯѿP? EP1KH)“- Jp5H@*[5@ pOh@)RNx@)i@Rټ` (CfixNU⥳0-8-8 p-@NM͔8/4 F6ӶœS|T(m GPUi_ZHfe//ޤDsO.zV^34C>stXʭN0ȩs ߴk#~j:WyITL7|>ѸW~1hZ =}<-En{Ӑ?Քz r*1M`ݛ4EwQIl`ruG]AM* I+'Y%D˱Q]Akv-ooo{},j ;C_kR8dBAV$sɩm ÎCn1r1ZƉ\R77WRrc #(֥#TY +n$X,U1$ zk%ճ|$0R _췗zo+5ܒo :HON[ɨ4KOMM "̸(H 09֮YqKU&3v6# İ+96"ݤS}{u|)0^5wВX^iYOiu7 om\ʋ,2Gq]$1Q5;o][jN$v` #"Lr {{2[\<˭* yb&v{9 ˍ'Rk]>٢+VrKcVP{#IԴ{[> m$в,ˀr0=k<[a=K_\_oou5ҳ"lAyk[K}%Ԓgi,.d̨sݸ讃nlmg?v˄o HwfDZ^h7ZNyM>nmX]Y$*f]A̡v9TxKUI/emije[~g>\@/'gm2YdXl(73chRϴõIC^uM"t˸Vp,TN pv1#8֦M^2|[BUPyh`ߒOW&mekw6"İM+y( Swa@8 2K;I.gʞFAsRj\*ir] -,m#dR nl~'ӭ.T?tb1uςŰr֭/]6W&a`sLr]G8>:@Džt{ywB_u6#}2<.4(dU 2Gia$wyu-0HҪ:Z_L1f$p1p ; 'R_f9pmi|X4 'Rk]>٢+VrKcVP{#z<%vÄxY?3@.x Wqooo{},j ;C_kR8dBA^&ihuwov&cYTH5gkᳲүnK9ww_hC)=k_hkRhSF"VGi%7 V>X `?#B#JK[IL35@@(+d"ִ ;}>jn-ʬhf ӆg;WF +S:¾%{HFH1p|iF $F˜;>}ϗ}}'eIxD |\pYBGke-$KLHd{hٕS( I\K.$uF;;F{` T:nY YHk=՚ZG yWa:^]ArPc`>yih]zGqe<1U\g}ɛQ/ Ry\gݵsPEP \I`aBTA%31$Q@Q@Q@Q@OG[}G?nduP|p)BE/\Wܟ3i`^K`lH_55Nk[Uo5[ QE1Q@Q@Q@Q@Q@Q@0ѣ.ѳWWџP?Rf 6HQJ(*[5S p-"N)T6tF"O)T6o)Bl(*:#J0 xij[7dV.:T9($ԫ:7*d@#ug,Y*au"uD\dB08]xAV2֟}jЈdqRPGԾW֬><RO}j=(O(zQJ=|W֯yCҐj~ >+WjElxg~z_*6FPH_VWr=HrN2GNOY.ׄ>3q@u]y;Ś6V5%E-gxw·6@!F1H*}v[Fݏk+}vˉ`}5̱2<Ʌ㍵YEfTU3|W96iw Oeq%%K#$dAZ"GҒKO%8~~f-#2N$.6 U jSk:p\H3bI84?o4mOMDmL\y!I/i VOD+W2(A>Ly KSdZuGhMocU71|C|'/:(}),hpGh [H'T\gtxv~ӯ';?P[`:/T,U]=nk(-KKH]}sؑ1 y!_➷m?`XCBP*8&.C{4uwi#˂젒JV"6urɻ-|@z<&Ibh FyE 032Q6EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP|FG_Q|OG[}Gߴ"o}8U.{㯧ҽwo⒠͜/kJP1FNqXq]x0>mZܬ_ tߵl&QE0 ( ( ( ( ( (>b*?5y\*?5y fZ )@SfBNS⡛ < @)T6tB < PO1)i@PO0 xZP\mԩ&a#VrV#HNqbV1sʠ\S'5*EZ~"qTXT>JUbjJY*r2]V>1ҝ{~|_$յN~+rǎXוQ{~o'Ryc~YJ9BS ${~y^ߥ?h(4?x1)r})n|ǁy9TDŊŎVG1TOl~V=?*;Mh}TM?j35Po>FYix0xX˪I'F]Wy_]wX(9(((b3:M͍m![W<וBGoNIl1A\K+29'Iw ̑ϻN SjOp㸸WhfxI*82qXM%ӠӡY@9#M*]XH9 ElPEPEPҐ+xB'K 'd}8IizE޿bnk`y,l%Pep]!T-uq-To`F3^ ftdbC r3@|GcOh4 U$nI;{2e ;_iiWz!kcsX6+|^^S{1i2;`2Ǔ([ξTOx=|szM' mm4i~ Ue3A c >J|qt))~2EOҤX`|1cTl~*:bX%1,|Uu<玞դU?ox*~:Y"+T$U"5Yc jR)Tj6f *b)UsN$,)*fZ3q""""EZg,BE00YĈaHViDiM5hHe}O'UZ?*cO<5`_q9*<^k*l!&W|''r}k ?ѹ?Z|5zԕYF؟'֙,iO$Q]g0QEQEQEV^è-]PE4xrO$ˇ\]͝J(g~\)bĒIff9,ĒKI$I5b(((keo{5?/i#|&K#!+Ĵ kzX&s IuGS\rbFp9 Z*y%1|3cueO# ԑK\ۺwde#c6y:EjOkoc!-QR]A;]y[YѠk3 ÐHVpmԒ4El?># K,yMlm YYXvwN^v]i&f\$aQ@ew]G <RGSisjio%XXč ?2Oq@cge{]_!ǚΈ* : g WךDԍ:ۋW +UY2諑:\ڤZ}n1#q>0̼z:((67v>H%* ,HIT6iw Oeq%%K#$dA@袊((Mk@MAcIn"VD<njpѶW8 _Mu-f>uB-+!A8>)W#u)H}ݬ,bF|`x'%ѯ#]+O>MR/6(Ago޼AqZ33w3ew]G <U55%@[-2?F^]Q[ot)awa ª# I~@0tU[MFoF2GtQEQEQEQEQEQEQEuёzo_.|FG_QxHKf*9$ðR9=yj[2^EnB#d+ϭA|=ٖ)krOceZܭP0)QEQEQEQEQENIt->{s$04͒r }jP̟O;Gf%kc`lⳖeE< h+6uE  0)ST6u"RMlESlE*EZͳҬG;RDULV3EZ?j#jiߵrb"8 F#Ҭ")ǽNTN3XH)bFb)ʝpV?n*EڤUUY9FS?Jx1֫xkFD@[c3^>Ui"1Z.&~˒221Vxۊӕ=j=+"Ij\M\E03 Ąc c YĀa1g,D£aSLaV8L""EZg,DE4{SMZ9}O'UZWO<5`_N*c˼4]ajoxSO4˨,m/2hUwʩ9 d[DV}g%==-V9xdԫ#yѧ)|?9V^6_zy+Kx+;X-6LRw߁WWe-u; (N@(((((((i;uдK9 i'h*v1#20L{גaagjZCs<L4+2h;Np=+[y3]55W^hfy 6}}ƠsRj\*ir] -,m#dR nl, 8%{[rFeebXl}q=[xMFb.=:yKtem#7v@7f5?hRdŭ5+iELlx27C{P:/SL[=ei$ñ9@+o_Uk[K}%Ԓgi,.d̨sݸ ~#ndٲmZA%a88 hY]G@ !r)p#!4%]s2rPii*XYQ}vr[j$Mz Ǒ})26$VPHρ48%A'Kv[[E$Sq,a%!ؠ w}qemArٛӊ5[ ZvjzK vK%LKG:Wi#NYzpUvC vIn;c~BᕚOe۾aw%A$ת*: yg٘"#bEl d_*uM.[K[&ٶ |XuxDѮo2op;]*8mrcx[sl+xtͶK[uQEQEQEQEQEQEQE/?*/5y8X */5yH;BOf("h :@R(lG(HHlE "ͳ5b4djsVLc5nļULcbLVLc9^1yDY;,!Q:UNӐj'<4!?:Ue^+R%;ԡqJJXCUx{S<52ڒMdJJԪHֱc)i=*q)2xU372zSڧ NkEL2(Y}W'ezhn4n5FYӔ~_+/ωA^v_zE}YO~EWqQEQEQEQEQEQEQE|M̭oFoY,\l>Q wcd.r=[yX6QC \Zl"y߻_/>r+> {`(((((((晩M]= ;nCFbhG;\+rW!YE7K^ 0 s1zтNֱ]\ ʂIB*d]E߄,4?\i, V$nD,22*٘O1pĦ%bz}ƟTvy|/˧Gu @&p'&CG(q׭u_iiWz!kcsX6+|^D'׵4f4۶&Yz)cĎOMGjs =vqyXAƶHq 0INi?mՊ;cv靼6ء 6K\pk=nDLx]7Dyp9ݵI'ؓW;̖V1s+MY?ݒHɗ;rL?u8 45\.`6JDaB XX| ^>]f"ݡT[[,{i!iYρ<<}toX |8msZۭQEQEQEQEQEQEQE|CCT_6j+h_(v_ F^T祆^))d*E4T*:*E ;!@RYuB#T 4dRgTQ"'95n%$hgHHμZ2/UdSk"zIPդ{UYc1ꄆeȃު:+NU8TQ섆QaF¬:L+39ā03q!aLaS0VȩVDDS H*4DE}O'UZyZ襭f+m=:;x$|UI''oK8Z;|vVMgЂTnȗ#֙"G4OVF PGqM=h=kr'»;[+;hm6LV s~zy3x;}R*iw ( (((((((keo{5~ m$nٿ{W~_+ѯ=ͫC qǝec 67vQ@Q@Q@Q@Q@Q@Q@C<^ d2F*Jab;Eh[ڢ^j3Q~y$!4݉jDŽZ{hR;+nj]Ə[Jcʸ*~N+V΍;3RBbt{c! d1$,kx^۹ʉ7ݹoSU븵Gt-藲NQ_;s8o-lỞ;[h$!%rp.{5 ob3\mo`kUVUQ9 9m|1m<1s{j2I@I~ˑgA!THw9s/<}In/m{f)`L\qަ.0ڿ?|B0+(f}wD]WY)ajkf!id20rA++'?'w}FoϮƗp-)5`W#U]K2ۘN[urQEQEQEQEQEQEQEuёzo_.|FG_Qy^9==z*wmnp0M{ OOuU^/%>`ǁ|>8MRax)xìz2% ( ( ( ( ( ( (>h?*/5yP꿴 '%TYW {~ g8 E4 ECgd"9EH=EfJfAhI׊ϥsFѧ\QF[x9D/hEr;ըҹ'!ƙjiLDڬ"YCyTԊ*e^+LR: EҬ"{~Ӑ*{T*uNz~Ґ"{T"qRs]1)8B):qRtF2OlSt㊑StU`dGSxSld>y킦T֝U(W=*aFaG 1S|jS52M)*zS sPZTi;նN~5OƳ3E2N9L}\)e?P5,'\dLZ 2%@׊Tkp:#"*Z"P23VgL$Su$OjA"GDYFDUt iH2D'Ѵly-#*F6TNO5ھWo"]¢L$rF2˔eצו|$׼E6k&+U$зcjwQEvAEPEPEPEPEPEPEPkGu&ӮR!V\P>Nkٿihהzjk8 |Т8'kIa7\|We͍oYDN-w(*N 0< <ZKoq9#<_i]wRKwuu? ero+c@0[y%I$Y$I'9Wb'>7υ>b&rrrFFyLmA*1IL?/Pj, 6@?ꂅF.,O[x]42$be`Z^2?xcCu+,vIW4bGrr@97fsayuk+2nk!GrFt8'7V<"ʆ0:A`q׉5koa, oEH Tz~j -o]|RI";ʤrywM{cM)u2\Y (UM"ɵkpyv Dz4Vp ۹#PLO_-}$O1|ۖpNq,I4s9U0\^@>WQ/V!\Z:Cq%믘Xi$Gb9TB|3ɺ8CxKR๒ka*;HmY]_qI7ċ&ծ:K4RE+CsqY7npyBXfF*΃qm][մỤ8 65A=Y۴FBp43lґ;s :(((((((:׃=7X> s^#b<{Bw$bF=+hF+/?Uxt1 1=*㲱G@R`D.E-oU(((((((e`l傽Sg`l+{5ubFx"bk6vSCSVQYvAEzGYJLꊱbz:⬕¸p)HW֣)ϥ[e*&\e()J^}*\ "'ኁ⮑P{~i2):/QWY?ur'D$Qt:.VtIꄊLjqǥWqX-Hjy׊ϭmgU$LvZ&jjꄊFsGSJё{IWHfdA?eרז|'BSC,Q\\r_46:ͤVPWQeG+,^27 _/MIm%).C1Xr\HxM$q2Ԣjz폀u OD kmP`(ey0v眐0u5JkemhB(&8y (さԢZYOj_O4W;|mgh$+"T'XzuSβ^7MjԐsʴ0 ހıh蚔2M$w.8R+p0q\ڦiaX!֮/T g< Y:((((_Z${H#0mdÝql׉jmUVe  a2sзëh$yVIT~;rAA#0Fk5 qōk)̐ `T9S;Whϯ}8餕%jm#ڼAeR/zXHh"I5y㈰Ȧ ;v>d:IdFE9WI"tu#'< U"ľ':v4G@UwgdHԈ$Oh6Z6Z-0H \dSQ&KFt6U<HP-F]QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEzG?nduW˟uёzo@7D^4e?*dk*8mN',=*/4_J=8D䜇"sSqMEu^zq]p) ަUEJ]q)~* jU_Z10Wi꿕(_Zx\'!E;ӂsO5C6{ڔ/W)<{GGKzQzS1zBv"bL@=i}*\JR +cQ=*_ni}k)D"/cңe欲Lm]?u :sVN1PӚM"^*/^+q:#"WuR'T$Pu늮늼/\S:"Zm\T ?:"Ψ3T}Wyʤ` tTRkFE5N@}+ :dTQZR'5JeM͑G5 *OZívAHF¥aQ4DEDELEDkDqMcTL=+Dq+O<5`_f' *E-tx>v42M2Щ$'0?U־JpiǕ[DM*Ņ܂KKi XO=h=k5te%i+k{]kimP%b@^^j :}ym1dxXbIjZ[ 6=BŔfNT1!|rQwJ(쵋[Y5[ZgtZVrѧ(F: ((((((ih׀W_+ѯx&'xȥYAx .)!S@,H ۇGEPE$Ml'HWR(e<*A tQROַE$3$E* (:*I[{xȥYAxG@Q@$Mkq-RC s^#b\'#x?Պ%wI8U*@^%Gҥ@+"{)kr"mi(`QEQEQEQEQEQE|@CT_6j?;?Ef.Z3 ]IH*5R($ZE0TY$QR/jELdVR;`U#t(J P犷xҮ )"󫑮"yq bYATQՄL2D1Fj*k9ɣ^(QƸǥXA{We8sdX{*)]6=Gz@)9NzW\"sMAߵLޚsWhQOQLP<Ž">FMiԠdh &aUbn3aaE\4-!*?a}).%@ife=2+7+LY#5%X)*l.;YzZ3 <(ɫ2jVf뎽j? kjqhm̨rɌ/$w@G-ltijeLזo_&_#͎8>[l:4r!% Ov|>ďvbPHH1Ju6Oԭn)fGi‘.I'm2R{{q;1,@ ͱmyl\j:~MTfѥQ Iopaq&p# ǙAx3[⾲/5!}6{wU.эďF^H zsfWPy_hd2*x$pKMs Ӽ8EDD6abRQ@Oq}bۗ3ش #d23/cxwaZ[hd87*Oc!,*m,T]$4Cy=GJidJ&j?oo43ncmzV}+#z}+(((((((((((((:׃=7X> s^#bn>O*$QlzwrN<Ͻq^^7:Oď *ld`;8⽒ F)4skQEqQ@Q@Q@Q@Q@Q@Q@5XltH&IYJ@͹L?C6!15ƌAܼ˵O!r=o[xuV6OIʉ5Ō3 ڲ$<'7sFtJf_6CO[' EzZ5Ⱦ=cPgx 9d[gf0k?}ϳk~t5@NakZ9#1s,}tĖèJM6\(޹37`W9O-}=Q>jW $ ܾM" ]ɏ, s\힩_ bHd]&1(4}ON~X9AXpIma8?6}PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP|FG_Q|OG[}G㟴1>o{*#u$(0410%6Z?pa*Ձ/F<襭Aπ|8m(((((((;?Ef/*/5yxZVZkzEHԋY*uը{qص_T t~ 1WFQf.z/T_J)\AޭUhc9aG j摄b/QqUVt54rT'sA5Msd2 PZ#Yu¦AޢAާP@ Y@vW 9HTQR)Qií`b֩6< QEd(SiBӪ%6BqN\f)1I%5:4EQSdp94LzzDf5!aޡaSݨ\换MWd:]>4t~]՗PIҸ#7fz'J'JuMT~M\p;)1VXdy;Q%9zUGqyU%"MR]r?JQ/ƨ5*zu625t_g2&QJVDZ58}O'UZo<RU|˦*'*g?-?">>և7z7zSS~WğBbsSײW OzQ[!EPEPEPEPEPEPEPk+?o fH6rˌB`g<(OS]jPާ5İ][~\7<>f"u?8dx;~\5+M֝=H/~ϲS-@UyfӤ8Y8:+4;:eݽ6v2IHbyDm \r1Oĺl:7}.ݤh,f Tr3(.H<4vkfn2-E,RGJCaǢ%UӥmƉrr;[?3 `JdpWik} 'ouuȺXU,%T'͡h: m?Jm?RKv-6I匌v !>Q߹w ˷$讲Dnm5Sk/fֶ6ܯ3J%ľI7[X-NľmnmAe%١*UZ6hu9 ߆4=_Z6O ]\<(g_* $R@<#=2: z+խ.-y, $7H2T1 v2Uɯg6m.9MuE:ʢ(?m\vkNwM=,JOK+9/fY;"Ց!P~$F14BXY7$Wz[r2BH=Q@Q@Q@Q@Q@Q@Q@Q@Q@OG[}G?nduP?|;<\ʼOzȡHۏJ)O[,2WqRgF<襭Hǀ:RRQEQEQX$NNu6}C"̓R^syhs7^HTpc㟘d8BQEQEQEg붷돳Oi,Vo)B r(.n"9.J"{B r6 H Ca(\grWRo-[]; u `bU`}ͧ8vg(v F^^ߴci%QuWG}+{W%Zj5gUZk&w@:ըj]Ec3;WT"W\UziU[_\Sr.mH12}tPGڧN-&[Ne: tLe: tUT*]9YNYzՔ]I'JzJxW\iQRz~L<H:bw"e!"QԋEk) JM%9zբ((EGފST1zm=S*-*dm"2*&晴HW~?Z r^^^NEYw\udI֬֫K T~nn?JvSجÜy;UUdJ'\ 0*zNFZhRV^[|dIE{5F^j2 jWdLFTQ5h:mQ#ZWyZ襯I\ׯ< $_ K]T`_MIcgXW*8jb>/cykv>eDEE,$FSlziĭ"Vg3״׋9csQ^TT[ԓP6KvN%u|䏕x'ڔ:\\Zi>mf;q&pOʼzV["QƓb;_6'c65]T TgLc`mv2@2k[-pd`r#A5bVԴ{{-B s32,˂00}kBK_ E,)m&%*O*>wF8$ZW"QƓb;_6'@3յ(t },"vLy#VԯPG}L nvI}]65]T TgLc`mv2d¡k:!|J[IJʏvу 9.|ML<#c kn.%irI#gbrI'I5-|+VuE(Ilpprs1o fn*3GNr6 v䁻 ]+ٹdlkji~w}:IkPe%-ĥWiGNh _ յd@J8b,['p+1|z6v|Ͼ[ENrɜ$v^jږoko{]h-fupP0=\65]T TgLc`mv2d¡k:!|J[IJʏvу 9ėWjuwfrF@\0xT^[Ԧo%ӴnT[ϯ=j[YD&"ŲwimOm79jΙ;77ۜ)ےdS][RTE.X9{56-ذbc%9f$Loy ? Ԓ¡k:!|J[IJʏvу 9Ck:Ȁ"qXNW8 99c mJ.m.-B4|38'^H=*Gu"XUh!r0 $L;Ճm῱UMFt6 ٹFNܐ7c&I-|*ĥg-'n9d W;JCk:Ȁ"qXNW8 99cͷ3 WU7C#'fs;r@݌ kjJHK/ffŻ L|#w䗗sIO!<ZZT<"-gYd/Ki1)UyQ0q'<`¿humgYR4 '9< rElo fn*3GNr6 v䁻2IkPe%-ĥWiGNhEn _ յd@J8b,['p+1fcʌlsy\ nH=Q@Q@Q@Q@Q@Q@Q@OG[}G?nduP83xs?[yUp>co7gM8tX~ OoT)QEQEQEQEQE^ ;{˛m o&Hyv.=*P͟*/5yzר|g`ljG R-F" HԋY32uQuQj-;#JJψu~"1W@.ޭsTc9Zz.m;UHu1SZ*}F-jT[s,AV k8K֬Zjd]4ZNa~VSڦ^sISBUº`sș;ԃQR oKR/9lUIEYRZB()}`44QAVecڛJNNixPi Bp*"p+Č /ֹm'Uߡv=kl W~;$\uUbNZNPWҭI*PWn^N;NԢuĮ"WrU3DR7CVfj(/ƨ񫒿^=k>RykVOj_"g26jDqT#jԭQUھ' *E-|r7?xkV)kS~>Rc TQ!eFur#VZyitgn4n5H_jfOj]Ŀ^^#?ʓ^xup#S׷WEZ>2i5( ( ( ( ( ( ( (>V^^M̭oF ( ( ( ( ( /+o%im{i,q! b"H1k`k3CѴyH$*RɌ2NcKu$S4ٴr$I#$Gᕕ@#8 5߮vlBZ9E% +(=ʑVg:^ 4!lFi$# iY7Clcm{MLJjw=XF`$>VS@6$ZWfuÏ V4xT[]H}BG#C wg,ñTr]E<7ڜ^ .B=7i@ W___Mkн<Hї3#X.NAh l$1>rf@ ?0  'Rn/th.}Bȳ.0 G-5|5ip՚0;3ŏknq{e>ꗦUXsaA&?)S$:gc>}a,L1 s^#b\'#x?Պ(f_v^$ ך/>w1O?J@jXf$^>eZݬ/ȅ[襭ڤEPEPEPEPEPEPEP͟*/5yxOwIJeEk5yZ֥^ԪkzEHgm2e*]ҨEW\u /EޯEjl[qڸ_CEUdm-!XS*gakHhQTѺV<+Jl㨋qsVc=UD=*l-NqVm6rMӥUNaHk ,)ȩT@zԫ޺$Okx"dnB* \ƗvmZ@˃̑xr M7ēG:#Yc ?xnGRO"q, p#e2PFNNI>tQEQEQEQEQEQEQEQEQEQEQEQEQEQEuёzo_.|FG_Qxq7iW׷ѿ|7o_JMTGKçLK[;`oT(((((((g`lxP?*/5yr-_0?_RU^"Ş6L*EQ k&vdSG@C+9e^9bD뒢44"WbY\y*EljŠQeMYCXC޹d-VW#s\SB4#8\M[bbȪqy(s\D[Cvh:qXymaOQAV=릜I6GXFjڬ a#q-S 檫sS+~U E=LڪUWTds%nƤ ګ+v5*c[F88P)p8VE04pkE#7PSApR%Mލޣ=h=i(ޚXcMƓ$8SHiҡȵYTd@R@L Db s1j#y1:z{֠r9YF;}jGcϵVs޸HH=괧Ԯj qTD sOk(ZSRsULjՉtRW\%~Nj0zՙ_3Z즆U={795bVUZ1FNJ{lja&=kDqcdژzV#c_dxI熿k5p@^yZ襮]O<kZi,mo,XF28%>vE6A$I565߳~x9gKuaeN{^^/ifp+C((((((([x{52 (((((-[wjjė][ڵrD9|c@2cSzƝ3bjzz91p^IJ,nh8a#,ᣯHKt y e^3 z%߆u-%_.:-E.x \1er*'!>y8o~mZ &v:Ry:%Ȥy. 9WV;o,77"L+nyU#Ʒksygc9if1?TFFR/L>C]j2 Vl#Yn`5AuuijŞdaRLnn 2EE1rMhdk9$~DCqʌ/r{)Ф/,R"LfR_QXdwv /?}^MN*tNyt{klMͤX~hbivycB ~ ( ( ( ( ( ( ( ( ( ( ( ( ( (=#?2:MV+ς:׃=7X丗ë FhѶăݯxD 399%sXMJ{^x(c~ K[|[襭AEPEPEPEPEPEPEP_;?Ef-_;?Ef,S\>#/*Ԋj%4kzPdj@jjU}RQHϟo,߳}xO={z|v?~EV QEQEQEQEQEQEQE52 keo{5QEQEQEQEQEYh1*MR]B+dK:o3A-Mn]Tд}V/,SX$~v饐+ٔ(\srPg:^ 4!lFi$# iY7ClcSL&hב=*L!TϜ. Nx3(Aq:C(mbŐ4y !JydHl:Qԅ#g:dOAoeXS,vw=.!}^UiF19If-}Q8aEQEQEQEQEQEQEQEQEQEQEQEQEQEQEzG?nduW˟uёzo@SCAr3r^1,2ܞώr Jo>N?2ۃK[|9WmZޭQEQEQEQEQEQEQE|ACT_6jkhLıeE?`7=l#%SRQ)YR)VSYvSdԊj ik6vAS#cWSYPe>r'穬؛?\}kq44q*oӜVtmϥ[99FOq蛑ڭFDh{ՄnjՔjIhҧGh܊ZzJ%qՔ~jmVЙ8U_TU2iަWSVRZ%ڽJE~}*Enk30Kj x~}*񎿍ldZǭ88U \~4cZ)OcޫonkS%ıڍ_w_i󋐔ZM?wRs.ja|wuegҘ_Zan:sdh8ein=*2zV2b ujoR3&UsJf;wnIJ.59곾iҷ \s: 9@(1#psU~=jIUvMH뚩#,#b`z)cҧ߭R]p%|ɪ63ަ1j"i}2kt'!iiƣcZ$rNCIɧQ#rMFM=2kDrM&' *E-|bBf' *E-tSjL>~_$M&h+\EXl=s Z9ܫp*P(((((((keo{5G%e-^7VfΑ?uCXrzzJn4-3En/x΍\Y?)##1 _9ƕſpsv`F@1<'\nfb%Œ)gz\<FވI֬ q :+`jYtn4.-խK<3$bxQtbe}Z`1Fp3Ԁ@0=EsoDW$VapāpOr8gWkqyqoj\$nnwd z+rO 1<(:12ۭYiop y8@ ѷ+u0b@y'9qdd>s]+ˋ~5kRa O3st8;# 'k_b֬DQ< u  :+pxOQk\:՘\1 ar823i^\[Z; jydܓzO 5ΌLvVl["\(žNp:<'.tmj.0n N@9gâŜFJ߸ ZԹH;S0#2I=F'F&Wػu6-.aO'8HZ:6ErNf Ho7 3|5~,7Wj֥AڞfqwpFAǢ$s+]՛v 0 -p z"'Z $ 7~SGF@0譃g5ѸҼV.v77C2̒xOQFщ.j̀;K|KSRâx΍\Y?)##1 _9ƕſpsv`F@1<'\nfb%Œ)gz\<FވI֬ q :+`jYtn4.-խK<3$bxQtbe}Z`1Fp3Ԁ@0=EsoDW$VapāpOr8gWkqyqoj\$nnwd z( ( ( ( ( ( (=#?2:MV+ς:׃=7X,{\\OoY <+:[y72?+[yUNuF{fw.2[3ox ãLK[xt)kv (((((((e`l@ת~пPMa=O H"x5=2`ij ij:"u4j%5 5Gd$J H B H f #lvHTTOJQ:5#o|5r'Gjˍ $3F6wqqڳb~rtjDi#r9Zuӽ[W$Iz6IyDzTǭLYl%꿿㚰_Ʒi^GzU%~J]1]W_j*E30 Zxn}*;[*ncJJ$U ;iC?V=E.jh.BϘ=:t%5VV~#qT~W\"29_NVke~:#]Ȥn4lQ1dƓQO&&H圆Q9FƴH䜆0Ri։s5L&cI $_ K__gI熿kyxFLDhcUu,d⦮9-O$\2i($I849k?Z9¿gWW9F 6r{^]|1^Mw ( ( ( ( ( ( ( (>V^$2đ<:,6e :GʰϨ>Z/On6yfgY;8kl͋ =N]B,ʗBKJW{xNr I(t7ʤ]rރs(@Qץg%otKf֭t G꤀B9 Xn%&tG*+~ &CêKݦ3a<+=S?۪HdGHS|HE-72AW[$#5,$+5<ZOiwKDQ% e"mI05pbL{K12Ajt2"(*H!kCy"$TPI,;um/(\.cרpWˆn`; n%HgIUATux`?_=t=VlѕH,JϜ* \+үUuf|MuqRM<8RNrI>6ZKy5=Zba-oxP&[th;$Ne 2;nzX_x^Kntt=VXƳ}Fg_92 m@1QEQEQEQEQEQEQEQEQEQEQEuёzo_.|FG_Qx@ß&]zU3I?w|8I:]K[Ճ|9`oLaEPEPEPEPEPEPEPߴ7;/Ef)W,; l硇~%SOgt$J H&SR)Tmp05 55 5G\$LFPRYuBE'b9q8s&R V~+6&m8ҍ`#QU߱YF?#|of7D^WL? S+~U(r|wvsR~UQ.uVO~*S+i&{R =jsOWVb]zSɪNžZT2p. ;gNy5PHM87<ժ8ť~TIB}kjٖU̙={A ji|u P&2q֘\v5nӭfHϞM&{Q֣v?c*#I |j6~3\Y:,3~U5$fYv15HGq5ZI=v㚭#]0Cdq* NvUW~p+~zHpqOL3U$ck&;Uv&b1ұ1R9!iӉ։44j2kDIBj2ij2yH䜄&M)4Z#li5?xkV)kb?_iI熿kb]><=p*=t*5֧~E74f|xO={x_'v^]|/ ( ( ( ( ( ( ( (>V^_D~WARK(.E #WkxkVa$m̢[H6}$0rd j+qu8\ъ2*Tr$`sl  BI7Iu=đy~\2rH=&;=0eăiʎqA>ъ2*Tr$`rElfΔ$~ė[IG ѝ$&;=0eăiʎq@0>ъ2*Tr$`sl  BI7Iu=đy~\2rH\uq-IJM<^I$bNI$I<棭59_ ф}̫-$HٜdTs wN)QTDeڤN@9$|s :+`6ht$$H<.@9&I59_ ф}̫-$HٜdTsEn wN)QTDeڤN@9$|sγ`lxgJIlK$~@159_ ф}̫-$HٜdTs wN)QTDeڤN@9$|s :+`6ht$$H<.@9&I59_ ф}̫-$HٜdTsEn wN)QTDeڤN@9$|sγ`lxgJIlK$~@1袊((((((:׃=7X> s^#b<[~iU[F@sbmN|Uꐍ* e9Kz]x >? K[<9`oZQEQEQEQEQEQEQE|Dv? F^N z%Q٫g-Di fθ2PjE55"ͣ%SQO&*j@k6Hzx5 p:S3UA9#,щeqɜW4QQ>V'(=*?cYѾ1VN(FugbI{J[JeoʱDj"zR+Yd$0+d\v |}j|SRfDpnyb\ b9#5jt8yZ{҉qګZ~yZ{Q{SY3I{c&{R!`d\s&;SLP|P"['5w-@ɞv5|KVh=f_5IjMTGqPsM/jkWi:֠it N6vbCqmȖVkj%'i$ۖۿh$(I @qy8* K3г7e-5{kKm6 uKhmt:$rJ1F>r7Z !.#t{|e vYIjOX~e݇ucoq5{8U1*S1 sFvlrFGI2O-[0i$Y^Fy a s=g'Jw!Yb8nv\摆"JNoqw5gn ;dsc9 4OM;P-wܚl_9|diM=+Z:mŤämtd ` n3$BXܗrjP\|B86ǃQWf<[gj:ji+9ݛbaFqiaȬ}?­_kXkc[Ďn2X"e 9˔ 1v㧻 ejK*;@w %Ac>"Q[-)caY eudn1CqMipZqHTInbDpe ¢yMueCeoj%mR%8bL (s7ɱ7aQs;ox'E&4q37!K+k=|=}F fGȒS|a;?([ڎiVH.Cʩ.1] qYR6:^"]E @CE+ǒO# Ms2ۭص6duIa|!d5H4wUȹHfHIeU%1^" DIQ ˁG\7v]8ܸ. +/b#gW )ܛ~SC*^W)Ir;Z69PDї_ KzψR4}Lش6!!.tA m W/Y{OiLWPy2嘳,ĬaWn-~{U 0:0ve (>wsokzo(+tMߕ 9dA_YKΈmMWVǒon((((((((> s^#b\'#x?Պ %PIBQQzaٷ\ⰚMϱ1襭W~eZݭ ( ( ( ( ( +/R^WKtۘ6u< ' 1s1kqeQ4o%?!I= 9#Ҽ[kOn :\}.8vx`<񎢷+K𖃤]xVKcj{s R!Y|N2@P̟O;Gf%?VacQ٫{H% F)³h& D <HMH B& B H CGT$L <MH f R)AP `Ք|)k9FD]$|ո@EX@EsN5cga exsJ4sY;Յq5(I823Y S, @,h+R+,qR8ELI |Vn$[P5P>i$KU]N٥֫oorFEV()r O!|U}( k?qP3FO[|gIzTlKLy9j$J_5Iߊ< $_ KZDchO#t*~MAPMzkSۧ$.M4 bܒ?ݴsWW,7ʰ#mC]+ <KY02TZ͍}+Kias< K1噄JO/fϵM{Z, i"@o-K6ݼF'a\5 hw67<j6mTn+GeN3U;H!Ԯ-7w|ܞ}3*I),{KDI˩3wRJVR(b3[#rꭎ@b#=3*J6;+ΈL>TlıH);PvEQڵ((((((h8Ag[{xoTZFgwrG]t$M7Ò}lF{0s±c>Xe>L a52#23^/جPyiu4Ϸv,#,hMx{ZuK}i 4'|&y>zD9|g;t6[jO&Dg}XVo6 *vĺ:Zޤɲ[q+3$|=S㵺RB]+G J-Q0ԶY藒 JeQ4c ;ጞ^ WMJomQe 0 @˔`9*%וuH=atRH`c4nk e<˝INۍ '[pX=wLwnv߱N3&DtH븒 U2O\c`185:/7ZsA :.|a X 1:p.$vk7ڵimXeIUaySc8sfH!2)Կet;RC.Z?>Fmn !XHݒ00CͥŨ]3fagtdžYBѭ5)u ]Lio4#As)fE _$!!A?0ٖokB-MdK4ȤlfYwL9#T܎AZlj5[s%$ҙ q*iVZݴi8fdYa8a#I{84K>S3'_,s@7hjx,4;4nL<\*"P8@X۟okVԡ.Oh0ێY3~U҅յ%K$]B%Q3b݋&>~BXFhFޝ^躩j=bXs~eJmÐ~omJ{}vkK). 4B,eM$]QQ]Vo܋&ldYMGCͥŨ]3fagj\wdf<?!'$ <1iwjU3dp񼭝ӘTc0U 1.0t-:|K]-g $Uzh[RѮK.gd(Z1\Tq(K9u&t|e(a_4 ؑɮK=ŝ5m6Xf Xf/PR1담o,t}GL}.O̱߽n 3VԵ {[{B Dm3+0(=(,AVO 4;H#Ƴy+v<ٹBׄt}:W+͎-$(ۍ69"+ޣs=NA1Ug$H¿ݜap5;{IǙ&O&j k;e8o0ưh1Gy2hmDYXFp۰嵫×:Fr*GC 0*|9Z *]YoAU#lqۻA⊄nN ua5o-]ȸq"o,͆FK>xo-lỞ;[h$!%rp< x7B~n۸lǖ#s0䕑W!cQ\h^iӬ!m-wHy(Bp:P`Uۙ(((((((((> s^#b\'#x?Պo?yWɝ9h7N0.Uωo+Y/?ARax'D/.E-nQ@Q@Q@Q@Q@Q@>ޙy凇Kh, # PW;Gf$__;Gf$szoAӁ8tEO CGDdJ =MD 8Hx54k6Hx5 P04ji4tdժi4tdT&1T OWfnsE$jJG9NqP(I;fIxY/|<#EdSI*e&I{YO\zT^&pĤ@MT`d/qޜq-b;;4䵴s3[Hd¶H<(䖾YY RLJUvT};pI [YD&"ŲwimOӷе,鮢Ȓݝ$@l#u{ #햓,є#o랪ppG4 3 WU7C#'fs;r@݌$xDZβ_bR'qݴ`NxϏIԦ"OKv~e֋'Rn/th.}Bȳ.0 G k_:)GLEd~Ӏۃ9xos0uSt7Q2=vno?+8S$ $ Ϩ_XK$S%O/}$ǤSisjio%XXč ?2Oq@ZT<"-gYd/Ki1)UyQ0q'<`¿humgYR4 '9< rk״+;+2gHͲ٢+VrKcVP{#5˔)%pQK($@¿humgYR4 '9< sao:d{~W#npnH~צ&5'!M-Qx^H=j^?vbyd7nqc[YY RLJUvT};pI [YD&"ŲwimO֦M\MndZ83pK~F !rCm῱UMFt6 ٹFNܐ7c&I-|*ĥׅŹ5/>I+ Fc (=shCm῱UMFt6 ٹFNܐ7c&I-|*ĥe 8pZW"QƓb;_6'c65]T TgLc`mv2lX#ĚjbIn-hʪBq YcL:39-Yʖ >ݙ FHВ¡k:!|J[IJʏvу 9Ck:Ȁ"qXNW8 99cshZżVͥ_GM=,jԑ(_~\:SmllJ K(m79jΙ;77ۜ)ےd%C"uB]i;s\6w7O%ߴLn8]8\zֆӍzqJ\Gf<*F]~C Ak_:)GLEd~Ӏۃ9xos0uSt7Q2=vno?+8S$ Ǣ ( ( ( ( ( ( (=#?2:MV+ς:׃=7XA/Îs6 -^D}ripOQ3ȃ襭8ǀ<8?mAQ@Q@Q@Q@Q@Q@Q@1~џP?3JѳWԳX=QLRm<x5RѴY(4j iԴtFDPO CGDdL <p5 184jni4tFdT ӁhsR#Uq7˫'שNk=[Y8Rc_sbZ%XJk'|*Kןֳ^:*Ɂׯe*b4VL=A/=ZY0:^Y$ZU'T<֤Y8noo/f"|ioUi2ߛsMi3q?7j|֚wUZL79TdFLV}Z90,21֡ix72dc?ic'yy)j&BL ^^IFϜFZ݇0BkT1IHM0%1IHZMZG<)4h&MZG4 a5iL&a5iҐL&i$sBM4MZ1ATc&ׁ?xkV)kJoO<5`_Hg: 8?4)٬sA8=)3Fi{ANڽo[֯y# ((((((((?QM>DdؑV6,cS0aW<jw7[j0<B u3LO~)ڼ]8ϕ*#'x}jH] 9"gy@G>l+78@ jPi\_-Ŋ7.θV##rv<U37"Mp^^}.mdh3/X}ݍ\iZj |9DC1]uzK:&.ݥAW;10hS5UXca籁 (a# Joj73{2W>bIrI-zƷi#WsiCsidV[cePq Csd>}igaSGk܎G$[A%ayoqI-2BUѷ9Y%6"ݤS}{u|)0^5wВ@2[\;c G4Mi1er?'O?V=jqEuG!i&v+"ņ FW@Dk {{yQXaX$}܄ljz%Y(4WSMJ)Ta~@^.5_Ck0^p-c+bh6;: 1`kGQwnm߱m3gM4CkI4fEN[ȑddF2CS@O /!VRH͌d OIE-}C￳?~t{3o<Ο6ԴֵVjB6&R>Wl rWtP'-GVԬRK^{"5LQɐ!<$s|F4$mPt #I̠ 6J盢:Yk۩m.{f̬:QAR5͒D|γ1yXvUPstPEPEPEPEPEPEPEPEP|FG_Q|OG[}G⟴y"o 3_J^! 2/8-ҽy^+`dwÇKK[Ճ|9`oVEPEPEPEPEPEPEP?o;Gf"]*?5y4q))ԳTSSbQN2$ D 8"PqO KGDdL 85<2`iiZ:#2`j 0jp5jxj2`jP-)jEHuDEPZx"cOI/rKךDMTw(0/y➳q?:A0(Q{0/y;U;ޏ;ޏf;Q{{{f94wo7NiwRǛ{֛ZL q<W0,<>iYz7U!\H[BբcS SKRHS44ije1KS SIH-L-HM4%1IHM4yL A4j0&M H甀i4IF2bHh!5F-iHͱ < $_ K_W۾yZ襦|] uE>hCќR{KO^^ 6_ iWjͅQL(((((((2g!-n[⸏9Ǖ2Xg9WA֭5q{ мVI dSbٿiik67ړQFH#\a LH~c3CdcyDCf˰c3 gVeBg[aDq`AObX$yP7v0Mps; p˯YxoN _O$ڬPZG%%Bs#IdQVϢ=WTVcE-V`0=zz 7Om+Jң1nOwrtewHp8wgZ^XieG";zI.i`!Et_ϯjVZڄSݲ9+ǁjɻn ǖpΙzLMGG sl@2^cpLy#@؞!; ܶnݻ9]qdևYka[K{ۆ^>4 Uk0€9+5)=g:ŶiZX[nwZH3rc2USqoC/mVK5 &kt1;Xe@ $8+d!F-$LA ;Sqr 卻u-V ?YX k&)cU]H^\:@EXI䵵2R- Z,l/#I83ΐ'rtyր+]υ}^MJ [H"6!ZdDuyUNXHj>inY:^XdF#V(Gd\`rtWaoOim%ecH? F mؼjw! Lvic\С (oEwv6x±Ƕ[C1oy2眫5($h:\\^%Ŭa6bH4]ՅmEdFwz?9l{~ ( ( ( ( ( ( ( ( ( ( ( ( (=#?2:MV+ς:׃=7Xf*\?iWYq xsv)kz|$ K[ՠ(((((((?h(v F^C^GCT6ZE!ME8u2Fih2ANFDӁRѬdJ <PjZ7ɁPOx̘pbטh UF\-y0]n9^a5WudLe$*њQs)74~ Iv!~nfBj3sOi&M&M0ePq4yM&# Lq4i HSi4i$c)M4Bi$a) M4Bi$c)4M&#HRiRU6h4̛ i4D0| $_ K_yZ襦C>+OAN!K"C;6}}-?{x'Qu񰢊)QEQEQEQEQEQEQExo!pi R[r|%<ݎЀyooCinHלg 0=} Ho i D$M&ƑUB%A9l\8zWZzN;RwR[}=G[ eTH20Pg յDT,#oٟ!wcR>Igug&|rak)4]8f'j4zxNk)5Ǹ]8AG>#_s+|v2Hu?}_;Ͻ}uր,[\mOv2UgsȻS l6h(Q֡_$M3"1ڸ*;sAV-.lKƏ7䷵hsKo4rfnq~bkR}>^OX< Hc&op$Z{K%Zj`9HH.|U.p0PĖ>doD|qyI'7}0 n,q՞%& ]pG>="K. KgҾoj|/F^[BY\H|8?@_]?+z IuX]Ziq #`J"& HHO']Ιԯ)Ib-mϗ @>8#qZ¿?!???Ͷ?'l?US֠י5ص"+|(UW'$ѓ_yGgyyϗ+ީaqkbجhaiL\R4b 0XHKdžM[VMnTc i#̌R6!r}jPOQI68RD,jI8@6ŧͪCd~Ͽww3޺G%ݽԗpOnGs"i rA0:+դj/:$qHZHZ"RC;NKN} {=FtddOhQZ:-mţh—1\:}Ky屌1!y!d-I{KyXn>R"xp"!*i-yrNc]I9 vtP<N='RKTO}>-$n8g{Z7YY`ppASpA!Դ{]lk{]+RӥeHeI4ETq<IJn{t]oy_EPEPEPEPEPEPEPEPEPEP|FG_Q|OG[}G7ү+?h_^/+9nR>JT0`4aK[c>]Z߭ ( ( ( ( ( ( (>`*?5yz%Q٫) @8:SR)1)AhiASJƉfiA 4j iAhHPj ih3&82`i Ӂh3& N E2]4yѬfL74ScU2`Ի-ԹbwP F bsFj<њV+u,њ,?hK7Q` f 3Aj4a9n-L&,CԙHZuZOV3sZZM4icSKRM&#)LRi SIT)4i T.i M&#Lq4i)3THM!5V2r QHM36HM%36 !A,+<R5?xkV)iϊ2:R暿t})k;i.h%Xw=j 0{Zj?٫xi*$&QE2B((((((( tSvM4IDUHߒ;Hk$uXcItKhY`%Ws:r~bGs_Bz|+;HXi"D79 䚧ė[{5CG)?HЖ+-;PH?ZY4alIU%Le,Bq:\ڤZ}n1#q>0̼zAo-4x$qh\i֫J 1MKc]_cMDϩAiSXIG467@3-?FOZr!xB3ᰙƅuJI,x죽x UWD*eV mNi[)dI`2 .cݻ$ޝ$Z֥-IVh,JPT*8Pl/i;y<2wƛ8Qz jo-SiWu+Ann"E| rkql&Cw+%2+1M  TX񶕦x]U5F mg@#U(X^iYOiu7\4eMeIχy2+Kӵ.ex]0 NQY6uʒdXмIl촭U5{MaO0UzU[.[Gfdor|vB06cWAb'`Qb_ _ŮjH_6m+HcDQC7ʤ$ᒣ$ca'ty>#j?nΞtiDfӵ8 )5'C;˵V47(ܱ8Ğ׼!?u5khk/cl`$`t$_}KMFE'Z)ghU &?>n6xϗIԠ<}smLn ZKzzD}O:pIIg~L{K12AjM;MR{G[q,U8'mΣRSKXinci$" csggh5-ldu89gH8c"3O'Rr{]|,CH=cRYیvK'#/O˹Jn#zjiR{K$ŋmgX)ep;PejĄ,^'KM54˛=똚9]5A$rIԚO6hp!o-UH*K3uu22FFOMC r巊Fݴ=kr1b x?'Cw+o|]WpޘO 16?b  Ȧ1/4,'3@#*yƫNJo?X]vl6 尊@(((((((> s^#b\'#x?ՊwiWb_yU8Ⳗ#>R`c]Zޭ ( ( ( ( ( ( (>` F^A^GC~hCi)hR3NI4PiX ;4Lp4j<ӳJƊCQNMQfTD 8dӁ Sce2`isQnncE2PiwTAsScU2\(jV-L5.j-ԻXPu.uB]n{њV9&7Ty4X97Q{BBԙQ 7SwS Qu!4Ԅb&M!jHM47u;'i4i HRi HMRFNbM!4ӱfM&jd)43Iv3r J3Ifmi(%;RFi*l(p(WyZ襯+<R$~RB@s4}-?{xߪ (!EPEPEPEPEPEPEP6Q!6#HU8>s :fTV-ĥ 2\̤%y%^XB̷heΨ|`#>{;ɒ;kkK<\{lf`IrI{H\X:s< $T@dd2 U<!\0, W_H"x-Fi F,̅|]$m:;F-D*T dF]~^++E:}ost$+8bhq@2k> }o|0M)o2Sb%"y*J&ҭ-ŭ:KF]Rd #@bmmw`{RnҵXt6cl r=ῴ.mOo%Yہ@Qs&6uվpaZClcn.`M%V:uց|A+H^9>lc2Xsf65NK e6ۜj,.olWwsZm &%"ɳV1]ŷR"Hc+#9fݾemrʜ+`SO\ e9ogtwm!Y & /MV[HZ4%GBB!wsNUq BgrjQkw$!ATÇD#f7cfXazM±rVH :p̹ hj^(4ťxZ( D"?8RAkX$O5zEi/e* -qЀ8׌GxnI[ 1ݭnqu/BH۱zK¤EΠZ1/VVUicJ 3Q y/&^GԎ4l @1배υFROt%1c2a_6e>_2؛?ꞸܷEͶ[H/X? o#P!͗Ry])%|=^[%ܷs[!#dTb>|nQW{qrMJitkm-?"k`,jb%^a|>ͨX]K[c? K[!EPEPEPEPEPEPEPw=?Qk?h(zѲ׏KIEBE-斛Kq٥4J&?4RH!34bԇ GPjlhH 85F .iXH\@ӁcE2LӃT@Ӂ j]4cE2]@4R.PPԬR&hGXr]nQ%FpѺGX\䙣57Q`7Qby-L!4KfsM!4IԄI;M4LNnBHM43Uc7 &h!4܃4r QHM;ع)p QERQE2B' *E-|A_oI熿k>#3M]fsFhϠ?f~(O^^3nZp(QEQEQEQEQEQEQE%x~A*- Xm_.8!ly^Y+O_]@/b7T,f(Ae 7=#Ö*C7|Y0a#ppy :ݼF*,. jBx;w4tpv*gu-+~p@F>L9,=. VKND&(C2~s啈5;ɥ>}ԛdv.ǩ ğBMh[IٿGڼ3fWnμj,=RZR)iyu"%R2ZbD\'.Yh<'^L(L6ܰtw0fM͏)~3VKjWO[8UFcseGk-e4AknQVeKU 8ltހ:K\Z+{NK>7` ]YbL(pNKrڔV=B PF0P8#1,r^jږoko{]h-fupP0=jxoOluX_Z%dGBOG˂vݭLJd^´-$FTld.I){{cI+k+]F/$rv9f>Rh (((((((O׵Xiz&}鑣Ikjo6STq+ k(A$J.*. hgA}>3;vmwgugx]s4uM4Q?k#oB<]pOU?]7_kK VO!^-[\tM/!^,[\tM}EH/Źw!~,[\tM}E.D5g!,[\tM(gV?]7_iQGCO|Y k?.Q2!+_\lM}s5jvG,DžgoW?[7_hQGAڞG}s4QS>.oxms4}s5]F[}S[lgd]$LW$d ԣ١,eEoW?[7_hQGCN;ř𶹌ٿ? _ fk (h_]|_oW?[7G!+_\lM}E*vGŧ,Džμĺo&Эo&ѢBO#CGx]s4]s5EY!???]7G /W\tM}xAѮTwWI8#>ƴ x-h%@WR2#4X\oЫo&A_Br,.f|E/U?]7I/T?]7_oQL\?Ms4 5MP>!*k.OЩo&ޢ 0S\tMxMs5OЩo&A *k._Щo&ߢ> *k._Щo&ߢÔq FwV#IԵW٧iw\&xZC0(r$-kyK="G#(|9C6#sGFnkC\ϫ6wc5yĸC偟`  xMwoi<עLȠE8c۸7'?Zjx+]u[;-"Y^eU$i(''g,j CZ#.Hm>Y@9aw&h4Z;Z_|/lēF[Ϛ< tVr:(񾏪j77io F~n]]N BG~9lƚa-$7!?AUøUb9i]Vabv ^Ї%ԆAŸZ4K:7Đ$NI)C$Xљ2X ү+BHԟOk+YnYH[2'#;A͢XgԠ4)bT$#]QRp\n|Wb#E7bH3(9a <5֮RG.dr3I:?)HfV;x_wSӼQaỀ:}֟tmV(f"\ J0Pvc8l]M=-j"G/9yL=_E+wJv]< faW I!b::}i.gXc'>Tv Tfsuvy3q/!'| -u MBQ;I6USd3h]9̬A``m+ݽk  퍕Q]L34$o #GTnaji FHHXD.p ª`N08s^,L3kZdxo.eR0\Jq6-]^[a)66v3(][owN@9,Yң+|(IC*Fn̰5_ Ťx]/n"ՓP T)f'~ҥpIȠα^s> UmQ 3,WlZOgԼ%ԞLn,!)(li1p/$,Vo21H_0(v,MPJ8P}gJK_($Y <!7q2;~QFMv/5-NBU1_<8'U0J x^4[.K;M qdp$(qs .eG;v'?cqj76wV3&vdppA]&-=;K.(oޏ!>̓#nrK F%g_O}Gn Ԯ s^#bG>]Zެ?RhHQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQQkYc3+aV$)#{>A<7V\[J6 dGhJ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (<+hɉy۷#' tv fi'Lkn䒲HM$;.ڛdM̭oFD5 9mfcyI$|8c^ ǧؽ̫:̌*nAl#YHo7bךnFh4JcQ+*5W״;Enj6!A#ʼndp&"1':I|es-ƛ#̱k"2I;,dE叛#P~M!%vVf+X*;rI$NNbX=x4n >Rlp!C!MHX^2eZ]e +khLK%rZ   џVz'--n$[2y .SH >lM+]7M'w+yxسm"䌩 t$ s$-di|CmmP*`FpzuȥV;S&ұD'{UU 65\/4Tu> $39 cbt kvy$-Gw,6m[Ekq;)er$b3|-oۋl;U"wDt t nm+J.DvcjCr$,VI~#.q ?KԦoGFQVe pkrC\?ۛUhHHXoHbh3,>YXn^;wVZ mfL xAl0HX#s0 \*Kn&ZfL,ĢnSi |Qsi*ӭ C ZBcA?\AxPKh9K1%c0@VsVԦuRcYn$c(gb qkQ<]xk'ZhL07;UO'$VjϪ4y6nng fcwo KOVz4z\pZݭ26JOq{/oOCn QInX[|I мwu+ph)X54d :PK-V_>dg1lgs(IkgqyTPI ;VR3;pg%Ɉ%۵wavF A!K  \\iKI{PeS6X*GAk簏O2K@n0-o! Sh͎nWFT0`8cJ&ҭ-ŭ:KF]Rd #@bmmwc./fYl_QWV{toԮ"U?GI#܌3eJz((((((((> s^#b\'#x?ՊoiW׷~wx5)l}<9`oX>ß K[ա!EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP}*Z :Gx,7$w dͳ7L6N@ uFU*}w(((((((((((((((((((((((((((((ihׅSPA$1lk "@<yg{{_ElgڢgUAl#g{ i i]y Y+$H9 wo ̤iY!M+1!ql/p;{<ZKoq9#<@OmdItVDqR!e1ڶj'%T|xSůom݉5ռo$MpoAg?6h5mKP.`M3:¸ Œ(ҮZ$}b}FU[S&amP_jҭB֖&U;~pbNmsSIGe7,rGZφXCt~ H|~OjmJKK.{$Db*CSZ~Dco}BhI9e)8w]NTd_ݎ3 }i\KNKf9QCbU򃝤Qg%^ >/e`HQn/WArojR\Z\>v"%3PB@jS{1i2;`2Ǔ*r=[RKKPM>g-'n9d W;JEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEuёzo_.|FG_Qx37?iW#o?iW[|"?m xov)kz$(((((((((((((((((((((((((((((((((((((((((((((((((((((-\#Q?I/PI3]b`B$*?@'Wϵ/۵mnEޞQ r 0 y)Nysmݺ,DweHȝNyCӌBkjY5Fc.izlڵBѡ(<8 pNIi/KLV1y0n*nd$UaG_G ?mo8펟i{"Ѻ,%r3,r.zԗƃxNZY1]*ݶdev 27$||G 3ñâZWZޛnvqWUwXZ6,;g{kk;B evSddn7c?"|Ca⤶:sZOn9UC&/|̍' ʠas~1|JFYKuY3"?u@o9d}FtLD gr(]Fll?%GѸd wy 6dH ~f* V X/a =Z-D˭wmNq*yÍ'#4 _ZZBo:I$chT0!srwiW"ne*$I4Vs_ivА HG9>S k2l]By3H;A,J[;F/C[چO;uU,y]Xb_P ;3Q˺7Ԑ#J/| .?t5Yb*e Sh e;Q:얗vw]2gVI8H G O[Ӥ]_U֮[q[?e8dVIW.)8pqbP6^[Y0]>+ ';q*Tuosos풦B@uVtu}܌ â(((((((((((((((> s^#b\'#x?Պ#ox^"zW?9o?JN孏<"?m⨼_{h# 1(HUTP0>6via/T.SZ+M_CN _M]CN 9cj+_uKP|kia/G2 jQ_kyN Coӭo*d>բ)40xoTs)_&.[U9|kuB_*d>S*+5F_*>6a')1_Tr+_F}B_*|kuKSAϴ诊6ni?a/N4k09O诋G|]CF _M|]CF 9rh_kuKRӮ_*aXN?Z9/I OOk0h5n?i3a p񷋱#>!/G2)w&/ %4k0)w&.[U/&~/[U9O(ƞ.[&^-Z9rfQ_ͬ|UN-`lk{KQy5񇋻xZ\9E񇋳#F OL[U %CgٔWo:7/w>TsgtWk/V,NU=ӥ!r+wFTKVzxZ/Aϰ诏W+=|M|U4Ż6Ts3J+K¢<x?-vfֱ_T{Dͭ|U 96_*ha_xmk)=|M|U#>?]O|UŞ+/KڠgW #>T^,]kT{T͟_Q_Yp|K|U/%+Z=F}E|} o?gֿ>_*oO̺QPr3+w6U"Ň]dUgtWx8Y_JWC.~T{T͟]_"x5?%ֿ>_*jٳ+a_?րR3k'/GAϮhϊR?'ֿ>_*JV:Z=fϯ(NJW]k?,^q .T{d͟^_ xſ2?/GAϯO[C.T^-Z=Cٳ+gk_/G%,Z=fϯ诐?+__Iu gWKIP|WĺQr3+J[]gnG~ڠgWR+J#B2U$>>\k#75אTSKdIH>٢ 51E|W=V<}0#8>l}}(z+i!S)$6o2¥|V` =c̝@w=u ݹ#{} [+l.㺅`i8!#pT=h oiv3Xh$d W5-N}VgP [2O+'OATSl: HFB،wD`pxHomX-[\%Ȍt (3%̍R/H oIcuth[1"[ŮXj?T46lʍCfw8  \]odi-BQsf3&[fS*9=]R\di]T`w&,,r1߿ڌo⋛m-- ^6$F0B/,0d"X[B҅J'dKU]U,f1X/rMJ񮳢=|-of;#g$22UL b":9{u<Trţ)Eq&E;S{nTNjnތUu](wNDƇh* S,,i1ȑMmm:m.^ʕ>l.d)HҢK33&g;?v1!sXe[,w5_Ebxs2l6G_ZzͅmO.-Zr=+ H͜g8Vt{I-5 ;I@ ;g1$+K7 ,ĐBгb2QBqO#O:Xw[rF4-4 F+5yicwuʳMy53D˱I ㊓XEαo4RZZ@WXCďS"e=.K}W:+&兓 ;#j Y0aIq`hp9=y"(W1N(>Q %o+=LK[P3%.`(y'#TV`? <8T0.'ӥXhq֓ri{Ր:R*r1ڟzS8^4c)A`S5t[ym=>RR½T3(19 D|wہWJ(LR(Q늶hS!I-̳`sR>&AW!EzqX:gpԛI_e'v)6Q`ۚ6.X#,\\P3$O&EBmקfTR|[]@Oғax*6F-ͪD{2ʖ >9bfDO" 2ɨ[3OMPQKVw`VAAEEC;wzշpT|tP]Aҷ,89QܭVV稔Q!=*D08Lj9?ZqV 4I]fnOgNDd8'3xVPɵ#MIцϽM%J h*||oBsS-<[s)@x߆fNOaKedXCjO84tۼ)kTu$g.nigb89LeDsgs[JK5(܏ZYr0kh9`TF5'*18X̼G@PcQe?<`va8l8,A)*ȌP< ?0>ȡ66ч*1X&-6ZN5"#nMч)l>ʻ} (Sޏh>C+쑄øR t;}+Xq{b-c#9vVmU,I2qVDH;:"2dAe1-rO8O#'8=^Ӂh\cXXgzְLm$ʗaʌ#ՍIJedSk\Bhϭ0 {^Ӹr S>A9 ?kQBNGHJP>qr?Ee}+T#4,c1Q9f dd֐DlҘG;)-  {h^# w)(fP(H5`Mx/݁j#SGoZuү5;& (H0m["#wQ774SPgi9v44lW *A@ڽU7v^֗W$E,`Q;ߚ4 FB_ݳF,5 AXꚄZllw)a+ofa Wv.*Y\b&l@%9I r3LGUgSC}z4u0Arp B|,|#a0]I{ټ*Ŀfy c#i-~S+_j.QZI_iUg栗T'ϖIy3勌8bsg t[GɕDMp(B3b<7*R閟c\J$,%\Ȑ>~Xxʑp7p.$A=Mo0 :7)nv cP!.]\9v w$xOIdY[;ۥ" g uIR-m3$7 ilѺ`%Sal}v^۩ʉ6ݹoSR^jږoko{]\h-fupQ}D~![j6Gc@a(E+ÕPP0n<~MMFI0I6ؑTBCapV?}?h;xӊ>oκ5lLr7Td `}/kˬݼ$(bJ]BCEƚpj2FGn.c,C+md Jnn>k cFkK"oBxpXr|pCz][^~6AZ<γŘ2N%hrz<><;*Pݹ0 pqn}ev/ayogmy5n< .ӆdž x^۹ʉ7ݹoSW$Ե-^IБd;Gu3I+p2NI 6bihQ`PX.nrV`?_q bi}:WqYj7E(V#]rC=z e{='Q_hZ~7C @ 8)\B:76"Z4 ^kfhK#0I+YmLӬK~ 1Vm[FT<1}Rl63vq)1O{g_\}ϻ_'|;g7ܞ~cjX}?QKy^&;2T~WN5;{wF;K!x㷒DgPVU%v@8dGHS|HE-72"}O?>C\;X̤dt*{;c8(2OMUlR+@a>AOGom$V>kz-;['ˮ}oط\961/W>#.6&2lnJ *Oa@tH``V7ˑu2% XDzΝѮ:t]ཽ]wR\"(|QC(>gpw6p.!I@Wrq3Uῼ?w<_f RuoyPKxvLj1[2,qM Jv1ݱ[8\}EeРl-+mͺGof%wC:I4P`&c2F[~pd_2U ːrB''^ΗEd6tV`c_]qg,KiWZޥm=﹒FN\9br}OeפxGNlͣq, <$l/'c/ʡݝj/+X<5yXiL@DhqV-/]zRmR-oRMBd-8׎>UҫǫjPsiqjiYD,?*Ga@Ʋ9j: 6bi,s!IX  ʮXDc%Οc Xo0K޻F ;a[ܭTfy-Y cY @ G\uF}FUws än;s9h9u؈5+[;iZ\ l.O &|dNދ9-n4K?2; ?̍kVԡ.Oh0ێY3~Uҋ[R-mu -esLΰp @ٵz5 H,ytt e)!P0!}ڞ ml-}0Uqu^{ E" <7;5mJ;.{tf`ӫsc9Dz6v|Ͼ[ENrɜ$vN(((((((g|E_PAur&@l sxePѽ)ԮRBqvSvp1UWv52ƙ8R VPP1\jz3 }R6)P_*e>[򹧹އ!ؤg9)Zj N.9Pr*ƾ9 />qد1 zzT!P AE1 ̚yE(?|) zEج 2)i>ت9jGrMXEĜv0jouǂ~|sE \S }i7>'QpBy.jJGE$_'hR93K"I,ãLY$ɨwy2cC1%%FUWs][Mu-f>uB-+!A8>⽸ysC $~%{+\@t^O/d">2ٰ\xn^G8"eGXR4EtQ32[d&5 2Vi:X=f]-媳R[ܩEhk״+;+2gHͲݙ FHаX..K#m8CnzP=oJ'ӾcIͅV3 tV]RP{IDSZܙDX]ôL|LJIMIS|jT^9d@;Z- Xo"үhfKw(c#6 gj4 U12H.K$G͏b>CNjA|u\%ťw%sFRJ1㝧qg.DԖ D\,( Xr{UxPTZlI-űUT(\!qV=şo3+[ʓf7}-o;9w=ezѯmWRN2lme\@fPŀ$MWeџU1-m'Td2@4It:\-!m321N@tvF7=UڽE}sI)o.W9c+pvyjR}rVnmkM1)̓<*7ޅvnwd0/4,'3@#*yƤ:N ~}سtgK yl ʤ,S@hiGKXc>Sbq,xFcMϵ1缸|i|*? }uzgZиG-,7cF(ĭ]6yLahcDti< /oDP1,7$Rո[>5[{m2jjP2tZ2f$RԪoWEzdKCYA bGk6ԣ%d%$#e*{[ź[4w 2LHózݐgu-Ui} *;Q=cREk٭ZUn q$p$z @xFI<+\Z_{fM4 +%yi댨eе~Whc[}8Ǘϝ?xzՈt/IysåjtgKy I#cRǟCDzWEy}9o=HRFWu rkR+Xz4y k&O$F b rT^wZ燵/^om"\MnE;D 0Ƿ|P`i=7O] ۴FwStp)!''ylg=\$4¡B¿*1W^faeΦ m-乕'p[bp2paH5߮vlBZ9E% +(=ʑP:*H`AFrURxO` =6>qm%\I 4l-1JsۚˢGSisjio%XXč ?2OqWf 9q[ZYc_K}sho>4QZ12}N  @[a'>lft-|{{hߓ2ӞFc ,l8f>5>YթPs+#+d#0{0zۃ o>C)FZ6dY6?'fg[z@㱵h%g ~48\2!BF?ZzBdzu1μXpX5r+A8h?>UB@Z'M#RMZQ]ܜ{)qmǠqJcL@Y6C^ug88x+ԍD@v+Ah"B1H);H8F}ɨ~T)2;*4X BzqrmEFF֥V:glHqH{L«wR"}2AWH I!Qsޣ208⑛''*L:Sr4xco M?5"P܂Ozqǚ_ܟjCoZZv*;aOvS>i;ی ᐟ$Mj"O ʛgRQSPy.vu?Xtڅ,PnEب"%Pp]<ǹ{5KG؞W[Ev.mbXn[ ѢHE6["bF60P x)%~$Z I]2{e`g4q:[{1?\ݞg]2c:OwksnR0Z̬KM@o3Ǣ=?j" ƹ ո]‚r)`B7(?_EӴo+y󛉤NVEl+6K1Rk6, 8%{[rFeebXl}qI7ozϕwrxVIgl;N#Q@ tPiCynpLo.kgJ0-m-}Ķڿ]_lXa4).:`>nNF%'H5/?[{țmX.tA.P J'VMV.s/K4bn2oګ6:>y\5VRQ12;fpSxܵC}Nfo9*y䅭ĥX6"L>7I,'=f-b,kn @FsFs݉9l^MgXt 3nMEAob/3@4U/v[hQ~CsXclr|`q+$3}]i Hnnkgk"9Si_ ˻+U:Hw{--!i|rv6(qT}Ooy?{~V>oۻ(©Ult IRLàrA8;22r)6z$p} ηwY@pJ7Ez&MB5)yzח1[鶐n0u RxD:-k{B 21 i6mm,_Q;yҼ͐OxqLw5m|Ʒ 8!e2R3'ElxcP5=mdv1# YApqҵ-W V5|\ FqKc: tPI[tX"Y^sck(Re#h2p23W-Cl[LҮߴ}yFָ7Fp8(g$BLbbS?Ɠp(W lc\wRuXmn[DQ0YS\nBl|0曧[kOat9Fm|?U(ҾrWPxiZ5 It"Fȉ B7ˀ@Q@Q@Q@Q@Q@J2:P3-"y1}6Ƿ;M9_6/P}k/}s;r@ADxos3kؾ>.aCr2>SN8"wmc=c\Y˯@MUA*D 1 slfXIiV9Q|S@5 ]MIbdȠRNM*D23֕QDsM'q8jbFWf=1@1zF)}U(Iɥ@>^i9[q#Ks JSy_Ɨjp}tޣZW5'X,jRԿ ^qUųn9ڀysSpPO"8'SLQ/>`&yqiR_0T~1a428!(9)z Sr[LS7g4qj&=֔푌#O[ ژ ^) 0xr#HzncRl҈ ޡ,ǦE}MrJu*DSZO=n)[!"5pYcTbyʬG|.jٶSKrU xH֢kt3irf(̽8f6r!2 f]>G#haQ &="X?^Ms[c< |0,B8P?ڡ :e2%vQ9lw&"j>Ɵ0|(b^۟z lf{9S~\0wXUܣar?tRɓ)&FĚPH*0ӾԛqB7yݷB~ab;s=)n8+i#rF9eƒž>SU C;{Hb& cJJ m8i4VY,t\vLSV fҥ}8I,sE2'R2ہQqX:R(N/x>;VѬv#HU]c;d]Gi`OIA|~|V|p7r*д}3ִ&t2O8iF?vyWⰵ)?ߋiI0j'O1d;^8>IMAbb6_61YP]wcFbOM50 MB<'5dD8W>4_r¥ <;(?\:shYL)j x%d:1"%|:2zԿB ˀ18*&L+'HϘR0ڹ15%r ciPDd9`axdS5Z5sH"/N݃cҬF*꣐iVba3sGOafsXRUط rE9FASGuĞ[wR#\ʻ$1)#.A;zv IȯG'wұ0N=' Z.>)p*V{y9<~;n 1؁V#Y͞R\E8f+n#oA8ӭ `8f34X)adQqPrȠ.} '*iخiJFi,O~H8sbu/QۜSdv@zqR|2џ^$Ai<o{iAf!FY䎴<Osc%2Z^s;`FkZʼnph9%QXJxFa^Y]RKKiZ4TGo$A  [C Bjfa+z.8+d{D@n!ݙ <:?kJ)u;y Z.+5j~ 1kk m^mK|?;@ϽI+wdqWNgؚagDg#OGS4^sWȄ4+l?G=Et?K4?>he#B] 䚟Pصe GWU*d4s4W{qS}}h4d`iBAN? U gÖVSmpY@P:]@^MI\hKbj6F+3 D>S$ro-}|,cq=Ew7~ kUMc n0;}:}kεu=d?VEwXv0LRC!e>ۭ 4]N34Vi Jԅu$Qp9J+<lb ~Q"+:R'rxrKr[ƢI?Zi9VH?ZFcZ>?'Q9Ej,vqghBfm?:w`N-xjϣKHÎ)SǨ3RlG{{l2X#;Eu H$7QKVB285i<)o'4 zyb(XhCjz[nSpPh%{oΓJ B=( ~}utˑ+Lxi##3EC?-mqxc6wc)5Y_^^R+]bK "RŒ1=/R%Fao>Szw[.\OIEU5<|G3?lUĉ(iBz*svO`~KVK2DagFG[?ƫnŗ6)9Ύd>Vvm{yJی|Bpǻs}O~ɿ|OxmWYع,yȣ9 uˊ4%[ yN p?'+ѢuJWykN-zU˥/Xu=6יȋK_!'yOIb]B._ED]o)ƚ-Wb BbO*8煎0(p9Q{FSpbzi>v?OҽRX7>">vJ|FAm_y\Ȟ[qk'B^ϴ{* "2 S{/,B{gvTb23(=(r_ C~/_qgU95ܜƺ֖ɕM ĶJTuP hՆdiꑟRSPT 9n>ms5v֟Ԍq4Jq84t:0ՎR$ӞqfSN)ۙJZD F~\}io' Gwh14<zrE4wi!ۀBLb 22}#?uI*XpPr|g:Ӱ68P#b9ahImU~Kqւ+$fh~I lOnFbK~9+nS^mJ$,P?w78T7*3 cԡLF{c&ԁ3~*aW8_JEdBK*89sJ9D\6ɩ|pkE#lT`J?U4?f#'Uʼn[u|xU&?ڦk]/B{j\|סHlNT4zo6?(^Әtz\Ed5[N;Zٖ$`^cS?y?njTM=y_&#TN8'ٓS.![-UҤ#7̻ ,_0S$5Y8V?(:Erp}K̪:1Ӆ%q4͵PTOŴ}*RA.EOΚڝ'MޥGCH,ؒ1Ms̟9?WKc"uQnW@>~JVv˖ M"B=z-qPRl>I֛` 9 Pi }C!rr h ci[9SD1EG4Xeb".;+72 ,NM}jٟYњA4[ 3=cЮ2vXpuA?)pOb*bFr?ݥBaI >aʊfR 0*bƸ,O3 A'<3ɥvf l@4G9ϱ $*rTCOO@~玿J[Qؤ3dnV#ڔG@}}j?) , XuץOݨ2џ֫bHpZ15Vd_FˆB-DZJ{,g< !\RXL pDG@3œ:p([\q.tU9^O+h}E{HG#ZIrGjG< M(p1S`d8 yʈ0{giEǕsua,/cRR/jjS&d\iw<kZ# 3 vto5j$Km3Fe'֎=XFvT.h 󴵹<v`<B&v` qǰ!^Y),v2{i CaTZE/[0!R?g)_-FX0IaZB11S<ØUٳQ治-I9cSWi:qHĞErQ8G-nOR͏Q؎Xų~UI?qP)m29$?J1 ֩:BHu=4% 74h'j)զ ('SEmX8xW"\PV7ŒuŒ⦚j0BHˊRAgikw0jskjf<9CC/-.W_V7p(;rNlh^4mEy5;3 6]0ǵ5:rLǣ~5O iOgc$;%Q$k# q[# hV:DupCr<99l3&T'ǟۍl5ij $!\0|1ڡ99ҙ}jvk2b.q<zŖW|o:E iN(ԛcGr3s. SJق3 Q85rG!=Jq֤)5D?⟃i tҝ6"ÿNqNfcZjMlKI[e, +Ng9"vA!ޣ{.id%d/?7tFx|:|XKKau(.U09?Ei53XiH~3_v4gHzdSZyHf7KVyrybS,AIO¥2'oJcO_jf֥(1i2[LLf$R`;69 mLFhK *ΐ?/j(4@RSF_Kul.ѷaJx}.TQz?۬ 9wܺo)j|=)’sMrWGt?W#+b&`({FV# .Ğ)qLRzp~4ʬ|נo)w@ێJрιS֦)a֕+yڜ^BqVV4^YSsSA(皙"i>42֞qSU%r$rqD1,摎>gHnwlH }*a@{oqեK HX TcIk_qgPӿ g34420/"C/m14CnYOY̎i`zWH<y.-?i?fqs@ |TuhmCcVc扂 8yf<,~uЯo\YݟWdfG5:'>+뷏~k|WTouloO{L5->]?2)0bkq|'}kOw"iv Ԁ{Z|H=l)}?<3r6) EDŶ=/l4Z "OjakFOlJU EZ[\G?-)?؟]̠t[)f#oIVځ.M(9?KvBexiҡ5[ߞxv?F&<3tA}]w3R źVFoڬ/-J}?BܪtRy0jOtgu|)fn;4š=-i84֖:`t Ry ^o jl: MD|%İ\C= p/鵿¡^Rts#V?U??JIȖl4%#s'G dn[o?V5>}G?J*3;gi5a܏vs4d0kpWS&&WR-i{9<{GA}A"8įki|:=eND/i"= N$>=+g [<>4vO[S&%_0eZzH߁?sZMLq_OT;ho1s{ò[Ԩ%NU2}KQ&<5x?奡5<{(HI,zn5zMGLoY.$1v9 &M&\c~kAM7d]Y/i{920rWs U^T ZJGbeᛶcM=[]j%m_spTT~ꑟCֺ/tͿV_j;SXw2usО+o:`7Nv& W>-|3?S.F:JklnB[wǝk}Q/_:>]1@ c#|3{[>?&|/vK0?&AǍAߕ>6to5MOuT?%ůf#AÝ2^ܔbqQ AZOV?V&Ԏz&BFJ hyE<+{t-Ƶ/\,sA ^ʹ-k`Esu@|(&{xE"̟rM+j\/~,eԙ7F"?:ϞvxqldRKv{igZ[jɔԝuqW6s.O'HXXOoj;3s?ko".o|rԩfƅ +qMe(>#d$'ӞvBʎح|1~+-??L[P^[LW4LQ*mf#g$q+B:^&ϩviKҘ2|4rKh0eWb{f]դ}~d۟ږ_/}̻M);F(J^ds|Ymgu|Wq֩S\Fc>p999Tkz_ ?yyh~vi]dlٳ׸(»gc?3E8{P.lc?O+Cg/$y}Up138O Hυ E;La`1v5#]I1&>y1Qiroa cwya;${@!BZi+5=w?0ZD_MծtKg,l-(?)dsKE}??g?E ǡ6n˰ZZMcQ{Iķ,ePWnߓqg|#~s&aZ79Fmdv%Ptp.m?ishGoV/s['ۚ >6?Cu?Пw&F2G?G+ds1q#я _='*R\I-xܨ͞X;9X]! @FBF4i'" L<ޛ.HVv12F`|9]w# ?Rx$7)Vx<.'`ϽW=<*TY5P;mp8^(-FU<:٦(FS)jAIWkt}NV!X8*!~0 qJ2vS>$ @!Qf@qPX $*2_fk26N`ew6<(T&%+yY7(r@MR@} Ӄ#NV#(XgG|QN0HfNPqM-4CsI0y1ҚJ=E͞8*nEf6J4GS 8iSTk#t4?'4AM7?֪0/+5x?1OU&LaO/Xgޕ[DXGpMHӺ;ΜA \|SֽA)b]ggfYF 4ݗ,j堺R?\=O,3˥ +摧RēI2cDW8SrɿҸ:^cs=\(ҹa~H5iX5akIxQQI.[bN,F(T(:ͱ$5FGN\jNAҥE7&O.yR ןj{D ?Z\R.\,@mBriv! CPBP@ puyj|d>\v; sэ[iWF%2 @bS8@ׯ t^EgJ[זW6*FJU?}w.Э,4g&Oʳ/hs*# `t5|hmIG)B}=u" qzi+U `j0xHWǡJIigdqŀj-F +vHDy}?aw\`2RaJiUhkaiƂMnM*udb˪Xغ R\pbGUsd ]@m]޺ʠnfNF@-/ Ʊ[sD!d@ q7n:{NV氱汦\`sZ-V "dڳm>Pw7Nř $쬮\} _+wJ##- EP[:D3GȐ#9ǭupD֟jjNi.]H?CA:o ۯ,\.NHEM^Q{ad`;jB#fq<5cG BZ%DJ{quoj0iRG,+(T$tV-Ng@v0>F,nͻVPa /:sfڼ:2Z܏ Keeh#PJ5>Ring=:}„ܬpf%,S=*j0d(_b XJܑ6Uu`J+rI<5h| e Ჭ\Δʏ|3[w#`aR5vmLe7{>QQ,2r8[q[*-d- I+'a ⓲4Zm1گ`Ǥ!e!MO+23C9?j{,.E%x=qjFOFm~GzaN22 S/LY$f )aSv>2A搗'P4{nO^zԈw@= 2?JyqU,)0nA4!;GwvoCJ 1F?@TRw&?10I߅0LmO`?Ja"3֓r*zF9MPc=V4.趨X5fmJPԑ66RG}SE^I,_Vj0z֞uRʚ^O4\`*n2@NÏ3jԮ&N匿VGs]U_c/kW#l\{R'*`y?JpT ʱE++*r:O!= FM'bd# LqFÌjdʎW*S~4zz1؞4P>$jy f8ٛbޘq4B=A=K)/?\^}y8$Dd }{mF5WÇy>Y`OG͙A)L7WH U5w6 1 W!{87OhRLnDZ5%QUϳSptj5uhl&ӠoZl?O/n-ngUK$McPsȴf5>Hw{{yA8xU_nez*8i:K<3}2`0kyQj 7o.HPة=SVc11*Z s>^_>:.uT\髾]Ȇ ٢u*w|qWu=r]܆&lg:M)yo( \u'z閷"[ p9&8Ϩퟥz?B>TXZ5iYz.>v9!ʌ~N*]" {ʻps$*ѻҴB hiHGeY?zf?ZӾmYI<#TBC%w+׊q+@ FVb p9MRKYtRBX^{k2w*ܟv,E)N^醴9]Qz|η>,SfҴT.IOC]7xs҄p~w[Iv89C\݆=bloWs2Œ*fP[hߍoG ^5WOc,FXfX"IzHF̓0;@s@6.xPIiD=NЃkTpk%NNu<+F2H'm>2".')$W]}gIz[Oʼ+``5$\,e!'9^)k88YmkҴeM47iJ8nY&n.{vѬqNwO =s:wZhTw[)y|#P;GV3{I?\uO5h쐃/A^wKcэ&fiz hZwGLCvEWPxQSNm-\%֡eNbmvtN3HGMYYBK.O )HuumO[sǹ,x/RĀ$ǣivvky)[OvZt]ZK)P)p$e+CҥJ)'i+ְF3K,Fv *=kӣ.Wjʮiϧڗ*Ljd,?%_>TF2F9rBe: 6Cuo*eۓkiugo0~q\pPgZwlmgw͔Hc^os7F71wVp0F=p gḱӯ,B.Q'zҪ{ biJU3f=@jZ\>I`>]۹q\|#i $!Tq\'nVh'׻gV5dhw$w;~`Gh 85Pi>eg,hG;xh۟$§_ZQczSoNH!,:zYTk"Dt^͢7A81GJ@:fi]X1-N!da#=H4صa9De+16N) qluY-#e c;ƃmّP'XPG % 0IJ5+$F0x%ũ N"Y^٦Ȳ̒FC+`=$W5YbWHIfYH+#j4IEj;w@G#+ǥrW:y%֥y2Fb 'fr2x>jڵus/nGcZsDϕ>RjCsc$-9o81#<Gө4cv6Tfe-䙓iKc+5Isw"Jyp,lo?[RK>}"RrOcȣ!ηXi|A5o;bH<CxrA[lC9m*I #_$/n[ l`r szOXg &oV*X=ÐvS)"6W[[j<fK+F,y0W25Agɪ o-M$t>jb>uOEs&Φ"w_n?F.RmȆP|_+zk]W\n +E$R8:= VZ ꓕV5uhIz$#L?Fu/Hu+?Eyf_P_ͻdܾa9s׷j|: (QEdEA (b2^E4HqNڊ()g$Sq(ڒػX҈z()M4R@vZ?VLs(s=;:/-b>ٷBW\Ltֺo?b[1+b@8 &;\ri9Ɗ+.?Z({MzQE7G!P*P QPFXrƏ-Iz(p&}vQWUSRJ4K$*gJ(藱GbRd K2qtF(̒}(BhOFhvdǨnF>QY8;~ty񢊕rǍ=ɐSh?*(P<_N|@rXER{33/!~QU»k|\fٻ`RGBFh!cbBnzDEY ۃsHR/Ҋ)14?4xN~z(;"䘴V5剘n2qx_D^Os^XZ]Td鎘 ([֧vjN|He$ E$V)k+tTɣ,+zlmr"9#ҹ__[-nM>RdTs>i+֧]ت V ԏ88=)T`ϽTV@ S,`HrP@ Rk I 2>QS&4w(F±0Esl,9d{*LO [ghz=15*́W8<Vb60&F#sc7{aklvpܴD@ D䯖bs@΍u@Mx/3;7'`{gVg%xȢl o)< 6c;qր=fVZ$Y?bv tkWq?yMF|kz_> }jw/ sgqm0N|/黏mmX`pxz.hmuO౸]MJZ;Rb#S}kK?.=޾t8ΗpȒ|ؾLʬ>vB+I$PeFXCKc]%ȏPFxwFx2<S.ak&}Z6#I&a#& ٍU_ ɧ:tOkvxa4]Eu o*Md@ppyq?p -? 56E<ƄFP{WmZS-6Qv p9][FkkHKũiVbXxZkA)m -)2oiJrQRզ7!#{ή €=+R Kۥg8r<0>ѤOa'NTbelpf{]99%|/XE+9=q?HP$WH +dIZel/v/~QΪjz\74Vљ%p mQrj\/ ŷ.Oڀ:+Z[(dd{ټ YN"O<#,~]y;TnN9_ ,-{WMDn[ph+1ހ=OH {jʼn8;n2k .(LN)Eۺ .qxmW: [8% lM78@:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?6:7C9g?H渇Ϳ 'Q+C i|_ [bw|@?Zonc֌E-7YSX/O++5ĊYܲ(tC??h.Ƕ?4*i+w<C??hҾZ_j:弾%@K շrT.[a8}bsşHjyrWcƇhjְi,_Idu Cq+ҿ\?xz?iϠOOsvD?{{ksc֠X$oM:U[i?pmɵ.t=?~'De?Zo?h?sӼ_s}-Y̩u}-i.Cdf#!oKE]/I[m\$ېKNiǏav=exÏ@(^mugTrL\WwjƩaf{řlegT1"2硫+#o*1 Lڸk5[iҮ'juCހ=7q?yϊ-koޢ5HaȇvIWgּ:gg2ʷVz4!&pѰ'9/ ?]z]&Ԣ{b4/I < 0_7&n*)uFW0BMRW\HCב4O1FhzRHX6SNy/MxWXIo$aK0Wӌi/6ҪgB0sb7C6i.t cH9OR8Zq?fy1Yrƍ(ϐ=p3W3\-{Ԑ1^B߾;voI;x̉UuwC;9 p6Aۊ?xxjGwcű2ޠOsֹ?\YT?.!hRW6gK)O7] QXKzW¸տ/zOȎ,Hm L;ּ(IPYt)t[{%PުAA+@|?vU @8ؗ9I`o軏Қ2¼#ݔ/QGnZדѥޡt?GD$@:jRIm-֗Ѐeej>'JM69G777OHG@{{IVFɺHy]TlFE2Ěue1mvHվ⪳nx)4'&HC#XW{At/jkfon^]L{j}/B'N1tS42P {ѻ,^EXRqVcuz㡪־'.'K 'p1IFv85XuxQ}LaK?`pK)`b-w &]HIGe0}#>"lq@j+Ҟ9ph%RAUc:)5;zJn2cs"䢉7:ooۭڇŖ FB\|ÓM<=h&hRAPgbQI'`@8=Cq?FOh?fj~*I4[ZMpBgn֬)dU"`*pA<5K;qq:褙 Hܰ1Xc*K].v-4ggc$!}4Yޥ\B8#Fr*@}iv)U FӍОxN-9~V`SI`x,$o[40 c_))cnp:g5,/4#5,πrI=p)t/^;NKeE1, 'sV6;==U1.=?ҼOk3-^eȳ"'x8zQIg Bmmegt[Ysyv#9x[ cd/4S\pfv'> LWwZ]{z@t*\ԭNsJq|eTo|eUxWzݞwXLzAWWM5tUULaGTŤSS># 0q{@ HlfbRFN`g鶖7Eb(mN꽾o|eT~.}wR}&__Fqc/=~x]o6ss?*|/}7Ϝ\KGQo|eTo|eTo}!y3c/>x]ό?*o8$^zTU@_F_F7GQGQŌ6ss?*}?#?#F7>27>27Ѿ ό?*teϮ]o 7p&l$ia,`e5t<`XT@_F_F7GQGV~{qkEG>k `x4w_wvwdTwx_F_P[#HFAN+uŘn--oe{ 3ſp`Wb2;PUGQGW?VK{=ۆ]̈̄NvR;UdGɗL-ߘ|H\:ό?*ό?*t_xe53mhaibI$Hdszۮ :mό?*ό?*^5990>6 Ԏ+bXOm1da#8_?#?#⫛C)kmLi3drʡ8m",]\[ڨXǙp,NAE$0hC|/|/rkZd6 .hO :=0:o,4JX,`͒qd =?#?#⪼:Ȉwr" A#)al pZ$\u(}ό?*ό?*uxGwpr:2 ~ ό?*ό?*o|/|/}?#?#F7>27>27Ѿ ό?*ό?*o|/|/}?#?#F7>27>27Ѿ ό?*ό?*^_٭kyx.#@(hI*$RA)Ui?W %F@~W?JY<uQPxG]"{٭%[f !!n,AqY?KqⴍFtw<ާ@ ??P C[u ;YR$->½Cxxվ ۩v_n/nWU[)2|`<uQTW-+~W?JY<uQg)sznnb+0ia - U+Ɵ]noo ?+Ɵ]W?JYFҴbWԵ30Cnmg$): ơ%̗Ȓg\2v2}FYxO<]z5 ZcCE"FU^0 o|/W]T7e>62N*77" :[iۭi?g~qyHbF7>2K/]WEkJt܇Hl=o }JͧnKoc!ԗ'[0+\K1e/QH ؊[y?_L$gH: j✏hGQ|eUk =.V]6s":mrPlH8<|fUk }=-nZ-6q#n:]smR\ <|eUk ?_Tt&Hsn|HE~dF泤DGq6Y2*N>@U?_K&g 5ݴNv*uDjX}$bO$ a$rI3LZ6f$H;8$} 7ό?*fKqsm/sհAPm/}#G@wό?*/ѱßZ1Ϛ(2ydGIk%$Wk88q>2[q;nIDT#G-J F#w{zM/}#G@wό?*/_Te,%98xT0b@ǩ?[hhGQ|eUk ?_I^G@wό?*/_T 8a(RUZhhGQ|eUk =l%=xKxi,# !kCh|4d1Arlzqҝ/įA$Ӫ>Kr?Ȯc▔kWVo#,^Oqw:X1nK.C39>zVnvv)FwQY7)+Npy[*[Xw0qfDdz67O~+I\j~$;MEp>f:0JG#.ei9)á\؛?w~yİO1tzh-S,If?5S`S”a|]J)l KT5+{if df9`9듑KykSS'umz2>RֿٮbHe'y6^MΙv'GI`1]#׿OG#׿O[b0g.,n`hZi漻rݲv"h$wƶ|zSvE$!oG#.G#.d>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`S>wwM`S`SXH,.VB "3j%^'$:fmoNOLt×mPmOI!UR[ɮO- J9jE. .Jw÷`j0^z0)-h=wIn#-eGMSh^-FPԡ6PF'2=z0)z0) JK"䴖i hBr?]iz~uhc .Hxϕ@Wq`S`S<ַ'1^_ Z&̪^S|_k{#Y5YF`G FG]#׿OG#׿O@{xZNNV!Y]ƏN+&QRx~}Z@diLm) ? tOMNcGgj})#$1ExA6F--۹4c.OT9IL !I]q=^F?]^F?]yՍ^Ϩ:opqBɀO#5 H֮X=6<:X2N:3J? ? |ZoZe} ̍(UF;'oq]wVed$Q8$~F={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t;{A?tGS;yA?t;yA?tGS;yA?t;yA?tGS;yA?t;yA?tGS;yA?t;yA?tGS;yA?t;yA?tGS;yA?t;yA?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tGS={A?t={A?tRlYoPQ|G>յ//,9]38^k4KfǬG+4zO'RAGoE_/Y ?/z €>hkhK?G4^Bo?/RzQ нg(?湽KNAV`2P79 N9^A нg(Կ^$ۯX[݉\ 'JVe]ZyEz_y'<#5_z hK?@kOzB[N9zVΛm4Z 2[$nU*@<3hK?G4^Bor kջh [ 1wJA?_z hK?@efӴ-K+N )ǵWjɠB\ܬ #m'pqz šhAXrȲflMbS :g-[G4RoY9=y74^Bo/RzPejhwrH-1 Cr.$ dmXؽLvǷtz hK?@=3H/b#e > C}s(#)#/RzQ нg(5k䶲M([qxMG^DAHyq{}Mx4^Bo/RzP\hڅ΃W02@qBILjKTDB24chO1ņsyԿ^E_/Y "# 22 PH@]$-O\ݱUKQm~ʪV(A?#^ нg(Կ^G_>E_/Y ?/}||ϟz hK?@A454^Bo/RzP| нg(Կ^G_>E_/Y ?/}||ϟz hK?@A454^Bo/RzP| нg(Կ^G^S˽>k;2+k7{fA\9jlj;@O@.._5[In p`N: /$;7`E=9fbY^0" G-WiGTw?=^<"'8?aTBZZ鋺 [@)((((((((((((((((((((((((((((((((((((((((((((((((((((w$Ŀ M_W;y_]覯((((((ldszFjvl۝tJeL"p2IFвxk{h/f1aVFd.XcP|K[igK[Ԣ2Dugp0rr1MIumcW 5}WRIVFklヴୂ@lg@xspT/ jREH\0fh.aFzh:j$wwZ Cj.lX GꚔڵ]̱(q8" p$NMڕPл9&eB̆6;AJIiShdKH51K+ƫiWwYneLNլ}sMI !Ku7b98R+U9.,滞K[]g%"rT ( ( ( ( ( ( ( ( ( ( ( ( ( (=7,QX;t ('`M{G~Lƿ, πF9LOTs} :8B CP|N?yEn $rMpHAk3S^v}aUcG*J;׎+kvv?{xr$o^]؉nQEQ!EPEPEPEPEPEPmCۿG_ WR)EbR1F.)@rD 1ȧKW)DMQz p)\@fQ8=)SSCkSkTii9*K1XP 늓.FS p?濐BӂԹ3xяb!!JѪQI9ӂ3UB=E_?Kx" N SV=>OP**ш$,4/~R١1"EX K4Xh)_MK8ULh 9{M.9r ߔXȣVvѶ;Wա+}*8-kh ( %G^F|_UdU,??QXȫ;hKաXȦJ4bʹi,/~TU,??Ihȫ[hG;KCJfx)k սjr)W$E7$EZ+M+O<{_?LE$ gVR1;ت`y)L_ʭRfRsq*`B%iU)ʊW1/GHc_ʧ+M+MHTb SSRfN"=&*BU\aQM=*B)1Nn#0=)1O"N8&)SHKBQKIA6 JZ(iO^^2nZ\;hrUtt_s=sLs7S_I=cV67j+2nOAB? z koyy>S|I=6P|9iklX4a88$q^\׋؝WNk-X(b ( ( ( Cj)\p%$>jy0~$8Ep~6|bt ;Y!n%%i!q}W^S Y1#`2*H +{x$qơU 8IEPEP?'%Uj*E5|A@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Jvʻl>du,qzv`־~(3KN#8/s=Ԛѥ`:yܢtʲP[מvIXcz|ñ]xq@8fvW=țsm^^'?g ~͏^[beQEDQ@Q@Q@Q@Q@Q@InQ|__|mCۿG_ 4QKH(K@ \P8 Rh)\D@)Rl0-8-8 p-@l4ƅO RټixxڦѦF JS# K JZEmLߥ/qKLmj,J"9*\he*ǒȣ?Q>DWI'E}jǗqHc t)+oM)OLm!Z4>c7L!Z!Z:dijZiZeLVENVV3 S!"V+M+VHJ&a*d%iTSHLP!"ELWaiDSO)mTeq1M"W=\Rmi434cQ)zޙ"bZ(iO^^2nZYWڍՇ=K=JW|Ku'\Cqq,`wZJ7o}֍֠dzrNoź_gӿӷ'u~_nkCDes1];K!);¢ (3 ( ( +Ş%º ]J!S+$o) FI)ܠ((((O;y_]覯B7'H@yg%)"X;p3$[Iu9nCMTDeڤ99Xug4:PMg]oq$mvߗ FwaYmI5aOK;'1Laf D\(mAUmHrk 59_ ф}̫-$HٜdTs wN)QTDeڤN@9$|sUl|;WgPkei '-jw=Otug4:PMg]oq$mvߗ Fw$ /h>U mlN2A*9A< ywKR2['HiB|FcB0Mq@7̹ q6 OF(Ȫ"2mR ' >Q9gY6s@<3 $߶q%Fmr?tgq'=3ZX KK[Y:G)RT#8+7$txY|'F2m#kfrq9Q258\oov!cy69 "?c(LkpyOb#- rr#ug4:PMg]oq$mvߗ Fwaqm-HujX)%Y%$Y#݅ݳp0^o pGvHr($R\0rn$txY|'F2m#kfrq9Q258\H+P]>}N+8  OF(Ȫ"2mR ' >Q9gY6s@<3 $߶q%Fmr?tgq;aYN@59_ ф}̫-$HٜdTs wN)QTDeڤN@9$|s5x{bkJ_Ȑ( *#sWiZxN( s^#bW{^"{D/.B/?Ŵra;[<g+ֽWG{UtCDOp* ( ( ( ( ( (<ow: $\(A; Z P(F"KP)RlD@)RNh@)i@Rٴ` ZpsOUl04 N N O PcBӂ6tF-ƘдFFXIS,'*TR+ʭ,*U=7P k~*`NjҤcfDAO[P*`go4Iu@ ~|@Ojp> ʏ ʴj5qi#?ji=AK> JvTf Z~I49RY? aZoD@3{SJDПJMP0Yh*2բ &a*dijZaZyS +L+SL2ҵ9ZaZyS 4LVV3 @0iZy@i)Zii$dRbE4i8&)Td)S)6f?x ߳7_֦ۧCGjޓ|C5;+7J:ǿg2?1Z~j4m*KiVE q$gqU&8*F֎\5:Ÿ"]֠ӭ^ix?vFXrj6:us& pO½":um]vt9+KS>\iڭB-̶ 2-IbvZ]>d8CrHp?πC+FXc{!yR{;_T@-ot8B9($g !r8 Qam4{.ģcgvNy vlB4OW͸% {s_MݬK[:UDF"nms$MnY\]In$w5GKd',3Fnf-}V ۷^!-?곞>;drww*j7p )u [u|m™"H+  \_xOz䗗ҮGlXYwԤ(5kHXO2gtq%COݐaY@i:um[}i٭̶Gdc*_S ,l㵅x839'rXxҬQ@Q@Q@Q@Q@xW_)xs]+ˋ~5kRa O3st8;# Xeׇ F{YH$Bn'6Jl+ףg'ZrO 1<(:12ۭYiop y8@ ѷ+u0b@y'9qdd>RQc)n-,Bc+]Ѩ<f'8+K b h'@kPjYtn4.-խK<3$bxQtbe}Z`1Fp3Ԁ@]kWQVF ܇[z\<FވI֬ qφŜFJ߸ ZԹH;S0# VbxQtbe}Z`1Fp3ԀA=EsoDW$VapāpOr8uoPeXnni& &#>mę8,vw&p ®2Ayi^\[Z; jyfI<'\nfb%Œ)gW5(o-YmmV@9།$rw%G=>,M(O(s]+ˋ~5kRa O3st8;# 'k_b֬DQ< u  :+pxOQk\:՘\1 ar823i^\[Z; jydܓzO 5ΌLvVl["\(žNp:<'.tmj.0n N@9gâŜFJ߸ ZԹH;S0#2I=F'F&Wػu6-.aO'8HZ:6ErNf Ho7 3|5~,7Wj֥AڞfqwpFAǢ(((((((> s^#b\'#x?Պx{ݸmϖ:CIUI+E)ax&)!')$r)VFy1QAyʃӏ?O]$vP3HF}&fr nVjH"@#!E€pay_%] ό3r5oohw?я/Tw~1x,~j^Clm~wɳi}|f=I _V.n,q՞%& ]pG>=Sī(;}/MwNuo C8aH (&X6kyA_7yrM+ 'BVeQ@=gjcayGn?q걮g== H%>_k']\EooO+8Գ;O5;3>7Vv^%٥C1-s엘wٽ> ( ( ( ( ( ( ( (=#?2:MV+ς:׃=7X(v}H ^ljH}y"I4%#,~юx^g ion*Y߀\kv͌ZZO _| Zp* ( ( ( ( ( (8Hw҈P+믍HϭG_#S#ZhZP(CgDb )ROxP)P< Έ@ҁO)i@P\iTS6uBii@mP4-Hr&EODrab4wzuc9QÃk ,q֬5+U@/sS*d+ TEךCHS,d2"HՅNƤX5\s֟՟/>LqP"8>gˠ!`V}DdU˥+6j6j*l>+wˤ( zF[*i<|(>֏j\SIe=;#TۥYߙn=׽idz׋vW>SPzё\O)G^'<WOҸkʵ)auwj{bfWױ#zQ] QEQEQEQEQEQEQEIu0ɔY2mbH7qܺU>]gIuNSD̶.@e.ɨBKW2&2̼zԯ}afHޠ ּdjEнW… L||)#<Ԗm눧NY*L'W+򻱜.Yzh1xju-:b3nXC?z LFs K͡ԮS/%i[%Y@1඾ӮwcOoc$XVn85&mSռ'r[l83]%pj: }? ON І,ϗ'Ȩ1i.Cs[}1o}E`xD$~bqa?K?}v 훾g]XxkQJ-e7zHDD:DhvV%ebx)U>mK嶵ְ_YOSqnUd;@/0Tv69ڸi (縷iI!X]2VQAVޱS;W˃r@ 33gĸMm;ZzW ,w,kHf76G!b… k^I_% C v@Z#2Ue+ų `ȭ"pa$~"a]hXZlP,Jbgxn P,|w"xGеdRiMUTbNs+D>XJoP vUѾEu$. ,:5 rI;7?Y]ggvt҆[ rA\60@5I-zyeh /MϘPCaFn>ܴ+8V=֗6PDlmwWD1Rm ޫg1ZK.DhP*I$"l*tKYk}/OPGk JrHPN2@ϸK7>~.+oL'q[dSG5[ ?PjrN-gZ59*ѻ!VJ* 1je@,G-g;w61}M˨Xu X]iFr@l`Z2˺®#oRc}=eIL 3̝*಩OYcj~my:(m3e[* ~΁@1-)_tsƧ-YŦ[òC*o.[2/xsK4l^FI2Vp$6)Z>g_\Xι8͹ rKӮn%Y&r/0~.+oL'q[dS\Xuw2̊n|`#3ٯEQEQEQEQEQEQEQEQEuёzo_.|FG_Qyg ^x$\UyM6 _`9F qTs\^{}_!06A{^[SQS($((((((>5#>Q|+HϭG_$E\P)P< ͳ1)P<j[:a ͳ*@( R*6uO xZͳQR(VDɬ:jt✱t+L+QaZF:U+uFsޭ$yb ҤՂ)WيiLՒS5!bSEzaOQTZPMD^)*6+E1ܠj^kA1tc1NE׭i]N*ۭm˒>;g⫼CoJ5eK=l<9*1ҺaPfCEߚ:UWJULTLfS\a;-0h8Ȧ0 -Zg,@˜ENQ9')TSLHRL"3Q#"i(Zjr+?fF}-?i=W |C3@/uO=k73jQ|CϴΕi-lD_=Lu&=h=jo.s0WOҸk+i#}K_jKy׏ӟj_6aUaEWpQ@Q@Q@Q@Q@Q@Q@xW_) O!ojmIl烆#>M2J\=xXYA}(fU$'vHL>,#Yf|AY6I8;19듞EnIOL%]}3_J6ҹ_dv$wxKp ]dN w2%A;@,ۏa[Ş$k9ψ5Sk66{&7_rqf'=rs֤ƞ*y|Kfmr7k0H@tV񧊖ĺȝQeRJwdYZčg5mfod&K\,Nz='Elx> MM$I}˜ŘZOxg. RQ##a[ƞ*[_"wEG_˹I* ݒf v}j35g d/s9@V4T ]e5m+;YGbGz,#Yf|AY6I8;19듞EnIOL%]}3_J6ҹ_dv$wxKp ]dN w2%A;@,ۏa[Ş$k9ψ5Sk66{&7_rqf'=rs֤ƞ*y|Kfmr7k0H@tV񧊖ĺȝQeRJwdYZčg5mfod&K\,Nz=Q@Q@Q@Q@Q@Q@Q@OG[}G?nduP~dk+H|#EUQEQEQEQEQEQEp?[(J$[Du`μ2`H +&DP*E*@*:@H YuԁiVmHYuMZ#"\ƦX*EN*P,x"H֞Y91RS4D9ϵ!N=i:cS*iZMPZTM)VvS qSfR+0Y)SJSR-HQ}-1#E"GP]JɟUFVfsƠx?*tU>=xw3Z.YZUW eZ(oٳiXnqbw;Y۪fU<_{qgӮ7iV!=*]־k2v#+ЏdϵϵCѸ1WӇEt+RoR:kujnMzs^_I?/OՅQ^Q@Q@Q@Q@Q@Q@Q@xW_)~Hh$P\3O*E%} X_Mn?bxIu)/u)[ˋUv+)r$zr:6Go#ā!ww$i++a;̉ԷY^VS*04c0{ۜu>S)-XK|1c E9 $ Vԡ.Oh0ێY3~UҀ;?iķ֗K6"С$T,gRw 쉈GM7 \wk V{RҰ' c㟙^%סx-oRK5EeP;kn.%irI#gbrI'I4Yz\ksj  LMPe2+-s>+{NK>7` ]YbL(pNKrڔ:\\Zi>mf;q&pOʼzTtnAel<3 FÚ*֩sa50FblXH۰~/V֗J귗uqQb4YĖ31.Cc$}b}FU[S&amP_jCͥŨ]3fagt0lѯݑp[̵CI6 F5]J_wsik8-d٘NC#dڨ1.'5ŝskk𼄤[[j'r?jkzj&nĮpϜj}mLj~!_r> 7ᰜr,}h<6iup@|$aI򃌌)>eqy{TvehTGʂ]_=M\мI fҵX"yvϳR26<ܟ@m4HE,:}B"[Yci2yD0dc?.ZOR-lb] U00B1ZRI08um{B)iv֥Rg]>*7T;6K!(NW&8q}XдO+o7n.dg2+r h|i{yi;rJoYʬ6O' H^XD7̤]rr[:.[Yl~y,F+#/hBԒU@cѹя3ۿn33]4{XcK-rI,uZ)Aw!Yh8Ip$5pf .)!S@,H ۇhOkon.5 H@ LZD 2`p78EnZv%dJqp f9e[y%"6Y@rRBz?ګE%Fn"BzFSG!e 9KajڕŵŦwRHehT$!* w$SDz6v|Ͼ[ENrɜ$vcl:\4.^RB v3`{ms&җS.#wL^}f1ۏ!3d<\kqżC s^#b<^mƄ6;nxog^=ys8z85%LJ#^8'K٠8f2wcu6&g~ >Aݺp /@@׷WD>EUQEQEQEQEQEaK]fM.DԵⷎVhQdiAeBNb~$it HT>H#К'A5-FQ;5G,c&;$`5χcYd>r2[˃BX9~5$Q|}cI&҈V$c׬NHHl!))T+6θDr EZ ͳQHUdMZ.z~*q s:6~n8z~E@1Z.?GsV:68YH*'N*Gҹ1 X/LǿҜJV݆TiyY99 3R*qI&܆*tX~Hԛ=Ȟ'N?JxLT:ScL)=P9Ss"J{5joMӄ`S9E&EYA=1sѷާ14=.Ai6Uԛ=8 LSJi^zg&>i=PRT>Ϳi* S*֣d*N:bLNe6^iij6NʲiU2-FS2lԍTZ2TLkU#DQt+Eګ`ḥ5Yӱi~I#tc2eGҵ?Z,<+4^ߥTtVCTWd&=  늉tEN *&aFEhRr*2*r*2+T)ā0FEh8^6GkWcЁtAx$t?k 7/;Y۪PT߈wn4]|-qQkMBPewa@U%~H*O>Q\[Y۵PB$FNI㩯5Jjڰ ݦ1[*r:M{Gҭu[}Vlq\#1D׸dJVG:ϧŠ"ږ~em.$D243Eٰ A kSB{L"g]o/N=+W?NN̆یbUy}'T4u{ln'L  J>PxQ\ͳ^[;+kiV퍄 63Ӯn%u9vWeGj^%O>-OR6۪lqs`W'Wwpn"-2"GHio 7Y@Q@Q@Q@xW_)?[_#>s_OxIu M{xr f a9[`V1ۼ9fF#qA:&Tz2孥ѥd$) Jvx«30(ymŭѢ!ƖOP/V+9_W be_\^,#=r@|LʾertPyONR+LDz̓X/Wk*ޚRE񮓩+}4;mb7{xlXls8C`Ί4⮹{]F1jy<@zw!-¤ӿ?W?kvf6g_J7Z=$BJlN[Dnh20#p Nۥνws[o*Kbc?&ԑی\pky溸Y&W/$1fv'$y$s^[]ZQ>t(vϔT85+2|,VX纱wuA$6=?w%BGme4thfh-m>n!2\/ԒO4 K,ȕnf8=6/$EM*Ifwb[JHOB T5jZg%otKf֭t G}T)`33;G cyx#Y$&#d1wG\DP XJtiw1ĨX9 11 XdptPAqi6x~ z{xk{"8K)[Caw>yA CooprTQ+7VԴk  44LW { @im^kP_[mh5]7C!WR;eH 5k%P?Y.3Z1gQuRBdrK$NQ1RU;$*:#+ZiOiI "K,EfG1 +"u*95<[|?==h)o;Vo(7(iuy]MƝtw~V<&Aq2oXg_Lcvʕ`B|ѹ<'}g3ێlu@ZT<"-gYd/Ki1)UyQ0q'<`¿humgYR4 '9< sElo fn*3GNr6 v䁻2IkPe%-ĥWiGNhEn _ յd@J8b,['p+1fcʌlsy\ nN=QEQEQEQEQEQEQEuёzo_.|FG_QxHKf*9$ðR9=yj[2^EnB#d+dKxi{s{}xgY7o^ZC*{QTHQEQEQEQEQENIu [>{Gm$Ы-rʃ=*PI.҈ +I:MEcTp*QRMQR(=zqQHQY6vS"jVlʵ"~fNBq9W*eN8nc) Tq=S*E^+xR)N)꼏Zx_Z02rOQ'OJx9h>b͞Ƭj\V)FjPi9"NxS$֘TE"^)9㊲TcE"'_ZmgeXȦSL^y]ՄchȬQ,QS6̾ Gmz##T.Hա"wLzD$Qr*3 Lf\qTǭkER=;e9ǒ#UpkJT~ZmLGay +:2;fB!<((((w$Ŀ M_W;y_]覯'5T{vUbK_v{TR]>6Mŭpimyk$\"ȭpd0]pUXBLķsokqȸImENJ1a]Oi [I091O˸OwOѵkK]nx-M~̆7.وe .|*buym4=2D"e|;IstWQw=RKQZi=餎9~!Fś܁#"ex#2Yy$B&&|hrp`~ xc4R vpzpA"56 ilG[VI*x)".훀eEQEQEQEQEQEuёzo_.|FG_QxBp?Jw *\J'>op;]*8mrcgH/#׷W NLZ T (AEPEPEPEPEPEPI6G_*4g6:Z0q VS/WLx*bEs6{t9EJQYvӈ21G"D3&M]=*8UPҹ*LВ$>uBjH+r$KZyNq9*-XU~&K`S*(R(f2bLǵ"2yLe!GS*ԪB# H@R*Jj@s[&.BJz)8" 12r֞Jx_Z03rg?i*Cs@T@4ըD]ڥ}Bzraaz6z|CRaa|HWҬ2zfRR 9OJk/cPR\œVJS k7Ԋ֘*/cLe5QLahjVeYx⭕LXJ&ёQBU^MDːMa(FEB;zTdv5v711P2uY]2'U9>RD(͕T=;֜Ttq]pFDyީL=^TB| Gמ;y_]覯5MSm=ӵ>n4n(vsZ/oe='NK)-)#$041UosmW:hs&ƌHJR~B[1!{ 7W" e`bҬ# v`4}.jS6ǟHW܏-1l'9|r[7MKGcrצDdy.vY$ȋ6F Zw {:}>>YaEl6Ġ|͟9,XU4mL)5{@7oH|"-N7+%ji^ҠLu--縆Co\6Y$'DžjSk:p\H3bI8565]gJk2JM?>_'R#>iM:C_M$l@4EbR>GRNKU-4xl4B9>v^I [B^ )*]pFJ˲ׅa}*UfYdY>Q#$,I ŝ`VFY`UXF1$Bv#bm!🆗T~B[-c/Ir Z2iv~NuQd`fgǼ(wNp9OiY >5!Fd[k1XV0pDd:IdFE9WI"tu#'< U#ssiN$p]4Z7vvDH HA(JHڻp MG\S{[A,I=|V4 I!B 0Tn6x5Ko9bHd$vߜ㛢: ꗚw>DKiujS>10psEQEP\\x=Z,` HeqI96wlH k.<5959qE #I(H9+Jf0ưKMUe0Wאj?lnuS󉸕Y//N|8BKy%I$Y$I'׮ZZi#-tB̦ 8V xI9_71 oe ?Uͅܫ1#G] Ȳ|GQ9 H\mrj{k[BjQpÒĘ۱@y@=%>SK+{{{}2 %y`ŎkgqyTPI ;Pk5Qej)4ݡmsAhpleX+ 2;:׃=7X>؟'ŷn"N3&Պ$f_ү.ɷny21^,,NNql+\F6sYNJ{ѳxbqk#[=m#Z״%O ((((((($YzJ#TWk?[DuҊ筺=]1*U0ӈ*bGJɳIdy *r*H=랤)J:T1 zkI G֭ƞ9%銰W$-Ui9V2fRb9ǥ")2qN19!UsץL{*J WL"a)jE^}QOS*JB犑W^¤Q11Z)ҕG<ԁsֶ OJx^=W41[( Oҝ{“֊&nD`җi'8/3S>v5.%)zRGZ`GZ=)v5a=)fZ_TeNx,ң+XEfN.~QZQ5j.*_c(FEV\j^/Q8yD2*2֠e.yj\}+q7eڬL;&tFEGSޫH^aޫ,3>E늩*ZRATp@HfHgJu<ڵ$QTA]3"hsT\֙ PNzWu9 £aSDºz aQ0aZpT ݴsW0潳ui箊?f*ԍOv|Cʿۥ[gXuޭcQ_>!`_sX|nz~H2xl#֌Zpp2ǧb[KO/V]_n++2 a[)޾"m៯菑clJXQE(QEQEQEQEQEQEQEIu&Q0WH%38Oܞ~c_lIuk-71jLi(ඖ~%a.e?6umKFk/PIk3Dr R2ǰOEoop%HKk(R 5=1Ieӟ}s%6k[k-%͔7C4ij=xg.3n3v:0*0䴋G\]B9{QaIrA-r\Mz3J'-(ӾyY~7m֤֧ז]Rwc2~I z]BK—M-o2Z[&`K@`3o˷y캅S$ #"p=zŷ5;m&>tr3M<᥌#l/dgR셾s1yjZwsli@Na@N(((((((+CD { K[/笊ch7̬W!YxWc~G?';==IsK4WY 1WyFQnIo$^ KnF3oGt1mxnr=SMmf;q&pOʼzU:(ڒ.v٨nŃ?!,#5b?jkzj&nĮpϜE$kqżCmf;q&pOʼzU:(歩j9gXWa8QzT6>eݾ$bX|y!xrEzG?nduW˟uёzo@GWl}O^ʼ]t[(#^Bx~Wq rǽc%rpie~͝k^^;ҟ3=kkHTQEDQ@Q@Q@Q@Q@Q@o%Q|ϟU:G_.F jk)HEJQRFϡ*h$Tj*+3 B)1WN: +B$qԑb4㠫F*ޮD+r4h*j Qrxh㜉S騼 5&g&=GL*TPل_N3LA긮DW}is*u^򐨧ӚWґTMJtF&bJ PitF&-sRЫJ12rWҤ (Z <(gN Z(м)y柁J5J$9 = H\(* ?o}{Iޓi>b-Қ@51IRROJiNP曁PR Pj2*r42*KLˏ1Ҭ2TeNxE"/QaeXȮQ2}*?a(EY:ǵZe^:W =i+1eVZ׶*)\utEJ"uh:uVTH͑@jׁZR^=j]3.d*2kZdYJ"\1US= "j¢aSG H0j9qk?g@ ;]4>#թ伏 s[ng1ҳ7 /m`ͬrӷ['_pw]¢FjOPe:gڶ?6c{ھҘ߫Ow_!+bV?VQE{QEQEQEQEQEQEQEs;y_]覯ѵɼE*xGb>fn&*E5|xN+.r\1T Lg)&2@9ȼ7ϩ˦E2jmR̨rɌ/$w]}'MRx{ fbQmm3rBկUo2vKe%UAmجErL 2gq\W-n.%{UWFn]ʣ|@bG 0 ݭ坌)JyQK@b3Q{$]R/~Zn(bcH9 Kƺj}N%D ]$vݜs8lMK-;ݥ#mXmvXpr|dXúU.ɵ m!>U'a#H$WN~}ٳDW{ yj,*GQZ>gi⫫[!{yΆHČ[.mbѭ7mdcਚ\3bXdB,t5*}Mɸn]F@$c'RnKT.Ұ\3+e?+ȦN׵XuCa]1]eϻcW7F-ݼ7]-Ŭ ;e]`ݻ` "SLܰ.4ycitY3Ql2@ǟR_ Z}ñiYP9sg` ( ( ( ( ( ( -o/5ny LAn8V}nxjxsiKT64RE9pfSq .N@2IbmBÐI hJp:+_޵Ԗzh{W=@.,dHOlǼe:5ĶI 9I#J08 ޛeڕjIwO|F)4X92Y߄ G~? [Kosv(Ѿn&;IP#vqqG$9Xn%&tG*+~ ,oZi:gUM^VKUv|+ b4/P7_hx~K@7\įw]za<40O=,j+"((((((> s^#b\'#x?Պ үo#HU|0Ǹ{ǷI_^^8&:jz0)((((((3)?߶҈>2)?߶҈ˈNz-J\l:hJ 8ҰűnsWN:hw+{ԃUNW "h'ҭ }*Ž+lHFN`$Ǣ:. bjGN+XDJ=T^ֺLrl91W:ҺaLUP;@ɮLU^UHTf# 0'"aBj Vщ v9WizV&nBpHl@)@J#4`J|q=+zуiX>J\4zҚWF) RM+fRd$bTS)Ci FEFGcY8&@TLa F2FevZ5eǠ{VYYeVs׏s&eFe$s nkq:"ʮ;n9@G\GDYQB⬸qְZ3,㊮\qWq3ҩ̜As'=kFlʜI@UnkdJ*VA5BAȯBج¢"aPtD䨈XuhZ9W-?C<~Zs-6ݥ[g(6b>ս*FW7kD*Zv6MQT9>S^) ԧ<cձSg|Fzn澊-_Czu%kaEWxEPEPEaİWAQKU$0ۡ ed#q@H݉=;QEQEQEQEIu54x%UVаe ܠHk|^:+O(;*FF\`6Egil| % ;1'8yC)障Z] >]qpCFxݴ @QxCKXYoS6q"[pcnY)Q=ƽ W&1K(i#lxC sZu-N,Z G&<,ȟ{bi_%UIRW;I5k}Z7ViPNpH$Caˌmu[i㸴L,a30 :̅Px8U/ssػI淖 &/{(1pWtxkӬEQX~XeYw&i% p@Qsmyᷖ Ae唰pFh-E:Viy%BN԰b䢠g# y p%]E乂 ;%YYUу)]T`n?Zyv<"G,YcHG$cAa1%XS5g/wc!(wnvgl8(ªEPEPEPEPEPZ ̷"XY[F@bTvnSCԡd{ vX,㑒,rmٴ|,ʐ;X,U?tn$tMjx_FעO&bo*fIO-88fٴ0ra$9`p P*ƗM߭+ %dУ ːA 4K=&-m>L%&O5UVL4GU `5}*UӯmV=R]#`xQ^@}\1d_V՟Uh-mmݲ$@..34}+NY7WIi*#$FxSp=Q@Q@Q@Q@Q@Q@Q@OG[}G?nduPC{w<!WX̾2OAPgQ4Rxq? $+c?ֽ|#{QT (((((($X~J#TWӿ X:0F ˈMԕEJQR)KM!ޯT^ \Y\9ѨJ v$>b-DuQUҭ3\Sdu icׇ&{ig*7V|g3ݵe"h0ӊ'>![ۊ7Z3$.~Hru/]5ߟM)\CoK4w`M3pp ;T5(/[,"LODg5|bA_ Du}UobGY3+g?k]I,<{8yBH n~Z-u=GJq1\_i.eQݻs)qs%}bėzmqij2jvE3HHB;p:>{]>;aԅ4a ieq&au[?-~͌c鏺=(ּ!g,u=.U~MĂgo9clh[pxúJUF|.YH |%̨q%p7sbbȶY,r{I\Jsi9dg%՜g w\ x]+A:me>hwArftr L.# Zwjax{Wvw:vDpy3)g2Go',?'ɍ[R[]BN t sL,7QՈKCMEIL%[>rGʼzP}Ztf%THZ`FJ7(N>t5Zrx> ϱxiP:IVV*9+ǤSisjio%XXč ?2OqZꚦgs=[[[H]O*Lb/ :V65Y_j)&A3y"yxb 8ShpiwzZ؝@u@Y1m2BNӵmzRL5$&Iyq+ 2$dp[w `u\7/sjWjsF3*'(wvqՈKCMEIL%[>rGʼzPhw6Zwsg\f6AX玢!Ԥ{ۉdAӻI? rx Rzr58o-lỞ;[h$!%rp<H/^C6TҮ' ~m1(B#XXݼ`ro麆suAI<71$ *r76Aviy%嶫} ԲMëa9-q;z5mJ;.{tf`ӫsc9@ҼkYԚi|33.n1nx, _.YmrW"6$ci=r2jHKCMEIL%[>rGʼzU8o-lỞ;[h$!%rp< x7B~n۸lǖ#s0䕑W!cQQjsjsEK7) ^F8PrV_i6ͤa)iӬ!m-wHy(Bp:P`Uۙ)crxt8 ᕙr#9<]\Kqq,O+I;I_䝪ѻx}Q@Q@Q@Q@Q@Q@Q@OG[}G?nduP+OqүuW8pg*-r>-όKxVGy9_ `qk۩C*[QVHQEQEQEQEQEQE|dUmWk_N|dUmWk\9'/RUDd+GSE:рcFu͋v/U*]+j.i:TuQ)IwwbQ sL::1OjpG42^:ǽDqSudt4z0+c5(52a&9W@1zk(Lp8TtP f1:S⚣z(űTd*N^D6*>wq3ljy=)Ҋ*&E(RiGU&FEtSSHƕL*EIEKC5=:ԥ})-1;.~fiuZk3YI&@pNFF*&XŐ8a֬0 $m@ÒDڢakh,"aj}P:4oVej;,Y,qzUI jÎ*"Θ1WdU$\6F| RiJ0 QizlYֳ5Ig8B¬H0jؙTD-ֽzin쿳}xO=u#Q'gV6-޺Oq wno.gOk?]f]Ƽ/K#&?͓ooF\\]wE҈:MC}ղ}w;Jz%Eۿ菎ϯ~߫ (# ( ( ( ( ( ( (9΢2ع.@+1#BSh|hyVE]kM[ Gl%f͌|ohuc3V`Z> 7“ʱMo2},EEtBUN[-^3&_Xƻv4sAh'y=Z= RXiMt2uQ2q"Ҫt8YAWt?:.$ܰDġB dՙH[Lj–A1ү E"22RwRb1;rO^V#QLn3ч +B+zHьqUa^*2KQtJ;X*c52Y28$fc"t*xI VSYTNB:#dȾdQz: 9Ǩ50dL1SbuT"$ǁt(9Hz"dR(L5(+h&("9A&Hͱ;ӨzV@ɧҁ*%P R>m>XBQE4yhiYs=JJLˎjWFF+6LT}Z dQ5 LýFⱒ4 a[ sŕc"aVsPS\FdҠojG3xDjkh+75^AW\:`W~ MNktĬ9ҌUUf]7E-hȸS 3eYӮ1ZRk>aWL:Qޫ5YWn' udZ988 ٯbe'e?va#糏|п ?on+}k|CḛJ>X^{?]kG~;gn%}h}j,SFO>S/lOtO Du|E'%zig~-{j'#|J((((((((w$Ŀ M_귶7kWUG`aES(((((($W~J"V$W~J"WqwG IVEUu0GSؿoi@0g[H֔=EpU-*UFE\]Af>^>b>"^ӃPީ a2tf1TVtAdڬ'zw&NQ : zTyZj$9"DRJA V1"tWF*E]0:SH'[D͏4ʑFm&-H:);"@ riQVHQESH)Rh06GRmL3QԵ ͚"7LojVQ0Ȭi&}Rڣ#a$kL2>3wCXH$ڡ`sGҡ~$o]UG3$֫ݫ.;q\GD]wj3UI0+8#5*~TUdj'Jh b Q^2e"ǭfڴCFtUj?_«=wbg Wo-?ֽ{i?xD䡧laOp1Ҡ \F\8ؿlۓ;6OEA{Ѽ{/)z7l:QB_.?+Vu{jO+? ( (((((((w$Ŀ M_W;y_]覯=Jh4Td3J|٘s`<qWom謯mV[heXe!y,̻jnե a%UM'mf] ˀptVluwWA,I/X;'#8# NF"qgƗuv52A&|D#n~pvk7V[:{^eë[ʪۧITqptWAi6wzu˻{(leI$ J8$cttoj]HY^oHAbA8gP]x/E{M_Lh.N>s8e[(lY7ޕ܇ ÏEѭJK Ke̩1%HOCLt/~~r:Zm:=CB|rnH]ejH^-ll#_>fJ7|nuڱ[ʝ|)ŢۋRx`e̮6<Ί-4+Vo N.sww2ܖ!XAwmb+`)Fi׶ͣIqgsn&Va! wDdL$%I tWQgs]ͪR\$jv$,7e14yBP,DHT-Fkt ԭgupLw3[V[ۻ+!#+y.U֞`[xɼӵ{41bncPî(O_CЭcu9.4߰݀%uf0s98l!-iھZem`H۷c5?Jτ4ij}I>K1ᅳBTlт$#sy izmoUy PξTH0yAFzduWie驫Z\[!4-B;{YHne¨b\d^úl]r 2RuDQ,=oq9:+4ĚzYzVr^ IFvD6#;øCIcOh4 U$nI;{2e z( ( ( ( ( ( ( ( (=#?2:MV+ς:׃=7Xwx?xBSl;}iWXd^3g%'\/֯v QQ|F@p۷t+ܨŠ(QEQEp6yF}M/)m']ævpmX9EwU=6kv76Z9lB!pBe(((/R\-krvS\)$$bv37wVy@%JA!*@ V~' ԥ#2G{ EFɹr,|\Ԟ >cP'$tu=ՕA$W~J"W}5?𪵌lg"c ?ι1;rFeRUzU]*=*0Ҷ ЇozS/CU誌=E^*JGҭ/Z,~NxT+ҧL~5Se*t5ub/]09fL?N;à#DUUӠ ֺ`a2e_*%%j*%ʐt=;ԩޢN*we!㨩*1TL*1THSץ2*hB((=)!j:4ke!O?ZZtu#}QR4j3ԇSXHn1j#6 P?JMW~3$,9DxҠokf+Jҡ~3$ MVYoj㠮CFt Tz75QЁ3؅ױo-?2 [*\Q^<>'NO1s]`պcq*=p;: t~lA\\jdO͒wEQW7) gZ_V~S,>[>|=qҽ\j>;?xgVQEvQEQEQEQEQEQEQEIu-mcSsf[QcdA|c9~A_ixIu6??5M!Ώ*wF8$ZW"QƓb;_6'@0譃m῱UMFt6 ٹFNܐ7c&I-|*ĥ s^#b\'#x?Պ'ogxW_|9o?JBR=|g f '*0{V*(((((uag}} "U:0\]sцNU('_o(e^O%zQ|ʽk>#eLbVZ_ʸf}==+oz̓֔=EpU)*UFE\^YCUV#\r%X^:P/ީ sȴEO5^>'z2t?N#U׵XNsL: jtazTy/*ډzTEuD5KPSWDL-H: +xDRz;Q[DD"ڎ_[#&8u%ER/JC6A%斬((Je+JR ԕ}RBS>'&5H*&F=M>=Ma#TFz5)jXH$owS~3$mҠojf=ƹfo_L*\:b@tyj}U9&tUwS7SUi:U:Ug<Bا7CTeWjkH-g\u-frEw(MG3*| -ֽ{in}O=va3ߪ3~6|CP79O?Q3C{=ys$9q XhۓN?So'hj=֚jYUGRNacm%vlxuna[?|የd'궘 _^WMA߹,L\]jA-ZErK(v0[rtxEyqW#t ZƇ`fi2!T0H:K[FVԼm5 &O&pN ʝp J+>N;)i˘EdIqf @liYnR'dn`eA(qԓvW/r%]ԭ$Q+ѓʉ#Gl/mr 7:yPI j5/çFЖI^@)mcwR6 890w+|u}[]b^o(7o,J"/QH9€ ( ( (9o'XӼMR mtk-# Y M `)ݜQ?-u4zjr&]x\ l#0 7a$dv)\?/iEu}5B a#F_ bo'' cL:39-Yʖ >ݙ FH/4KOMM "̸(H 09ִ#ԴֵVjB6&R>Wl rWxϪ7,df%6x/^kh<*`{*H!ظ!mwq៳3R/կsQE@QEQEQEQEQEQEp?DU*_s X:=' W&'t}H/2u}]jxGф#ִ@+B+44*UB ~:]Cf>Q?Jq̖XCVtS5e9j*u2 ޫ!VsL: N WCҧS]Pg4L@zLYL@T{WDL$L*EQ!R/Z,Dy@R[D͒OSifM҃MSKZ" h+cJ~GZd4)SzztS -znIE+)  ?JJHBp*:Vl) fi nKQɬ5Hk}ڍ=7ݬdi2x&= HP"6D*GQ1L$MyJw\:"D*=gU,B]j+gT$P?J.s\s$/ҪzǭUojj䍐sT` uEfl~u7j/&8zN*bN]W| M^=߉ܴs׍pp@=^<>'NO1zGg/Fg(p*=yEz7(v F^qYb?Cr(Ȥ\_\Ȟ_]סY¿6elڮ ^\䁶+a%Οqxҋ[kG$( 2ڻʌ+5(o-YmmV@9།$rwr?Omdu+ !u,M"@n 7>d藶3M;d@С7!o-8,ULFi5͗ڤč捝1؄T?/6oe=ϗ# bAgQ'|9Xn%&tG*+~ EYRGre'Ĝ}I((((((((((((((> s^#b\'#x?Պ,o ү幯oo޿x Z |HTc=r?{xgkaEU((((((>2+߶҈E_MdUmWk\Y'/ReuS/Q\2>nqч9QZUP$sT"*]+%U=68j}hES)UrkFE*g${UHsV6sMNӽuERY: N uADj`r3P)Ra$N *`psUӥLtŘIt G5"j,HzDiYD"Ni֩(85 9ҴL͢Z# `M-Y$8A#UbJ)iwzwDَsւIJL'4QI͎ZLli1=:Rc2DcQұɦ1B9FN95P9?JOҞǩL(rjFo^ty3x6$*=W=rs\fDo֫1+\:`]Ȫf
8F/XOvۋiP+-f5YG%PͳQ#:,$Zњnhcc41m2m8q;g|?a[OwĖ![Š( ( ( ( ( ( ( (99*N}J1W#8f-X^UMN&(z Uo>o>Ԅ担ioʛEKcSY!~xԶZAMsڂޔΕeS ɠb-!rj6nl}j&<Xɚ#~5 <'$͢9u4=sޢc\fDlWsڥc*#9Yx֫#t\fΘ'a֫+Uw9rM0Dnz q@4E0D.q15;cT]0Fi'Z+31?vE&86cUz|8I 5 T-]17ZO>OeOwy8o~mZ &v:Ry:%Ȥy. 9WV;o,77"L+nyU#Ʒksygc9if1?TFFR/L>C]j2 Vl#Yn`5AuuijŞdaRLnn 2EE1rMhdk9$~DCqʌ/r{)Ф/,R"LfR_QXdwv /?}^MN*tNyt{klMͤX~hbivycB ~ ( ( ( ( ( ( ( ( ( ( ( ( ( (=#?2:MV+ς:׃=7X丗ë FhѶăݯxD 399%sXMJ{;SPNG.9q سx=?qk+Xp)((((((>2*?߶҈~2)?߶҈5ˈNz) R9IM<ޯp³c81=k:Ƭ^:d_pTB4cJzύ8192*=0VTu$DaTt<ҪS#t溠y"Ҷ~0=UV*u8<+29ջ[V21,+w 9]NJ:Vf2DݩpjnH~5YDTZIޞc1h1b)j%P޴F=(6%TY>>0r$nsE.`_)搰ZnRRCFXLZioJ"N)4Yd4dVRf79*V|O]{?>'r]T>#͟>!UoC͏k77J%P٫*kl+ Ţ.jxX i'lZ>Jp קŠ(((((((((w$Ŀ M_W;y_]覯((((((cU=椺WAR%huTf [o˃Tܺ>ci_bXHK +W;(Pj t2civB捃+I3FA Ҳnٸ0ƦM :ѯ#7zU B8\g*PvEtPዋ!#ivAsCȕLɴtu G!(u-Qn9 X˾8.{]C8!e5g-$ҌcsI$2[ݓ%pÇ((((((((((((((:׃=7X> s^#b<҆Hgx;G#ֽbY4d'˹=3˯8{Am{xO,X?ڽڭl&QE0 ( ( ( ( ( (8?Ju:}M}A?5pw[DuyA>b jU5Mq}6XCҭ@܌B0XwF/D[քOJq?]Y\58эYFQ}j-td$۽A թbLZ\SQ2}M3wAa>SLIG0Xy>ޥi$X ilSI5E(XM,4}"xex>˜VNF ڢf:ڣ'kTlqғQ1a) fPv3cPsFnzQ3cR ۽sNF=7|SݏJϽrGLQ ^i{T.ة:";UiK#ꬍQL4Jb}kJ!H85??'mAUi/ CPRޕ湯L%P٫sQ[fkVcFi5qgVN?'~-|wO|=a[O%vPOͯ~(Š(ѵy5iiׯc32TU,3 #PEPEPEPEPEP?'%Uj*E5|m+m_`>Qy8@ 4VSQ{\:͠\1 a'9qdeWkqyqoj\$nnwd z+rO 1<(:12ۭYiop y8@ ѷ+u0b@y'9qdd>s]+ˋ~5kRa O3st8;# 'k_b֬DQ< u  :+pxOQk\:՘\1 ar823i^\[Z; jydܓzO 5ΌLvVl["\(žNp:<'.tmj.0n N@9gâŜFJ߸ ZԹH;S0#2I=F'F&Wػu6-.aO'8HZ:6ErNf Ho7 3|5~,7Wj֥AڞfqwpFAǢ$s+]՛v 0 -p z"'Z $ 7~SGF@0譃g5ѸҼV.v77C2̒xOQFщ.j̀;K|KSRâx΍\Y?)##1 _9ƕſpsv`F@1<'\nfb%Œ)gz\<FވI֬ q :+`jYtn4.-խK<3$bxQtbe}Z`1Fp3Ԁ@0=EsoDW$VapāpOr8gWkqyqoj\$nnwd z+rO 1<(:12ۭYiop y8@ ѷ+u0b@y'9qdd>s]+ˋ~5kRa O3st8;# 'k_b֬DQ< u  :+pxOQk\:՘\1 ar823i^\[Z; jyd(((((((H'#x?Պs?2:MV(>8^g{:W+Vk5O Ζn} ̨w2èJ?syF]i*٬6ׯs \)_^W{QLAEPEPEPEPEPEPIFG_->3(޶҈5_t{9[dLSP)T+GNDjd8"Tr/աqZɉG5z'rԉqZˉWb~WHӍ?>8mXC\D4ZSS+UU>2c D52>QL#Q- TݍUVJֺc# Dj[>ƪzqR~## Djxn][J>Ʒ\K <6:Vё`O ګsOZ84>l 81Z$6O8>:VK0b ;AӷUs -o\ gڣMhRBަ#֣-.E(-ji>\O<"Gv}M01sPZl c7aM-aL-YJF"3w3HD֡w%#hֆo΢v+r7Dc*lҳjs眍xD'֡fݝCXy"UnXx5TY_Twںa ֩yjY_Ngq(vƳj;{UN# vT,ij&5cRCLij&5G I c^:ljݴs׉潳u_m?G<\ޑDUΕ2q-f1YQPs^JѳWRnvk}^=ع4s0w4QYͳ[37꬈Im8 ?Wτvm?rWC>4W]ama.$BڅW.$2: lP4J.R]>4dWm _'n}*t!'W?@G$R:BeRB.oAd w&)&W qfv'9$1]-wGx.O˺_"%j(c,Yn#jI٫ ew]G <U-5EPRA \E"N9f ($ޣH7iBu -vfCL\6CuyMkq-RC#[YY\kVkZWMNh3%H@D[ t99_:[ɭeg* l~0<2ۥE ݑ$*v(= p5B^sC'5b6[:Qh?(((((((((((H'#x?Պs?2:MV(ſh^wm5?g үaZLG~.\Wׄ?2GbESQEQEQEQEQEQE|gOm*koMc(VLd}+ ǭ&NSPRVrԪj5"ͣ("&PSҬFa8닺5~:yXpԈHg\27"F8щssYLոIEjZ+tFrElTUNjE8;%l}*u`GZǵL+xKjRR tFF2i[֥Vg֤ Gҷe~*@֫+cZFN%žךqOLĜ0vPnVD8 4ǽW9<֡}j& s*֍֎`%}h}j-֒`%,:曼{e8r)D=p)\I=i5.E(/qL,4is-DqnMfj2+)HDs7Q~t"hG+{UINjIrI5W={)cFv<ڻid389&;ҥL*l c=DktJƢcOcQ1R8jHkݴsW9{oF ic{WE%ُJѳWW@2[*.GҼ55Wluњeͯ  䯱OíN]tO2mW+c ( ( ( ( ( ( (9U mlN2A*9A5Kfo^6q$J"@S>ъ2*Tr$`sl  BI7Iu=đy~\2rH=&;=0eăiʎqA>ъ2*Tr$`rRA3[\E:IyV0 ޵`lxgJIlK$~d]ӝeGʲbAH<`G8 5ķ4y$39$$󚎷 OF(Ȫ"2mR ' >Q9gY6s@<3 $߶q%Fmr?tgq V /h>U mlN2A*9A OF(Ȫ"2mR ' >Q9ug4:PMg]oq$mvߗ Fw$ /h>U mlN2A*9Aâ OF(Ȫ"2mR ' >Q9gY6s@<3 $߶q%Fmr?tgq s5ķ4y$39$$󚎷$txY|'F2m#kfrq9Q258\ s^#b\'#x?Պo4f_ }iWm9ֽKs8W˧B6[5擱-{ωqZּ#oe'A/_m^Z-aES(((((($ZzJ#T_?k_[DuƩ'SP)T;GNDjE< jU5Gm9UMdNEXeFr';y_]覯-/nͥ?&Y2Z/,ʾ[6$ 5fyZCw}ci[D-t&}I4^0kfbFjAn-UvPB$"~&@o23xEoG ؋0P8*|̳o ِ 0m-,4[;]-'hIMѯʹ|һn) obz6ثK%0 |džlPɽcsA9z~}gitSܤ48L!hHePvdח6v6]ZI$mʿ1$BO*v#)QtآKym#ĉ.HG.,I ,4ŎȼAY ^t]7< BRlXHD-*tLÏ2ìAenNF:X rb|ApMKy~Zi 9r,V%PsNaqm-HujX)%Y%$Y#݅ݳp0F]P[V[yΒ8A).7@a–`TS5(o-YmmV@9།$rw'"A쬴5eb>¦9p,# @g@R4*5NByA崠asy8 *+_Ey&.,I"_YTTyS6zi[gA2[<6'XaYIfb~4쥦mimnm 'XI^&( byGX6FrKPu6$nOp̲$#<-@1+/̻m<=gʶ%Uf9nx\֍T:hi6&Iky $K+ѯ"6L3!`[ n},ZiV`+8LG ˖0V Z)ԭ.濴#{!_1@#Gls|$&jžn㻓Mؿ Cϗ< ")kGMPt| Ѳq@"ޝ[MJ /UfpBj0bۧlGU0V=6%g0;l\ n7m#l9U;uk-lbKx2mKXG9rF9#rt~!#}WM]UIeGsC.0 gxZ+u,q0@4~C,mf7H l#wSB"#w .6&=g'Jw!Yb8nv\摆"x~UMAb֗vn\H򪳳n bF*IbiڅƱo`/?| 3H,rԦFc..!v Ȫ8%ltymT8.'TAběcLj8$'u i1m yR)'2\ՏYpVfF݂҄8D%!Ts> }o|0M)o2Sb%"y*ZůjgXf2WaEgânѮmoM_w򢼁q,y!k77~y Jm0MQEQEQEQEQEQEQEQEzG?nduW˟uёzo@?A;$*9U\9]>\´ 2?x6U VIYlovw B1kel(b ( ( ( ( *Gg#XAC#c7#9`GZg?oE 3Q6.%OڌX)&$`2QԂet6FS@#s:&x5 JTmFɳ#t%qÊu'47wVB3ps?_[(R$[Dur$#+&NSX4z:M@ Hh"u5(5x5Gm9AQMJH섮iE/=Rڱ㒮k hߡj7eE&1W#|h3V~>>8э=unk=~Uav*Tոbq3q-zUj7aQkJ%޵*5L6"̥OJzTVzԁke3'NXIN;TK!1Uc2 !9=j~—y'ϵV KzsX}}FG8r7jBFG8r*ڑjy&}Ezn5e(o>T{09HOsM/~xY9(g犍F3F۵c)F}j"Y6jͣl}) TLjMcߕ@:3~U]cGZMg~a߹>I߹K]PY/^ҾARuӀ%VsJǭDƺ'+!dұ֩sj"iQZ$qTlj2kT9i5_}-?xS@Oٷ>g2AӠյ5bxX%Q٫2kh"Jq-f-ϽEE89~/qrhɤϽso+VG%}_x8'm9?뒾Ϯ[>=ޢ0+C(((((((w$Ŀ M_'Zi]jz5h^J2<qb06ߙ̧;m2˴)uP&0"i`b_:f5'ՓS"3,+7F` w;dEMv N]N-kQP6KtN%u|䏕x'[w!WHr%Aĺ򽓮Bl.6T) p1Fa's#Y q0 nG؞!; ܶnݻ9]qdAiAw@"7!J1Il3'V&Gw y vo<Qe=FQ{[6Z%ϑ !7'UnC1ԃtFVw-K */*lx'>Cnc2I&_:̺ΟuwjWH~=G͹mP+Fsյ(t },"vLy#Xղ3hZ5.k-Dh.e,Ȥd$#'2-|3Ev&Yݿ.ɒ!j+\&u}RdS"[!AC1Wյ->P6\ ̋28 0`Z"qgƗuv52A&|D#n~pvm _C/%}gc ۉ񧋅@DJG ۛsqqڔ:\\Zi>mf;q&pOʼzPd]rj&l[`Kr1wӴۻU1~-B8gKo,?B0murm8^oist%FBe$|ܺj1+-[pDY r}K7յ(t },"vLy# Kx7B~n۸lǖ#s0䕑W!cQG4.Rql}7sv %&>SNeAik@Db*VjZ5\iz݌KY&+pJq=IsG.=/q, 9b恑15]v'=5m bA> l3JF7qv厏Q6ܣs`jږoko{]h-fupP0=t>۪iAwixo9EsǖA7;HXcZ]jy9彳$ y vo<ai>$uAmgx } ‡7KaspT/ jREH\0fh.aFiˬ^_} Z  rweP8 1QꚔڵ]̱(q8" p$NMuGۭ.>RGEżʱ[,>rذ/16 \Zp4ǣ;@7b{ٲ `; FLB\֎j-г ~Gnca<4CB{MH+vt}8]vrPPGU(]QHrᕆW{]6o:kA]˫=7Jm;wb#QP i a&E˹$M噰w'Գz 彝͜7sku$NWrqOhOޛw C$ncS\*,j+ O:u>M3HN][s6}QEQEQEQEQEQEQEQEQEzG?nduW˟uёzo@9C`MO#Y3?{Y&?^ mpcc5vK=r9>&8zkk¿g#xz}kWֱئQE0 ( ( ( ( (2/ h:j&}:E%`''$քCkoQH8Ppb8_I5G_&_Y|jIm:,gf2`iţфԊjjxjHMJT6s,jMd VQWQXո ֮G.Er&FsVRRqd7(gq^jrrk1%Z\%Y'lɬ%[ީZ>kZ-᪲ffZ'5T=<=+fZW8檇ǽ8?Vf[Zps\ӄZ.UwxLNjjS'Ѹ}p,n4sIڇ0P&=i Cښds)@Ɏ5|s)@M/ZiP$/'皌 ]EB=0L/MDD{5D^^i=kEE <犁rycWy;Ly{dqWD`1H{dg4K֪0t %'ךFMTnO&La,u55uF6&R STYF5&-Z$qԘ4lբG!54j2kDIF5o}-?xA5߳gGkVĿt*/5y]z~!g? 'Z^W:0_I3Qc߃{m?rWwX{?ڶ?%}[QEQEQEQEQEQEQEQEs;y_]覯MSilHʮ yc 9te<窜 m῱UMFt6 ٹFNܐ7c&I-|*ĥ m$в,ˀr0=h@ZW"QƓb;_6'c65]T TgLc`mv2I0̼zYY RLJUvT};pI [YD&"ŲwimODŽ5 LYR3lC5.Tu.nu*dF`(T:(# !ao:d{~W#npnH$YY RLJUvT};pI9IԚO6hp!o-UH*0Mr I\#9TR*fwF8$y[{xȥYAxhIz(oe5$7;ZG]LclVl, _ յd@J8b,['p+1fcʌlsy\ nM{=qqIxd$ep/w)Mt7R$GMͪEݾ ac73=ǭhIkPe%-ĥWiGNh _ յd@J8b,['p+1<MuqRM<8RNrIwF8$y[{xȥYAx\B-ⶖm*8i cUΤBĎ\Ck:Ȁ"qXNW8 99cͷ3 WU7C#'fs;r@݌>&.:5ڴؒ[ck'7PCn*{VX.NAh l$1>rf@ ?0 4$xDZβ_bR'qݴ`Nx~β i1-NnNrxoiWuOnnKu$|$p=*şo&w:cy#lxb1’o fn*3GNr6 v䁻2IkPe%-ĥWiGNh ŝ6kk$d[s'x}t{c{R"پ+*|r|ї_ZW"QƓb;_6'c65]T TgLc`mv2q(((((((H'#x?Պs?2:MV(ſhKp*McKW~*?ti\$\")9&LïzjzٹvkbQEQEQEQEQEQEQEp?[(J$\DuJuAؐx5GldL HSO'SRP Ԋ՛Gd&X fUVbQVk)FLesN9yē sޱNr9xZ׼~9Zj;uކ7%Q٫s^B_vY Ī.^R0 *&:p^!٣4-@&?{m?rW5gVe'Vס_i<[Nj݂txh1A'ں-+;cXga5mm3RwOnI>YT U$6dw2YnQYf[oqn| _jw:Z4ֺ@$Hm`'bzFtWqK૯ŭ@`12Df3&J߉>m cՆy%HȃV"Ht9ր+-е;Hf6pL v-kNBe=R{/ڥ;HW O=F1i؞4; Vnݻ ]qdQ@Q@Q@Q@xW_)j\]Kpۨ!O-3  umK/ƥC>r17Rx_N[^J]SN[E 0Lr7ɓw& HԡP[KDo\'pGF*x<gPnE9,༽]^O.>g-3_rK;7|Kk뛇%.#E2Ac.rbI& ;A,hxFsv\;Nxƞ\mNDVK[!a~_4yN+Ѩ]%'szO'[ޙh\Y ͧf9Yn%@!dD0I ANՐbizo[Nqqr;n7Pz>k97$P"\J\$Hv WFg s[h\O ^^'K4m )]/Enkkuݮ-F "abʜ+rg#ᱩx9fԠ]M5([DyQo?x(|}{$WqZfxuk  p+ƳD<ȶQE$I3ck:=l /sL|` +t>mAпgIzlKs:)_!p- ?44-O:faݷ~ 8814u&PљkA:[W-o"Ee?qhOEI<]\Kqq,O+I;ILmA*1IL?EuxMͫ݋`(bW3Io$!aR J +~Nvh|.s|o#HE?lH9z(S:V, --d1Yh^"nFqtxGү-5XJmZmF̀K9 Ic@zTMZ[Jf#62}*BO޶)>Y%Pa dog|Ϳ{x:|ˏRWZ֚w ^]Y (JH\)^RbM2P1LP @ezcJ1(  K J1M/|ֱLҞEByT/7=jsk`NtP4jq@yLdMQU*&Um]7EVy*&tWgtƘ$iMBMfL1KTlԅDijjZG$)jii3TdM4#sa4դsJBM&iF^4}-?x)z~?kU1h_(v_ F^O^CCT_6jjntCSc[ _i^ _jְküo[joXJȷwn!l!H 䍡kxja;LԮ#x4QV2iF(^r7IEYggWoo7K6ۮVW/ U#Ҵ٬urVDņ J(5j47NФv<̍cʆciiEQEQEQEQEQEgX7hE].|ᇔq Ƕ,WWKӚ v:HL amIAxW_)]CAۤUX KfYH]qz\\AiY[t&xe9TIFj./-KKǕ Iswc 32 ۹u4ۿ[U X . tYgP=W*ڽhž0c0CAiZTv;M.CnLa!<6 K 2춶H\6tVI%۸,$( V ]P{S"s7-Y7bMۃ<3YiImxWx. !ccc4{53:ya۷~+83K-l4Kio{p&!*Q mfP?Ez6gQ`M+XK xb ])cLqWRʪp[77z]Ufa{dmn{&'v 9䃜EuD#hѥcZ2"d'jn#n@!|ng}:++ dY,bs +B(^\YCi<~2FJEw0rxEv #yB=:z+𶑠Y^ϫɩ^Y\AkqiF+LH23" I -^Gt -k'Pkdu*BYlN->;"ͱݬLvp؉yAh}-;NW0=2,r "]Y>aBUXxV8[K5f-W&W':j(EPEPEPEPEPEPInQ|+믍Hw҈1IALPo<x5RѴdJ <x5-Dժi4tFdP& R)hB4]Z pj:cP:ՅTSի9@JKۚíc<5k$w /NާIyk SuS,VZK׭Ldw%L 1/HfDY>^#DHI1Y/=itY2y?&i|߭C/yZ<߭RY2q>PAMyN*0-*/7>[~a0,20j֫4&=kELeU^{w淍0,:w7׊5i'&f'F[0.VFZFZHUHM0ZG4 Z[ija5iӨ9FMe91Ū2h&MZG4)4i V)4i T4SI1l {{ކc*.^O^D|CT_6j\s)5&7'<V׿6ZZ(I_k֑QTbQEQEQEQEQEQEQEs;y_]覯ZSՍOwxc"+[r&EŒA_ixIudԴ^ӵLKk}fx"G[dTH205pp^[[Z$OMɍ0~}y5#ĖwVrjkw)rHH%slryV'4沛N\{ZTs5<);r7s w;O#Ak{N|wy oy.u(3y݌e.2T2Aq 7$uoIj HLr8q PU K;oFᱣ-G"ٛnqsX>ԟO׮m&&ɸ}[ 0[vrNͻoZ{K+wMeVK3lw,70;ݝX,"ҍZ\3ʏvʬÌ,l0+Fo/أ6FQksfyo.!Pxi|U}x}-{IYܻȨBgosө 彝͜7sku$NWr5ޅ57g\k64yV\#m[_*p<|$ZV求LH{2K|r?2dg5 Xst%fEH31^9;W-KG񭮥}fP^)#XHg$;FxmEO,q$gcv8ڎ+1;Qu&֮-cZ c4R9 0drK43#%5-5(\^yy,sf90l{/ GgfWCO+)HKAZ GW ,`l&N?iO?WOs ^H%u]V9t\HRz5sDu+/JvXk[cBPws֤eOOw{}mIa[w@5xuMv-H_7 pUyGII?d_WQy^Fs;}gnͤ Ԅl,au8ڄF$N vvi?j<_0~]awou%ۼȡZemB܇ sJt9?-ui,ΉRa dA4NS{1i2;`2Ǔ*lxzB+^oQ9 Y11>ajikK[qh0W;gsyylc Hl EzfY8N-7JeQ N I3M%LYK&*UqӊT5|Rc%Y1dҟV`dtVn16FqGY8cO~ 78?7NʥCzʏ=}GKفTL?*ojj|ju{;=#Ky4`8fĥQlY59u4[iȵ,eެA @xܧi'F}T [k9R۳ X׈4xrɦkǝt3AxWmo=|pwvYZjkZUn{k5!ec)I+`w9+gAoZϧ_%a-aǗ6#X q}ay^Igi=x rS 5'-GVԬRK^{"5LQɐ!<$q IJkyKir"q?Rb? 9JEFB&;U*.T W_ kH&^ ٨|(Pr3P]?KUϷv m+"@9blu[AģUcla#ɛvϙZb7 8Յ发Ix|yuU88#V!$o!m!%_Ld..`gޫFuJ I.I;ecy=OZf,DPj}1Ele}.)@.7 P 5[8IMJ'7 j]I2H!zA? kjiqhm̨rɌ/$w_Mq\hJ?hQSjȂF|]%K葭>khᦰar<٥m߿eY]`3u)H}ݬ,bF|`x'--UD(g :$ 1'%=#x}gU{o.YK}m)0x29i;oim*hPG;4I6e*Ry]>o*YC@/G;-+UM^lmÌ}n^AbK~"Qن0]PL<ͤoC-&/أDY,0R2P cge{]_!ǚΈ* : g WO:ޕaawwjGkm#0 K*FqZn Kv(5*rKحM;Ƨ:XROu4ԢmFw?4 ~`>_ǵ_V2 &E72C7YOj1+#sݘs]umf "#6ìs]yMf'gC\|v̲OkYUf?"FR@l "ڴ]ŴW[q$7hѲpFA*qGnj.NAh l$1>rf@ ?0 :D*KMϹgKtR42hͱK;"qKU&3v6# İ+9 Z?at1->Ӝc韼=h???}s=:s]`o{r&}R{ialfd8B.Jy0BG|1.5 f>ݠ:]ƒ7nbF㟘*qup>yub cll\s"3`_[6W hb*m"PIS|=ϥ֢MȬUE$:G#Ջ iv.y廂ɋA2E!Jq i.lC!IݚLM<8o#7[dӾEOs=$vU5* b޻LRpYA@I@Ơ#c҃j IrqB:,F`2ptwsN!&V#4ΦaUV4mS#kl(7+T; #I]=G:ʦ<)^ |r|=_=4[$Eu:2cKJwUbBxim6I[{͉YP:60&pV6e.tաiAj:$9+EV_J.b`ݲTH2A6,>s\CX3g ַ,U9Y2J?C.@k0w?'i'fه,")2Cමִ6-{JoXK+\c3BVmT.Zķ ͼw٭܈,l%E#vu.g cÏ7>1zMܶpjvkfDHFKzdNHEliֺ<֮o'Rz#Y $9e cqsek9pG%[ӊMu-f>uB-+!A8>⤋B'[ȴ-Z)'Y HᱍHh֍&w{qv,vmț#DhP<#P0zg}(to5VmVP**02H>EϚ1rv}se;q$+M\j#fq9 o,gdvKRHVL8)"}Q=$oiwFHwMikk4+(XT:AaaJ.Lnl/#exfMu$0ҺI~j -o]|RI";ʤrywM-OGI^}}wdhS|, @Rsֺɾ$Y6u~>1nY/.ZFw;{21P An"Eip5EpV\DeyR(ٜᐶ\ Q$mL֬:ǽ\0R?R7 :>>wF=GK+;>kz^wFU9h󚗳޴y֟ L MESO!柳iϦ)lzUmJrDOLTeF[ޛDQQ3uHUM-M-L-T-M-04ZF874I80!ja5iIMRFI&MRF2I&5I9 M74M3'!sIIIQIL½e?x{3OjfXvT6j:/JѳWR{:m:?mjU'k¶5ke=Š(@QEQEQEQEQEQEQEIuIղdTS#4[Y%OgX#Uw$Ŀ M_2vaq%a4$&ђ8pj/4$OFI0#*S2 ǎmBIy&vY'YN,rLI]χ1xmm-fyhÈDRF+TTm :S}ĐD[̌A33*Y! @4X ΋[|8]͍mit}/͔Fڛm*T>ϯt-&ۃsvBQ8Fn%"G lk^>7 n2ka!kq*b\IiTe?9 TĞibD\BJd2t 4ie b&daœni2z $v6Q"H-U'%;I9f'ryM we1RU\$"haC š{b/7QTyJBೂYbcj<7kV:ך ahDf%2[dnvq {ǹ'[XI.В.xP|dli:Rս^MŴJ%Xaapr 必ntvck;t"ň ̑#6^HpFn=د#6\*43u qO A:/:O v,$̍|kpɸo$G&痋ˑ,T098g vMa,*\l;b6$UwQח\}NfRHh nPvaQqutXgΙB(9r M#*H^dbf_/F'dž00(8|/s7n#$qp]Wx_)_ Fή@S7I@Uef!xc4X+݃U|@z<&Ibh FyE 032Q6ǨjvVOkh%#GXϛ*ƞ\a$(Tpw`@ .q_ZP`t[A:0! "`C29RsS^#,, `H Dj* rݍذu农gc+1i#I5W`ߓ2sVmi6}{=gD` e ewilF/.U2:\C,?[YMc!K7-u:ϗ;AbĬ{ʻsrm*Z]A3]e&@9*܇qܭ45jZntiŽv+٧Y+l8^P<c5ܐE˱KœnlBuqyu5W)wбh24[ LbSŶ8><9=8^9.=g'Jw!Yb8nv\摆"_ؚwo6yT7p}_b}G6vk|S \g>}QEQEQEQEQEQEQEzG?nduW˟uёzo@=A*_iWrܤ{/ 1eϿ?٥zc^ZQEQEQEQEQEQEQEy$:Du }}InQ|@ ER(ZPi 1٥24Lx4)iX!Ӂf4hH (5iAhH5<5B 8zV6ɃST Ӂh3& O Pfx' N Ph5 STӃToԡlk<6]j]ԬhQnu.RRmn{ѺR.7[yr&FwhI7Q'Tu!ju!jv!$MLMOP404UPyjijajijePyjaji4IʠԄ$a*&ORFiHSM4BiHRi3ILX3HM&i)\IJd6)4RS%Q@e?|^2nZF|CT6j׮ѿP?4OA٣4њs) կkBmjaEP (((((((*E5|st-lWFy 7fV$ ܡ%K~Lw$Ŀ M_7א[uyq<6啙b ('(,-4E2ܬ˳k!n!U?tK,e5R2cySkE,?p\: f [Qo]Eg7Q)f眚#յ(t },"vLy#>+{NK>7` ]YbL(pNKeMޝuyk}};DRI$R1G<6.9ϚXb;8(5\9ڡS'Xcωɐ}luSx4kz5x?AJF#p'BhwO*6.nL +vn b8Vԙ/Q L͋ X9bNNy9umIRP `Lطbc? 4tўt;{+ԕ@]KxUUD9$j>jc=Kr)-wUf;>gIYXn]a5o]eɸs#*˖N !WTqڔ:\\Zi>mf;q&pOʼzP꺷p#F@gXayJA X&8pqK xpxdMg6Iqeixk0ͼ}-6|_f1sWʐuF#qҤյ-E{Btyy̌U dUI(Htm6M>٥Ɍ+ vJ(qUMҬIR>b}f2sy\_ڶz]pfl\0bCrs6pHh# DpF !N2I$I%$ ue𽥔*Dm#o4n1)?$\?ڔ:\\Zi>mf;q&pOʼzPY/An.}cv]@n"e;z(](@.wCrGʼzUxumJA]h]2fC% R{GJ5 0:}zIྞGdb{a3&<ɛ*mIcOh4 U$nI;{2e Z}jmnfw=rbr0I# W$Loy ? EPEPEPEPEPEPEPEPEPEPEPEP|FG_Q|OG[}G㿴/үhxwxG~͐KVde R w]^^8u7%9'Wա0)QEQEQEQEQEQE_6vIl̎؞2p +A_-q#ʇ|sWV?]7N g k=N; .dɠHA#k4|Yg?V]7J f kMTQ.>/7+-x}w5u-cj.x?_+-OjĶn}E/e*Gƿs4xw5M{*ˢgxs4'RQKxc\ @g|Rk쿯"~'c]lM/"'c]lM}E%kW>;S,k-|RcR`d 6~}E/a֯{~?|wY[?KY[?_aGW~3E|OBλ [!@#6Ok=2K--cFI[MmK-/W(Z̲lTq('ZeG"'g]lMw5}^%k>;W,k-|RQKc\ ?_Ƣa_ xw4w55CZe"~'c]lM1|'⢊[lr?f|4Q"K+v_/бo&G?,k-b'V}>4>O fi/#6o&΢e^aUG'*_]lM'!+_\lM}E?dxꏢ>.> _ i-*3ms8%5%3Эo&Эo&բDK'ſ+k.x(?7_h_r^_ik7<4\X2HW5[u['g.]3gʉuύw i?w k)}]BOA_Br,.v|G7U?]7H| 5M۴Qas3A|_B? k)?Ms4 5MP> *k.OЩo&ߢ 5M׷I_XyfnoǛn8+(>>xk^hMos[S%Rĉ˖*60*Pvge5ޅ57g\k64yV\#m[_*p>wji FHHXD.p ª`N08-B[H.h ܙ#Xo*\e;32&>FGS".{0؀XU_fN X@$z8W\(3H'5iͅ~]լ ɸzTpA5VI4HK38$<$P>%Xti+$5=fB af!ʶB/49I\Mopʹ EB!3 ,]Hk|/hZ˩XoIJ'Fsʊk7ut-bJߙ%uQo}y4xxWUѾże' K)A"-urIԴԍTDid \ Jv< 'x@Ԋ!?L?$Tw,*RY:YGxyd$lH/Oi{5:H bu,,@lsc94=JmFn3=Z4 3$d+-qԪ66uvpHI%I,I$I$Պ((((((((C! VO"j7/_|/MSKl$hw+é 0!:TzNeE}n]E,*%F6ڮYdؓƖd,+j9ng"K4m(ٿnŚ }m i#B!'|h6rW!qZ)ԭ.濴#{!_1@#Gls|$Խ@-{]J{Td-,# nl&H`YAy !K{Y `ECۑc#9$kOiQo剙c]Cn :#s [xpb4i3i9Dmr"7л't] EP3l~dE-mw|2';}G˶iVȜy{sYunEԚ#,󄝶J#2m*ơp2@t [\K"8e$0ڀ;uxPԞXOq2*k+.|~״mQYҾy*TtG"9Ĉ>h`36`f?SCqi=yL~fM(<4`;p ,jEăU#ĕ@#O/@>BsRz{KmגGۖ#s1gI{K?[꭫Z}nmYXʅ ab+Q^ItXߧdb4a`P2X*--sYh1*MR]B+dK:o3A-OBgrjQkw$!ATÇD#f7cfXs7fsayuk+2nk!GXEiyYA{0N\#FVt OF(Ȫ"2mR ' >Q9i6 "RIa [b3AFPš"I[O1cymp"3 <`$?25Fug4:PMg]oq$mvߗ Fw$ /h>U mlN2A*9AgOOXKcwwyue5kxee2-fWEX1 H_𭯆!'Y^ ;&(.MJ&X[`*Xtp DF[ͪA9aG096ht$$H<.@9'%lXGEW$iUUod\@Y'ەv\|=>so#$ow&vqfgɮOF#eYo1 F$0rdkpyOb#- rr#xyyo|row۔6TQAFە]l  BI7Iu=đy~\2rLks,ѣV[H698R>[Ki=Д\ƾYɄ|ٔ|ʇbolzip,E$#>;pda,) ]Ӆ|'dUo6I(39҄o8|{#hd3?k6Ew'İ V8; M/ #j.Phjz;CnbgRc[4co`$>@;z(` ,H@ rks,ѣV[H698.S="yH',?rHF;~ubНJHl[SuQ#4Ųn!g2"c^S3y< U*]$q Q/*\,ʐ;X,U?tn$tMGEQEQEQEQEQEQEQEQEuёzo_.|FG_QxÿW~9o?JHrܤ{wr|M_^^ 6?j'QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQETsO XBꁝTsܱ@{u3:"nUbB:J@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEIu+ t^RTi'K@JF xW_)[,ɧiD־t t%2 @rI'qr(M[[[K&t˺$knCV2:hԞc-FI'ɷ~!Y!D+_G}R+- [{Y-*˲8o;[N׷)Y Uh9; 6eoX4{iB@vm +Z:mŤämtd ` n3EDŽtXﭕ$MSϾp$Ҫy٘Vͳxs}l(ͣ= wY8D Gp?}4844n 2;<6pT1ܺ۳ɦ\}1 v3|RwcnmvξL."$|铵rpxɮ?WWCq} 0yLO(dn /61dMStOzO}[o7nd82kLP&ڥ 1{rVX– REΡ---lAr!*fb \Jq'$`[RYoKgE2IO}A#Ri ne{M}k k4}.jS6ǟHW܏-1l'9|ď!X,ri1,g ?Nh-t)!:f32dFd|rW7n&qq/@BK//0UJk|=':Ρq;AIQ |\`j4{;˖#ўH(`+ʹ(|dό% F.-5EFVV IM]J& I xPda,qҢ e0.݄t][N>5_Qu7B6FDopAkOa$pv; pno_Y^m @W|zdV_}>_g;yӚϠƍuF=J;FbHZ3C!c(@@@JQkFƃIvemиHD:BK#wT+ݖCG㳊X-$(yYD)I  ^O?/$ s^#bIM v{xk{&8K)[Caw>v|K{巶nt{O7Q|q 34rau[?-~͌c鏺=*ŮG'l]U9bo: .*9*[Ao-ikyX{J 2$u 1n uG)ڬb}ҮK}oط\961/W> ?"걷Eվk4E2;'*2\Mu:8Ѿ<06ypYA+ƭ]gPlJTčظnGeyogsg mD)! .ӕܣgz_C~&xӼŵـTP|i eTcza -ylu[cgUffڒ.v٨nŃ?!,#4^jږwt<I*HÒw~i$`z& ucWU[w"ĉ69.oSUῼxniQ`3ҫ@au[?-~͌c鏺=(}wXMV0\^hrˀ Ϣ50#>d]!v=&ȡ$P6yqgmg5Zz5:nŷbvݕ#Z#|8 -ZYqB3H$1DU'sKfկ G䐐ƈ]Uf`p*M[I}*X?`ηvSs! : ::>FAi'm;yLJlt`M4dQ,̻d\m;p֤4cĺtޟU#+fa#r8PRw{-g2xPѰa8^[Y0]>+ ';q*孋%O[{tͺ263di I8 @]r9V|Tޝ$2u5 Ja($JHu$ yIhrxɰ]F_hɧD*Gis'5N^-(})&%y$0W;F捱l`Xd\KM>^C*Žr26$L6 椖Aԁ3m w"@77P}]>iۍȅ P-Wմҥ& {{7l72ạ̃dOY?%yNuݥIԬbn  QAN:Owv[Pݶm VXYXב@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@OG[}G?nduP~|;*ҽ]yUu-l{0|OnZ޾!m_Xy>poqg?G.: %⩩h'ڔWsTsTX: %>5p?40(5x<[U!񷋿ia7G2 j_muKS_CN 9Roӭ_*5 :?!/G2)`dZ?/Qx\9Yyx]Ͼ/L>5w : % gtW_x?40k5KQtWţƾ.[U/&.[U9Oh5 : %|\i?a/G0}E|_ T|WЗŧ5KQ̂4Wſ4U8xZ?BOFoTi5KQ̃FOѭ_*?ѭ_*tW Oѭ|U?/ѭ|U9O(G|\?f>_*1sU9O诎G_*|[[9%%-$m.RU|I_TF}E|z,qmku.Qr3+owcMd}_*(__Iғ_ϊ5_S[ž)}o{D>F}E|)KS'PQzcPZ|\|UcA_纠o kY|Ui#>>+y[TZ^aQ_]smhKR?3kX=F}E|{ ?fֿ>_*x`Z?/GAϰ诏G_*h`Q_['ֿ>_*F}gPr3 +_YSv. =fϯ,h8>%ֿ>_*ͭ|U#>>ş3_/Bŧf]g9 g;Y|U._*jٳ+?JWK|U/%^+Z?K=fϮ译GWĚQ G_/GA0/S@U)NJ5ڠg4Wg^)_/G%^+|M|UgהWc~+ .Tx8Z=fϯhOYKP+`]g9%~- .T?Y#?KQPr3+)|u%Z!x5T)%2$Go~sl_wU"e+ [vc}6>4_)j 7ˀx~OaR+0^NSmGNӻxJΚ춖D[#\y;k8}ط%eJ g#ެ>.V 0v rH o`` Ood33Wg\sǩ#-Eޣv6]\ɝwl ( +.-5-c9`kJ0~[83 C6z[ ai8'#q@j:V-aue3 u䌀d}Tz>pGj kH'T\g6[[XI#l!KlF}"0vjYSVMcO Ogq KI2AUC6AR܆<.x|7Bb4vF262f$2RcW_\,eZVRr~c;q 袊((((((((((((((wLX>O IeuYP_5d-G k4{k + xos@хUר|kmu{8M&孆FWb֍{Vw4['NѲu EhN‚<tH+J/i61**.H)iDc62:qXb yp4`EIClfcDlX(6g[L+Xx 3\ñQ#f oOƟ%U'r;dݜ?Jp894ùi䊜^ǵ8ppqG0(1/Om@CwܔLQR, zZڭ8(s)PӼNaM` zSlZNASF9VB(Hʊj}LxcvsK|N0nbqK9J$njS QS{T;P5i}n]*@1HG*Kkg9L|$2U;Hz H\jq`so$SRlM$e}dؤ[XFnk@ۤbm5BqE/[ .A݂Z|!%i N=V‚:k%S[!YCv>pj #}J޸|GrZKx@[QFbZok\Bxkl \3Ũ\`i& Z~[70'$Vvr3Rc7x(Mq! NnF:>0rd~M:Z =kY-ici \E<šl g"2MB4rG\[GV^ч!1M8+l@@"Xz{F[ebTFКxۏj98Gԋ=4{F[' ٲ:*' 4Oz= FH-ciCh'֗TP[5ׯYTm'Y [KڏhɒH`}ĵ?(<y{N1rcdbaNqZ.w`>2Ơ?*^ч*0V5'Q)Oyq 7M>o1){Nb>G/L 0'8M1y_֗cFG`fx'Da@I1z9b Z>s+Sd`(7ܚ=!c`[sPk!&,-$V!u2,OeLMAS3bvp/A$瓚f,dk-@pvo,1)(둑ymAܗE/%fu977sOu{wUQm˴IªQ%$drh}煡)rrNIJܢ&SYAfֱy-*+[-mSFit]HV`Z(6C2użwIR5!1e]%["* F dqá4.äjH H;|\*Wi$~7m]K?7m~9qjI)_7X,&/-$n8g{Z]'yӡMTq5ޟ[A;Jʲ0si:okq{AvEpP`r=GF~٦^M.sAGFFAe h栓kao+ds  ^t+6;Nt,$[$~[SFJDd]71=y7w6pߴB9]8lFzU.X-&;ʖ8q$!'Px~ma/.6 nq~Ȩt"K۹7j6K+HUZCKO3`񢢕ݵd<Cy^.7ۍx^yi7%׵ [oRI5ӻB(Iʜ9ҋ?}Ŗ[OvdѦl XS@PX2I9 á… blӍۻ: jdV[XTt4YQVc1ʪN>&,zmٚf,,nk$ag8Fa 3\F-5x6q~e_)x};='GWZ~pd~*Iː6ЃҀ=2}j͊EcC' h "8Xϱ.C( 2g`[h)?soQC#bs#$yubo?f1cj$~%ס&"$&MݭӉ]x9#^ =( y vo<ym>&nm:}>i J\{l`]|1&΅݆.{uhk %%̙7eUIxRYdw.M7U?bp<~Lm^ҋ]wXgl[{y0:I&㹷099 #մٴmfKh{+-h*Y# dz EQEQEQEQEQEQEQEQEQEQEQE /r2"~h5h.D6Sh9 2Di w{i.订eVJ~"ӠcamC[Hq e^9VR;  u)X$j5*Rvq84)i'ǂA4!8RM8 hϵ( O'(Wv枪 }KaaN )hyr)\2> N <\a q]qP-➁YڕVS1Nn>*X=j\ 9 ]~UFqϵ Cx#* L~W4!B`{; ?8 RmP~r}\U[E2nUޥX1q4;&?Z_J*V!9(F"O Xȣb}#PhfE+ܖI<ШQ lU<(P7PqH3Hgs%wT,vytb很^Q6˴j2$r=v3#=NI< \Tw>܌u#wK杧#D8U*謧zƘT.:╝݂3{crirX)\c8I8mz婑 Ga!p7_i&69d սMT(rF_]楲j㍼дbzA/jL䈶|ؠy-K~9uI ,&_1tb)dE /TձN1PyY?M#2Rq` wn8R:`PfCr:5*'Iڗl>U<94D7c2\L "OaNkyO|P)8Sٰy'HcRԛ! 9ڤoP +g)=XzNaO֦2Ğ1c񨑁ܿ)pM+Y6sO<D{́B9R-zh-FOe* 8Q? fx\;Ny,J.euX.s~4r=1aJLXb laJ$ps}*/6@0G7rqC8;$~4YL`9pIq9?3wAa*ߊR [?F #."QSa9چXS8M`JNA@`ER6sNP : c9.Z<c8 L| RI4 qE*} ?9T Pxo‘dڝO֓oh߰K~t?{& .O<`zeӖAӒ)Xċ A>5wq][|$P>%Xti+$5=fB af!ʶBj?3RN5N]`$(' gW5w^hsx!Odekh.BgA&X6˛ >Vzh Fh3FfKbLơJם'Rk]>٢+VrKcVP{# cge{]_!ǚΈ* : g 1SY =G[O-OcmKG[Wsgr1RŹj&i&=V\8"\H$Pּܹ..-ΉyO_tM!0A3Q͢i6bİH Y7mvYg&×F4+ c5Q r ccȱƟq5鮭],BcmO1JеymJKZ t{wY2(#`)xV?v1]H饴?*J2?\[ipiH#Ui@00B8a_dG3}Fm[m\|\i'F}T [k9R۳ Xׯ;Keݼ[:)dmsJ_ؚwo6yT7p}[7Oi<jp+Xۓ"ȋ vIֽ66> om\ʋ,2GqQšk٭ZUn q$p$z @ YfTJRP7IeyyGqy~>/{duhV*IF s29ֽ蚒z薲5ea@jcjC_}M%6y{* 62ǵX"ekyR}g>^ngZ5 QT^XMr̫\ IL:39-Yʖ >ݙ FH5ۮvgKx-fbBF2`}("]hJWOni%-,c%r-JV^[j-mi# gEFЮ <}y%Q|3cueO# ԇIԖ/Onpao-\#TŀhM-:}bi|5q,R:#~E[ڌiy Ym?SWB!_Gnq_>w/LZ%ե56qZE˦ܯ)L2lhty-'"^M2őq6 ]K}g}]Om\ZMJX]LĊZ:X\2mLtt (4aHmfڔbXԄ$$dlQzoqx]CKw5IIvoBx7; vN;Or6YAj9ǢjH-b{5Jբu-ܡ2ڤOAh>OI'uK]Kl)FV%dݷ!m;q ܺ|*? }uzgZI/.t8tUl o!u]< 8$lj_~(JO/'6 _MjYukF!D {AnJ~bkS\mTK#h؁m>_ǦK6vӪn4$4czd-8#ǣ+Y^&8T(@W^&1p9};Lm2rKlR@N,2@u&{͚"[Ug(0eeR:EI \H"WU YI5&է-k!Fc 2 Ur;s@tUJm.mR->_dk ^ =je ax^I͉4ݹǪmoCRiZU[e7($@˟ k1xD/-)GEp>`2TuAӋB'[ȴ-Z)'Y HᱍH3諚f6t4jo=.Hba}23䲀{qMDE;9iDi97D"ŎԋnUKy* |@m? R3mV6 本?Bh6cr4\ y7rƧFNBAT R)e;\1.H :RX`S\xӶM>mÔ0#S˴QRG֡x.r{iCa9 (* EG)~VV@}4ݻOII ϶E*TGQ r:֕4Xr@\m<{ Fy Ӟ}N~AnUA?ZC.")8Kz T})X}i(9$5KsN``4T j@B R>ϵ!JVђz?IIҋ#y{hh(8<0 >Y!ϾEzIxyP h2sTO fJ/?Aקy<06m}˻e4t7 )Lw"#)?\Uט !XXFءk܂4n،:8'/pz(a<HF=E'IȦa3E!'<`'iv|Ϲ5ʃ3FGeP;(P9\N2nM`Ȩۀ:ԪёLBB#i=@`)Uc"*YdOH5JTi$*=N{cfF9Wl rW/iv'~sw~Ew]xU4X͎s{`aI"[ct"A'fFNE?x]o[2vР8| N AcDռ}i_滅//Z+}6 vMsSOQ9%xRaArZ&F!-&͢MPj77[o:W7~wBFG_B[_QO8?Ϸ|\xt);}%W/gcW(?h .x}{Y%(nQGq Wmgv5u:k+9u H;^(aF$psU 5} "*3h ycBr0 ,Ly SIɥHFzzҸj(zi'LB( Ǧ(4Zz1*2#4r3H4q" v+n?tdpi|xiC z`+^`=NqU9ڥ\ [ڗ *m2RP.jt P'@Ji%@==M4jKʏ֑,2qBFG%8;"AJ~Ksf&6Uǣ`zzҙ1ژikr7a{S@s!Fta@iM C ?ZQ#8{%ȣqrOɼI_ΥHr sUw6~ Ђd:xSx # q*QO U[6*qT v/ #ܓMn]}.U",8ٗ1U::,D!\[lԋ(m*4ryǤQi:q+,H[ T!gL\aU7VYĮ'-TdQ] KԴ"U;sAP[g=pOˆw `8nGߊY2e$qJW8\9#Zwړn<(BC෡9A RO;(O,W#pSp~4 }$qHܼ'S?Q܊YϸaD8W<ԍ;X)V % Vp:S. ;sr vٙN;[φZ3 Ēh!5QbMm2(,(0֗\:gt{ ld]BfKKnvGl0(} o^>!4.4G#|1 xU!uoSR;X+[k8:+yIim"[FJ=#ChtusML#bp\sEEwW h5H 2[0x C>qIPC.o aEcO-m~ ]6\\m+ӷzxY X#c%v4:@ |{L>Q,SYhstWIƐ}@V3K=r述y&s4H種 /ǖ3G{)`'??D=nGВ6~GM!sTVKvܓSLH\cryJR,fn<jo&욌m#HH>iኡry-a.c ( BK#ҩ +~ MR(Rfxwb dY峷Oo¬bB2ǷQqXmcʴam~uo'oOyֲcNǼ x'(kԎiHd ,ӿ5uYZf!I[zT.)Ew:\ZMA$Pw69sJR{y?.O.InR{Vҗi?@8S"?g#"A=Z~!z+['g??#2C>M\͢ONr#fU4+ք6YfXK*F֚{ ţ; XAyibI <9E1*}y8j^ Hyo}-Ku9qX计|2_D j)jAW#'-uwTBO9E m~MORKvM`N DomY{U!1GAc}/Or%iso2 $|rqȨ`#Zofn\,sWe7++*EkIdJXQF>V~vVPİ,"m"sN9z+cQ+ ܩ8Ȫbf=}W'u y@`M8(OEQN{~u }H,ZPH:}~'vm'9#|̇Z{#c)[qNnrO7Zω-k:18 g$p>qVFs5o)NG]t4QαQO#m` څOJ؏t+㢧R&9itO{K67$o/5 ,XK_"ȋ6?EY?JA[_"[YZ?sv8(_Z=T#)Eu8]=dq4c'^s,B[;|%N>֣ KZ}O -8m/?Oҽc B)~@=p֖Znueqsv[1 FFe_}hK;J6}p?Ơ{Z28c u1XZ"ʎ`sPmlM>R3RjPуjs~4 "-чҍ.yz3#N6gƗvg^Xs<.>pI;c;s!KPˏ9-A߭=枠'QH=$;pX(IAFOg?KOOPZvF۝JlG8'$*fnH~(m &;s H=*嶁\` .*iH۽隒;#b))GZ]˳ ZUPVRc ϡ'uK3?>9f_s)9Ge_JW##/4W)_Ir18m r?ګM`Q?DEy>u6`R\Z*bFa { (ld@=?:6FpOҢXy* HIeG57iP9g>K#5/n-hzy Gy27yK`z9?Σo9>?JdT4mkUg#SU'郯Zh5'-K)ʂ>AUu}TЖZs.K]_Ȭ>|{X`Ic+O2Č }TgU' WJ8Ji2c9[űJTb9?f9rc"% dƻ'j5=cHNTA5.)yVYF pĮ?F;Yٶ`z 8JS>eՈZ[SVY]Ԩ(iV&>i0#P'upp>U^?*3 Tۙrպ!]VWZ* C S#:z|L;b$ Ü}M!d?CQSUSn@"TMb9$ } 4 y?Jh5&(F ]dEefVS!eq}iɰTW3"3H&av:Nq"V4"g. EBHGX)="G0WQL*A=LZ8 TbFqb$Fy4db-HƔ(95VaqNJirQOրB` p13l{RHG;O:+:۵R?3lIR [>b,p(_eKY STaz "27P0@'|d?˖`j]+ Qh(xSN `kx5 A9 qREmȼw1ihv+_i3.H Hua4 @: lLG[_O9Qc(w63ά%jJ_-MJ}DL.GmkQ7Raw{z9n-Dqt-PXa̤3Zǫ 8j֭Bcv'3"N gQ D18$7<Ҿл%0O`8!l * H+f*Gx+ek& ,>HF8}*gs=*6x 8W}',rj8'S)(.V>J =GmYb[?qБKXXs|ʩ1Y2J}= @fRG$RF482CHIN8DA}v]E>ÚV!>Y% h- V7]@+ 6FQQT[ Uf@YqRV'8G"q*֐%gAHWFmn RymmU,Ǘ#tt{eܪKڢF@6@zI͓ #>}ta& 9ޓtQvnI8~~oƺ4)dYd*><čda2+wv6~![HNQHnXG<-{ʄ\Zq֭5 A$ K;T3g'8S/NbT7CV<<眃\עxJύHam3'vNVH60')S:Ԝ8!ܼ氹aO 74Jf#d ҭւ =cR.ADU@y8MIi=zlcZ,/N$>=zv/,}5nowo_qiw=:J'&CfkM)ox83fu،iOLkO77[)Qug}?QfW29sL`q] Ux/՘?G+9yP3WAMRMՠiﱏZK&  @.xIBCqgv&_ j\ZK]~pԬ=]QձM> 3Դw0S|T:BGՉi=w)co&/wRT}jk{% 9r.,jTΧ?"Sxo1Ak|d ,>m=+:𺍷֛Kj.rm3M*Q;V5ڍ{ΛзSQfMs5tK|B?DJi.wg͌ Odt-R;YwlMӵ>/.oΟCgZo;45v44a1k͸؜V59ims]촧b&vG2n7lIt%[oj[\4÷k\M.U ѦJ֍o~{֫ۿpw?OAuH7'7UYd8jkؾFI)|E rr-I3~çY?џ: j|bZ[)2gmI#5y)o35O?irh 6O XyIЏZ WlM*?U'"X pH=̟+#x>~|gmo&Xlm)iVn*O,V"Zm>Շkr?&~=#'L]O]Hpd|d ,AV٭~{v?vj:{spHc#0,M8jHxdD5lt[ڙ=mO ||Cuk#~k7i7u1mj3=Pռ=woR99TVkP-G `RL!'4=q4 /w-1;xzMfls#NN1T86=r/m 4 ݒqud]\l'UySl/ }kO )_݋7nَ4oEwA䕶u}b0IR31R2F}Zb#k6%Y S >lcSRcG wtNEck ިkTBxfmo&빃 P߆M;؛p3k_ ^gLo&f MO$ >hW=) o ާuMF~|Lo&Ivdsǜ d-ԌVyliaۓ,y22~TѾFRDֿ5<~Ptd{rSǵD/ =kwU=ZYkxR;s AY I(d+瓚ж{֓@rxPzϏ<5xF6zOEPeG{cGֵW2}4pfRd%kĈk>zRj(cQK/A}qj"kkn_f&RlcRv 5\΀`u<*n!ab~=9 }㵭aSR!37:[X,h{벫?w;5PNzSM *;be `M3mCym2qrK\1DR Į}[·x>/J`dc.9R]IC NvƤvSV]{n|~{jX|~no&2U6df~syeٟv"]_ZOIs]t¶X~bP 2:|/:wcwo&P)Ǩ@tǚW,Tyf+^Cԣ -w@F@'{<霼%gtTYǔ|⺙<3|I"k3&>8&{<M}2نƲ}Ԍcu'δOF/@dzEɽ'ަIKwScpe NzSkݤ֘kY}7?V/8>_1׊υ/?ywhOP8M70m.^Nc(qQf6h%@7ݵ\2x=xLV{fN[x-1%]ӞA:/XWbpoW$hB&;}*Vz;L8b1E?T@Ic`*Đ囟j)8!;(qP֤edܡ5I 4\VcNR4 9[ҚK8aYFsOKsHH=8!=:r8#OA4?0 fa'ùHJi+LLM?6xܩ sے}* ӱ]OH$8]MQҳl4N@Ӣ/4Z>4EM?֩WHE1iXh61F5oE^Ȗ`3 ՉL5^ZL"+$o zn8q+Ynk^Y^XڴY͌o,ǀը<;=l!*Vޯ[dpi ێVT& [B .nVhf=$&F\Gڻ8ˋIgh&"}n[Yt"U 3=I<Vu˂ܯ Vu֠j@dd9S\ùp] YB̸:#n}7Q Bs#ʼl[EI&#%`Hv{wusK ɷu\V0k^W3we',X Iҵ̐q<OFb;s)\`DXaK1Xz7~ y>_ itch5)L `}8~4ɮ'^%;|NF}_s^$6eSI0Q{Q{Եl=1E9GTe #RI8붛?0UeGt2#6W織+B}+CEXnGr*;Oָ0Znɭ>gN*EGN' q$0$mA TU&D# 2qh>Ku6% p%ڊ={B)$$}{E~p\\Q>*OM̫1)eԣk-yF9=)SU&BVn1w:?nȱDVN?hR\:-1v ϫK%\Ec@x [(gZ34F9\*OSҨ̑6)`S2JID*O4\IҼ+6gc5֟q \p Ȗ3є:5煓PH<ۋ(Yᇿ̦j/b]9F*R[ZX2=jlb}GMlVP܌q*@8}g}ۍEln ;mHc*H :#|o3#85PtomZdkh,$&.U6'[kcLng0C qHF1YM_(?kuG,ks SivK33a%^\>Pcr wiYIwpTPVSȔmU8RPj/W~uj7sqn`\ 5}~l!:W|O{Ȕs$y*'EEzQ\ބ7i"jR23*7C6&1`18/'8O?OwpR\X%L/r(ɬ9e}EPfh.LH݌*_Jx Mm\Γ"?# _ιPwRX:XyԚ֥JFpu=E39S>I-c-32ʣ\˩^\>V$⺣ntGԲm%S[h }o5E '5ZDRAD@FCi5RT\78氩T8k wtsM$qtC֪KɪQ\uoG*yDpp YUVQ[87fmIgo,@ ϧNMu 7#*+˥x-\6@{:1[XC6C2 :J5)תEVug.m$ !s/_𨤿V/n㿕fktq*݅Ob.u^pnR)8}oQ[Jշ .o-BU]+P;m&HQ%93V/; H1 *1jnP\"B }Z};c u"Sw0i  .#np9 %7yF ժ.ZK3'ۃ \8ՎI$Bfc˭  j))ֵ=mI|@m1d}zS#Ǣ*e8[OmpHQT.$Q+Ѕ鶊7V"L2*5օy=BΊ>aI#ڊxgf䕹.%:tvaT;جPU5X;hþ8Ws˕N|6KKZYC+ml/$=jkl&7N8^3?ZMjm]ά$[dJvmr>%-)B+hWUJM]s;W r qDPN o}x1})a+rGn5VNՃC+D?' \7Fe"o-y|e: F11uZfxQyR'-#A#8,gA?yOjSJBMRt}֥7eLn6[/t3G-%pT ö>#Z4y"[3UC8) g \Kp$cԃU|@zE$H3_;ł{xD0) !%! `VrqCc)c꧱,>*4ehJ.ULTmd&;GaQ}5kon#; 0&tW5HI|5J]G cBbYǒeo9}\eS~wb'R.%,m6etH^i0^#T'V<3 봃\ƈ4=9I1WbhԕEV>/MѨgs]NIkK??U.d7u@9+"~k(#޺{yW<ާ}V1~9`&/wssW2B\^?1[א a'F2N=d3ͼ"B;bOIZKoĩh&x{y|h>T6-Qsiw"/f"-Ԫe瓎]ktk{r_! ;]7K张1rGZ܉l-P>~TX8U^}6QŵOvi#jNщ8r:%moOo+n8 [$nd;Kz0j֙ɪO2Iv kVyݓ=,D$8:H{#>\F+t2/)f7b#3뻬}9v-m己#K89s[TK:Q]9 oi`x^eGT D!$e8<AnkZU}n[%`9rѯl&Y\HHk̭ J %-YھX0 10ϭKmtY͵+Ɵq8tbG4*1FJ-Wc0NJA\fB?4urzJuJ;bdGFXUmk%[Q$L xzEh'b|ϦckzG"#4NUn"\uֵ59(z{WgӃG76ه5GG ~eEq1A5ۧDi-Pu{YTtvO}9Q*UU"z~% L[D.˃ .BL~geM6]KSy3[l1[ydh呑QV(*zSMZ]{ҍX_#9s1'z'x W9w0aR26pO5?<9娘+B[ݸxJ҄$"BxbdӞe꥛pkNʇqEe$J`Ri K7lMhJm$1̥NZjJNVrm0.-c~~#5Za3R[ieVbK?7xijii,0B9T-ʌvU)M=:B#$!2=].InRl/]YM]I: dCg$0N=@&;{QjZ<GCҡl/G#gn<+.xnťDG*l,G d*%%ZQ&Q4;JV m&9T svVvڵoqWa]ʗKAm{~55x]?ጱyb(J뱙`w؋g%$q*g"y2LrOңPuC&ɧ2;BQNQ9;㈯ʃu"3Ȉ]w&zgAm<C*\?hppxztjZ-WWdfsJі'-4)(diܻ{ ]FH )9 <(IiQIlȍ@Z_ ωo'm&qֵ?lՠoB #y-F4CHh e1 V _ǎG@mWLF}M;QpZ+ q91=:S|u#Q5f޽fM ,M?X-ԧ#xgmMO?]m|A¾s??׾K{WGe=K$\rIlt9>j)uk ,}B8ʏ<}J(=JqvZ?ۭFKh! s?<>&7!/-BƻB<#Y0TWZ vm6.1zb  xWR'+^ِ\O+Z:c$/OiMF,q(HyĚŰF"|r ?ָV{+y/`,DLBA<~u$.I#5 ڒBT`8 n:VQ%o"E!!@^ T:կ؆m6=(v hont98]YO _7wTvZpԠrpg>7T>4ѕ0 #x"u{O4Y(Qq{pcZ_@SޏjoaƸ?1 #No8./ue\b6=SFV6*vYJ>Rj|{WpSB l0GT~!?l-+gX'͚$ҟ$g2@W 3Y>]9-4P QxBeNk۹ͣMxjFI#r$=VO,f9R;Z:]Zq ~[kkhV`cFTTu&.KƓX\B[֖R5Ŵ\ W%a:3$>&1s[p7ZK.1.C,)8,GN?_mG*8ޞ|V`i>j_7!̕~V_R9 t4ۉ כ%UnNz~՝pIn8>IsAj!ҍHw6Q#~Wé{Έei[56NY!Gc AJ-=*Tj s%wn us^I7'c,6䐅R9 rYXmw O^ZՑܑ[;V(\'pBQn~x B}kE7(ReO)79;s 8eRrc[A{6 iTGǩ*Vtc9c;vX;tф?ČW#b5-Ƹ=8ųmd1 փfG@#aA.xVvj,tW ii̺>OoO@My OO2 bl:ۋZG(ʓr|WHd8wset>^N;6c%[,k?$ޓcQĒ4!i9ws1Rdޛ;r|nSB1w8yV;U2n3j\iGr& =B "|QS&IV%-<1ZZtDs?EM|=yF->˵E]X3oF-szzkDpF9Zfe^yۃQ*ke;@(3m8iM4ʙ0iw8=M8 rx2iЌϭ0q$S ;:zԢWYc9FKeF>ni82֙,4ۛvs، 194$H'+ԬS-;4l|ʫ# )>R0Sʮi? #mݿƫ= 3#*z~0f6n2Jsj$?Zh«ȼ#C ^9\`Ier?b={f G"2I t 4;_GfK }]"I' ]e  u &SmRI Rdg9\:HZ$kuɪjeO̿ui>VtKe ͎,Č9Nӵ P\}(ncՖtfMA.X%܉*,yxnyosI.#Kvi="h+:cCïiCsAl1 =_SUA+-oCu,KGA$f@2e~^k_?̽o0y+5-AM>mcY0UX`ArOcȧx^aoemhA,LI;dWÞW(R^jWEϕ&I;}7T2D58e?E̛:Dޟ}44QI+"CQϕpdoYu]p>U&j(Te̓H-Yjz3NWiXo,BI%0 or!ԯۏE]}B6r~u^} (eEҟ<(z{R"q; j(`dNchjKb#cJ#袓6Ҡ`sTQI NhKsZ%0h#8*Եn4+[kf _,{p}1Z-n6DĮ}+I(pqɤ?("Sth 4sE"P}@@*8$QECla<'=袕SYTAE^T>QT` zUMJ+a(R/\b7=(;^^&Zhu9I2,nNN7 =z]ҀG2KAt ګ=?1{ˑ4= QEdZQӒ|Ɗ*TUʻ4&ANnB$18icEJw J~dEW${ >Hm)rb1Cof I ,)u Qd+nϵ!Ka,J(Ɛ֣X9袐b_[[O&bHW}zN=ΩycyivۧJSِlc:c(lKZڵ9M!̒QRZ cw֫_Š(`N~4;I!Q@ 1JvE#9Oi#֚}.&X#g pֺ6ҢoaܖG lQEtXϨnxJMV/CDr>!ѼUk0ivͺ2I[ 鎘kq5ڠLJgؐ([BNEA40́,I&9QE+Iqڪz(<EHS束PCVyb691GB{QE;pciT#8(Ԡ,EV'??ƥ6'vBfYpjq?Z(.LpyFኟŠ(c 摲OΊ)tT?)&1yTiIb)݅p0^xN$Pi7Vv#ûGE8VdIhtZ]Π4SSM Hv? `U1$0EL)H8N38h $1)0 hQE g 4Q@@M(E]&6QE#K;`4iҚ;E8(^z'Z(̀FQEb8)P~r>Q@ ĂO4J>S!cSt7y#EI96(O9TM1c=}P&¤]=q(5nNIH$ph ۵:MӚ(@`sMފ(W99<*4qAmEPsplash/docs/figs/sodshock.pdf000644 000766 000000 00000212644 13261626263 017165 0ustar00dpricewheel000000 000000 %PDF-1.5 % 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream xM"Z~#Sq^O2qGC:'":dYmdzi܈i~iΗ‘3#1Qj1Dʼn8ꮍ3SwuPGL\2Ͽo͑3Sw>:9 &ΨY<#?s$9z[ˑ9s{Gd^ΑFd:Gbb?^?sz2{/}_<2?-x?O>yٗlqӎG{wEMq!]Aj/޾;®Dw)C 2Ox>OKDH`WyRWa˰SeK`@2{!:G|m# =:4N޾]nۗ (e/# AS'A;k_^_R!~;EMQ 빿I?:P$+Ow8G$|)zo!o;=޸uȥ۹?_=r]# =<6Nm:2 =Ie{}LE# 9<9/oų<S+mtܛ3_S.mH)<7)n}4/Q"\CDSy/cߊ$B~"^ 6kNa \鱗:P$aKq[DH("^ v~k"(_G9ފG7y "^ vQMQK\Bϱ޾]o.[iNn{~ ?r+뢶BZ N75 #fʃS'w %W3`(.f'RcZF)Oro=+x<:NDSy/w3(fP,f')ey}DczCDSy/5 "$J5 )k vGeg#׳zbɽ+VܻQm9P$+N!ުW7 T]|TPO"gza-|iB ekR=lVCu{@,_q5גt%7>[ZF؋V TH#l zMgV=jbY={|qX nr[S~/~p"~(FB+nr[Xjp[oýü~ C|E`7acza-)v_/~0]d _)b U&lL/|¦YD}Ğ}Hn{8tq']AQŃ1lɮ݂<Xӏת´Ÿ@6˜#߯_= J=R-}ƲjDU#_N}ŴPWX&.-Q?.Ҝ&+ffOv,C?b/)|7o19X&%7g7',=9X|Q-]l2v *=eG^]; ʀʶ飻߶'oL[ e\d,o1;? [\d, > )~iq0Vv7g~^r(cZb(^sL .1P* -X\d, ۤf2kb.ބ ᷈ emowy0Z]'ly;Th13Y1OQϘ99UcPX[1WL[ u\d,/-~`ɼ >OP}d%3ڇl3ѷ_8AO˦s*mɌiq^<$(^PؿAV{XY=1-n1.^eBaYn9]lA7<\1-n1.l7 q viquk.2#\61`WL[sL() |(LiLR7ѷm] eBz5EʬȘ~ .2 &ϛ )6 WL[ 1m.2 فb-dL[neB͗a97_hViq0v6˄YuIl*^9" {E[P:J#Ϳr݋_p޾T׹tKm.G\gc(*u.?-^@a2mҀ[NdL[#|1 “dv۩5Ș/ +\d&^} _5 ƟǭGp_N@`-i,Jua?m8l8$U5x毘>yHP@9 fa+_|7d o]R3F"Ͱ d}) vRJ`bVmݾ`Zb({3$˄¶{DՅlXd7Ldo1eL9X&=Wgq`[x .EaBa[(%yήXsÌ3|ӌeBa` ]gchganqe9x:b=uEp?^= xd7Cƥo83.%0.x]"eL[MeBay=HO;@6'X)7 nѼgo'z½f,=ix1|v-M  Tl5l?2ɲ@`9X&Q91fSmJ h~V,9C̀|(fy .>⣋ ф< ,[ 4㹅 䚰팻 uTPnjn3(=|2-›g2, 6UQ|J7|Ĵ@ڒ"cP}vwQ[W{-\dužի̓dmșoһєv4l<5{ׯxW< tN a~8tpf `6^6m.2 \mB"^-r)Gxa4*\d&FN`_py- g 0,Ԫs[ŽDxźZgg6q97{yY-niN3 G5f}`6 4˄–7l5bag>/#᷈ )l/u<9Cdgm08/WL[ ~n1"cPâ(Lfi|f1Ba1 cP;5Fl}hjiq)pL |l8[=1]?4҅4˄¸89 (L& 7@yd \98L(k`.8, -ŶKeU뼮|I̎j>ёb*p (N ( u[Wߍ)l $RKOȉ_y-bZKN'x` -$c]y)5eAU^]1]O#-¾P)\d, [KC lznu0-n1>Ml.2 O{prD'aPֈ'b憵 Ӿ8s0keēK3C)tZuh@`}bݴbge2(?'6g cpz}nq49x:mT$f;ܺY];݀9|Mg0 ZIDm gxmk:7vEX=' }2$\0/CM2wD6o'ypp˂3cf`xcjgўm5+bڂ58r}bmdGrqPKpr+`'6cZb Ur"0pYŬ}O -n1.ӌ5~8=n-奰1S%-n1^m9X&ƒ>'Ke95-c \ajѧe4(WE" !c*׮dpY2\s@z!AP] E0A0\bSΈw-7$ѓ !-6F/5wb _G*ӌOnnƉUd0-ֵoX&f=7CXLo9z1BnUA2,Y, Ŭ ჳ"c@x4)n WĀwĴ@eBa{OQwUFqĈipyd-Caуwr:s2h4cPؖ9 Oݱ1>nnq~^8X&>m])`,)a2mJr'Ea2:_x11-n19v˄cLI&)M-Ȟ6j2 d/uXY1ՆK 3lXsU"fBa +:7Np .왇# O\#۰qeq, Q4˄˜y SlA}EpeĴ@xE2080 B0>R{Ĵ@xG@yHP=TMU#< b yھ` '\x7` Cp1 (HUI\Z/O9KȠh\d&fmAtx.ME enAU|`7_CY26WR,@n K@:fx\˜Xvěbd [ڧ"ZK\T%Z8ݿbZb E2p9*d |u围a'$v?Wf8HlڈC}pZ<"OFq4$0qWIx\muહ\2T['\H:xl3`b&,5o)y_?fapy? -EeavcV`f8=|6UY%Pn{xmY%NӃa7n/o|xt2"mO?XTસ-<4-nrԤq\b=q{ c\d, .:[#"_1-n1.5/,ﶺԸXb% FL[ -.2 [s\8=[o W:u<50fgkߘ!­%^ء:[s5oL[ m.2 ?8nffxd\p,mgm<֎xZ12.lT.2^Zk8fN M6[qy$fiN;C6gAagFZtV`f[3 a,jA-)3s}őR 6ʧ̗3sN CUkȸ,"㵭Cpnqa2 W*FpR\k4j Fb}W X.2^[K״3s3bZb\!pL(Lq3{/=d$\;m"2x6l7 %F*\d, ^u>'7L["! j6CU̍p^kĴHv.2 [|fw^pk3-ƅ1Q.2 ٪13*YyȸDc*chb@ >WE"d,ʞDK?㩫1cZb$\:bK&{%[5F ?23C*9|[ .]ql1< +uEm}mxF%n# d$yHe-ȔsɊ-)!V|<Oߥ9X&eVlO;eh e -F¥sL \Ak¥5y&tq*wM\ua2ۣN*S}Ʋ*Ib(ۻF,/rq/Yם#1G\<茄E%Hd$E#n Btq/\[µy&č|#n…\d-s!['0^ :!ȸ[f33l?X| v XVifxy`kct``38[%3:Hl_qZQ"^&_S-UZ3.ܒDKs`F0ўYN̶>is:Hq.2.Wk,Ocf֛[nĴrL(mZѓwĽq^;enm  {᥵iO1L^`wFK"^Q˜#wMd^#EKk\DGd/_:r[Ow~^%~_ȸ\>Awy(֯X w#+mYY,>gr(.2^c[Vb<aΌa\= 6(H T悴ɲŸ+faM1-n1>1fO\d,;F܆#-sL |ư^X!G܄qa wIO_`tiwM`{Lu \8e7]`aHoy;Yn)Ul=ն 1 / M mei3l`..EA2fnD qv z3݈>d܍ ׈3U^} ";Შ52.Z\"VgbB/o1befY9:Tx#g$|u.2.Q_&e[q5.^Y_I!#M#:qZTEHECՈ^U2TW.m".bv~R%ѫCƝ|[x螤ϸ{ֽ]uocɃ6  EƽA=$;MԈiy<.yF"reT++7ȸn!<׍e6y&wbNݻOb0"Z<]O"2>kub\1dˮ@L)d̶5'U#sL , c~/1-n1Z{t.2 EEÍ"a\'T%;ۢa%)[]_`NEY XXV[G& qZ<E٘l2 d߽QDYmlc_2 `ȸ*R9:媏p0mkEڿbZb\E2phn:3[*\'ߘ [D!\d,)z̬Ì4_   X_ܶ|,q=(P놋Kl[)zhnABܫ:o'@8LPCdLfCDi(R0`p/ʷeٓkaLNF>}3Q~8L |Y lb.)@X}8 e}qJ A=p,.W wYpq^q.ˢ >1 =6/mwf͛}-IsCnj_>Kol;͎S|zg&l\d&>XNR f0,^6{e ˎBV"_}/Cɪ'⍑yG{ kKpk 3V1g2["ژ'ۗ(4x)lVۺHҞ7e_r; VIH. 24feu/Tug("rq+`U56/sN5f}j^o\=3.ws u+zn#-EQ[D믗fR V/XXil;<$X##7ۡ-UŬQ1Z/nln\d _vH/&lL'.2ue{a17&$7/vVj'̧UX1R8aF\yh8c߁j|;>ϱ$6A%7QꙌHWmW1S}9evn4CjQO+ "%/?CՕlj VͲlFĉT-4[x~ծ V!û }?ޝՆdx{Vo\^گn[N9b#XMƦ=Uz3B_Z?Vz-2! 1Up *&c} / GȂqygՕy:4ΙexT&UMؠ+D0~Y5,Kyۏڴa )Y'vr z ׏p\d8K:f$œg5!Ҙ~F5+T3Yٽ5L~a#b=ǏՐSRvγzDt+S@u`,~d,#cpއuǐ4vnW=ȓJ^c5,, iE_/eoeU"cּ$բ>VW8MNXD"XS:)oȠkJ4aKĐB^e"FK\Bư`iUc50T%OT 2+T ϏJG|,K>@-K?}#۪_q aIO7fX&uQ}2\LcI7]cxv t è<t¬0bX'?[ vۑ:|7̾鵵̩_NF~>'¡BՆ31 ΞQg;4M0v5Tş<"Ba5^I+?>y^'gkw̩ob[\H}AlbdV/ث1%fCyPpfbkpo"WB~lmRXzQkIhǖm·-'ز<} dv).'؊S`Kś7[ymGCˀ9yiBVn/bm[lZ`*-v>6mMplV j0aGXd竃=<~vƆC~0ө+p،Sb*ciکů{klcK :Vg;4nl7ǪkVYA( h-o<|,xo|JcutsZVEq4 C8"0L_){neT@_$EϿx>~??'[u=M?:?=G?<-.W|֮ 6! YcG WّEߗ/0Cd3EHVqDž>$`<7V;UȠЈ/6{ߋN`v v #0#tD 8D.\ ?k=b%]\a^&%cb4 GJVEvc(|rGs'o\F 6`|0("eU@'EfVgSAYz_t9X-_,sIJ-` V7 x帷3lCnO-p;9B r0ԫyC5eUʦ,U=M+ w\O9rj RŅ%S<<|2ׇ҇-J[:ȆJF$$xvvQ[?nA 59A`haRk,)+YTXH9XR/!s1E=f-DE<Y 3q٢T evx$˩H.AwE/-1ֱ,V-vYe%獕_`#} ژ TPcU˴:!"Ke1 -Q?d/jG[Xwmߢ8@2o6XʊVxsPdN5WuVRs]ޫ\E )OK+󋅵BwysBt 0x0նX^B/i oj"~xp kN2,V<fƬjGګ}^UJ7W?7Wue>}B΋5Bxծ]͕axЛ+  ]2>Hŷr ڰB#@ʕ !Do]_yIDUZA駠D~ ~ \;1ZkWc8$()Ø_GV gg*d"4U=%CSL&@`x<XR#J*zx olOC +.[;LB6㌭x/plzz)lv3{rQ@aY|(ѺV(>M0Z\"0!zpn|!Uov~wQAccttR9w;b%7X;g$7 \Xҏ-־Yc6(1ٰ[jl ^b6c zs3O.ϗ⹶a7`:ka{|QqbO2Ծ3 ;b!U|a{LO K$qd,к§y9wi[V5W?̝d6VaP1hDM0^g{a:8|D1+>>-[SPvF G1n _m#s?s+<.5J韭,W˰$h9-9_?肟5F4GtVcq#"ɼ\i?ș?8H~|Vf̿Y`bF&_oŠ/;v:EdOcu"՛,,OS茶0m|`&D_)kyXT4v xzyP'&^0a`Ǵ^ή".fġzf^踋0Foa2Mh{i.%"bO4+GtRp煺/FNGdy_)b$R#n^Y鸋YK&ӄky %~/, ِa~8 ϗ0 ̖;&*|~ v#'\3n"$lI&_d {&&d/^XL:^d^G 򅓰Fy y WmDnB/:a1Y0,&{x\-|jM%Hۈ{͇y֌zA+4+E}UEIزH+G܄y + [ iB'a/zeqR% C#nƼE/XL', Y"grq7/a2iMR&r: = iBW _[u0ɸ5 a2o#NŒd-p/0oa%Ly%4 7Eق$E~ W/ل4/+˾z8.t0)^4[f{'"5o/a2M#eVU~zS#KLtWДI8_%l{]'-p/i$s(R?#1&yNgIDL= iBq/0oaxMXjnMؘ^]d|U}ӄξ>_X8 c6-LFB\2&l[x]'ȿ|{WǽlBQG _t`$[#ntrq= _`qko_Җ~ /ܕ iq7aczawq~+E4l>^vߖl߱|+PD btn%p޳ݾ^t;ș'Rދ^R؀CLSEI$|ˋ _A%靰 yz7Eu:$EN Lھ^x{u"p9&,FB0&\x ȘI44uǾ%xp0z7U[%IDV>%L a}Ľ6oQ&5b#r'Ic:2nBJ^/a.H+`2l zU8#zⰌ{Ջv Ddş?i2}4\^_h8Y0} .㦻T_'$aL%#n;.2N\_}-\e/5[Ocy/>/v'w$1:ߑM|GE/= ]ܧ5&lL/.2N۵'| x o>iBg_p/|"ޗ# x $wIg,/μ_rth2؋^]oV &w@1Y|`MuQ)ߦk?OOM{ч~jbL̒%R^jyGE/ݒbW?& '~dR/ Jq- _(nHpý+.2~ sw/,FB;3&lL/.2Nl K{ԷEKNX+p`p8 _[M|Z^ ][KEza1n»RK,/J_`/[q^#U5UOc$sx1Ҋ'p~ufh2`W/n2]W6)PtNUdlۏ> Dl{U/dhRhyR\:&kL/[ '~LG{(oayj)nza1.0ڈCL/.2NǻL]߹^rKx{T -,3&lL/.2NL zuKHpc$TiM0 -CIS.dv_0-n1/0 wݻyjOp5u &وZy@45.zO5d"0öajNXyrG=R=}aA՘{9P^iqqa֊ƾp@I 1>/oH#o"6QɎ%vJU;|-#-%Z98L |1ER#>D3L f"0'u5a9hύ6pmm2UOձxc|<&`O!랥8L2ǬŝBb6L[ ]d& G+RqupeiqMMEamk{Bqrx4W6[e##˂|-./ϳlbxyH0 [l?%HXL[ e-͝\d&ޙϿ"gW|5"2O0e!:$ݧelo1L<] 0N6{`VC~nq)LN3X ~"6#_v[ mhN3 pgcoJ̊\ ._ _sm,>Q岻Owغǭ@zN*z11cY@ֽXc k~}~-N ˄Vj0uJk`_-n1.b2yf[O0xc+7 ۰6f, [6R*⥚|?E_*EaBa۸lcŗWu@Cawm0Ο?9)kl ycZbXX.2 o 7_Sgs8oL[ tV:N.2 kȲIX{Rfhiq2ÄցƆdpxN0-n1?:K"c@X9 />wc./>RKPeB (C3qQE2 34YZxzΞ^x""ǟ?Y=QFe@/A;1Ycg, rϼKT%|gcy-A2,WyoLaݾK;3.sUqmǍX(f+Ks9R:*"lVˁ bf[QX;a<CU9X&e ̞4EK3-fG.2 /;;|h@npmKxpi#|!XyL{-n14cP5f/5 2/p^-n1]F[=& Qjr,`ぅ`V0aTPw9JH|lɮEx!cYPCc`- e@Q~Hśd_)x5cZb&Md+w,f`1/\8?`Zb&eBa[W),]7g]G~wmi2wsh[ b_Cau1vo, IYcO1xb3 ڵ*9,:C#-fU<"0u}+6ʬp,_e@6&ħc3 }Hiqx)}効8L |{0ba1c[Gގ,L1ufZv,ȲH12שm;fo1/'(l+.\\AaL[ 'G.2 OR\wchx=G׮%M{d5-fT$;$cP-LJa2T#-Zr#d/7f^;/>I"njP2v ڞ˒c\3beBCt]˧$[/A-~  i4 -f%l>emldׯ1TX٪3ŀeBaVк,/L< πiq1f1q ~X >0 A{8L(l\usa2-;1-n1/mr . d4cZb&^m\d,?z5,b(kPv4!$o"Y OMڻYsegoxqhòz+#5j6٫-/d6͆n$n1-n1// Wט׳X!Lv C2 dK8}0r}A<̛jy2uq 9pwClCoo1~xiw p ۆ%w3'\&6_=8l; 3EIyLlՓ3CY_}%F<aƐ7 jbK~P k"p-ff3\dL lem}RTۻ~N9|)QeY-f_JrL \lA;J[%NP&cƲwDfΥ*Wf Lv-2) dwV b|gB-^,cZb&=hJEaBaita2KL[dE2m4rńiq?E2fux5(LfYݼ[ƴLv)-PX&n55/KbGTKGhLlgdC;h)^hjڀiqZpL(lD/шūU/ʗ"&9,%U@;z6 ؏;fmlα =ۆ]# _+5[b&ӌeBaպR`|mWeuıثz ,xV{WNcXO0-n1]J[J" 2dXnWL[dpEab' /0F-BK0-n1"U.2 w^W=[Nl(]y{6A&^ n]@ ݂y83#^.&3%<$pYPw,.Ky,`8bZb&96=.2 _1ՓX/xd&E6=N3?NooaCY+es9,I G\b/8ܹmnqa-9 mܵ[cf0=1lp$3eĎEyHv{0w_b+bX2Z{l!0,uP]v l4Lk}7<-"l4`wm CIm[}!|@0m ؽ{ Zއp {d\-feN4ƴLv1l.2 Op *o$dǖXs6;V"Qp tnL[d?_HSB򝻭F\hĂK. S=b6VaCa[yN3.mX}0 ٬h2biZ~-f ipL [80GryiŐ)cL{lPN3 ?GO#V*ke>#yH0 2Ⱦ񵆬Q-iq8L ٽ^=KӈxG<CY?AaBY~.2vøm_A&.R" 2e8Ʒxx|Ll \d, 2`V.y〫@Y)\d\(mW6e7J7e Ca.2Z&SS[<؛7M2]H~* t} #6[dfCnAUucԵ,QH>Cx 1b aR`lOD-$$CY[82.m!q%| շ5ʍO/ô(5=7d,N3^Z,4blAan[b&7,˄ҰEkkV#Xa"(kpӿXǙ`. :}ė M5EaBakK`F1!_ܓZ^.2孽o1FSObZ76nFkΟ\·^242'Sudo~= RGeEaa~AU7?HyėfPئ"0})_d}~ Ks|ݾBdR- woc1"&#n0 x"=f>{f R(-_yd\x=‡-^$'JuM"ӽJtFP橫Dxsڈ#sD=$BM.cW+=-=$U/DĢ[b&C2cB`:=6`Zb\-\d&0Vf>az9/R؞?EaBa6뗍(L :˜ZcLܿtkė3qa|S"0puM=5/L[ ab>F0ާ8}q 1"㾏q6ӟ^ͷekg/x)|im~xj*OVu6#yյJ.2W]9Ƅ,[o+71-n1. #eso [b*R2ȲqTqխ/CeeB),tS;o|)\DuÄllC}!իgP=lq~ܙK2X8ziWm0KKjoE# O.4V=Xg`P^L9^8,o5;` -F[sq1yb84 1-n1>ÄH&s07.ƈ1oasKxLKw:m=ψuM]dR p|i%JxH0 Z9j24&%rY', |ǀ{a[mބEI84۪l[[d 03ݢ3i2l $YȪs C_ݷ5dk4gZܙJGMhi[e/cb^PLh`iO;8!Çt-eįjrqni~=$VuB" D/\ zH0bqpU2ÿoL[Wsq@i_ +3mN,\d9"/3mo#v>dgkpCƲVE{SmOD_d\e|&1<+Wܖ3/yHp0p fML9,8'cLvfEМf8q;AUll3.Z?Xb˟Ef[b\xm2do(>v~-b2y:}~@7ec/lnqZb2," 0-*enpqY2u5 Up0d\<3.'\d\"{pݘ+8[bԘl篸x'g$|t.2.Qn\&H8]\ .2.QVV(7ք?/{;#9ȸ- pc0*aqΈd$|u.2.mK a5bjn`YB^R`kr}Jrq/\-@Z1ґƭ%Z9ȸ%{Sf`fj"_1-n1.2 O{7qv[C2$ {EʢxFs#v[>MCsLal]8(``NNTfY-zpL D'닂9iN4/ܕ lydzւ-/ƶX\0X iq-n1?5/Q,boO5<:[;`LiN3#qݹ|x:C٭t|ŴL.22VcV`W{f+g&shsɰ*6c4|.<k5l1C?ٍ txXlo9XA a63٩!QsL ڜ`A٘0-n1f-"c@IOط\,)ڍ{Vu lCy%̊ް?Ɨc=322S c|sE 8G6/* N3rW:aƳ\x(4uֆ]1:*nY9]R8,{م*XW?,$ :3o};hNjeƲpyV5vx Ncʌ2n^oW0.d ט{`BF7nNXHe^^DzpUud!7C 2B$ =KDz7,,s aH0 G:唧f],YTdG\( [슊X|N`jq!d(ח^uDf)Bcd,E\^Lq U82*Q=Aw^2wӍc6"vi<1:黅7i 0 fWu[5OׯZ}vHUh*bη \.n10ۤbvXIuo>f-/7n>43e] A`κn ?8@5+~]DY6ۢO|X=9HFL 7>؛u1uO!u<`0L+\m<>D=bv~;zP2̄"(O k38Zf"fH`v3֍nFRaZ4S7ÏEX@'ƌx;ke@(M{ Q.ZcZ@܌ : 84nmL^qRCnȉ؃TcnF LkS #ǣԕKp }so/>\FXxb"`|Oϰ$Vnje7~2 #?rtsXzR|54: NKdhrn7HhᦏtÃNy y̦l0~曞Þ,0Qˤ9tw0^_27 ,w L:}.y.9U/?_Ä9miƟΙdt H *3gfӻ,z Ո٪δ5R` sxN0 o]6ՈyݭiRgBPgL՟愩ȔbV]uhp: Yy}@ιa2*sE+f#{(:f3 ]~uS@ SFsp> nE0v ' pM?@yb܍k\r #$޲񞅷TQ̟ '.z9:JL? @wjOKG{T nA~3\ vD43ON&Oub ,#cTSi`Bu pPCIO)?<*b(]d2.ުpt:Yዘ7t| 7E o!xo:,esLsx9`fօ:]Wp2:eEá*[Y},9l1I10C-ɤ`'7هوbU19HR(9W+# Vӳj{, NzkZT5<5Z24΄6TIp¨Ԕetz%tlQ'ۤl+w D5C8j𙽴apC閅A.%p'7 p%{1 .7::A;_m(&ʭ/\>\>an-6EտH@H_!x9sC!|.QtȐ|)rG)*Q7զ~eU!AEU%w G l G'۫{uWt+6Lp]# wP:ǧ@pv-rvٱV,{1mmFn6%;N`ړUA0Xdk-~sޕ>N!VG PYμmM!`M[EˢͬLq?GW{䧚Xfu=aζS60rJhMmZK|%ܥE銁 ߍNE"8GbYyS;l^ιg"~~f"s7M3&GcHJeN%KT3&:343p:#j0L ? :CפL=0S 2Leꯙcwh5D76Cj&jƄ ] ^Kw~+Q͘?fhT3j#w1Qo c3tڣf0$~G~cOO_ X7 ƟiOO&kqx".^OEh[)8Z0njP[® ydY0ߝX-d?aY-7pDR nGw7p ۈC?`ڄV2aD; 3Co6t%d :+'5oYy\&)CV,)VLtVjz@bfC(ڤZm[9XlDj"Ziki;bzfڝ2P!C T/LS(;T;EQxt+V: ,5"'TFo)^#Q7PN. ,\}''Tܓ[ߨS[6=Ii ]ͯ~*''T~PQ%oS+{"`<ܘDa VEU1'TD|CQDu"^)Q%" >\ U;: vOW>FUO8^p7䞶-Z BntZa'Snr[:<-:`-/ԩ5PڒJH `X01t{7I橸&f%,1()f2owN޷J"^s$TZNDStOf%,1?V ; ; ,"'T\CJTV)4'+ba JW=>bN{(Wd{EU|Ndkh= [Ǭo{*'bz?f2Oųޫ~2qF5ΧCRۑp`VX3')N(1U;Mf ?ϥ;QWXUI&m2+NIfzeN &U]x/XUIqRR؀эT+Ty{ՙVz`,s><VSOJSq4 0;UֹUn0W F2npX1 DI<ƮiBnItج ep㆘; y9%nt ^I{sz 6OaYIlI &߸We^l/wz N|7n,½^½KfR^x-)LHZ7& wQq& pswnԝfz{W08Ld\@XYa/#F˸;CՕ  &l&2] <7>{4C1tXfPu&'̜ۘqn_$ftA.Vp*֘ᬓKb`f,_ȸvpUw1`vO0caƺDƵ ep0Wf F ..S^cv tu;3j"[u/`J}q ¸DƵ]})#2|_`0vV.>!` ,/W0"Ld\H>.Wx/Pͯ jzT{XK;]Jfَ/t3:TDƵ K$uP<fWUj!!b1\bVX=`;&2] |h`.Rq%!wq#a  Lga2k_yyALHj1U8$Kṅ?c}>  >89&GIXIUQ(iv+ C8|g7tSp|q Lz8HxCڼ_e d\@v! `H_z\t0q"a-u5f׾lp CIzm"E -\Y*q3 *z&2]$|rs҇poWՖ>[8z@uJ@6ncE FZ7k /B{*Wop18j'v k\,+fct՛_HU0@K=XϤ16)7[jtk3RI=HUMq#a†CC~p!YB!Um\1;7S235zZ-e6C,2v+A#;^d4"f2eNq#L?_ptpq?E( 'k8Hv{cE}aPa4y1܎>,+z\ lEM.gfI,6CG 3xm*ۘc+emHf""Wsk2=|/W0f ^}M8H; ]+Z*N .GrGSJVbqp1;\A <\: T9EmPȊBM_xC-JaDUd!?Vdu\=zZ+P a"]eů:\]DWh}Lt%^)%%WW05G séoc&ʽf$K2qtl7CX=`J 9peƳ_EHj3VeX/\=`Ȳ G+,> oW060TK㞐.Ku֚ BUT%Wi2TAn~S:Um/hEգ pϗWW0,$X;HRbZ 'Z/W0UԥMd\Hxk82/WO#rV Gnx6+SdP`ʎa Ep"$:D_,%|ct!&2.z5UriX=`$q5qt !.ޮl!KE̠vO0:Hr]|KX^ o" uVe5q]A٩R\CƥB^V`fX8P/.#ZCZMU(24Uya9Ld]$O"$,vN.ctݸ,Ld] |5.1ݪ]C\2^b%FZ /m L2Y*It U m&fqL %ͯ`$}v6`tYC2s o`Q{̸ F}ɂ9#W fv0`"wE!ZNF3Y E+Ao0qtYu̢G5N7Dw㟧Cʼn! 2b+ |ki;9FYKĮ<%#5tXt6Kpby.]i2I舉 h_6̇"vlx{5J5*.^{SyꄃYeW]$=h/E|N"Ձr,|j~ZD'҃G#hG\_Pͯ (ZCoaT9fW=h/Ed;׈w BU&Wqpx'{kwZ98 (CEd{Wm2#a*xZ@n<硷ZǏ͝a1OJЦ+r&v,`_Pͯ ntC RD#StXܖs)alTJf%GgJ&6MQ!3Pͯ %'_[ B\ɉ ezA6>pgUyX)&Nmbbq%ý'b3w'wwɀa bJJ,Tޚph}ÖX$DҲEVVHPͯ ɭmd 8Rh|f Ոw󮝽O㡇CA#1iyU> Y9'X"(!IUG/%"D zd=#|(k\HBWgWLѢ{^FU?;!*D2\uՎ‡ei\b<2QT% 7\% Ā3EԢP3e ݊ t wP\8;x_(boˑ_k$㡇LIjeIԒ ^4f^e%y:kiXgLL*ߝI+!;9~CCx%rs \ KLD0&G=&'pᏟJ>im+C5"uva) b`zVx2{* jW'A, 9}H b` ڥ~h- ƚHb3b5joU~ږZD{Hٙc *CtOpmcMIe ٨9 F{(UKw2XtqC+E.GB.&WPͯ ZtQ ߒTūam2øNn:/SD0P^ͯ a/l.*.*&W#\6 Q{ئ>2^wjӧK WܕY$vͷ7Pͯ ȥqwO?Έ vW2#"7Π=^W^XSgW#NrSKRXbU b&2,/b9.'X 3-\A89:J K루'[() jճ eW:-D ,2ںپ]ގXa73\!pq]a\r149D915FA}>-~IA:gWK{w텅Qf< L<RD4J4xU6C5pPsy'WFa/h/EL+a:]1P `P,t c2bp3<_嚯f}̞ƥw0fqDA8leXD 8kr'ں' 7Įfhj~1' P<aoLĮʻVa2T+*r'XK ޛߥHbs_A 8o\8 F"j4}F II+g+[gWÇFoC-#mQX"C5DzŃ']qn?evOpk~Ѯm fz*XQ_A`9t&RRP3(3/Wc{ {x{U4ZKMxL4z pH!f  B.ֺ' 0W^t:M8?# LARL^)I)@/p.c {s 1(~1ΜCфq93T+*bvO0KR$d2dWū6滛mi7 1'42-jF0,)m@)aa<^SC WNt+^7݌ݛF@caV_AHv}(+"H S Ĩr_gD z{s;s,\ܴ&Zui_AP`ʵB bTV,j~!ŵ~h-=r-,{l}5bgh%l鄚p&C_ B7PtO0K~1kiDPo-١"(G%G\]BsGX4.e\^,+XwxŸvh}Wy9Į:Ar5&W:X F{(2 g6 p *xyIQw92T+ `Ҥ,Q>cl%9\"2\nZy1(1Es_Ah:?@wJ UUwնmMJA(`̱kiv>-8sL8;_N:fE6³uO0Kc Z>&C^ZU %w gSsfhj~@''hUЏOM3T3jTA@P.9F@ +{-h#1:0ۮwh]xOHFڐ[;$V,"z#C5aZ'?J9Pͯ O7F*T3'l/8; m5(}_&# g'*_Z&yږ\?/.ˆ2ϙ~{珙2Z?}"1*<C{ yV*a?IS*a_h,gi2.$*3!<ՙ_3*J-9~;"&. F[M)!}|x#)~#x;df>i Q]osTzyaڽ&2] <˽w~ \WW0O#T lB/rEq1 >,mXu(vTB+8IXFqT:A"K.p)K]IPa;-~FK8t\;U2.CkvRS/Eì7f+&BU:¸Wr] /\,ƪxK{#ѡߡEٰˆg"2#QY4h^(ΎژUŹ_8٩ oK͎: @pJRjm0⌣fI&2.5I鼮$ KY9ߚIy_6Go Ƭ Uybօ.E3clȸy&6Qr=8KXO g^: (id_X=`|}TGw̪ͫoܺ;h$=yb$y_޾x fְAY5 L8IW;WbSMd] <+K&UrU.p^X=`f"T(1+=f$€D{-8{u@,•~F6 jOEi&2ޛe`i,t <eBQwhhu>7V+Ld] |h1@cF͢ %/<4aTMd.}%ܣ4}ۊ R1 ǻZHpiN[ᔍu ff)x9 dn$Fz$ Z+(3V%sxUSqZ-dq+v"rl@Ƶ d^S4`]a F+/`xjnX%>GxbgEBCoXjWŸaJY&@TKTE%՚%KzN.k`ҙЭF03#50q݌]H2̈́<Ls#Co&Ygx5ZDasx,rT(Me6fQȜ~343uũt:78'skߚ|,%yRunvze1=mX>jm 1XIv[__anIso%OՊw\ud~Y]1o*ѭN@z+ԚxV9rΛ8 eƗ )o GM"3x:ow^8CqXwsBA;~ }gS_)㠏CV 8=2r#1~B(o&C$g(Ed7OqAy`/vrΫJ)us.R8 fK73̑1I+V>- V>c$<ӽ#V>e6"lԎyu#OK#JlD=wX*IƱnÊvlr݋pd[Nd"޷ErNpmAO c_bon_`֥Eʹ&qƕeڏ`fW?zN ^{?Ć600xpE<~Gf$6n/C⢝G=ڼEM7C<Ih|zs(2C8<<Lj\x9GQ܊SVNls (߱>ys7;n&`YH7Umo^y@;0ANO#„&7x1*uܳ&+z*A=xۛ9 Vmd=K(O]j;0&2`@./4o20:fѻ6) Dx`NT8\LDQr'$݌I7c}phCt3:]f ;f@ gFT٤#Hnap:nuxȅXs< x 7W9W aYZSl:`<~sZ'3T9ɇ^<¢)z1j(M|<_xs9NPZ滹NSζ\+i5>y1j IssersA^!Fn..pE^.X%|/,kM?v٪s [t2U]7sNQI^ӛ^Cy$lX\^^B4 Mw^|0J6[ayʯ91wcϟ_k`|9A=4i50?cnv̭}1Et17wyNP籓 ɳ= Xi w4aLLq}zhw^c&zk9`^c;ksg{1#3y|a[%90`;~\(a[}-f }/RZۜ܈^mF`X(#J{9թR>!I0 & ȗS[n9,o^Eӝ\ {a踛·k`=;Q /NMBͫw>ϺԫbG<@@5!^V'GpU4Sqr~|JQtÄ?X<]pV9ɗ |]8/ce\}0)q_]a#n587F:71R.Es41 |lCFQr!0=1b0yZjvC`'~U> 8LvT1LYFZpc*=rQssmUH ;03t|%ZX=5?BHA*) ?=>qm&n&C5<5wLOɘfap]@q чyʷ# ~`O&"厷i,_O+!Mn2~SV'MA)>ћxOc[)]\)*^xm~Dא"f=c#N%#>COG3ӧdgm~Bß]%=&;}N[ܖ[0d.ms*$f_5ƽ/^XlfG%b9(t]i N>WzX>:;X"Io/{Xs.!bƾ.EnG~wq2p!oʡ`51ߗpVjXuꊽA4C|kmXET&'mU%O1i샲fQQmk r,Š8lfM(g{!qLzj;|Tj~eטu[~^ܑyX _~__Oۀ133\@_f21"b~x??eu:1bye3'Uk/~63b~0b 3tŽNo0Y>m*fhf*,z fLtfif$yX Nἠfݿ|G`a|/.Řᩛ37mĝ{ZrIDmQu/kw!Ӳ P vOpZ;d0)UO ?1{ iBW XSd"^X;{`s"p/, U(LdH!#7Y6zZHU^5MvVb ?mNbeԄd"^X^8&|#/ sjфDOa%ńТ/܄d"^hl'L7a2p) SXLœ{a:b{!ȸVꅃiBG?>j"0bBHo܄wձd"Q'vJ¬ DƝ0@-OCeBH;޸ 鄫'᳟a,_p/JLd O\셃 2޸ DO2&\eSAhѫ2ܷ7Yll:a"ub5ѯ ".UULa"1NI'bc6XMd k&UM(LdޟC4+ȸޔi&e0^8Ld.4+} Dƽ !6~&( Op a 4!ϸfP/$Š"텃iB~~0xP)\撄K?;U荛Ld ʯ B ]y&6>7gTJe޸W]谐`/z>+z>3Pigy^|O91L^<˸^Z~bD c7&\q.OOp3S_c5,J%½y DƽX\Fs{|q)7&'&|<=Dƽv6Щ|/܄a"zIq:a YiBǶ}񼫗S8>$w1ܒسaSX&2ߦkɜ_`/zv=!ܘ7n4:C8Hx)0z||*nG"pSf"^xW N8&t~ŽɚR ܄|MxQ~/$?cMg7 p/7Š Յ52nV؄DO~Bz=e"^ ;`Bh{8nLd>- 0mo sE/t kC/L)~ M(Ld.Ǚھ^vK8NuK|-mBDO`MF%2e4=elR:;M59~6~fgT}E+Dƽ*3i2S4)Ht~R@u~cv@7n40S_p{^㲐Ld /J cIo\'60S<ΕiB[7vncj"^x}T@x}ef7ndz0S_pؾƽ𾔧Ld oܘp-DMh{xH+2`v- R2q/c)71;\AXv^: TQ׍Y'V8W0[:GWVJX⳷불GjR~k?!X|K[@:oW0^DG]@OݺW~"25Hq v|IDʵxWW0Z$Ld] |r=yp0+5(;qcrv&2. JX oX]A,BQGv„w7Fi`ܷ/xwMn3(xeOT1x.f$]B{-S]-rհyD?? QUWbT"z\Hoj""x(p<6RV2V+ۧ8@xCQx@8cq=9KX=`\s4G sy8}%? '7'Xj{P'^`6_̓߸ԋKF&2.]7QJ("}V3f+&2R"Fe+V+$[iI~;'>\Ɍd&&2] zymcҐVc\KԚ,ȸvl(@c`FW.޽㭚 fy@LHƵT7܂ խbl`oW08j"EWhYlœOWŀ ,d={!BI/ ץ/ՙY X=`|[G3q"a-D#4g?z\H/DƵYXoN?(̪\yq$~DE‡~Okj1oݿb0‹ KY9YYmxw3뚖Z \T>«[2AW3qt6lr元X%{UK OOZUYbRfWW0NYH0:@@VA(= {-eWFҰ{HUٕ3 g< A>~DsGlKϓ'V0aw,ldU ³"1Hz٘L eb9Yxɛ2u0uK,.mfe d\jmpLVvIoe<ɝu+fp[MdMz~UN} _X`B"DUN/e cZH0:Htp$*FfP/*Ȍ3 8>jP;ؾaQAffF3.fs(ʌ Bũ&3heI$/Vfݿ`Q5f۪&2]$Zx[ìwf|2FFj" Rq<0%Sz5/\\0ΌoK0NK@TQt8rqZTG Qy,,fat_z\j"D«׵ 3Wszõ='LJx}҉,RU 3l/TyaTj4 fVWݓ0Љ=`fⅣ g&GnĂPemV+A5J5D$QJe;Q$ravT+- Rij9]#AlZ \V b`iD=U>$Yl< ebq 48zH ?REE(jkĠ:m8B!U~69HV#*m/W0REVMd] 馂Gfb&=b|Ld]$ǣ$45X=`j8Hn@$U; F9R`Γ a2QXfCu C8HLh;CL ?ƵTZ9 fgtʺ߰z\ :`ȸv0"x0]/Y{cux<G [*_! 1PPgWd݊rZUwjj*kT ,E€d ^1Vֶ׺ֵj՚@òb&#c q`tsĈUfTLت#:=K \xn1b?E~f!Ԍ)/vo<ڃS`XXp).%TkhZX/E3Y` Ӈef4"aVlrPz\ .s4.=tI,,ap3,$X;|DQXx`cjA,취4ŠmBj3 d'U[T\,>z\ 8H@ bf4 T+*XtO0KAO8EߜLĴ|q3P8&2.V'2#zv3dk.)-V1[O";\ <FD1Zeapk9V F* G((nw:o|lȸv0 u L. ECk ݊jqXuQUX=`T3f""aF@ tgGȨsdhOpo( &65ŨbJ߰z\ <k&2.\j3-,l~X=gdYD卻5ȋ) @c7G C2Jj\eFߊaDj3xP8@v*4k"ы=77v+ 3xj 8YRwqNs0>, {8H3!{d3Xf0\˨ۉVDzGA2i v +1y FŸExl[w4eBŢwW~3a>Dg㟧mC>9ڂ) tyj#N3==ĸkĦҮ 6p&E'*ݘs~RL;Ācm F{)EznH"#;'?{mϕsݏqV6CQ?=5y5 5rF1֞F vix:wy`6Ѹpg.o%L{K‚ը>I`参A\ jx^,0.رd8GB;A7XN\J. v=J"g[ bA_`BGrTZ /; ?_pʜjMIuq魌?rƺy U1j=?O!dl%vd.C5p PT,M1fKLD ,AtO9e?'s BwX3Pͯ 8'{;!B+?мϾ@5pސvO0KӺn ›bg2[D 8o v;J(sL å@5pLRX)MS C2\)@[YOSc L\1/cFĀƁstOpi^r"1?xԺ~aeoJ]7C Lػ2"1jq__A ?T'?F<-*t'WTDfj/h/E CϽ 8>腞 b%b^Kpj!{_rrBQD[t+*2#LK0K߿+Ƹ? հ/Uk4.{-BcAbK-.sQ(Lp:"0x"7*^T[Mi;P ϢCf /b=A@#Dn|50k?xh x7>CDGkM3.=E̓^rt+Q^c{}pсu"xNZ<ͯ | =h/E<(: (%\]t-SGu)aM)UCMߛx_/r┡[_pՑmۖ| H1@!A5^[FC [L\+a`{枨\x=;z" `C^ z8j/hQ_A𔇽=YD =<}B^Z#y9^Ey`q 3a/՜*j J00?g>K  ӸvzUo]n~Ĉ1%O#(b Qi*#7*^4qƼ-›T#xQ.8'W8l}9(9ͯ x>{l5XЪ#D;OoW[k =sNCqf_AX3P<;Y(1C68\u< [Wkn\QF_` jxp߭u*mbSZ5oKZV-0{b_-]$-"\ t\8tҵ=岷,SObbO/<Փesz8&ey痸Z[h*_9NX 7[w'0]PE0QwD7`_>^=<c.$v ^s3&k@Itoxg]$n=\ &\1l1g1b|SF}ka4!qiar<ɍ/=Փ)F &Q-9OeQ0a}m0Zq1j}d.jɟr1\'fY[9# Kј|=U=Ͳ,tJD7ww/8IED`L\5i3/oZtI,&Z~ŭ3y&*-8Xt7o1t1N'e1e&qy>-`O]Y{9s2C8cTmM$d1oYxLcgLC4Im1dbh ssM7S Tx_VBLU8ZL}fP$7[LYsMsuQ#̓Xxyjb #0)cڑ2n `Ʈo{ԗQb>%D7 g1bIO,4/4phYeMPv bJ6mBߺ(eehX$7>ܒiKLeO<P~OB~,F"RGtPꚷEt˜G@Q饴Qto-2C$.Ab O&Ql:®Mo2?Rg'5ϢPvg1bX]?07Vx Q񔣫_J,['݆X;G5_LV[wE rÉH_)&wqp!u9+ICE5~$F"ȱ<-r"@"TGii!B\cI<@*QAc?]$>< I)0`f=pEIzlaBnnB)`pt:93n @ lNC$n=9ފ Zët(mVmjt˚(s%0&qGqI-V:M}hg$$I6I-;˰6+#1(> lr7Mcy *YRoIK%ޚek{,͵롘I|uai}OûhI,=,֨X簭3 R,uq0s`}8Z*d Ŕ>oͲrP# ֎gWՓ4SskCxY[vx{8czmh}$ <C$7 }y7EEbf-ch",Z9|M1N]3l xoadi<0^AI/q҇IHwPtgH5N.;''oW֟hL\>hD7C8IxŊċB!Ӄ _Ahଡ଼4 8/-7 o=,nZ0%)x& $vyKTY{1I|<1]M+ȧS:Px,BDH, @A ʈ22 pXeYvi\~aO-f&t' MMEɛ!^7,%uF]>=,/-<="3wȧ"Ք?o4W6mOsw@gCxb,1а_>]' h0j ^H'Ťĸ_ i QSToM]+R+;ULPm>\hXdqZ^ObLû8- `.-[HHEcXfb@#.T'iz@KSݔ}1%wnObyw_/ĩI|,l|G&rb®6K%6kL[ƒݖek<ǖkmY\1f@Df/[ [a\tM/ga\*׆=XIz|q{)/K7Ƣ gJWGiD.H$-; M؜HUdSXlc!Q|9M]]!{BAQL>!tUX BNrAnՒht1nd_̢[1xEYAdcn@wQؾ]n"D/ڕŶEm SEŇҋG7U^2< 2۩UMۊjBoqBTR4ʍEIAA,稖X,s^VkĬ䋷* w> T H(Pa1fm5z*<.)L7.aw])QkR~ A2`7rD<u Ո43*1X] QOډbSH//y딨dXA }-J !˿5:aLb^ByFJ?`itU(!yݗ^R AK{z)RD1BD[E!Hԧ2HcyVu5F&}% ?¢5y?. De;TL@b_ :.uU O1[>y#Q&h…dG8L-DDfD>sR7rc^ h!,&:F>[DK0 #5#q#%+zd6֣6S- MDN3bFUTD*bRJ62>bP!$-5Șr\d#k(Q`(TV&Ȧl$;bƴG*"ߩLW-C}fP8w#qgo@G |M"8DwJex{d\1#~oF>vH+<|!k΍"APcsIE i7`,t`#GOpF1<>1f'C|Ϊl ƠL] i *SQ?.q#]!dq3b@BOh x8 4!bs$AG.9Ğ5 lzWR/i ݝvxw]gGkÁqx T ?ӤoA4I Ow3ݞ˛n8^S+^~oپF,n\&MW 45/IdyLJd? sWL.͞ οgYvq5z8E)E17ru]rCeqp}/v?ϟ?X],??~eL~ݏ_ %˲?|@2 )'/=A}*+=ku#LؖzI}s{P;W ]KI)=:&~ŎОFI~OC:fmerƔL/m5{KPaNl.., T,kܝGVsz:=\j/9.#\c`ڴA$UKC@־Ou]kC\cgQ'7>~JπjFQg-Qrg.s)DwoXƙTeS0DhU^-`1v/2*&ey^{JyqFYȽiG~hG%!\8f36+H!8,pSnM U,ޞ`vUyt:۹Wn}$Q~yeXħX *!yX;H _-tO`>Ʌ SX;2`X9/p >[hիZUTcjnq|U ]?t|x>aD)CG/#^YQc!R)\ҫ3伌`ѲOUTXw#rUA= qOM -ts( \`_c+2Jt VcKwCXgPf js#snIz Zt kA"kjhׅ;:ë8(bQ ?!n)0zUqDkF<'V;DƺW /:'i=+,0 pJ8B|Pd6NځxwV{L+|BȈ}n={wx$lUcr^o(R!d;sT.d9 =!.)Z`Y Dà: ?KF`l/gʪ ْDW{VN>ǯo5hWܥAkj4,buQs5P-ɸk/zȻ,K ;q5HL>2S7ໃQ<9 Pjɵ[/fuaT;e8ϐgْ9+ȡ31l$%9Gח'_Y[W啿ig-g endstream endobj 4 0 obj 57844 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Font << /f-0-0 5 0 R /f-0-1 6 0 R >> >> endobj 7 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 612 459 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 2 0 R >> endobj 8 0 obj << /Length 9 0 R /Filter /FlateDecode /Length1 9140 >> stream xy{|Tյ3g9Gf2LdL&!0IN`@#@ $m$>p9>0H%VeQߘM]3)W8;`fy9 "<.-[uՖ\\\kӳBܸܰܺ}܍!bUjƔkU[sy~q{xMj^ɥzE""/-jkYkeƍ&k u|)/# %X̹V'#P~̏d1'!BRa5"pa}VV,1*sv\!'5,)ɶNIE?"&&t@.)Zw:ܝN|S?Y?lebbLVic*ZZ,*kUe-VX[UVC}U'UTֈIT[-VW EERW'z3e0N`@T0qjUZ ;h2A`T9%'q֖#/]xiHo&Po_GkPZsaCxjC؜U g(Hއu `l+p5UUʱ!V?V=?$2֑!1N(s'ˋBdbQGKKVoȔgd=V߮ fxr:J',mu^;\L9 jgWJGŻ\@MĹ>ޅFKi$-2R17cL./[m䫴foE`32uA6x^)4,#3s~ S1F`U<\\RT}̧w(?F9l< }&iB9B ǞΞƩƈU{)g2VS1݈3OP]}7|nYVwks[ߞ  O܄?8r2} d2ώ!;:I9\N'Xug /7~W;F7.K| P*Z2D8)Pf$L5d>ep[~&pl!@ 9`4zàׯ79'X8E*$OPsd ,Ձ,I j(ByiIE_*J;:Fk" D6٥ap˩1FXOQMRȕǨ}~wYR8܅eY}sLkܒ>gv?ang4jGdpG]Y̓KMmZWqK )ijDnf)ݧlf9llXm8Clv f7m:-Tflb>be4n]Y6ƘaYTEOi;)b /ekY:NĈ& ͙5`I1s^WFρ*U<*Q1%JM2Zr3QZkN$î䵮5Wc%w?[4{#7ҷvf9n^bOw#C8Aj(#!MV Q Yd -i)4pDS~R6E$!:M,gނEc/ō5fd;Yf3h!1jsE?c#gؽ#-]W؞~ٮ߄yn##{%qOFk k79k!*J舎CFP@`8j_wL5 k ,`!%#~`ϖAdF۶m`ߊÈ,olg%۟8D󔭶LQv1!-DGoa%G(fk+&MMJvv}Vpkz뭶 v {{ˬͳFj=/ΎqU**Ǫ!l(t Ձgj(zq}O؝]=;wU=ݶ{L]ɴE7^xY[ߵ?uAq-{ϞKw}Ǩϲg0`T1gi't~m3_סY$4bv$c9Oxq zy~ԏɩȱ;]Q,E?SyJuEPy*ؚbGn冪"kilyxș`iʚet!:sMpY>'q(9{ U'|dIFr:L}XY:D,I01_eV{2۽<{Ѓrad50ӐLF?0@4 C9)Y2 Y eNg0VS&ENde v9q^BwlڥR8\^f㵬-fT,t1v[cvYur+S.jhY:}-ozy韐y Q14xӢjWrڜDC0G&Ϩutp41n΢Ӧr'?0lMN-ӼieDJD^o43u{)4,s pNU=i$'6y9~@ 0Sm)٬4?n78˜mNƙ@a`a5mɁ ^7`c*<-ݭfIHΞC+0!_S7I_j'JH s1z&ԬY.⨙1ߛ]0yէnp3u?ؿ|ȋ}M!{xSLyt] M޽! zt:0 $@od#L/ 7?wq8Df.s_sqLypZ) 0jBTA#< ` 6?8f~*x Rp-ƍ솇2g.eg>(lG73lE,掍 ֒!pYxf,7]1 op& w}#&" n@ VwjހOOH^ /g\x~(0&*+\aC!fQҽ 5D t+12;l||%WR✅t'} z87S3 }c}Rnw?7O/I/MH?i<9QyO'\Lhz@%r: y7%UԃFу 5QFjH=D2$Jfvܟ*]Ga\q,iy"WW25`_%A \(qtATA%v^ďS_ܚM[ !z{ SeE ] ~bWTkB} ,5VAEIJ.gvJ;Iys< |X@'rX5a Qqq+(1#5LJNT ϙbg'R\۾1KQ"DFFȢs``t$g9g4HM%E))DG:8U׵MAp5Ei+ Wi8hp7kx\-kMJOKĨV߯^,u>am15H?*HcG8Jw _t1p/+' [m_+Mw*e:v|Sp23 p8߁I *@GL1{@F$?HE*Ca_N"%ZXqXkIv"5 ~\>6(5+N^c |TI~$I@՘'QQxʯۂzBV `oj6 d B d BXX6aqYg(2LEp"KCD-=K>"ȬzOe{äc,/)1<rRG w|=>aOx mI&&Ljۄmi2s#0@q*@@-J ALJ}kj^ilnnl-b.!E6߫߫ v݋K#ކD.Ȯblj`!۠%PHH(&RIm(R{uCuQTA^D踡 񬀄ZnKCtP#ɇRDpDQ<0֪CHGA3)B%'W"KS,B.0EFzYU],rxMg~xNO`&>{$:}(׮4"wY_"7 yo3輫V?,齥ّޙ[3FgPɑg|! noC{;!+ȟy+G'(UW^7'ENI"~_2>yjڠVgjZfhjs*Ye+׬7P>5EӒ#C՛5Mo7=>'ݝ qw&crs|B>jL"M'Y ܟ촵&uǍ+SXy/!Ak"(q *(V)dF"r#]Be`aB•BBϕ$Q`$PiLLTF(Ee( F+t/QQ,=/QQ!Dϲݱ3.q]ɻ{1޺v`,% u-^Bn%mb787bb6w %K؎=j]Z푙 FCQQkCK^[ó[_'OY endstream endobj 9 0 obj 6391 endobj 10 0 obj << /Length 11 0 R /Filter /FlateDecode >> stream x]n0 w=t|u@.znT@- 2KA t/Nsl=,zu&\F8:U`Sy*a[#νն}PpaݓY { u}q WpF!W]'{8#dx۸/se:Ғ^ ~FwAy4u +j)9O{ m)5ĥILFU QZ5s)\2̢_~%7ӤJw~@u랟{M_CeY>*}\ endstream endobj 11 0 obj 294 endobj 12 0 obj << /Type /FontDescriptor /FontName /HVJFXF+Times-Roman /FontFamily (Times) /Flags 32 /FontBBox [ -203 -428 1700 1271 ] /ItalicAngle 0 /Ascent 750 /Descent -250 /CapHeight 1271 /StemV 80 /StemH 80 /FontFile2 8 0 R >> endobj 5 0 obj << /Type /Font /Subtype /TrueType /BaseFont /HVJFXF+Times-Roman /FirstChar 32 /LastChar 120 /FontDescriptor 12 0 R /Encoding /WinAnsiEncoding /Widths [ 0 0 0 0 0 0 0 0 0 0 0 0 0 333 250 0 500 500 500 0 500 500 500 0 500 0 0 0 0 563 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 556 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 277 500 500 0 500 ] /ToUnicode 10 0 R >> endobj 13 0 obj << /Length 14 0 R /Filter /FlateDecode /Length1 5056 >> stream xW tTչ>J "<8gj[NȚڪEKKo{W`źu]sϙ=Ĉ(H"w{&J"~6iUk7q9;W]j:O^gGۊ95ЕwBz19'wݴ9yB~kͺ6;k6w58O}֓/ ɃٛPfr`wb=lIIg<-i󿨚wj\<*XCIqY'5eo깘΅ldk6.TCgb[#Zvޭ sĺAS#_k6EQ ލa0)߈I`&`>pi3wF:2t(\KS R@Kظ '/+((-\t`Sss|lrnNT4AfQkghn*8T**/.<Ѳzʛo^{{?ؿzd{G[z rYs|%Z瓴쪙sqUnp}r36w9[a0pףMqFi}t 8N_4UA<^Sˤ-t?zʣX5A:A\S#=ˮFeh2=v<@?0~:HGW+gs,EAD0ӣ 53?2#޼q)1i=+̻oV**ꤵOt'l/(dXM!#O=]Oi?G)a[iv\ހk}Lgei&rk4,{?=Lb%`ΰEzIYj S}3ҭX+z>rv[X뤫lN|v߳ PFTE_^A瓘kyBJtB{yy|vNc&\Di'^{Got9M@ 4O[io;m*>l250dc+ӑ"l+x^):M)&ana?aٛKι+ș7R%I%Rm}}}sg="5dg$Y}HC'Y%bըs6V,X7¶;Y?a1bϳ7 v0۹qp7vɻZ?yL"ZtLz[:)/-ݶ?޴ vۯ/v9*='p(zKzAm=/ -'Щ'qn@2vp ܜ5U>1 qWMuGJNr9vyZ}X5r†-G3PZm)† U} 5l<~ɓ**M5kjݰ0~_ g,^`H 3nHGӒ 8(ؠm[:@GHclRvmѼ0gxdAY]Qҏ<\ʆ1 ̣夰K)RIf{v}*ʧTy*ߦ/ kdffY,?f"3I''ɷ=yraf,Z]ml_oo7ʷ4+eO}Н=\Sjcʔoc^;*WmocXQsAo%E{YE7b~sԗi?F;VSe쎖 C;=ںTMbe#1K0Z)Q[Jm*˥" d*Y׌PG5Aj*1Ύ(}-ʹ E1ȟc٨rOP-UXݨGW[}k^k/4nv꣔'}'|ǫ-eV7#.X?Q2aMJ;)JBY{^YIYQk9rmRRfUc%QRL*SKTU <eJkr R2Pns:gsؙvNrf9ǹƺܮ1Ѯdp\8q1Ozx9܂8lbY' 8Rlթ+ ᑱJOq_)㙬Q*3+dL!10b aZ, 5s1Q+CuXlš|C*rqpd-] 7/p&Ö[m`g~?o؋h^$\DŘ,d[u'V,+PY\˄@iJ-7anI[,oE 0_^ж~q k`݃nrU1'Sж!ol+y@kp_GK {6kץ\\!lȵ#ps@w\+ rwX]6^/ݸq)7Ж1 endstream endobj 14 0 obj 3321 endobj 15 0 obj << /Length 16 0 R /Filter /FlateDecode >> stream x]j0 ~ Cq]C`tڍ}G3"9縡 l3{>=%pAFϪ>8tuN4uj_YpxaC,sK?4'Tۂ%x5.Yi=fq[##Ks4Hbx$TZh\VN w+Ž7n3."9OD ELe7_Dp endstream endobj 16 0 obj 224 endobj 17 0 obj << /Type /FontDescriptor /FontName /NGCTIB+Times-Roman /FontFamily (Times) /Flags 4 /FontBBox [ -203 -428 1700 1271 ] /ItalicAngle 0 /Ascent 750 /Descent -250 /CapHeight 1271 /StemV 80 /StemH 80 /FontFile2 13 0 R >> endobj 18 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /NGCTIB+Times-Roman /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 17 0 R /W [0 [ 722 556 ]] >> endobj 6 0 obj << /Type /Font /Subtype /Type0 /BaseFont /NGCTIB+Times-Roman /Encoding /Identity-H /DescendantFonts [ 18 0 R] /ToUnicode 15 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 7 0 R ] /Count 1 >> endobj 19 0 obj << /Creator (cairo 1.12.2 (http://cairographics.org)) /Producer (cairo 1.12.2 (http://cairographics.org)) >> endobj 20 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 21 0000000000 65535 f 0000070323 00000 n 0000057960 00000 n 0000000015 00000 n 0000057936 00000 n 0000065471 00000 n 0000070162 00000 n 0000058088 00000 n 0000058302 00000 n 0000064787 00000 n 0000064810 00000 n 0000065183 00000 n 0000065206 00000 n 0000065894 00000 n 0000069311 00000 n 0000069335 00000 n 0000069638 00000 n 0000069661 00000 n 0000069926 00000 n 0000070388 00000 n 0000070516 00000 n trailer << /Size 21 /Root 20 0 R /Info 19 0 R >> startxref 70569 %%EOF splash/docs/figs/hyperbolic.pdf000644 000766 000000 00000104321 13261626263 017500 0ustar00dpricewheel000000 000000 %PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream xͮFs8ۿ{$;Ă  YpTwHoEy<{<>L~>tO5;ӟ?~_?2!ݟMlsN{6Hbq{$`M }•l~ٻ*҈֞^ P#N:U㠂:q!⠂oX 'ǜ9ߗ/!isvGmӿE;J^[r( pB3N}yS.[ڋgIC~~zho&s헍a=avđyh'R?^b(O#iy kA%L'8lD;f :t=|շł܇7>?M@U*DY" Ed:PeU%0]"9QYJX,C`Ye bCyqF%L J3G8-D($4|)|d2Q8+D(L4SBܿQ cFno$Tx"]&+']9VNmKOk]sovd$ifc=Ky;CNw_;zW` 4}&a:CC{֚XܿGmrϴwM*1umZ @E6$V[?;}!]I_-O6I Fv:'O6I kbdׯAzrl׽&M'?$)ڥI]&4F-)WzF_i^'$CBs|bmC-8,N*vG'%mRO_ /Q)hT5}|*j4_hC"ӆ 1Ff  rƠi%`6 72UɞH4TallDChDCEYcd[D-cci$6" 7<2 lb4\'N38 (N4s`@Hb˧; OV}-^LW N6̲ңKV%Kk!6Fqk#`y~&I*0P&>2uyp8|4΍b4\)661H6\*lCHj~V;~k?+\I?3J?+F=S`Y.KU`YX*h(gfު~VaTV}4 ,>KYU%Kˏ~VaQ,P|TFZ(>*M-QfH=x? _+?ȤP|TF GiT4Ьi+Hgރ~?mK)AM(^i/"\Z%"LD!I& 0M 0H| *(jqW*/ 0M #&$4)$a%8sd_R~GyMp8UGyTN|U;񉯃@=qǩ?ΣNˠNj䢯FMٷk}&ױ)7ݵ*'%ѻ˯ҷ@ӷG|z1+/tI pvHR`?88(u?_H*, z>&ԫƺ,$C?L}C 6d'8\.z<{rL%z[G;TGo5nRM j''OI*{<`#c&{kDzbK4;jSnkc|dJ<3`84X" 4T# &љJi&q|&H[ͅbcÍb C#3`8ͼ4X,f 4T-Ns8i_6Wpۺ􂈾Io뤿o)oPOH=@z?ݤ@,JU`I)Q*̼V"jT++GHgIopZt ,D:Kz["Žk%2F0^4F"QHiTYmi}5R /DKQz!hh^4F&&Qa?OM=TDVݚd!"K% ,K`$,Y,8 Z4zqJa e <.1H`Y e bĩG aĩS#S_ZP"BT!a O8ķT0CT"4uHXkp% .*?_hiO:?lh endstream endobj 5 0 obj 4337 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 1191 842] /CropBox [17.28906 646.0001 861.1876 818.0157] /BleedBox [0 0 1191 842] /TrimBox [0 0 1191 842] /ArtBox [0 0 1191 842] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /ExtGState << /Gs1 17 0 R >> /XObject << /Im2 9 0 R /Im4 13 0 R /Im5 15 0 R /Im1 7 0 R /Im3 11 0 R >> >> endobj 9 0 obj << /Length 10 0 R /Type /XObject /Subtype /Image /Width 301 /Height 300 /ColorSpace 18 0 R /BitsPerComponent 8 /Filter /DCTDecode >> stream AdobedC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]Y ,-" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?ކ[X)+6Fk&{U 4Rj@1K C%&Pi)Sme4(SժAQaY6ǜVMLOSRG13g\L٩(R՘j ҟ4@[QL@/.k{UXԪeɥx))ž) Z5U!sSc^qY!v/,(Է*BAS,D&Ɋ)P(Z%5fj[\5t(aJ⠸# U!l\E&XCs^+-W,>K !8JqO5_Ϲ5N5sW`TQ9.dR3T'SR|qSBҰ[PqZP[*6\dT^BJ!3UdSU,)ty]wAٲ5:Rm0b]A 3ډrkǫ16F*\L\8L vX^kO friSY1y7`j94x̮+mmL`VݽęaMUNg)5Uz,Fj<TY +ռ ]tMj2dSWI4{ҲRTZuj8JҰ5 nO[^*:=><(dJ$5%]GXɹ")l ҭx Etv+F|eV' k2{ HjUVRJ탎+>ٓ5Z!$ISjK ,ڊ1]Ng1y(6k.T]2y [ a^Kɨ-^:rjuMC9=xMeWJks 0kѢ(vjMƸ3MCl;TW2nsYMO2iN,x$˹StZ5Ǜj$ $ʚKp+~Tm>Xa0=NRUu&3\Ơq4>XV8?5Y!"{:SSv:PK]a[:xSVlD/{ϭu|JʐMPQz܆̴9c_-j֛?*{޲1$"\V5jjɲ;n,Qk[1їҟY7ÚU6cu r:Z槱¶Λջ_U%YO?:Sc^+ҏ]+WQ%rzM\q{ϭuۏd Rƺ[[lJuk5kHkBf]Dit M[Zq熬{Nnֻ}<~k+J_K5}֠W݋]e= qz宾?xV~\Raԃ|JoIH|+ĀWA|7C(F¹4>M>BkQΑ KWbj99ڢƹdՕ{'EjhSkrm3Ql)57޻¨FLD ]E8U4~:\3.5%FcVOj 4VeTN 05h|W]ͲU5X7¹ R.+GF11nP3S)C4A1LUiR̐\'jD.c˨6@>r8JźlJ%Qni}kT#3F=yi [ up0- K5'UfHL$5YH5iW"HM!\{Y:f/DI"+r*&)⢒L*61[K›^T5w7X2fy=km0nWT$@SW6cۆ՛N1VjV-Թ&]==xQ.׭hyJtm՛jcȻ^ $LctۀknN9:CYpcyh$0-jW, MһVqq TW7WjŹ$Žڵ&dY0uzUёP*R+>>[4 YwIWZl)汥sSw yx]aTncAz 8 nҭZ{p 'յ+M_V0zUIYB*ɽ> ͖ol ճ.Êm NZu6z͚jl﹩/z.؆> Vú>[+ VZsN7-g:jU.TQnE Ѵ+MkC8`9ެ-44 i9gycQڭG`+Bб؂%jbk6Y NAP\ˁsURu+rSnsKdrº/rV^mxZ"i!neFjzrkQ5.*` xR&fY hCsieޗ4uN E60k-:V!]\ ] 5I5!P«I:T`5";hjiZEJ2RNqM2@ɩJZII*5a@QUnƕ:kN,ba\U*co7JUKa\[bNь2 un+ YCNGZŞB("<`ӂpZx M8 b cK*j`(<%scM!sM<>աxh1S/b7]\baV}Cx**cJ0-oP$vH*E:KS:H ;4QE-QNZ#Yb*' -ꤗ>]'0M-RhAڪmU$]ZA!> stream AdobedC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]Y ,-" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?ip!O<$T* CSGXbɻM84CPh1 V'Z-\CQuiF? 7Z6":j̓Q&i&A$'.ح8ܹ4P%B$Kh, 35[Bic}Xl[}jyGTNCepix,$+TXyu +U9"ܲ&+TWAVUYĠjTqYXTC9Y,֌ruV_Vr6 S$x^jhj9N:UrKqFͼyT)J~)U[jSMwԥ8sVbȢV㚡!Rim9j YN*&[]تө{05711!f8v WIoc\)Wa8pET$(5aF3L[ZA g`F@0(e}QГMRVI Nu WvQ5".갑O%!E iƈ0aY` S R9jzӕwS8SD?fQ I ͚Tjc 7t*H$v8$|եn4j9Z6 UUw84+*SL':75~ TTp1SK n3RY2`afΙp̄>K&6ViGoYb[³U>MMR.jď'qmmOgWصQs#frbrٙj"vJMr3VzԹ#5i3Z0Q'NEUeX,7Q ,ew v1i<)Ȕ8rɷS>[SЇLҥF~4qۥZdѵ\jEKmط\Q9b,j.Zȫ sM,V-ZG\TJKd.*,;mc&la}ҍ=Ѵ圊7̕V>"D|$ ?*UD)bK.v,ܕ0l58 W,hy{mY1[NqoV ~>-Y2K|UĚFwU2Ϛէ eii7_\^m*)ݤ,ZDuŢdf{k5xskzV򮶟Z޹uXVȻ_5i{Zm5L]Tc5J5Sic5dkT"2ҕ+[rǿ\ƺF,VmrO\|(IjUh[tCYcAOuI|ېִZ<W:7_^r3]-⨦][0}S^Yn\> JIπвbD4] PZ6XV'@-Y?V,fŽ8nj?ޑsiEZKy ]%h{\l5`5^F.t_3Jrk#[Sn<{V^k֧TMT[2o˪=3]vy Vf O6Sֺ FmIҹ0W]ޮv!uV=Z"`YEjhXsX7RsYmkqOG8DSSVULStZa%XUJ#iM7X鋯Ʒ°t>++695:nJlz [3C΢qX:(x}bnY6fhd+$ZwmlV3+ ٯ/nCb!SAR)5y|۠`|°g}GRO&#QcTc?S\DIG杪MPjmLOչ eT8q  JkjQ¹#_՚%Z j\.*7C}֡8i!vhO 6FHr1 a*I9ֵ;8qǿ6FkF(ŭzV=}vX$biɓ;m;Sl*Qֳj OFj[ۦ2ip1 UϽl4cLkZU 6 -TG);#")񭛨l>1:\GU%i:0ŕ@ZU~QUIlwGb25w6ShA5TTW 6&T[|`cdTӧb$4Jb64j~̭5_m+&3Uf5rSn3L}iʟ6MoOWkU+U,Q\P82IV Q#{P\'4PmJ/0kE3Q^dINӻ՘Y>.1Z,Y1Ivu]S.ȩZq ~5hDQ[jPHj1B;vۊҊv.«4djY"_z͒c$+N0N3Yr8 U'.V]?4HSl$Ԥ/6^F.f`R1TMsg޷-p>|;Z$V9jJmyB\r=iZU@+.x*3Z֌jòg3aZ>jVQv%hDŽɪrfL k|qVD8xLnpbP*p]5 A a&ݧw N;MZ]¢&6ifT3UZvE5ZըȄUK> stream AdobedC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]Y ,-" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?zE1Sg-A9*h2icEY1wÖY^Vn4{Eg 2j dMn&E]h5qUo<3ޫ#QƋ1aRDqQ*pi&*)rjRUw84޴ݧ4q✎O0mIe'PXM>It;5^IB.W5r5 AxNiHjt xk: sҥTU yJII<ː__-S[RL0PMȩ\HdU;W6O!jEZr˝$)( "jkfjfALUX&3Ҩ]=Ԯx [H5ygprjEޱorJ-A5$WLȾZ 5tB$&3Cuʹ^*Cj!C Q--/Z*"sQLN*Gj)5k-TsK jc!#li毣Jp+mf|>\ZQVD~_&g`Tb٦l֌!mӚϽZmZkfbeesnRMҬ^]jWw"cԫ8'uGrJ ľUΥOIWe 㔎*붠/8UP#gPyDUSHsMj皷,JUSb r_&-c4[Q>z} Lw-O#yUi-v\LQVrYW0>W[ W*%֥ZjIdYF*R>)ri85".id WE-vNз-,hLI"lM7p2IZA" ըKsSʊY+ \n"K9i@O-RHg\PĆ6ɫ"n 9$Uu5eJ̸@Er5 Ҫ#Nj)6MԎ* :<Ֆ|SqUhۅU UظZd5E_75lUxTj[uRyg9̃KP&K#Oܫe:VU37S\coEKVNk23VcsWL"664\u۪&`fDrVizJr6Gi&gpbjT8D(Tg',92֟ j Yg*UC#3Z3W@V,|vb@&:UUb%@"b4.Z_ݪݵfQ2&*xBV"N5"dԱCG%jڜi*QVp296I [5 O<ւeNVCZ.<ь2ڂ7U7q@o7E*yc5U,qK7iTf:+M$Uc\Q![?5^^* So"/7S+(,c(U"AUFh]XD' 5 mdbq@65 @TUnӾգnI,Mf* /%qYⴣR5 ʫҫ;i02j'h p P4(~JI$5*S"KXLsS4F1,Ra F`+B|.w-db|NK@~b*+HTV@G\dXW9n(2O5iE>Irj x*LxeQ R Qhd.T՘d&(ȪgyMCSGAM0:XD)bS&7 '(B MV&jťϘ6AZnN5<(G&ojЎc=ꤳ,q5(a VE UYlj.:]TqQ֭7ݪ2K!㊨P¤Eh$W!)⚐~zU<~Zέ W\6E>o<>z6 2m5^HsTCqٮAlQU 4VaW-L<-Al2j&6HkAU*F=hjP^~j8 Xc9&>ZwA 4gjn EYVnw=+NF3/Z,?/sYkuɨdE[Sq<;Vj$dڵh-K<̆X4 'sMiQ9Ztnj@ dU6fG;HDzIc*ئ :xQ~zUa.GZKc6 [DVjVGqMym+'SZsD66)r][Y`}S(eڵL[ۚC6դ~M,X!+BQP1PP*44(28kظ5P{S`53F.:cZpGZ\IԗZ,5&ow N;Z `KMi 3X1Ajn&ǽh208\7baiX.:eգq"`UHMS_4NnX>MIڜ4'T~j48jKYqK}/5IRhʱ*NSJ%@d(vUNw ETYM*ݟ9*~[M=FMjEU;861cIy1f)Ez$q* 4 Rqʉn2j)o7|Uv;M[p뚊8i6*%ZLSsU$sK,i7JȩakU]w- vWIAȢ6ԩ&2npart3U̒bL^\;-M]>+-#26RsVU=2OZ56+v2z]K.ԫV.nPrHOJ"MI sP+.jh~^*qZ`5lKzU6)oeHAS U#cSjG|AcmAwdzVnHimEvjӎƶu mYRl€%&6j9_< #dղL [xvƛwv~K[l7? 2ycVDaxMJF ÇV{D)'Z~i>ӃVʗ1z=g']n&3o/YgDv,̘Z> +j9|SnIUQA7V2ҩY˿ƵSN=+I&,'3V.uPSboU4?)ms0+6 >| l ekPJ]Y$Vy&T*"YABLr69G {T^ sY|&ja*;W1k&*)RLԗKjM`bl۷-U,#ՉF*@v)Ī\QN>SLYV5Pߚ~&WWN]5XR5fBA$RS/ީMEVC1Fyp80j=\sZo² ;V]jU;M4ӌ)AՆr1M-Xd٩&F(X H큊?ܦs%?V;V 5_RtEX?&qu5J-CǶwڵR@dTU**vWցޢ$q֍E8V.˺kS3Xquz~J˶؜~U֠uyd*nZ,A5B6.(RMRE]U朤oI9 rT%&s-7[n@Z\{l{Vj?QWuth|e5OėyT^6W.I6RZfqZ } k&8-^jjtHƔ$\mhRKEjbeV61zGi8_dB.?ԏfoS aQ!in?g*[QQdeXaX;EsFGP]ZcY>Փڡϭ7bVe3wr8ei>7VodJucXNfM,8%74)aK^AҞ+ 2TP9ŶvK?_Vb~ߟi~m1ЪYB3S_FHRG^i m.U+W9:`㊆E5ӆ%Y\iSPִ.H犫9+U~L`=&7JӚ'犍mȧ۴TP+}[["7<-kU4}EWԛ͸޴ݖ=No 'TӑGeM.T9!^6}åi[}+[Wr3[4k"aMXO8 |N7-4Hz$)i :ߝ-7OF7%jqGdԅKrTbb7 э4|+*I IU4ʾZ7DޙI5S0UXf6 iIlk+sMҵ#Xcۘ[FlsHed5<7 i qT %Glɥej.jNԩmNU쑧mƭUdl|EY5V̒TѰUZV}Tko.T$8_!igqHO&UN@\&+>PbAAnlÉ7 j&֥YeU"[|Up$ӕTRF4@KڬsNP SUmSvGk-&[VhUs+HmĪ5RgVq#j+1cTS,ۏJCN]Vu5`ťK }CƵ/Wuf&UCP>iQ W8h=pjimC+O Wd$Ld;j+j3S5#9,j^VB,)}b7qNyK&ըU6OiD1&vVbA{ W>yG&]q9q qZ$"bN*QQ* RBjwjm&RTk endstream endobj 16 0 obj 7073 endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 301 /Height 300 /ColorSpace 18 0 R /BitsPerComponent 8 /Filter /DCTDecode >> stream AdobedC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]Y ,-" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?J(((((((((((((((((((((J((((((((Zkh((((((H+LF*;kTTsQk E=8je#((((((WYUĺk.nY) C%PkdKGJ?8Xh(((((Vq]l Wźk.aUa0MC!XgdV8%QEQEQEQEQER 6+tb?"neN5+qYֱ[9;w=oi08REQEQEQEQEQE->&E|fZpeΝjU!=f?Z̛KmwWMaU((((((QW-. l9{9d`¨+>eR)MQ>njV0HjEQEQEQEQEQEQE8orP<)Tf3UT,zU{>rEhARQEQEQEQEQEQEQEԉ)Z\F5gL~FM= L-QEQEQEQEQEQEQEQ)(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE endstream endobj 8 0 obj 1877 endobj 11 0 obj << /Length 12 0 R /Type /XObject /Subtype /Image /Width 301 /Height 300 /ColorSpace 18 0 R /BitsPerComponent 8 /Filter /DCTDecode >> stream AdobedC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]Y ,-" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?dG-]Tj˜VR#5Q*i`/ +@bT]z{wS$S+X:Z(3R*#Z#_JFQګJ:V]ⰮpN Kq&kbFb2k@3KpqYS1 >IT!vsNF81{LD[Zf/@vY2LsBIȓu[EpJ]RE.E.Q&*E՘\nH"\ŀk#5pE-ҵmcZ]< 8DoKfqY}hYHln K: Xi Qɪ.[~k9$ӣi*OԁEiiBiKRn4$ѮjIWjPNd[$9)gQQ AUZV&$JQIsJUsڴ!*ʷn*<=Nme[CUS$jwxYo:0؜ϭu6ASI*lA;\'e,nHˊеA5~~q]ڏBP/&HVQXri]yT+#3bSZ* E)LӁ8.i3ڤEuLWhG"] +IhnkBS;Ee^rE2cиpdV k[N~kEjZU5UچboD¹gS5l l>`kj G En"LQU+~vGw/@IiXЬ#&= O^z x ثnmm\;5g^+yp(m$H"AuvL͚f1Z V9ڱb{[M,Va-kp/pq[!FTJdTÃI lP4Kw ZogrE'-WڃՅqS& YDp֬$\,>5;w Rϑ֪UU)6=뢵ΌEA+H-jR+-#jmXZV}Ti_,Qv5]"]a>oBV)bܳUY8Z|Iմe^2ɚysS,gOe<յ!֠FUKVx=k#h,:U2]hȬw~qO]~Z͕1.k5~8ُܶԬyxeu*̲bbw] I5g򵙫K*7ҽb.ՙYBraj3 bT\`Z0S##A1s!Ojr,jL>([gڹH Th7jkf8lw)j֖ WGn F)#ڰn?uu_>6|zM°$eMs{k(K0z/L`pjyyX⃞3ElJN6+ZY֪^|6L౪z^4#aNq]"|UG;Zi8Yjb:-)ǘLNـ< :UF& mZ<>RrMs(sҲcY^Zʒ5'(])kZjyDqZϕKeu]%FX%3`zJ!ϵssS[Nյ+T5|&)J |ӯ$VdgtۡKO]M\W#8KQA"˲s >ȋ={kyPf_z${T%JQ ԈZS" k1,ՍϷ( y75k^ɶ-\d]&%CS]R]f3*\lPۏf#\*#oAtC~S^'9)EW@,x}¯]]j -rJAH$Y~*)SZjOL֥#.jL)a+0kʈUff*zVt&3~6ßj5oh%*ų-#Ri2`qT6*€U#U#kʽen*WELRzX0n* K X0sUZa=j)ExvJd)ˁQے!?I֥ۊԷTjÍֶu/+p|ƵH(*at uVg"iw7Ȭˉz$W㚒C!r 1"VU)%Lè&Zr*A=@TpJfwV#-DȑP$Y =iۨ Ep3U#%hqYeḼ=ֶ.|XÓRA6ӊҶl5i6ާ˚؆a$X5x1"ƛ[v % Y7l54@)CWu%TKG4BjP_z՟4Qfpk"I{vv &MdVlmEMxF xIZ) ؂-5}?vBMEdx[ Xdm>MVLPTO0S$*!!޳ hiJX۹1Ț~j&MO$@*UR*AOR*Up*UG&JU @n;zB TE+"4n0Y6Q6k%{b`0i'ƞ*͞c"ZlnkQvqT"dZe2+Cdɦ9 XSOLf#Wb59*dŻP%gթ"qXWLK}y0.*kx5J]gy45TX8T)&ь3J$NEO4U3܆\ʻg* IS)( p*Z> endobj 19 0 obj << /Length 20 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >> stream xROHQ6Axw )vuYm[Ңgߺ3ӛ5œ]`鲙}v*b{a[QÓ'a?dy֭S{=5ڊ^-CT#hsM9s1F9 1w7;aYf ]%{w;ћ9 \Ir< X}I<>Uw(gRVzWOelπ~v{|u׶>UEP>,l%KTn)=J+vp,ZSk9xw"zmMWzmʨ)(ͳDf[xf8:罊ZIE?9Z*UVPog~~\?A< =ѯ tIsQIi!3NTc)[d@f endstream endobj 20 0 obj 704 endobj 18 0 obj [ /ICCBased 19 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [0 0 612 792] /Count 1 /Kids [ 2 0 R ] >> endobj 21 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 1 0 obj << /Producer (Mac OS X 10.5.8 Quartz PDFContext) /CreationDate (D:20090920235205Z00'00') /ModDate (D:20090920235205Z00'00') >> endobj xref 0 22 0000000000 65535 f 0000034285 00000 n 0000004453 00000 n 0000034152 00000 n 0000000022 00000 n 0000004433 00000 n 0000004677 00000 n 0000024600 00000 n 0000026642 00000 n 0000004842 00000 n 0000010591 00000 n 0000026662 00000 n 0000033220 00000 n 0000010612 00000 n 0000017318 00000 n 0000017339 00000 n 0000024579 00000 n 0000033241 00000 n 0000034115 00000 n 0000033287 00000 n 0000034095 00000 n 0000034235 00000 n trailer << /Size 22 /Root 21 0 R /Info 1 0 R /ID [ ] >> startxref 34427 %%EOF splash/docs/figs/starpart3.png000644 000766 000000 00000050201 13261626263 017273 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEg9NJ%7ݸMJ(*c6"X3GI$3է_ /D EG0 [U͖u+AAp-ĩW\6Rٰ>dm*=YSOi~;%j'џ9VOSLe{g?5KɎmxad~|zx߼vtr1.Gua\-׫Cr^كKo?[y ņl:X۴}hiz7ӣf#H&w4Cc  ˒qE t1"]`2 B.`YS?!+UϛPomkO<(;}QM3AN IDATx흏eo1 6_m4TJ C[RmR7H*-UShQ4A+1Ƅ&MɘM,P%_4jGX"z;yٽ3{y;;;;{޻{Ξ癉 }wW; zQ_05UVNrWFdჵoR5lsk/%Py; .UsfǏWǏꩧ9dɒE۷o$Ղ),9p@u`E rk,9v6mڴvc Zɓtl˗(E^yٲe+W'JO_:_>uNk Q#JжAY֘ EJj(eTR\ù 2>RrXҮHvA[6m"uX5lQJR^4bM/z#ѻ=]:|܌Ov8&JRP*hT|ҧR榘RUN?R%'ZЧRҦRkJ}(wYPꬄRg_/a8>4z+UFȗ+o-QC W5= )'VJ|ֈUQ4> M: dZXshK(JA)(5* W(RJ?\l; -QKLzTT&DQU*qߤ|2DʧSlU*ҘJ$W#) )锔ҍUWX'3Yh_ЙRP JAv{JARϗЍRg}|{VcLmgJm<Ц[JD( |*QJ2*T2ʾ$#lTJ\U-o#R}]^Ub)VLFUe3=}Jt5]gT1wIo锷J-yv4RlCRPSM ?_]Dmke|RƧ]VhT^T&䭒-[>D( 'yD}*Ot++C2uUaoRP JC(P*S?0TԪRѬ%LƪԦ2穀*KWʨL9i^ЙIS#ֺSy2& ^).U|ԜQ(%^L+UEX)^J)J]f6P]˕)ڷU=/]G*@hBrE4*W٩Uj̗QM+EP8JUT-əjd(R]\RjrS$J%̗/~Pj+UGK*m?~UTşR9%J)NŬ(Q7V2= U>JI|L)^8Чlb.OT ȑϪIꖣVMŤCSUQ꿕N5;RS٥>BTG#*KNt(5_īR)fb5TyFe:65kTanF]n&6̦|:k眀xt"&)|N7fL.]v2-Ztr mҡJAV*ȥrJMTԟ-a!j _O)7=E0R(jʭ]?ўJq"[e=LŽ*Z˗AmҖsmN㦼"zUzM=$U5NxDiREԙJOCAqUjaJ>@SsZ&׏}(7JRPj(?VCީ_R7=J(UnUT)m)٠w+Q<C6"JI"'2E}224]6VA$>HL-839s'3%myJA6jk$bR*Kߥ敺OըcJE%/FT)lpa̖gT&FM-mwhֆӥNi;#2) (KډERcldJAqVjV>R$bZ)ϨRVyv-(e)%ROѪ Tr*#Ԕf6'F!9 ᅗ_~ Qi JAVjRA)(աR*"z; 3#VB~Z+3$cUt=>rgx>:'/S*Ťh3BcNJLZ#$'a^tO?(WJ>RT'ڇ=TwJR(0Ĺ'RJiJiL'(̭nV2On'ޘj+{'j}+wj|RեIwY=)gTw?I*(:T% Ia}RPCWQj3u@R&Jk%eUJU)%QF%-dAG.T-g*7H JI/'DJ=uPA>Zaύ TJUjcoHR:K8Vk{f*U*%;AKjlqSacV륕#ΙM)厴L6ՌRj (Rns)(I[BZ8Ni[Vr2fJRg٠;m)I a[B^#9%gOuZ"n1jL0ˣ"eIF&[Q"?J:!i?tA)(5J ;_^QXF )J-LRH9rdѢEX8(w6ǘy!,@kFڦ >!ːr<`cR^0kJ7RPROZfH\ JA)(kJe(tJY3*evQN(+6-[>u#dO&˹'V)HHP|sΡKmi$ZB3.[Ѯ$GV3uJA)(ՙR(QՎ½t0+b2JV\).8p`k#pXwD!!LDs,vˤYR7HbT,>5RyTRPjJRP* [I$SJT.ʕb^|M'OLɔT'P2pgJQ8BJl;ʚ U ]1zDǰF[er,yчUt}"X Jb$[|}:fw}u蒮B1ݒ (e1d Z,_,'Kf-/O@)(5>J?RlFP J ]\PrrAy/P*,OUu(S)КW*clGS|)\ScǎIQZ)!-?ogٺu+]$_ҚMVjy$O$OَH&P@OP J*u4tFIs}0?(:Rߗ0ZJ/O BWG_RbYe2Vi(tOT&7 ,DJ*OaA4t$Oz;ʲ+3'A:&^$J^Nq:E^$|OI4)c-9JÔ7*JAzs *5|c SC)(՝R|T"J%|TTUQ2,*IJڥjb HzyJ)UR^CBKŒq>qöZKڧ Tɧ-5tdZV㌊KI$OQGFITJ}(Q9{DO>y2Xl(%V'JZepg5ׁKT>2=ٌtu; 'x Nnq[|OBR9t5\ѰsNs޽tao yFhLFe'Ĕs%tYn}B)(՝R(a~}RP JGN(W:ez#*RJ)莬8e|QRmƢK\^-D-JARsHZHt_u+_D&E%JQ&dҩul*oHJ)yz$QA} Pz8(9 W.s\: nηX;D,;[.R2Gk.J׹N[##i*#e@ViS =11|w R*JRVE(|:) Y)Jn]ٔ5qRtwN|UYXS'{u{I^eJKW8HtC% FI7tC=SFu}FyoJZ*TDEMFPOW T J}!L:6mP JRRPj~?iXxH4Q腢x<<%:vSZUXDS'?G:VJ)SK'Cl BЕ5k={\}d yCYD>p tI-@=!)zfQ=&%R ( jk/~P JKRCd@T&[)ԑf>$ aJުRtw]G?tJEO#td}fZ#ӈ, %IHsswC҈\_^|ǎlWO#C{Ga'q()5 jK0KA)(ՖR,aZⲩg+J>R:Z:)ֻ.Zx3hx$y'׮%t\%.ݻ&ΖRjG %Eq~饗+7Ԑ@td@A +rt[<|Ψٶ9$(T7J}WC-Q@)(5J2ó>FD$J*TO +a%S*ͼ\57)#CX)](aJFЭW\@k ϙV䏧N?O(tDWR#;JA%@)(Jp ů^RGז2+%!,٠jĴINjR9u0pJs=}sn^MJI3,gQ )\,'?o)#=lv$O$d$#VJWWfRm)J<܇91&R@bPj.PhQ8'*K Lq۬(E9+%㞸?V&TUq$ WDHRBF2Iҝ5 չE29[x5݅)/D)SGOdQGJRP J*RsEJ^>gSOR=fOZBgT92@2iwXfVpǾePM-QLFV*C X)J{(ΐRP* R*gJ7HL2VV|z)NUKBLR+NPqb3pSk]֕oiRAT3ɫj/Oݹ{],G2Xbʲ)3oJ4s.y(bRTJPcfV$OUMZPJXzS(u>%('á)s8Lw(Y$%4wvz06ISlr#A)"z}HzөXE%@)(T(:T꟔K.S4Z&(LaQ$UlK'RHQhQ_s5ӣ>盾/avܹe<Ҝֈ䄖is ɫ $\Tˣ>iLR%g) ?Ay@KFI៫JAK%@)(Ja cT-N//R'AFr:{\̎ߺu+eK2 -$2 outJCg1>-!g[rẃĉiXViG@QJAzu vFy(Zw*RGHu.KM %$=OD$)EOj*ɫjFITJRJ1TJ(tT m&΍Oi&sE #|ڵkI) Z ;w O&YۤUx9 ΙH C " k'ɢK`eI0yUH)5R)w)(T;0 U*STh")FJ,#sI^SD I)XCWg8اc6lILyuV>D+"qB4q(jq0.aܔ☇RP%jC =x+5TҴɏ2|H0(*jkRDz5ZBn:>4iħKO<\䈌57f*#顐qQcɚJV*cO@Dݲ\Uzw VRPj~.ju3*z'JEʧB2OǎK 6yCtM7wS"ťxӢÇ /rQXfrwIdKffyox.e KRP J*ƭX+5ZA*gQJZd4Z0y_4Ĕ IDAT[5jt'nJ.!.-XA%Ug=7gN|81bUz$LC᥸_fy8}au .Y¸)ĪXL.":">+l6o]`V%d )E>=CQmܸDO{HB>N}iĉs}+]n.'0+Ws4'z!NQz$k;ǵFS)R,ar)(Z c''(S%RgT{*gER"6OO>ҥKבċBbn9)E-!uǎwݪU'4J.,[Y1hD/+5R|jSP JAvwTwJ1T*Tٕ5J71R>>i:ts&QV-7\D'R;!tI]xᅟ_e/{ ^kɫ .*H%dDGDGб/==٘J"y }t%۳3BݨQhJ3ɖWʜGRP JuKPJ^>Nw0RS3)7!zO`a&I4{>;RHP6f#6K޽G7׿_>|#](ZjSW7l@> ƨ&1L45/NfX@y$P JT05K7) mY}|ټUfTyvL\iFMt/ 2mQPk_;=H]tEovm?+^/}K:S\AK{|OѕM I=-AkqCKkйH(UM u (:T% e:!vO@)(՝R/.a8 .Jt**̔Zm J?DP mt{ԣ>J}ݾ}W^IzQ׿5}GyFz}ߥ?Wzы^?=쳯Zm۸v~駓LF#'\:)ռ7 -4©ɾ[A)(R%?(R(ODөR}3(J Hoa _YnOy~{߻o~>1Jȭ/)iyJȪ׼gw>|ڰa j7y9"}yL)L;j0Z/M{ʊJ":R)KzRm(Jb>W:%Je*&zR>Rm#x㷼-/~?//tOj>O\x;v }Ȱ/˯y>,e_>ѣG{9RgdNm)RmYrOD$>/VIRنR*KUA7=eP JuJ_JUۆRPj(]EZQLJ+T>*&t"%/ҐC CMn兔P}/| g}_WU_W~3w2V(ٺɠ=yG_I|;t˖-[.䒫+OÛtkT&S'A4yTF*.hf0>9'3R*R_u.X+iĔR+ccUKJѳy6DJ^C RqHʽVZB1n:ʓȘ_v[Vʢ>O}7Iu]=%Uz+Yozӛ^?۷nʃI)S>?Mu4_f'>-4oUy3s:`+e,uSM)^ZBbEo ^Ԙ+5j.R?(a6JTЃdTXnɪO:tR>Ke23UڿW\AI҅^)镯|%}{߻qF)RK/]j.[7Rt޽Rw)k.o&2,V5}ӊ-9e$i I}Hћ\^$ WYʈ)399yta WJA)(R(tcAL!+ 1!s7F)t/Ro޼yϞ=<gA&~_,ꊚ k}s/x?e:vO?]Z'RުRѹ?V3]hobW?O}n4+_Ç)ҺRFP JA"|_(R/(HuJ:r}SzM/Zl Qֻ43>ɛ6Hÿ+\F{%Ỉ𬂒BL=×\r _}7nܱc3jwE/"("EqA޽{*1ZN R*#>~^RFTn)֘_s[R):>`oP Ju% IO;dWΨ`浦[RDwWD|o[$r)UTyɜ}b(ڿ駟~WZsx<_G?w]|+d~}VYnݛ6F{c5gX31{lzVlRVL }J ]Dї!:RP J#%tKe~"RPj[~?a%WJ7P$*b)5O]Cz}߸={ܹY7ބoJSy/m{Iz&@'ڪ.6 J.5Pz=j]>Hyc>I\ͬ6˗$r6檫ڰa?I+K.ټyÇo.R(҈R'ʫH;۳鮴[H=ٌ"R>oF+\=Dϓy.#T*RPj.*fjVN,ݰRL 뤪D& K$S$Q`%˱®~dm:-[I?/HUVO6Q/|aɳ[neǎkPuwsיdONIkilX}=d)ry"5J7&RP5yRlvOt>TRTB=y,>j9RЯo&4X(!q&2DG]Xd)BZlnG!֭#Ky]wu뭷^p6=dƍ溆ݻwW^yM7FW_}_g˖-HY=z\-t}pS#Aw=D-{JE7w'$JI )j(:TG%RrK /qKM>:7tySVIgN9QzDz+%A[lk*eK7PDﬡ+'-SC򑅔r~;~Hq1X4D웚 ɒ-?ƆR*#JTOc>WRP JRP JJZr)]տ˖N. Qj#\1B:a9"5@XT"7x5grݥVf?ŢԊLzf޽԰U IC&=ZC%DI)E7QEF9~TT}!.iC'cKT8()*VfS V¬~JUԼV}P0TRU2F3[*i2iW8Xih0fԻ tp[*tFEԶm(bxL<5rJnn|[i}/σU1I$JyJ-mŬSnΜV]Pw0JUӹ 9(S%̮RL AJ5=j^$FѡNɍ(~?eS!e/ؖJLt P JAR~\RP J)P2 ?ey3ɜ*oxqKJ|2Eh,ObR<|KZrqƣh e9u=HNDO>M\ˡ.\Dޭ#a ƁX7K۽R.P JAnB P JA)(&PXAw)*RcUZT5GA%7%U[nt-ħOCX _ڜ{Pʢt8g@R 孊JP^U%@)(J1T*S6G^c~Mbq+c{ g_{q-BI[V>>))_3_22VLjX)] </QtkA)(QRP*MTaFyp2?K' ;/ث2 ]LnRZwAlb+ᦇCƧ菼^C$ b*jU9'6(Jm/JA)(Uԕ%@)(T@)cU`)eF*3 )JЃ|j1RV* 'p_Ss?sĚ0R$#.UX54x(T7J.JA)(L+:㚶52O*Ef4]wnyjBw MˆQSBɦi퓡=u)?#?RU&t@b(TR.JA)(Tⓐ*2$~zi2,c*Iv+-Wk$M>)/8d̟=2nbBϫ8yx);jn(JA)(ԳA)(՚R<~0nJO)gstO$+h.bpHzt[iMS`֍MDy-r]yR%Jx}jŌz>yR7RiRR̔$&2nCT3nFZ#vQ["s+늻 S@΢9vX%L5=+c%Snh!#YV5QRP* R*uK Ԋ^,QH{Mt4~t;<#ΐL]7MD2[Ejd|ԯOƆh߄tAE>qNŘUP JAn(JSQTIWPw:3޿HǍ2E:t_$$O2蹏LD6g>eEe PTO^,^2V-SgҎ)uc P JAR-aܔJA֔ Jۻh jV}RWl4'XFLC<|ޅwu K|22thx#.{[^/-$U>R]P JAn(JSQS!FxB[j| uInHڧNA@!]TH|wD6h qħsC+SGWSc^PRPT[KRP JA4U64ksOz*y,Z/m1Gt^͹9M"a%n2)%|O|DOr|znjXZ,ڻNMԚRP* R*uW T OZ)oa66'ޠY?Sv)Ս[F;2i2>E{c3>MJ=bZΫ>NhVRP̿u(ԀL53?jbRىې"5Mut$ۺeJ!xֻiQTL)RJU>ҹeTRԧSR(?(JTeI|jUHyp29iS2VvH*M2kFRoĒQ@&* 9J,RRP* yJ^EqJANR¨)%@#w*e]E"#VF}L$Eq'N5-Wl!tn2esY)oIXP >֠PK-QUBwRP J9eJEalA:eȴ <` )ևL-`LKރ/L}b^)o *CZ*](WJA+%DFO)eR'(uiJu$1ƤrIdqؗ:%dJRf`zt(%xa'>!*zsJQRP JuԶFMxJAQPFL)G?O?oWɥz2 3yȧSak^ hJQWU HVPS*$RQaTeL:oQk6TwJ^h)5cRREbi2>iCT)jSIifhѯIyJ|>B  )̳o֕RiRR T=bԤ}n`)5ۤ_So=3bb}!3[: ̌Uwf-Q TIDATRJm.JA)(T%cFut#+64I}镉=7e*T9f&?l!̋RP J%@)(J_¸)#e  FFF&D CB"oU9-oc<`C JA)(ՙRRiL:bFS5M 峌L:e!?G0IVi9;\JAjx:=J.P JuTT% !K.OsfzI^)@?=.SƪF,JZ]5hnԨ)5ǍRPjĔFJ))O(/Ӯ]8i hQ<䣁?>g.~̱BD%ks(:T=QRRE)>-y*+/Or ! *Kdԁ;RKJRPRLT:7F^#r\FK1My};=) m=g(ϯ]tT)TnPJ qRP J^pT<Ÿ)1)?ec0}0)%QJd5J)R r(5d5J)R rT JAO"O%@ yҏ9 dʿjCیFE*̎Fæ`ۣVU(U=_p[c E4Gז ÿ Q="Q}x H6;`P*hO5|DqyT©?&9 q p=fd JuTWzD7v Uġ[ p1ˑҨJ*U"B~RCwPj4ڐ)7Tlf!Г$N}*;<7'ᕽRUB*}W2/ث 6G_kJM=/bGEgxzNRe(jT[50Uħ闢.~I63YU</uP$ U\>ӍRjv q/~n͎~/xwH"^<_;or+|փ~?бwfu+X~'5a8O_o8GpTZsXe+}^wNH4 }wS=~;a)nަgkotC|JxPl~ۍߎ߯>$gC[o 2~;~g/Uȶڱ7oTrmx~#b1ߨ)56/>K>fr1||%* Ҫ0yk$qTگa^bU󝘅Xq$~כ~;懿fD"|ga9Xpѡ91*}Q (@@)ZJ*P udtfmu &~7sp<1MKl'RdF'RJ&/~r JPF<\ A()^ZJ*P VDtSDIENDB`splash/docs/figs/xsec3D.eps000755 000766 000000 00000011414 13261626263 016512 0ustar00dpricewheel000000 000000 %!PS-Adobe-2.0 EPSF-2.0 %%Title: xsec3D.eps %%Creator: fig2dev Version 3.2 Patchlevel 1 %%CreationDate: Wed Aug 18 16:30:42 2004 %%For: dprice@cass77 (Daniel Price) %%Orientation: Portrait %%BoundingBox: 0 0 349 303 %%Pages: 0 %%BeginSetup %%EndSetup %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save 2.0 338.0 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /DrawEllipse { /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc closepath savematrix setmatrix } def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def %%EndProlog $F2psBegin 10 setmiterlimit n -1000 6633 m -1000 -1000 l 6783 -1000 l 6783 6633 l cp clip 0.06000 0.06000 sc 7.500 slw % Ellipse n 2175 1575 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3750 3075 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4800 1575 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4800 4950 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 1950 2775 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3472 1822 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 5625 3525 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 600 2325 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3300 675 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 878 772 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4972 2603 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4725 3600 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 5700 675 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3075 5550 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 2925 3900 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 450 5175 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 1200 3675 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3750 4425 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 1950 2775 1969 1969 0 360 DrawEllipse gs col0 s gr % Polyline n 1950 2775 m 750 1200 l cp gs col0 s gr /Times-Roman ff 300.00 scf sf 1425 1950 m gs 1 -1 sc (2h) col0 sh gr /Times-Italic ff 300.00 scf sf 1725 2025 m gs 1 -1 sc (i) col0 sh gr /Times-Italic ff 300.00 scf sf 1725 2925 m gs 1 -1 sc (i) col0 sh gr $F2psEnd rs splash/docs/figs/starpart2.png000644 000766 000000 00000046257 13261626263 017312 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEg9NJ%7ݸMJ(*c6"X3GI$3է_ /D EG0 [U͖u+AAp-ĩW\6Rٰ>dm*=YSOi~;%j'џ9VOSLe{g?5KɎmxad~|zx߼vtr1.Gua\-׫Cr^كKo?[y ņl:X۴}hiz7ӣf#H&w4Cc  ˒qE t1"]`2 B.`YS?!+UϛPomkO<(;}QM3AN IDATxݏ-uKe$YCB=$`(]TF W e>P!bi<^J%Gfκk֯Y3{f߳|^/s3;{ٱAAAAAAAAY"U <lBo*>ÎR* W駟:/}K:묛nSOkk=nHT4ɗw^u^/59s_ڜfj4MKɮ]noRȞ={hr}}c=V=%վw>&>5 DӈsOw/*U]S~s=Uױ{9%Z*s_o+o6|rU_W˯ֿ]]}u믏8O~7^׾VGַ:Xw2Lb;:R ٤AUέsv>R ZSQTtBj1oj H|~&빸MePœYB[BM$f%$x|(tN7[JU QTeEU%U~NY&sR}i1A j5 UFVܟ)wZR$U4|줘2f"SOȈ9/yX\EUoO]7ܯ)_@ ֊17ÁH U&BZK%Td2)_BōS"< k*UU0E[*o|HL]j)dRC"RӤTU}J~Uurڪ*:'œ}ͷ.YVGjVMR퉪wIsyUc\M&sc/pXRI՞$=v'HEI,H:U\By2S5$h+=IR mBZBUVtF.f=W=k~mEYDejh @jIuST IL@ ֙LRZج{cGwj'~CIZ^U*M J kVMO-{nvy>3no%{dӖ ƓꜨRUURK> Hm[RU)}UTmj28A %0eJbLKH%F5H'YSծQqwQ7Gnyz~q`lv&^U?5H+HuIjզ@ 6ʏd6㮹(8OIIJ/iUYIL1}qIOlR R{HZSaTEk7Η4Q|T ghU*gu֖"Ux IL6m "HHM1>Of$`b멺k?d5^]@jsI5{HHgU;Kf+Ӟ d<)/U唩6 U@Twq R 5 HHeI-sqZ0 yJ6Wi@G*ȫ:# UG8K~z@j>R#M{] @ L;$kyz =.5y1WYt>&ck*: ¥<HHCdR RYR2 $rOb\bL Qjt+}*UUt餃HHMIL6T4tX'${VSEc{@ @ HԌޙߓhO,LZI&9I1q4ۜ1 Oc'WO]x[ԪWH4:۹L6*8K|g-@jKj+{Zd`M[NUiX. *)3LR'Y}DoIO#|&݌M8U'8P8ZU֣~H)hR@j>R;M{]d d>i4d9իMYA~ HHM'Zj]ӹݥyODjd 5-}T*&TY~?Τ{O`?Ej#| R 5n ODjyO&t c3HUo7}PCҪ;\U]P?$5qs HD?d"kӢ)ޤ)wO}ViP<4HzͨPTiXTZѹr"6hTh:,R 59ɼ`{@ x|jbIO}NG'5q1%mؙVʃٚAr&9ħR)?x]jR+6R H호;R kz 0yB,]T-jOsgɦME @* QHHMjs3;!eJў4&S/#]9\:>ngvU#`Ve-"Rd)hR)R R>ݿ.~'11멷kPL_KDikrXAC=?EUI}4{R 5eR RGjO M;'Mq mmb:J/8GšCUs8m%R+HJofiKHԔFy)TH%K(ɓʔjG\B<,SUPwTHl~Ɍ*E<&H撚+LZ3xhbOwĞ A-jY'SLU֠J:e,ZNU},OgvEݔ@ @jX*z[ RNjJ(Og,|IϹ'YBnvw/!T- KuQuZnGiU R/d%A_@jID_@ &66.PY`R{02ztLOr V5VoOlYU׀:>Tgx@jЉޚR]K=R 5EbN+k1 To!e[RIO )dzt yEU=PlYjTݛ^H}1u#) @ &M5}~}S5LO1ŞNI'tgm{ɡڤ,U[lև)ԫ3YÎ_x Rv&T+dbҞD!ELKQ q&?oh-k*.*.HUsHƒ: O@ f"L6=1Ӊ=]stsws^H O& =xqXCUEU*U[ѬR VI=;%5L\{B[A)IGDlp<^*o"V_*M")2))SB*%<9ML-^O Mz*Z*)ogR R YKտ,~OILA{Ғ'N*pIcVV%lEJUusٖr;MHeOTQD> HHeI0 $% OO&F}n&0ŵlzaqUevbZ@ 6()oe%+"5SK*Icܓ) ֮LZ=mqUMժL^˨PGHHm?)ԛ3YŪU.&n!5Γ)0s4ȓtL\w RSn&US.Ʃj[[*'R 5+eiKHGjj H5phOM$=yS2>tJ7eᤄnTiRA9UiXZXROQԇzE& HԆZc RMj[cP{t\x 0t “Fݣ1DS\u꩛d+]2(ƪ⤾ɦm R R> HF: OJ{K=eў <}Ve'YbXzXt]VUT!҉Ի2ٴHԆZu@j>Rd^]$ОZRC=Oa2=iLG;1%)a UrSGhcU1v.ےTHRoH|ޓɌΓ+j?$i&ĤfDw]hXmz+(Uӎж'L@ @ HԤt!7*$yҒ&1%HL,͋`vӽSuWN2(rH} ))? R3P&Ij`!uBJ{ Lw1ytM1ꋆe6way̢T+@ @ & HԌ.dHN.牓 Z潘>CqLf =eVȭ= lJ73t+WUJ-@ @j6RĜ62 Rp&H@RĈaQ'#LCy}7(u)̑ ʨʌ4SD'lE@ &#!i[Y p2U~ny:B*dbYȓd )wtZҍMVi;I:gd ưTTPpR?@ @HHMjT}yR“(a:ZMNCӍN6azUZUSqrJs'T& R %L@ @ T>Y.JP$ӓI/גn*$^v7WZM%UpXK-Vz_9R R:())?J:腔BJTQݏ!d6 ۂN}lEJUƵsञ讣2'jgR ςH<~3))?tS.OОQ*0a-~zZDRxvʨD9eF A(XzN& R R~@ @jT:跍P]FR<=C.ecz:XGtA3~zlԡ-ׅ*>EQ5TN Ur ))? R 5iQtTғAo))1$kvʅq.xaQEE-$N*T%ZA9u| 9^+alE@ &#L6R 5Yj[AB )A7K(1K ^R'AȏlBzTSw)5Zj))QHHY蠏;^ל )DwHi@%ڬ*|>HU*okۤ%˩D@ f#l;@ #r"VT䩒TAHiYo`VPS@J,2%'QΨdΝ!UYq2i9/ʌQL2?krHz& R R~@ f$d&HjWHzҤt }=*v#;sLwu6ft-%(8U Kʴ3JXNHHMI6t]Hz&Rړ* OTBs;t ;a-uI0K+RQLXNYR 5#_fΤR RCC{KG*QHeī˜/PUT tj>PisUZ颜gӪ)RR bRgg{; R 58˒R944;xIHhOʻT"=R1h*Ҫt[BUݽhù=t+]SBHDڀH-ML6R 5Y/)}hR .Nq&kDU x'sO/F"[qRDYפW[ir*P5n|TW6#)tH 2YkRzTo-nVsBJ/B*$H:ciY{ER&6KrZ:*QZ=MRvT[NeTiRV9HԤ)d3INt ӻ"e:肔771$u ^˩f)8&Nne%TJʓg9ʩ;$T R 5g"5@j7~RB<tHuQUnlċZ9&R~m r# `ujf'u9thSZZ@jFR"Ʀ@ )suR3R9Ǭ0R蠋^t,[ %&&2ռbUET|TʌQEZ(rR Rjgȥ 又_@ f$LޮRD*3hBJL."FjKJhR/شՖUGim)iRUb:h{Ą,P܄ EP&]ZZ_AyQEʱJ[gzEJ7)+87fR R @ &~RAnB:y"I7Q}`{Ur`y$JCNFMtBAg=U&ofTSKUI=#))? R 5iޙYA] OD!%%Vm R*A*G(rzY& R R~l@ &#!i[g))&H(WH ҞUt\{"R|,H5;pj%z[ IDAT;CƵK&),WuT$5NgYjgEY)TȉXP@jFRweiFNeRzes^By|ޖ&z:祛U:˧RTNl>r}t) R 5#l,iRf=CJcRb LRTB) 1"H|_|E2dK׃aGJQ>:HHFL@ @ TiT<:aNOĤLO(҅FUdHiY*jO' b*7 ?yQ;*ibIR.Z#H>&⤎@ @HHM*pK ROtK* bOdM8?FeVP׭tJnULRP^ NL@ @ HԤIE\,5-ASWv'ᳬjO['/(Rmw곋k' R*nI)Z/I}Q@ fR R ʏNHhS9].1)`M}^HqOw)m_S8(rR[%JAjhvHJL6" Rl-In9^(O}1ReajObbe7/lj:V[|%R;٢y("5b<;K-XGHHMJL@ @ T "SRcBJbHb/ *z >sv/|_o'IsUbVoD4aS*IJo9T8DL@ @ HԤ1HQ_,!uKb8&r`8|fobiPM(f*^ז:{衇;UUp-&L*"'zIcl\>$up& R R~@ 3;"gfHyH*1Fqkwz2j)14K(j?fQPj)j/O6Po6ouԯwUqRTKœbxR|iQ/)*NacNG\(?:HԌ?H6'55)C[5Ei6Ht7ChT-^ۤ`SSMuEfz)x9҅y27I)"5_}IMk @jFRgrI-@M @j{j1M]TTKyT@Kd O__I. )2ZOӟ'tPSHRLOP-Ecb!j6kzT\w nyRN R@LR 5#f-H-:̬YKeHqIyY)Uff K?9ϩ%]tEnRi袐:%ԃZ{1bRs PHHMf @j2R\dH%/#HMryNJ"Ux@ŋ0i5|M .K}jRz^WV=Tʓ)hkR|wL%E.+|z׻woZVj*㵪RNѤt!}IRHHHL@ @ LE*.D-KkstV^{ҤĖH}NsTTG5zꚩ.>vW}{װ9yQM+M8wl13fzPHMKI] HH>N8BkS}0WvCM)pOeZx 7g?G 77o\tE?i`>zP&xzwo$DJLOxHHHL@ @*KL@ң"OfZL}t=3Gekʨl]0}Cz _6~o>O6SO=Ujv\&IN3 olVD8RqA)IR 5#WeΤr % :{U%S\GJ׼NRenR{%ԧ?~wy{^w}C?m˩2CQ,}tb8yRG@{#MK*^ T*z.)@ #g ,kYIBU:*t9Ec>6ȼ{k!z}T ZO" .'?{Z~!_{7 y{RtP(sjV(>I[ۢ{MtjoAzK*DV`TH HHTTeHށٸ.HI3|lȗC=TWI7׀K^򒷾դ.H>:2-H*]KyTTo9E2,Mѵ-IMq)jH5 ?K}!%5_@ ^RH31ˬbR8z8t1;fVT(j2]tQ]6}s+5fI9<[uSOժhTm.jIj\Yj29kó${A$I4AjڀHԤ)2@Rt븉^\,fE.( E epk@ʞeo:R)r(.fBrJ$)stB{ҤщۚKhHHMI}%K'ҵGפȓA絔I➂&zYG/$թISF=^_ &k)$U N3 :DT3))3yRzYGхvͣg}`Wml+~e7ZAm~M}g4T:~eqGy$'17T<} UR 5#{3))!UTmURK:6_wK<ʦ.:j=ES뉪j)jQ!wqRǏ}.2L!^IՇHHCcHH{챉H1IHr*9&en,.Q)9It٤Tc~ R|G>^z4M#Hd:۹lKR8KRS&Kj `O2wR>7Ck'a2wلL&]Mw;zt"YHtNSHH)Ic*ߔIAA^NY)z}7<[QZҽgm PF.܄9:IQ-WvIPRvEWR 5#2))MXT#E}t27۹;VzYK©bTLn1ƹu6N{D5Izo!euA @jNReR R 'EJ P4 胶{8ݛ'{8S97u1N1:EH)bۏ[)q_PH O2I RzЁYNYrR R9))?*JL8)INxxZoo7s,r I2)onB Rb<'5nfRjtB:'))? R3-mG*S9Ȩ.ÙyI|ڴ,LOkRщt)iR 5#3LR!} )óZU@( U&tӽGTQbbBPx}ck{ں^ :D*AOR Eٷ HD:۹ܙɦY ;KT)G룏X.p2(>I|<STQ u1IA,&>ǵ'=*KnǤ3DjDJAtHHL@ @ 1唘 JҪt9,'RVPQah-sj#L^PL X{;b&; e@jFReR RYRWdɤn(x9塞:2N=l')DC]TTv,bu.Ądv^)!5ncCZ@ 4U 6 R3z, $MΆ}rn v/2"US|*,h[y,t[H )1:R[TcݞHmmRUf}R 5#k2V Rf0I N>P)X>LIQEq|JORwv?G.n=.>7IBJ{ vCT㉽x[) R U3Y @ >(Rf93&X.xQPQ%bɤCb?$>.A>~޹ S~tBtЗ! )IUG@ f$u}&}R In LN}HqUzVPpU;e>6_Jד^8O#%V͐v: lJmH_n%Hd:۹ɦY ;KdIopv@UUyTLJodtMpvK]BїossnNԬ)IR 5#s3.Gն1)Re*MN 8ǻ&Jway~!ݖo;1o= 5k@ @jҀHHLp!$iU/8)vä;͊$%S{gU%鏳͐*j#0IRԮL@ @ HԤ'%(RTN RxO2UѮ?4Q^-e,#tɉϥ/=ќi7IwнBJNn43))R RC*U}YM VŷH'Xv7˼mm=TcN/>JuxH ̌'A GUU9]gˋCMO < j{t$ I ))* R32HJA唩j\]xCA}&vqDtS}G'nIoI R R 5mR 5Ieڛg$,U*MJDz=LULa^<In**)INs^H R:j8K@ @HHM.D]Sf+=kU|ErD]UiXzAJkKF')twqvOq+ AL@ @ HԤw%yUt9e:Z=oy ,TY84j'AYHiOC;SAgꃇHݐ HHIv!enrS告[btPe&Ea 3qo=iC(SE'I5RzZO `HHHL֑Tg^|@ Չ'$-N IUo)tE5B^c-tt}ܹ¡tnR)ӓGjBj:ྜ3SA @jp@ @jt-u~bUQQV9?۫JHA Ht<驷:M_]0)n*J{{+I)Z:6JY*_NY}tQNVJ'Xf7=իJ2!hXaե@PEB~}lxRǷTH=/)=R 53))b'ء*x+]So䩢V(hu>7Թ* ^{W9n{izB IOB{ ;}H;{HHL֑T)4_?9QN(,P8ݩ$,/Sez_thwKe޴HHFL@ @*Kj^&Y^NU}1}*+Tʻɱ LL}.*'`Tr#I $H HH)4:*ޢ*P%FhMU*xl(C=]67 xZt_sk3))R R:3FS8VS,ʃ5W4#ӶI&bO,oGL@ @ HԤYUyórTeEJ U&,=XaSpUΫJGc QpިM텙z"uS& R %uV&FjHusٖ#NTr6VNLXqQTeja +f ;&-dOe|yA<%eq)K3)) UT ,nʩ@4nzKVf+݃%bJ"=]n;ҞDdb W#@ @jHHǙl&VzXNeTiXZPתLXWړ-̑ ^mLIGO&=.{3R~R R R@jFRginv*QNLUAQ ҚEO@I“.臎^cͿ R 5_eR R gAJ5<{SN:>A33LASzB +,IFnF{JRN)"o)TȉXPڵkW!%(t9kR.TZCU [SE^iĤ'&2&&i%>l@ @jRdH-R 5v._dHj"NTA9SC꺨"UX*K 3+lt 5Si*Of!圢i%))* R3z~&F.t+=,LR+Sey3[5N4 $A|ƓiAڀHHMI}# $UVSNSKzT\%TiXL^ 7&#{LUj^O&ӈyDT6 R 5eR R 姺Kt9IV|@UX%nŬtJe؈hC^O8OSϯon@ @ HԌ~ɦZU9[Sㄫ `a +YWT+STAr0]0K1}+))* R3Cl T᪂*V5VaKb i=)蚏t54 S} HH)4ՙgYT%NVIjZUɢjD]5ږ*_F.azT-0jBO? HH)4\9m?SUqXIUfQ5敁^LӧXӜνZq IDATdH}K_Jv(@ ngWfR R gA*.Vܪ )zaŪ^z,0-jBLT>l@ @jR?@ @Ouy%˩n+-)iEV2yVPF \<&)noAH\r&=:iaHmiRE.zTPNyPU0E,LUEURNKҩ27%V.&HՇ )?DL*RT[: R[Aj LHӿ-_rX )zai^DMFqh7\lvuIL@ @*KMMwHƟؿ_]xᅢUǙ_UZ%Ņ%iL#~.\c:?=1IjP1CdH{bKjUtS|O R3W)zk}S RA:=L9T-AQ5$,6А`O1ŞM,ʨHZH\x)d+A@ LgK͗E9ջvePUC˨Ұdxv|FLzג?;D՞UgTT-okL[<1 *sT0 R RS@jFR7M#H[NV7FѨZ~bBXց L&0*8w<"15ZT7tӵ lRY VmIgi:,QNS*5D;^ՠs ~ђnÔ,@ !{3))S/-zP݋L2Lu?UZm)H` <$JSfjPR 5w+ H6"SVzwIs SU`i[:˸dd2Ҙ'4J} R 5? HHeIL6 'P)z jz/^[Ia3FJŤ=j"OR R 5U@ f$G34R?K9[顪+-USİ2<^e{ӹIPcҞ\>Z@ @ HԌs&FO/cJ4sah[ya.1 0K̠Dq^<ՠ@ !3))SW*Z USxEe2y†R:aaaZ@%UY*TeҤ*Te,RUTܵﰮcH#E RǤ*Te,RUTe,RH5)_dHE/WZʩJkUܯxWs?6LQYܯUOg1i<l1$}je=#R;-P)_enTY**kP2US*FӅGK1 rH|Y Aϰ @AAAAAA{-oWyi5NYo ՎXf>6Hj47T[<7gz,:}(i ٲ[U_gίQ:?*HL6Ԫ~` >a'/b= ; EWͰԒZ@jM",Yjk>DMսl7Ԝ,J\x]j? R[:֙:}m AAAAAAAAAAAAAAAAAAA&4A  ˧(4 dL'dʠ                       dmۣIENDB`splash/docs/figs/starpart1.png000644 000766 000000 00000015576 13261626263 017311 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEg9NJ%7ݸMJ(*c6"X3GI$3է_ /D EG0 [U͖u+AAp-ĩW\6Rٰ>dm*=YSOi~;%j'џ9VOSLe{g?5KɎmxad~|zx߼vtr1.Gua\-׫Cr^كKo?[y ņl:X۴}hiz7ӣf#H&w4Cc  ˒qE t1"]`2 B.`YS?!+UϛPomkO<(;}QM3ANiIDATxFr 33.f@af0a֨%UUa[gFu +ý((? jBALeOxŏr.4Ѣ,6b:_Q}\hEeJj^>od|ʕԽ//4ѢlcnmqUu6"6H 8Dn(z]Rj&#J=T|}[]DCuy$K<)@@ICuy6I]}%jta~GN,?X5_ .˿;{ aK$x$13xWx"!I!^|?oURThAfI+хn|HyW:p̙v IA/ٔ&iCeMz}R3I&V&rP/L[K<&pzڢxBBܚ:>xz.sOyϟ/L#Zyš^/~-WFI)s'5B\] *sʅ_L㤺}O\vlRY I-x[Wx/5;Z;ir0sus*Ķ=381wWdrmO\u\sgN$x$փ 2%})Ȕ{z͠v%b3(PP~9'EŶr]Kq_ ݳ҃3~q) ) %".~΃ R%?/UR\K#=8=\@R@LIq,oƗpjz)gcW;$m)Pԟxݗju HFKR:zrN?H =>ݣ rƃ & ?,SR`I m)PԿxtw r5B6IUjkPUB.% o|DXT9_qRhM$UgnOiN3x}RJ!bs7x''$L/$xQIk,ЯI /fngnm*x%{3<.ub :I!Mx={bB;ճ'.&ֲQfɔ7 Dg^f;h HT[bA ԓ{͠vI,A Bʒ+R8vR BUorȤ^UO%IRClŶH ER@(KClŶH ER@(KAR8wȒMiwOII͝џ| bIG5UW~3gHj '[[ I!^LRr?')4eW>!)ūf6H M;OSygk)4e=g$5wF ;gqd BYR!6bA $ClŶH Ub^IpbA x͠vIH eI m)XR_!6bA $") %zb3(pƮs9dR{/,A Km=fPl;HP$ClŶH ER@(K}==xn/ Z S}ϟߑxi֪.HO浡W6!6bA $") %b3(`I} m)Pz<3}<ޫ@Ip0I=설.ܣ{Z|}m{!%{g>%$b IGR.ՓƗsJeI-xBب}G3?AR@$UK@,KClŶH ER@(Kͣ$)4uV- q5= v%b3(@R@y9=#`I} m)ʒzR ) I,A BʒzCR ) I,oA K=fPUv.Lj@I,A Bz-<=&`I m)P?x͠vIH eI m)ʒ_R ) I,wA BʒzR| vUˏy͟vIH UPRsv]k8 BYR!6bA $ClŶH ER@(K=.`^։D'U]hv=^7Lj7 EGH eIk~V~L9~AI_@GKE7͠./?RэUI{ZыȤ֋XRꡖTG@wH-w'1n^Mi֙_')!$qƯ'-Iwt`t *.ϝbgM| m)ʒzJR ) I,A BʒzfR~ClŶH ER@(K=fPl;Hz*rN]>|v±+]UoGb'`I m)P_{͠vIH eI m)ʒwR ) I,A B'5xnYR ީ؇wtfg5vtzrN=КFf} I$U[i&OLRe\Kas6j? Z")lL=R?l͒z6f>$)lM;ݪm=N KPLTE#59/yr%& 6|o)3lե,CU/FRIOLLOfIRivp Ff`\VlLFB<2,/(h"o,re3)b6&9_#<\? YV YB{\?x} ys_mic<uYvS OIb?9V) IDATxݏye$dlŒq]F *ELU@QbSqH"WI$ldbdHA!ˆzRZ(A5 )E85iPwy{3g޽㏯Ξ}Ϟ9                                                                       O `kb [R\ZK50֯͝#J>Xf+Uz{{kzN>}α:O?o+W\tiݻvK]uv9|uM֪|ֳu 6S_ 6mD7&}K*.U+֯_OO48uT/?ω~Կ* H9 {G[M6JgxkH .)CatRtnw5!R4jR&ǤD#HM5j^Su+aRg*CJR. Cܒ^ =-zrW1oƓ]n;vLHӒԌ@ :jUGɤnI17HW9ޛ Wڟ }@R> RJHN=AIђLO,E %rb4 7$2M;I<1)$6UQX: M9Uw2-_;U&!JR3@J&@jfI;&@j&IUc9$d'XФNgɓOꢤ6뛔'RbUJlW&y)SҕnTE_a[_^GӺ@I:#R Ri R3ERgxO5|3ΨJ<۠L!eH&_n>{&)JJ>)O-$G1ZD;gW}ÀHHm RCH~-U 嫖 3:3+o%UNt?n“2}Mc/{cfϩ#6çRt<)JU?O|T?fN]* 7Jw)Z6)qH/ߥHE-Jf6-7*E*Ft`U^LGkR*J7j53ꪜTQKj̗Q-"0QS\@JUc.qLN5؞@jIU|KUToL\ Y|eBn?~UMwOˁԤM%IUӱ*}RF^mѱ҅A *MOi8R|PR4=QQz(͕+zQk]Z0GB귗~jvTԼw7ԥJueoIf$:H2J\EH)5U%HWTfM1Zϴkpna4qt;7#&zXXrj:d*)OJާaŠݰW?z0~BWH4ʑyJdd RO]5UFjsP3zrUC*W~xQO* ?^7jJ+]p'zU{өJu 1"t%!J2z'RJjbJާ+M]s-cF%ZI@ ).oM 5 0\IM_/wѪ*A\UpŬʰ7x;b))2} `Sԓ$hLtQx.`#1}o\Y&΍><~$3=R 5R5uM X)_,ɬ}/m`ԋ:F*X'YX2kT,Tomz,< A`QGS.ޱ/x<"I cxer1s%؉E'%')eRKFeIeTIL=<)_QTyR'JHBJujԩ U7jE3K򅑸x-7]V+_{… Bꏗf?7*&5yT R% [63+AjPUE9)_!eҞN3=QO -1tQUX0iFE/a^t=/%O{)"a=R _K{"EJJҪH (CjtUe<)NyyHsoč4g}+Zժ|Q{꣒Iڱc(wB@ :$oJ2!RR !QCj3u"RI @IJ!KRɏG5W64 ݐiѺ:-Q%R,?uBJ04qw &qJ&}xR !aޥ'=7&HTtI%7!T9Rw)n%Te֟R5()9߮ Lo C 7\hiimVҤPR>RtnT#^MQRD1&Rj eOꏔZ @jHDc~R&mTyd'\R T-u Jr!%c<:5g+4ƻ2]#x 14ymɛ̗kQte$_?m3qOR It>Cd)uR/EGIiX<UOM?F)\{n\xP[k2%䛧iwaX-{hz 7Y&+] g0dHK-#R'#SKLl骥)F"U[TJ/2&RԠx+V'CGvdFG4ՓrVzuI:/ԪDl+:!zS?O3ts HA?TYI@jIԐeJʦ [-tG|rJ7y}:Z褸۷owTH &*/51L涬Q2et.$b(cO3}?*HH@ @*IUe)iS֫&I*KwM6L8?(. 1]p*8Y.ӕJ4eI/l`xBǤ>iVe ,yчܯfH%))J@CR$B*Zw|9%}AWz&rY oȔSDIhϩs3ݐOz49<;4L6u?4PL,Js֒- _5TSl^'5CH,cRGz)2 X,):&մ]W9*W'CUrJ{:iD=B6) au]ί]њoDt@p^v -y뭷ޤzjykq0ڇFʯ;ØB)~GI;)30RR 5PF7*o-I:W?BOÒҪZae<*ƴ]0T:FMԤLD )SKiO>ÔUIHb/\|W^ٻw/|c[C/qcd:MjPYwI{bd#p˻'vURϹ[<z@j̤nI&RK >5{{z֠175O*{2Ԋ&+:?w':؂A̅" ! tA$-O{gs̶8 -K4-#&%I2rHUM^Eމ镁HuHod1T%WK2 iGO)&a9^Ph%'OND8M]fV"c &*d(5BI2DB<L:7nt%⟴$IBJTe'- lHR' U A @7{d&RuyWa~ R%.Rj'"uQ%5QIQ }xSl߾ ))Y.qцKpÔICeTEx1)]9IdĄKH&!E3.h;( oO $xN!w1Ɔ)_(Io,;z"@vHLwR}Jx*,򪼧 )V%T_9sR+-!#a'݅dO[lYvumƪ8y0ZG2@ԶRT8z/sRmCƼAT;*#KRsNeEdvO<:IT JJ01 KHQ%dʩٔ*H<=rbR{Ѱʐғls2{駌t+zq UQ?ד*.yI\tZMC'd=!Uԟ,IsO,L;_Zf&ԅ>*"K2ReQZ˩()9.J_ɤXܺJSHM9eH^e~rcGJ%t_8#%=d.+!H [IZf͓O>y!v6!jTWdHK僨,9BtE BK2Iǖ~,H̐ Hl~?iu9)(aBʫڟ>nx)Ν;G&%uT4)qΑpj2!a]m۶mٲĐHy'UTt& R`יTʐZL0dHwOHY5k HM?qL{ fHRUƐr*AJ3D LWW'EwG4)]-qfZ#ӈ#3*y Ĉ|Ρ&t)׭[Ǫ<)>m:vD'IfHMÁKHAjH˒RKG#OJ2N۸jjri<+'&+j$n-3Ѥx$Cݘ>>rFJ(9]!p7Sljuut믿\We '8pUJe,pEEOζq-$26s)"R˕O嫉vQL)u_15?#UN:Bd)())ZU_kR2HđfKԧUy+驹ܔP9I'"e2'[y -I HMF1|V?:UyR:DjA)+ HH2OZI^Y8UH] ØHV%+&1)/x!EEA5S/7"$_~ \Eq >; 顟:t=Aŋ,f|̎Jr̖yt3)ݳ=SEja'@ E>ɉ1A dcIE=%Hv{RrJ GI]raOrU2fLJ{bI2Eھڨ…'4ٌWj\!V[W0Z(N^̜ċ1&@݅a^)ӏ * HHzWI@ @jTۊZIP% =iRYC唩)jHԨcjO<٬%Kf-AGG1 x1T*ŒdtuO'&LG M귔d" .Q':H̑̿@ ƻ2R)q!PJFY,EJRRQUΐZFzRԜaZZ4{lZ_M H'>|^!4b=\02&݋d cXrFD+}i>b^H7BjO8#NhKq/ T9R (ɬ͉R 5&=za$*, VRRʐcfohfٔI4=YjZ>AJki*}Ԡ1aC\ڜ _5%'UK9>H>!].2&oOC>^jIτ %牢}s) A)tITRf=]*Nt۷oפeP+E*=8uQ#&z!9+#Py&ŪK݉.7T8CJR R HT~[IfJRJ!- )]HU˜JVc &9C j-oiR `F *L*Y j/#tzg9<`alNLonzMr@v3 qds:gȯ0U tXN:$ R RHuH꿕9'{T/Y'ߏϓZbϣRZ%kҏΣ'4).iQ ŋ|ӯuѣGϞܜG@&4˱H]EyES_IJ-cؓ9 )CX95M&^HHT)( Di#5xM/ O;byŖ]&A7s q!EWaU:A #oK2kg)iRXxB^Ҥ<+XD%|=:~85 j~J$s7|DaO+Rt뽽e=5DGq.-ݔPEO{2g ],I",HQ%uUMkHM< Rc%)RR?]$%)U)56SHe:΍'aٺu >|G^RwM6l RrR?z(ɓo۶g3d&M$tt C;P();גēTQw&b֠G˒$1uU"ܑ* WY^jIdTUH L>[z2R[cI߱~Λ6m%Mt%KЖ:|&zꭥ>wR *)OԦ0RT1=;w~( IDATŨP&7T<5kÇ:t )gE7W$^)33GGr<|GIdGf\%@js|TUTϕdz@jdR_@jZIMMjXU%|''C*hUڟz,FQ߹s'먄z6?PDTZQQqcKbrsXJ#%Px\AfG8}ɞXp@Z]X 39Odc&J2kfAjPR4!RUR/ UFǦ&%#ƮH[Yޤ~$Fj}3ke4 )SKݠ:I*!$VX÷ibk%"E|I֮]KmwﴇkҊτ;tU5?q!t^Qc-J׳gd|Du^oJUT/ͅ };3vs\r*G=%Z F'I%Uj$6[!I HU H>+肭HN].yRFǏO^F /j =Y;ΉR>kM#LzHM?g IX5*Mឨɑrc'_(ɬ>@TY˩a $nt0U_)j$⩧"CO'E8o߾O|?#?3?3}GUϽ}'{1xJOqs>>z:2B×WhUysNSlyR|8Z'AjTR_(I*b RiRk{zF@w"?[|Q nBVR` X &)tI?W#5 nfDQ3lV?я~o}]zw}w )}իWRTK2I&׉.L94S(Q':2ԑX*ɿnl>o߾Їoc=V_/]'g/^x=pɓ' a֑ Ć5Zn =&B+4TdZԑi"UHIbIfԐ5ϥ#u5R?EfDL}RtOD˩ iSRUr脞I!rS|a޽<);#<ּ#UջxO}Sɪک޹{ꧾ/|2dh9/l9#>PD4JCO>d@ RR$UtJ()Qw)QU3Jw8 RRTS9)ByudHڃF֮]{u=ofC=T{~c_WzW^3)is!ؖkV`auBqսkk4_% ,n)E꾒tˀǖUVR/Gm}Q=RkBRi$5D")d~T wz-zO6w$d)I5/Z~_O`5`Qm{L20\X !ezoTD> Sx7 U]ڹs'⫿;&jժ{Rwy)k^k&2W}әDk[r Ќt}D7H$aǀ,-E_UW")B#CV"nTH$'0^YſR$ODT gT#G?DLZPp*{];)jHV^}I:aHyU)RҺsg$Πn械R2KO*&TʐސT'ջ8礤55kI-X?P.5)R}Rꓒկ+R|rR_)iU;Ϙ7x-ŝrAgK MJP>+(Ezx^UScw}_/~k_y?woR_R}ٳBJHáY4NnOGFUzΈt'}O[lKO!iR"ŢhFUhRgA* h&сnDb 55hiH}>THGJ4[WgϷV% *7T$.\h }TYvR6_*"ՒtO|')t?cUGu,[ JH /vA*t?dd}*uRךn! DDR!n-Ip#WisXT o(w0K/ɓ'z)|{eַ:t3 'y޽{ތ=iΰ(-δs m:g ZɦpnP?@)2~E109&ռ? H~I;hk UZJ뢖)2umCj%RF*&mkImQEDO=I'Ji"C0c̬,QLOJ(E0Q&[7nxmoy˖zZNo!^ymڲ7n?J]{/m SN@\V4RodbKM& ULjK: R'JRsKpOTѣGo8@jyj} /5x֏bUFBn7س'8Cbjd}wY <$.pJaO7t˗7o:tꫯGyG%R=Ou]G*DUw}]s5unW^yqU$%DUHJƖ[C~:7Jz-ӭ*Stu"ٳ="5_ڞT*ÇoYtӶ:=z /{.=u{]F$OU{SXԯe*T)IUմ=@Tѣw4a\-o7MjaH0 3B1<,mmJư""?uZ"=\3qD!R<",n޼dR}IFѤHۨ-V7uMTUƓzfBꡒH)X@jfH.) %'ƺJߏ-]ƻJTEz,f$f9 J<X }뭷V$:Nz*t9D*&E)A>IEI #oH· Frԭu[u.袊 GRD\x -~%U ]&|<VNϥ:ܐ2t!eNE%]Q1t;śRJJ9XˇHHMg@ :$ْ&)߹ S57KʂQ(zK]'ME:/4_55U"U ,SQ'G'Uܛ~SuOI]辴B>=+)!%73B֐bO2ŀ(}R+nL p HT) HH\IfT'ܨbXE!k#&2K*bL^bUҕ._>DP*6t ]I]"BW}ˀ}TʓRQ7͙ŽhLU,)Z|@jly)ɬ»Hu.5/\wIJn`FœJ觸Ѱt$U.xFpQőr 18 -Ci%r;9ɜ*Jf<q%O=:xMbXLb@ -GGhr )kRQOusCL.\D֕p8 #UA @R) HHT:)*  맨R91)+՛GAstKQDUmTk&Z@%L|Dz0աǛsꤪ(ݜS0Q@LsGmyUQ(mҚU_xw~O}U~XW{χg oCw*I~rDts'DPPypB"[y @j<'E9m%)򤲟Β)SՒV"P*=&@ mQ(N4)_~OsnNz22|4'!:nB$DJ=Զ@ @R@ @*J+]+/t~ydJwLa֡;NO$=#!(I5A>Sj4%}}q(I'):66)/ɓzRu:Wi}}.P&TI@ @ 8 R RC?QIJU'>^CD~mf[ѬF nƔm=O'_E *yDIDATy1i[0VROǾBH~?n˜T5x-R {J2m]| R@ޒL)WG_/?/ɓjjVO^a+%]Dy@R-Pn-Ut[R]("7B*?ZΤL9oQKVR J2]FHH5U[.2tjK򘸄 /˰QkJ=|^Mo@JHU)t@ :$$Fݻ5TvXITYHS1#R `ǞrIM?,JR R HTn-ɬ XM*^'eɣgjL3ATE[p*FT\S@JR 5sրH \$u$3HJ,.hD*_%E{K0}-,; LI@ @ ҩ)`gK2k|cV(Ek0{Uf1#In¤U1 &Q2Q{\dyXw))+%)RR/dHynuC!ed0AyLw%J($UU'xˑō~,=݅YU R JR R Nqhҷ:ut9_ҔPʈbͣU *p|tgN*FowĐJHlR !;K1gY~nhbXtHVKJp /@|k#8FC/(QR Rݐ$ Rf RsƛJy9,)D1#sn'2y *i\ͻɏTJQіc@ :$XU,@CRQJkF0T1P)IQFSzI:o3$OJձ7[;R{Jb-oQ RFj)2R]TMDKIrQӉ08VUCdHE&mCostL\'/TJH](I(g\R !J|DR RcM੩=5r/`Ojc.+-< 6ޝ'˱D<KH}$ Rf u%R-|x-ER<~+K2ktuqʬF{Ҍ8r]M[4<)jж=<\)J'Ծv%n, HT H ‡gˌ$L*4,1yIAzjU5hG cTfC{ʐr @j=#,{ 5)%5R' R,,c($ d'OStx RH) HTq@ @j{RK2b=<&?eILnU%M=R⌎ 2ߤ/ H oRgJ2kNO\3 U1&3DS)GWՙ<lJ=cSWHL[@ @ds%ARѼPu,}{ayI!8K^d`HEb˅)t!Hm( HTbˁԦ>))+S+/5O 5\@TӺ@- մ9&UeIc'@jF2D_jNHֵ$F{T0\*I-xJP# .OG 71PY.R H и@jI$%rG"@gL]7!X]]x%)-K%)]Ԉe6DVo׻G=!u$ RkCo30HMC@<4]xH(ɬC_3 4Rfo IPQG+H)Gy|RGӆIC{c;)o3x,m֧y;ez, Rh?պZZ}G|j]#=s$ 1H`LkV&Ԑ: bIa@ 5 5 )*^ऎdH iIz괍6}-xؤza) HTrR Rb8PDORU~Ŕ2އxWHq@ T@jԳHq@*Oɬ2+qS0M4bCvXךC*w)vAפq) I'2k2RS wRUd5MR eoI@jlk2 ޒ8R@6 RS!EXIץSUF댶Tad64)m9 =&&גN=lw;:v}۝v9W:qu]Obys w9Ne/5U80.Hkv7qhgq*s27sls])nL/v&yL~o]a 6n7w]7'ݹL؟nz{nP)$ķtwNR%؛v숛o7 86QO]j \KmR?pwn7SK 65;QTPVu'ƻ=_hy^Sj? ضZ5?&]?߅%xM{?g|Zm]H <%nz gAAAAAAAAAAAAA3"@)RRBd)k@ AFQf9=! yeTFNP RM5:8H!H{@ Aƙ?RRTj)&%n9B^k@ AB "                                                                                 a?q#&IENDB`splash/docs/figs/sedov_example.png000644 000766 000000 00000135566 13261626263 020225 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTEUUU  !#$&()+,./124579:<=?@BCEFHIKMNPQSTVWYZ\]_abdeghjkmnprsuvxy{|~  "$&(*,.02468:<>@BDFHJLNQSUWY[]_acegikmoqsuwy{} !%)-16:>BFKOSW[`dhlpty}ąƉȎʒ̖ΚОҢԧ֫دڳܷ޼+tEXtSoftwarePGPLOT Graphics Subroutine Library5? IDATx콇wW7~J+H͌4MI;@QB`猤]_zNr$9Iڱc-vرV_^ ﮆx?xs4iҤii&Ć}r/)54a=MB )MtH/Ԑ4Bz@,PCJ$'0SjHMgcyJ 3)A8RCLJCjϡ5αc*<jJϮteQ ͟OJLO )MCPVtm44 A;~ݜw?OX{ãOVH10!kڐ3]|[w^ߓ> ,#ogiX7n~S@})iCk!pW<9#- -tExx3 )bJ@U!5Nz4:DR=6̃)g/\v='݌z><ˉhA----/zQg4[!e M`D߽- THc?S~xTR:GL;F^>_]ߜZSs._-f2`$2Lfbڐvn[V+zЌ~Cp([b5XPg~X'@.`:R'FP:Zt:΀LLR/R$Du]h< dF8YIvt!Xf2\zXbQ0W}EՌG:>poƢHF ɞWSԕ!5J&d&@p`(ҡB` ^p٬, _{*`(T ER:RoDJqpZ:ib*4O''M&˃PBEXwttP$OҐ(,o6ۀYU[jq=nj$;~E؛hlbbHJ^on_ L}Bdv[ud ~?7w8:)";ף[cd*#cU'Bt26 2T*tX4Dn7FJX^A2Qr>0O i=EBd[ZC#/5б=`VYf{-HWcA/1T4Rϔ^8X Fh\RoJJwD8QoV$3Vk(S\XϤpʡW_ˠEJqiYn)Y/ `pY*dRET8 R 7Ѹ2RulQKR\^h:Dml6wmm+|&ʵs!|;nh ǐ /V[]!v|:A (R'-BM`F4.14;'ڜ^?6QF{ۘ'$ПGh"޳SOHT#~]+吡 z6 "*ΟJbo7qe)!NGbkrPG5kʘtNt$K|J~p"cIe=@P4Z2w;mfuT1!5b )$b)1O2[n0- x|F#nj s)&C52Vn?" vqTZ,w#hXY7S_sc?BM`F4.1IY<R /؜PtuPi`wY-fӉH繉WFyy@pݑ sPIˇ,U 1U&1bݬW!FT¯?BM`F4.h_n<wa[[M*m;il*{vXߴ g0LH:,lELA0͗4Kf^NR,s4X!jqlsӗߨm YZIB486O'JAuD7Mx"X b D۵Z,s!{WPI_T ވƥluxRuL}4`#pň@Q(N7S{V@2U(HPBSnUzBR @V!&0{#)Y﹨cb`؝p"4ݨӫрe7 BBı]C*ܿFx BT!h0.O ΃"&8l"sۡbINCCl71I8!%0[2Qͭ,m7PN\4y>!80Lא^ #lW^؛hLzgH=nJ Px&JEE [gsT!Kd* U<^>h7JXJ|T ވƤiGJw*N_8.;!YL#~b\Dq",!^(~cTQS5;-Gh zY+W>d%;)PBM`F4&M=R2^rq>䱳pm P(B 'O@8(UX! b*ff 7 lfI<N+ԧ:h'bo71iڑRHM`ύ\e؍2AbχMP' y- cTFI`p9r'd+>ΥwKQHU ވƤ ): {}xfwp v[Սl"(D! u =>=G,á\aDTT*ff琽4Jd7d(G{[L @\f> zV .އ.|0PBay4a "TK*$oܼyF FSRc~(4Zhbވƣ)B MPtX^N}#6K@} kQ@}"։D80VZUTA)@D57 Ie6;P"CLՊkѐS3bo7Brx}{\&p9˚NMK"(SIz 98!MS@ f RmDT {GR"P BCD.Q$uy PO#8XzR&ZTK$6n^ #mt8ՁBM`F4M#R 1RBj`;&Q|b;}/MqD@`Hd8*RI XGVC(A :{#{Gӏ85a'k|*pZQ[p9}B(px8~~&SWU*jv[Ho~Io8TĩHw3^/gJBM`F4M=R/UWRjЃ(j(1P''9K dIz_zwP@uTTwU):ՙU ވ"ߡrDRFZEV`oWse5BEDqi P'MOt+*0U"\ FM@.JȊY {E RH!#7۸ibN3߿{6QzŋD ;Ob(TL]?bn߽~f+b%<'EUN`5|J- 5^HAjgԶXc38^0K;zJv+BuT?UPAw~ze˵6rڵ* :z;zFZY /RۻةjTsn2,-@w!Ӈ>$geqQMT*PA z@waidwuN+k $ʘ '|D4H jPJ=:loQ4W +8^ .9}r$@qcC%g:/L㩛߾sM6qpVY"럕 o'p&>1@M!Vl㆟z1;l45As}F`d@xF+* TzTp=0;TtQlC/ಊyL+Rrs#`3r &sMH5Kde1@_G!p51QDJO*Taz/\@L"ajnq`qRS\2ytAR!&88 E2-HQ8Gm Bŗ_"&8 hKLgh: B<~E|ڀz4Q2!"w"o X%,{醈>ARe2&jkOSHEN~Zeqj("].$Qb ԏe:')SÉ?A.֭;Hk鍤K(BQ^[xVLI7THuMd293I~ )A'L"gjz 5u%(Xn0)%PE9NzJP2XQ8^l  }I@j,gԐ1G' R|'R,mJ]vNM@9^.FM dX("A_:M(Z͔qN}IH*qJ+# !st#E")݋=8kSq)\%Wq >܃$بQNf K8sW| &k CAKhf6%A_S氕 !st"\A۷]B. 2Rh^"#}W0Q'qQ\^B TA~?5_HQ@^1BlN2M]oxH3R 4|~_-(R0RHo4lBgO8R]t"Ξ8 D&JSOX1E|?`%H}6r{3Bk#>᾿}Rb)Z'Rp4!LWi^!5^.5A8sd@D%'P?m⻸bvz"p)LNtVkH X'+yiMNW[(::hU !ݸ(RH]Sb Q'*S4w Z߅ /]=/nKۻPP.KsbYbo7K^QQ|X!;9XH }7H4G>!}.&R0Wt IDATTSNqM}Iܛ]X6X=d}~H3*foDH85Hlgs=rYK3$I]|Mp5^H+x}rz%JfD6S5Q I/X\&VV1Hb3ވFiFx /}JktDFN#%&Jē*'$i˴/uX4~Z:a‚J؛hjn?_֛hJ&nI>}|ۄ.&JPP|?Y&|I_$ҡ#}~Qoq0(2SIbo7kjz#bac9NCF6'ПyiDJ L=O?C C~P_69ܑ v\U为aٔ?U!&0{#9׸~-f`>҉$I:F}J#SyD#5;dBk9bfwj.M-/.JN؛hb^EH-B:hI}v~}w~Yd )>72Q@}OQ=R4SBqFSBm y~vG?M3x?4%AV؛h)aG$wfd  Πt\tߋ:_ 9Q'㤄UW~*f g(hҏԦ lI=)Nޮ(7э(@IdJxRʽ%mXخWګǕ/T ވFF޼ P7c( 0RC/ۙ^L)%9`j 67R!ì_"،Z5Hݾ`tx2^(ů ;=ӛ ``"I?hH%.\hwmd) YR{Z 帣AP(|=V\HU|WTW̒Tݠ씘)ROq]I)M/ꭎgls0JT؛hԚb^xKfw Y=V!&0{#)dݝۃQ"hg3pXij _M VJR0R(Rr$AAH-:krV;\_Ue5,>P؛hM3Rݛ58C|uU ݼB6ŇOdPTO3#L5%7P.Dڃˌtא:G"//\~$6@H7io:AVȿ" :2=)DjtK?G6%z7BQ~zaT fz RiH͛qߩ{XSn̛=w ~0 R H=#!S%^vu6p"@>,]I)G '&{#Wn.t q(%IN L`lAAG'%5S])$NqinF@*foD#֔#5hsiv5ۍ+ tk$s\lgoH :KFIzuMbS!&0{#_ b5H]@uBT_ )R;qHxH~%%Z-C]bo7JP< afl\VnY~dk$J=-K PTw3#qO@wuyLݠZ r:qV5LӍԥP/J=4;;=QSxɔ/ x1Ro{͞iDH#ep~H]+;:H+Rvw$[+1R6œ!bs7jzT\ᑂ#H Ca恐$J)To`(F*SZS H=R!&0{#)]'TE'icf#`?^ iG;͍*foD#Ք EiCXQ@+!%^+5H*~{# ɐ:V!&0{##;G) e䑪RJ[DJEYn1EZ1R[]ԑ 7~# 'Q{# 寋*fvBat5'H]dGhnb^+S]kwHuSD)Ưމ_ uރeKmoGۡz 7yѨ4JdtOpڽYu88lU ݼ0;siI=g;Dm~f:>ى/^~߭;ffV v_"]i=6xlf)R9bo7I 51.`,1ãv5ۡ"E _2%OP N)R"#s)x"7L2-j^ѐ=H|̊=1) ^x*d ѻGSfRlx̢P"] ܢ AA 7ш)>6F8`;luiW%3{lX֩<«c>ɰ0s_Cj:)?83FRfw Y=jXY 7K !L:+TO JI sHz mfYq/zm&T ވF#Eoڬ/vJ[Ajc/Y YۙPTQm">H [A҆׾nQ/E*BM`F4=6H[0:<@)xOtH=?'MQt@%"KWߗH"]^,}Cq_gO:)A-8G^IV.}'e}38͆"*foD#r$ySy\⵰|"),C|ts9?iLFI΋I7=F2|<4A} xѿI{DC[wV,| SGd,C$^FHAi%2 JIqHa#E2Kv,`jcR\WpR@*BM`6ѡK+Ca<=~w6o4+dajm\,И:(I4YEFG I(UD.ayITL؛hRTF!ԝeNжTi2,rH]$ۂ1A ~$}RVjsrىTT؛h&!}25z|E@jhq Y3 FG-Mq;dJ~)pJ P}(EOf$R睙Jf5B"Rbo7QK3"u wC hO@,<MѤ/LIꆕ9?J$゛fg2lyHqeiq^TX؛hRp.Y=:HMvophWmH=_֛Z3PĹH(駒S@uD5)Ȟn"_ƋvJkӂC))Rbo7[VLvW0)ph=Յ{_jS4C!aJ*)P'" gM'shD_ IV$H=f _iDƭsK+FmZ 8LwˢeS_; `DR\%AQ > ={l&dHT ވn~R|Cjq`{"e@㇑Zћ`2_#IF5 65 Pݳ8G1%ΥSCJx:}"^v~Y"#|ljdk7q$%GʡBM`F4luD˟$ GtPy]'"+\jgpJ씂 *S%@%5)c $xoO.Xq] ) 7ѰSFO䔯 R sv9 8$CtG\0T2䏋M"Q45A:>҂d4;Ly{X~A_\>m*foDVwoH)t$RߛC)'2SEX)Xg ՏlCAz넪*uWx%6g@ >훝ùhP%F˼zZ) 7ѐS|}35d<RK+Fr/EHmI[.] 47 S?nHA l(~m tON-\V#ĉ{#z)v'ѕ (Bj`r/$6E`cYe(SOwcJNU7ߋ,(&h'M<_\1;=t:Jd0H }2*foDU>+L*I}WKBB1Sfxv Omx|_%!E!PXU/XL K wCxMHᵇKHI2{ W.o~"HI/'"/Rw1R(2Kmظkw>K") ؈gyCPuW? i6 R }@Ų:MȰ]V3Ӑ 2R]C')EjqYo8B|D:d(p~d5"TNIݿPO,F^Wz*miخ !?,tRbo7J= R|*cE {~ze829.]QH8:NQp_.CS%zJPsHlʙmRKR&e0J/ dH Ml!75-輑"B 1ғ>h3ajH!3~/m6В1E(N]x9P?RqXuO<>F=_~ R,{`9@^s3| "R *Ġ7T|L)lV7V;b6w،ˤ⋙m }`SC[*T Tu@LDD/,(Jxv FАz35nD:d(r[()R3*4y:rc>8u:͟"E]FqF?>mlqG|ކ׮ }PT*)V]3O(lL>\h!n!Fnof:&c^bq›Ǟj&"n|Ij ) pԧ Ο_Lg8~)&elpcHj֡$rU6qC'UdBF f$?^Hԉo{V'R\8es ¡Vl~w ')^Y 1TU"[U?p\>DqNNL\- +Vp }|',S[3Hu}jHLA_©f G{ d~O\:$P RQDX)= Ob` Շ3Xp Y=|R|'o4Dz:T)Yp<[jqj2abd/N G,U'TJxw:g0=XIvMl_RwTh  Q: aн /XۇȭҫC.>r) cE%<" 4I0QslgADf6a^YOD}N *4 e8 1n J-+"% & MF7'ԫ)jy\C% cEš$‰]> h"[yЮsuLF!Ck*foDRԊlTz}?HTrXHA6;8KAB*RSE`Dz D gHZL IYM0A^_5^d_'RWU ވ .cm~K+Д 3;۪n`Ce 8Aݗ)"bE$&'b@ѬTwq1j.)gDvJF&AgJGyX5bo7!t2w;R);xMLvKǃA:)TTaW,K@IJpQ<%NO0+78*9IAjQG)# 7:M:(:~t"(5waeNKEiN`iZ*(TU+ 'y>ERc)2l1pUCqTƝ}.bo7Ez"yS rs& ET+PݿO T*bW,/D<>ē,\N_$ha"|+""J"uY؛h`MFM~򹼁zLJzi9](2T`TqTqXdZ> =O8s"f 9}R}kLP]*^iɑBM`F4&U R|hKwCzHTKY ~@E?L |_l](L&uHLJ(E9zbJېz%EboQ&MmV)EAsh*W t|_& z"!fTkDT]!N5)NI"%2-v/H+MFvOF>݌/N?L uM\O} "8@f.F ߬䳉myGR^R!&0{#Hښ7'˥vx ZZr e" 扥AEX]~H7 $<>P[6 +׷vq<gvZMFƾߋ*foDhm<n*RnhjrXQ0Yӕ7F7 Jo&'f77(K kXAl1W%E 7g=;)қ 포a7l,U1EČddD`quwG1L& d0$LR(v N_!{RET ވdN8@疋ʱ}@e9}zlaYfM'"ہlej"+ђ?0A41-Vy|TXEꀜiݬW!AO#=$H=BM`FԷ&ܺ#(l91R|T7Z)b>b*eGKq cE'r`8?IQ D!Y̭ł~ +KzN؛써_M::8RC ,2mrjsCYY+3k D鰚WB +xKW i׌&Z)Z{8:iV˹dv&00Q{ɗ(9N92YmN?J5]0UV9 K9 8.o IfJ}8T{Ve E#JCMa %0-P@fb6T2QHը t2 (Ba+*L郃b,6#15Q`:2Qf1J^w" 7Q?R:cx0"k򅰡!fTgRXr:V X+LBk ‚˘%Nt{}p4ʬ(;8QB׿SɅjQqJ(|CD22M)&c=5Huf+Ωtx`,Η LPYȯWH(BN  e2\bbٝ@?WFe<zVZʧWcA}YHR8M(Ftjgד1kIT#%s'jmkg*Urj" ~p6^vt= )Lerj}vl5u& (RC)^.FtZ1B԰3~X9󷴲BjG`4Z/S)H% S, l:Bav\N, !NT:+ʛ&rPE:@DQd4'&J"&~B eboD扅w1$<{]J*H(mv7YKuLL!U-X!cUȯgkD<DQ8FZ:+ Tkl'f)^f6ri >RUuN,boD቉71`gju!%T |B*&ӹ2UUݝVQVJBn=ͤkDL66Jjjw#j\:=.jH`4XxoU|fQ("K} dhvۭfY7B!!BQ.W7v{w`ڃx YD4L%u{BވKQ!%f $-6ʗfkwa#Z!sFhm5FlnmZP/v[MH򁅊|~m{AEboDQ Q  Jz~Rk#.QчV"5S|vh5,wtR_a n /*D?'Rn6pt^ u/(ay[jxxemMjHDboDE5 t"Պ^oKxH|5^Fj@c N'dP̵Y)sj<,Q J8@) 5;{#R!ņ iLٝn0U*ت"$PF 9lV>yH!bkG, {\[c^|/Q 7ېr{|ދ%VE E+TRj5Ff/Z098H}_T써XhH^z]!-.Gh#h,O$I$#E0B \:6},%w疀Ty0EHIҰ7#g՛Hvɷ*?BwO>oVi;w=WCD lV] zjǣ4ۯ ׈:a(IϾt :|wGl$F۞e3Y,U: D{K^e-ѥ'%鼧vvSOTވ:ԱtEƁO=K^%;r8gs]{ObE^K$ }4]4 ׯ&\PO{sMƁO~^ w.WT:OG3 Sw(vvI.F0\! p-?BFKwN(VG^Ϟ<*ބ`nD14R~~'%=(u [8mRn>t4sw.dS1U<ӐD5D,}>Shi4Rlb8P]CJUxL'5X' )Mc&)G~lRN*vBSү#C_㊔Ľ&_*i_t {7+E4ѱs~iu*:}ɞ&? 8wth2ӴG )SJ-ztOSp"tv4i:x)GԉZŧuZ 4ބ?=WNR"R'>/N}>^Hi:fz|=ߌ)&McQRVBd R~!i[]C>j@ibDJni=U:eO )MLD;6l ]651k4^Q)y0Fex^ϖ6 )M4iҤI&Mӧ1:V/МvMOO:OkL\tVhbOclRNԊU} )M#:xgU54T*Fw1 %jHi&hRƯ"5ʑRզI@h,5vi+s4\Dj u^h/?0G&M4iҤI&M4iҤI&M4iҤI&M4 iƚ4 WښMM+m_JCJaJs4i&MCWjҤI&M4iҤiT\zM&V!ѤipҤiҐҤiҐҤ?Fu--"'hҤt₽OkҤJTt[ iҤ$FiҤBꑒФI$ׄb)M:wO'I^_3EB:a(M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤIh4,MO ?~_8SjHib{R'^!iX~IEO`"<Ԑ: J 0RgR!:~ qɳ'_A?OJo'P$ϴvd )MCPbdLRcNGa =R\C·N0CG/ sqH10MRϾz̬\Gx<*c9{,6{su>t:ʬ?P!!\yN2V:Rs:r{~^CGn~X. QC'+CAϪo1SHqY{'ɍw!u+Ej=/H'=?lR_LHqM󟾎/R7 ! #Gj^&\ B*b"vtFJ Rg)E*ft"yY=cQЀH},FN3TD,!xWVzIН奥%WWU! gOdqJ<5c: @NGßKzљ,PDˬO`4fbEB?fɈB\ayJA.n>:R-G钷)PS8$#' H2[vp8rmɈ$Hm~==1Q5,dȑ@ 8HRñRbM0,V FbɵlPTkFs ٨תR!]K#ADž2!jVhS#\H o}D"jLxZd9pt5S(նZo;>}w~7v|&\Nd X X[*nSA1tuFzhRLO#(0O67IfrF{_?䏟}_|?߾7ZR!NŨ$HM2:B{߼Mw}Ͽ ?/>轷[Z$趙05_WAj̇f n8A2@b}c>o1>Wo>zx|txpp >8<:~M[?||GYYA(EꯩHrS2𾭔d$Lvg +_}|zt|tnZm~o?3v߿p{c=wZBR %$2}dVb4;|To>+d{G&`iPfZT*e$Zܬ-uxƛo?$÷mQbX!_Ô"WT٧!5 b.VoWV?/|~'7#Dx!Jb*%n?f~-䲘Dũ!U &cS~*RBpzw~VY(kd< ~x䝽J.q)%*foDHuf+ &Jorxw}OF>J¡v:&X :\./WS|Yo:|ֻo5#~Jd 7Ѹ R\L2MT"SO)Y YSY'da(#Yuňd=VQYcTQ5Rom^+g8Ibo7q%`eY:xoOnx*x88vL~WXo-qk}ay"YVd<@8?Go4aͨ_PT ވ%q5xz4G4?bz\X_> 'l8]ex/ EkB@_~~XL+8" 7ѸR'ahNҲe}w_wx\z[G^8UˤMonvffBw%V+É4Tfkgw?ݽz:ߎe"w 7ѸRjNp:+қD>x#C57+L"6 iz߻{۷nzs;wap_٢b!BdPl{GoϿH-P_U ވƥiEWh/3ݱ[|7~;z:Fo-[K6OH.ܹw蜁ff,eff̖u 33VRI%T觤7hm[:ߚs#k~zuoV6WWUT@Jyy9@@O0//?U^^``nhlr[oJQǫR< O(YJCP  C@;=XzC]hNܩKI Rݪ2tƶ4` =~8vӟR7QHI+SDիK'W!r}vO ڛ-FAjkAW^VRiXG? ħL rURRuop|fFupuG SO+C?1g~#J^ t"Qe(Is~j|f2@ pBM Kc >OCX$Vť VNR =íJ!B &0'AqE*GcJ! eKSIQG~k~zt$Q:xǐAA b% +*\J?T;<62@hsݬAӑ$ɋ߈tt) (E59AޚI`wGr(hPy$N&D ,oZ,hVȫ`gYL F.=dEb O)OoDIR"GRw~$]l/{]>{SINUVR y"݉ĉ ҏ,ҲVHJ+jfZT}C<@ jfTI.RHsr}WcnwۋݕEcf3" Ax'ҜE?z DF Q *ЩFSCotz]vZZR7QHI(OY cr.8;\AXG[YU\T $P,^Da-S9Ⲋjxt-76wOͻ<GA"x<רS+(XHoDIR:"ͥ"!4qA@O[H&Q5e0ˁ=O MW@߳xEEq*'/ "Qky .C(th2L#EZT*zԥP4y7ц뜛g\XP(C "ԷC -+V*/(.C?769.n_C M(IJG+O@Sfβ׽0=1 EU!23I$J W4TiU!LB-Л;Gޕ;j}jIT< U(97wLb'@<\t;[&}%ТDOt*S)U&,W%*301,m܀omA[O$ ߈DJܥ"*mCAfڛ-TA_ˢ@ĉE_Ec"?TTKub^,rc(}\*M2MTrfc>X$7(xEQ+몫AW$6(' '6L_r PEZ SfʨQi aȧ΃ݹ.>FkQ(9<\YE约Pp QSC}m6# Ed~,(pF\(XTNШrr ˠQ5PvkDW|Y.?H~%GRq.pAJe%D}Mf=ZMeYIaz |CxPVE Soޢ/'N-0Z?''v#UZY_Kӻzx nN41:XH7oDIQz"s"om2v$.VɁ ̣jk*JaTGEZW4P",UT ̬܂ҊZ,-NZ2+v]/#LJ9[9g;Z(()̇ :>(O|xc*ک"FE)J+`5X:\p3fAkHb)JOD]LGkn|݌Z*+-͡>dQߓE9 NE *dTQ"EV6` S b];z CZU]_R~GDR%psΌ:G/)~Q'.U"LQ)շ߲}*3+'SuJhkuzvTNEE꿐O`R>(}u*sQ0t~q͍zD}c7?(b$Bi "?~?Q̧*SZcck(@:5hREi`!KP'KJcx.&}޳{z$R}mM&xj@T^NǏ߾yCQBIO\"T1PU `To/7FR̶YocS'flR0eHWDIu݆Bg˞f&*+J PE}!/\JH:-()SFssؔûu _/uT(XHį dCJ28 ]nC;:-3"| VQRU`]$c*}|;\q" TBE2]K/,.m:ox[޻ MLB? =s9#_='1W[57:n3uŅT<,TDA1*Xcb S`Zcg V-OEW_+bS,c ~)2Rץ(WMkaѤ ZG}QEoĹFE&T=Z}UVZSS:| .vڬjvKKG҂<{&NM,{hn'겢,zH(.QXBb X>~0%Zkkwzvo·'ή&BՏ(ABĈH=< Au"Y[v-kv L*`ҧzT$c|$ V**)w>|A%zd65Z\= =fHK: @>d2%sJ1~HepBCq)6ֿxI7=  ^Q]^ )Q?H--Dy+6SdUhB%ZZol\. ;Ѣc)~6_DB)KS%x1:<&T;"p ;FڬUk"HsG>Q|x~CI*vGT>E~Dz**6e͹}'h!Կ'A{JgT%_1q)^]6OE/M0B> 4Q0bQ,~7Q$J7cԫWdǬ\N)5ZSSýs4jdWdPϸ 6vw)gvo+pgM>T>G,*P$TL}O-O}[PZ^PYw=qt;XH;$Vm Wbo]6)sq - (7'ûwoG}/FPģJc?NefT+5־YQ t>hgE˥m BiȦ}&|u̍v5ZjU]uyIl?a4)G 1>EɾtrçLTPk֦i'l#{SHѯ_p$H!?8HF*o+5)}u0x螛쇝HJ`R%EyYPk*"}lx@b@*RH)VB؜{i, Kƀ+HSAJц M:0s ,z#F؉TUVϹ(Q$QE)DAS +*}Iz#PB^JmHg!oH~%Aع0'2quF>ݦRX> 2)HQ>G=(Tb1Et; s),wN,xWMc!K~%A!84K;w9=lҢzQ^N&]a_dA1G?0L1%3S}C}IcxY=̵߬S]_ &0~#J20D*ҀE}ۥ@W [~x݊j%9Y߿Cmt"EXHD8PE~T۷a_*k `S.q0toq,U o7ċwM8u*}Mh3?3d&(jh!%HEP(T\PG(idcV^Q)lՙmó 0³߈tG*Nh|=7`(5/oF;:x&%D$F)MY`Sb65Z˛ݦ=B_ &0~#J)&3؆a&usL5)JQPm Mվ/ĉVQ#)r/צV6n'#Jo(UY@ _m,F6e~ ]5^(^''q؏)PV:Vo{7dԿ$AM`FxDkr)"ǫ~`O역*-&k?ݲTl$aQR5*v:ER ?HTn!HWkMͽó.i8w5jXutRa o7ċ)5MKA탫 [q_g}ܗ}?D# aiD ~M!PjSo=׋-ZU^dOL:b.ީ`jn-赕M0%1DAV(~ +hm C/wnBmOQIu)B(bTz!E~t9q{q 7Ԕ5)*["/ǤD g1;vKm vϖUԫ@1<'k"R`! &0~#J&GHhQ9=hЀ$?!'hUB)>PbP~LՏMQEroB16,xVOnmLXH$ jYBy4&B|.{,:(B'oR/Qb'R=+] 9a_>"F:ls3&ŪMMJ8Q%NѡSl 5ZJgnr+I9օԽ7Q U[i߹ lzAg6h tʤ^}ǫMgŧ*SB:q\-%i]acئC . Q9 ԶLn*txmB4!M &x#7A#̭cڻ3hg˳U?' %ZEq9@2S: Z]t͌w7Eڊ|93ՋDIEM<(T=Kuy:(hw IDATJ*)g$Sq2fU!u#AM`FhqM* "OF[ uaUHEy9X$ꜱBTbLF~9yUu4)(v7E(XH]K~%Z>Tt{~oɻ01ԎyA*K6#IzGE)1{ çܢJϯw[>x,1 $ ߈G4 _։{HmbSq2? TSXZ6v-xwnC͑}RoD}G FRY |i4jԊlj!1gņ*M'>fe痔WY۷yLȞ$coDx9%.Un\+T *Gp̢H]ĤC˨DmȏS2K~ցw.|2%\\(ъ)ݎ^x脲m}ì~_'~4%)7 TjY9 _G6?RgoD ViEJkC ԍfC'J։7:/Ռ+[Mqk~TjPu {2|4ݬU : &0 +RC ~c;͖97lg̖uiϤ):UBXݳ\"ԧr؍nnwwN#BD( O iH'CMcnd*.tԍupKxR"@CRd'S}G#{[ӿ} n: :RoD V:#E"PgOkޅv#Hd*Ϝ`ʼn%Uȏ;K9 ˪d߬ûq {Lj"( QHQ.PF=Y?l,)dX%H.(DmJ|7Le oz޳{>Y$: &0~#JȿRKk'|:\rNRn@HR,FJXAQј{{ZZojslbkĦ"u AM`FXEfH?HUjoqOj* rn$N7zӈzH@ =SƦ9&;` /AM`FX㾴C*BJ:z>;Ǻd,yEBHŊEDE )#'N34vϸ/÷S&= o7*\J;7ɶ1vY1t4%cdOq~հskl_= ߟ̶)RoD;JWv0tsLحF~H":!bTo=S΁R6Y޿zJ<ηYTv$ ߈v)2|{àAmYmڨ$!teS8m~ U #Y#q(xۄHmK~%VD!] ޲ԻtH<{u}u%~Z%qJ=(qG2E'>fej5:KĂw0t6^i[oD Ǥ,)8|h5Պ );7RAJԦb=S_/GKЍnԦ7QBH.YVOwkC=,R,GujqӬ!c!!AM`<{:&Ύ/Tf(C۝U<IH}3KQCNH%yHkrD*+D wO.h.CxN^FP qIƥ{n٨#fB;;~vv0;zY 2g#Em[+ RQqߪU]jM(T'(GO!RzqmʥI#w"HEOnΟ@HUQHy; }3DR+oDT%SQߧ|"SKG*TzEtVJwbKq]z&ROe/h_Xm=1BwO 3힐{bU-OE\4@JlX3kǼ;/w' 6?G%RƏG4NqA*i }lu f{# NWNt vY+DR&Rpv) .5!eT5{q/ AHHRԮ^-wo3?6j6-]D꟣"YԱw3z^oWpW8O* )n>8.Y j?ϞcbϞQ^waxoQxæ=1'AY0ד)˻ nǹ\\{B,$TȔ÷rBv8Y JͣAHS9~-бtXJ=Ip2j7#AM`F0 {% )Ym~8;G;јuI:miGc6Nҧ^oi% ߈&IRγ+EgXH2DMqXwDQ{&f]Kٟh2 D &0~#J>4Moqys'wq~S,U wM/DjR(a{'V .x=7lիKWK/{>]D ٣߈%I+R e`4|晟hePdQ)wA\@wA<\ 7Q$L=^)}3GᇋutcFF*-fX2'߳-7R)ju6=_Q#W7DH$;wCMsnĎ(O/u5)N*;g3w9!G%ɋ߈D"yAVox v] c]f Gf!ZF.M0%J5B 'JeW*:KSτõ[i*HH~%HRuޥp`[jemeYan&.~5)E b%jRv#E`:[TRURm-}SNM3֠ ~l% ߈$T*=Bi^G+T_ ȂH)6Dl*:R1EoR?fU n*[z .m9lS)! o7IA*BUYNٚ53hШH3_O*QɤD6SQ7Kónq8gV  &0~#JD>戇4A DJij6}^-*K r`,L!uz2TzۤğH NN6 56o߄[zTy2$ ߈#T*ݐ]%_?Bۤy XAWjq2{O ~f o7B.PG7;459`MI.t[a!Lz_G.Me\Et{lq_vu]*Npj &0~#JC*.L%<"?u`68xw5%g^z7bS}1~-@"eR_cQ/y~_A (v ^zY8a߈/=~H?:k_8#.ӃmMFAQQV{ lIDB%%AGo72oQIemJkهg< ޟnE)A(*R.(7D~J Έ hފ|rBZg* T (* LTIy:;oB+VnF&AM`FEpB* U_ftBzutcj7V)X ~-ZJ2U9ŎN$Tj2ʁIi2ЌӳrtEܮ jjQZR2R*# #,n tqNٛa입L:7)VPżR(2ۤPv.xW;`R&2/ϥoDqV;I)V%*.$QhAGx{)?o܇/fmtw{b! &0~#b'HHRYP -Cw+H?)2**͂Ib)Ol("=JXy'T탉0);0)7*?zamTTFxD~ӤE4@RT>֢15lFTUYqA))oeTBXXp7H%FQs2Def7<-CfՂ.R .ߥzJYb'TR>Eay"U]6,÷~$)N oSZ`)aŔ8T,8OA(ԇWE}/M(`>LM 4J1b#U'AFȌDX&n} t6U4/17 ~0L$*?T4U\Dh"q|P_QiTԇJ9ydۄ?Lj} N=d]=TɁ_r;IJK>USNnHW 6QT):"{8*B,HD@s̜J@Fklh>y(x:d+󐪑 JOөV?scPo+QEyT>*Ahp#8!$KPA?y\/.g"l]#3NU؝5澨.U-A2RI#&HEZ i(xҩ^,+)ȥ7OQ).T"T1'CU;}}V?ff҄`j웘w{WCÕ&ˤ"EK˪XMAYP*j++J ss`)x*H+Y̓E߾̓yi˿qz~[j2"H\LRT/˥EDdG\ TF,;\]tώ6`͂[# *6U$V,08Ex ŲOٹEU(3m IDATƧ<"xo5h&R\J%(.KBq9|RK! t,_>7nhO` ShT>{ "P)CU/i) ]}%ez՛fFiT~dəɖo)kپ%B~<`-]# U)`G9SĊh NtTEeQ URr;Zk4WoպϦ+tB JTO$)a:UҩųquOvٌȧj*a";VE8MW|HF7,LrJ˪j*5<5nB#dK秖Q A({ń_ KB?2t~G]C} VTVW/?@oHTX!"d1,QDPEٹEeC5 Z?} 3Z!)?~!HNefV5-ݕExjRTgآD4V+,,%&APQ*3;.VTցOg5uNL;<7lVӧMD \ JJ%~.E~*cۘc.LM LV)K Hz+X\}CpD'Q^jwxnB JQp\JF =ʛk&QL%nzV@ws9fF;&vRT`G)*U UG$Oȟh=`Q%U5(3mcNom,@N|]xj']MVυHH~A)a mc˻w㘝im`DeyI{XcXf"Ѣ 'ʡHITjhQZ;:17.o0Z%AM`F$mMaWIm;q{u.̌u,d* T\A8z`4=!" CUҹ} m=.=A7DRVVVa'E=SkGܼ_Z; %T~TAJ%Q/$3 :<4a#]=C#c〦 OŕӋP8"wW[+K$X.t\nдsprp Th;=`*@ړtJR =YxOb BR77x7NoA" >\ln,/-.~#`qiiyum}kg6"V]s#-&F}C@( ~<7 kNS*?XZ?8G|o./N77VWWkk`OПnNV󣃝MgVH})AX9oDO&HqBTmZ74ê:::::>9=;{ts~mo1C#OxoDO>D+Ux^gФý{|qs?50  ȸIy">?XrO ڛpPD1X(L~,7#R1Fy"G)XPuzsCghl]>8:B#d_1=2b#̋$\IN@%'XbW5z+tjlXho{}yѳ0;1coY:Z ?J>~#*JbYQ'-y ݨbrbd[$d쉳0. i oDҔsʥXTAȝ` u>{{[+l{UJ%QEGB5$>Hx1tTI͋+~b;)Xu:dXF?У-_(yXHN (R#MͻCZh/<4%R ܦA:"%֤RzdDqjZZW7ϼt~䱈HF*X)Dlk3R f~#%JYy"HQC*~,Y (h*H/+CL,=Չ}+e#ξr,x]X"P?ѻG' o:7"1eD 'o~-(Cӟ]#H|oD| cԗFeQne1zm2 oF7"@%>ӻD݃N7{d KӾDO~#`X~txugTKDo*}'wLk1%9O4MK5į@cMfpI$,CXkiO}KY|%)O?#l~' % Gɝ\_nHŜ=H=]e*A*#8D'qc =aǜzzAV㋴Db,Y ǧg̗ .Kw~,<$@*aV3=]Sd %;)_,A/O>eJXբ_?e9=)KVL{_M0<sIȸx]36fYdɒ%K,Ydɒ++K%"`Y UAJªw'(J ¿THJRE3HJddɊ$6F&RIx:2R#Jb-F/V ]B]EF +J)R@VJէm&%+JbTF\GE +iJbj\& ~'#%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Y>gɽdWMY+y',Yq,YɒO Y$P%K,Ydɒ%KV"!seɊsbeɊddɊddz F~9%qeT\%K28>$H^ח%K8d&RdzL"NdJ;=/OGŹ+@\*CΥdɒ*&]=1XwH(eɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K,Ydɒ%K5ZgVIhIENDB`splash/docs/figs/starpart4.png000644 000766 000000 00000050125 13261626263 017301 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTE#59/yr%& 6|o)3lե,CU/FRIOLLOfIRivp Ff`\VlLFB<2,/(h"o,re3)b6&9_#<\? YV YB{\?x} ys_mic<uYvS OIb?9V) IDATx흏eoJ HBC [ƒؔ65ZE_) 1EbB@mDhcJScFWc* DЪ+/9w=9{g޻fy9{g` /](D}<`VZ ^Z]ir^nj*f`T+ǩ+yiO3ۓ;ȤJt[%l ?fRlCRPS] ?_]DOmke|Rާ{MDKJ=2!ol!ނI'RFP =>)\UJWʋWѥB[eAn(jmP J TUԿ(asU1YKުLƪClRުh.e^)R804yAg'3{DZOʘ$x.V`x'IZfR3TLfNS*PꟕO)(5k"JJUQ*VREJD.2_MJ)Soz*pU*SR&22E}r!y0]+1PV?!aQS@meNp|LNLcJA9TCO&fYm+5*5Αb sJՑﲨJ5ŏGU+UTNoAa;՟Rʧ1=J2ttϬlAOiR^5a/x@Riݪ[*Z5Q28vN@WE?_B_: JqJNW[U0PjīR*J(UQ-yvzM{U@؇R@XA7MGKfMċI֨ƇOys6?f|iў={jv@ i\*ԸOUJQPj*bQSjsP3+crP*R)* eMUh|O*mưACӸ o^՟^ GUMSqDuiREԙ'KOCAQUjaJ>@SsR ׏}(OKRPj(?VCީ_fR7Υ*TUQfޭF)^QJLJY巹7oD)H,I }VMPr V^~l/6I>1/N<83l77pjRQQR~Y4JtDeb`&)枀RPj+K6RPj%D~m1gR&)w+el#吱6T*)hHOROeI>2%ڞ*bcJAY_+CRԐzxUJ;%+jR*$RO޴iH8(w6ǘe!,@kFڦu >!pH5烿,%%R}}JA)(K.ah3$\ JA)(kJ]l(tJYuQEI:ULۜ?>dÎLwD=,X\#!CGy.Uτ#h -ϸnE :YOzױ\ JA)(ժR(,QՎ½t0+!gZVR\>?z뗇GnXwD!%!LDs,ġvˤYR7K"*>57R9TRPjJRP*[I$SJ-S.ʕb[|M+WLpO JB3k$nuZV |GY+F/LRoڠ:d5]ak}שy P JA)(JAzi sEh*) m qو(u^ȤSJy6sJ|nb5γݦ:yCSאĪ6e7uBKSi|sdɺb?: +%vyXp!]^X#a" a DүBL")JfR1JA)(50JMRJUξ*e> X*i~ ]1TRTMhR3E1L.}2JZUR$ӳ5NzǶlBtaT%2Q2xC-1uߔ%Pw's)zD#(Կ,a(T_RUw揠('.g :)Tjkh+dө ^pp#G^(td'iuᬈ H"IΓ'ZL׮][ CkM&eAZu9㔪|?RPC>QA~MA)(5J}Uv=}Jj(I)7L) =WJp$΅rJJtJǿn52IlT&EqVD>QZ)!-?gYb]$_ҚMVJ'RbU&y&v`~vDLPoTRόOAk&)U{TGJfRj)uїd&iJQK鞨Leͩo֯_ω)U,¾hv LIwne9$+" H/VJgNtLȽD)I%.%?:%2ɓy&eeY0gu_pLRj@)(աR(JA)(5K>a~հTIR*N UGwdx(ʼnɰPu!S(3>I&CePO Ck7]tSO=u 7r-t嬳΢%].m?- YHw;),0;H݉R)Vu RT56#2z:Lk ө/ѣGen$,E囃@Q4 .>YqR"NrŊwδB:aBkOW^hŋ׭[V1$%EYzlzID햷JR(szC(SJRP J"&FNky"Ñ Tʪ~X-(EI)d_#)%Dc7VV9Bv = ɐX)x%MAt+Dp1$ eQw߽{UQy*]\j:m]CbYed%t>PRPjԕ2P Ju*aJYM\3UJE酬[%.kJ8E)IR=|Ժ=$M2%wˊ+pDK V!vW#Ȥs9箻{(T7%-lD#6gG}郡3DW0IǦmJAQjP JRCZï 'R\j~^(Jy#JI^ҩ#GP:RG?N[B+ 猔)ҥ!aYtRZnݺիW1 eQdtg%eTdQCihGd0Wyuɢ2JMFMRJZ55 _jK/0ߥȀJ9RԶc)f>$){Ju}Ν<^J+'#ْAɚidN#F4$R"S AWH#rޚ{:S E/^Vy92t޿wDv$f@y(R%RP JgK}VlRcp0|IuҟۚYF+$spVTgk׍j. igK )8]?sɕkH#NKH2nwy߾`BҭtGi >agTlbI5{JA)(ՍR)PKP JR?Lh:-G)CD$J*TOȠ1mMPOWi4檹IJO\F S2'6ZnZV/=|J'tEN핊kfR3v"R(JRP J*F_A-[eVN%R'BX&A[%iX)]s(EIs"\w?ՠ"UR YrJx;I&.sj:D HǏghe#nqT2[{g?++{3Rm)WJ<܇91&Rs@bP.S*rT)R!S6+JQJɸ' -UU'I2}U8B+)Oh$џ!WîpY+̣29[x5݅)/D)SGOdQF*U%@)(fR}NJz(AQ\6]yOn"txSr) 9 xʒ([ď+xR}N A ?OA[% IyAOP$Spj6Đy-Ln@D4CVigR?"KDnz|.q_`'ZB ΅s鶐IO*MVHRhا#GХ^bd:s{(7g!58-ZJ_I R/GOj<]RZ&%!/i0KjR.JA)(UKR#wh'EɓQ*rU?HFW((] IDATrϦ;L~΍ZQRUO$ܟLjV1R0=(Ŗ\fs 3bJ'F2uX"zM 6 j(:T%R˚Y+KJQR)2^)Jw@xJ÷l0d".ʨ-ZDbq"ŧ=KJh > ~DyL͕̓g7HnDc'wZөNז0jR#TPR)F_3="y1&ڷoߞ={בċBbns9)E-!u ,ݼyիFj=34Ag<VVcL9-KJD3fJ )(T;RP;Y*V*JݚRj)NyR:[n1'+b#)Z( "oN=}CT?ozӛwV瞣$R|&o<ё:tsKOOİ|6b?w#+J]Up*~$!R#ЁRPCU¨)u^}*ҩ2vtj~JE}o40S~_) 2"r讚E5K[};w/9e_7tmF+WC9qעD{`{vFHu mU9ަIR|8RPS]BJUb TwJ4U {*/{%'0Iڤ%I'ߑBKLYbO?e/~_E/׾Fﲏ?N ռy(^p'RWut["d9ɝ&%߬;P~N*5R)LFM)|JA>fRU6>tall˪fT*]G@)S&tڤ>|݋B /^p[m+}+_'?|0%R??WW]uNW?;Cyt%;uS&CyvKZ,qh2wt :JUcCJAzC CeP JuKΤc+R&45vd(2CRO&C[%ݞ')uq#֭[ovҋ/~ԧ;ʯk?Oӯo7x뭷ҽ׮]˵zd2NEʏJ Hoa _ٲe O-oy _BJȭ7͟)ik~iJȪnUVR۷oA5/QTO&ʤm:Lț!D,yeӞr(FTGJF^(TJҩObN啊&R'Q*6Gc/73̫_jOw/~oyŤW%/'G]pOJḺ !%P-K G z1%^ 3Kx.UYĦ&A)(՝R/az*~m(UMnJATw^m:kEa3*SRt*Љ{Bv6R&v6sRCI֭[?x_җ~>;D+ql=Sd>?7~<M:KT3N aTL`4)OGSxms@fRP JuJJiԹR#TJۯ8U-)#F٨R'B)yw&H!)[i -[(O"cկ^uUַ(zK_~]z)sEu]TuYg__׼oWXI)S>Fu4#N|Zh,PmZig?ZS˳M5zs W+>$ /xR#DRRPj(JT*QA>x?oR* `c%>6kjz:L)K2JU&:twSt7ߦtdzk_K7,Znt:s͛_o~ , 'ye]v]xʚ$Cƀ;TM_t0vdN F}!)wFr)z$XBgd)#6CRPj)5 ̣ oP JAh8*E Zh5}jJm Y4P W&w0J7{ᙰ7ԩS֭{駯Z2Uz^ eQw\s뭷WzMoI >C:aVQa)B{+S%DfSTRSV|h_>J=^*m)5s).V˙^MRJedtOht e^=Z缐⟒no~3Rw|׾׿"kVXqwr#/oX޾};'R;d&3 ^Y-jOZE&3F'b{0ӘqJATT% iUJtMB)(՝R,!!&xعmEo cRJE(oRϞYWJ#QORD{=sl\귿>1JX&2׽'|8E?>$Bi>UVQ"y&kh_S>|kDM7UdHinsjJ%}Pc ;0JAz'$Ig"R*:w8_&1|beI¹^qWUY}*SYڵk֭ , ͛ljUW]urUz{okI<[p!7'4cε:L*9 )d قRFP JA"|_(fR.HuJ:r}MzM/Zl Qv43>ɛL :t衇Ϫn/#o[k>!ꦛ>я*[l{ho q&fL*Rn$pnP97ZO}s)(TJ=]BT.r)(F[5VytE-V"\S$2MJl\C3'wʤ$ /\bE]Dn-]*ʮ,X|kHw߾nݺ]v=8=u&!}&"۝yJ ;/R6th"f6 J.5Pz=j]>Hyc>I\6˗$r6ؾ};?I+?uԪUz{3Π"(uu:ZJՉԾfQ6'/+t*TJ5aUsqJr)(fR?/az*~P JAViĢ k/RJF0L0D{RZ,ɓNZPD͗,k rדk׮Dn%}٫n&͛>Q?a֭/x ȳ{wKRcqגf}%ew3>Z;y%t_DZen*aԔJA֔ e =R~JMR]| wU^KBvKӰ#a`ƵD qvEbQTIiq/\rķnٲ|ڿ?A]tYgsQtq2iѢEd ysg o?sHիW_s5֭>L>ݲ\A7?ߡ|CwT{sw{B.a>RmTJTJ}UR4nIR&O;FQN&0euTqD} 鵰R>|$Hs]TC]|] GRuCt"#!'Hr"D叱!DOc~URP JRP JJr)]տ˖V. S/Oc$2M^뺙BSH','X֖H5*R[nsiAJB+PE.Zgo)){FI)WH%cD=*zf%@)(T(:T%̀'Zݤc }PɱvH7j$s&m`4'gej2њ?XV1$ tT´RJU BhN"FL[4+,q3V,jOb^sI'RAZI-qnJ1+tRP JLP[KM|q['DKS57kʊQQ4ѳi5Z5UVX&NVq5}W5F]辴A>gS"LF)?86F)%jO*yM:AS(TJ=_RPT0JE+**JAf4"E&%-VQڲ$EGM'RT'ݦ|fx |}."h"UnVR(RP*M**VSTzRX)WΣsAJTQSBԪ ZB7 ͖d'҃i=IeQ:32ETLsGVyU+5(TR0Jy&=Ŗ}{o)}Xg2?^ٗ^B egPmҖUOk|̷U~#/oJWc3>mLZP JAn:(JSQQF&?:7O`îp{X^&3:bݤ(=$=WMeOy4QI"AҩUԪrOmPZRPTKRP JA4Rƪhu5mՙje/T*:gwݸkꗩ^.i 174-f4 :3M{C'Cy{n2rT$cɫ$:L;KRP J*RP*M%> )ģЧuG%@)(Tz5(ZS*FM))⚺{b&YAuѠo6WZv(W@xSا=uD+Qw\m*GR-^{1cOG=MSRP* RPU*Uƭ4~HYjHkD.jK|nby]qC2yԹz3Ƥؙ QJ'@: [7'0颻p^&!4n,k5|}#TOa2*27}+aRP*Ujq #+%V51ʟKCy"o7I]kt,letofNbIeDG;dd22̥2J]VyxƭsVUP JA)(P JuFS)#V4]F6Lp//qZ3a[}$N?l1)PF򪹗)^_{["2IRPS֕*UR& %m7e+Ĥ78*ΓD/'e9sI\'SS>}bҥ :tjǬRP Ju %@)(TBR\Aݥt_[ZG_$zTm/y~xHds&S!/PT&)Ee^,c|u&R*Ux 8P JT0ߜT*-hDcP[jZteY>2d:.ov5Er'#S^L{7r¹WʸbIR)EFJRP JA4m&:2*/ɓ2,%ջ!iX!B'8-$l 5I*PO+V~(\ P JAR*JA)(T=Ь yOkՏEz=?TSL"a%n2)%|O|DO.v|+XZ,ڻNMRP* R*uQ T OZ)oa66'ޠY?Sv)Ս[F;2i2>E{c3>MJ]b:Ϋ>NhVRP̿u(ԀL43?jbRىې"55 h|7k-nd!xviQTL)RJU>ҹeTԮfRħSR(?(JzٮTeI|jUHy29黳%֫lIwF"PInILX{4z,%D׌2yU`Pbi0_RɳUJ-ԍcP JufRr.4rR*u^!U"VF}M|;;}PԤ^S\"1J_Ubf%cAy/P*XRPC-af)Ũ*X*+RPF.S* ;` )oUJ4=12I#[O3G|2-z0*{|%^QjE 3W'(ԠTXR P JMY)&V0RF )|2ʈRGi^G#oL ]/gD&}SBF``*>TJO)*zsJQRP JufRR3AKaJ<)xy8Dr>yF^Wo҉~2j!iB4*@/R/Ro2TJ&j6+eA}Z#(SfRSJA)(*U* tΖ̖I^&NR&'Ѣ/zRSadVpX>Odu}((JjD vǧY~IUȔpįq噊[1ڢ]U Xh+JA)(UԩRP*Ms\ݽ\ (IDATJ'OGÙ)O2y,Gy0/2F}ijDgKRP JA4P JuFM6mbŤI]ĖD2ytD2yD* ֹ؉.}4*u~ P JA)(JAQS W4)IӱHkd(*)%jӫ8mOS书܂RR.ӏ~̱BDc%k(:Td מ(P JAV"xyLܛdd2 |#J_+aj$=`X3%@)(Pj3A(UR((wf )b#نIk8M,eB1\y*q#P9cO((:J PЙR0JȚf1{I< {Ӫb{\){lP J>²F(:TFMtq(YWڕ)٩ԯRKQjg P Jj(5V)a.*+/pK=c\aYzԅ2T>fR1WR0:X¨)u`uAbR2 -S4>8uL}=ؔg֢O(Ӛ,P JA˘J)a#a’yI"l]*b=~>&REL ٠<ZYˆ+MJYN.SOa/`@RMt)JA^@&RޞڪƤ]+5 ,Z)1RKRP,XQSj(:sW`9TLZ^+^(5E A:pSi#TJA.Rcզ#V*eRKU2u0X%.|jC%@)(wYNT!Rtyn+5h fCѾ׷ã(u( ]JT9<_¨)տLf̧B :(ZӁRPP <:`OJU^^2]Ĥ}j+H nn_=7i^-Qʝ2=Rmc[}ܬJA)(5f=N)FST/DB+?)S>lZ˰^ަ΂8iRR3(5RPxS+QSP&i &S13<2ӐvذH=dRR!*構*U*6cʷ&"?}lK+LL=ivffnP/OPRPJN=RL Jɂ"R=MJTz0M`W 6za/ ϯ]tT)TnPJ qRP J^pTz8TƤTäTPDE+LO"O%@1( D"EP+GKpJEY1)R r(5&OJGLW-v{hsѨH sz{J ÿ Q="N}x  O ÿ Q="N}x A.f⿟o(.T,'QD7v MaǬLBABTo~{>hԝ`=f9RUIJ$c(կPj(J 6dJ U%Z*rʎN)jxeiTPJ(e_UKC;B0I\^t[c)S*c0:iJA(uM&,Ci(y_҉ SJWDa 7?R˧p|Q*@Þr(RmQ>E״2}CteH?=ު!<}~.՘qw;34!6=G~О39fsfswZv;~$xFvo ͂;so7oӵ7w.R_GWG<(6 qoWwNֳ~ס7Br~Z }|ȗ*yd[؈7P*6v`WC~sKiULJ\o}N5 8U*sXW0hmUs1] MM|, 8YMu_j]w,ACxӰ,C8̡91*}Q (@@)ZJ*P )udtfmu &~7sp<1MKl'RdF'RJ&/~r JPF<\ A()^ZJ*P VDt:IENDB`splash/docs/figs/starpart6.png000644 000766 000000 00000052614 13261626263 017310 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTE#59/yr%& 6|o)3lե,CU/FRIOLLOfIRivp Ff`\VlLFB<2,/(h"o,re3)b6&9_#<\? YV YB{\?x} ys_mic<uYvS OIb?9V) IDATxݏ\՝p$D1+Rjh 6Bm6k$F7٠lhRvu]>K$[wn._9߹9'O;9y]                                                                       ,b74Lu75ӣsQ{r$N\xݻwlmfGuvsjó7q9#&dڵtDLΉv[,Ydŝ niNQ71:SB*} T)R$#=&U/RUR RF9%K2iR寸ֶ {iNWTU"H4=R8)Y)$W`Uh΋cCTIR9YQhw΄?/Di6%wNf)Z$[@jI>aq)'mj˞Hʊv"SJԣ UQ '3YqujmP+qfZ_U ɠN- ֪9>uy% n9Hejf)TH% RFOdd@*{TH*/'$m^v yg%{DoNHH"Ri-I)RDA[czU%»Wt.& @֨LZRO@R Rԟ,ɤ[@ fdRe(t'Ѡ? y9R:zTŒRQJժrӐ̸eEBuT!]TF)ZĤ)$ R3JߖdĤK|oUk?LTˡx'|LJUAoJwǤh]hA$E*X1-'YJ%";wL"$%^>=ի4p"dE͊tTHH @jѐ[%yC(:&!ft35.Ws="=Jf> $SΫhd53 )%)QR^)*%K2%LYR{rUI)AH)^;kH)H)KˎRxizZLnOTgDB&l,ei޹s4Er RTrk'@ f.I5DTERJ'YzҞQd$%^?=zΊLʲ)AO7՞T5#7Y-UEjUfdlg泱@yHɇȘɊq[2fबN*egӦMQY)YW~z] HԌ%1R5R R} Jz*J>YJ`ha_{s##(NϤ"zyw*$"qc/nޝ"uJiS) ?_ե@ ?S5zBIK2tR'!ZQLU3EFBHpV(?>G j 0x=ϔB$G5*k6*1U(gUƯPDR )$Ab)!HԌS%)KTT2# bUE}N.Ed.epٽ{ͬiFMϐCzؒXEjOu͐,@/SW> O82m^;JR 5VRURTR&IǒH-VR)J.&u0JPʒ"lC#?%KvOM4'qc/$:Qn y+Yd &\9HXMbm$ē7ƤMI@j>'@j0R/H-RAj^D]r **EYeܼ\7LR!uY?HZJM 5Z:lD )rc`Uw'[gO=0UW1x )DZ<_dWA)QR$# >HH \@ ?.4\OR Z(ˑ!ǽ"U3sad lH@z:ETni곉J^Lq; P$KÎgč*4]ZXh6[D:ԶfvFN R 5'5@jFIHIe2dFl= *ZΡ:)Rf{;R~/uH=\JR6R3*QG'=pS 6Ž7 ␋AEWB"Ka ѾNHd\guw>;n(/{+ɔ\X@ HH@jI)J *nURID D IIܗ&mbǎ~ ot똋 Je˯*P#R!HWKRRDݸ+}gnАA=NO;8pc.\sSlϞ=/T.n۳sswO[)t4RZ|@j*K0I-DwI@_ӓ9RO R_fln轖HRr9YJX&?Υ=Y`@Np]iÆ 3sAr~V w۵gEM 4uyvR*SC$HpD /R 5%P#N6RuTURU0'ǠjT܎s )gd]jtȊs9׉3 ^u#O*x@Xy%~YJ[lIε}/IK2L*H5I\]j*)ϗd'T_E*JqJU~PTR,YRXhLZ%"̬ѣ/*9F^̣G!Bi%PQfj,R+яE)ѐg%)RR*ɔ8LJKN$Z%=yKNAQKCoON➰.KcF)9t&G2/O0,=m#Rrؖ HHԟ( HHHHHEN'FÆl/.V+}Q{9B9*R"]V=寔<HHHe/dZ/BTDѣ}vˣX~ruׅ'B ~\Fݑ:!GD]Ҋ\j8elJyR<5#oZig=3aO+)$%R 5bI'o)!T IDATQR_, WO'!,yb 76F,;LtGʗW6}?|uaQpҖ-[hqJ8wEHG"-]t\]2MXlٕ.._̇D .x $CN!5{d,E"`\r- R$h.RU(HRMRU_:%u<1]|Y:r:[̷ I_~$藚&Rբ!UiR/~(H? 5QR'R&9R*T$I1RMw:X&dӽ-~)GǓ:p_J8mKw7'>Ow}~碎'j!g Oyrzr0~X)!S+EbVI$8)lOIH&)&e6OI9S[&Hjۥ@꿖d\A:S6cV43)iFDw/+]= \m:"R4']>˻.K_]|Q whէ|Չ9b78fJ8#. rdU/@2HPHHԠIUH %)O`EOxmWq17 ? QE]w*E<r?|~C.?{]^v5]\u*"Emyrn.ȡCQ^h6i%MTLg&"7" UW1*"UHTO* _- H-&Rѿoqbѕ[R(R6YR%x @Jt:=fn۹n`ix 1r|7TUt S.~;7|=/D:V\黡N5/ ;LdOP+DőVZv`J'T4RC% SRTU`_MAa84)[TUyRU%R@I&JJK2-:/'SebI*5=WV'JOꥎg]z!7SJRGk_Z}㗾qw_eBox,)D,X9/bnRLJyKMA/:Qmҟq uɑjxw|/ RI!σԨI}$X}fH>3>H}I}ϓG?z 5󤌄BO\yR,v=.)ԑg6M7_ o;og\!1o˧>Tzk}x?SC֯_I۷V0`4 3IYT=Ji=TtI}$ 5Ӥ*I$}DZ !7AoRD}@HL`1AJK*ԫ,U*O fHqERx<;H]!ίrH^Ĺ{}Co_yW {#:Iu)&*)9LK)Si|]gYT 3U1gThvR *)?!jI&3H !vHŠ~۫ @ ,R U5I?;*eR?W`Ԭ}S5HJRCTE:^Pt zr[[oR?j_7 ?/ʭ-CU3_{ӧ%;vc|DJuYWqq*_jPVXF!S|zj'1H|kSX.ͷd 5\RR@oR_+FO wƤ~~)#nIiK-):OLrE+1u5>H|[yοwp?IӚHԶO*YmT'Zq)YFO"eYxm{ZĤ.c!RZǼ(ԇJR3O*HuL}KʛK fTpHuL替d>Ǥ:)|ԇK2R^YQAS}Ҭ9tMɉ3jFqa%d)RfEܳI:u_O//T/|~_/<~Y@ʯx yNKIK+椆EBN_7C_E>dGmI}$ 5煰Xԏ'IuuH2HMȑTOYMR/RiRs1WDi:vR{U'E%)9x$$%+)RgHJe˖ov[e=ε"O|+_smyqW]u#mHOg"eZ'1YuR_QOQ[lR- H0ۂ(";htTUXIURC#'N"UuIۤ/ 'KRSN,))gZ 7NTRc&Uy"`fU$R9KchNOwqua]Dޅ\^Yz$)͉SV.͢.-l'& @*Q)"Dm#wx"Ip U05.6fU7TCCMS]_nrJ7\{˖Uh~%՞owO}ꩧy>^_f 6:Gl09w/GŒ<㦥RJHnZl4jUONK'_{4 z"͒ r~5ёb;K2RA$#UɊ gn_q.䰥T=`:a\? QEx3*"^ *T|\":q℟cxwqǞ={h]?Tv]w}3q{* Sw1ΧJDÂvd%UĺWH.R3n5m^HԥIYfRJڶR=VI@jIu"IU )>)9/j,.֭o]Uzd{=:G3.r 5Z_p]wu9C+ \z-[WwD4|4<|4^h*wyzcukkTHZ+U8 u)2Gʙ:|I}$ xHʑ:|!ɒLuMOkBk'jOnK/$5uEfp::D>r1PůWIx6ls(x򗆺p႟ꫯ߿8}|G\'go,rz8U *v]/㵢{To]TVRvJH; HA7KRc R R UHԚ$%& CC"Kkw69.O9WɗYpĉ'" q"%\\~s^\o,]~O]?ַQc-s_t }RhpM8Y LK!&zv7/\Tf/1ʨRo-/R$ 5zR@jNI@j yQ RʙJ:Xhc(>de l)"&jH")Ӊd!9^Å:?ݾFa{]\\x Wez^r_EG)!KzD6lkJӧ'UsϗH$.aH5b}&"eAT@J>!I*vdnk`DHU 5nR@ @ _>5Od2ѫHRDo"% 6l UU6aHҒlPڥT ֣ޥb_hpܷDl߾*Hͻ8O<L%k;}oͺݻ%|imPN MypRrUkFR Ef H Uɘ;j!H] \@j~$ H]RԠQߛ-YUӢ[Gb{? DDtSӝQِ#(%`DU׋yX]xcϞ=w~tkUD͜r[jC!ZDz=T)B8`XYa]'rM"ŸHRU'kR[(RHH]rvQ R 5$R;:fOdn qR:zMaKfuBy9.7V= uFb;x&O8Hυ͛7Zz4D'_YzU'{I!q5VWs)LRMGQ!ȱl:)WSڰWV.A @ @ fK2iR+!`32n"^{r\e$GqȋIn<{XO鼫CB.Pc>%9(Oǩr6v/CCܡCyT:4&<)%]]&Tq(gɪI^I@ @ @ @ @jIW2dŠz~m"эI5SQnՁHIY)ZTR]J 2+Ef,=8T=+3rpe8j )NT͛7ިux\Rn#=HWuKI xOa[\bN;v'fzrPfN]5U*N)%)))ϔ@ fxI*KܨH1BDB KbU"FJ qTk'D,$IQ)[融t$II -kb:XWnBiIJDԤ./ HM7!`I@jIecA IIUT $&.) hR=N.w90""'U:kooQ1F:IvH)F"Qz;CT]\"5g%HFY:bʢ,IyڑR$&)~HzKI@jIJTR un@j">[LD-)K-# HU!rrAJR3IjuGHm RdeZzsZXtw|YPB\2ܘ^W)Z$.jX/ꡲ$$JY=UeI b0ßģapX,,+AꠕrR =z5i~N٫MZZ))_"WjjVX $-J#RHq5LteEugpA(!%[t򎢘LU=ZTJ2⬝HHHIUHMJ2Rjm )L %$y1RNj .MJxNݎ.!>)\~""$:ʨux}PkйHU=rKe$ҬijUAB ˍeI]SR6V_IDATHIT)<')")ԁԬ?eRMΐ:*"Ux:Pt)َ 5#K9좪'9*e0W]@fEeiO))GUjRX3.xe˨EHJ厂R- HHZVɐ2UY"VVU ߠzƑ*:jRBs<ΫM!Nqu%?A% נv7AEuHX*K!R.]bcVR UFJ)) %K-bRgT?jRlL)7:"R׷"mq'1&f~ZQ7/&Udz(H% ґ"IO+JJ+jTCAjKI@!R R 5nRdZIIVfEI4"wRs=4IYURѪMuU=C|̹:|B'hYoJsҗ MBz2=đR.fu1 @ FCꎒHHHT旑:nRYqY7 67ԝei)GYdUd]ꤸHUJR R R 5(dFm6ʕL{xVO|-%I:<*K jnd :d{U*I*sWdŤ* HHHHH>%ج^ԚD]}]U$Ҏȍp"䐈RYҐJq5}vN %O: <(U R 5Rϕ@ JI͗dJH:?*g<#jNd`kSU0 W= |-D9Oϋ9R R R 5֗@ fT%Y:S/?RrKnc0"Ȓ,g})O cb(Hy^|D @ @j46@ JIm/ɔJ|.UdIGgRlTd\A%]2 2*`QW`R6cGNYRn:Qp2OA R 5RJR R R RQR|BZZ0Z8ɘLH%j9j2,=#*3ŞR'Hj+> $,I[))z$!Uf`c~BQ֙,)m.IY.Ke&U6ו5{ꕓ*wkU'1HHԅHHHMJ2Ry2Hֈ`n,YfNIQtԯ%`ٲ)JO% @Jn R Reg褮*HV\T)PuNo +1)KϨb#(Aoi]zt4RsJjeI@ @ @ HHM5%)3X%IJn[nH 䫼ݠbI1 ROHHTA  ԾHT)C% H_Z%$#l44+N%B-}(}j{*9}d|q))&u$ uѨ٨Nv[R1bIfX>-أ%YZ>dZZT2Cӫ'<]+|odHHH"%)RRJ2R'׹f_Z"E*HY:>*$wVld3I3}(V R2 5DRϖ@ fTJ#Y&UT#vj~R_b1?~ԩ JR %R"ucI@ @Ժ4IUȑH "4jTX[aU^*rl|Qh.IS|~TLhP"K Z)"@ $M%)):! YH*K֞Zxv6fO8{V)O#(˩dpTj_;JUjJbuHԠnlJ݌]H%QFQR 5f~Z-V,l_ C8dDŠ]/ % H q}"R4aNNzoREwM{)0UJS~ @z'sI}$Nұd4zzF{ \93JỳHM8c>*z RΘ*HH .FRVã|!~o02&Nfrg5~27$RE =9 5R RC={C"Ut"'@ Af3UY&4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu翤mIENDB`splash/docs/figs/xsec2D.eps000755 000766 000000 00000036513 13261626263 016520 0ustar00dpricewheel000000 000000 %!PS-Adobe-2.0 EPSF-2.0 %%Title: xsec2D.eps %%Creator: fig2dev Version 3.2 Patchlevel 1 %%CreationDate: Wed Aug 18 16:28:09 2004 %%For: dprice@cass77 (Daniel Price) %%Orientation: Portrait %%BoundingBox: 0 0 349 331 %%Pages: 0 %%BeginSetup %%EndSetup %%Magnification: 1.0000 %%EndComments /MyAppDict 100 dict dup begin def /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save 2.0 362.0 translate 1 -1 scale .9 .9 scale % to make patterns same scale as in xfig % This junk string is used by the show operators /PATsstr 1 string def /PATawidthshow { % cx cy cchar rx ry string % Loop over each character in the string { % cx cy cchar rx ry char % Show the character dup % cx cy cchar rx ry char char PATsstr dup 0 4 -1 roll put % cx cy cchar rx ry char (char) false charpath % cx cy cchar rx ry char /clip load PATdraw % Move past the character (charpath modified the % current point) currentpoint % cx cy cchar rx ry char x y newpath moveto % cx cy cchar rx ry char % Reposition by cx,cy if the character in the string is cchar 3 index eq { % cx cy cchar rx ry 4 index 4 index rmoveto } if % Reposition all characters by rx ry 2 copy rmoveto % cx cy cchar rx ry } forall pop pop pop pop pop % - currentpoint newpath moveto } bind def /PATcg { 7 dict dup begin /lw currentlinewidth def /lc currentlinecap def /lj currentlinejoin def /ml currentmiterlimit def /ds [ currentdash ] def /cc [ currentrgbcolor ] def /cm matrix currentmatrix def end } bind def % PATdraw - calculates the boundaries of the object and % fills it with the current pattern /PATdraw { % proc save exch PATpcalc % proc nw nh px py 5 -1 roll exec % nw nh px py newpath PATfill % - restore } bind def % PATfill - performs the tiling for the shape /PATfill { % nw nh px py PATfill - PATDict /CurrentPattern get dup begin setfont % Set the coordinate system to Pattern Space PatternGState PATsg % Set the color for uncolored pattezns PaintType 2 eq { PATDict /PColor get PATsc } if % Create the string for showing 3 index string % nw nh px py str % Loop for each of the pattern sources 0 1 Multi 1 sub { % nw nh px py str source % Move to the starting location 3 index 3 index % nw nh px py str source px py moveto % nw nh px py str source % For multiple sources, set the appropriate color Multi 1 ne { dup PC exch get PATsc } if % Set the appropriate string for the source 0 1 7 index 1 sub { 2 index exch 2 index put } for pop % Loop over the number of vertical cells 3 index % nw nh px py str nh { % nw nh px py str currentpoint % nw nh px py str cx cy 2 index show % nw nh px py str cx cy YStep add moveto % nw nh px py str } repeat % nw nh px py str } for 5 { pop } repeat end } bind def % PATkshow - kshow with the current pattezn /PATkshow { % proc string exch bind % string proc 1 index 0 get % string proc char % Loop over all but the last character in the string 0 1 4 index length 2 sub { % string proc char idx % Find the n+1th character in the string 3 index exch 1 add get % string proe char char+1 exch 2 copy % strinq proc char+1 char char+1 char % Now show the nth character PATsstr dup 0 4 -1 roll put % string proc chr+1 chr chr+1 (chr) false charpath % string proc char+1 char char+1 /clip load PATdraw % Move past the character (charpath modified the current point) currentpoint newpath moveto % Execute the user proc (should consume char and char+1) mark 3 1 roll % string proc char+1 mark char char+1 4 index exec % string proc char+1 mark... cleartomark % string proc char+1 } for % Now display the last character PATsstr dup 0 4 -1 roll put % string proc (char+1) false charpath % string proc /clip load PATdraw neewath pop pop % - } bind def % PATmp - the makepattern equivalent /PATmp { % patdict patmtx PATmp patinstance exch dup length 7 add % We will add 6 new entries plus 1 FID dict copy % Create a new dictionary begin % Matrix to install when painting the pattern TilingType PATtcalc /PatternGState PATcg def PatternGState /cm 3 -1 roll put % Check for multi pattern sources (Level 1 fast color patterns) currentdict /Multi known not { /Multi 1 def } if % Font dictionary definitions /FontType 3 def % Create a dummy encoding vector /Encoding 256 array def 3 string 0 1 255 { Encoding exch dup 3 index cvs cvn put } for pop /FontMatrix matrix def /FontBBox BBox def /BuildChar { mark 3 1 roll % mark dict char exch begin Multi 1 ne {PaintData exch get}{pop} ifelse % mark [paintdata] PaintType 2 eq Multi 1 ne or { XStep 0 FontBBox aload pop setcachedevice } { XStep 0 setcharwidth } ifelse currentdict % mark [paintdata] dict /PaintProc load % mark [paintdata] dict paintproc end gsave false PATredef exec true PATredef grestore cleartomark % - } bind def currentdict end % newdict /foo exch % /foo newlict definefont % newfont } bind def % PATpcalc - calculates the starting point and width/height % of the tile fill for the shape /PATpcalc { % - PATpcalc nw nh px py PATDict /CurrentPattern get begin gsave % Set up the coordinate system to Pattern Space % and lock down pattern PatternGState /cm get setmatrix BBox aload pop pop pop translate % Determine the bounding box of the shape pathbbox % llx lly urx ury grestore % Determine (nw, nh) the # of cells to paint width and height PatHeight div ceiling % llx lly urx qh 4 1 roll % qh llx lly urx PatWidth div ceiling % qh llx lly qw 4 1 roll % qw qh llx lly PatHeight div floor % qw qh llx ph 4 1 roll % ph qw qh llx PatWidth div floor % ph qw qh pw 4 1 roll % pw ph qw qh 2 index sub cvi abs % pw ph qs qh-ph exch 3 index sub cvi abs exch % pw ph nw=qw-pw nh=qh-ph % Determine the starting point of the pattern fill %(px, py) 4 2 roll % nw nh pw ph PatHeight mul % nw nh pw py exch % nw nh py pw PatWidth mul exch % nw nh px py end } bind def % Save the original routines so that we can use them later on /oldfill /fill load def /oldeofill /eofill load def /oldstroke /stroke load def /oldshow /show load def /oldashow /ashow load def /oldwidthshow /widthshow load def /oldawidthshow /awidthshow load def /oldkshow /kshow load def % These defs are necessary so that subsequent procs don't bind in % the originals /fill { oldfill } bind def /eofill { oldeofill } bind def /stroke { oldstroke } bind def /show { oldshow } bind def /ashow { oldashow } bind def /widthshow { oldwidthshow } bind def /awidthshow { oldawidthshow } bind def /kshow { oldkshow } bind def /PATredef { MyAppDict begin { /fill { /clip load PATdraw newpath } bind def /eofill { /eoclip load PATdraw newpath } bind def /stroke { PATstroke } bind def /show { 0 0 null 0 0 6 -1 roll PATawidthshow } bind def /ashow { 0 0 null 6 3 roll PATawidthshow } bind def /widthshow { 0 0 3 -1 roll PATawidthshow } bind def /awidthshow { PATawidthshow } bind def /kshow { PATkshow } bind def } { /fill { oldfill } bind def /eofill { oldeofill } bind def /stroke { oldstroke } bind def /show { oldshow } bind def /ashow { oldashow } bind def /widthshow { oldwidthshow } bind def /awidthshow { oldawidthshow } bind def /kshow { oldkshow } bind def } ifelse end } bind def false PATredef % Conditionally define setcmykcolor if not available /setcmykcolor where { pop } { /setcmykcolor { 1 sub 4 1 roll 3 { 3 index add neg dup 0 lt { pop 0 } if 3 1 roll } repeat setrgbcolor - pop } bind def } ifelse /PATsc { % colorarray aload length % c1 ... cn length dup 1 eq { pop setgray } { 3 eq { setrgbcolor } { setcmykcolor } ifelse } ifelse } bind def /PATsg { % dict begin lw setlinewidth lc setlinecap lj setlinejoin ml setmiterlimit ds aload pop setdash cc aload pop setrgbcolor cm setmatrix end } bind def /PATDict 3 dict def /PATsp { true PATredef PATDict begin /CurrentPattern exch def % If it's an uncolored pattern, save the color CurrentPattern /PaintType get 2 eq { /PColor exch def } if /CColor [ currentrgbcolor ] def end } bind def % PATstroke - stroke with the current pattern /PATstroke { countdictstack save mark { currentpoint strokepath moveto PATpcalc % proc nw nh px py clip newpath PATfill } stopped { (*** PATstroke Warning: Path is too complex, stroking with gray) = cleartomark restore countdictstack exch sub dup 0 gt { { end } repeat } { pop } ifelse gsave 0.5 setgray oldstroke grestore } { pop restore pop } ifelse newpath } bind def /PATtcalc { % modmtx tilingtype PATtcalc tilematrix % Note: tiling types 2 and 3 are not supported gsave exch concat % tilingtype matrix currentmatrix exch % cmtx tilingtype % Tiling type 1 and 3: constant spacing 2 ne { % Distort the pattern so that it occupies % an integral number of device pixels dup 4 get exch dup 5 get exch % tx ty cmtx XStep 0 dtransform round exch round exch % tx ty cmtx dx.x dx.y XStep div exch XStep div exch % tx ty cmtx a b 0 YStep dtransform round exch round exch % tx ty cmtx a b dy.x dy.y YStep div exch YStep div exch % tx ty cmtx a b c d 7 -3 roll astore % { a b c d tx ty } } if grestore } bind def /PATusp { false PATredef PATDict begin CColor PATsc end } bind def % vertical lines 11 dict begin /PaintType 1 def /PatternType 1 def /TilingType 1 def /BBox [0 0 1 1] def /XStep 1 def /YStep 1 def /PatWidth 1 def /PatHeight 1 def /Multi 2 def /PaintData [ { clippath } bind { 8 16 true [ 8 0 0 -16 0 16 ] {<11111111111111111111111111111111>} imagemask } bind ] def /PaintProc { pop exec fill } def currentdict end /P10 exch def 1.1111 1.1111 scale %restore scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /DrawEllipse { /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc closepath savematrix setmatrix } def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def %%EndProlog $F2psBegin 10 setmiterlimit n -1000 7030 m -1000 -1000 l 6783 -1000 l 6783 7030 l cp clip 0.06000 0.06000 sc 7.500 slw % Ellipse n 2175 1575 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3750 3075 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4800 1575 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4800 4950 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 1950 2775 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3472 1822 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 5625 3525 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 600 2325 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3300 675 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 878 772 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4972 2603 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 4725 3600 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 5700 675 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3075 5550 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 2925 3900 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 450 5175 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 1200 3675 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 3750 4425 75 75 0 360 DrawEllipse gs col7 0.00 shd ef gr gs col0 s gr % Ellipse n 1950 2775 1969 1969 0 360 DrawEllipse gs col0 s gr % Polyline n 1950 2775 m 750 1200 l cp gs col0 s gr % Polyline n 4527 543 m 1002 6018 l gs col7 0.00 shd ef gr gs col0 s gr % Polyline 60.000 slw n 1830 4744 m 3705 1819 l cp gs /PC [[1.00 1.00 1.00] [0.00 0.00 0.00]] def 15.00 15.00 sc P10 [8 0 0 -16 122.00 121.27] PATmp PATsp ef gr PATusp gs col0 s gr % Polyline 7.500 slw n 1950 2775 m 1800 4800 l cp gs col0 s gr % Polyline n 1950 2775 m 3675 1875 l cp gs col0 s gr % Polyline n 1950 2775 m 3450 2175 l cp gs col0 s gr % Polyline n 1950 2775 m 2025 4425 l cp gs col0 s gr % Polyline n 1950 2775 m 2250 4125 l cp gs col0 s gr % Polyline n 1950 2775 m 3300 2475 l cp gs col0 s gr % Polyline n 1950 2775 m 3150 2775 l cp gs col0 s gr % Polyline n 1950 2775 m 2400 3750 l cp gs col0 s gr % Polyline n 1950 2775 m 2925 3075 l cp gs col0 s gr % Polyline n 1950 2775 m 2550 3525 l cp gs col0 s gr % Polyline n 1950 2775 m 2775 3375 l cp gs col0 s gr /Times-Roman ff 300.00 scf sf 1725 5100 m gs 1 -1 sc (\(x1,y1\)) col0 sh gr /Times-Roman ff 300.00 scf sf 1425 1950 m gs 1 -1 sc (2h) col0 sh gr /Times-Italic ff 300.00 scf sf 1725 2025 m gs 1 -1 sc (i) col0 sh gr /Times-Roman ff 300.00 scf sf 3825 1875 m gs 1 -1 sc (\(x2,y2\)) col0 sh gr /Times-Italic ff 300.00 scf sf 1725 2925 m gs 1 -1 sc (i) col0 sh gr $F2psEnd rs end splash/docs/figs/starpartfinal.png000644 000766 000000 00000137703 13261626263 020237 0ustar00dpricewheel000000 000000 PNG  IHDRRJ>PLTE#59/yr%& 6|o)3lե,CU/FRIOLLOfIRivp Ff`\VlLFB<2,/(h"o,re3)b6&9_#<\? YV YB{\?x} ys_mic<uYvS OIb?9V) IDATxݍ[[ C"JE@KE--#Lat1E]CL`ŒDMш։hnh-!-eSz/^{=@8*79|kﵿ2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(2(JZ|OK?~,y;y Hh0O̽F/>2~uZKM,͢ͺw޾}{,IQqW[ wV=0F=YWw@~Q]͛mϟ|Z}?^Ⱦ5B*Ba/ᆝL *lHLEIxU5)"RÊTg"S/22S21~(v8?RxauZ3=;SLEH HT ,pjIQ'Hp!( 00 GA'i*l*P9o)-ieq~řyE$)IXS\=;&G9R"b*ϵT)R'zɐRIbIlLO~'`(Q1R$F*и-hÜ(d '+c{6Sޏ Q>7H֗B_ )E wII!e! jZTISM b (B}[HBLRHy߭Ա\Rя{YC?0)6I ՘r>]t)R#8ڛLS䛺Nq?h>7xqђS\IO~ΤNIy.2~:cRT)Z"0eH _T(oh7&EpVקĹ25OCʧ5ď0*DL '2Qx/܂s=NIm@TqPN)Rdr5,¡* GoZ NixHn}RgI'"c)Ι/!(M>! )UHwtO)J~WlkRH?΁E%ꉤB? )0mȾiIͦBm()FTKSy*A38HV |r[EL!U^(ajk0 El?Oh,)T^jb0(' 󗚧r`])BxsRz(uh-œN*Z6SƤH䓚M""*L>@)<0'A-jP{}])q4OD=TUEe/?\R8RۧDi)vQ5MQ:G:;cu?iPMѾ_r=\8/J JlQ m~")~eIIಁ#EӒz/3I )rè,A,XUa r)<呂i*s1-r,EBREDS^):Ee%z¤@TT~g"MdXot_imb=DIԤ&TWnHDEђu,GuBQ J#@eI4~/4%SV>#%jTNELp▟"{%%xRY&Tu#a @C rHS > Ez~(M8gb{+_bjTj/FbQ%e?%Y|0ujR0%Hbrnþ$ߋIqiC?|Q+P4EM.:~hD5)qӒbD1ߨtRH$V^˗睟)l$u()0$)L4)s{~ )[x)ʜFP kwhLNzI\Hy|S'OAR(.tb@qlS _T,˾3yep+gkV5 )Q+c)Wa*%撂?R@ %E,(E `ɤTIQlI=IU )!Uw]w&AU@AY_JGҫJebz~R\ψzKDD8Q`)EaR\AȧXRYGRL Ec)5NSڬ)£wo+*E|J/^1Rc ~\QSAQp'%G% E^_\R\/T="U UH)ӳ?&KJQ-P4%*+GPw E9()1H=KSp@O}1PϜʉP*(NLR M')Iʙ%􊭊֭/2l^6?7%>IYzhTR3) "9 h$MyoV{~<) eNtOMEsT7nݍ<9!RoJ|y)<Y$&9kSdsHq:KR )|P[J* *M%)G*TTCej+H\"=SPt'36> R`DKRT)<8_)H}N*(KS\b@yHyQITO Xܹ[_ёe(E/.:"xKb9$jiܗOʙ*0BډE;'Gnbi/|16Y/*|b2D'kpb=!CI)q1cI]4SՐ;I$e@D%bz}EuqRl])(~֯ zQs H2P 6ǖnʽYe)-|)*GEFQ4C^%|ɣ}үE5Ob£SI&g*%!C> %(UZ"u Uż qDqhF{ 7h$t-REj*v eFR%ŵlTLEžQ(O8I8n)$9}<  Ϥ'X'ME FTl0ŐJ1T} IAU FP9Pz:VT( zV[HX 멹٨JBDFH.XS^H%:#RT,\R0HT@׆U+L<@P "va?^׆΂Tf3!UTT`,Gj%)^+ HkE 7q}'4zzLۚAE!Sm_J|"7h;)Ljq1h*om)n$g*eΘTۧ0=*4zCrTgxZՠBg&E`=igzLOu/.ć))!7R)I5œʿr%D*@I^BG*g/Mg̛LR` -F>D{B"8P6EςJ} ¨'_NJN eTCI*TE|0=|Ȓ9 `ssK2DQh[ߚl{ӓruTѦH'V{H$u~};P{ ׷񊔦 LTxH|Ln4A!VLW$?zwFI7H#5&S B p_6ݕB '=Nrvww5%j*"-=&*B9"2C}Q|9 lS9*Ɍ^? [(QW&lМ}@MMMYRCJ)j4¦I*'+PP$\Z)g<5e[%!mI7j׎I1vxQrcW,R6Аj*z-f%M'ʑ/R8{"WQR2BAa*Lz > /#Ԋ(%-B\"1^Z{iʁuFFNC;*H=6BR|1ɤ`q❹b#NӐ"y*RLaX)Œ'e}{r48y"yr ֗q+NMVVRA) SKR )nTliH'FRDT5Ns>f ې)(f݁ wB|K~u- Ÿg{OIPHTϑX HHJCuZSH':QC)]{o=80ͻ-=Ehۤp/-P2iӠ)pBzClȆ'۫KV6̈́(a9GH_d=AR]Jx' ]h甔'xbIb^*R3k}xf(؛8 *Kq8pY'( {8:%3"M )OU<œ"zbwq}HJt4BnCz|ms wtc*dʸrbUWДi;T1Oރ| "#2Q1 {΀ԏ )5"9J 닡:E`ۛC%[G V 1/5bJ?ȫHU)$EQ Tl*h] );u*1S'*74^i .]YN0)?rr_wb)hj1y()ȒL!Q篂 GTqB&Edtk'YvhMOco B~ѻScTP¸ ''ߓY(~`Rs$M4ujSaRh #){.+ʡBkbEb܊(J$*#e %Q Pe*ʔ1T)qљTSEW w~:~Ξ S ]\ƴSK1Tr(j'7k?=KNώTr|I/#$EInQPi#Hzb9(v\ݏ)*24TZV/ߩp ٿ|.WFBé_JR/49Rmm^lIn*qrP2 L䈲K/&DQN 1Sbx'FD| YI)q /PHTgjӁMy=⤈wWDSjfOVwM*O.x) {DY* *9K8y)b-R!XE bH)TTaR'V҄(7%3LQg05@zBZE߳&7K(['qeDꏧ\VHT#p5!ŠrEY??>ٝo@9Yz}擲,2;jPZ";Hiy+yT*UH苉9R۳ U,M%rI-R#m&TB$NC/բq,1(Ħ-R!8LQ!PeeJf550%݀([t`mݾjNeHUSH̼t䩗o: -3<,{}{5GtH<ဢ ze1}ut%+qlqsGfPϹΗ "%>YԤJSH-2n`z]b$œ *()?bII "i妟fD@-9˖,PPS>)rB_H)QVAYo~@-r)o4U)ZV(A_r@"kv&ڔ2 5]sMv450O ` `k4&A%墐(v)LylPR7p&'l Eͅs!OEOOJA@ʵ^0(q$+) jM&)hS¢+;K]` rA}ګ IDATH*ԟ')n<$HGŐyKST)]')T<:IsTvH"Ŷ J[@g!dڅWhEy"a ;臃E/{h#j~)T1Tτ'Ʌ{f_ (z&Cp[iTpb,hDx zFWU?~1D6?;%>M:R$Mf)EYS7ozjH6 IU*UԱ{[^s{~1E!Fec!(q:q/ )ki>|!LRH-#l*W$UWWSzNp )j D "~$ݾ0 ΓzpK GaO**LP))UYžFܢ]ۜ Kj~ART*YGNi*+Λ>n҄= '($=QK!~OЅ aV޴ֱT4嗽+DU;BO]9J0ʼnr!%M-I$f+==o )nBr* (9i4[m) QҞ&4=DƂI zo[GJ-c<}I0ő AQtJHlt'*R9:/J lS2LaNzsT%Ĩ՞2SfHU ML]*jT0!%.(B*n*" T.)׶ܧOvLUX# rz>78F@.M2XTƔz~ )7٠\VM 4MEHKSQQ8MQR4?M$ٹ ⢊'&ڝ9PR95$ácj-"# sXJJy$LxjL-7%fg9SE1) 9THM2f?PBJOk[Q[}nQsv^׿)8J娡,*)K)CtIQRhQ" ʸi/DZ3"IE@ׁR3Pdt/D* yPw\HYLҦIvDLۘtdh'FMAPSwc-|I5/ )P2DEN)Y+%#\5G(F~E奐5v)@28=^dIe@YQ?Nbh`Smm<)=^P4%-etR>%J#`G3')<.gUMh닌9;tY"iA$"ҔOm*Rt,H/FjG1).MHEM=G=+J?ył\dsu4)jE (z?{ևRRP )p"@jTBpӌ)(%Λ=˥`)FLӾIS1Pj$Ep?zIqUW9qKХ1(ϒ2ijd)A: 81{Ķ/%.f^*Q!R|:SRw@RGBlʾI9R82{6bW1Q K82!=4'|4&7DԨ.PhSbc8#5WPPPTeHow^ L$UTc:rij@yRIMz&)|&>T5Lʴ0R3&QT̓^4+w&偲H TYR!Ա]Y6ijCEP׉?ݭ߅ѯ4dGISD%%}oxRh]K=4 X=9OZA}^J4I]etW2!Z'Qd.D '|N2Qsg?(bTm$~ y~1̚qux- )l ҮS< CC M;R{ZƔΎKW`MTeu}-^( Ǟ-xL1L2]<(Yx#1,PI#R'\O)[T+gXpqQ$%HiO;.nL4%W ANER9etyP9H trp Cj׳:ܯ^Sȋ4P|rlC{&72=:6i JhZbKaCBŊUCxEBfρ%ŋ!udI\d_|'t =F*<[$E:}ВKQrn{:S ')%jO (Ɣ-LRԲ0;B2eWAPx(U[8S * r jHm8p[RnZWߑC5=СFpŞ;&L9d%=zU&YiM.wiYݢZ1/i__+L?Mfx/4 GSIé9T6M # =H=4 3$i/@j.>7םv)𘊄^'kx'&TZT(SN:!zzD"۽ΏϗEA5(tRyrT4}mLR<7KR~sc,Bj%t*E;SXIJ$#۳G ,>:(zv&*u)R&7JICXQR54EE`I_tIz")Wr״HYʵ4iz;3Y ]wyóQ9j~CB~!Q2O _{YQn׌MYRHJ|믓7RH(GS^El¤RTD$)`Otä&~vB;KRp 9nH%!Ehm5Dvzeoes f!UI:d$}ԡNSjWOSeHjT.Jig2M (ﻖJMꭞg*L*xH::~>!+Q,&|RlرaAmLDcQW2(-$w=U^5e(9kDʐiJ);dnDJW($)EOr |WHGRBbc*,JdTsDȒCH2A3 Q,5tr8pbv4U77TNZʧGb&ՠ|M4)=eIYR~ H9SꟖ_SKbPpRu4I6*xm0Cʘ"楦p} ]-Y@I lՠV\ܝa{t'xC]p!LN!9)S++)N_T['J{~!R,Ri(J eGLs=?]FIᓵ)S&wb>C j5RD͒$wI0U/ $o^2\9S I’RIqN] z"UDOl`s{T5{ł$<˘BK&nޚs`( Rfу]&灨CџtA]La"THgƔ!.ބD1 c7\%2u:RoRS`(΀Ը"e7j02Ypgr ;% e : OH SXحMTD!Zbeg7ebIMMqۿSXQI+o ?%)&:*?{1dE e[kuR0aut}nQST'v$妧do~,zR$ukD:wӦ)5 KNϓTʮ)#Bw@ݻOՅM=RR>!zј7kw@Xx]c>(X}>(*,>r;x]W0t3F]gױ!ist:x'iRE}9|S\LʆEMARaSTOj 6@N!BA@9*ARؔBdPJy!@+7LF]كu8iHDZ3#Dm**A!s8>pf'/Im&)Z|euZ̑r Ft9M+ԥ~@.GFRaJ|5~9z# xOޗpfκY}ntxW[qŒBM5H*ވ(doДC~'$)%ʶ%%.JJKA:zuV kcvFaœRs8KvnjIUR)HHS!Q<^ܥ4LM~zkIc)IJDOd(u01=`14F\(rpMIt}Fw NȌwH& j!eLE%g6wT qQu$HݹCԻ6mjrHuR5ژz$ T"чlpTgvfX 2A]B d*|*ϵ":ށȒBG{yʒgSVO(TnϯTRw}R4Ř%B`ojjĐSm gQQPCN'4ҏm6CB=jB!P_}4{՟+oIٚN패ZBR ?+O%4"&є۲PϪ KKSNBJ=! ~+K'/c%>IFw 9F- hmfYfܼ%ET]BӼ#eD5i)م3!śJTw7J*1cIf\6>'2 FWcD'DoTYMä۲JaoJc‰9Jt/BDRHY#UM/'I )ߔ}{:)IzٛYmJSxMHC`|PNie%)ԈRSK`F2I9h#ątΞTlPT^]"wR5<[ ZωΗxEiS)zHqΓTȔS$GDN$eQUTKT'I.b1R]0mIu^xS4Lw 5qWWyB x#TT5|0ѱ0Iy(G/u.4ŝ4Bu8f|~MPyOi~$!Rn&%wM${&bJ[(3 7rpzkRkZR5kk+~>$) ?DtMU%]\"囊z$HTorBl1YO)3J"9jfdg%3\A()pW OEMR7IMّeMyig@y~R,#_&޲A)ft#5 IG,pswa9?أ]Iwƞ%E2T;X٘FΘ \"x]t5DC{ou 딸R/U>=KRn؂3^G'G M }(7F^?렙w I2P-\âGvAR-캤"ŎŭESTHi 8)ȍInEQV5Oyf*!ʼnktB!uny]xl6^[ y:2R-pDZR*G ɺPTR.$!t>>EE#  (U^ jp ~2IYQϞaS$U]\_܇>/d xO3)ITzXeJ>US[/؝ZYjqN6I]5P&IԓSr>[+Ri{}!MVJDxy2%LEXv'7_DQ1SAQ>B BoV]hS͎ A`ʜ=fLu>eJISK+)r={ s#€%:>G :H9Sh0/e|!UIH bQ0 (p>뮞zHS3Rދ\;O5efH#uvNM*g.]P ' (yym}Q)\Kx1ՑbDrW탋eS@7 lUGN Bʼn8),J||#LIR/n}\359 .U {Ui"K-yn?Nhi+#Q+,=2UDJRL~RﰭHOsz4SYGjk* )TDwʐy 7x⍓ZqǹXREN}>k%_T00U-)~ta:!7?LYȷR"IjO%,@ڞ[ /v#et7s(4C^$f'ȉhFI11KLUR;M5(l?. 4(i ?a^2W[`Xb8=L嘪MR|ϘSAESTg(w-IRCe=<)jOf[^,7S{.:H'r%mU]W'Ɲ(D;(G5QV~RxhzI(OTV'= J%`,_zBIexڒCROHr|D=Rr MLIn' O$)DjwdE؜s.$Wuo6HB3' [i{ uu' OaPMy"HE*ŔOI jc{~Iy"c$Ŋ3:R9KzcH4Hu,k;mXխeI3YZ7⛫^p="'(mDU*+.ōRiEjDŤ">/RmURVaR!S9t:_))X'D~߳g&O_盍g]Kj>2SՙJƍ3 *9]?[)WzSћ[XOդ XORx?\;(}QL)c KM u3R⏈jc;~ E:N&)]|6\zL|'HlKR{}Ԫ9R||GGߓGnhnO6̯SJDG ]CvӂI"Ih m?#|I#ɵ0Y:Hqy.B]*)'(W&sIRghL>䛍9QɆmcT;o^^o~ӧׯ__hqaS}ny?ȯ; bQ~0(HAeuf )/)GMZ"uƦ"zw*R(k R"5.IlɋnTRr5y3' c.]j|""CԔ%Æ-} %6)\02s{lfOA+~;gyjLZפjU/@ u~'l0b=n4VeM}f4UT`r=+BJySZu+PFYXNV24z9ox u2Ef^"YʡU4eIw)qq9 ML'0)~*ސ 'g;w\2;eÀǖcN8'B>Ǹ˪}Uf,)RDŔhRh*(G* Bˎ̿@ru_SgiSo%qDFj> aJ]D:X~/: bTTo7-`RJǎBp1.tp{Cř"/09$= )w+/4l萔DE䘔ʐ2xXR{;Բ#FXF"KW~_?}=u}ҥU$$O0aUDZ)EʼnE"'SHL)hYwZR&KI@eHYS3R֎LUDz<x?3?to74>y*铇Դ^-":}Ks'y )a9Ȉyx8V#|Ln,5@ )gI0iH *6.AjBjm,MUi6x~U&Hef:V6`}V-{x- +VTt|)&Hi*#wDR7SI%H%St0MKeI () ,Me=>!޻*z&+(ROoLv !Ԋ>g ޞ7+ăJ#tJ1^*qQ^Hcp/ɨ*J+TH%()8Lٷ?{8).U¬gWϊt_gkkg}u^Fy?T 7N:zA*ICy(~%L%5XVH|*Ao#P=O^ƤħO  !uC[R'Y/#:o\4!EW~O+W^tغ.?(|xQQ"t{b9*@3o7<#TD"D+J\RԔ!bl9:m1))BԶogd_)-'Oo??YЉ[NT?/ݻwʣOֻ%b$b=HP`B FrQ}}0@=/t9oōHЉr٨1R^O;%"BP//¢"P<`8ogkall_llfVΎRWٚ IDAT>elG~Q]x}ly{bZJ-muOωC*[LN4LD9RcJ\c6.i5o}Qx%~>-uhjLl4e|4yZO~Ӿ']_=@RtIJ-FR\͜0E/C+_1W )?'"b /8)0(H 6顭֎IPTeh:+8)cˑ=rC@uh7DU0EU/#Ř ~yK̮%Rl/k)TT餠8),DRҟ!fR.wɃ\mhoRj_r3= 2`)q?;CWpOxE&BPO uHDR`J|DT U83"D>Cb9I53̀dw`tk}!7>^hzwQ@ MN/呺@6ڤhɗP%+@%p> N WSVO4uQTNJ3%) θ<dIM nOe#cicSSS(I o*_7=,KuOD*$?YP _`HwLeB<)dJDDH")&RMJi'5ˑ¦|RÁ$E1*B 7+J)K-<5a*NMW>Rorc]j'R {|$mCG5߆S&6 Wc)[ ;z.^B}C~9r R7?}]||X;V3R!R:|: $渨@ZbS򗬤%SHT5=HǏHaP /|e}y D{j g^skM CqTXSqɵK~_坡vuXF\D:~nƓWlJofTNWς]Yɲ_-?}Ts"$)os/HS/ m%Ŏ١)E݌scYnr_ʂ./<W;&onmz{[ߘ\=ק0SbS{Ǔc ҮT:Rض?޽éS*ͽ^ϏY^jC-5~wwks`bkuUzpڕJz*/^oxkW|Vj\8>3+5!}T`_k  y/.\HZJ\wҔ9P-XUiuQzNOܑQTM% H1> @r!4rYLno.L-5%~R'YOdñ-W(L]ztobE:\oM$E' 8Tn8Iy{1ΉF7[=QIE&X?jƤr є%Ѩhj|p{se`b}aƓ'J?{5Ƨrjzbh{aCC{2R2Rj1[8`()|jC>Bb ITN抐)q"Sp@U)ΒTDT!RO[&Fd:4v8H}cF*dǐanӉ^Lʟߵ_Hw6(6BӳKEҁEHm+(OMr?$!J"U [I1(J *L9a%FS+K-C;˷.<TfƚL+L>连E2HQOD;/œS'E,t[@kHfV[_qrxoR_{O6\{MFJk~gzXILTԳ`o/"^g>+NE wu፫HJFIp(H./dsl8R(7]}jw{FcK_v66ܾ>zpH54O4^}7jA>dV76##()P'N :bP8(HSp%04:LTԧ %cfJhh|fRZ/W!(" }3%eN{2|H텅K֞oWRG uTF^:aNq6H5ܵ0߽*u~B#Vv}KɣuQ\ЌҖeGWaW1RyIQp2H톏HV덌NNΜ7~ӓ3WVu4Rrձ͕]_|{Ȭ[2˜bE`+@1'PQO8L!VMC)IѤ3,UZ `B|QwIk T|}@ߧWr*3R3c Y0sč}P^ _z^ޕk7!(E'Lu*RRڅ|Q|Zر ʋW̬Rwgt2EE<@HH<`u (w=|}ᤡU\,*}i^IzhE5^{towҵ3;YW0;:3uIP=6);J2K6_T>/) զ,lJ#3[0( 8^2EDYR.IcQS];ꈉ{?Vt&<}'[OUo2R76vtOdԑZ[d(]DL $*ǷNi3}vDK #5"aDJfhb1yWvNt.ٙm%{Qjv[;LڵɒZYw,u]o3R-ueIJ/'"~x*Bzh<6…:>I1R@q[8%.`٬aŽYw8K:<)_Ts0Ճ@u2?jD~'R CkokuyykKu:NUڟQUn=W3R R\135q n;cdy1:*R-E.s) |W(5SLKms@5k )&IGP(?AN;LOct%%a(QTYʺ{{#ztǬw㱓9MJ;Jx 2m|^]s-18RDŒ*TYWpώP=$ESIR'z|RQ[)ҳ2+ea`R®-qNw@n"~gY{_{Ըp8=zв&tȓe(/GBO|^Dz**ST'I?5F**ʰP0PIy)* -n?43}v$EL xP^_u0urO}|,JW.ezjD}ڦxeޑbMFOHzEHqFA%Bϟ'}/;@t#s7M"IT]'8+b_/G?7؝m OhjDlWI uj^rMˢQzզխM2DMy/f#Џ'Jd?M,w;R{J\(;DEud*|`Rw@oA!E Ge(-8DaRYlL 2R}Lҽ{? ן>yzSJk;펔 @ v6ˉ?jIz:)'EQq?q_YITGuum$Sy TY_n}| sPv9ƄIh*E OYLLĝS>ֆ7o,\~})SjzM;{T6W N hCHvM1=m})"y|wҘ:y%* .=>b^EvϫzD mJ*!D:Qk+sӖT{kpdvBMMM"Y=i||jScqO'DO )9I7JӌM մqnL>ET Bk<? XQS*T!' U&ȪXo`q%0YRFx=JNEARhuyY?_]__h醈!ړz`hX8;22%eqԐ,hƍ4`PaPTR~{ c"xXPԟN#5+FKeJtl/j֢xQf OP(ƽ!% JZYYYk9jT2* ~I1jDK)EmBfeRyG(l֌CZ*e~pOHõ)ITz)O7$1%IS:OIYQ.Bq5mMfFo>8ˈZYZjFYjU-<_^IJDR=yBI|SIJâ *R{2xT2(TşibF )0W;ÙVxTF5GhI,w1(1P`JW41,(9jii=MHQb0*/o=Ip꿝ZX?6s:,/ IQ\4Q^CEo!N8@JvaIfJ\ )u3"s/c$BQSP _g;p0)(u~KTNzm m*%Hdy[ :Z[%MR[I&j~U>n+fuϑZ؀]l ARQkjTRO>,IT'H')q"'z\B,+Ytd=;I`8ǩ^ISt@^PI$yG)=/`GP=AToen}oH=T"CP3'cRG8pSUDwUKKHsH٦E<:䘏ݡLւEx'԰'GE, 8/xdj4t [AU^yAZ_6לx~ խ7}yǟ<}ѣ RGR R3HHԵ9~nAL2.LBb(_eƯHxbRPUMYWwpG)Z^.B`+iX>jP}CsԵaF{xៗc IDAT5q*$QT\g}YzUݎ+R$Hn;ytQS\XeTDa‰#M\X3 V ơTXeH={b ՗ˡGB[O)?\7JVLQ7&!<T7EIKOCk VtP<qJtP( eȵ\!o 梛:B'p/\zY x0G`ՋSԵb7mB lg] )ƨ &T5Vՠ+C*FHF 5T"Tj- rP!,u~LQ_KUTtB?APPG#>ND.G a B%6&)Ô#-geU#%FYd`r>G#%45T# /ܒE+u%M)|gGb>1Eu9j\C5BT נ1L/coi1k 8W(͔$JT:S's{ҖY)WXX{;aI9u魺:yH$tR.c29s'QQ/Nä:MԣcDuI0=q1rR)#;IU JPw٧") Ub‚7y>PPG7qNbܪ%=p,EM`j:(STN(*/RL u~uˆyHZ'(L MU}8k2UWmL)U\_t"ϖ5pu`B9-<4b]u6jIEP% 'Q!E}0G`Q'Q_~0vԾ"R(@ ;kO@n\#ߴk?|ŰA}B~Ect"qj9I[en_/*=v4F) ?a+)!eRDRBBJ*Dyi'q6f+2Z%d)07*j$Q# 7i{!} YRt& &o. cDp/ (ÔKߨläSoT`*~(wK> ^ |,D,yԿץ)XK*h'NE "ŇRbJ84(d+(!rLzl?Qԇ. TX^` sxﮒ) Kwqz—E4Dϙ8M-Yxh'g]& $j$/c0[8W>Q#JĆNDRi$y+fDQs?Xo,%:f ,Rh55/,HP83*.*!˗H TGIDP# XTa?W@J5RYDIeGŠ3*Ta32T[&Uj ]_b .1Ixߊ+ +vG%:D)JMA\.$5KELĻbآXH$QUi'*+HDUT 6XjMf Le-3ZGN(r `|)b9*f(.'`d}\هx!إ2rM}9*{9F%D)Z2+[ ?C 3E|6'\:BI*,EqT,LI Y).RHTQ5?OPCLAUA q% J'"%yM)%0O\+tCICznL-wFR RcʉIHHzf&_Rc4J FʞzDQH}$  o4"%#kTalal.!)IsS6%1|R9C1HRO ކ@}AJ(ETJ!Dt)p.+R7q9~9E'u2X5}~9]彇I"zr%AEid2L?dG$=ʍZ}?z&!IeH' &" )HI ~%,G]VD"`:IrI* F*m*CJ7e5H{b͵'䚯.*앐KN,)$AJGMqT?S9bUԐ^lX|QiEt ܔ%)@ ={Rum%8OI`]+e7QEl^_ ʕ')B*=&NH h* P[ƕeUVHQ%QhH<R)ƸJ4)°BJÍRLI}?:<TR XC P!QLSp3 M == sez}H}N}g% b+R'3%T=XZT&K=!eb->D쌤.&z_2uԖeQա?X` v. *gjQ- uI9a*ȋESϻG*jHDO*bA*)x3V$<:) 5 '>@) Q=eRk^ݿþ7Qb/P ;) phN>tM'C454 ]pgEE1G4u[3D ؋ڈ BۋB,tSVWisT(NgC(D(D3rbUB@ uhO )eQ2 M _KUx_' jQC/'@OX/j")S]DGSsuR{}2'q$4$CMtVFVRV)b"aɹiK)Th+ N?d^T^[y2H7CQ(iQޣV ՐBjDLY J%=B)idDMOM)"cB9 s_zVbW{> ooRI$QdЌH)%>'p=?XZ~wC<>b+]88 0!+>iH`n GxU_DIAUoH@Ii>}p1LpXGJO(ԪUUn*+OKJ;c)M9'O4SlOS8'i.{?zniB+L? 3Gs8ICT_M)*Pm=6"x+F W3P6ّqD^z(nh$*ѠcIK!qxt[0Zh?"t) )`E!+o7C=L <—?#?ϔL}q;Tm:\`I )/,DOL!HކPRRR>QapScJ@% Dyb=R,SъHSԔx`{񧢧X?'$ n6K\glLRp|'fbOC%ҮF隸QUSBvPeC${,SEjRbArP~& Ji*ȯ)9):*BzИzc5Q)pmJ'_<~ǏQ-~"(E ɤbI]BW)6v p"8 sԈHṴ}NPɚ<[ͤc"|I]%a=9*Ry2d(.Qfp%jRwlR?VIGQ]U'V!!*z3 :z?|`DR)K<TݍUXus3D=Oezt9tUR'NdJM<|) $MbYZTHS~w,LRU.T<#OTPa|tIJMJDM%9^3E4DULER]؜Rw7씍(ecsL@?TgJP Խ{)Sb4EcI )KTP50$yoUn|ekO)Z<qNt}vd;M]t L("( fT^GR0~\][cn@Eޓz榋|&9\dR,@v{QO_OT:}QC)HvLDIIiJ5J]IhMkjI=PqOzol \&xe1%Q[ 3aC(:"'ĎT&(K̓tIyPR0H~RHYRF6u͑ -ƒ#Q\R-U%߰EԘ@bse'(4Q(OETaD T$<)F*uAUy"'.'1ti I B/3WD5àђJµ> ^R{!höh- IʓMIy@A%< ~[đ)2GA.qO4AqQCBTI%nT@TD7SLT!EM}&ume%%ILt0m#Bit5<9^*iDN"RM/z*~>@%e%"H=E3:cSfTc 4$lA8M㇎ 5rzRABPYQtt{ Oȣv=QmR:`4epRԉ> RH.m/ @`Pai#QI֔-KrLA`zXQ "}!@A@zbާn )L AU(qנ?=TO QÔ+U"\T) BE:FzmIݶAV7 ޫEe!dT2({S- 0'mrh#J+b_QeLqR{~\4d ɽ9i LS9r|dPaU <7 ϸA쒄339?π>2 ::F%E\TkC6Vv⏺M<(ҏSki'@ORw̔$ej>Q#J=y*gX)/)6VDw0sNKTvYIݸTu#Sz3O(|(9}A94(Xgi\ϼ )JMQ. 8ʻChJ#|{4TOp:F)ʃQ?Eـ^XaP dEW JZ|FQQj4VH&,gDycT^0t]CNllNX+J}*c*nSH-"ARF!l[3Cp4TV>:O Ur2 oEI EQP2)fJ)S$ujRW$RX0UMGf<` bU"5λ4.QhW5 QRY E8aQaJ% *n*'id_8)|q*D7+. -<pBPCUj2&)Wں+Ty<B0% !I#܃CP'1z䉕- =TJTP*X};S{Pa$P5DQ 6v(*29H r\,R1 M_mI^O\}d'_*ं"E??3pT3OD )ٔS:_. RlH8*x'ϖU4QTQHO GuʛRDqS)[K$y:DMnH^X[lj72%j.QT:V(>CFI|Ϋ9f 5&(J&JZ8MK[ǚAT)xtGIJGr#zb\*5A`P/d(ϓ k&+^b\'*ZS*8{1hyJ~FYU J(JHR=^ ޓ|$_Ń8%ڞzRIT$U$ .xbHfRtY(~&* t 78)** ) }j*}!DJNRԒ$=yχڒLXx_EeJERE6IJK&2)oIqN(}>TʦKڔ˥EERPuFR<Ć/⯷@QS&O^I y1CUОpbTT͡)~Ns9"'ܢ`)%HiEF#N313{x (yY4UnEIٻY*ɊrҎD4NQH[~ H&KP#(ҰXڊpZ4GrdI1qx#e}"=?"e˕2pr"c%Q p2C6bY 1ER"Ŕw1J^q d['p !O<}EYMQl,7e|@TԏXSR[u`gA6sS9Jq7: >lLvH.uTFu+#ݠ1&"HQT$'V M!\Ğ2CЪ,ym[0EIr")ןK2(;A[Ƴcj@McUkc(s3QGM&"+4{&)Gr5P` P1EѪmI-e,uܔŅ(p\8@ _'LUmTF5PHTD`};n(:7*S%:Q3rǗĦHNVv@ܒ0GdDMdDy<9 er/PFTf&>[!G'V Q{#(ڏU<='1be*otZ.*A-1dT$'!3AaOq QUI2?6}փϹƩ\ jGj=M">J?%6ϘQ9<K;)9C眍)*йvrr5?ETjIgghoI)#I{4"%@H9WT ⮠]&;pR%'V#2J1I% ։M (܊Qi.UziW]? !)$Mx@I#~1f:} eQ馄65{}u.J%'QILSl.jbp;[& rH宔(#ZQHʔQ _pѾ@QF2ήi6Ti;vR/WeYP^Q܇ 'E< /T@) )˪%\ƪ"2crJB9R(*G6 H 5 [,jQ_.{`\} S*o<&)=]DyJ؉A7K bBDٞj@Y[YռJ墴QC<*R~k%EHЀJ,JhI*3e4.Bc)/ԆHștpR&DX)+FfN 9_wX֢•*Y4!S(Ӥb{ JdVDTH'* Ptuf JoF)3KLCnvL)(#@*؈I*/h4S:&){>Ooݡd5ńLOL"gRؔZ EBE Y%:4 ^DY,ὬtA_?R tIp@Ibb(eX\U( ?VqhJ<2u7n'tO (͉PSDM"eaeFUA 7>b*zrJOn)y@GR*SPebH>%ɕ'OERg=AI. j!PR/QfNqB޳%hd^OnQ9Pe=- )y(y V%PQ7NrDe?wbjژsL#vNQL"nHi3ʇӭ4EyXҮ<HIS괜"<)즙@ ' CMmhʺ~(w[v|.NS<5J1; wi'0!p1_?^TemxֆdL;$os'{ZS%O|O$u<*QTwA_fp (OS5CM*L)E-ha0׻H9P$*j[=dpI89M#':O`St_q pjsL553η#*!%4eZpY#zPEivp P!Qğ.:y?U2Cs|ڰ;4U3h z.&StͺT[CLPbSjD~7 aۘT8FRh)v2= 4l:TM*}=꣸%WE=0ս41<^XT%*0&E<%v?m5@&(afwdTYޓc0 E[( {%4eAɦsvFZbԧp'VER<;(%uPBZ[%) /PtfIRSA餐43z됂1z="b +@b67(Um[.qQNOU24ՓE /XmP2 (DVx&U܊Q3^{ vh"e=82@[ =-+9-%Mo4) U$'TGh3MU'ʓQ|WrTf\ǻr)j^R%r&5t? ~H'V uTƕPPCD9ś\HQG;?[^ԊbkFQd vN[=3eI cfW=%(ԬI*\QD@Ox ȧ 9lx5£?5$E\S~RV#55;1*<+)L)׎mʻSmfsIsRe16F ,'kٞ_-bIpRXTsBQb&(9[TI[빛 O*&{"PKʐr=[a@4/7[|?pQ?Ħ*SԜGH,+w0>,R./Rvbu?H&Gklok#{cbQ*])yA=:}g|mpTǨ4{SKH3IqIz<גV{3~4.ٸXimz"mI{Fʐ:Dvj7[jDJ+H-Y$=ݢD4=H6fZ^o$Cl$ HyD3'b#sZ.q%Rۑ N<'ildXjv즬-N:0>.PLCʹSkEK)y{Mvla)>Hi,2ՙbI`JTg>khzn6/p*k3gܱO;XS09_?7E2&Jyl 9^u5۫$EǘG4q RB)iWܞ]?}fRvP=g-O<)Ѷ?sZη1wn5 sEў~}6B*Q6R'[*;ۨ#>{= .CR9Z|}-|)*T;ARSz%I2u*kidw?(6Q/O4&#AR4iցrP>]g`q%e[ #u1rm~]REf> Tɚ$FroW"P[{"j~Qߩԟ!AE@uV բ6kf-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢE-ZhѢżIENDB`splash/utils/grid2pdf.f90000644 000766 000000 00000005571 13261626263 016155 0ustar00dpricewheel000000 000000 program grid2pdf use system_commands, only:get_number_arguments,get_argument use readwrite_griddata, only:open_gridfile_r,read_gridcolumn use pdfs, only:pdf_calc,pdf_write,mean_variance implicit none logical, parameter :: usefixedbins = .true. integer, parameter :: nbins = 270 integer, parameter :: iunit = 13 integer, dimension(3) :: nxgrid integer :: ierr,ncolumns,itransx,ifile,nargs integer :: ntot,i real :: time,rhologmin,rhologmax,pdfmin,pdfmax,smean,svar,rhomean,rhovar real, dimension(nbins) :: xbin,pdf character(len=120) :: filename,tagline character(len=20) :: informat logical :: volweighted real, dimension(:), allocatable :: rhogrid tagline = 'grid2pdf: a SPLASH utility (c) 2010 Daniel Price' call get_number_arguments(nargs) if (nargs.le.0) then print "(a)",trim(tagline) print "(/,a,/)",'Usage: grid2pdf gridfile(s)' stop endif do ifile=1,nargs ! !--get the filename off the command line ! call get_argument(ifile,filename) ! !--open the grid data file and read the header information ! informat = 'gridbinary' call open_gridfile_r(iunit,filename,informat,nxgrid(:),ncolumns,time,ierr) ! !--allocate memory for the grid data ! ntot = product(nxgrid) if (.not.allocated(rhogrid) .or. ntot.ne.size(rhogrid)) then if (allocated(rhogrid)) deallocate(rhogrid) allocate(rhogrid(ntot)) endif ! !--read one particular column (in this case, the density) ! call read_gridcolumn(iunit,rhogrid,ntot,ierr) ! !--close the file ! close(iunit) ! !--calculate the mean and variance in rho ! call mean_variance(rhogrid,ntot,rhomean,rhovar) ! !--set the parameters for calculating the PDF ! [in this case, we want the PDF of ln (rho)] ! rhologmin = -15. rhologmax = 12. where (rhogrid.gt.0.) rhogrid = log(rhogrid) end where !itransx = 6 ! !--calculate the PDF ! call pdf_calc(ntot,rhogrid,rhologmin,rhologmax,nbins,xbin,& pdf,pdfmin,pdfmax,usefixedbins,volweighted,ierr) ! !--calculate the mean and variance in ln(rho) ! call mean_variance(rhogrid,ntot,smean,svar) print*,' rho mean,var = ',rhomean,rhovar print*,'ln(rho) mean,var = ',smean,svar print*,' svar,rhovar = ',-2.*smean,exp(svar) - 1. ! !--check the routine ! svar = 0. do i=1,ntot svar = svar + (rhogrid(i) - smean)**2 enddo svar = svar/real(ntot-1) print*,'svar = ',svar ! !--write the PDF to file ! if (ierr.eq.0) then call pdf_write(nbins,xbin,pdf,'lnrhogrid',volweighted,trim(filename),trim(tagline)) endif enddo ! !--clean up/deallocate memory ! if (allocated(rhogrid)) deallocate(rhogrid) end program grid2pdf splash/tests/test_interpolate3D.f90000755 000766 000000 00000026634 13261626263 020240 0ustar00dpricewheel000000 000000 ! !--unit test for interpolation routines ! program test_interpolation use projections3D use xsections3D implicit none integer, parameter :: idimx = 100 integer, parameter :: idim = idimx**3 integer, parameter :: ipixx = 1000, ipixy = 1000 integer :: npart,npartx,nparty,npartz integer :: npixx, npixy,i real, parameter :: errtol = 1.e-7 real, dimension(idim) :: x,y,z,pmass,h,rho real, dimension(idim) :: dat,weight integer, dimension(idim) :: itype real, dimension(ipixx,ipixy) :: datpix real, dimension(0:maxcoltable) :: q,w real :: xmin,xmax,ymin,ymax,zmin,zmax real :: columndens,dxpix,err,dens,datmax real :: trans(6) logical :: ifastrender,normalise xmin = -0.5 xmax = 0.5 ymin = -0.5 ymax = 0.5 zmin = -0.5 zmax = 0.5 itype = 0 ifastrender = .true. print*,'accelerated rendering = ',ifastrender call pgopen('?') ! call pgenv(xmin,xmax,ymin,ymax,0,0) ! call pglabel('x','y',' ') ! call pgpt(npart,x,y,1) ! call pgenv(xmin,xmax,ymin,ymax,0,0) ! call pglabel('y','z',' ') ! call pgpt(npart,y,z,1) ! call pgenv(xmin,xmax,ymin,ymax,0,0) ! call pglabel('x','z',' ') ! call pgpt(npart,x,z,1) ! !--setup integrated kernel table ! call setup_integratedkernel ! !--check value of the integration at q=zero (can do this analytically) ! if (abs(coltable(0)-1.5/3.1415926536).lt.errtol) then print*,'CENTRAL KERNEL TABLE OK' else print*,'coltable(0) = ',coltable(0),' should be ',2.*0.75/3.1415926536 print*,'error = ',abs(coltable(0)-1.5/3.1415926536) print*,'ERROR: CENTRAL INTEGRATED KERNEL VALUE WRONG' endif ! !--plot integrated kernel ! call pgenv(0.,2.,0.,coltable(0)*1.1,0,0) call pglabel('r','int W',' ') print*,'radkernel = ',radkernel do i=0,50 q(i) = i*radkernel/50. !print*,q(i) w(i) = wfromtable(q(i)*q(i)) enddo call pgsci(3) call pgline(50,q,w) ! do i=1,maxcoltable ! q(i) = sqrt((i-1)*radkernel*radkernel*dmaxcoltable) ! enddo ! call pgsci(2) ! call pgline(maxcoltable,q,coltable) call pgsci(1) !call pgpage ! !--setup one particle ! print*,'SINGLE PARTICLE TEST' npart = 1 npixx = 10 npixy = 10 x(1) = 0.5*(xmin + xmax) y(1) = 0.5*(ymin + ymax) z(1) = 0.5*(zmin + zmax) rho(1) = 1.0 pmass(1) = 2.0 h(1) = 0.35*xmax weight(1) = 1./1.5**3 dat(1) = rho(1) dxpix = (xmax-xmin)/real(npixx) normalise = .false. datpix = 0. call interpolate3D_projection(x(1:npart),y(1:npart),z(1:npart),h(1:npart), & weight(1:npart),dat(1:npart),itype(1:npart),npart,xmin,ymin, & datpix(1:npixx,1:npixy),npixx,npixy,dxpix,dxpix,normalise,0.,0.,.false.) call pgenv(xmin,xmax,ymin,ymax,1,0) trans = 0. trans(1) = xmin - 0.5*dxpix trans(2) = dxpix trans(4) = ymin - 0.5*dxpix trans(6) = dxpix datmax = maxval(datpix(1:npixx,1:npixy)) print*,'max datpix = ',datmax,maxloc(datpix(1:npixx,1:npixy)) if (abs(datmax-0.035367373).gt.errtol) then print*,'FAILED: central maximum wrong, error = ',abs(datmax-0.035367373) else print*,'OK: central maximum seems fine' endif call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,0.0,datmax,trans) print*,'TEST WITH ACCELERATION' call interpolate3D_projection(x(1:npart),y(1:npart),z(1:npart),h(1:npart), & weight(1:npart),dat(1:npart),itype(1:npart),npart,xmin,ymin, & datpix(1:npixx,1:npixy),npixx,npixy,dxpix,dxpix,normalise,0.,0.,.true.) call pgenv(xmin,xmax,ymin,ymax,1,0) trans = 0. trans(1) = xmin - 0.5*dxpix trans(2) = dxpix trans(4) = ymin - 0.5*dxpix trans(6) = dxpix datmax = maxval(datpix(1:npixx,1:npixy)) print*,'max datpix = ',datmax,maxloc(datpix(1:npixx,1:npixy)) if (abs(datmax-0.035367373).gt.errtol) then print*,'FAILED: central maximum wrong, error = ',abs(datmax-0.035367373) else print*,'OK: central maximum seems fine' endif call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,0.0,datmax,trans) ! !--setup two overlapping particles ! print*,'TWO PARTICLE TEST' npart = 2 npixx = 1000 npixy = 1000 x(1) = -0.25 x(2) = 0.25 y(2) = 0.5*(ymin + ymax) z(2) = 0.5*(zmin + zmax) rho(2) = 1.0 pmass(2) = 2.0 h(1:2) = 0.5*xmax weight(2) = 1./1.5**3 dat(2) = rho(2) dxpix = (xmax-xmin)/real(npixx) call interpolate3D_projection(x(1:npart),y(1:npart),z(1:npart),h(1:npart), & weight(1:npart),dat(1:npart),itype(1:npart),npart,xmin,ymin, & datpix(1:npixx,1:npixy),npixx,npixy,dxpix,dxpix,normalise,0.,0.,ifastrender) call pgenv(xmin,xmax,ymin,ymax,1,0) trans = 0. trans(1) = xmin - 0.5*dxpix trans(2) = dxpix trans(4) = ymin - 0.5*dxpix trans(6) = dxpix datmax = maxval(datpix(1:npixx,1:npixy)) print*,'max datpix = ',datmax,maxloc(datpix(1:npixx,1:npixy)) !!print*,'datpix = ',datpix(1:npixx,1:npixy) call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,0.0,datmax,trans) ! !--set up a cubic lattice of particles ! print*,'NORMAL LATTICE TEST' npartx = 50 nparty = 50 npartz = 50 npart = npartx*nparty*npartz npixx = 500 npixy = 500 dxpix = (xmax-xmin)/real(npixx) call setgrid(npartx,nparty,npartz,x,y,z,pmass,rho,h,weight,xmin,xmax,ymin,ymax,zmin,zmax) ! !--now call interpolation routine to pixels ! call interpolate3D_projection(x(1:npart),y(1:npart),z(1:npart),h(1:npart), & weight(1:npart),dat(1:npart),itype(1:npart),npart,xmin,ymin, & datpix(1:npixx,1:npixy),npixx,npixy,dxpix,dxpix,normalise,0.,0.,ifastrender) ! !--check output ! dens = rho(1) columndens = dens*(zmax-zmin) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,columndens,err) print "(70('-'))" print*,'average error in column density interpolation = ',err if (err.gt.0.05) then print*,'FAILED: average error > usual' else print*,'OK: average error same as usual' endif call pgenv(xmin,xmax,ymin,ymax,0,0) ! call pgpixl(datpix,ipixx,ipixy,1,npixx,1,npixy,xmin,xmax,ymin,ymax) trans = 0. trans(1) = xmin - 0.5*dxpix trans(2) = dxpix trans(4) = ymin - 0.5*dxpix trans(6) = dxpix call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,0.0,1.0,trans) ! !--NORMALISED VERSION OF ABOVE ! normalise = .true. call interpolate3D_projection(x(1:npart),y(1:npart),z(1:npart),h(1:npart), & weight(1:npart),dat(1:npart),itype(1:npart),npart,xmin,ymin, & datpix(1:npixx,1:npixy),npixx,npixy,dxpix,dxpix,normalise,0.,0.,ifastrender) ! !--check output ! dens = rho(1) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,dens,err) print "(70('-'))" print*,'average error in interpolation = ',err print*,' dens = ',dens,' datpix = ',datpix(1:10,1:10) if (err.gt.0.05) then print*,'FAILED: average error > usual' else print*,'OK: average error same as usual' endif call pgenv(xmin,xmax,ymin,ymax,0,0) ! call pgpixl(datpix,ipixx,ipixy,1,npixx,1,npixy,xmin,xmax,ymin,ymax) trans = 0. trans(1) = xmin - 0.5*dxpix trans(2) = dxpix trans(4) = ymin - 0.5*dxpix trans(6) = dxpix call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,0.0,1.0,trans) ! !--take cross section at midplane and check density ! print "(70('-'))" call interpolate3D_fastxsec(x(1:npart),y(1:npart),z(1:npart), & h(1:npart),weight(1:npart),dat(1:npart),itype(1:npart),npart,& xmin,ymin,0.0,datpix(1:npixx,1:npixy),npixx,npixy,dxpix,.false.) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,dens,err) print*,'average error in non-normalised xsec interpolation = ',err print "(70('-'))" call pgenv(xmin,xmax,ymin,ymax,0,0) ! call pgpixl(datpix,ipixx,ipixy,1,npixx,1,npixy,xmin,xmax,ymin,ymax) ! trans = 0. ! trans(1) = xmin - 0.5*dxpix ! trans(2) = dxpix ! trans(4) = ymin - 0.5*dxpix ! trans(6) = dxpix call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,0.0,1.0,trans) ! call pgend ! !--take normalised cross section at midplane and check density ! call interpolate3D_fastxsec(x(1:npart),y(1:npart),z(1:npart), & h(1:npart),weight(1:npart),dat(1:npart),itype(1:npart),npart,& xmin,ymin,0.0,datpix(1:npixx,1:npixy),npixx,npixy,dxpix,.true.) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,dens,err) print*,'average error in normalised xsec interpolation = ',err call pgenv(xmin,xmax,ymin,ymax,0,0) call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,0.0,1.0,trans) print*,'closing PGPLOT' call pgend print "(70('-'))" print*,'SPEED CHECKS...' normalise = .true. npixx = 1 npixy = 1 npartx = idimx nparty = idimx npartz = idimx npart = npartx*nparty*npartz call setgrid(npartx,nparty,npartz,x,y,z,pmass,rho,h,weight,xmin,xmax,ymin,ymax,zmin,zmax) dxpix = (xmax-xmin)/real(npixx) call interpolate3D_projection(x(1:npart),y(1:npart),z(1:npart),h(1:npart), & weight(1:npart),dat(1:npart),itype(1:npart),npart,xmin,ymin, & datpix(1:npixx,1:npixy),npixx,npixy,dxpix,dxpix,normalise,0.,0.,ifastrender) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,columndens,err) print*,'average error in projection = ',err npixx = 1000 npixy = 1000 npartx = 2 nparty = 2 npartz = 2 npart = npartx*nparty*npartz call setgrid(npartx,nparty,npartz,x,y,z,pmass,rho,h,weight,xmin,xmax,ymin,ymax,zmin,zmax) dxpix = (xmax-xmin)/real(npixx) call interpolate3D_projection(x(1:npart),y(1:npart),z(1:npart),h(1:npart), & weight(1:npart),dat(1:npart),itype(1:npart),npart,xmin,ymin, & datpix(1:npixx,1:npixy),npixx,npixy,dxpix,dxpix,normalise,0.,0.,ifastrender) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,columndens,err) print*,'average error in projection = ',err contains subroutine setgrid(npartx,nparty,npartz,x,y,z,pmass,rho,h,weight,xmin,xmax,ymin,ymax,zmin,zmax) implicit none integer , intent(in) :: npartx,nparty,npartz real, dimension(:), intent(out) :: x,y,z,pmass,rho,h,weight real, intent(in) :: xmin,xmax,ymin,ymax,zmin,zmax integer :: ipart,k,j,i real :: dx,dy,dz,ypos,zpos real :: totmass,massp,vol,dens,h0 dz = (zmax-zmin)/real(npartz - 1) dy = (ymax-ymin)/real(nparty - 1) dx = (xmax-xmin)/real(npartx - 1) ipart = 0 do k=1,npartz zpos = zmin + (k-1)*dz do j=1,nparty ypos = ymin + (j-1)*dy do i=1,npartx ipart = ipart + 1 x(ipart) = xmin + (i-1)*dx y(ipart) = ypos z(ipart) = zpos ! print*,ipart,'x,y,z=',x(ipart),y(ipart),z(ipart) enddo enddo enddo npart = npartx*nparty*npartz ! !--set other properties ! totmass = 3.1415926536 massp = totmass/real(npart) vol = (xmax-xmin)*(ymax-ymin)*(zmax-zmin) dens = totmass/vol h0 = 1.5*(massp/dens)**(1./3) print*,' testing ',npart,' particles in a cube configuration' print*,' dx = ',dx,' dy = ',dy,' dz = ',dz print*,' mass = ',massp,' dens = ',dens,' h = ',h0 print*,' approx density = ',massp/(dx*dy*dz) do i = 1,npart pmass(i) = massp rho(i) = dens h(i) = h0 dat(i) = rho(i) weight(i) = pmass(i)/(rho(i)*h(i)**3) enddo end subroutine setgrid subroutine geterr(datpix,npixx,npixy,datexact,err) implicit none integer, intent(in) :: npixx,npixy real, dimension(:,:), intent(in) :: datpix real, intent(in) :: datexact real, intent(out) :: err integer :: icalc,j,i real :: erri err = 0. icalc = 0 do j=2,npixy-1 do i=2,npixx-1 icalc = icalc + 1 erri = abs(datpix(i,j)-datexact)/datexact err = err + erri !if (erri.gt.0.05) print*,i,j,' xsec dens = ',datpix(i,j),' should be ',dens enddo enddo if (icalc.le.0) then print*,'cannot calculate error => npix too small' err = -1.0 else err = err/real(icalc) endif end subroutine geterr end program test_interpolation splash/tests/test_fieldlines.f90000644 000766 000000 00000016575 13261626263 017641 0ustar00dpricewheel000000 000000 ! !--unit test for field line plotting routine ! program test_fieldlines use fieldlines, only:streamlines use render, only:render_pix implicit none integer, parameter :: ipixx = 1000, ipixy = 1000, nc = 100 integer :: npixx, npixy,i,j real, parameter :: errtol = 1.e-7 real, dimension(ipixx,ipixy) :: datpix,vecpixx,vecpixy,datpix1,datpix2,vecpixx1,vecpixy1 real :: xmin,xmax,ymin,ymax,zmin,zmax,dxpix real :: xi,yi,datmin,datmax,err,dcont real, parameter :: pi=3.1415926536 real, dimension(6) :: trans real, dimension(nc) :: levels xmin = -0.5 xmax = 0.5 ymin = -0.5 ymax = 0.5 call pgopen('/xw') print "(70('-'))" print*,'ORSZAG-TANG TEST' npixx = 400 npixy = 400 dxpix = (xmax-xmin)/real(npixx) do j = 1,npixy yi = ymin + (j-0.5)*dxpix if (j.le.10) print*,'y = ',yi do i = 1,npixx xi = xmin + (i-0.5)*dxpix vecpixx(i,j) = func_vecx(xi,yi) vecpixy(i,j) = func_vecy(xi,yi) datpix1(i,j) = func_stream(xi,yi) enddo enddo call streamlines(vecpixx(1:npixx,1:npixy),vecpixy(1:npixx,1:npixy), & datpix(1:npixx,1:npixy),npixx,npixy,dxpix) do j = 1,npixy,2 yi = ymin + (j-0.5)*2.*dxpix do i = 1,npixx,2 xi = xmin + (i-0.5)*2.*dxpix vecpixx1((i+1)/2,(j+1)/2) = func_vecx(xi,yi) !vecpixx(i,j) vecpixy1((i+1)/2,(j+1)/2) = func_vecy(xi,yi) !vecpixy(i,j) enddo enddo npixx = npixx/2 npixy = npixy/2 dxpix = (xmax-xmin)/real(npixx) call streamlines(vecpixx1(1:npixx,1:npixy),vecpixy1(1:npixx,1:npixy), & datpix2(1:npixx,1:npixy),npixx,npixy,dxpix) npixx = npixx*2 npixy = npixy*2 dxpix = (xmax-xmin)/real(npixx) !do j=1,npixy,2 ! do i=1,npixx,2 ! datpix(i,j) = (4.*datpix(i,j) - datpix2((i+1)/2,(j+1)/2))/3. ! enddo !enddo call pgenv(xmin,xmax,ymin,ymax,1,0) trans = 0. trans(1) = xmin - 0.5*dxpix trans(2) = dxpix trans(4) = ymin - 0.5*dxpix trans(6) = dxpix datmax = maxval(datpix(1:npixx,1:npixy)) datmin = minval(datpix(1:npixx,1:npixy)) print*,'min,max datpix = ',datmin,datmax datpix(1:npixx,1:npixy) = datpix(1:npixx,1:npixy) - 0.5*(datmax + datmin) datmax = maxval(datpix(1:npixx,1:npixy)) datmin = minval(datpix(1:npixx,1:npixy)) print*,'min,max datpix = ',datmin,datmax print*,'plotting integrated array...' ! call render_pix(datpix(1:npixx,1:npixy),datmin,datmax,'crap',npixx,npixy, & ! xmin,ymin,dxpix,0,.true.,.false.,30,.false.) call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,datmin,datmax,trans) ! !--set contour levels ! dcont = (datmax-datmin)/real(nc+1) ! even contour levels do i=1,nc levels(i) = datmin + real(i)*dcont enddo ! !--plot contours (use pgcont if pgcons causes trouble) ! call pgcons(datpix(1:npixx,1:npixy),npixx,npixy,1,npixx,1,npixy,levels(1:nc),nc,trans) ! call pgenv(xmin,xmax,ymin,ymax,1,0) datmax = maxval(datpix1(1:npixx,1:npixy)) datmin = minval(datpix1(1:npixx,1:npixy)) print*,'min,max datpix1 = ',datmin,datmax !print*,' plotting exact solution ...' ! call pgimag(datpix1,ipixx,ipixy,1,npixx,1,npixy,datmin,datmax,trans) !call pgcons(datpix1(1:npixx,1:npixy),npixx,npixy,1,npixx,1,npixy,levels(1:nc),nc,trans) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,datpix1(1:npixx,1:npixy),err) print*,'average error in stream line calculation = ',err if (npixx.eq.400 .and. npixy.eq.400) then if (err < 0.00019) then print*,'PASSED: error within limits' else print*,'FAILED: error too large!' endif else print*,'setup different to usual one' endif !--------------------- ! dipole field test !---------------------- print "(70('-'))" print*,'DIPOLE TEST' npixx = 400 npixy = 400 dxpix = (xmax-xmin)/real(npixx) do j = 1,npixy yi = ymin + (j-0.5)*dxpix do i = 1,npixx xi = xmin + (i-0.5)*dxpix vecpixx(i,j) = func_vecx_dipole(xi,yi) vecpixy(i,j) = func_vecy_dipole(xi,yi) datpix1(i,j) = func_stream_dipole(xi,yi) enddo enddo call streamlines(vecpixx(1:npixx,1:npixy),vecpixy(1:npixx,1:npixy), & datpix(1:npixx,1:npixy),npixx,npixy,dxpix) call pgenv(xmin,xmax,ymin,ymax,1,0) trans = 0. trans(1) = xmin - 0.5*dxpix trans(2) = dxpix trans(4) = ymin - 0.5*dxpix trans(6) = dxpix datmax = maxval(datpix(1:npixx,1:npixy)) datmin = minval(datpix(1:npixx,1:npixy)) print*,'min,max datpix = ',datmin,datmax datpix(1:npixx,1:npixy) = datpix(1:npixx,1:npixy) - 0.5*(datmax + datmin) datmax = maxval(datpix(1:npixx,1:npixy)) datmin = minval(datpix(1:npixx,1:npixy)) print*,'min,max datpix = ',datmin,datmax ! call pgimag(datpix,ipixx,ipixy,1,npixx,1,npixy,datmin,datmax,trans) ! !--set contour levels ! dcont = (datmax-datmin)/real(nc+1) ! even contour levels do i=1,nc levels(i) = datmin + real(i)*dcont enddo ! !--plot contours (use pgcont if pgcons causes trouble) ! call pgcons(datpix(1:npixx,1:npixy),npixx,npixy,1,npixx,1,npixy,levels(1:nc),nc,trans) ! call pgenv(xmin,xmax,ymin,ymax,1,0) datmax = maxval(datpix1(1:npixx,1:npixy)) datmin = minval(datpix1(1:npixx,1:npixy)) print*,'min,max datpix1 = ',datmin,datmax ! call pgimag(datpix1,ipixx,ipixy,1,npixx,1,npixy,datmin,datmax,trans) ! call pgcons(datpix1(1:npixx,1:npixy),npixx,npixy,1,npixx,1,npixy,levels(1:nc),nc,trans) call geterr(datpix(1:npixx,1:npixy),npixx,npixy,datpix1(1:npixx,1:npixy),err) print*,'average error in stream line calculation = ',err if (npixx.eq.400 .and. npixy.eq.400) then if (err < 0.00019) then print*,'PASSED: error within limits' else print*,'FAILED: error too large!' endif else print*,'setup different to usual one' endif call pgend print "(70('-'))" contains real function func_vecx(xi,yi) implicit none real :: xi,yi func_vecx = -sin(2.*pi*yi) end function func_vecx real function func_vecy(xi,yi) implicit none real :: xi,yi func_vecy = sin(4.*pi*xi) end function func_vecy real function func_stream(xi,yi) implicit none real :: xi,yi func_stream = 0.5/pi*(cos(2.*pi*yi) + 0.5*cos(4.*pi*xi)) end function func_stream !---------------- ! dipole !---------------- real function func_vecx_dipole(xi,yi) implicit none real :: xi,yi real, parameter :: Bdipole = 1.0, Rdipole = 0.3, eps=0.3 func_vecx_dipole = 3.*Bdipole*Rdipole**3*(Rdipole + xi)*yi/ & sqrt((Rdipole + xi)**2 + yi**2 + (eps*Rdipole)**2)**5 end function func_vecx_dipole real function func_vecy_dipole(xi,yi) implicit none real :: xi,yi real, parameter :: Bdipole = 1.0, Rdipole = 0.3, eps=0.3 func_vecy_dipole = Bdipole*Rdipole**3/ & sqrt((Rdipole + xi)**2 + yi**2 + (eps*Rdipole)**2)**3 end function func_vecy_dipole real function func_stream_dipole(xi,yi) implicit none real :: xi,yi real, parameter :: Bdipole = 1.0, Rdipole = 0.3, eps=0.3 func_stream_dipole = -Bdipole*Rdipole**3*(Rdipole + xi)/ & sqrt((Rdipole + xi)**2 + yi**2 + (eps*Rdipole)**2)**3 end function func_stream_dipole subroutine geterr(datpix,npixx,npixy,datexact,err) implicit none integer, intent(in) :: npixx,npixy real, dimension(:,:), intent(in) :: datpix real, dimension(:,:), intent(in) :: datexact real, intent(out) :: err integer :: icalc,j,i real :: errij err = 0. icalc = 0 do j=1,npixy do i=1,npixx icalc = icalc + 1 errij = abs(datpix(i,j)-datexact(i,j)) err = err + errij enddo enddo if (icalc.le.0) then print*,'cannot calculate error => npix too small' err = -1.0 else err = err/(icalc*maxval(datexact)) endif end subroutine geterr end program test_fieldlines splash/bin/.keep000644 000766 000000 00000000000 13261626263 014447 0ustar00dpricewheel000000 000000