From 189da15810deabd739d7c11c6e95fea55739fe60 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sat, 1 Aug 2020 15:13:49 +0200 Subject: Initial import from archive --- .gitignore | 22 + AUTHORS | 0 COPYING | 1 + ChangeLog | 0 INSTALL | 236 +++++++ Makefile.am | 10 + NEWS | 0 README | 0 config.h.in | 131 ++++ configure.ac | 94 +++ data/Makefile.am | 3 + data/displaywindow.ui | 91 +++ data/elements | 19 + data/test.hkl | 7 + extra/synth2d-manual.odt | Bin 0 -> 31679 bytes extra/test.hkl | 4 + src/Makefile.am | 14 + src/amplitude-r.c | 86 +++ src/amplitude-r.h | 19 + src/anneal.h | 20 + src/argand.c | 118 ++++ src/argand.h | 26 + src/cdm.c | 1202 ++++++++++++++++++++++++++++++++++ src/cdm.h | 77 +++ src/cflip.c | 910 ++++++++++++++++++++++++++ src/cflip.h | 28 + src/clean.c | 544 ++++++++++++++++ src/clean.h | 27 + src/colwheel.c | 153 +++++ src/colwheel.h | 26 + src/contourise.c | 441 +++++++++++++ src/contourise.h | 21 + src/correspondence.c | 73 +++ src/correspondence.h | 20 + src/data.c | 162 +++++ src/data.h | 34 + src/displaywindow.c | 1609 ++++++++++++++++++++++++++++++++++++++++++++++ src/displaywindow.h | 76 +++ src/dpsynth.c | 291 +++++++++ src/dpsynth.h | 37 ++ src/elements.c | 86 +++ src/elements.h | 33 + src/elser.c | 811 +++++++++++++++++++++++ src/elser.h | 25 + src/geometry.c | 324 ++++++++++ src/geometry.h | 29 + src/gsf.c | 669 +++++++++++++++++++ src/gsf.h | 30 + src/gtk-symmetry.c | 243 +++++++ src/gtk-symmetry.h | 50 ++ src/gtk-valuegraph.c | 234 +++++++ src/gtk-valuegraph.h | 41 ++ src/luzzatti.c | 91 +++ src/luzzatti.h | 21 + src/main.c | 471 ++++++++++++++ src/main.h | 69 ++ src/model-display.c | 411 ++++++++++++ src/model-display.h | 27 + src/model-editor.c | 831 ++++++++++++++++++++++++ src/model-editor.h | 69 ++ src/model.c | 523 +++++++++++++++ src/model.h | 79 +++ src/multislice.c | 126 ++++ src/multislice.h | 25 + src/normalise.c | 721 +++++++++++++++++++++ src/normalise.h | 32 + src/options.c | 191 ++++++ src/options.h | 23 + src/png-file.c | 125 ++++ src/png-file.h | 23 + src/refine-brent.c | 138 ++++ src/refine-brent.h | 26 + src/refine-cgrad.c | 339 ++++++++++ src/refine-cgrad.h | 26 + src/refine-lmder.c | 439 +++++++++++++ src/refine-lmder.h | 26 + src/refine-lsq.c | 320 +++++++++ src/refine-lsq.h | 26 + src/refine-rns.c | 122 ++++ src/refine-rns.h | 26 + src/refine.c | 359 +++++++++++ src/refine.h | 102 +++ src/reflist.c | 287 +++++++++ src/reflist.h | 83 +++ src/renderer.c | 205 ++++++ src/renderer.h | 31 + src/statistics.c | 223 +++++++ src/statistics.h | 31 + src/superlattice.c | 142 ++++ src/superlattice.h | 23 + src/symmetry.c | 464 +++++++++++++ src/symmetry.h | 89 +++ src/testmain.c | 68 ++ 93 files changed, 16660 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 config.h.in create mode 100644 configure.ac create mode 100644 data/Makefile.am create mode 100644 data/displaywindow.ui create mode 100644 data/elements create mode 100644 data/test.hkl create mode 100644 extra/synth2d-manual.odt create mode 100644 extra/test.hkl create mode 100644 src/Makefile.am create mode 100644 src/amplitude-r.c create mode 100644 src/amplitude-r.h create mode 100644 src/anneal.h create mode 100644 src/argand.c create mode 100644 src/argand.h create mode 100644 src/cdm.c create mode 100644 src/cdm.h create mode 100644 src/cflip.c create mode 100644 src/cflip.h create mode 100644 src/clean.c create mode 100644 src/clean.h create mode 100644 src/colwheel.c create mode 100644 src/colwheel.h create mode 100644 src/contourise.c create mode 100644 src/contourise.h create mode 100644 src/correspondence.c create mode 100644 src/correspondence.h create mode 100644 src/data.c create mode 100644 src/data.h create mode 100644 src/displaywindow.c create mode 100644 src/displaywindow.h create mode 100644 src/dpsynth.c create mode 100644 src/dpsynth.h create mode 100644 src/elements.c create mode 100644 src/elements.h create mode 100644 src/elser.c create mode 100644 src/elser.h create mode 100644 src/geometry.c create mode 100644 src/geometry.h create mode 100644 src/gsf.c create mode 100644 src/gsf.h create mode 100644 src/gtk-symmetry.c create mode 100644 src/gtk-symmetry.h create mode 100644 src/gtk-valuegraph.c create mode 100644 src/gtk-valuegraph.h create mode 100644 src/luzzatti.c create mode 100644 src/luzzatti.h create mode 100644 src/main.c create mode 100644 src/main.h create mode 100644 src/model-display.c create mode 100644 src/model-display.h create mode 100644 src/model-editor.c create mode 100644 src/model-editor.h create mode 100644 src/model.c create mode 100644 src/model.h create mode 100644 src/multislice.c create mode 100644 src/multislice.h create mode 100644 src/normalise.c create mode 100644 src/normalise.h create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 src/png-file.c create mode 100644 src/png-file.h create mode 100644 src/refine-brent.c create mode 100644 src/refine-brent.h create mode 100644 src/refine-cgrad.c create mode 100644 src/refine-cgrad.h create mode 100644 src/refine-lmder.c create mode 100644 src/refine-lmder.h create mode 100644 src/refine-lsq.c create mode 100644 src/refine-lsq.h create mode 100644 src/refine-rns.c create mode 100644 src/refine-rns.h create mode 100644 src/refine.c create mode 100644 src/refine.h create mode 100644 src/reflist.c create mode 100644 src/reflist.h create mode 100644 src/renderer.c create mode 100644 src/renderer.h create mode 100644 src/statistics.c create mode 100644 src/statistics.h create mode 100644 src/superlattice.c create mode 100644 src/superlattice.h create mode 100644 src/symmetry.c create mode 100644 src/symmetry.h create mode 100644 src/testmain.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..053e2d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +src/*.o +configure +Makefile.in +Makefile +aclocal.m4 +config.h +config.log +config.status +depcomp +install-sh +missing +config.h.in +config.h.in~ +compile +src/.deps +src/Makefile +src/Makefile.in +src/synth2d +src/synth2dtest +stamp-h1 +autom4te.cache + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a459ee6 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +(c) 2006 Thomas White diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..56b077d --- /dev/null +++ b/INSTALL @@ -0,0 +1,236 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free +Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + +By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PREFIX', the package will +use PREFIX as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). Here is a another example: + + /bin/bash ./configure CONFIG_SHELL=/bin/bash + +Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent +configuration-related scripts to be executed by `/bin/bash'. + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..2cd8062 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,10 @@ +EXTRA_DIST = configure data/elements data/displaywindow.ui \ + src/argand.h src/contourise.h src/normalise.h src/displaywindow.h src/gsf.h src/cflip.h \ + src/data.h src/png-file.h src/symmetry.h src/anneal.h src/cdm.h src/dpsynth.h \ + src/clean.h src/gtk-symmetry.h src/reflist.h src/geometry.h src/model.h \ + src/elements.h src/statistics.h src/refine.h src/multislice.h src/luzzatti.h src/correspondence.h \ + src/amplitude-r.h src/main.h src/superlattice.h src/elser.h src/gtk-valuegraph.h \ + src/colwheel.h src/model-editor.h src/model-display.h src/refine-rns.h src/refine-lmder.h \ + src/refine-lsq.h src/refine-cgrad.h src/refine-brent.h src/options.h src/renderer.h +SUBDIRS = src data + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..4805f63 --- /dev/null +++ b/config.h.in @@ -0,0 +1,131 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `bzero' function. */ +#undef HAVE_BZERO + +/* Define to 1 if Cairo is available */ +#undef HAVE_CAIRO + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `floor' function. */ +#undef HAVE_FLOOR + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if GTK+ is version 2.10.0 or higher */ +#undef HAVE_GTK_TEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `fftw3' library (-lfftw3). */ +#undef HAVE_LIBFFTW3 + +/* Define to 1 if you have the `gsl' library (-lgsl). */ +#undef HAVE_LIBGSL + +/* Define to 1 if you have the `gslcblas' library (-lgslcblas). */ +#undef HAVE_LIBGSLCBLAS + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `pow' function. */ +#undef HAVE_POW + +/* Define to 1 if you have the `rint' function. */ +#undef HAVE_RINT + +/* Define to 1 if you have the `sqrt' function. */ +#undef HAVE_SQRT + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0383f88 --- /dev/null +++ b/configure.ac @@ -0,0 +1,94 @@ +AC_INIT(synth2d, 1.1.1, taw27@cam.ac.uk) +VERSION=AC_PACKAGE_VERSION + +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE(synth2d, "$VERSION") + +AC_PROG_CC +AC_PROG_AWK +AC_PROG_INSTALL +AC_PROG_LN_S + +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h]) +AC_C_CONST +AC_FUNC_MALLOC +AC_FUNC_FORK +AC_HEADER_SYS_WAIT +AC_TYPE_SIGNAL +AC_TYPE_PID_T +AC_CHECK_FUNCS([strdup]) +AC_CHECK_FUNCS([floor]) +AC_CHECK_FUNCS([pow]) +AC_CHECK_FUNCS([rint]) +AC_CHECK_FUNCS([sqrt]) +AC_CHECK_FUNCS([bzero]) +AC_TYPE_SIZE_T + +if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) +fi +if test "$PKG_CONFIG" = "no" ; then + echo "*** pkg-config not found" +fi + +AM_PATH_GTK_2_0(2.0.0,,AC_MSG_ERROR([ +*** GTK+ 2.6.0 or above is required by Synth2D but no GTK+2 was found. +*** Please make sure you have the GTK+ +*** development files installed. The latest version of GTK+ is +*** always available at http://www.gtk.org/.])) + +AC_MSG_CHECKING([GTK version]) +if $PKG_CONFIG --atleast-version 2.10.0 gtk+-2.0 ; then + AC_DEFINE([HAVE_GTK_TEN], [1], [Define to 1 if GTK+ is version 2.10.0 or higher]) + AC_MSG_RESULT([2.10.0 or higher - good]) +else + AC_MSG_RESULT([Lower than 2.10.0 - might be usable if newer than 2.6.0 - let's see...]) + AM_PATH_GTK_2_0(2.6.0,,AC_MSG_ERROR([ +*** GTK+2 was found but the version number was too low. +*** GTK+ 2.6.0 or above is required by Synth2D. Please make sure you have the GTK+ +*** development files installed. The latest version of GTK+ is +*** always available at http://www.gtk.org/.])) +fi + +AC_MSG_CHECKING([Cairo version 1.2.0]) +if $PKG_CONFIG --atleast-version 1.2.0 cairo ; then + CAIRO_VERSION=`$PKG_CONFIG --modversion cairo` + CAIRO_CFLAGS=`$PKG_CONFIG --cflags cairo` + CAIRO_LIBS=`$PKG_CONFIG --libs cairo` + AC_DEFINE([HAVE_CAIRO], [1], [Define to 1 if Cairo is available]) + AC_MSG_RESULT($CAIRO_VERSION) +else + AC_MSG_RESULT([not found. Some functions will be unavailable.]) +fi + +AM_PATH_GLIB_2_0(2.0.0,,AC_MSG_ERROR("can't find glib"),gthread) + +AC_CHECK_LIB([fftw3], [fftw_execute], [], AC_MSG_ERROR([ + *** fftw3 not found. fftw3 is required to build Synth2D.])) + +AC_CHECK_LIB(m, main) + +AC_CHECK_LIB(gslcblas,main, [], AC_MSG_ERROR([ + *** gslcblas not found. gslcblas is required to build Synth2D.])) + +AC_CHECK_LIB(gsl, main, [], AC_MSG_ERROR([ + *** gsl not found. gsl is required to build Synth2D.])) + +AC_MSG_CHECKING([libpng]) +if $PKG_CONFIG --atleast-version 1.2.0 libpng ; then + LIBPNG_VERSION=`$PKG_CONFIG --modversion libpng` + AC_MSG_RESULT($LIBPNG_VERSION) + LIBPNG_CFLAGS=`$PKG_CONFIG --cflags libpng` + LIBPNG_LIBS=`$PKG_CONFIG --libs libpng` +else + AC_MSG_RESULT([Lower than 1.2.0 or not found]) + AC_MSG_ERROR([ +*** libPNG is required to build Synth2D]) +fi + +CFLAGS="$CFLAGS $CAIRO_CFLAGS $LIBPNG_CLAGS" +LIBS="$LIBS $CAIRO_LIBS $LIBPNG_LIBS" + +AC_OUTPUT(Makefile src/Makefile data/Makefile) + diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..d98253d --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,3 @@ +synth2ddir = $(datadir)/synth2d +synth2d_DATA = displaywindow.ui elements + diff --git a/data/displaywindow.ui b/data/displaywindow.ui new file mode 100644 index 0000000..c09638f --- /dev/null +++ b/data/displaywindow.ui @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/elements b/data/elements new file mode 100644 index 0000000..ef672be --- /dev/null +++ b/data/elements @@ -0,0 +1,19 @@ +Hydrogen 1 0.375400 15.49460 0.140800 4.126100 0.021600 0.024600 -0.101200 46.88400 0.000000 +Carbon 6 0.730700 36.99510 1.195100 11.29660 0.456300 2.813900 0.124700 0.34560 0.000000 +Oxygen 8 0.454800 23.78031 0.917300 7.62200 0.471900 2.144000 0.138400 0.29590 0.000000 +Magnesium 12 2.268200 73.67040 1.807500 20.17490 0.839400 3.018100 0.289200 0.40460 0.000000 +Phosphorus 15 1.888200 44.87560 2.468500 13.53830 0.804600 2.642400 0.320400 0.36080 0.000000 +Silicon 14 2.129300 57.77480 2.533300 16.47560 0.834900 2.879600 0.321600 0.38600 0.000000 +Calcium 20 4.469600 99.52280 2.970800 22.69580 1.969600 4.195400 0.481800 0.41650 0.000000 +Titanium 22 3.565300 81.98210 2.818100 19.04860 1.893000 3.590400 0.482500 0.38550 0.000000 +Manganese 25 2.746700 67.78620 2.455600 15.67430 1.792300 2.999800 0.498400 0.35690 0.000000 +Iron 26 2.544000 64.42440 2.343400 14.88060 1.758800 2.853900 0.506200 0.35020 0.000000 +Cobalt 27 2.366800 61.43060 2.236100 14.17980 1.724300 2.724700 0.514800 0.34420 0.000000 +Copper 29 1.579200 62.94030 1.819700 12.45270 1.657600 2.504200 0.532300 0.33310 0.000000 +Gallium 31 2.320500 65.60190 2.485500 15.45770 1.687900 2.580600 0.599200 0.35100 0.000000 +Selenium 34 2.298000 38.82960 2.854100 11.53590 1.455500 2.146300 0.589500 0.31630 0.000000 +Indium 49 3.152800 66.64920 3.556500 14.44940 2.818000 2.975800 0.884200 0.33450 0.000000 +Tin 50 3.449500 59.10420 3.734900 14.17870 2.777900 2.854800 0.878600 0.32700 0.000000 +Germanium 32 2.446700 55.89300 2.701500 14.39300 1.617500 2.446100 0.600900 0.34150 0.000000 +Europium 63 6.266700 100.2983 4.844000 16.06620 3.202300 2.980300 1.200900 0.36740 0.000000 + diff --git a/data/test.hkl b/data/test.hkl new file mode 100644 index 0000000..6444a89 --- /dev/null +++ b/data/test.hkl @@ -0,0 +1,7 @@ +a 1.0 nm +b 1.0 nm +c 1.0 nm + 0 1 0 1.10000 + 1 0 0 1.20000 + 1 1 0 1.30000 + diff --git a/extra/synth2d-manual.odt b/extra/synth2d-manual.odt new file mode 100644 index 0000000..1d332e1 Binary files /dev/null and b/extra/synth2d-manual.odt differ diff --git a/extra/test.hkl b/extra/test.hkl new file mode 100644 index 0000000..6331d6c --- /dev/null +++ b/extra/test.hkl @@ -0,0 +1,4 @@ +a 1.0 +b 1.0 +angle 90.0 +1 0 0 100.0 diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..225e23a --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,14 @@ +bin_PROGRAMS = synth2d synth2dtest +synth2d_SOURCES = main.c displaywindow.c data.c png-file.c reflist.c normalise.c contourise.c dpsynth.c argand.c symmetry.c \ + gtk-symmetry.c gsf.c cflip.c cdm.c clean.c geometry.c model.c elements.c statistics.c refine.c multislice.c \ + luzzatti.c correspondence.c amplitude-r.c superlattice.c elser.c gtk-valuegraph.c colwheel.c \ + model-editor.c model-display.c refine-rns.c refine-lmder.c refine-lsq.c refine-cgrad.c \ + refine-brent.c options.c renderer.c +synth2d_LDADD = @LIBS@ @GTK_LIBS@ -lfftw3 -lm -lgsl -lgslcblas -lgthread-2.0 +AM_CFLAGS = -Wall -g @CFLAGS@ @GTK_CFLAGS@ +AM_CPPFLAGS = -DDATADIR=\""$(datadir)"\" + +synth2dtest_SOURCES = testmain.c reflist.c data.c +synth2dtest_LDADD = @LIBS@ @GTK_LIBS@ -lfftw3 -lm -lgsl -lgslcblas -lgthread-2.0 + + diff --git a/src/amplitude-r.c b/src/amplitude-r.c new file mode 100644 index 0000000..50f6e9c --- /dev/null +++ b/src/amplitude-r.c @@ -0,0 +1,86 @@ +/* + * amplitude-r.c + * + * Plot of R-factor against amplitude + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "main.h" +#include "statistics.h" +#include "displaywindow.h" +#include "model.h" + +static void amplituder_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void amplituder_show() { + + FILE *fh; + unsigned int i; + FILE *gnuplot; + double scale; + ReflectionList *reflections; + ReflectionList *model_reflections; + + reflections = main_reflist(); + model_reflections = model_calculate_f(reflections, NULL, 69); + + scale = stat_scale(reflections, model_reflections); + fh = fopen("synth2d-amplitude-r.dat", "w"); + + for ( i=1; in_reflections; i++ ) { + + double residual; + signed int h, k, l; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + residual = 100*fabs((reflections->refs[i].amplitude - scale*model_reflections->refs[i].amplitude)/reflections->refs[i].amplitude); + if ( !isinf(residual) ) { + fprintf(fh, "%f %f\n", reflections->refs[i].amplitude, residual); + } + + } + + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + amplituder_gpwrite(gnuplot, "set autoscale\n"); + amplituder_gpwrite(gnuplot, "unset log\n"); + amplituder_gpwrite(gnuplot, "unset label\n"); + amplituder_gpwrite(gnuplot, "set xtic auto\n"); + amplituder_gpwrite(gnuplot, "set ytic auto\n"); + amplituder_gpwrite(gnuplot, "set grid\n"); + amplituder_gpwrite(gnuplot, "set ylabel 'R (%)' font \"Helvetica,10\"\n"); + amplituder_gpwrite(gnuplot, "set xlabel '|Fobs|' font \"Helvetica,10\"\n"); + amplituder_gpwrite(gnuplot, "set title 'Amplitude-R Plot' font \"Helvetica,10\"\n"); + amplituder_gpwrite(gnuplot, "plot 'synth2d-amplitude-r.dat'\n"); + amplituder_gpwrite(gnuplot, "replot\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} diff --git a/src/amplitude-r.h b/src/amplitude-r.h new file mode 100644 index 0000000..53ce9c5 --- /dev/null +++ b/src/amplitude-r.h @@ -0,0 +1,19 @@ +/* + * ampltude-r.h + * + * Plot of R-factor against amplitude + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef AMPLITUDER_H + +extern void amplituder_show(void); + +#endif /* AMPLITUDER_H */ diff --git a/src/anneal.h b/src/anneal.h new file mode 100644 index 0000000..2efcfaa --- /dev/null +++ b/src/anneal.h @@ -0,0 +1,20 @@ +/* + * anneal.h + * + * Structure solution by simulated annealing + * + * (c) 2006 Thomas White + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ANNEAL_H +#define ANNEAL_H + +extern void anneal_dialog_open(void); + +#endif /* ANNEAL_H */ diff --git a/src/argand.c b/src/argand.c new file mode 100644 index 0000000..7e94398 --- /dev/null +++ b/src/argand.c @@ -0,0 +1,118 @@ +/* + * argand.c + * + * "Argand plane tracking" + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "reflist.h" +#include "displaywindow.h" + +GtkWidget *argand_pixmap_widget = NULL; +GdkPixbuf *argand_pixbuf = NULL; +GtkWidget *argand_window = NULL; + +static fftw_complex *argand_draw(ReflectionList *reflections, unsigned int width, unsigned int height) { + + unsigned int x, y, i; + double mag = 6; + double max = 0; + fftw_complex *data; + + data = malloc(height*width*sizeof(fftw_complex)); + + for ( y=0; yn_reflections; i++ ) { + double am = reflections->refs[i].amplitude; + if ( am > max ) max = am; + } + mag = (width/2) / max; + + for ( i=0; in_reflections; i++ ) { + + unsigned int xd, yd; + double real, imag; + double am = reflections->refs[i].amplitude; + double ph = reflections->refs[i].phase_known; + + real = am * cos(ph); + imag = am * sin(ph); + + xd = (width-1-((width/2)+(real*mag))); + yd = (height-1-((height/2)+(imag*mag))); + + data[yd + height*xd][0] = 1; + data[yd + height*xd][1] = 0; + + } + + data[(height/2) + height*(width/2)][0]=-1; + data[(height/2-1) + height*(width/2)][0]=-1; + data[(height/2+1) + height*(width/2)][0]=-1; + data[(height/2-2) + height*(width/2)][0]=-1; + data[(height/2+2) + height*(width/2)][0]=-1; + data[(height/2) + height*(width/2+1)][0]=-1; + data[(height/2) + height*(width/2-1)][0]=-1; + data[(height/2) + height*(width/2+2)][0]=-1; + data[(height/2) + height*(width/2-2)][0]=-1; + + return data; + +} + +void argand_update(ReflectionList *reflections) { + + fftw_complex *data; + + if ( argand_window == NULL ) return; + + if ( argand_pixbuf ) gdk_pixbuf_unref(argand_pixbuf); + if ( argand_pixmap_widget ) gtk_widget_destroy(argand_pixmap_widget); + + data = argand_draw(reflections, 321, 321); + argand_pixbuf = displaywindow_render_pixbuf(data, 1, 321, 321, M_PI_2, 1, 1); + argand_pixmap_widget = gtk_image_new_from_pixbuf(argand_pixbuf); + gtk_container_add(GTK_CONTAINER(argand_window), argand_pixmap_widget); + gtk_widget_show(argand_pixmap_widget); + free(data); + +} + +static void argand_close() { + argand_window = NULL; + argand_pixbuf = NULL; + argand_pixmap_widget = NULL; +} + +void argand_open(ReflectionList *reflections) { + + if ( argand_window ) return; + + argand_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(argand_window), "Argand Plane"); + g_signal_connect(GTK_OBJECT(argand_window), "destroy", G_CALLBACK(argand_close), NULL); + gtk_widget_show_all(argand_window); + + argand_update(reflections); + +} + diff --git a/src/argand.h b/src/argand.h new file mode 100644 index 0000000..56c932c --- /dev/null +++ b/src/argand.h @@ -0,0 +1,26 @@ +/* + * argand.h + * + * "Argand plane tracking" + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ARGAND_H +#define ARGAND_H + +#include "reflist.h" + +extern void argand_update(ReflectionList *reflections); +extern void argand_open(ReflectionList *reflections); + +#endif /* ARGAND_H */ + diff --git a/src/cdm.c b/src/cdm.c new file mode 100644 index 0000000..d0a1a91 --- /dev/null +++ b/src/cdm.c @@ -0,0 +1,1202 @@ +/* + * cdm.c + * + * "Conventional Direct Methods" - triplet and tangent stuff + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "symmetry.h" +#include "main.h" +#include "gtk-symmetry.h" +#include "data.h" + +typedef struct { + GtkWidget *emin; + GtkWidget *gmin; + GtkWidget *amin; + GtkWidget *symmetry; + GtkWidget *refine; + GtkWidget *auto_iterate; +} CDMWindow; + +static CDMContext *the_cdmctx = NULL; + +static void cdm_solutionwindow_open(CDMContext *cdm); + +/* ........................ The Maths ........................ */ + +static ReflectionList *cdm_findstrongreflections(ReflectionList *reflections_orig, Symmetry sym, double emin) { + + ReflectionList *strongest_reflections; + ReflectionList *reflections; +// unsigned int target; + unsigned int i; + + /* Initialise reflection list */ + reflections = reflist_copy(reflections_orig); + strongest_reflections = reflist_new_parent(reflections_orig); + + for ( i=0; in_reflections; i++ ) { + reflections->refs[i].parent_index = i; + } + + for ( i=1; in_reflections; i++ ) { + if ( (reflections->refs[i].amplitude >= emin) && (reflections->refs[i].h != 0) && (reflections->refs[i].k != 0) ) { + reflist_addref_parent(strongest_reflections, reflections->refs[i].h, reflections->refs[i].k, reflections->refs[i].l, + reflections->refs[i].parent_index); + } + } + +#if 0 + while ( reflections->n_reflections > 1 ) { + + unsigned int j; + double am_max = 0; + unsigned int j_max = 0; + + /* Find the next strongest reflection */ + for ( j=1; jn_reflections; j++ ) { + + double am = reflections->refs[j].amplitude; + signed int h, k, l; + + if ( am == 0 ) continue; /* Avoid Trouble if emin is negative... */ + + h = reflections->refs[j].h; + k = reflections->refs[j].k; + l = reflections->refs[j].l; + + if ( am >= am_max ) { + am_max = am; + j_max = j; + } + + } + + /* Add reflection to the new list, with a link back to the original copy */ + reflist_addref_parent(strongest_reflections, reflections->refs[j_max].h, reflections->refs[j_max].k, reflections->refs[j_max].l, + reflections->refs[j_max].parent_index); + reflist_delref(reflections, reflections->refs[j_max].h, reflections->refs[j_max].k, reflections->refs[j_max].l); + + } + + /* Chop off everything but the first 40% of the reflections */ + target = 4*strongest_reflections->n_reflections/10; + while ( strongest_reflections->n_reflections > target ) { + reflist_delref(strongest_reflections, strongest_reflections->refs[strongest_reflections->n_reflections-1].h, + strongest_reflections->refs[strongest_reflections->n_reflections-1].k, + strongest_reflections->refs[strongest_reflections->n_reflections-1].l); + } +#endif + + return strongest_reflections; + +} + +#if 0 +static TripletList *cdm_sort_triplets(TripletList *triplet_list) { + + TripletList *sorted_triplets; + TripletList *triplet_copy; + unsigned int target; + + sorted_triplets = malloc(sizeof(TripletList)); + + triplet_copy = malloc(sizeof(TripletList)); + memcpy(triplet_copy, triplet_list, sizeof(TripletList)); + + target = 9*triplet_copy->n_triplets/10; + while ( triplet_copy->n_triplets > target ) { + + unsigned int i; + double G_max = 0; + unsigned int i_max = 0; + + /* Locate most concentrated triplet */ + for ( i=0; in_triplets; i++ ) { + if ( triplet_copy->triplets[i].G > G_max ) { + i_max = i; + G_max = triplet_copy->triplets[i].G; + } + } + + /* Copy the best triplet into the new list */ + sorted_triplets->triplets[sorted_triplets->n_triplets].G = triplet_copy->triplets[i_max].G; + sorted_triplets->triplets[sorted_triplets->n_triplets].p.h = triplet_copy->triplets[i_max].p.h; + sorted_triplets->triplets[sorted_triplets->n_triplets].p.k = triplet_copy->triplets[i_max].p.k; + sorted_triplets->triplets[sorted_triplets->n_triplets].p.l = triplet_copy->triplets[i_max].p.l; + sorted_triplets->triplets[sorted_triplets->n_triplets].q.h = triplet_copy->triplets[i_max].q.h; + sorted_triplets->triplets[sorted_triplets->n_triplets].q.k = triplet_copy->triplets[i_max].q.k; + sorted_triplets->triplets[sorted_triplets->n_triplets].q.l = triplet_copy->triplets[i_max].q.l; + sorted_triplets->n_triplets++; + + /* Remove the best triplet from the original list */ + for ( i=i_max+1; in_triplets; i++ ) { + triplet_copy->triplets[i-1].G = triplet_copy->triplets[i].G; + triplet_copy->triplets[i-1].p.h = triplet_copy->triplets[i].p.h; + triplet_copy->triplets[i-1].p.k = triplet_copy->triplets[i].p.k; + triplet_copy->triplets[i-1].p.l = triplet_copy->triplets[i].p.l; + triplet_copy->triplets[i-1].q.h = triplet_copy->triplets[i].q.h; + triplet_copy->triplets[i-1].q.k = triplet_copy->triplets[i].q.k; + triplet_copy->triplets[i-1].q.l = triplet_copy->triplets[i].q.l; + } + triplet_copy->n_triplets--; + + } + free(triplet_copy); + + return sorted_triplets; + +} +#endif + +static TripletList *cdm_findtriplets(ReflectionList *strongest_reflections, Symmetry sym, double emin, double gmin) { + + TripletList *triplet_list; + unsigned int p; + unsigned int q; +// TripletList *sorted_triplets; + unsigned int n_total_triplets; + + /* Initialise triplet list */ + triplet_list = malloc(sizeof(TripletList)); + triplet_list->n_triplets = 0; + + /* Iterate doubly over the reflections */ + n_total_triplets = 0; + for ( p=1; pn_reflections; p++ ) { + for ( q=1; qn_reflections; q++ ) { + + double E1; + double E2; + double E3; + double G; + unsigned int r; + signed int ph, pk, pl, qh, qk, ql, rh, rk, rl; + + ph = strongest_reflections->refs[p].h; + pk = strongest_reflections->refs[p].k; + pl = strongest_reflections->refs[p].l; + qh = strongest_reflections->refs[q].h; + qk = strongest_reflections->refs[q].k; + ql = strongest_reflections->refs[q].l; + + assert(ph || pk || pl); + assert(qh || qk || ql); + + /* Determine the third reflection in the triplet */ + rh = -ph-qh; + rk = -pk-qk; + if ( pl != ql ) { + printf("CDM: pl != ql: %3i %3i\n", pl, ql); + continue; + } + //assert(pl == ql); /* This failure mode should have been weeded out ages ago */ + rl = pl; + + /* See if the third reflection is available */ + if ( (r = reflist_inlist(strongest_reflections, rh, rk, rl)) ) { + + /* Trust me on this ... */ + E1 = strongest_reflections->parent->refs[strongest_reflections->refs[p].parent_index].amplitude; + E2 = strongest_reflections->parent->refs[strongest_reflections->refs[q].parent_index].amplitude; + E3 = strongest_reflections->parent->refs[strongest_reflections->refs[r].parent_index].amplitude; + + G = E1 * E2 * E3; + + /* Convincing enough? */ + if ( G > gmin ) { + triplet_list->triplets[triplet_list->n_triplets].p.h = ph; + triplet_list->triplets[triplet_list->n_triplets].p.k = pk; + triplet_list->triplets[triplet_list->n_triplets].p.l = pl; + triplet_list->triplets[triplet_list->n_triplets].q.h = qh; + triplet_list->triplets[triplet_list->n_triplets].q.k = qk; + triplet_list->triplets[triplet_list->n_triplets].q.l = ql; + triplet_list->triplets[triplet_list->n_triplets].G = G; + triplet_list->n_triplets++; + if ( triplet_list->n_triplets == MAX_TRIPLETS ) { + printf("DM: Too many triplets!\n"); + return NULL; + } + //printf("DM: Found triplet %3i,%3i %3i : %3i,%3i %3i : %3i %3i,%3i G = %f\n", + // ph, pk, pl, qh, qk, ql, rh, rk, rl, G); + } + n_total_triplets++; + + } + + } + } + + printf("DM: Have %i concentrated triplets (%0.1f%%)\n", triplet_list->n_triplets, ((double)triplet_list->n_triplets/n_total_triplets)*100); + + //sorted_triplets = cdm_sort_triplets(triplet_list); + //free(triplet_list); + //return sorted_triplets; + + return triplet_list; + +} + +static ReflectionList *cdm_definebasis(ReflectionList *reflections_orig, TripletList *triplet_list, Symmetry sym, double min_alpha) { + + signed int j, jmin; + ReflectionList *convergence; + ReflectionList *basis; + ReflectionList *reflections; + + if ( reflections_orig->n_reflections <= 1 ) { + printf("DM: No reflections!\n"); + return NULL; + } + + /* Circumvent normal creation mechanism in order to enact nastiness */ + reflections = malloc(sizeof(ReflectionList)); + if ( !reflections ) return NULL; + /* Start with the entire list of reflections and work backwards */ + memcpy(reflections, reflections_orig, sizeof(ReflectionList)); + + //printf("DM: Finding maximally connected starting set among %i reflections\n", reflections->n_reflections); + + convergence = reflist_new_parent(reflections_orig->parent); + + while ( reflections->n_reflections > 1 ) { /* Remember 0 0 0 */ + + double alpha_min = 999999; + signed int h_min = 999999; + signed int k_min = 999999; + signed int l_min = 999999; + unsigned int i; + unsigned int i_min = 0; + unsigned int p; + ReflectionList *equivalents; + + for ( i=1; in_reflections; i++ ) { + + signed int h, k, l; + unsigned int t; + double alpha = 0; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + assert( (h != 0) || (k != 0) || (l != 0) ); + + for ( t=0; tn_triplets; t++ ) { + + signed int ph = triplet_list->triplets[t].p.h; + signed int pk = triplet_list->triplets[t].p.k; + + /* Does this triplet concern this reflection? */ + if ( (ph == h) && (pk == k) ) { + + signed int qh = triplet_list->triplets[t].q.h; + signed int qk = triplet_list->triplets[t].q.k; + signed int rh = -ph-qh; + signed int rk = -pk-qk; + double G = triplet_list->triplets[t].G; + + /* Are the other reflections in the triplet still in the list? */ + if ( reflist_inlist_2d(reflections, qh, qk) && reflist_inlist_2d(reflections, rh, rk) ) { + alpha += G * (gsl_sf_bessel_I1(G) / gsl_sf_bessel_I0(G)); + //printf("%3i %3i %3i alpha=%f * (%f / %f) = %f\n", h, k, l, G, gsl_sf_bessel_I1(G), gsl_sf_bessel_I0(G), alpha); + } + + } + + } + + if ( alpha < alpha_min ) { + alpha_min = alpha; + h_min = h; + k_min = k; + l_min = l; + i_min = reflections->refs[i].parent_index; + } + + } + + if ( alpha_min == 99999 ) { + printf("DM: Convergence procedure isn't working sensibly: alpha is too high for all reflections.\n"); + printf("DM: Have you normalised properly?\n"); + return NULL; + } + + /* Remove the reflection h_min,k_min from the basis list */ + reflist_delref(reflections, h_min, k_min, l_min); + //printf("DM: %3i %3i %3i removed from convergence list, alpha = %f, %i reflections remaining\n", + // h_min, k_min, l_min, alpha_min, reflections->n_reflections); + + /* Remove all equivalent reflections from the basis list */ + equivalents = symmetry_generate_equivalent_reflections(sym, h_min, k_min, l_min); + + for ( p=1; pn_reflections; p++ ) { + + signed int h, k, l; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + reflist_delref(reflections, h, k, l); + //printf("DM: Removing symmetry equivalent %3i %3i %3i\n", h, k, l); + + } + + reflist_free(equivalents); + + /* Add the first reflections (but not the equivalents) */ + reflist_addref_alpha_parent(convergence, h_min, k_min, l_min, alpha_min, i_min); + + } + + /* "Convergence" is in increasing order of connectivity. Reverse the order, examining + the last 50 reflections on the convergence list */ + basis = reflist_new_parent(reflections_orig->parent); + jmin = (convergence->n_reflections)-13; + if ( jmin < 1 ) jmin = 1; + for ( j=(convergence->n_reflections)-1; j>=jmin; j-- ) { + if ( (convergence->refs[j].alpha < min_alpha) || ( basis->n_reflections<10) ) { + unsigned int parent; + //printf("DM: Adding %3i %3i %3i to starting set\n", convergence->refs[j].h, convergence->refs[j].k, convergence->refs[j].l); + parent = convergence->refs[j].parent_index; + reflist_addref_parent(basis, convergence->refs[j].h, convergence->refs[j].k, convergence->refs[j].l, parent); + } + } + + return basis; + +} + +static int cdm_phaserelationships(CDMContext *cdm) { + + if ( cdm->strongest_reflections == NULL ) { + /* Get the strongest reflections */ + cdm->strongest_reflections = cdm_findstrongreflections(cdm->reflections, cdm->sym, cdm->emin); + if ( !cdm->strongest_reflections ) { + printf("DM: Error while searching for strongest reflections.\n"); + return -1; + } else { + printf("DM: Have %i strong reflections (%0.1f%%).\n", cdm->strongest_reflections->n_reflections-1, + (((double)cdm->strongest_reflections->n_reflections-1)/(cdm->reflections->n_reflections-1))*100); + if ( cdm->strongest_reflections->n_reflections == 1 ) { + printf("DM: No strong reflections found!\n"); + return -1; + } + } + } else { + printf("DM: Already have strongest reflections.\n"); + } + + if ( cdm->triplet_list == NULL ) { + /* Find the triplets */ + cdm->triplet_list = cdm_findtriplets(cdm->strongest_reflections, cdm->sym, cdm->emin, cdm->gmin); + if ( !cdm->triplet_list ) { + printf("DM: Error while searching for triplets.\n"); + return -1; + } + } else { + printf("DM: Already have triplet list.\n"); + } + + if ( cdm->basis_list == NULL ) { + /* Determine an appropriate starting point */ + cdm->basis_list = cdm_definebasis(cdm->strongest_reflections, cdm->triplet_list, cdm->sym, cdm->amin); + if ( !cdm->basis_list ) { + printf("DM: Error while defining basis set.\n"); + return -1; + } + printf("DM: %i reflections in the starting set\n", cdm->basis_list->n_reflections-1); + } else { + printf("DM: Already have basis set.\n"); + } + + return 0; + +} + +static unsigned int cdm_phasebasis(ReflectionList *basis_list, ReflectionList *reflections, Symmetry sym) { + + unsigned int i; + unsigned int n_assigned = 0; + + /* Unset all phases */ + for ( i=0; in_reflections; i++ ) { + reflections->refs[i].phase_calc_set = 0; + } + + /* Assign random phases to the basis set */ + for ( i=1; in_reflections; i++ ) { + + ReflectionList *equivalents; + unsigned int p, j; + signed int h, k, l; + + j = basis_list->refs[i].parent_index; + h = reflections->refs[j].h; + k = reflections->refs[j].k; + l = reflections->refs[j].l; + + reflections->refs[j].phase_calc = (((double)random()/RAND_MAX) * (M_PI)) - (((double)random()/RAND_MAX) * (M_PI)); + reflections->refs[j].phase_calc_set = 1; + n_assigned++; + + /* Centricity */ + symmetry_centricity(reflections, j, sym, SYMFLAG_PHASES_CALC); + //printf("Set phase of %3i %3i %3i to %f\n", h, k, l, reflections->refs[j].phase_calc); + + /* Assign all symmetry equivalents as well */ + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + + for ( p=1; pn_reflections; p++ ) { + + unsigned int q; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + if ( (q = reflist_inlist(reflections, h, k, l)) ) { + + double equivalent_phase; + + equivalent_phase = (reflections->refs[j].phase_calc + equivalents->refs[p].delta_theta) * equivalents->refs[p].multiplier; + + reflections->refs[q].phase_calc = equivalent_phase; + reflections->refs[q].phase_calc_set = 1; + symmetry_centricity(reflections, q, sym, SYMFLAG_PHASES_CALC); + + //printf("DM: Setting equivalent reflection %3i %3i %3i to %f\n", h, k, l, equivalent_phase); + n_assigned++; + + } else { + //printf("DM: Need to set phase of %3i %3i %3i\n", h, k, l); + } + + } + + reflist_free(equivalents); + + } + + return n_assigned; + +} + +static unsigned int cdm_assign_code(ReflectionList *reflections, ReflectionList *basis_list, Symmetry sym, unsigned int phasing_code) { + + unsigned int i; + unsigned int n_assigned = 0; + + /* Unset all phases */ + for ( i=0; in_reflections; i++ ) { + reflections->refs[i].phase_calc_set = 0; + } + + /* Assign phases to the basis set */ + for ( i=1; in_reflections; i++ ) { + + ReflectionList *equivalents; + unsigned int p, j; + signed int h, k, l; + + j = basis_list->refs[i].parent_index; + h = reflections->refs[j].h; + k = reflections->refs[j].k; + l = reflections->refs[j].l; + + if ( phasing_code & (1<<(i-1)) ) { + reflections->refs[j].phase_calc = M_PI; + } else { + reflections->refs[j].phase_calc = 0; + } + reflections->refs[j].phase_calc_set = 1; + n_assigned++; + + /* Centricity */ + symmetry_centricity(reflections, j, sym, SYMFLAG_PHASES_CALC); + //printf("Set phase of %3i %3i %3i to %f\n", h, k, l, reflections->refs[j].phase_calc); + + /* Assign all symmetry equivalents as well */ + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + + for ( p=1; pn_reflections; p++ ) { + + unsigned int q; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + if ( (q = reflist_inlist(reflections, h, k, l)) ) { + + double equivalent_phase; + + equivalent_phase = (reflections->refs[j].phase_calc + equivalents->refs[p].delta_theta) * equivalents->refs[p].multiplier; + + reflections->refs[q].phase_calc = equivalent_phase; + reflections->refs[q].phase_calc_set = 1; + symmetry_centricity(reflections, q, sym, SYMFLAG_PHASES_CALC); + + //printf("DM: Setting equivalent reflection %3i %3i %3i to %f\n", h, k, l, equivalent_phase); + n_assigned++; + + } else { + //printf("DM: Need to set phase of %3i %3i %3i\n", h, k, l); + } + + } + + reflist_free(equivalents); + + } + + return n_assigned; + +} + +static unsigned int cdm_expand(CDMContext *cdm) { + + unsigned int total_assigned = 0; + unsigned int total_refined = 0; + unsigned int n_assigned, n_refined; + ReflectionList *reflections = cdm->reflections; + ReflectionList *strongest_reflections = cdm->strongest_reflections; + TripletList *triplet_list = cdm->triplet_list; + Symmetry sym = cdm->sym; + double min_alpha = cdm->amin; + unsigned int refinement = cdm->refine; + + /* Expand as far as possible */ + do { + + unsigned int i; + n_assigned = 0; + n_refined = 0; + + for ( i=0; in_reflections; i++ ) { + + double beta; + unsigned int t; + unsigned int n_tri = 0; + double alpha; + double sigma_sin = 0; + double sigma_cos = 0; + signed int h = strongest_reflections->refs[i].h; + signed int k = strongest_reflections->refs[i].k; + signed int l = strongest_reflections->refs[i].l; + unsigned int j = strongest_reflections->refs[i].parent_index; + unsigned int refine, assign; + + if ( !refinement && reflections->refs[j].phase_calc_set ) continue; /* Already phased? */ + + /* Calculate the most likely phase for this reflection based on the active triplets */ + for ( t=0; tn_triplets; t++ ) { + + signed int ph = triplet_list->triplets[t].p.h; + signed int pk = triplet_list->triplets[t].p.k; + + /* Does this triplet concern this reflection? */ + if ( (ph == h) && (pk == k) ) { + + signed int qh = triplet_list->triplets[t].q.h; + signed int qk = triplet_list->triplets[t].q.k; + signed int rh = -ph-qh; + signed int rk = -pk-qk; + unsigned int q = reflist_inlist_2d(reflections, qh, qk); + unsigned int r = reflist_inlist_2d(reflections, rh, rk); + + if ( !q ) { + printf("DM: q: %3i %3i not in list!\n", qh, qk); + continue; + } + if ( !r ) { + printf("DM: r: %3i %3i not in list!\n", rh, rk); + continue; + } + + /* Does this triplet concern already phased reflections? */ + if ( reflections->refs[q].phase_calc_set && reflections->refs[r].phase_calc_set ) { + + double omega = reflections->refs[q].phase_calc + reflections->refs[r].phase_calc; + + sigma_sin += triplet_list->triplets[t].G * sin(omega); + sigma_cos += triplet_list->triplets[t].G * cos(omega); + + n_tri++; + + + } + + } + + } + + alpha = sqrt(sigma_sin*sigma_sin + sigma_cos*sigma_cos); + assign = 0; refine = 0; + if ( !reflections->refs[j].phase_calc_set && (alpha > min_alpha) ) assign = 1; + if ( refinement && reflections->refs[j].phase_calc_set && (alpha > reflections->refs[j].alpha) ) refine = 1; + /* Assign phase if it's reliable enough */ + if ( assign || refine ) { + + ReflectionList *equivalents; + unsigned int p; + + beta = atan2(sigma_sin, sigma_cos); + if ( assign ) { + reflections->refs[j].phase_calc = beta; + reflections->refs[j].phase_calc_set = 1; + reflections->refs[j].alpha = alpha; + n_assigned++; + //printf("DM: Assigned %3i %3i phase %f (alpha=%f from %i triplets)\n", h, k, beta, alpha, n_tri); + } else { + reflections->refs[j].phase_calc = beta; + reflections->refs[j].alpha = alpha; + n_refined++; + //printf("DM: Refined %3i %3i phase %f (alpha=%f from %i triplets)\n", h, k, beta, alpha, n_tri); + } + + /* Centricity */ + symmetry_centricity(reflections, j, sym, SYMFLAG_PHASES_CALC); + + /* Assign all symmetry equivalents as well */ + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + + for ( p=1; pn_reflections; p++ ) { + + signed int h, k, l; + unsigned int q; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + if ( (q = reflist_inlist(reflections, h, k, l)) ) { + + double equivalent_phase; + + equivalent_phase = (beta + equivalents->refs[p].delta_theta) * equivalents->refs[p].multiplier; + + reflections->refs[q].phase_calc = equivalent_phase; + reflections->refs[q].phase_calc_set = 1; + reflections->refs[q].alpha = alpha; + symmetry_centricity(reflections, q, sym, SYMFLAG_PHASES_CALC); + + //printf("DM: Setting equivalent reflection %3i %3i %3i to %f\n", h, k, l, equivalent_phase); + n_assigned++; + + } else { + //printf("DM: Need to set phase of %3i %3i %3i, but it isn't in the list\n", h, k, l); + } + + } + + reflist_free(equivalents); + + } + + } + //printf("DM: This cycle: %i phases assigned, %i refined\n", n_assigned, n_refined); + total_assigned += n_assigned; + total_refined += n_refined; + + } while (n_refined+n_assigned > 0); + + cdm->n_assigned_expansion = total_assigned; + cdm->n_refined = total_refined; + return total_assigned; + +} + +static void cdm_sortsolutions(CDMContext *cdm) { + + + +} + +unsigned int cdm_systematic_expansion(CDMContext *cdm) { + + unsigned int i; + unsigned int n_solutions; + PhasingSolution *prev; + fftw_complex *cdm_in; + fftw_complex *cdm_out; + fftw_plan cdm_plan; + signed int h, k; + ReflectionList *reflections; + PhasingSolution *sol; + + if ( !(cdm->sym & SYMMETRY_CENTRE) ) { + error_report("Can only automatically iterate in a centrosymmetric planegroup"); + return 0; + } + + if ( cdm->basis_list->n_reflections > (sizeof(int)*8) ) { + error_report("Too many solutions to enumerate"); + return 0; + } + + /* Enumerate the solutions */ + n_solutions = 1<<(cdm->basis_list->n_reflections-1); + prev = NULL; + for ( i=1; i<=n_solutions; i++ ) { + + PhasingSolution *new; + + new = malloc(sizeof(PhasingSolution)); + new->n = i; + new->phasing_code = i-1; /* phasing_code might not always equal n */ + + new->next = NULL; + if ( i == 1 ) { + cdm->solutions = new; + } else { + prev->next = new; + } + prev = new; + + } + printf("DM: Enumerated %i solutions\n", n_solutions); + + /* Prepare */ + cdm_in = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + cdm_out = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + cdm_plan = fftw_plan_dft_2d(data_width(), data_height(), cdm_in, cdm_out, + FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + for ( h=0; hreflections; + sol = cdm->solutions; + do { + + unsigned int basis_assigned, expnd_assigned; + int x, y; + double max, unflatness, entropy; + + printf("%7i, phasing code %06X", sol->n, sol->phasing_code); + basis_assigned = cdm_assign_code(reflections, cdm->basis_list, cdm->sym, sol->phasing_code); + expnd_assigned = cdm_expand(cdm); + + /* Transfer to Fourier array */ + for ( i=0; in_reflections; i++ ) { + if ( reflections->refs[i].phase_calc_set ) { + h = reflections->refs[i].h; + k = reflections->refs[i].k; + if ( h < 0 ) { h = data_width()+h; } + if ( k < 0 ) { k = data_height()+k; } + cdm_in[k + data_height()*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_calc); + cdm_in[k + data_height()*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_calc); + } + } + + fftw_execute(cdm_plan); + displaywindow_switchview(); + while ( gtk_events_pending() ) gtk_main_iteration(); + + /* Calculate "unflatness" and entropy of map, and record */ + max = 0; + for ( y=0; y max ) max = am; + } + } + unflatness = 0; entropy = 0; + for ( y=0; yunflatness = unflatness; + sol->entropy = entropy; + printf(" : %i + %i (+%i) phased, unflatness=%.5f, entropy=%.5f\n", basis_assigned, expnd_assigned, cdm->n_refined, unflatness, entropy); + + sol = sol->next; + + } while ( sol ); + + /* Finalise */ + fftw_destroy_plan(cdm_plan); + fftw_free(cdm_in); + fftw_free(cdm_out); + + cdm_sortsolutions(cdm); + cdm_solutionwindow_open(cdm); + + return 0; + +} + +unsigned int cdm_tangentexpansion(CDMContext *cdm) { + + int res; + + /* Set up the phase relationships */ + res = cdm_phaserelationships(cdm); + if ( res ) return res; + + if ( !cdm->auto_iterate ) { + + unsigned int basis_assigned, expnd_assigned; + + /* Phase the initial reflections */ + basis_assigned = cdm_phasebasis(cdm->basis_list, cdm->reflections, cdm->sym); + + /* Expand */ + expnd_assigned = cdm_expand(cdm); + + /* Report */ + printf("DM: %i + %i (+%i) phases assigned\n", basis_assigned, expnd_assigned, cdm->n_refined); + + return basis_assigned + expnd_assigned; + + } else { + + return cdm_systematic_expansion(cdm); + + } + +} + +/* ..................... The Other Stuff ..................... */ + +static int cdm_window_open = 0; + +static void cdm_execute(CDMWindow *cdm) { + + char *string; + int total_assigned; + Symmetry sym; + float emin, gmin, amin; + const char *str; + CDMContext *cdmctx; + + sym = gtk_symmetry_get_symmetry(GTK_SYMMETRY(cdm->symmetry)); + + str = gtk_entry_get_text(GTK_ENTRY(cdm->emin)); + if ( !sscanf(str, "%f", &emin) ) { + error_report("Plase specify minimum |E|"); + return; + } + str = gtk_entry_get_text(GTK_ENTRY(cdm->gmin)); + if ( !sscanf(str, "%f", &gmin) ) { + error_report("Plase specify minimum G"); + return; + } + str = gtk_entry_get_text(GTK_ENTRY(cdm->amin)); + if ( !sscanf(str, "%f", &amin) ) { + error_report("Plase specify minimum α"); + return; + } + + if ( the_cdmctx == NULL ) { + printf("DM: Creating new context.\n"); + the_cdmctx = malloc(sizeof(CDMContext)); + if ( the_cdmctx == NULL ) { + error_report("Couldn't create CDM control structure"); + return; + } + the_cdmctx->auto_iterate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->auto_iterate)); + the_cdmctx->refine = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->refine)); + the_cdmctx->sym = sym; + the_cdmctx->emin = emin; + the_cdmctx->gmin = gmin; + the_cdmctx->amin = amin; + the_cdmctx->strongest_reflections = NULL; + the_cdmctx->triplet_list = NULL; + the_cdmctx->basis_list = NULL; + } else { + int restart = 0; + if ( the_cdmctx->sym != sym ) { + restart = 1; + } + if ( the_cdmctx->emin != emin ) { + restart = 1; + } + if ( the_cdmctx->gmin != gmin ) { + restart = 1; + } + /* The others do not require a re-calculation */ + if ( restart ) { + printf("DM: Conditions have changed. Recreating context...\n"); + /* Tidy up */ + free(the_cdmctx->triplet_list); + free(the_cdmctx->strongest_reflections); + free(the_cdmctx->basis_list); + free(the_cdmctx); + the_cdmctx = malloc(sizeof(CDMContext)); + if ( the_cdmctx == NULL ) { + error_report("Couldn't create CDM control structure"); + return; + } + the_cdmctx->auto_iterate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->auto_iterate)); + the_cdmctx->refine = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->refine)); + the_cdmctx->sym = sym; + the_cdmctx->emin = emin; + the_cdmctx->gmin = gmin; + the_cdmctx->amin = amin; + the_cdmctx->strongest_reflections = NULL; + the_cdmctx->triplet_list = NULL; + the_cdmctx->basis_list = NULL; + } + + } + cdmctx = the_cdmctx; + + if ( !cdmctx->auto_iterate ) { + + /* Do one expansion based on a random starting set, display the result */ + total_assigned = main_cdm_tangentexpansion(cdmctx); + displaywindow_switchview(); + + string = malloc(64); + snprintf(string, 63, "%i phases assigned", total_assigned); + displaywindow_statusbar(string); + free(string); + + } else { + + /* Systematically iterate over all possible starting sets, rank by FoM */ + main_cdm_tangentexpansion(cdmctx); + + } + +} + +static gint cdm_window_response(GtkWidget *widget, gint response, CDMWindow *cdm) { + + if ( response == GTK_RESPONSE_OK ) { + cdm_execute(cdm); + } + + if ( response != GTK_RESPONSE_OK ) { + cdm_window_open = 0; + gtk_widget_destroy(widget); + } + + return 0; + +} + +void cdm_dialog_open() { + + CDMWindow *cdm; + GtkWidget *cdm_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *emin_label; + GtkWidget *gmin_label; + GtkWidget *amin_label; + + if ( cdm_window_open ) { + return; + } + cdm_window_open = 1; + + cdm = malloc(sizeof(CDMWindow)); + + cdm_window = gtk_dialog_new_with_buttons("Tangent Formula", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, GTK_STOCK_EXECUTE, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cdm_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + cdm->symmetry = gtk_symmetry_new(2, 2, TRUE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cdm->symmetry), TRUE, TRUE, 5); + + table = gtk_table_new(5, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + emin_label = gtk_label_new("Minimum |E|:"); + gtk_misc_set_alignment(GTK_MISC(emin_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(emin_label), 1, 2, 1, 2); + cdm->emin = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->emin), 2, 3, 1, 2); + gtk_entry_set_text(GTK_ENTRY(cdm->emin), "0.0001"); + + gmin_label = gtk_label_new("Minimum G:"); + gtk_misc_set_alignment(GTK_MISC(gmin_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gmin_label), 1, 2, 2, 3); + cdm->gmin = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->gmin), 2, 3, 2, 3); + gtk_entry_set_text(GTK_ENTRY(cdm->gmin), "0.00024"); + + amin_label = gtk_label_new("Minimum α:"); + gtk_misc_set_alignment(GTK_MISC(amin_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(amin_label), 1, 2, 3, 4); + cdm->amin = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->amin), 2, 3, 3, 4); + gtk_entry_set_text(GTK_ENTRY(cdm->amin), "0.0001"); + + cdm->refine = gtk_check_button_new_with_label("Refine Phases"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->refine), 1, 3, 4, 5); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cdm->refine), TRUE); + + cdm->auto_iterate = gtk_check_button_new_with_label("Automatically Iterate"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->auto_iterate), 1, 3, 5, 6); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cdm->auto_iterate), FALSE); + + g_signal_connect(G_OBJECT(cdm_window), "response", G_CALLBACK(cdm_window_response), cdm); + gtk_widget_show_all(cdm_window); + +} + +enum { + SOLUTION_COLUMN_NUMBER, + SOLUTION_COLUMN_PHASECODE, + SOLUTION_COLUMN_UNFLATNESS, + SOLUTION_COLUMN_ENTROPY, + SOLUTION_COLUMNS +}; + +void cdm_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol, ReflectionList *reflections) { + + cdm_assign_code(reflections, cdm->basis_list, cdm->sym, sol->phasing_code); + cdm_expand(cdm); + + displaywindow_switchview(); + +} + +static gint cdm_solutionwindow_response(GtkWidget *solution_window, gint response, gpointer data) { + gtk_widget_destroy(solution_window); + return 0; +} + +static void cdm_solutionwindow_selectionchanged(GtkTreeSelection *selection, CDMContext *cdm) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + gint snum; + PhasingSolution *sol_it; + PhasingSolution *sol; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(cdm->solution_tree_view), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(cdm->solution_list_store), &iter, path); + gtk_tree_model_get(GTK_TREE_MODEL(cdm->solution_list_store), &iter, SOLUTION_COLUMN_NUMBER, &snum, -1); + + sol_it = cdm->solutions; sol = NULL; + do { + if ( sol_it->n == snum ) { + sol = sol_it; + break; + } + sol_it = sol_it->next; + } while ( sol_it ); + if ( sol ) { + printf("DM: Displaying solution %7i, phasing code %06X\n", sol->n, sol->phasing_code); + main_display_phasing_solution(cdm, sol); + } else { + printf("DM: Can't find solution %i\n", snum); + error_report("Can't find solution!\n"); + } + +} + +static void cdm_solutionwindow_open(CDMContext *cdm) { + + GtkWidget *solution_window; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkWidget *scrolledwindow; + GtkWidget *solution_tree; + GtkListStore *solution_list_store; + GtkWidget *hbox; + GtkWidget *vbox; + GtkTreeIter iter; + PhasingSolution *sol; + + solution_window = gtk_dialog_new_with_buttons("Phasing Solutions", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size(GTK_WINDOW(solution_window), 400, 500); + + /* Window layout */ + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(solution_window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + /* List and Tree */ + solution_list_store = gtk_list_store_new(SOLUTION_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + solution_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(solution_list_store)); + g_object_unref(G_OBJECT(solution_list_store)); + cdm->solution_tree_view = solution_tree; + cdm->solution_list_store = solution_list_store; + + /* Set up columns */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("#", renderer, "text", SOLUTION_COLUMN_NUMBER, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + column = gtk_tree_view_column_new_with_attributes("Phase", renderer, "text", SOLUTION_COLUMN_PHASECODE, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + column = gtk_tree_view_column_new_with_attributes("Unflatness", renderer, "text", SOLUTION_COLUMN_UNFLATNESS, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + column = gtk_tree_view_column_new_with_attributes("Entropy", renderer, "text", SOLUTION_COLUMN_ENTROPY, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + + /* Scrollbar */ + scrolledwindow = gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(solution_tree)), + gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(solution_tree))); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolledwindow), GTK_WIDGET(solution_tree)); + + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolledwindow), TRUE, TRUE, 5); + + /* Add the solutions */ + sol = cdm->solutions; + do { + char tmp[32]; + gtk_list_store_append(solution_list_store, &iter); + snprintf(tmp, 31, "%06X", sol->phasing_code); + gtk_list_store_set(solution_list_store, &iter, SOLUTION_COLUMN_NUMBER, sol->n, SOLUTION_COLUMN_PHASECODE, tmp, + SOLUTION_COLUMN_UNFLATNESS, sol->unflatness, SOLUTION_COLUMN_ENTROPY, sol->entropy, -1); + sol = sol->next; + } while ( sol ); + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(solution_tree))), "changed", + G_CALLBACK(cdm_solutionwindow_selectionchanged), cdm); + + + g_signal_connect(G_OBJECT(solution_window), "response", G_CALLBACK(cdm_solutionwindow_response), cdm); + gtk_widget_show_all(solution_window); + +} diff --git a/src/cdm.h b/src/cdm.h new file mode 100644 index 0000000..a0cae01 --- /dev/null +++ b/src/cdm.h @@ -0,0 +1,77 @@ +/* + * cdm.h + * + * "Conventional" Direct Methods + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CDM_H +#define CDM_H + +#include + +#include "reflist.h" +#include "symmetry.h" + +#define MAX_TRIPLETS 65536 + +typedef struct { + Reflection p; + Reflection q; + /* The other reflection in the triplet is -p-q */ + double G; +} Triplet; + +typedef struct { + Triplet triplets[MAX_TRIPLETS]; + unsigned int n_triplets; +} TripletList; + +typedef struct s_phase_sol { + + unsigned int n; /* Reference number */ + unsigned int phasing_code; /* Phase values, binary 1->Pi, 0->0. Only works for centro */ + double unflatness; /* Luzzatti 'unflatness': sigma(rho^4) */ + double entropy; /* Entropy -sigma(P*ln(P)) */ + + struct s_phase_sol *next; + +} PhasingSolution; + +typedef struct { + + unsigned int auto_iterate; + unsigned int refine; + double emin; + double gmin; + double amin; + Symmetry sym; + + ReflectionList *reflections; /* Overall list of reflections (used to store results) */ + ReflectionList *strongest_reflections; /* Strongest reflections (used for phasing) */ + TripletList *triplet_list; /* List of active triplets */ + ReflectionList *basis_list; /* Starting set of reflections */ + + GtkWidget *solution_tree_view; + GtkListStore *solution_list_store; + + PhasingSolution *solutions; + + unsigned int n_assigned_basis; + unsigned int n_assigned_expansion; + unsigned int n_refined; + +} CDMContext; + +extern void cdm_dialog_open(void); +extern unsigned int cdm_tangentexpansion(CDMContext *cdm); +extern void cdm_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol, ReflectionList *reflections); + +#endif /* CDM_H */ diff --git a/src/cflip.c b/src/cflip.c new file mode 100644 index 0000000..2b3dfd6 --- /dev/null +++ b/src/cflip.c @@ -0,0 +1,910 @@ +/* + * cflip.c + * + * Charge flipping iteration algorithms + * + * (c) 2006-2007 Thomas White + * (c) 2008 Alex Eggeman + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "displaywindow.h" +#include "symmetry.h" +#include "gtk-symmetry.h" +#include "reflist.h" +#include "dpsynth.h" + +static int cf_window_open = 0; + +typedef struct struct_cflipdialog { + + /* Dialog box bits */ + GtkWidget *cf_window; + GtkWidget *cf_go; + GtkWidget *cf_stop; + GtkWidget *cf_next; + GtkWidget *cf_reset; + GtkWidget *cf_iterations; + + /* Algorithm guts */ + ReflectionList *cf_reflections; /* List of 'modulus constraints' */ + ReflectionList *cf_listout; /* List of structure factors returned at the end of an iteration */ + Symmetry cf_symmetry; /* Symmetry of the algorithm */ + fftw_complex *cf_drive_old; + fftw_complex *cf_drive; + fftw_complex *cf_in; /* Input realspace array */ + fftw_complex *cf_out; /* Array on the other end of the FFT of 'in' */ + unsigned int *cf_support; + unsigned int cf_tangent; /*Coehlo Constraints*/ + double *intensities; /*Array of pixel intensities for auto-thresholding*/ + double *amplitudes; /*Array of reflection intiensities for auto-thresholding*/ + fftw_plan cf_plan_i2d; + fftw_plan cf_plan_d2i; + unsigned int width; /* Width of the array in pixels */ + unsigned int height; /* Height of the array in pixels */ + double delta; + //DPSynthWindow *dpsynth; + + /* Tracking */ + + unsigned int running; + unsigned int n_iterations; /* Number of iterations performed so far */ + unsigned int period; /* Number of iterations per symmetry application */ + +} CFlipDialog; + +int compare_doubles (const void *xp,const void *yp) { + + const double *x, *y; + int z; + + x=xp; + y=yp; + + + if (*x > *y) { + z = 1; + } else if (*x < *y) { + z = -1; + } else { + z = 0; + } + return z; +} + +static int cflip_tangent (ReflectionList *listout, double emin, double gmin) { + + ReflectionList *strongest_reflections; + TripletList *triplet_list; + unsigned int i; + unsigned int p; + unsigned int q; + unsigned int n_total_triplets; + double E1; + double E2; + double E3; + double G; + double Th; + double Bh; + double *Mh; + double maxMh; + double *phase_tangent; + double alpha_tan, phi_a, phi_tan; + unsigned int r, t; + signed int ph, pk, pl, pph, qh, qk, ql, qph, rh, rk, rl, rph; + + strongest_reflections = reflist_new_parent(listout); + /* Initialise triplet list */ + triplet_list = malloc(sizeof(TripletList)); + triplet_list->n_triplets = 0; + + for ( i=0; in_reflections; i++ ) { + listout->refs[i].parent_index = i; + } + + for ( i=1; in_reflections; i++ ) { + if ( (listout->refs[i].amplitude >= emin) && (listout->refs[i].h != 0) && (listout->refs[i].k != 0) ) { + reflist_addref_parent(strongest_reflections, listout->refs[i].h, listout->refs[i].k, listout->refs[i].l, + listout->refs[i].parent_index); + } + } + + Mh = malloc(strongest_reflections->n_reflections*sizeof(double)); + + phase_tangent = malloc(strongest_reflections->n_reflections*sizeof(double)); + + /* Iterate doubly over the reflections */ + n_total_triplets = 0; + for ( p=1; pn_reflections; p++ ) { + for ( q=1; qn_reflections; q++ ) { + + ph = strongest_reflections->refs[p].h; + pk = strongest_reflections->refs[p].k; + pl = strongest_reflections->refs[p].l; + pph = strongest_reflections->refs[p].phase_known; + qh = strongest_reflections->refs[q].h; + qk = strongest_reflections->refs[q].k; + ql = strongest_reflections->refs[q].l; + qph = strongest_reflections->refs[q].phase_known; + + assert(ph || pk || pl); + assert(qh || qk || ql); + + /* Determine the third reflection in the triplet */ + rh = -ph-qh; + rk = -pk-qk; + if ( pl != ql ) { + printf("Coelho: pl != ql: %3i %3i\n", pl, ql); + continue; + } + //assert(pl == ql); /* This failure mode should have been weeded out ages ago */ + rl = pl; + + /* See if the third reflection is available */ + if ( (r = reflist_inlist(strongest_reflections, rh, rk, rl)) ) { + + /* Trust me on this ... */ + E1 = strongest_reflections->parent->refs[strongest_reflections->refs[p].parent_index].amplitude; + E2 = strongest_reflections->parent->refs[strongest_reflections->refs[q].parent_index].amplitude; + E3 = strongest_reflections->parent->refs[strongest_reflections->refs[r].parent_index].amplitude; + + G = E1 * E2 * E3; + + rph = strongest_reflections->parent->refs[strongest_reflections->refs[r].parent_index].phase_known; + + /* Convincing enough? */ + if ( G > gmin ) { + triplet_list->triplets[triplet_list->n_triplets].p.h = ph; + triplet_list->triplets[triplet_list->n_triplets].p.k = pk; + triplet_list->triplets[triplet_list->n_triplets].p.l = pl; + triplet_list->triplets[triplet_list->n_triplets].q.h = qh; + triplet_list->triplets[triplet_list->n_triplets].q.k = qk; + triplet_list->triplets[triplet_list->n_triplets].q.l = ql; + triplet_list->triplets[triplet_list->n_triplets].G = G; + triplet_list->n_triplets++; + if ( triplet_list->n_triplets == MAX_TRIPLETS ) { + printf("DM: Too many triplets!\n"); + return NULL; + } + + } + n_total_triplets++; + + } + + } + } + maxMh = 0; + + for ( i=0; in_reflections; i++ ) { + + signed int h = strongest_reflections->refs[i].h; + signed int k = strongest_reflections->refs[i].k; + + for ( t=0; tn_triplets; t++ ) { + + signed int ph = triplet_list->triplets[t].p.h; + signed int pk = triplet_list->triplets[t].p.k; + + + /* Does this triplet concern this reflection? */ + if ( (ph == h) && (pk == k) ) { + + signed int qh = triplet_list->triplets[t].q.h; + signed int qk = triplet_list->triplets[t].q.k; + signed int rh = -ph-qh; + signed int rk = -pk-qk; + double G = triplet_list->triplets[t].G; + + /* Are the other reflections in the triplet still in the list? */ + if ( reflist_inlist_2d(listout, qh, qk) && reflist_inlist_2d(listout, rh, rk) ) { + Th += G * sin(( qph - rph)); + Bh += G * cos(( qph - rph)); + } + + + } + + } + phase_tangent[i] = atan( Th / Bh ); + Mh[i] = sqrt( (Th*Th) / (Bh*Bh) ); + if ( Mh[i] > maxMh ) { + maxMh = Mh[i]; + } + + } + + for ( i=0; in_reflections; i++ ) { + + alpha_tan = Mh[i] / maxMh; + + phi_a = strongest_reflections->parent->refs[strongest_reflections->refs[i].parent_index].phase_known; + + phi_tan = phi_a + alpha_tan*(phase_tangent[i]-phi_a); + + listout->refs[strongest_reflections->refs[i].parent_index].phase_known = phi_tan; + + } + + free(strongest_reflections); + free(triplet_list); + + return 0; + +} + +void cflip_mask(CFlipDialog *cflip_dialog, fftw_complex *in) { + + + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + unsigned int i; + fftw_complex *in_new = malloc(width*height*sizeof(fftw_complex)); + bzero(in_new, width*height*sizeof(fftw_complex)); + ReflectionList *reflections; + + reflections = cflip_dialog->cf_reflections; + + for ( i=1; in_reflections; i++ ) { + + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + in_new[k + height*h][0] = in[k + height*h][0]; + in_new[k + height*h][1] = in[k + height*h][1]; + + } + + memcpy(in, in_new, width*height*sizeof(fftw_complex)); + free(in_new); + +} + +/* Perform a single CF iteration */ +void cflip_iteration(CFlipDialog *cflip_dialog) { + + double re, im; + double am, ph, max, i_max; + signed int h, k; + unsigned int j, indexj; + unsigned int x, y; + double fzero; + double emin = 0; + double gmin = 0; + double mean, neg; + double coehlo_thresh; + unsigned int mean_n; + double meansq; + double sig; + double delta; + unsigned int i; + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + ReflectionList *reflections = cflip_dialog->cf_reflections; + ReflectionList *listout = cflip_dialog->cf_listout; + + cflip_dialog->intensities = malloc(width*height*sizeof(double)); + cflip_dialog->amplitudes = malloc(reflections->n_reflections*sizeof(double)); + +// listout = malloc(sizeof(ReflectionList)); + listout = NULL; + assert(cflip_dialog->cf_drive_old != NULL); + assert(cflip_dialog->cf_drive != NULL); + assert(cflip_dialog->cf_in != NULL); + assert(cflip_dialog->cf_out != NULL); + + if ( !cflip_dialog->cf_tangent ) { + + /* Transform back to reciprocal space and scale. This time, the driving function is + being transformed. */ + fftw_execute(cflip_dialog->cf_plan_d2i); + for ( x=0; xcf_in[y+((height/2)+1)*x][0] = cflip_dialog->cf_in[y+((height/2)+1)*x][0] / (width*height); /* Re */ + cflip_dialog->cf_in[y+((height/2)+1)*x][1] = cflip_dialog->cf_in[y+((height/2)+1)*x][1] / (width*height); /* Im */ + } + } + + /* Impose observed intensities */ + for ( i=0; in_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + re = cflip_dialog->cf_in[k + height*h][0]; + im = cflip_dialog->cf_in[k + height*h][1]; + ph = atan2(im, re); + am = reflections->refs[i].amplitude; + + cflip_dialog->cf_in[k + height*h][0] = am*cos(ph); + cflip_dialog->cf_in[k + height*h][1] = am*sin(ph); + + } + + fzero = cflip_dialog->cf_in[0][0]; + cflip_mask(cflip_dialog, cflip_dialog->cf_in); + + /* Impose symmetry */ + if ( cflip_dialog->n_iterations % cflip_dialog->period == 0 ) { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + + } else { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry & SYMMETRY_FRIEDEL, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + } + + + + /* Transform to real space */ + fftw_execute(cflip_dialog->cf_plan_i2d); + + memcpy(cflip_dialog->cf_out, cflip_dialog->cf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + mean = 0; mean_n = 0; meansq = 0; sig = 0; delta = 0; + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt(re*re + im*im); + mean += am; + mean_n += 1; + meansq += am * am; + } + } + + sig = sqrt((meansq/mean_n)-((mean/mean_n) * (mean/mean_n))); + delta = sig * cflip_dialog->delta; + + /* Determine the support */ + for ( y=0; ycf_drive[y+height*x][0] > delta ) { + cflip_dialog->cf_support[y+height*x] = 1; + } else { + cflip_dialog->cf_support[y+height*x] = 0; + } + } + } + + + /* Impose non-negativity, sharpen peaks and apply feedback */ + + /*mean = 0; neg = 0; + + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt(re*re + im*im); + + mean += am; + if ( re<0 ) { + neg += am; + } + + } + } + + mean = mean /(width * height); + + double entropy; + entropy = 0; + for ( y=0; yn_iterations, entropy, neg);*/ + + + for ( y=0; ycf_support[y+height*x] ) { + + cflip_dialog->cf_drive[y+height*x][0] = -cflip_dialog->cf_drive[y+height*x][0]; + cflip_dialog->cf_drive[y+height*x][1] = -cflip_dialog->cf_drive[y+height*x][1]; + + } + + } + } + + } else { /*Rework the Algorithm from here to incorporate the Coehlo constraints*/ + + /* Transform back to reciprocal space and scale. This time, the driving function is being transformed. */ + fftw_execute(cflip_dialog->cf_plan_d2i); + for ( x=0; xcf_in[y+((height/2)+1)*x][0] = cflip_dialog->cf_in[y+((height/2)+1)*x][0] / (width*height); /* Re */ + cflip_dialog->cf_in[y+((height/2)+1)*x][1] = cflip_dialog->cf_in[y+((height/2)+1)*x][1] / (width*height); /* Im */ + } + } + + /* Do Tangent Formula Wizardry */ + cflip_mask(cflip_dialog, cflip_dialog->cf_in); + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + + i_max = 0; + + for ( i=0; in_reflections; i++ ) { + + cflip_dialog->amplitudes[i]=listout->refs[i].amplitude; + + if ( listout->refs[i].amplitude > i_max ) { + i_max = listout->refs[i].amplitude; + } + } + + emin = 0.5 * i_max; + gmin = 0.2 * i_max; + + cflip_tangent (listout, emin, gmin); + + i_max = 0; j=0; max = 0; indexj=0; + + for ( i=0; in_reflections; i++ ) { + + cflip_dialog->amplitudes[i]=reflections->refs[i].amplitude; + if ( reflections->refs[i].amplitude > i_max ) { + i_max = reflections->refs[i].amplitude; + } + } + + qsort(cflip_dialog->amplitudes, listout->n_reflections, sizeof(double), compare_doubles); + + j = (int)(listout->n_reflections / 2); + j = (int)(listout->n_reflections / 4); + max = cflip_dialog->amplitudes[j]; + + /* Impose observed intensities */ + //unsigned int n_sub = 0; + for ( i=0; in_reflections; i++ ) { + + h = listout->refs[i].h; + k = listout->refs[i].k; + + indexj = reflist_inlist(reflections, h, k, 0); + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + ph = listout->refs[i].phase_known; + am = reflections->refs[indexj].amplitude; + if (am < max ) { + am = 0; + //n_sub += 1; + } + + //printf("number of Zero reflections is %i.\n", n_sub); + cflip_dialog->cf_in[k + height*h][0] = am*cos(ph); + cflip_dialog->cf_in[k + height*h][1] = am*sin(ph); + + } + + fzero = cflip_dialog->cf_in[0][0]; + //free(listout); + + /* Impose symmetry */ + if ( cflip_dialog->n_iterations % cflip_dialog->period == 0 ) { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + + } else { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry & SYMMETRY_FRIEDEL, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + } + + /* Transform to real space */ + fftw_execute(cflip_dialog->cf_plan_i2d); + + max = 0; + + memcpy(cflip_dialog->cf_out, cflip_dialog->cf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + + /* Find the Threshold Real-Space Intensity */ + mean = 0; mean_n = 0; sig = 0; delta = 0; + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt((re*re) + (im*im)); + if ( am > max ) { + max = am; + } + + } + } + + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt(re*re + im*im) / max; + mean += am; + cflip_dialog->intensities[y+height*x]=am; + } + } + + qsort(cflip_dialog->intensities, mean_n, sizeof(double), compare_doubles); + + coehlo_thresh = 0; + i=0; + while ( coehlo_thresh < (0.6 * mean) ) { + + coehlo_thresh += cflip_dialog->intensities[i]; + delta = cflip_dialog->intensities[i]; + i += 1; + } + + /* Determine the support */ + for ( y=0; ycf_drive[y+height*x][0] > delta ) { + cflip_dialog->cf_support[y+height*x] = 1; + } else { + cflip_dialog->cf_support[y+height*x] = 0; + } + } + } + + /* Perform the Charge Flip */ + for ( y=0; ycf_support[y+height*x] ) { + + cflip_dialog->cf_drive[y+height*x][0] = -cflip_dialog->cf_drive[y+height*x][0]; + cflip_dialog->cf_drive[y+height*x][1] = -cflip_dialog->cf_drive[y+height*x][1]; + + } else { + //cflip_dialog->cf_drive[y+height*x][0] = delta + sqrt((cflip_dialog->cf_drive[y+height*x][0])-delta); + cflip_dialog->cf_drive[y+height*x][0] = cflip_dialog->cf_drive[y+height*x][0]; + cflip_dialog->cf_drive[y+height*x][1] = cflip_dialog->cf_drive[y+height*x][1]; + + } + } + } + } +free(cflip_dialog->intensities); +free(cflip_dialog->amplitudes); +} + +static gint cflip_threshsup_deltachanged(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->delta = gtk_range_get_value(GTK_RANGE(widget)); + return 0; +} + +void cflip_finalise(CFlipDialog *cflip_dialog) { + + free(cflip_dialog->cf_drive); cflip_dialog->cf_drive = NULL; + free(cflip_dialog->cf_drive_old); cflip_dialog->cf_drive_old = NULL; + free(cflip_dialog->cf_support); cflip_dialog->cf_support = NULL; + free(cflip_dialog->cf_in); cflip_dialog->cf_in = NULL; + reflist_free(cflip_dialog->cf_reflections); cflip_dialog->cf_reflections = NULL; + /* Don't free cf_out, since it is now backing up the display */ + + fftw_destroy_plan(cflip_dialog->cf_plan_i2d); + fftw_destroy_plan(cflip_dialog->cf_plan_d2i); + +} + + +static gint cflip_window_periodchanged(GtkWidget *cfw_period, CFlipDialog *cflip_dialog) { + + const char *str; + + str = gtk_entry_get_text(GTK_ENTRY(cfw_period)); + cflip_dialog->period = atoi(str); + return 0; +} + +static void cflip_window_close(GtkWidget *widget, gint arg1, CFlipDialog *cflip_dialog) { + + if ( cflip_dialog->running ) { + cflip_dialog->running = 0; + } + cf_window_open = 0; + + cflip_finalise(cflip_dialog); + + gtk_widget_destroy(widget); + +} + +static void cflip_update_iterations(CFlipDialog *cflip_dialog) { + + char *text = malloc(40); + snprintf(text, 39, "Iterations performed so far: %i", cflip_dialog->n_iterations); + gtk_label_set_text(GTK_LABEL(cflip_dialog->cf_iterations), text); + free(text); + +} + +void cflip_reset(CFlipDialog *cflip_dialog) { + + reflist_free(cflip_dialog->cf_reflections); + cflip_dialog->cf_reflections = reflist_copy(main_reflist()); + + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + cflip_dialog->n_iterations = 0; + cflip_update_iterations(cflip_dialog); + ReflectionList *reflections = cflip_dialog->cf_reflections; + + unsigned int i; + + for ( i=0; in_reflections; i++ ) { + + double am, ph; + signed int h, k; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + am = reflections->refs[i].amplitude; + ph = (((double)random())/RAND_MAX) * (2*M_PI); + + cflip_dialog->cf_in[k + height*h][0] = am*cos(ph); + cflip_dialog->cf_in[k + height*h][1] = am*sin(ph); + + } + symmetry_symmetrise_array(cflip_dialog->cf_in, width, height, cflip_dialog->cf_symmetry & SYMMETRY_FRIEDEL); + fftw_execute(cflip_dialog->cf_plan_i2d); + + memcpy(cflip_dialog->cf_out, cflip_dialog->cf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + + //dpsynth_update(cflip_dialog->dpsynth, reflections); + +} + +void cflip_initialise(CFlipDialog *cflip_dialog) { + + ReflectionList *reflections = reflist_copy(main_reflist()); + Symmetry symmetry; + cflip_dialog->width = data_width(); + cflip_dialog->height = data_height(); + cflip_dialog->n_iterations = 0; + cflip_dialog->period = 1; + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + symmetry = cflip_dialog->cf_symmetry; + + cflip_dialog->cf_drive_old = fftw_malloc(width * height * sizeof(fftw_complex)); + cflip_dialog->cf_drive = fftw_malloc(width * height * sizeof(fftw_complex)); + cflip_dialog->cf_support = fftw_malloc(width * height * sizeof(unsigned int)); + cflip_dialog->cf_in = fftw_malloc(width * height * sizeof(fftw_complex)); + memset(cflip_dialog->cf_in, 0, width * height * sizeof(fftw_complex)); + cflip_dialog->cf_out = fftw_malloc(width * height * sizeof(fftw_complex)); + + cflip_dialog->cf_plan_i2d = fftw_plan_dft_2d(width, height, cflip_dialog->cf_in, cflip_dialog->cf_drive, FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + cflip_dialog->cf_plan_d2i = fftw_plan_dft_2d(width, height, cflip_dialog->cf_drive, cflip_dialog->cf_in, FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + cflip_dialog->cf_reflections = reflections; + + displaywindow_set_realspace(cflip_dialog->cf_out, DWR_GSF); + //cflip_dialog->dpsynth = dpsynth_open(reflections, "Current Diffraction Pattern", 1); + + cflip_reset(cflip_dialog); + +} + +static gint cflip_window_go(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + + if ( cflip_dialog->running ) { + return 0; + } + cflip_dialog->running = 1; + while ( cflip_dialog->running ) { + cflip_dialog->n_iterations++; + cflip_update_iterations(cflip_dialog); + cflip_iteration(cflip_dialog); + while ( gtk_events_pending() ) gtk_main_iteration(); + } + return 0; + +} + +static gint cflip_window_next(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->n_iterations++; + cflip_update_iterations(cflip_dialog); + cflip_iteration(cflip_dialog); + + return 0; +} + +static gint cflip_window_stop(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->running = 0; + return 0; + +} + +static gint cflip_window_reset(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->running = 0; + cflip_reset(cflip_dialog); + return 0; +} + +static gint cflip_symmetry_changed(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->cf_symmetry = gtk_symmetry_get_symmetry(GTK_SYMMETRY(widget)); + return 0; +} + +GtkWidget *cfw_tangent; +void cflip_tangent_toggled(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->cf_tangent = 1-cflip_dialog->cf_tangent; + +} + +void cflip_dialog_open() { + + GtkWidget *cfw_window; + GtkWidget *cfw_period; + GtkWidget *cfw_period_hbox; + GtkWidget *cfw_threshsup_delta; + GtkWidget *cfw_threshsup_delta_label; + GtkWidget *cfw_threshsup_hbox; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *sym_define; + GtkWidget *cfw_support_label; + GtkWidget *cfw_process_label; + GtkWidget *cfw_period_label; + GtkWidget *cfw_gostop_hbox; + GtkWidget *cfw_tangent; + char *text; + CFlipDialog *cflip_dialog; + + cflip_dialog = malloc(sizeof(CFlipDialog)); + + if ( cf_window_open ) { + return; + } + cf_window_open = 1; + + cflip_dialog->running = 0; + cflip_dialog->cf_tangent = 0; + cflip_dialog->n_iterations = 0; + cflip_dialog->delta = 0; + + cfw_window = gtk_dialog_new_with_buttons("Charge Flipping", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cfw_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + cflip_dialog->cf_iterations = gtk_label_new(""); + text = malloc(40); + snprintf(text, 39, "Iterations performed so far: %i", cflip_dialog->n_iterations); + gtk_label_set_text(GTK_LABEL(cflip_dialog->cf_iterations), text); + free(text); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cflip_dialog->cf_iterations), FALSE, FALSE, 5); + + sym_define = gtk_symmetry_new(2, 2, TRUE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(sym_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(sym_define), "changed", G_CALLBACK(cflip_symmetry_changed), cflip_dialog); + cflip_dialog->cf_symmetry = PLANEGROUP_P1; + + cfw_period_label = gtk_label_new("Iterations to symmetrize:"); + gtk_misc_set_alignment(GTK_MISC(cfw_period_label), 1, 0.5); + cfw_period = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(cfw_period), 5); + gtk_entry_set_text(GTK_ENTRY(cfw_period), "1"); + cfw_period_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(cfw_period_hbox), GTK_WIDGET(cfw_period_label), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_period_hbox), GTK_WIDGET(cfw_period), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_period_hbox), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(cfw_period), "activate", G_CALLBACK(cflip_window_periodchanged), cflip_dialog); + + cfw_support_label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(cfw_support_label), "Threshold"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_support_label), FALSE, FALSE, 10); + + cfw_tangent = gtk_check_button_new_with_label("Coehlo Constraints"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_tangent), FALSE, FALSE, 5); + if ( cflip_dialog->cf_tangent ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_tangent), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_tangent), FALSE); + } + + cfw_process_label = gtk_label_new("Maximum unflipped intensity"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_process_label), FALSE, FALSE, 5); + + cfw_threshsup_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_threshsup_hbox), FALSE, FALSE, 5); + cfw_threshsup_delta_label = gtk_label_new("δ:"); + gtk_box_pack_start(GTK_BOX(cfw_threshsup_hbox), GTK_WIDGET(cfw_threshsup_delta_label), FALSE, FALSE, 5); + cfw_threshsup_delta = gtk_hscale_new_with_range(-5, 5, 0.1); + gtk_scale_set_value_pos(GTK_SCALE(cfw_threshsup_delta), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(cfw_threshsup_hbox), GTK_WIDGET(cfw_threshsup_delta), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(cfw_threshsup_delta), cflip_dialog->delta); + + gtk_widget_set_sensitive(cfw_threshsup_delta, TRUE); + gtk_widget_set_sensitive(cfw_threshsup_delta_label, TRUE); + + cfw_gostop_hbox = gtk_hbox_new(FALSE, 0); + cflip_dialog->cf_go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(cflip_dialog->cf_go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + cflip_dialog->cf_next = gtk_button_new_with_label("Next"); + gtk_button_set_image(GTK_BUTTON(cflip_dialog->cf_next), gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, GTK_ICON_SIZE_BUTTON)); + cflip_dialog->cf_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + cflip_dialog->cf_reset = gtk_button_new_with_label("Reset"); + gtk_button_set_image(GTK_BUTTON(cflip_dialog->cf_reset), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_reset), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_stop), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_next), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_go), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_gostop_hbox), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(cflip_dialog->cf_go), "clicked", G_CALLBACK(cflip_window_go), cflip_dialog); + g_signal_connect(G_OBJECT(cflip_dialog->cf_stop), "clicked", G_CALLBACK(cflip_window_stop), cflip_dialog); + g_signal_connect(G_OBJECT(cflip_dialog->cf_next), "clicked", G_CALLBACK(cflip_window_next), cflip_dialog); + g_signal_connect(G_OBJECT(cflip_dialog->cf_reset), "clicked", G_CALLBACK(cflip_window_reset), cflip_dialog); + g_signal_connect(G_OBJECT(cfw_tangent), "toggled", G_CALLBACK(cflip_tangent_toggled), cflip_dialog); + g_signal_connect(G_OBJECT(cfw_window), "response", G_CALLBACK(cflip_window_close), cflip_dialog); + g_signal_connect(G_OBJECT(cfw_threshsup_delta), "value-changed", G_CALLBACK(cflip_threshsup_deltachanged), cflip_dialog); + g_signal_connect(G_OBJECT(displaywindow_gtkwindow()), "delete-event", G_CALLBACK(cflip_window_stop), cflip_dialog); + + cflip_initialise(cflip_dialog); + displaywindow_forceview(DWV_REALSPACE); + + gtk_widget_show_all(cfw_window); + +} diff --git a/src/cflip.h b/src/cflip.h new file mode 100644 index 0000000..e39e07b --- /dev/null +++ b/src/cflip.h @@ -0,0 +1,28 @@ +/* + * cflip.h + * + * Charge -flipping algorithms + * + * (c) 2006-2007 Thomas White + * (c) 2008 Alexander Eggeman + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CFLIP_H +#define CFLIP_H + +#include "reflist.h" +#include "symmetry.h" +#include "gtk-symmetry.h" +#include "cdm.h" + + +extern void cflip_dialog_open(void); + +#endif /* CFLIP_H */ diff --git a/src/clean.c b/src/clean.c new file mode 100644 index 0000000..1335a2f --- /dev/null +++ b/src/clean.c @@ -0,0 +1,544 @@ +/* + * clean.c + * + * CLEAN Algorithm + * + * (c) 2006-2007 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "displaywindow.h" +#include "main.h" +#include "data.h" +#include "png-file.h" + +static void clean_noise_open(fftw_complex *dirty, double max_am); + +/* ........................ The Maths ........................ */ + + +static double clean_tau = 0.1; + +static void clean_reconvolve(fftw_complex *cleaned, double ax, double ay) { + + signed int h, k; + unsigned int x, y; + fftw_complex *in; + signed int height = data_height(); + signed int width = data_width(); + fftw_plan plan, planr; + fftw_complex *clean_beam_reciprocal; + + in = fftw_malloc(width*height*sizeof(fftw_complex)); + clean_beam_reciprocal = fftw_malloc(width*height*sizeof(fftw_complex)); + for ( x=0; xn_reflections; i++ ) { + signed int h, k; + h = reflections->refs[i].h; + k = reflections->refs[i].k; + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + delta_array[k + height*h][0] = 1; + delta_array[k + height*h][1] = 0; /* Phase = zero */ + } + aperture = fftw_malloc(width*height*sizeof(fftw_complex)); + plan = fftw_plan_dft_2d(width, height, delta_array, aperture, FFTW_BACKWARD, FFTW_MEASURE); + fftw_execute(plan); + fftw_destroy_plan(plan); + fftw_free(delta_array); + + /* Find the maximum *amplitude* in the function */ + aperture_max = 0; + for ( x=0; x aperture_max ) aperture_max = am; + } + } + + /* Normalise the aperture function */ + for ( x=0; x fabs(max_am) ) { + if ( positive ) { + /* See if this subtraction would result in a negative function */ + if ( (cleaned[y+height*x][0] + tau*re ) < 0 ) continue; + } + max_am = am; max_re = re; max_im = im; max_x = x; max_y = y; + } + + } + } + + /* Add tau*peak to clean map */ + cleaned[max_y+height*max_x][0] += tau * max_re; + cleaned[max_y+height*max_x][1] += tau * max_im; + + /* Subtract tau*peak*aperture from the dirty map */ + for ( x=0; x fabs(cutoff) ); + + fftw_free(aperture); + + printf("CL: %i iterations performed\n", npx); + + clean_noise_open(dirty, max_am); + free(dirty); + + max_am = 0; + for ( x=0; x fabs(max_am) ) max_am = am; + } + } + + if ( convolve ) clean_reconvolve(cleaned, ax, ay); + + displaywindow_set_realspace(cleaned, DWR_CLEAN); + displaywindow_forceview(DWV_REALSPACE); + displaywindow_switchview(); + +} + +/* ..................... The Other Stuff ..................... */ + +typedef struct { + GtkWidget *tau; /* Loop gain */ + GtkWidget *cutoff; /* Cutoff */ + GtkWidget *conv; /* Reconvolve? */ + GtkWidget *pos; /* Constrain positive */ + GtkWidget *ax; /* CLEAN beam ax */ + GtkWidget *ay; /* CLEAN beam ay */ +} CleanDialog; + +CleanDialog *clean_dialog = NULL; + +static void clean_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +static void clean_plot_graph(char dim) { + + FILE *gnuplot; + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + clean_gpwrite(gnuplot, "set autoscale\n"); + clean_gpwrite(gnuplot, "unset log\n"); + clean_gpwrite(gnuplot, "unset label\n"); + clean_gpwrite(gnuplot, "set xtic auto\n"); + clean_gpwrite(gnuplot, "set ytic auto\n"); + clean_gpwrite(gnuplot, "set grid\n"); + clean_gpwrite(gnuplot, "set ylabel 'Amplitude' font \"Helvetica,14\"\n"); + if ( dim == 'x' ) clean_gpwrite(gnuplot, "set xlabel 'x / pixels' font \"Helvetica,14\"\n"); + if ( dim == 'y' ) clean_gpwrite(gnuplot, "set xlabel 'y / pixels' font \"Helvetica,14\"\n"); + if ( dim == 'x' ) clean_gpwrite(gnuplot, "set title 'x-component of aperture' font \"Helvetica,14\"\n"); + if ( dim == 'y' ) clean_gpwrite(gnuplot, "set title 'y-component of aperture' font \"Helvetica,14\"\n"); + clean_gpwrite(gnuplot, "plot 'synth2d-aperturefit.dat' with lines\n"); + clean_gpwrite(gnuplot, "replot 'synth2d-aperturefit-peaks.dat' with points\n"); + clean_gpwrite(gnuplot, "a=0.01\n"); + clean_gpwrite(gnuplot, "fit a**2/(a**2+x**2) 'synth2d-aperturefit-peaks.dat' via a\n"); + clean_gpwrite(gnuplot, "set label 'a = %3.5g px',a at graph 0.6, graph 0.7 font \"Helvetica,14\"\n"); + clean_gpwrite(gnuplot, "replot a**2/(a**2+x**2)\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} + +static gint clean_calculate_clean_beam(GtkWidget *cleanw_fit, gpointer data) { + + fftw_complex *aperture; + FILE *fh; + FILE *fh_fit; + unsigned int i, width, height; + double last_am; + + width = data_width(); height = data_height(); + aperture = clean_aperture(main_reflist(), width, height); + + fh = fopen("synth2d-aperturefit.dat", "w"); + fh_fit = fopen("synth2d-aperturefit-peaks.dat", "w"); + last_am = INFINITY; + for ( i=0; i last_am) && (am > next_am) ) fprintf(fh_fit, "%i %f\n", i, am); + + last_am = am; + + } + fclose(fh); + fclose(fh_fit); + clean_plot_graph('x'); + + fh = fopen("synth2d-aperturefit.dat", "w"); + fh_fit = fopen("synth2d-aperturefit-peaks.dat", "w"); + last_am = INFINITY; + for ( i=0; i last_am) && (am > next_am) ) fprintf(fh_fit, "%i %f\n", i, am); + + last_am = am; + + } + fclose(fh); + fclose(fh_fit); + clean_plot_graph('y'); + + fftw_free(aperture); + return 0; + +} + +static gint clean_window_close(GtkWidget *widget, gint response, CleanDialog *this_clean_dialog) { + + if ( response == GTK_RESPONSE_OK ) { + + int n; + double tau; + float cutoff, ax, ay; + const char *tmp; + + n = 1; + tmp = gtk_entry_get_text(GTK_ENTRY(this_clean_dialog->cutoff)); + if ( sscanf(tmp, "%f", &cutoff) != 1 ) n = 0; + tmp = gtk_entry_get_text(GTK_ENTRY(this_clean_dialog->ax)); + if ( sscanf(tmp, "%f", &ax) != 1 ) n = 0; + tmp = gtk_entry_get_text(GTK_ENTRY(this_clean_dialog->ay)); + if ( sscanf(tmp, "%f", &ay) != 1 ) n = 0; + + if ( n == 1 ) { + tau = gtk_range_get_value(GTK_RANGE(this_clean_dialog->tau)); + clean_execute(displaywindow_outarray(), main_reflist(), + tau, + cutoff, + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(this_clean_dialog->pos)), + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(this_clean_dialog->conv)), + ax, ay); + } else { + return 1; + } + + } + + gtk_widget_destroy(widget); + free(clean_dialog); + clean_dialog = NULL; + + return 0; + +} + +void clean_dialog_open(GtkWidget *widget, gpointer data) { + + GtkWidget *clean_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *cleanw_tau_label; + GtkWidget *cleanw_cutoff_label; + GtkWidget *cleanw_fit; + GtkWidget *cleanw_xa_label; + GtkWidget *cleanw_xa_unit_label; + GtkWidget *cleanw_ya_label; + GtkWidget *cleanw_ya_unit_label; + double max_peak = 2000; + char str[16]; + + if ( clean_dialog ) return; + clean_dialog = malloc(sizeof(CleanDialog)); + + clean_window = gtk_dialog_new_with_buttons("CLEAN Image", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(clean_window)->vbox), hbox, FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 5); + table = gtk_table_new(7, 3, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 5); + + cleanw_tau_label = gtk_label_new("Loop Gain:"); + gtk_misc_set_alignment(GTK_MISC(cleanw_tau_label), 1, 0.5); + clean_dialog->tau = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_value_pos(GTK_SCALE(clean_dialog->tau), GTK_POS_RIGHT); + gtk_range_set_value(GTK_RANGE(clean_dialog->tau), clean_tau); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_tau_label, 1, 2, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->tau, 2, 4, 1, 2); + + cleanw_cutoff_label = gtk_label_new("Cutoff:"); + gtk_misc_set_alignment(GTK_MISC(cleanw_cutoff_label), 1, 0.5); + clean_dialog->cutoff = gtk_entry_new(); + max_peak = displaywindow_maxpeak(); + snprintf(str, 15, "%f", max_peak/200); + gtk_entry_set_text(GTK_ENTRY(clean_dialog->cutoff), str); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_cutoff_label, 1, 2, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->cutoff, 2, 4, 2, 3); + + clean_dialog->pos = gtk_check_button_new_with_label("Constrain to be Positive"); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->pos, 1, 4, 3, 4); + + clean_dialog->conv = gtk_check_button_new_with_label("Reconvolve with CLEAN beam"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(clean_dialog->conv), TRUE); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->conv, 1, 4, 4, 5); + + cleanw_xa_label = gtk_label_new("a_x = "); + gtk_label_set_markup(GTK_LABEL(cleanw_xa_label), "ax = "); + gtk_misc_set_alignment(GTK_MISC(cleanw_xa_label), 1, 0.5); + clean_dialog->ax = gtk_entry_new(); + cleanw_xa_unit_label = gtk_label_new("px"); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_xa_label, 1, 2, 5, 6); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->ax, 2, 3, 5, 6); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_xa_unit_label, 3, 4, 5, 6); + + cleanw_ya_label = gtk_label_new("a_y = "); + gtk_label_set_markup(GTK_LABEL(cleanw_ya_label), "ay = "); + gtk_misc_set_alignment(GTK_MISC(cleanw_ya_label), 1, 0.5); + clean_dialog->ay = gtk_entry_new(); + cleanw_ya_unit_label = gtk_label_new("px"); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_ya_label, 1, 2, 6, 7); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->ay, 2, 3, 6, 7); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_ya_unit_label, 3, 4, 6, 7); + + cleanw_fit = gtk_button_new_with_label("Determine CLEAN beam"); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_fit, 1, 4, 7, 8); + g_signal_connect(G_OBJECT(cleanw_fit), "clicked", G_CALLBACK(clean_calculate_clean_beam), clean_dialog); + + g_signal_connect(G_OBJECT(clean_window), "response", G_CALLBACK(clean_window_close), clean_dialog); + gtk_widget_show_all(clean_window); + +} + +void clean_aperture_open(ReflectionList *reflections) { + + GtkWidget *clean_aperture_window; + GdkPixbuf *clean_aperture_pixbuf; + GtkWidget *clean_aperture_pixmap_widget; + fftw_complex *aperture; + signed int width = data_width(); + signed int height = data_height(); + + aperture = clean_aperture(reflections, width, height); + + clean_aperture_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(clean_aperture_window), "Aperture Function"); + clean_aperture_pixbuf = displaywindow_render_pixbuf(aperture, 0.05, data_width(), data_height(), data_gamma(), 1, 1); + fftw_free(aperture); + clean_aperture_pixmap_widget = gtk_image_new_from_pixbuf(clean_aperture_pixbuf); + gtk_container_add(GTK_CONTAINER(clean_aperture_window), clean_aperture_pixmap_widget); + gtk_widget_show_all(clean_aperture_window); + +} + +static void clean_noise_open(fftw_complex *dirty, double max_am) { + + GtkWidget *clean_aperture_window; + GdkPixbuf *clean_aperture_pixbuf; + GtkWidget *clean_aperture_pixmap_widget; + + clean_aperture_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(clean_aperture_window), "CLEAN Residual"); + clean_aperture_pixbuf = displaywindow_render_pixbuf(dirty, max_am, data_width(), data_height(), data_gamma(), 1, 1); + clean_aperture_pixmap_widget = gtk_image_new_from_pixbuf(clean_aperture_pixbuf); + gtk_container_add(GTK_CONTAINER(clean_aperture_window), clean_aperture_pixmap_widget); + gtk_widget_show_all(clean_aperture_window); + +} + diff --git a/src/clean.h b/src/clean.h new file mode 100644 index 0000000..c8dc4af --- /dev/null +++ b/src/clean.h @@ -0,0 +1,27 @@ +/* + * clean.h + * + * CLEAN Algorithm + * + * (c) 2006-2007 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CLEAN_H +#define CLEAN_H + +#include + +#include "reflist.h" + +extern void clean_dialog_open(GtkWidget *widget, gpointer data); +extern void clean_aperture_open(ReflectionList *reflections); + +#endif /* CLEAN_H */ + diff --git a/src/colwheel.c b/src/colwheel.c new file mode 100644 index 0000000..95f551c --- /dev/null +++ b/src/colwheel.c @@ -0,0 +1,153 @@ +/* + * colwheel.c + * + * Colour wheel definition and visualisation + * + * (c) 2006-2007 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "displaywindow.h" + +#define COLWHEEL_WIDTH 256 +#define COLWHEEL_SIZE ((COLWHEEL_WIDTH)-1) +#define COLWHEEL_HALF ((COLWHEEL_WIDTH/2)-1) + +#define rad2deg(a) ((a)*180.0/M_PI) +#define deg2rad(a) ((a)*M_PI/180.0) + +gint colwheel_show(GtkWidget *widget, gpointer data) { + + GtkWidget *colwheel_window; + GdkPixbuf *colwheel_pixbuf; + GtkWidget *colwheel_pixmap_widget; + fftw_complex *colwheel; + + unsigned int x, y; + double am, ph; + + /* This isn't being transformed, so no need to use fftw_malloc() */ + colwheel = malloc(COLWHEEL_SIZE*COLWHEEL_SIZE*sizeof(fftw_complex)); + for ( x=0; x= 360.0 ) ph = 0.0; + d = ph / 60.0; + + hi = floor(d); /* Divide the colour wheel into six sections */ + f = d - hi; /* Distance into the current section of the colour wheel */ + if ( hi == 6 ) hi = 0; + switch ( hi ) { + case 0 : return am; + case 1 : return am; + case 2 : return am*(1.0-f); + case 3 : return 0.0; + case 4 : return am*f/2.0; + case 5 : return am/2.0+am*f/2.0; + default : return 0.0; + } + +} + +double colwheel_green(double am, double ph) { + + double f; + unsigned int hi; + double d; + + ph = rad2deg(ph); /* Convert to degrees */ + ph += 180.0; /* Rotation of colour wheel */ + if ( ph >= 360.0 ) ph = 0.0; + d = ph / 60.0; + + hi = floor(d); /* Divide the colour wheel into six sections */ + f = d - hi; /* Distance into the current section of the colour wheel */ + if ( hi == 6 ) hi = 0; + switch ( hi ) { + case 0 : return am*f/2.0; + case 1 : return am/2.0+am*f/2.0; + case 2 : return am; + case 3 : return am*(1.0-f); + case 4 : return 0.0; + case 5 : return 0.0; + default : return 0.0; + } + +} + +double colwheel_red(double am, double ph) { + + double f; + unsigned int hi; + double d; + + ph = rad2deg(ph); /* Convert to degrees */ + ph += 180.0; /* Rotation of colour wheel */ + if ( ph >= 360.0 ) ph = 0.0; + d = ph / 60.0; + + hi = floor(d); /* Divide the colour wheel into six sections */ + f = d - hi; /* Distance into the current section of the colour wheel */ + if ( hi == 6 ) hi = 0; + switch ( hi ) { + case 0 : return 0.0; + case 1 : return 0.0; + case 2 : return 0.0; + case 3 : return am*f; + case 4 : return am; + case 5 : return am*(1.0-f); + default : return 0.0; + } + +} diff --git a/src/colwheel.h b/src/colwheel.h new file mode 100644 index 0000000..d925477 --- /dev/null +++ b/src/colwheel.h @@ -0,0 +1,26 @@ +/* + * colwheel.h + * + * Colour wheel definition and visualisation + * + * (c) 2006-2008 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef COLWHEEL_H +#define COLWHEEL_H + +#include + +extern gint colwheel_show(GtkWidget *widget, gpointer data); +extern double colwheel_red(double am, double ph); +extern double colwheel_green(double am, double ph); +extern double colwheel_blue(double am, double ph); + +#endif /* COLWHEEL_H */ diff --git a/src/contourise.c b/src/contourise.c new file mode 100644 index 0000000..56e10b8 --- /dev/null +++ b/src/contourise.c @@ -0,0 +1,441 @@ +/* + * contourise.c + * + * Draw contour maps + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "model.h" +#include "data.h" +#include "renderer.h" + +typedef struct { + GtkWidget *rad_mod; + GtkWidget *rad_mod_sign; + GtkWidget *rad_real; + GtkWidget *rad_imag; + GtkWidget *n_contours; + int nx; + int ny; +} ContourDialog; + +typedef enum { + VALUES_NONE, + VALUES_MODULUS, + VALUES_MODULUS_SIGN, + VALUES_REAL, + VALUES_IMAGINARY +} ContourValues; + +static GtkWidget *contourise_dialog = NULL; + +static void griwrite(FILE *gri, const char *string) +{ + fwrite(string, strlen(string), 1, gri); +} + +static void contourise_map(fftw_complex *out, unsigned int width, + unsigned int height, ContourValues vals, + int nx, int ny, int ncont) +{ + FILE *gri; + char tmp[1024]; + double maxval; + int model_marks; + int xn, yn; + pid_t pid; + int status; + size_t width_n, height_n; + double gamma; + double aspect; + int landscape; /* 1=landscape, 0=portrait */ + int fit_x; /* 1=fit the x-axis to the page, 0=fit the y-axis */ + ComplexArray cxar; + double xsize, ysize, scale; + + gamma = data_gamma(); + + if ( ( displaywindow_mode() == DWV_MODEL ) + || ( displaywindow_mode() == DWV_DIFFERENCE ) + || ( displaywindow_mode() == DWV_EXITWAVE ) + || ( displaywindow_mode() == DWV_REFSYN ) ) { + model_marks = 1; + } else { + model_marks = 0; + } + + width_n = renderer_width(width, height, gamma, nx, ny); + height_n = renderer_height(width, height, gamma, nx, ny); + + maxval = displaywindow_max(); + + gri = popen("gri -b -no_startup_message", "w"); + if ( !gri ) { + fprintf(stderr, "Couldn't invoke gri. Please check your PATH."); + return; + } + + /* Initialise */ + griwrite(gri, "set postscript filename synth2d.ps\n"); + griwrite(gri, "set page size A4\n"); + snprintf(tmp, 1023, "set x grid 0 1 /%i\n", width_n); + griwrite(gri, tmp); + snprintf(tmp, 1023, "set y grid 0 1 /%i\n", height_n); + griwrite(gri, tmp); + + /* Figure out the size */ + aspect = (double)height_n/width_n; + if ( width_n > height_n ) { + landscape = 1; + if ( aspect < (19.0/27.7) ) { + fit_x = 1; + } else { + fit_x = 0; + } + } else { + landscape = 0; + if ( aspect < (27.7/19.0) ) { + fit_x = 1; + } else { + fit_x = 0; + } + } + if ( landscape ) { + if ( fit_x ) { + xsize = 27.7; + ysize = 27.7*aspect; + scale = 27.7/width_n; + } else { + xsize = 19.0/aspect; + ysize = 19.0; + scale = 19.0/height_n; + } + } else { + if ( fit_x ) { + xsize = 19.0; + ysize = 19.0*aspect; + scale = 19.0/width_n; + } else { + xsize = 27.7/aspect; + ysize = 27.7; + scale = 27.7/height_n; + } + } + printf("CO: aspect=%f => selected ", aspect); + if ( landscape ) { + printf("landscape, "); + griwrite(gri, "set page landscape\n"); + } else { + printf("portrait, "); + griwrite(gri, "set page portrait\n"); + } + if ( fit_x ) { + printf("fitting x, "); + } else { + printf("fitting y, "); + } + snprintf(tmp, 1023, "set x size %f\n", xsize); + griwrite(gri, tmp); + snprintf(tmp, 1023, "set y size %f\n", ysize); + griwrite(gri, tmp); + printf("width=%f height=%f\n", xsize, ysize); + + griwrite(gri, "set x margin 1\n"); /* cm */ + griwrite(gri, "set y margin 1\n"); /* cm */ + griwrite(gri, "set axes style none\n"); + griwrite(gri, "set line width 3.0\n"); + if ( landscape ) { + if ( fit_x ) { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+27.7, 1.0+27.7*aspect); + griwrite(gri, tmp); + } else { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+19.0/aspect, 1.0+19.0); + griwrite(gri, tmp); + } + } else { + if ( fit_x ) { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+19.0, 1.0+19.0*aspect); + griwrite(gri, tmp); + } else { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+27.7/aspect, 1.0+27.7); + griwrite(gri, tmp); + } + } + + /* Read the data and plot contours */ + griwrite(gri, "read grid data\n"); + + /* Give it the data to read... */ + cxar = renderer_draw(out, width, height, gamma, nx, ny); + for ( yn=height_n-1; yn>=0; yn-- ) { + + int first = 1; + + for ( xn=0; xnn_atoms; i++ ) { + + double p, q; + int xc, yc; + + p = model->atoms[i].x; + q = model->atoms[i].y; + + for ( xc=0; xcrad_mod)) ) + vals = VALUES_MODULUS; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + cd->rad_mod_sign)) ) + vals = VALUES_MODULUS_SIGN; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + cd->rad_real)) ) + vals = VALUES_REAL; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + cd->rad_imag)) ) + vals = VALUES_IMAGINARY; + if ( vals == VALUES_NONE ) error = 1; + + ncont_s = gtk_entry_get_text(GTK_ENTRY(cd->n_contours)); + if ( sscanf(ncont_s, "%i", &ncont) != 1 ) { + error_report("Invalid number of contours"); + error = 1; + } else { + if ( ncont < 1 ) { + error_report("Invalid number of contours"); + error = 1; + } + } + + if ( error == 0 ) { + contourise_map(displaywindow_outarray(), + data_width(), data_height(), + vals, + cd->nx, cd->ny, ncont); + } + + } + + if ( error == 0 ) { + gtk_widget_destroy(window); + contourise_dialog = NULL; + free(cd); + } + + return 0; +} + +void contourise_dialog_open(int nx, int ny) +{ + ContourDialog *cd; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *text; + GtkWidget *ncont_hbox; + + if ( contourise_dialog ) { + return; + } + + contourise_dialog = gtk_dialog_new_with_buttons("Contour Map", + GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + cd = malloc(sizeof(ContourDialog)); + if ( !cd ) return; + cd->nx = nx; /* Number of unit cells */ + cd->ny = ny; /* Store these for later */ + + /* Structure */ + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(contourise_dialog)->vbox), + GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 5); + + text = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(text), + "" + "What do you want to plot?"); + gtk_label_set_line_wrap(GTK_LABEL(text), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, FALSE, 5); + + cd->rad_mod_sign = gtk_radio_button_new_with_label(NULL, + "Modulus with sign: √[Re(V)² + Im(V)²] × sign[Re(V)]"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_mod_sign, FALSE, FALSE, 2); + cd->rad_mod = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(cd->rad_mod_sign), + "Modulus alone: √[Re(V)² + Im(V)²]"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_mod, FALSE, FALSE, 2); + cd->rad_real = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(cd->rad_mod_sign), + "Real part: Re(V)"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_real, FALSE, FALSE, 2); + cd->rad_imag = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(cd->rad_mod_sign), + "Imaginary part: Im(V)"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_imag, FALSE, FALSE, 2); + + text = gtk_label_new("Number of contours:"); + gtk_misc_set_alignment(GTK_MISC(text), 1.0, 0.5); + ncont_hbox = gtk_hbox_new(FALSE, 2); + gtk_box_pack_start(GTK_BOX(ncont_hbox), text, FALSE, TRUE, 2); + gtk_box_pack_end(GTK_BOX(vbox), ncont_hbox, FALSE, FALSE, 2); + cd->n_contours = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(cd->n_contours), 4); + gtk_box_pack_start(GTK_BOX(ncont_hbox), cd->n_contours, FALSE, TRUE, 2); + gtk_entry_set_text(GTK_ENTRY(cd->n_contours), "20"); + + g_signal_connect(G_OBJECT(contourise_dialog), "response", + G_CALLBACK(contourise_dialog_close), cd); + gtk_widget_show_all(contourise_dialog); +} diff --git a/src/contourise.h b/src/contourise.h new file mode 100644 index 0000000..9d81922 --- /dev/null +++ b/src/contourise.h @@ -0,0 +1,21 @@ +/* + * contourise.h + * + * Draw contour maps + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CONTOURISE_H +#define CONTOURISE_H + +extern void contourise_dialog_open(int nx, int ny); + +#endif /* CONTOURISE_H */ diff --git a/src/correspondence.c b/src/correspondence.c new file mode 100644 index 0000000..e745d42 --- /dev/null +++ b/src/correspondence.c @@ -0,0 +1,73 @@ +/* + * correspondence.c + * + * Plot of calculated against measured structure factors + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "main.h" +#include "statistics.h" +#include "displaywindow.h" +#include "model.h" + +static void correspondence_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void correspondence_show() { + + FILE *fh; + unsigned int i; + FILE *gnuplot; + ReflectionList *reflections; + ReflectionList *model_reflections; + double scale; + + reflections = main_reflist(); + model_reflections = model_calculate_f(reflections, NULL, 69); + + scale = stat_scale(reflections, model_reflections); + + fh = fopen("synth2d-correspondence.dat", "w"); + for ( i=1; in_reflections; i++ ) { + fprintf(fh, "%f %f\n", scale*model_reflections->refs[i].amplitude, reflections->refs[i].amplitude); + } + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + correspondence_gpwrite(gnuplot, "set autoscale\n"); + correspondence_gpwrite(gnuplot, "unset log\n"); + correspondence_gpwrite(gnuplot, "unset label\n"); + correspondence_gpwrite(gnuplot, "set xtic auto\n"); + correspondence_gpwrite(gnuplot, "set ytic auto\n"); + correspondence_gpwrite(gnuplot, "set grid\n"); + correspondence_gpwrite(gnuplot, "set ylabel '|Fobs|' font \"Helvetica,10\"\n"); + correspondence_gpwrite(gnuplot, "set xlabel '|Fcalc|' font \"Helvetica,10\"\n"); + correspondence_gpwrite(gnuplot, "set title 'Correspondence Plot' font \"Helvetica,10\"\n"); + correspondence_gpwrite(gnuplot, "plot [0:] [0:] 'synth2d-correspondence.dat'\n"); + correspondence_gpwrite(gnuplot, "replot\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} diff --git a/src/correspondence.h b/src/correspondence.h new file mode 100644 index 0000000..fdc4622 --- /dev/null +++ b/src/correspondence.h @@ -0,0 +1,20 @@ +/* + * correspondence.h + * + * Plot of calculated against measured structure factors + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CORRESPONDENCE_H +#define CORRESPONDENCE_H + +extern void correspondence_show(void); + +#endif /* CORRESPONDENCE_H */ diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..5a2829f --- /dev/null +++ b/src/data.c @@ -0,0 +1,162 @@ +/* + * data.c + * + * Handle the input data + * + * (c) 2006-2008 Thomas White + * + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "reflist.h" +#include "displaywindow.h" + +signed int data_width_hidden = 512; +signed int data_height_hidden = 512; +float data_a_hidden = 1; +float data_b_hidden = 1; +float data_c_hidden = 1; +double data_gamma_hidden = M_PI_2; +ReflectionList *data_reflections = NULL; +int data_image_scale = 500; + +unsigned int data_width() { return data_width_hidden; } +unsigned int data_height() { return data_height_hidden; } +double data_a() { return data_a_hidden; } +double data_b() { return data_b_hidden; } +double data_c() { return data_c_hidden; } +double data_gamma() { return data_gamma_hidden; } +unsigned int data_get_image_scale() { return data_image_scale; } + +static void data_calculatesize() { + data_width_hidden = data_a_hidden*data_image_scale; + data_height_hidden = data_b_hidden*data_image_scale; +} + +void data_dividecell(unsigned int na, unsigned int nb, unsigned int nc) { + data_a_hidden /= na; + data_b_hidden /= nb; + data_c_hidden /= nc; + data_calculatesize(); +} + +int data_read(const char *filename) { + + FILE *fh; + char *line; + char *rval; + unsigned int whoops = 0; + float gamma_angle = 90; + signed int laue = 0; + + if ( data_reflections ) { + free(data_reflections); + } + data_reflections = reflist_new(); + + fh = fopen(filename, "r"); + if ( !fh ) { + fprintf(stderr, "DA: Couldn't open file %s\n", filename); + return 1; + } + line = malloc(1024); + rval = "hello"; + while ( rval ) { + + signed int h, k, l; + float am, intensity; + float ph = 0; + int res; + + rval = fgets(line, 1023, fh); + if ( ferror(fh) && !feof(fh) ) { + whoops = 1; + break; + } + if ( strlen(line) > 1 ) { + if ( line[strlen(line)-1] == '\n' ) { + line[strlen(line)-1] = '\0'; /* Cut off trailing newline. */ + } + } else { + line[0] = '\0'; + } + + res = sscanf(line, "%3i %3i %3i %f %f", &h, &k, &l, &intensity, &ph); + if ( intensity > 0.0) am = sqrt(intensity); + if ( (res < 4) && gamma_angle ) { + sscanf(line, "angle %f", &gamma_angle); + sscanf(line, "a %f", &data_a_hidden); + sscanf(line, "b %f", &data_b_hidden); + sscanf(line, "c %f", &data_c_hidden); + sscanf(line, "scale %i", &data_image_scale); + } else { + if ( res == 5 ) { + + if ( (h==0) && (k==0) && (l==0) ) { + printf("DA: Data contains a zero-order beam value.\n"); + } else if ( (h==0) && (k==0) ) { + printf("DA: Data may contain a zero-order beam value.\n"); + } + if ( (l != laue) && (laue != 999999) ) { + //printf("DA: WARNING: Data is from more than one Laue zone\n"); + } else { + laue = l; + reflist_addref_am_ph(data_reflections, h, k, l, am, ph); + } + } else { + if ( (h==0) && (k==0) && (l==0) ) { + printf("DA: Data contains a zero-order beam value.\n"); + } else if ( (h==0) && (k==0) ) { + printf("DA: Data may contain a zero-order beam value.\n"); + } + if ( (l != laue) && (laue != 999999) ) { + //printf("DA: WARNING: Data is from more than one Laue zone\n"); + } else { + laue = l; + reflist_addref_am(data_reflections, h, k, l, am); + } + } + } + + line[0] = '\0'; /* Prevent this line from being parsed twice. */ + + } + + fclose(fh); + free(line); + + if ( whoops ) { + fprintf(stderr, "Couldn't read file %s\n", filename); + return 1; + } + + data_calculatesize(); + printf("a=%f, b=%f, width=%i, height=%i, gamma=%f\n", data_a_hidden, data_b_hidden, data_width_hidden, data_height_hidden, gamma_angle); + data_gamma_hidden = (gamma_angle/180)*M_PI; + + return 0; + +} + +ReflectionList *data_getreflections() { + return data_reflections; +} + +void data_free() { + + reflist_free(data_reflections); + +} diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..94b6d72 --- /dev/null +++ b/src/data.h @@ -0,0 +1,34 @@ +/* + * data.h + * + * Handle the input data + * + * (c) 2006-2008 Thomas White + * + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef DATA_H +#define DATA_H + +#include "reflist.h" + +extern double data_a(); +extern double data_b(); +extern double data_c(); +extern double data_gamma(); +extern unsigned int data_width(void); +extern unsigned int data_height(void); +unsigned int data_get_image_scale(void); +extern void data_dividecell(unsigned int na, unsigned int nb, unsigned int nc); +extern int data_read(const char *filename); +extern ReflectionList *data_getreflections(void); +extern void data_free(); + +#endif /* DATA_H */ + diff --git a/src/displaywindow.c b/src/displaywindow.c new file mode 100644 index 0000000..a4ceec0 --- /dev/null +++ b/src/displaywindow.c @@ -0,0 +1,1609 @@ +/* + * displaywindow.c + * + * The main display window + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "data.h" +#include "png-file.h" +#include "reflist.h" +#include "main.h" +#include "normalise.h" +#include "contourise.h" +#include "gtk-symmetry.h" +#include "gsf.h" +#include "cflip.h" +#include "cdm.h" +#include "clean.h" +#include "geometry.h" +#include "luzzatti.h" +#include "correspondence.h" +#include "amplitude-r.h" +#include "superlattice.h" +#include "refine.h" +#include "elser.h" +#include "colwheel.h" +#include "model.h" +#include "model-display.h" +#include "dpsynth.h" +#include "renderer.h" + +typedef struct { + + GtkWidget *window; + GtkWidget *xcells; + GtkWidget *ycells; + +} UnitCellsWindow; + +GtkUIManager *displaywindow_ui; +GtkActionGroup *displaywindow_action_group; +GtkWidget *displaywindow_window; +GtkWidget *displaywindow_bigvbox; +GtkWidget *displaywindow_image_widget = NULL; +GdkPixbuf *displaywindow_pixbuf = NULL; +double displaywindow_brightness = 700; +gint displaywindow_autobrightness = FALSE; +GtkWidget *displaywindow_status_bar; + +int displaywindow_model_names = TRUE; +int displaywindow_model_heights = FALSE; + +fftw_complex *displaywindow_in = NULL; +fftw_complex *displaywindow_out = NULL; +fftw_plan displaywindow_plan_i2o; +fftw_plan displaywindow_plan_o2i; +unsigned int displaywindow_width = 0; +unsigned int displaywindow_height = 0; + +fftw_complex *displaywindow_realspace = NULL; +DisplayWindowRealSpace displaywindow_rs = DWR_NONE; +int displaywindow_view = DWV_PATTERSON; + +int displaywindow_xc = 1; +int displaywindow_yc = 1; /* Size of synthesis in unit cells */ +UnitCellsWindow *displaywindow_unitcellswindow = NULL; + +static void displaywindow_switchview_real(unsigned int was_rescale); + +GtkWidget *displaywindow_gtkwindow() { return displaywindow_window; } + +int displaywindow_mode() { return displaywindow_view; } + +double displaywindow_max() { return displaywindow_brightness; } + +void error_report(const char *message) { + + GtkWidget *window; + + window = gtk_message_dialog_new(GTK_WINDOW(displaywindow_window), GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, message); + + g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window); + gtk_widget_show(window); + +} + +static void displaywindow_about() { + + GtkWidget *window; + + const gchar *authors[] = { + "Thomas White ", + NULL + }; + + window = gtk_about_dialog_new(); + + gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(window), PACKAGE_NAME); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(window), PACKAGE_VERSION); + gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(window), "(c) 2006-2007 Thomas White "); + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(window), "Crystallographic Fourier Synthesis Utility"); + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(window), "(c) 2006-2007 Thomas White \n" + "\n" + "Research funded by:\n" + "The Engineering and Physical Sciences Research Council\n" + "FEI Electron Optics B.V."); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(window), "http://www-hrem.msm.cam.ac.uk/"); + gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(window), authors); + + g_signal_connect(window, "response", G_CALLBACK(gtk_widget_destroy), NULL); + + gtk_widget_show_all(window); + +} + +static void displaywindow_close() { + gtk_exit(0); +} + +void displaywindow_disablephasegrabbing() { + GtkWidget *disable; + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/tools/grabphases"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); +} + +void displaywindow_enablephasegrabbing() { + GtkWidget *enable; + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/tools/grabphases"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); +} + +static gint displaywindow_changeview(GtkWidget *widget, GtkRadioAction *action) { + + displaywindow_view = gtk_radio_action_get_current_value(action); + displaywindow_switchview(); + + switch ( displaywindow_view ) { + case DWV_PATTERSON: displaywindow_statusbar("Patterson transform"); break; + case DWV_PATTERSONE: displaywindow_statusbar("E²-1 Patterson transform"); break; + case DWV_KNOWNPHASE: displaywindow_statusbar("Known phases"); break; + case DWV_CALCPHASE: displaywindow_statusbar("Calculated phases"); break; + case DWV_REALSPACE: displaywindow_statusbar("Real space estimate"); break; + case DWV_MODEL: displaywindow_statusbar("Atomic model"); break; + case DWV_DIFFERENCE: displaywindow_statusbar("Fourier difference synthesis"); break; + case DWV_REFSYN: displaywindow_statusbar("Fourier refinement synthesis"); break; + case DWV_SIMPATT: displaywindow_statusbar("Simulated Patterson transform"); break; + case DWV_SIMFOLZPATT: displaywindow_statusbar("Simulated FOLZ Patterson transform"); break; + case DWV_EXITWAVE: displaywindow_statusbar("Simulated exit wave"); break; + } + + if ( displaywindow_view == DWV_REALSPACE ) { + displaywindow_enablephasegrabbing(); + } else { + displaywindow_disablephasegrabbing(); + } + + /* Saving images from the model display is not allowed. Use Save PDF in this case... */ + if ( displaywindow_view == DWV_MODEL ) { + GtkWidget *disable; + #if HAVE_CAIRO + GtkWidget *enable; + #endif + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/save"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + #if HAVE_CAIRO + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/savepdf"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); + #else + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/savepdf"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + #endif + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/contour"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + } else { + GtkWidget *enable; + GtkWidget *disable; + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/save"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/savepdf"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/contour"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); + } + + return 0; + +} + +void displaywindow_forceview(int new_view) { + + GtkAction *action; + + if ( new_view == displaywindow_view ) return; /* Nothing to do */ + + action = gtk_action_group_get_action(displaywindow_action_group, "ExitWaveAction"); + #ifdef HAVE_GTK_TEN + gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), new_view); + #endif /* HAVE_GTK_TEN */ + +} + +static gint displaywindow_wilsonplot() { + main_wilsonplot(); + return 0; +} + +static gint displaywindow_falloffplot() { + main_falloffplot(); + return 0; +} + +static gint displaywindow_dpsynth() { + main_dpsynth(); + return 0; +} + +static gint displaywindow_simdp() { + dpsynth_simdp_open(model_calculate_f(main_reflist(), NULL, 0)); + return 0; +} + +static gint displaywindow_argand() { + main_argand(); + return 0; +} + +static void displaywindow_addui_callback(GtkUIManager *ui, GtkWidget *widget, GtkContainer *container) { + + gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0); + + /* Enable overflow menu if this is a toolbar */ + if ( GTK_IS_TOOLBAR(widget) ) { + gtk_toolbar_set_show_arrow(GTK_TOOLBAR(widget), TRUE); + } + +} + +unsigned int symmetrise_window_open = 0; +static gint displaywindow_symmetrise_response(GtkWidget *widget, gint response, GtkWidget *symmetry) { + + if ( response == GTK_RESPONSE_OK ) { + main_symmetrise(gtk_symmetry_get_symmetry(GTK_SYMMETRY(symmetry))); + } + + symmetrise_window_open = 0; + gtk_widget_destroy(widget); + + return 0; + +} + +static void displaywindow_symmetrise() { + + GtkWidget *symmetrise_window; + GtkWidget *symmetry; + + if ( symmetrise_window_open ) { + return; + } + symmetrise_window_open = 1; + + symmetry = gtk_symmetry_new(2, 2, TRUE); + + symmetrise_window = gtk_dialog_new_with_buttons("Symmetrise", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(symmetrise_window)->vbox), symmetry, FALSE, FALSE, 7); + + g_signal_connect(G_OBJECT(symmetrise_window), "response", G_CALLBACK(displaywindow_symmetrise_response), symmetry); + + gtk_widget_show_all(symmetrise_window); + +} + +static gint displaywindow_closedown(GtkWidget *widget, gpointer data) { + + fftw_destroy_plan(displaywindow_plan_i2o); + fftw_destroy_plan(displaywindow_plan_o2i); + fftw_free(displaywindow_in); + fftw_free(displaywindow_out); + + displaywindow_in = NULL; + displaywindow_out = NULL; + + return 0; + +} + +void displaywindow_statusbar(const char *message) { + gtk_statusbar_pop(GTK_STATUSBAR(displaywindow_status_bar), 0); + gtk_statusbar_push(GTK_STATUSBAR(displaywindow_status_bar), 0, message); +} + +static void displaywindow_free_data(guchar *image_data, gpointer data) { + free(image_data); +} + +GdkPixbuf *displaywindow_render_pixbuf(fftw_complex *out, double brightness, size_t width, size_t height, double gamma, int nx, int ny) { + + GdkPixbuf *pixbuf; + guchar *data; + int width_n, height_n; /* Size of the image without border */ + int width_i, height_i; /* Size of the image, including border */ + int xn, yn; /* Iterators */ + int bx, by; /* Border width in x and y */ + ComplexArray cxar; + size_t data_len; + + bx = 20; by = 20; + + width_n = (int)renderer_width(width, height, gamma, nx, ny); + height_n = (int)renderer_height(width, height, gamma, nx, ny); + width_i = width_n + 2*bx; + height_i = height_n + 2*by; + + data_len = height_i*width_i*3*sizeof(guchar); + data = malloc(data_len); + memset(data, 0xE5, data_len); + + cxar = renderer_draw(out, width, height, gamma, nx, ny); + for ( yn=0; yn 1.0 ) am = 1.0; + + data[3*((xn+bx)+width_i*(height_i-1-(yn+by))) + 0] = + (guchar)(255.0*colwheel_red(am, ph)); + data[3*((xn+bx)+width_i*(height_i-1-(yn+by))) + 1] = + (guchar)(255.0*colwheel_green(am, ph)); + data[3*((xn+bx)+width_i*(height_i-1-(yn+by))) + 2] = + (guchar)(255.0*colwheel_blue(am, ph)); + + } + } + + free(cxar.re); + free(cxar.im); + + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, FALSE, 8, width_i, height_i, 3*width_i, + (GdkPixbufDestroyNotify)displaywindow_free_data, NULL); + + return pixbuf; + +} + +static void displaywindow_update() { + + if ( displaywindow_pixbuf ) { + gdk_pixbuf_unref(displaywindow_pixbuf); + } + + displaywindow_pixbuf = displaywindow_render_pixbuf(displaywindow_outarray(), + displaywindow_brightness, data_width(), data_height(), data_gamma(), displaywindow_xc, displaywindow_yc); + + if ( displaywindow_image_widget ) { + g_object_set(G_OBJECT(displaywindow_image_widget), "pixbuf", displaywindow_pixbuf, NULL); + } else { + displaywindow_image_widget = gtk_image_new_from_pixbuf(displaywindow_pixbuf); + gtk_box_pack_end(GTK_BOX(displaywindow_bigvbox), GTK_WIDGET(displaywindow_image_widget), 0, FALSE, FALSE); + gtk_widget_show(displaywindow_image_widget); + } + +} + +void displaywindow_blank() { + + fftw_complex *blank; + + if ( displaywindow_pixbuf ) { + gdk_pixbuf_unref(displaywindow_pixbuf); + } + if ( displaywindow_image_widget ) { + gtk_widget_destroy(displaywindow_image_widget); + } + + blank = malloc(data_width()*data_height()*sizeof(fftw_complex)); + bzero(blank, data_width()*data_height()*sizeof(fftw_complex)); + displaywindow_pixbuf = displaywindow_render_pixbuf(blank, 1000000, data_width(), data_height(), data_gamma(), + displaywindow_xc, displaywindow_yc); + displaywindow_image_widget = gtk_image_new_from_pixbuf(displaywindow_pixbuf); + gtk_box_pack_end(GTK_BOX(displaywindow_bigvbox), GTK_WIDGET(displaywindow_image_widget), 0, FALSE, FALSE); + gtk_widget_show(displaywindow_image_widget); + + displaywindow_realspace = blank; + +} + +static unsigned int displaywindow_brightnesswindow_open = 0; + +static gint displaywindow_brightnesswindow_activate(GtkWidget *brightness_entry, GtkWidget *brightness_window) { + + float brightness_new; + const char *brightness_string = gtk_entry_get_text(GTK_ENTRY(brightness_entry)); + if ( sscanf(brightness_string, "%f", &brightness_new) == 1 ) { + + displaywindow_brightnesswindow_open = 0; + gtk_widget_destroy(brightness_window); + if ( displaywindow_brightness != brightness_new ) { + displaywindow_brightness = brightness_new; + displaywindow_update(); + //displaywindow_statusbar("Display brightness changed"); + if ( displaywindow_brightness < 0.000000001 ) { + error_report("Watch out for rounding errors!"); + } + } + + } + + return FALSE; + +} + +static gint displaywindow_brightnesswindow_response(GtkWidget *brightness_window, gint response, GtkWidget *brightness_entry) { + + if ( response == GTK_RESPONSE_OK ) { + displaywindow_brightnesswindow_activate(brightness_entry, brightness_window); + } + + displaywindow_brightnesswindow_open = 0; + gtk_widget_destroy(brightness_window); + + return FALSE; + +} + +static void displaywindow_brightness_open() { + + GtkWidget *brightness_window; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *brightness_label; + GtkWidget *brightness_entry; + char *brightness_string; + + if ( displaywindow_brightnesswindow_open ) { + return; + } + displaywindow_brightnesswindow_open = 1; + + brightness_entry = gtk_entry_new(); + brightness_window = gtk_dialog_new_with_buttons("Brightness", GTK_WINDOW(displaywindow_window), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(brightness_window)->vbox), GTK_WIDGET(vbox), FALSE, FALSE, 7); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + + brightness_label = gtk_label_new("Full brightness = "); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(brightness_label), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(brightness_entry), FALSE, FALSE, 5); + + brightness_string = malloc(32); + snprintf(brightness_string, 31, "%f", displaywindow_brightness); + gtk_entry_set_text(GTK_ENTRY(brightness_entry), brightness_string); + free(brightness_string); + + g_signal_connect(G_OBJECT(brightness_window), "response", G_CALLBACK(displaywindow_brightnesswindow_response), brightness_entry); + g_signal_connect(G_OBJECT(brightness_entry), "activate", G_CALLBACK(displaywindow_brightnesswindow_activate), brightness_window); + + gtk_widget_show_all(brightness_window); + +} + +gint displaywindow_brightness_auto(GtkWidget *widget, gpointer data) { + displaywindow_brightness = displaywindow_maxpeak(); + displaywindow_switchview_real(TRUE); + return 0; +} + +static gint displaywindow_brightness_autotoggle(GtkWidget *widget, gpointer data) { + displaywindow_autobrightness = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(widget)); + if ( displaywindow_autobrightness) displaywindow_brightness_auto(NULL, NULL); + return 0; +} + +static gint displaywindow_model_names_toggle(GtkWidget *widget, gpointer data) { + displaywindow_model_names = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(widget)); + displaywindow_switchview(); + return 0; +} + +static gint displaywindow_model_heights_toggle(GtkWidget *widget, gpointer data) { + displaywindow_model_heights = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(widget)); + displaywindow_switchview(); + return 0; +} + +/* Display the Patterson transform based on the given reflections */ +void displaywindow_show_patterson(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + /* Amplitudes squared since this is a Patterson transform */ + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * reflections->refs[i].amplitude; + /* Imaginary component is zero */ + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "PattersonAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +/* for ( h=0; hn_reflections; i++ ) { + if ( reflections->refs[i].amplitude > 0 ) { + sigma += reflections->refs[i].amplitude * reflections->refs[i].amplitude; + n++; + } + } + sigma = sigma / n; + + for ( i=1; in_reflections; i++ ) { + + double Esq; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( (abs(h) > displaywindow_width/2) || (abs(k) > displaywindow_height/2) ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + if ( reflections->refs[i].amplitude != 0 ) { + Esq = (reflections->refs[i].amplitude * reflections->refs[i].amplitude) / sigma; + displaywindow_in[k + displaywindow_height*h][0] = Esq - 1; + /* Imaginary component is zero */ + dev += Esq - 1; + } + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "PattersonEAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Display the known phase synthesis based on the given reflections */ +void displaywindow_show_knownphases(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + unsigned int n = 0; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + if ( reflections->refs[i].phase_known_set ) { + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + n++; + //printf("%3i %3i = %f %f\n", reflections->refs[i].h, reflections->refs[i].k, reflections->refs[i].amplitude, reflections->refs[i].phase_known); + } else { + /* else don't include it */ + //printf("%3i %3i not included\n", reflections->refs[i].h, reflections->refs[i].k); + } + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "KnownPhaseAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Display the calculated phase synthesis based on the given reflections */ +void displaywindow_show_calcphases(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + unsigned int n = 0; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( reflections->refs[i].phase_calc_set ) { + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_calc); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_calc); + n++; + + } /* else don't include it */ + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "CalcPhaseAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Display the current "real space estimate" */ +static void displaywindow_show_realspace() { + + if ( displaywindow_realspace ) { + /* displaywindow_update() knows to look in displaywindow_realspace instead of displaywindow_out */ + displaywindow_update(); + } else { + displaywindow_blank(); + } + +} + +void displaywindow_set_realspace(fftw_complex *out, DisplayWindowRealSpace rs) { + //if ( rs != DWR_NONE ) fftw_free(displaywindow_realspace); + displaywindow_realspace = out; + displaywindow_rs = rs; +} + +/* Show the current real-space atomic structure model */ +static void displaywindow_show_model() { + + GtkAction *action; + + #ifdef HAVE_CAIRO + if ( displaywindow_pixbuf ) gdk_pixbuf_unref(displaywindow_pixbuf); + + displaywindow_pixbuf = model_display_render_pixbuf(model_get_current(), data_width(), data_height(), data_gamma(), + displaywindow_model_names, displaywindow_model_heights, displaywindow_xc, displaywindow_yc); + if ( !displaywindow_pixbuf ) return; + + if ( displaywindow_image_widget ) { + g_object_set(G_OBJECT(displaywindow_image_widget), "pixbuf", displaywindow_pixbuf, NULL); + } else { + displaywindow_image_widget = gtk_image_new_from_pixbuf(displaywindow_pixbuf); + gtk_box_pack_end(GTK_BOX(displaywindow_bigvbox), GTK_WIDGET(displaywindow_image_widget), 0, FALSE, FALSE); + gtk_widget_show(displaywindow_image_widget); + } + #else + displaywindow_blank(); + #endif + + action = gtk_action_group_get_action(displaywindow_action_group, "ModelAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Show the simulated Patterson based on the structure model */ +static void displaywindow_show_simpatt() { + + signed int h, k, l; + unsigned int i; + GtkAction *action; + ReflectionList *model_reflections; + + for ( h=0; hn_reflections; i++ ) { + + h = model_reflections->refs[i].h; + k = model_reflections->refs[i].k; + l = model_reflections->refs[i].l; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, model_reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = model_reflections->refs[i].amplitude * model_reflections->refs[i].amplitude; + + } + + free(model_reflections); + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "SimPattAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +static void displaywindow_show_simfolzpatt() { + + signed int h, k, l; + unsigned int i; + GtkAction *action; + ReflectionList *model_reflections; + + for ( h=0; hn_reflections; i++ ) { + + h = model_reflections->refs[i].h; + k = model_reflections->refs[i].k; + l = model_reflections->refs[i].l; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, model_reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = model_reflections->refs[i].amplitude * model_reflections->refs[i].amplitude; + + } + + free(model_reflections); + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "SimFOLZPattAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* This works because of dodginess in main.c */ +void displaywindow_show_difference(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + if ( reflections->refs[i].phase_known_set == 0 ) continue; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "DifferenceAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* This works because of dodginess in main.c */ +void displaywindow_show_diffpatt(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude*reflections->refs[i].amplitude; + displaywindow_in[k + displaywindow_height*h][1] = 0; + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "DifferencePattAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* This works because of dodginess in main.c */ +void displaywindow_show_refsyn(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "RefSynAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Show the simulated specimen exit wave based on the structure model */ +static void displaywindow_show_exitwave() { + + signed int h, k, l; + unsigned int i; + GtkAction *action; + ReflectionList *model_reflections; + + for ( h=0; hn_reflections; i++ ) { + + h = model_reflections->refs[i].h; + k = model_reflections->refs[i].k; + l = model_reflections->refs[i].l; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, model_reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = model_reflections->refs[i].amplitude * cos(model_reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = model_reflections->refs[i].amplitude * sin(model_reflections->refs[i].phase_known); + + } + + free(model_reflections); + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "ExitWaveAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +static void displaywindow_switchview_real(unsigned int was_rescale) { + + switch ( displaywindow_view ) { + case DWV_PATTERSON: main_show_patterson(); break; + case DWV_PATTERSONE: main_show_pattersone(); break; + case DWV_KNOWNPHASE: main_show_knownphases(); break; + case DWV_CALCPHASE: main_show_calcphases(); break; + case DWV_REALSPACE: displaywindow_show_realspace(); break; + case DWV_MODEL: displaywindow_show_model(); break; + case DWV_DIFFERENCE: main_show_difference(); break; + case DWV_REFSYN: main_show_refsyn(); break; + case DWV_DIFFPATT: main_show_diffpatt(); break; + case DWV_SIMPATT: displaywindow_show_simpatt(); break; + case DWV_SIMFOLZPATT: displaywindow_show_simfolzpatt(); break; + case DWV_EXITWAVE: displaywindow_show_exitwave(); break; + } + + if ( !was_rescale && displaywindow_autobrightness) displaywindow_brightness_auto(NULL, NULL); + + if ( !was_rescale ) { + /* Things which need updating... */ + main_displayr(); + main_argand_update(); + main_dpsynth_update(); + } + +} + +void displaywindow_switchview() { + displaywindow_switchview_real(FALSE); +} + +static gint displaywindow_geometry_open(GtkWidget *widget, gpointer data) { + geometry_dialog_open(); + return 0; +} + +static gint displaywindow_aperture(GtkWidget *widget, gpointer data) { + main_aperture_open(); + return 0; +} + +static gint displaywindow_luzzatti(GtkWidget *widget, gpointer data) { + luzzatti_show(); + return 0; +} + +static gint displaywindow_correspondence(GtkWidget *widget, gpointer data) { + correspondence_show(); + return 0; +} + +static gint displaywindow_hpfilter(GtkWidget *widget, gpointer data) { + main_hpfilter(); + return 0; +} + +static gint displaywindow_saveimage_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + printf("DW: Saving to '%s'\n", filename); + if ( displaywindow_view != DWV_REALSPACE ) { + png_write(filename, displaywindow_out, displaywindow_brightness, displaywindow_xc, displaywindow_yc); + } else { + png_write(filename, displaywindow_realspace, displaywindow_brightness, displaywindow_xc, displaywindow_yc); + } + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +static gint displaywindow_saveimage_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save Image", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + + #ifdef HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-output.png"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(displaywindow_saveimage_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint displaywindow_savepdf_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + //printf("DW: Saving to '%s'\n", filename); + + if ( displaywindow_view == DWV_MODEL ) { + if ( model_display_render_pdf(model_get_current(), data_width(), data_height(), data_gamma(), filename, + displaywindow_model_names, displaywindow_model_heights, displaywindow_xc, displaywindow_yc) ) { + error_report("Failed to save PDF"); + } + } else { + error_report("Can only (currently) save a PDF from the atomic model display"); + } + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +static gint displaywindow_savehkl_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + printf("DW: Saving to '%s'\n", filename); + + if ( displaywindow_view == DWV_REALSPACE ) { + + signed int h, k; + signed int h1, h2, k1, k2; + FILE *fh; + + memcpy(displaywindow_out, displaywindow_realspace, displaywindow_width*displaywindow_height*sizeof(fftw_complex)); + fftw_execute(displaywindow_plan_o2i); + fh = fopen(filename, "w"); + fprintf(fh, "a %f\n", data_a()); + fprintf(fh, "b %f\n", data_b()); + fprintf(fh, "c %f\n", data_c()); + fprintf(fh, "scale %i\n", data_get_image_scale()); + h1 = (signed)-main_max_h(); + h2 = main_max_h(); + k1 = (signed)-main_max_k(); + k2 = main_max_k(); + + printf("\nWARNING: calculated structure factor amplitudes are being square-rooted.\n"); + printf("This is the right thing to do to get structure factors from (eg) a CLEANed Patterson map\n"); + printf("If the real-space estimate is a structure model, you'll need to square them.\n"); + + printf("\nWARNING: l index is fixed at zero. Check this is what you wanted.\n"); + + for ( h=h1; h<=h2; h++ ) { + for ( k=k1; k<=k2; k++ ) { + + double re, im; + signed int hd, kd; + + hd = h; kd = k; + if ( h < 0 ) hd = displaywindow_width+h; + if ( k < 0 ) kd = displaywindow_height+k; + re = displaywindow_in[kd + displaywindow_height*hd][0]; + im = displaywindow_in[kd + displaywindow_height*hd][1]; + + fprintf(fh, "%3i %3i %3i %f %f\n", h, k, 0, sqrt(sqrt(re*re + im*im)), atan2(im, re)); + + } + } + fclose(fh); + + } else { + main_savereflections(filename); + } + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +static gint displaywindow_savehkl_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save Reflections", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + #ifdef HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-output.hkl"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(displaywindow_savehkl_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint displaywindow_savepdf_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save PDF", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + #ifdef HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-model.pdf"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(displaywindow_savepdf_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint displaywindow_amplituder(GtkWidget *widget, gpointer data) { + amplituder_show(); + return 0; +} + +static gint displaywindow_refine_open(GtkWidget *widget, gpointer data) { + refine_open(model_get_current()); + return 0; +} + +static gint displaywindow_grabphases(GtkWidget *widget, gpointer data) { + + if ( displaywindow_view != DWV_REALSPACE ) { + /* Should never happen because option is disabled */ + error_report("Can only grab phases from Real Space Estimate"); + return 0; + } + + if ( displaywindow_rs != DWR_ELSER ) { + error_report("Can only grab phases from Elser Difference Map"); + return 0; + } + elser_grab_phases(main_reflist()); + + displaywindow_forceview(DWV_KNOWNPHASE); + + return 0; + +} + +static gint displaywindow_contourise(GtkWidget *widget, gpointer data) { + contourise_dialog_open(displaywindow_xc, displaywindow_yc); + return 0; +} + +void displaywindow_kicksize() { + gtk_window_resize(GTK_WINDOW(displaywindow_window), 1, 1); +} + +static gint displaywindow_unitcells_response(GtkWidget *widget, gint response, UnitCellsWindow *cw) { + + int done = 1; + + if ( response == GTK_RESPONSE_OK ) { + const char *xcells; + const char *ycells; + unsigned int xc, yc; + int scanval; + xcells = gtk_entry_get_text(GTK_ENTRY(cw->xcells)); + ycells = gtk_entry_get_text(GTK_ENTRY(cw->ycells)); + scanval = sscanf(xcells, "%u", &xc); + scanval += sscanf(ycells, "%u", &yc); + if ( scanval != 2 ) { + error_report("Please enter valid values for both dimensions."); + done = 0; + } else { + displaywindow_xc = xc; + displaywindow_yc = yc; + displaywindow_switchview(); + displaywindow_kicksize(); + } + } + + if ( done ) { + gtk_widget_destroy(cw->window); + free(cw); + displaywindow_unitcellswindow = NULL; + } + + return 0; +} + +static gint displaywindow_unitcells_response_ac(GtkWidget *widget, UnitCellsWindow *cw) { + return displaywindow_unitcells_response(widget, GTK_RESPONSE_OK, cw); +} + +static gint displaywindow_stripzero(GtkWidget *widget, gpointer data) { + main_stripzero(); + displaywindow_switchview(); + return 0; +} + +static gint displaywindow_antialias(GtkWidget *widget, gpointer data) { + main_antialias(); + displaywindow_switchview(); + return 0; +} + +static gint displaywindow_unitcells_open(GtkWidget *widget, gpointer data) { + + UnitCellsWindow *cw; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *xcells_label; + GtkWidget *ycells_label; + char tmp[32]; + + if ( displaywindow_unitcellswindow ) { + return 0; + } + cw = malloc(sizeof(UnitCellsWindow)); + if ( !cw ) return 1; + displaywindow_unitcellswindow = cw; + + cw->window = gtk_dialog_new_with_buttons("Number of Unit Cells", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cw->window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + table = gtk_table_new(2, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + xcells_label = gtk_label_new("Number of cells along x:"); + gtk_misc_set_alignment(GTK_MISC(xcells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(xcells_label), 1, 2, 1, 2); + + cw->xcells = gtk_entry_new(); + snprintf(tmp, 31, "%i", displaywindow_xc); + gtk_entry_set_text(GTK_ENTRY(cw->xcells), tmp); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cw->xcells), 2, 3, 1, 2); + + ycells_label = gtk_label_new("Number of cells along y:"); + gtk_misc_set_alignment(GTK_MISC(ycells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ycells_label), 1, 2, 2, 3); + + cw->ycells = gtk_entry_new(); + snprintf(tmp, 31, "%i", displaywindow_yc); + gtk_entry_set_text(GTK_ENTRY(cw->ycells), tmp); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cw->ycells), 2, 3, 2, 3); + + g_signal_connect(G_OBJECT(cw->ycells), "activate", G_CALLBACK(displaywindow_unitcells_response_ac), cw); + g_signal_connect(G_OBJECT(cw->window), "response", G_CALLBACK(displaywindow_unitcells_response), cw); + gtk_widget_show_all(cw->window); + gtk_widget_grab_focus(GTK_WIDGET(cw->xcells)); + + return 0; + +} + +static void displaywindow_addmenubar(GtkWidget *vbox) { + + GtkActionEntry entries[] = { + + { "FileAction", NULL, "_File", NULL, NULL, NULL }, + { "SaveAction", GTK_STOCK_SAVE, "_Save Image...", "", NULL, G_CALLBACK(displaywindow_saveimage_open) }, + { "SaveHKLAction", GTK_STOCK_SAVE, "_Save HKL...", "", NULL, G_CALLBACK(displaywindow_savehkl_open) }, + { "SavePDFAction", GTK_STOCK_SAVE, "_Save PDF...", "", NULL, G_CALLBACK(displaywindow_savepdf_open) }, + { "CloseAction", GTK_STOCK_QUIT, "_Quit", NULL, NULL, G_CALLBACK(displaywindow_close) }, + + { "ViewAction", NULL, "_View", NULL, NULL, NULL }, + { "CellsAction", NULL, "Number of Unit Cells...", "F3", NULL, G_CALLBACK(displaywindow_unitcells_open) }, + { "ScaleMenuAction", NULL, "Display Brightness", NULL, NULL, G_CALLBACK(NULL) }, + { "ModelMenuAction", NULL, "Model Display Features", NULL, NULL, G_CALLBACK(NULL) }, + { "ScaleAction", NULL, "Set Display _Brightness Manually...", "F5", NULL, G_CALLBACK(displaywindow_brightness_open) }, + { "AutoScaleOnceAction", NULL, "Automatic Brightness Once Only", "F5", NULL, G_CALLBACK(displaywindow_brightness_auto) }, + { "ContourAction", NULL, "C_ontour Map...", NULL, NULL, G_CALLBACK(displaywindow_contourise) }, + { "ArgandAction", NULL, "Ar_gand Plane...", NULL, NULL, G_CALLBACK(displaywindow_argand) }, + { "DPAction", NULL, "_Diffraction Pattern...", NULL, NULL, G_CALLBACK(displaywindow_dpsynth) }, + { "SimDPAction", NULL, "Simulated Diffraction Pattern...", NULL, NULL, G_CALLBACK(displaywindow_simdp) }, + { "ApertureAction", NULL, "Aper_ture Function...", NULL, NULL, G_CALLBACK(displaywindow_aperture) }, + { "ColWheelAction", NULL, "Co_lour Wheel...", NULL, NULL, G_CALLBACK(colwheel_show) }, + + { "PlotsAction", NULL, "_Plots", NULL, NULL, NULL }, + { "WilsonAction", "gnome-calc3", "_Wilson...", NULL, NULL, G_CALLBACK(displaywindow_wilsonplot) }, + { "FalloffAction", "gnome-calc3", "_Resolution Falloff...", NULL, NULL, G_CALLBACK(displaywindow_falloffplot) }, + { "LuzzattiAction", "gnome-calc3", "Lu_zzatti...", NULL, NULL, G_CALLBACK(displaywindow_luzzatti) }, + { "CorrespondenceAction", "gnome-calc3", "Correspondence...", NULL, NULL, G_CALLBACK(displaywindow_correspondence) }, + { "AmplitudeRAction", "gnome-calc3", "Amplitude-R...", NULL, NULL, G_CALLBACK(displaywindow_amplituder) }, + + { "SolveAction", NULL, "_Solve", NULL, NULL, NULL }, + { "GSFAction", GTK_STOCK_EXECUTE, "_GSF Iteration...", NULL, NULL, G_CALLBACK(gsf_dialog_open) }, + { "CFAction", GTK_STOCK_EXECUTE, "_Charge Flipping...", NULL, NULL, G_CALLBACK(cflip_dialog_open) }, + { "CDMAction", GTK_STOCK_EXECUTE, "_Tangent Formula...", NULL, NULL, G_CALLBACK(cdm_dialog_open) }, + { "ElserAction", GTK_STOCK_EXECUTE, "_Elser Difference Map...", NULL, NULL, G_CALLBACK(elser_dialog_open) }, + { "CLEANAction", NULL, "_CLEAN...", NULL, NULL, G_CALLBACK(clean_dialog_open) }, + { "ModelRefineAction", NULL, "_Refine Atomic Model...", NULL, NULL, G_CALLBACK(displaywindow_refine_open) }, + + { "ToolsAction", NULL, "_Tools", NULL, NULL, NULL }, + { "SymmAction", GTK_STOCK_CONVERT, "_Symmetrise...", NULL, NULL, G_CALLBACK(displaywindow_symmetrise) }, + { "SharpenAction", NULL, "_Deconvolve...", NULL, NULL, G_CALLBACK(normalise_dialog_open) }, + { "DethermaliseAction", NULL, "_Normalise...", NULL, NULL, G_CALLBACK(normalise_dethermalise_open) }, + { "ExpNormaliseAction", NULL, "_Exponential Normalisation...", NULL, NULL, G_CALLBACK(normalise_exponential_open) }, + { "GeometryCorrAction", NULL, "_Geometrical Correction...", NULL, NULL, G_CALLBACK(displaywindow_geometry_open) }, + { "SuperlatticeSplitAction", NULL, "Superlattice S_plit...", NULL, NULL, G_CALLBACK(superlattice_split_open) }, + { "HighPassFilterAction", NULL, "_High Pass Filter", NULL, NULL, G_CALLBACK(displaywindow_hpfilter) }, + { "GrabPhaseAction", GTK_STOCK_COPY, "_Grab Phases as Known", NULL, NULL, G_CALLBACK(displaywindow_grabphases) }, + { "StripZeroAction", NULL, "Strip Zero-Order Beam", NULL, NULL, G_CALLBACK(displaywindow_stripzero) }, + { "AntiAliasAction", NULL, "Anti-alias Filter", NULL, NULL, G_CALLBACK(displaywindow_antialias) }, + { "ModelAtomAction", NULL, "_Edit Atomic Model...", NULL, NULL, G_CALLBACK(model_open_editor) }, + { "ModelLoadAction", GTK_STOCK_OPEN, "_Load Atomic Model...", NULL, NULL, G_CALLBACK(model_load_open) }, + { "ModelSaveAction", GTK_STOCK_SAVE, "_Save Atomic Model...", NULL, NULL, G_CALLBACK(model_save_open) }, + + { "HelpAction", NULL, "_Help", NULL, NULL, NULL }, + { "AboutAction", GTK_STOCK_ABOUT, "_About Synth2D...", NULL, NULL, G_CALLBACK(displaywindow_about) }, + + }; + guint n_entries = G_N_ELEMENTS(entries); + GError *error = NULL; + GtkRadioActionEntry radios[] = { + { "PattersonAction", NULL, "_Patterson Transform", NULL, NULL, DWV_PATTERSON }, + { "PattersonEAction", NULL, "Patterson Transform with _E²-1", NULL, NULL, DWV_PATTERSONE }, + { "KnownPhaseAction", NULL, "_Known Phases", NULL, NULL, DWV_KNOWNPHASE }, + { "CalcPhaseAction", NULL, "_Calculated Phases", NULL, NULL, DWV_CALCPHASE }, + { "RealSpaceAction", NULL, "_Real Space Estimate", NULL, NULL, DWV_REALSPACE }, + { "ModelAction", NULL, "Atomic _Model", NULL, NULL, DWV_MODEL }, + { "DifferenceAction", NULL, "Fourier _Difference Synthesis", NULL, NULL, DWV_DIFFERENCE }, + { "RefSynAction", NULL, "_Fourier Refinement Synthesis", NULL, NULL, DWV_REFSYN }, + { "DifferencePattAction", NULL, "Difference Patterson Map", NULL, NULL, DWV_DIFFPATT }, + { "SimPattAction", NULL, "_Simulated Patterson Transform", NULL, NULL, DWV_SIMPATT }, + { "SimFOLZPattAction", NULL, "_Simulated FOLZ Patterson Transform", NULL, NULL, DWV_SIMFOLZPATT }, + { "ExitWaveAction", NULL, "Simulated _Exit Wave", NULL, NULL, DWV_EXITWAVE }, + }; + guint n_radios = G_N_ELEMENTS(radios); + GtkToggleActionEntry toggles[] = { + { "AutoScaleAction", NULL, "Automatic Brightness", NULL, NULL, G_CALLBACK(displaywindow_brightness_autotoggle), FALSE }, + { "ModelNamesAction", NULL, "Element Names", NULL, NULL, G_CALLBACK(displaywindow_model_names_toggle), TRUE }, + { "ModelHeightsAction", NULL, "Atom Heights", NULL, NULL, G_CALLBACK(displaywindow_model_heights_toggle), FALSE }, + }; + guint n_toggles = G_N_ELEMENTS(toggles); + + displaywindow_action_group = gtk_action_group_new("synth2Ddisplaywindow"); + gtk_action_group_add_actions(displaywindow_action_group, entries, n_entries, displaywindow_window); + /* Weird choice of initial selected action ensures signal gets sent on opening window to sort out menu enabling/disabling */ + gtk_action_group_add_radio_actions(displaywindow_action_group, radios, n_radios, DWV_SIMFOLZPATT, G_CALLBACK(displaywindow_changeview), NULL); + gtk_action_group_add_toggle_actions(displaywindow_action_group, toggles, n_toggles, NULL); + + displaywindow_ui = gtk_ui_manager_new(); + gtk_ui_manager_insert_action_group(displaywindow_ui, displaywindow_action_group, 0); + g_signal_connect(displaywindow_ui, "add_widget", G_CALLBACK(displaywindow_addui_callback), vbox); + if ( gtk_ui_manager_add_ui_from_file(displaywindow_ui, DATADIR"/synth2d/displaywindow.ui", &error) == 0 ) { + fprintf(stderr, "Error loading message window menu bar: %s\n", error->message); + return; + } + + gtk_window_add_accel_group(GTK_WINDOW(displaywindow_window), gtk_ui_manager_get_accel_group(displaywindow_ui)); + gtk_ui_manager_ensure_update(displaywindow_ui); + + #ifndef HAVE_CAIRO + gtk_widget_set_sensitive(gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/model"), FALSE); + gtk_widget_set_sensitive(gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/dp"), FALSE); + gtk_widget_set_sensitive(gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/simdp"), FALSE); + #endif + +} + +void displaywindow_createfourier() { + + /* (Re-)Create Fourier data structures */ + + if ( displaywindow_in ) { + fftw_free(displaywindow_in); + fftw_free(displaywindow_out); + fftw_destroy_plan(displaywindow_plan_i2o); + fftw_destroy_plan(displaywindow_plan_o2i); + } + + displaywindow_in = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + displaywindow_out = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + + displaywindow_plan_i2o = fftw_plan_dft_2d(data_width(), data_height(), displaywindow_in, displaywindow_out, + FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + displaywindow_plan_o2i = fftw_plan_dft_2d(data_width(), data_height(), displaywindow_out, displaywindow_in, + FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + displaywindow_width = data_width(); + displaywindow_height = data_height(); + +} + +void displaywindow_open(const char *filenamefull) { + + const char *filename; + char *title; + GtkWidget *vbox; + + filename = basename(filenamefull); + title = malloc(11+strlen(filename)); + strcpy(title, filename); + strcat(title, " - synth2d"); + + displaywindow_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(displaywindow_window), title); + free(title); + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(displaywindow_window), vbox); + displaywindow_addmenubar(vbox); + + displaywindow_status_bar = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(displaywindow_status_bar), FALSE); + gtk_box_pack_end(GTK_BOX(vbox), displaywindow_status_bar, FALSE, FALSE, 0); + + displaywindow_bigvbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), displaywindow_bigvbox, FALSE, TRUE, 0); + + g_signal_connect(GTK_OBJECT(displaywindow_window), "destroy", G_CALLBACK(displaywindow_closedown), NULL); + g_signal_connect_after(GTK_OBJECT(displaywindow_window), "destroy", G_CALLBACK(gtk_main_quit), NULL); + + displaywindow_disablephasegrabbing(); + + gtk_widget_show_all(displaywindow_window); + + displaywindow_createfourier(); + +} + +double displaywindow_maxpeak() { + + fftw_complex *out; + double max; + unsigned int x, y; + + out = displaywindow_outarray(); + + max = 0; + for ( x=0; x fabs(max) ) max = am; + } + } + + return max; + +} + +/* To be used with caution */ +fftw_complex *displaywindow_outarray() { + + if ( displaywindow_view != DWV_REALSPACE ) { + return displaywindow_out; + } else { + return displaywindow_realspace; + } + +} diff --git a/src/displaywindow.h b/src/displaywindow.h new file mode 100644 index 0000000..bdd339d --- /dev/null +++ b/src/displaywindow.h @@ -0,0 +1,76 @@ +/* + * displaywindow.h + * + * The main display window + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef DISPLAYWINDOW_H +#define DISPLAYWINDOW_H + +#include +#include + +#include "reflist.h" + +typedef enum { + DWR_NONE, + DWR_CLEAN, + DWR_GSF, + DWR_ELSER +} DisplayWindowRealSpace; + +extern GtkWidget *displaywindow_gtkwindow(void); +extern int displaywindow_mode(void); +extern void displaywindow_open(const char *filenameull); +extern void displaywindow_statusbar(const char *message); +extern void error_report(const char *message); +extern void displaywindow_show_patterson(ReflectionList *reflections); +extern void displaywindow_show_pattersone(ReflectionList *reflections); +extern void displaywindow_show_knownphases(ReflectionList *reflections); +extern void displaywindow_show_calcphases(ReflectionList *reflections); +extern void displaywindow_show_difference(ReflectionList *reflections); +extern void displaywindow_show_refsyn(ReflectionList *reflections); +extern void displaywindow_show_diffpatt(ReflectionList *reflections); +extern void displaywindow_switchview(void); +extern void displaywindow_set_realspace(fftw_complex *out, DisplayWindowRealSpace rs); +extern double displaywindow_maxpeak(void); +extern double displaywindow_max(void); +extern fftw_complex *displaywindow_outarray(void); +extern void displaywindow_createfourier(void); +extern void displaywindow_forceview(int new_view); +extern void displaywindow_disablephasegrabbing(void); +extern void displaywindow_enablephasegrabbing(void); +extern void displaywindow_set_gamma(double gamma); +extern GdkPixbuf *displaywindow_render_pixbuf(fftw_complex *out, double brightness, + size_t width_o, size_t height_o, double gamma, int nx, int ny); +extern gint displaywindow_brightness_auto(GtkWidget *widget, gpointer data); +extern fftw_complex *displaywindow_outarray(void); +extern void displaywindow_kicksize(void); + +enum { + DWV_PATTERSON, + DWV_PATTERSONE, + DWV_KNOWNPHASE, + DWV_CALCPHASE, + DWV_REALSPACE, + DWV_MODEL, + DWV_DIFFERENCE, + DWV_REFSYN, + DWV_DIFFPATT, + DWV_SIMPATT, + DWV_SIMFOLZPATT, + DWV_EXITWAVE +}; + +#endif /* DISPLAYWINDOW_H */ + diff --git a/src/dpsynth.c b/src/dpsynth.c new file mode 100644 index 0000000..33e9c3e --- /dev/null +++ b/src/dpsynth.c @@ -0,0 +1,291 @@ +/* + * dpsynth.c + * + * Draw synthetic diffraction patterns + * + * (c) 2006-2009 Thomas White + * (c) 2007-2008 Alex Eggeman + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#if HAVE_CAIRO +#include +#endif + +#include + +#include "reflist.h" +#include "data.h" +#include "displaywindow.h" +#include "colwheel.h" +#include "gsf.h" +#include "normalise.h" + +typedef struct { + GtkWidget *image_widget; + GdkPixbuf *pixbuf; + GtkWidget *window; + unsigned int width; + unsigned int height; + unsigned int colour; + +} DPSynthWindow; + +#if HAVE_CAIRO +static void dpsynth_swizzle_data(unsigned char *data, + size_t width, size_t height) +{ + size_t i, x, y; + uint32_t *dataw; + + for ( i=0; i<4*width*height; i+=4 ) { + unsigned char a, r, g, b; + r = data[i]; g = data[i+1]; b = data[i+2]; a = data[i+3]; + data[i] = b; data[i+1] = g; data[i+2] = r; data[i+3] = a; + } + + /* Now make it be the right way up */ + dataw = (uint32_t *)data; + for ( y=0; ywidth, dpsynth->height); + + if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { + fprintf(stderr, "Couldn't create Cairo surface\n"); + cairo_surface_destroy(surface); + return NULL; + } + + dctx = cairo_create(surface); + + /* Black background */ + cairo_rectangle(dctx, 0.0, 0.0, dpsynth->width, dpsynth->height); + cairo_set_source_rgb(dctx, 0.0, 0.0, 0.0); + + cairo_fill(dctx); + + max_u = 0.0; max_v = 0.0; max_intensity = 0.0; + max_res = 0.0; + /* NB This isn't really "the angle between a* and b*" */ + theta = data_gamma()-M_PI_2; + as = 1/(data_a() * cos(theta)); + bs = 1/(data_b() * cos(theta)); + for ( i=0; in_reflections; i++ ) { + + double u, v, intensity, ph, res; + + /* Convert to intensity */ + intensity = pow(reflections->refs[i].amplitude, 2.0); + ph = reflections->refs[i].phase_known; + + res = resolution(reflections->refs[i].h, + reflections->refs[i].k, + reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + if ( res > max_res ) max_res = res; + + if ( intensity != 0 ) { + u = (double)reflections->refs[i].h * as * cos(theta); + v = (double)reflections->refs[i].h * as * sin(theta) + + reflections->refs[i].k * bs; + if ( fabs(u) > fabs(max_u) ) max_u = fabs(u); + if ( fabs(v) > fabs(max_v) ) max_v = fabs(v); + if ( fabs(intensity) > fabs(max_intensity) ) + max_intensity = fabs(intensity); + } + + } + + max_u *= 2.5; + max_v *= 2.5; + printf("DP: Maximum resolution is %f nm^-1\n", max_res); + + if ( max_intensity > 0 ) { + + scale = ((double)dpsynth->width-50.0) / (2*max_u); + if ( ((double)dpsynth->height-50.0) / (2*max_v) < scale ) + scale = ((double)dpsynth->height-50.0) / (2*max_v); + + sep_u = as * scale * cos(theta); + sep_v = bs * scale; + max_r = ((sep_u < sep_v)?sep_u:sep_v) / 2; + + for ( i=0; in_reflections; i++ ) { + + double u, v, intensity, ph, val; + + intensity = pow(reflections->refs[i].amplitude, 2.0); + ph = reflections->refs[i].phase_known; + val = 2.0*intensity/max_intensity; + + if ( intensity != 0 ) { + + u = (double)reflections->refs[i].h * as + * cos(theta); + v = (double)reflections->refs[i].h * as + * sin(theta) + reflections->refs[i].k * bs; + + cairo_arc(dctx, ((double)dpsynth->width/2) + +u*scale*2, + ((double)dpsynth->height/2) + +v*scale*2, max_r, 0, 2*M_PI); + + + if ( dpsynth->colour == 0 ) { + cairo_set_source_rgb(dctx, val, val, val); } else { + cairo_set_source_rgb(dctx, + colwheel_red(val, ph), + colwheel_green(val, ph), + colwheel_blue(val, ph) + ); + } + cairo_fill(dctx); + + } + + } + + } else { + max_r = 4.0; + } + + /* Centre marker */ + cairo_arc(dctx, (double)dpsynth->width/2, + (double)dpsynth->height/2, max_r, 0, 2*M_PI); + cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0); + cairo_fill(dctx); + + cairo_surface_flush(surface); + + data = cairo_image_surface_get_data(surface); + dpsynth_swizzle_data(data, cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface)); + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, 8, + cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface), + cairo_image_surface_get_stride(surface), + (GdkPixbufDestroyNotify)dpsynth_free_data, dctx); + + return pixbuf; +#else + /* Cannot do this without Cairo */ + return NULL; +#endif +} + +static void dpsynth_close(GtkWidget *widget, DPSynthWindow *dpsynth) +{ + free(dpsynth); +} + +void dpsynth_update(DPSynthWindow *dpsynth, ReflectionList *reflections) +{ + if ( dpsynth->pixbuf ) gdk_pixbuf_unref(dpsynth->pixbuf); + + dpsynth->pixbuf = dpsynth_render_pixbuf(dpsynth, reflections); + if ( !dpsynth->pixbuf ) return; + + if ( dpsynth->image_widget ) { + g_object_set(G_OBJECT(dpsynth->image_widget), "pixbuf", + dpsynth->pixbuf, NULL); + } else { + dpsynth->image_widget = gtk_image_new_from_pixbuf( + dpsynth->pixbuf); + gtk_container_add(GTK_CONTAINER(dpsynth->window), + GTK_WIDGET(dpsynth->image_widget)); + gtk_widget_show(dpsynth->image_widget); + } +} + +DPSynthWindow *dpsynth_open(ReflectionList *reflections, const char *title, + int colour) +{ + DPSynthWindow *dpsynth; + + dpsynth = malloc(sizeof(DPSynthWindow)); + dpsynth->width = 512; + dpsynth->height = 512; + dpsynth->pixbuf = NULL; + dpsynth->image_widget = NULL; + dpsynth->colour = colour; + + dpsynth->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(dpsynth->window), title); + g_signal_connect(GTK_OBJECT(dpsynth->window), "destroy", + G_CALLBACK(dpsynth_close), dpsynth); + gtk_widget_show_all(dpsynth->window); + + dpsynth_update(dpsynth, reflections); + + return dpsynth; +} + +/* "Glue" - to be removed eventually... */ +DPSynthWindow *dpsynth_main_dpsynth = NULL; +static void dpsynth_main_close(GtkWidget *widget, DPSynthWindow *dpsynth) { + dpsynth_main_dpsynth = NULL; +} +void dpsynth_main_update(ReflectionList *reflections) { + if ( !dpsynth_main_dpsynth ) return; + dpsynth_update(dpsynth_main_dpsynth, reflections); +} +void dpsynth_main_open(ReflectionList *reflections) { + if ( dpsynth_main_dpsynth ) return; + dpsynth_main_dpsynth = dpsynth_open(reflections, "Diffraction Pattern", 0); + g_signal_connect(GTK_OBJECT(dpsynth_main_dpsynth->window), "destroy", G_CALLBACK(dpsynth_main_close), dpsynth_main_dpsynth); +} + +DPSynthWindow *dpsynth_sim_dpsynth = NULL; +static void dpsynth_simdp_close(GtkWidget *widget, DPSynthWindow *dpsynth) { + dpsynth_sim_dpsynth = NULL; +} +void dpsynth_simdp_update(ReflectionList *reflections) { + if ( !dpsynth_sim_dpsynth ) return; + dpsynth_update(dpsynth_sim_dpsynth, reflections); +} +void dpsynth_simdp_open(ReflectionList *reflections) { + if ( dpsynth_sim_dpsynth ) return; + dpsynth_sim_dpsynth = dpsynth_open(reflections, "Simulated Diffraction Pattern", 0); + g_signal_connect(GTK_OBJECT(dpsynth_sim_dpsynth->window), "destroy", G_CALLBACK(dpsynth_simdp_close), dpsynth_sim_dpsynth); +} diff --git a/src/dpsynth.h b/src/dpsynth.h new file mode 100644 index 0000000..b10d978 --- /dev/null +++ b/src/dpsynth.h @@ -0,0 +1,37 @@ +/* + * dpsynth.h + * + * Draw synthetic diffracion patterns + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef DPSYNTH_H +#define DPSYNTH_H + +typedef struct { + GtkWidget *image_widget; + GdkPixbuf *pixbuf; + GtkWidget *window; + unsigned int width; + unsigned int height; + unsigned int colour; + +} DPSynthWindow; + +extern void dpsynth_main_update(ReflectionList *reflections); +extern void dpsynth_main_open(ReflectionList *reflections); +extern DPSynthWindow *dpsynth_open(ReflectionList *reflections, const char *title, int colour); +extern void dpsynth_update(DPSynthWindow *dpsynth, ReflectionList *reflections); + +extern void dpsynth_simdp_open(ReflectionList *reflections); +extern void dpsynth_simdp_update(ReflectionList *reflections); + +#endif /* DPSYNTH_H */ + diff --git a/src/elements.c b/src/elements.c new file mode 100644 index 0000000..96b33e6 --- /dev/null +++ b/src/elements.c @@ -0,0 +1,86 @@ +/* + * elements.c + * + * Elemental Data + * + * (c) 2006-2007 Thomas White + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "elements.h" + +Element elements[255]; +static unsigned int elements_initialised = 0; + +void elements_initialise() { + + int i, whoops; + FILE *fh; + + if ( elements_initialised ) return; + + i = 0; whoops = 0; + fh = fopen(DATADIR"/synth2d/elements", "r"); + do { + + char line[512]; + char buf[512]; + float a1, b1, a2, b2, a3, b3, a4, b4, c; + + fgets(line, 511, fh); + if ( ferror(fh) || feof(fh) ) { + whoops = 1; + break; + } + if ( strlen(line) > 1 ) { + + if ( line[strlen(line)-1] == '\n' ) { + line[strlen(line)-1] = '\0'; + } + + sscanf(line, "%s\t%i\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f", buf, &elements[i].z, &a1, &b1, &a2, &b2, &a3, &b3, &a4, &b4, &c); + elements[i].element_name = strdup(buf); + elements[i].sfac_a1 = a1; elements[i].sfac_b1 = b1; + elements[i].sfac_a2 = a2; elements[i].sfac_b2 = b2; + elements[i].sfac_a3 = a3; elements[i].sfac_b3 = b3; + elements[i].sfac_a4 = a4; elements[i].sfac_b4 = b4; + elements[i].sfac_c = c; + + i++; + + } else { + line[0] = '\0'; + } + + } while ( !whoops && !feof(fh) ); + + elements[i].element_name = "EOF"; + elements_initialised = 1; + +} + +unsigned int elements_lookup(const char *name) { + + size_t i; + + i = 0; + while ( strcmp(elements[i].element_name, "EOF") != 0 ) { + if ( strcmp(elements[i].element_name, name) == 0 ) return i; + i++; + } + + fprintf(stderr, "Failed to look up element\n"); + return i; + +} + diff --git a/src/elements.h b/src/elements.h new file mode 100644 index 0000000..525e399 --- /dev/null +++ b/src/elements.h @@ -0,0 +1,33 @@ +/* + * elements.c + * + * Elemental Data + * + * (c) 2006-2007 Thomas White + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ELEMENTS_H +#define ELEMENTS_H + +typedef struct { + char *element_name; + unsigned int z; + /* Units of Angstroms and Degrees */ + double sfac_a1; double sfac_b1; double sfac_a2; double sfac_b2; + double sfac_a3; double sfac_b3; double sfac_a4; double sfac_b4; + double sfac_c; +} Element; +extern Element elements[255]; + +extern void elements_initialise(void); +extern unsigned int elements_lookup(const char *name); + +#endif /* ELEMENTS_H */ + diff --git a/src/elser.c b/src/elser.c new file mode 100644 index 0000000..d562f9f --- /dev/null +++ b/src/elser.c @@ -0,0 +1,811 @@ +/* + * elser.c + * + * Elser Difference Map Algorithm + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "displaywindow.h" +#include "reflist.h" +#include "symmetry.h" +#include "gtk-valuegraph.h" + +#define ELSER_START_PHASES 1 + +typedef enum { + RELAXATION_LOCALLY_ORTHOGONAL, + RELAXATION_FIENUP_HIO, + RELAXATION_CHARGE_FLIPPING +} ElserRelaxationType; + +typedef struct stuct_elserdialog { + + /* Dialog box bits */ + GtkWidget *window; + GtkWidget *go; + GtkWidget *stop; + GtkWidget *reset; + GtkWidget *solution; + GtkWidget *beta_widget; + GtkWidget *itnum; + GtkWidget *graph; + + /* Thread control */ + GThread *work_thread; + unsigned int running; + unsigned int run_semaphore; + GStaticMutex display_mutex; + guint display_callback; + + /* Algorithm control */ + double beta; + double gam1; + double gam2; + gint track_best; + ElserRelaxationType relaxation; + + /* Algorithm guts */ + ReflectionList *reflections; /* List of 'modulus constraints' */ + fftw_complex *in; /* Holds structure factors when needed */ + fftw_complex *trans_out; /* Array on the other end of the FFT of 'in' */ + fftw_plan plan_i2t; /* Plan to get from 'in' to 'trans_out' */ + fftw_plan plan_t2i; /* Plan to get from 'trans_out' to 'in', i.e. calculate structure factors */ + fftw_complex *out; /* Current iterate */ + fftw_complex *display; /* What's currently on the display */ + size_t array_size; /* Size of the arrays in bytes */ + fftw_complex *f1; /* f1 semi-iterate */ + fftw_complex *f2; /* f2 semi-iterate */ + fftw_complex *scratch; /* Scratchpad for calculations */ + unsigned int width; /* Width of the array in pixels */ + unsigned int height; /* Height of the array in pixels */ + + /* Tracking */ + unsigned int n_iterations; /* Number of iterations performed so far */ + double *e_evolution; /* Array of values tracking ||e|| */ + size_t e_evolution_size; /* Size of the e_evolution array */ + double best_norm; /* Lowest value of ||e|| so far encountered */ + unsigned int best_norm_iteration; /* Iteration number on which the best norm was achieved */ + fftw_complex *best_norm_grid; + +} ElserDialog; + +typedef struct struct_elserpeak { + unsigned int x; + unsigned int y; +} ElserPeak; + +ElserDialog *elser_dialog = NULL; + +static void elser_grid_multiply(fftw_complex *a, double b, size_t size) { + size_t i; + for ( i=0; iIteration number: %i", elser_dialog->n_iterations); + gtk_label_set_markup(GTK_LABEL(elser_dialog->itnum), tmp); + +} + +/* Scale up from the (small) grid size to the (large) display size */ +static void elser_do_display(fftw_complex *display, fftw_complex *grid, unsigned int grid_width, unsigned int grid_height, + unsigned int disp_width, unsigned int disp_height) { + + double sx, sy; + unsigned int dx, dy; + + sx = (double)disp_width/grid_width; + sy = (double)disp_height/grid_height; + + for ( dx=0; dxgraph), elser_dialog->e_evolution, elser_dialog->n_iterations); + + elser_dialog->display_callback = 0; + g_static_mutex_unlock(&elser_dialog->display_mutex); + + return FALSE; + +} + +#if 0 +/* Constrain the origin to prevent wandering in a conditional projection */ +static void elser_projection_origin(fftw_complex *f, ElserDialog *elser_dialog) { + + int width, height; + unsigned int x, y; + double re, im, ph_offs; + + width = elser_dialog->width; + height = elser_dialog->height; + + re = f[0][0]; + im = f[0][1]; + ph_offs = atan2(im, re); + printf("ph_offs = %f deg\n", (ph_offs/M_PI)*180); + + for ( y=0; y fabs(max) ) { + unsigned int p, allowed; + allowed = 1; + for ( p=0; pwidth; + height = elser_dialog->height; + N = 8; ssx=3; ssy=3; css=2; /* Gives 2px radius circular atom supports */ + + peaks = elser_projection_atomicity_findatoms(f, width, height, N, ssx, ssy); + grid_copy = malloc(elser_dialog->array_size); + bzero(grid_copy, elser_dialog->array_size); + + for ( i=0; iarray_size); + free(grid_copy); + +// elser_projection_origin(f, elser_dialog); + +} + +static void elser_projection_modulus(fftw_complex *f, ElserDialog *elser_dialog) { + + unsigned int width, height, i, x, y; + + width = elser_dialog->width; + height = elser_dialog->height; + + /* Transform */ + memcpy(elser_dialog->trans_out, f, elser_dialog->array_size); + fftw_execute(elser_dialog->plan_t2i); + + /* Scale */ + for ( x=0; xin[y+height*x][0] /= (width*height); + elser_dialog->in[y+height*x][1] /= (width*height); + } + } + + /* Apply constraints */ + for ( i=1; ireflections->n_reflections; i++ ) { + + double re, im; + double am, ph; + signed int h, k; + + h = elser_dialog->reflections->refs[i].h; + k = elser_dialog->reflections->refs[i].k; + + //if ( reflist_inlist(elser_dialog->reflections, -h, -k, 0) == 0 ) { + // printf("Friedel error for %3i %3i\n", h, k); + //} + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + re = elser_dialog->in[k + height*h][0]; + im = elser_dialog->in[k + height*h][1]; + ph = atan2(im, re); + am = elser_dialog->reflections->refs[i].amplitude; + + elser_dialog->in[k + height*h][0] = am*cos(ph); + elser_dialog->in[k + height*h][1] = am*sin(ph); + + } + printf("F00 = %f\n", sqrt(elser_dialog->in[0][0]*elser_dialog->in[0][0] + elser_dialog->in[0][1]*elser_dialog->in[0][1])); + symmetry_symmetrise_array(elser_dialog->in, width, height, SYMMETRY_FRIEDEL); + + /* Back transform */ + fftw_execute(elser_dialog->plan_i2t); + memcpy(f, elser_dialog->trans_out, elser_dialog->array_size); + +} + +static double elser_calculate_norm(fftw_complex *delta, unsigned int array_size) { + + unsigned int i; + double total, norm; + + total = 0; + for ( i=0; irunning = 1; + + width = data_width(); + height = data_height(); + + f1 = elser_dialog->f1; + f2 = elser_dialog->f2; + + while ( elser_dialog->run_semaphore ) { + + double norm, gam1, gam2, beta; + + beta = elser_dialog->beta; + gam1 = elser_dialog->gam1; + gam2 = elser_dialog->gam2; + + memcpy(f1, elser_dialog->out, elser_dialog->array_size); + memcpy(f2, elser_dialog->out, elser_dialog->array_size); + + elser_projection_atomicity(f1, elser_dialog); + elser_grid_multiply(f1, 1+gam1, elser_dialog->array_size); + memcpy(elser_dialog->scratch, elser_dialog->out, elser_dialog->array_size); + elser_grid_multiply(elser_dialog->scratch, gam1, elser_dialog->array_size); + elser_grid_subtract(f1, elser_dialog->scratch, elser_dialog->array_size); /* f1 = (1+gam1)P1(Xn) - gam1*Xn */ + + elser_projection_modulus(f2, elser_dialog); + elser_grid_multiply(f2, 1+gam2, elser_dialog->array_size); + memcpy(elser_dialog->scratch, elser_dialog->out, elser_dialog->array_size); + elser_grid_multiply(elser_dialog->scratch, gam2, elser_dialog->array_size); + elser_grid_subtract(f2, elser_dialog->scratch, elser_dialog->array_size); /* f2 = (1+gam2)P2(Xn) - gam2*Xn */ + + elser_projection_atomicity(f2, elser_dialog); + elser_projection_modulus(f1, elser_dialog); + elser_grid_subtract(f2, f1, elser_dialog->array_size); /* 'f2' now contains delta = P1(f2) - P2(f1) */ + + norm = elser_calculate_norm(f2, elser_dialog->array_size); + elser_dialog->e_evolution[elser_dialog->n_iterations] = log(norm); + if ( (elser_dialog->n_iterations % 100) == 99 ) { + elser_dialog->e_evolution = realloc(elser_dialog->e_evolution, elser_dialog->e_evolution_size + 100*sizeof(double)); + elser_dialog->e_evolution_size += 100*sizeof(double); + } + + elser_grid_multiply(f2, beta, elser_dialog->array_size); /* 'f2' now contains beta(P1(f2)-P2(f1)) */ + elser_grid_add(elser_dialog->out, f2, elser_dialog->array_size); /* Xn+1 = Xn + delta */ + + if ( norm < elser_dialog->best_norm ) { + elser_dialog->best_norm = norm; + elser_dialog->best_norm_iteration = elser_dialog->n_iterations+1; + memcpy(elser_dialog->best_norm_grid, elser_dialog->out, elser_dialog->array_size); + } + + /* Tell the main thread to update the display */ + g_static_mutex_lock(&elser_dialog->display_mutex); + elser_dialog->n_iterations++; + if ( elser_dialog->track_best ) { + elser_do_display(elser_dialog->display, elser_dialog->best_norm_grid, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + } else { + elser_do_display(elser_dialog->display, elser_dialog->out, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + } + elser_dialog->display_callback = g_idle_add(elser_update_display, elser_dialog); + + } + + elser_dialog->running = 0; + return NULL; + +} + +static gint elser_control_reset(GtkWidget *reset, ElserDialog *elser_dialog) { + + unsigned int i, width, height; + + /* Create structures */ + width = 2*main_max_h() + 1; + height = 2*main_max_k() + 1; + printf("EL: Grid size is %i x %i\n", width, height); + elser_dialog->array_size = width*height*sizeof(fftw_complex); + if ( !elser_dialog->in ) elser_dialog->in = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->trans_out ) elser_dialog->trans_out = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->out ) elser_dialog->out = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->f1 ) elser_dialog->f1 = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->f2 ) elser_dialog->f2 = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->scratch ) elser_dialog->scratch = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->best_norm_grid ) elser_dialog->best_norm_grid = fftw_malloc(elser_dialog->array_size); + elser_dialog->width = width; + elser_dialog->height = height; + elser_dialog->reflections = reflist_copy(main_reflist()); + if ( !elser_dialog->plan_i2t ) + elser_dialog->plan_i2t = fftw_plan_dft_2d(width, height, elser_dialog->in, elser_dialog->trans_out, FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + if ( !elser_dialog->plan_t2i ) + elser_dialog->plan_t2i = fftw_plan_dft_2d(width, height, elser_dialog->trans_out, elser_dialog->in, FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + /* Set the initial iterate */ + for ( i=0; iarray_size/sizeof(fftw_complex); i++ ) { + elser_dialog->out[i][0] = random() - random(); + elser_dialog->out[i][1] = random() - random(); + } +#if ELSER_START_PHASES +// elser_projection_modulus(elser_dialog->out, elser_dialog); +#endif + elser_projection_atomicity(elser_dialog->out, elser_dialog); + + /* The old display array is going to get freed by displaywindow_set_realspace() */ + elser_dialog->display = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + elser_do_display(elser_dialog->display, elser_dialog->out, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + displaywindow_set_realspace(elser_dialog->display, DWR_ELSER); + displaywindow_forceview(DWV_REALSPACE); + displaywindow_switchview(); + elser_dialog->n_iterations = 0; + elser_update_itnum(elser_dialog); + elser_dialog->best_norm_iteration = 0; + elser_dialog->best_norm = INFINITY; + + /* Always exists - was set up when the dialog was created */ + free(elser_dialog->e_evolution); + elser_dialog->e_evolution = malloc(100*sizeof(double)); + elser_dialog->e_evolution_size = 100*sizeof(double); + gtk_value_graph_set_data(GTK_VALUE_GRAPH(elser_dialog->graph), elser_dialog->e_evolution, 0); + + gtk_widget_set_sensitive(elser_dialog->go, TRUE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + return 0; + +} + +static gint elser_control_stop(GtkWidget *stop, ElserDialog *elser_dialog) { + + if ( !elser_dialog->running ) return 0; + + assert(elser_dialog->run_semaphore == 1); + assert(elser_dialog->work_thread != NULL); + elser_dialog->run_semaphore = 0; + if ( elser_dialog->display_callback ) { + g_idle_remove_by_data(elser_dialog); + g_static_mutex_unlock(&elser_dialog->display_mutex); + } + g_thread_join(elser_dialog->work_thread); + elser_dialog->work_thread = NULL; + + gtk_widget_set_sensitive(elser_dialog->go, TRUE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, TRUE); + + return 0; +} + +static gint elser_control_go(GtkWidget *go, ElserDialog *elser_dialog) { + + if ( elser_dialog->running ) return 0; + + assert(elser_dialog->run_semaphore == 0); + elser_dialog->run_semaphore = 1; + g_static_mutex_init(&elser_dialog->display_mutex); + assert(elser_dialog->work_thread == NULL); + elser_dialog->display_callback = 0; + elser_dialog->work_thread = g_thread_create(elser_work, elser_dialog, TRUE, NULL); + + gtk_widget_set_sensitive(elser_dialog->go, FALSE); + gtk_widget_set_sensitive(elser_dialog->stop, TRUE); + gtk_widget_set_sensitive(elser_dialog->reset, FALSE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + displaywindow_disablephasegrabbing(); + + return 0; +} + +static gint elser_control_solution(GtkWidget *solution, ElserDialog *elser_dialog) { + + if ( elser_dialog->running ) return 0; + + if ( elser_dialog->track_best ) { + memcpy(elser_dialog->out, elser_dialog->best_norm_grid, elser_dialog->array_size); + elser_dialog->n_iterations = elser_dialog->best_norm_iteration; + elser_update_itnum(elser_dialog); + } + + memcpy(elser_dialog->f1, elser_dialog->out, elser_dialog->array_size); + elser_projection_atomicity(elser_dialog->f1, elser_dialog); + elser_grid_multiply(elser_dialog->f1, 1+elser_dialog->gam1, elser_dialog->array_size); + memcpy(elser_dialog->scratch, elser_dialog->out, elser_dialog->array_size); + elser_grid_multiply(elser_dialog->scratch, elser_dialog->gam1, elser_dialog->array_size); + elser_grid_subtract(elser_dialog->f1, elser_dialog->scratch, elser_dialog->array_size); /* f1 = (1+gam1)P1(Xn) - gam1*Xn */ + elser_projection_modulus(elser_dialog->f1, elser_dialog); + memcpy(elser_dialog->out, elser_dialog->f1, elser_dialog->array_size); + + elser_do_display(elser_dialog->display, elser_dialog->out, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + gtk_value_graph_set_data(GTK_VALUE_GRAPH(elser_dialog->graph), elser_dialog->e_evolution, elser_dialog->n_iterations); + displaywindow_switchview(); + + gtk_widget_set_sensitive(elser_dialog->go, FALSE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + displaywindow_enablephasegrabbing(); + + return 0; + +} + +static gint elser_control_close(GtkWidget *window, gint response, ElserDialog *this_elserdialog) { + + elser_control_stop(this_elserdialog->stop, this_elserdialog); + + gtk_widget_destroy(window); + + if ( this_elserdialog->in ) fftw_free(this_elserdialog->in); + if ( this_elserdialog->out ) fftw_free(this_elserdialog->out); + if ( this_elserdialog->f1 ) fftw_free(this_elserdialog->f1); + if ( this_elserdialog->f2 ) fftw_free(this_elserdialog->f2); + if ( this_elserdialog->scratch ) fftw_free(this_elserdialog->scratch); + if ( this_elserdialog->best_norm_grid ) fftw_free(this_elserdialog->best_norm_grid); + if ( this_elserdialog->trans_out ) fftw_free(this_elserdialog->trans_out); + if ( this_elserdialog->plan_i2t ) fftw_destroy_plan(this_elserdialog->plan_i2t); + if ( this_elserdialog->plan_t2i ) fftw_destroy_plan(this_elserdialog->plan_t2i); + if ( this_elserdialog->reflections ) reflist_free(this_elserdialog->reflections); + /* Don't free this_elserdialog->display, it's backing up the display */ + + free(this_elserdialog); + elser_dialog = NULL; + + return 0; + +} + +static gint elser_control_beta_changed(GtkWidget *beta, ElserDialog *elser_dialog) { + + elser_dialog->beta = gtk_range_get_value(GTK_RANGE(beta)); + + switch ( elser_dialog->relaxation ) { + case RELAXATION_LOCALLY_ORTHOGONAL : { elser_dialog->gam1 = -1/elser_dialog->beta; elser_dialog->gam2 = 1/elser_dialog->beta; break; } + case RELAXATION_FIENUP_HIO : { elser_dialog->gam1 = -1; elser_dialog->gam2 = 1/elser_dialog->beta; break; } + case RELAXATION_CHARGE_FLIPPING : { elser_dialog->gam1 = 1; elser_dialog->gam2 = -1; break; } + } + + return 0; + +} + +static gint elser_control_trackbest_toggled(GtkWidget *track_best, ElserDialog *elser_dialog) { + elser_dialog->track_best = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(track_best)); + return 0; +} + +static gint elser_control_relaxation_change(GtkWidget *relaxation_combo, ElserDialog *elser_dialog) { + + switch ( gtk_combo_box_get_active(GTK_COMBO_BOX(relaxation_combo)) ) { + case 0 : elser_dialog->relaxation = RELAXATION_LOCALLY_ORTHOGONAL; break; + case 1 : elser_dialog->relaxation = RELAXATION_FIENUP_HIO; break; + case 2 : elser_dialog->relaxation = RELAXATION_CHARGE_FLIPPING; break; + default : abort(); /* Only in the event of a screw-up */ + } + + elser_control_beta_changed(elser_dialog->beta_widget, elser_dialog); + + return 0; + +} + +void elser_dialog_open() { + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *buttons; + GtkWidget *beta_label; + GtkWidget *beta_hbox; + GtkWidget *relaxation_hbox; + GtkWidget *relaxation_label; + GtkWidget *relaxation_combo; + GtkWidget *track_best; + + if ( elser_dialog ) { + return; + } + elser_dialog = malloc(sizeof(ElserDialog)); + elser_dialog->reflections = NULL; + elser_dialog->in = NULL; + elser_dialog->out = NULL; + elser_dialog->trans_out = NULL; + elser_dialog->f1 = NULL; + elser_dialog->f2 = NULL; + elser_dialog->scratch = NULL; + elser_dialog->plan_i2t = NULL; + elser_dialog->plan_t2i = NULL; + elser_dialog->best_norm_grid = NULL; + elser_dialog->display = NULL; + elser_dialog->running = 0; + elser_dialog->work_thread = NULL; + elser_dialog->run_semaphore = 0; + elser_dialog->running = 0; + elser_dialog->beta = 0.7; + elser_dialog->best_norm = INFINITY; + elser_dialog->gam1 = -1/elser_dialog->beta; + elser_dialog->gam2 = 1/elser_dialog->beta; + elser_dialog->track_best = FALSE; + + elser_dialog->window = gtk_dialog_new_with_buttons("Elser Difference Map", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + + /* Structure */ + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(elser_dialog->window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + /* Parameters */ + beta_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(beta_hbox), FALSE, FALSE, 5); + beta_label = gtk_label_new("ß:"); + gtk_misc_set_padding(GTK_MISC(beta_label), 3, 0); + gtk_box_pack_start(GTK_BOX(beta_hbox), GTK_WIDGET(beta_label), FALSE, FALSE, 0); + elser_dialog->beta_widget = gtk_hscale_new_with_range(0, 1, 0.01); + gtk_scale_set_value_pos(GTK_SCALE(elser_dialog->beta_widget), GTK_POS_RIGHT); + gtk_range_set_value(GTK_RANGE(elser_dialog->beta_widget), elser_dialog->beta); + gtk_box_pack_start(GTK_BOX(beta_hbox), GTK_WIDGET(elser_dialog->beta_widget), TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(elser_dialog->beta_widget), "value-changed", G_CALLBACK(elser_control_beta_changed), elser_dialog); + + relaxation_hbox = gtk_hbox_new(FALSE, 0); + relaxation_label = gtk_label_new("Relaxation:"); + gtk_misc_set_alignment(GTK_MISC(relaxation_label), 1, 0.5); + gtk_box_pack_start(GTK_BOX(relaxation_hbox), relaxation_label, FALSE, FALSE, 5); + relaxation_combo = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(relaxation_combo), "Locally Orthogonal Constraints"); + gtk_combo_box_append_text(GTK_COMBO_BOX(relaxation_combo), "Fienup Hybrid Input-Output"); + gtk_combo_box_append_text(GTK_COMBO_BOX(relaxation_combo), "Charge Flipping"); + gtk_box_pack_start(GTK_BOX(relaxation_hbox), relaxation_combo, TRUE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(vbox), relaxation_hbox, FALSE, FALSE, 5); + gtk_combo_box_set_active(GTK_COMBO_BOX(relaxation_combo), 0); + g_signal_connect(G_OBJECT(relaxation_combo), "changed", G_CALLBACK(elser_control_relaxation_change), elser_dialog); + + track_best = gtk_check_button_new_with_label("Track best solution"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(track_best), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(track_best), "toggled", G_CALLBACK(elser_control_trackbest_toggled), elser_dialog); + + /* Progress graph */ + elser_dialog->graph = gtk_value_graph_new(); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(elser_dialog->graph), TRUE, TRUE, 5); + elser_dialog->e_evolution = malloc(100*sizeof(double)); + elser_dialog->e_evolution_size = 100*sizeof(double); + gtk_value_graph_set_data(GTK_VALUE_GRAPH(elser_dialog->graph), elser_dialog->e_evolution, 0); + + /* Iteration number */ + elser_dialog->itnum = gtk_label_new("(uninitialised)"); + gtk_label_set_markup(GTK_LABEL(elser_dialog->itnum), "(uninitialised)"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(elser_dialog->itnum), FALSE, FALSE, 5); + + /* Control buttons */ + buttons = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(buttons), FALSE, FALSE, 5); + /* Reset */ + elser_dialog->reset = gtk_button_new_with_label("Reset"); + gtk_button_set_image(GTK_BUTTON(elser_dialog->reset), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->reset), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->reset), "clicked", G_CALLBACK(elser_control_reset), elser_dialog); + /* Stop */ + elser_dialog->stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->stop), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->stop), "clicked", G_CALLBACK(elser_control_stop), elser_dialog); + /* Go */ + elser_dialog->go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(elser_dialog->go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->go), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->go), "clicked", G_CALLBACK(elser_control_go), elser_dialog); + /* Iterate fixed point->solution */ + elser_dialog->solution = gtk_button_new_with_label("Solution"); + gtk_button_set_image(GTK_BUTTON(elser_dialog->solution), gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->solution), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->solution), "clicked", G_CALLBACK(elser_control_solution), elser_dialog); + + g_signal_connect(G_OBJECT(elser_dialog->window), "response", G_CALLBACK(elser_control_close), elser_dialog); + + gtk_widget_set_sensitive(elser_dialog->go, FALSE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + gtk_widget_show_all(elser_dialog->window); + displaywindow_forceview(DWV_REALSPACE); + + displaywindow_disablephasegrabbing(); + +} + +void elser_grab_phases(ReflectionList *reflections) { + + size_t i; + + if ( elser_dialog == NULL ) { + error_report("You need to keep the Elser dialog open"); + return; + } + + memcpy(elser_dialog->trans_out, elser_dialog->out, elser_dialog->array_size); + fftw_execute(elser_dialog->plan_t2i); + + for ( i=0; in_reflections; i++ ) { + + signed int h, k, l; + double re, im; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = 0; + + if ( abs(h) > elser_dialog->width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + reflections->refs[i].phase_known_set = 0; + continue; + } + if ( abs(k) > elser_dialog->height/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + reflections->refs[i].phase_known_set = 0; + continue; + } + if ( h < 0 ) { h = elser_dialog->width+h; } + if ( k < 0 ) { k = elser_dialog->height+k; } + + re = elser_dialog->in[k+elser_dialog->height*h][0]; + im = elser_dialog->in[k+elser_dialog->height*h][1]; + + reflections->refs[i].phase_known = atan2(im, re); + reflections->refs[i].phase_known_set = 1; + + } + +} + diff --git a/src/elser.h b/src/elser.h new file mode 100644 index 0000000..258b70d --- /dev/null +++ b/src/elser.h @@ -0,0 +1,25 @@ +/* + * elser.h + * + * Elser Difference Map Algorithm + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ELSER_H +#define ELSER_H + +#include "reflist.h" + +extern void elser_dialog_open(); +extern void elser_grab_phases(ReflectionList *reflections); + +#endif /* ELSER_H */ + diff --git a/src/geometry.c b/src/geometry.c new file mode 100644 index 0000000..7a2d621 --- /dev/null +++ b/src/geometry.c @@ -0,0 +1,324 @@ +/* + * geometry.c + * + * Geometrical (Lorentz) Corrections + * + * (c) 2006 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "main.h" + +#include "displaywindow.h" +#include "data.h" + +typedef struct { + + GtkWidget *wn_entry; /* Electon wavenumber */ + GtkWidget *cr_entry; /* Laue Circle radius */ + GtkWidget *wn_label; + GtkWidget *cr_label; + GtkWidget *wn_nm_label; + GtkWidget *cr_nm_label; + GeometryCorrectionType correction_type; + +} GeometryCorrectionWindow; + +static int geometry_window_open = 0; + +/* Apply "Lorentz" factor to reflections */ +void geometry_correct(ReflectionList *reflections, GeometryCorrectionType correction_type, double wavenumber, double R) { + + unsigned int i; + double a = data_a(); + double b = data_b(); + double c = data_c(); + + for ( i=1; in_reflections; i++ ) { + + double corr = 1; + double g, g_xy, g_z; + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + + /* Calculate g, g_xy and g_z */ + g = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + g_xy = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b))); + g_z = l/c; + + switch ( correction_type ) { + + case GEOMETRY_CORRECTION_TYPE_VINCENT_MIDGLEY : { + + /* Vincent-Midgley */ + /* eqn (9) from Ultramicroscopy vol 53 1994 p271-282 */ + double corr1, corr2, corr3; + + corr1 = g/(2*R); + corr2 = pow(corr1, 2); + corr3 = 1 - corr2; + corr = sqrt(corr3); + + break; + + } + + case GEOMETRY_CORRECTION_TYPE_GJONNES : { + + /* Gjonnes (without divergence) */ + /* eqn (9) from Ultramicroscopy 69 1997 p1-11 */ + double corr1, corr2, corr3, corr4; + + corr1 = 2*R*g_xy; corr1 = pow(corr1, 2); + corr2 = pow(g,2) - (2*wavenumber*g_z); corr2 = pow(corr2, 2); + corr3 = corr1 - corr2; + corr4 = 2*wavenumber; + corr = sqrt(corr3) / (2 * corr4); + + break; + + } + + case GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE : { + + /* Gjønnes (with divergence) */ + /* eqn (37) from Ultramicroscopy 69 1997 p1-11 */ + double corr1, corr2, corr3, corr3b, corr4, corr4a, corr4b; + + corr1 = pow(R, 3) / pow(wavenumber, 2); + corr2 = pow((g_xy / g), 2); + corr3b = (pow(g, 2) - 2*wavenumber*g_z) / (2*R*g_xy); + corr3 = 1 - pow(corr3b, 2); + corr4a = pow(((pow(g, 2) - (2*wavenumber*g_z))/wavenumber), 2); + corr4a = 1 - (corr4a / 2); + corr4b = (wavenumber*g_z)/pow(g, 2); + corr4b = 1 - (corr4b * 2); + corr4 = corr4a / corr4b; + corr = corr1 * corr2 * corr3 * corr4; + + //printf("corr1=%f, corr2=%f, corr3b=%f, corr3=%f, corr4a=%f, corr4b=%f, corr4=%f\n", + // corr1, corr2, corr3b, corr3, corr4a, corr4b, corr4); + + break; + + } + + } + + printf("%f %f\n", g, corr); + + if ( corr < 0 ) corr = 0; + + /* Square root here because we are working with amplitudes not intensities */ + reflections->refs[i].amplitude = sqrt(corr) * reflections->refs[i].amplitude; + assert( reflections->refs[i].amplitude >= 0 ); + + } + + displaywindow_switchview(); + +} + +static gint geometry_window_response(GtkWidget *geometry_window, gint response, GeometryCorrectionWindow *gcw) { + + if ( response == GTK_RESPONSE_OK ) { + + const char *str; + float wavenumber_f, circleradius_f; + double wavenumber, circleradius; + + str = gtk_entry_get_text(GTK_ENTRY(gcw->wn_entry)); + sscanf(str, "%f", &wavenumber_f); wavenumber = wavenumber_f; + + str = gtk_entry_get_text(GTK_ENTRY(gcw->cr_entry)); + sscanf(str, "%f", &circleradius_f); circleradius = circleradius_f; + + main_geometry_correct(gcw->correction_type, wavenumber, circleradius); + + } + + geometry_window_open = 0; + gtk_widget_destroy(geometry_window); + free(gcw); + + return 0; + +} + +static gint geometry_select_vm(GtkWidget *widget, GeometryCorrectionWindow *gcw) { + + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_VINCENT_MIDGLEY; + + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_entry), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_label), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_nm_label), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_nm_label), TRUE); + + return 0; + +} + +static gint geometry_select_gn(GtkWidget *widget, GeometryCorrectionWindow *gcw) { + + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_GJONNES; + + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_nm_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_nm_label), TRUE); + + return 0; + +} + +static gint geometry_select_gd(GtkWidget *widget, GeometryCorrectionWindow *gcw) { + + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE; + + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_nm_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_nm_label), TRUE); + + return 0; + +} + +/* Wavelength of an electron (in m) given accelerating potential (in V) */ +static double lambda(double V) { + + double m = 9.110E-31; + double h = 6.625E-34; + double e = 1.60E-19; + double c = 2.998E8; + + return h / sqrt(2*m*e*V*(1+((e*V) / (2*m*c*c)))); + +} + +static void geometry_set_defaults(GeometryCorrectionWindow *gcw) { + + double wn; + char string[32]; + int i; + double r; + ReflectionList *reflections; + + wn = 1/(lambda(300000)*1e9); /* Answer in nm^-1 */ + snprintf(string, 31, "%f", wn); + gtk_entry_set_text(GTK_ENTRY(gcw->wn_entry), string); + + reflections = main_reflist(); + r = 0; + double a = data_a(); + double b = data_b(); + for ( i=1; in_reflections; i++ ) { + + double g; + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + + g = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b))); + + if ( g > r ) r = g; + + } + + snprintf(string, 31, "%f", r); + gtk_entry_set_text(GTK_ENTRY(gcw->cr_entry), string); + +} + +void geometry_dialog_open() { + + GtkWidget *geometry_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *geometry_type_vm; + GtkWidget *geometry_type_gn; + GtkWidget *geometry_type_gd; + GeometryCorrectionWindow *gcw; + + if ( geometry_window_open ) { + return; + } + geometry_window_open = 1; + + gcw = malloc(sizeof(GeometryCorrectionWindow)); + + geometry_window = gtk_dialog_new_with_buttons("Geometrical Correction", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(geometry_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + table = gtk_table_new(5, 3, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + geometry_type_vm = gtk_radio_button_new_with_label(NULL, "Vincent-Midgley"); + geometry_type_gn = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(geometry_type_vm), + "Gjønnes (without divergence)"); + geometry_type_gd = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(geometry_type_vm), + "Gjønnes (with divergence)"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(geometry_type_gd), TRUE); + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE; + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(geometry_type_vm), 1, 4, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(geometry_type_gn), 1, 4, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(geometry_type_gd), 1, 4, 3, 4); + + gcw->wn_label = gtk_label_new("Electron Wavenumber:"); + gtk_misc_set_alignment(GTK_MISC(gcw->wn_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->wn_label), 1, 2, 4, 5); + gcw->wn_nm_label = gtk_label_new("nm^-1"); + gtk_label_set_markup(GTK_LABEL(gcw->wn_nm_label), "nm-1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->wn_nm_label), 3, 4, 4, 5); + + gcw->wn_entry = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->wn_entry), 2, 3, 4, 5); + + gcw->cr_label = gtk_label_new("Laue Circle Radius:"); + gtk_misc_set_alignment(GTK_MISC(gcw->cr_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->cr_label), 1, 2, 5, 6); + gcw->cr_nm_label = gtk_label_new("nm^-1"); + gtk_label_set_markup(GTK_LABEL(gcw->cr_nm_label), "nm-1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->cr_nm_label), 3, 4, 5, 6); + + gcw->cr_entry = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->cr_entry), 2, 3, 5, 6); + + geometry_set_defaults(gcw); + + g_signal_connect(G_OBJECT(geometry_type_vm), "clicked", G_CALLBACK(geometry_select_vm), gcw); + g_signal_connect(G_OBJECT(geometry_type_gn), "clicked", G_CALLBACK(geometry_select_gn), gcw); + g_signal_connect(G_OBJECT(geometry_type_gd), "clicked", G_CALLBACK(geometry_select_gd), gcw); + + g_signal_connect(G_OBJECT(geometry_window), "response", G_CALLBACK(geometry_window_response), gcw); + + gtk_widget_show_all(geometry_window); + + gtk_widget_grab_focus(GTK_WIDGET(gcw->wn_entry)); + +} diff --git a/src/geometry.h b/src/geometry.h new file mode 100644 index 0000000..80f9c6d --- /dev/null +++ b/src/geometry.h @@ -0,0 +1,29 @@ +/* + * geometry.h + * + * Geometrical (Lorentz) Corrections + * + * (c) 2006 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef GEOMETRY_H +#define GEOMETRY_H + +#include "reflist.h" + +typedef enum { + GEOMETRY_CORRECTION_TYPE_VINCENT_MIDGLEY, + GEOMETRY_CORRECTION_TYPE_GJONNES, + GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE +} GeometryCorrectionType; + +extern void geometry_correct(ReflectionList *reflections, GeometryCorrectionType correction_type, double wavenumber, double R); +extern void geometry_dialog_open(void); + +#endif /* GEOMETRY_H */ diff --git a/src/gsf.c b/src/gsf.c new file mode 100644 index 0000000..aa18da8 --- /dev/null +++ b/src/gsf.c @@ -0,0 +1,669 @@ +/* + * gsf.c + * + * Gerchberg-Saxton-Fienup iteration algorithms + * + * (c) 2006-2007 Thomas White + * (c) 2008 Alex Eggeman + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "displaywindow.h" +#include "symmetry.h" +#include "gtk-symmetry.h" +#include "reflist.h" + + +GtkWidget *gsfw_iterations = NULL; +static int gsf_window_open = 0; +static int gsf_running = 0; +static int gsf_n_iterations = 0; +static int gsf_centroid = 0; +static int gsf_atomicity = 0; +static int gsf_zerofzero = 1; +static int gsf_fracsup = 0; +static int gsf_threshsup = 1; +static double gsf_fracsup_phi = 0.15; +static double gsf_hio_beta = 0.15; +static double gsf_threshsup_delta = 0; /* i.e. all positive values are in the support */ +Symmetry gsf_symmetry = PLANEGROUP_P1; +fftw_complex *gsf_drive = NULL; +fftw_complex *gsf_drive_old = NULL; +unsigned int *gsf_support = NULL; +fftw_complex *gsf_in = NULL; +fftw_complex *gsf_out = NULL; +fftw_plan gsf_plan_i2d; +fftw_plan gsf_plan_d2i; +ReflectionList *gsf_reflections = NULL; +static int gsf_antistag = 0; + +static void gsf_pin_centroid(fftw_complex *out, fftw_complex *drive, unsigned int width, unsigned int height) { + + unsigned int x, y; + double xc = 0; + double yc = 0; + double total_int = 0; + signed int xs; + signed int ys; + fftw_complex *out_copy; + + for ( x=0; xn_reflections; i++ ) { + + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + in_new[k + height*h][0] = in[k + height*h][0]; + in_new[k + height*h][1] = in[k + height*h][1]; + + } + + memcpy(in, in_new, width*height*sizeof(fftw_complex)); + free(in_new); + +} + +/* Perform a single GSF iteration */ +void gsf_iteration(int gsf_n_iterations) { + + unsigned int x, y; + double mean; + double top; + unsigned int mean_n; + double fzero; + unsigned int i; + unsigned int width = data_width(); + unsigned int height = data_height(); + + assert(gsf_drive_old != NULL); + assert(gsf_drive != NULL); + assert(gsf_in != NULL); + assert(gsf_out != NULL); + + /* Copy this structure if required */ + memcpy(gsf_drive_old, gsf_drive, width*height*sizeof(fftw_complex)); + + /* Transform back to reciprocal space and scale. This time, the driving function is + being transformed. */ + fftw_execute(gsf_plan_d2i); + for ( x=0; xn_reflections; i++ ) { + + double re, im; + double am, ph; + signed int h, k; + + h = gsf_reflections->refs[i].h; + k = gsf_reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + re = gsf_in[k + height*h][0]; + im = gsf_in[k + height*h][1]; + ph = atan2(im, re); + am = gsf_reflections->refs[i].amplitude; + + gsf_in[k + height*h][0] = am*cos(ph); + gsf_in[k + height*h][1] = am*sin(ph); + + } + + fzero = gsf_in[0][0]; + gsf_mask(gsf_reflections, gsf_in, width, height); + if ( !gsf_zerofzero ) { + gsf_in[0][0] = fzero; + } + + /* Impose Symmetry */ + + symmetry_symmetrise_array(gsf_in, width, height, gsf_symmetry); + + /* Transform to real space */ + fftw_execute(gsf_plan_i2d); + + if ( gsf_centroid ) { + gsf_pin_centroid(gsf_drive, gsf_drive_old, width, height); + } + + memcpy(gsf_out, gsf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + + /* Determine the support */ + if ( gsf_threshsup && !gsf_fracsup ) { + + /* Threshold support */ + for ( y=0; y gsf_threshsup_delta ) { + gsf_support[y+height*x] = 1; + } else { + gsf_support[y+height*x] = 0; + } + + if ( gsf_antistag && ((x+y)>width) ) { + gsf_support[y+height*x] = 0; + } + + } + } + + } else if ( gsf_fracsup ) { + + /* 'Fractional' support */ + double max; + unsigned int max_x, max_y; + int n, target, i; + + + n = height*width; + target = n * gsf_fracsup_phi; + i = 0; + + while ( i < target ) { + max_x = 0; max_y = 0; max = -1e10; + for ( y=0; y 0) ) { + if ( ( am > max) && (!gsf_support[y+height*x]) ) { + max_x = x; max_y = y; max = am; + } + } + } + } + gsf_support[max_y+height*max_x] = 1; + i++; + } + + } else { + + /* No options selected for support - consider everything valid */ + for ( y=0; y 0 ) { + mean += am; + mean_n++; + if ( am > top ) top = am; + + } + } + } + + mean = mean / mean_n; + for ( y=0; yn_reflections; i++ ) { + + double am, ph; + signed int h, k; + + h = gsf_reflections->refs[i].h; + k = gsf_reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + am = gsf_reflections->refs[i].amplitude; + ph = (((double)random())/RAND_MAX) * (2*M_PI); + + gsf_in[k + height*h][0] = am*cos(ph); + gsf_in[k + height*h][1] = am*sin(ph); + + } + fftw_execute(gsf_plan_i2d); + + memcpy(gsf_out, gsf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + +} + +void gsf_initialise(ReflectionList *reflections) { + + gsf_drive_old = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + gsf_drive = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + gsf_support = fftw_malloc(data_width()*data_height()*sizeof(unsigned int)); + gsf_in = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + memset(gsf_in, 0, data_width()*data_height()*sizeof(fftw_complex)); + if ( !gsf_out ) gsf_out = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + + gsf_plan_i2d = fftw_plan_dft_2d(data_width(), data_height(), gsf_in, gsf_drive, FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + gsf_plan_d2i = fftw_plan_dft_2d(data_width(), data_height(), gsf_drive, gsf_in, FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + gsf_reflections = malloc(sizeof(ReflectionList)); + memcpy(gsf_reflections, reflections, sizeof(ReflectionList)); + + displaywindow_set_realspace(gsf_out, DWR_GSF); + gsf_reset(reflections); + +} + +static gint gsf_window_go(GtkWidget *gsf_go) { + + if ( gsf_running ) { + return 0; + } + + gsf_running = 1; + while ( gsf_running ) { + gsf_n_iterations++; + gsf_update_iterations(); + gsf_iteration(gsf_n_iterations); + while ( gtk_events_pending() ) gtk_main_iteration(); + } + return 0; + +} + +static gint gsf_window_stop(GtkWidget *widget, gpointer data) { + gsf_running = 0; + return 0; +} + +static gint gsf_window_reset(GtkWidget *widget) { + gsf_running = 0; + main_gsf_reset(); + return 0; +} + +static gint gsf_symmetry_changed(GtkWidget *widget, gpointer data) { + gsf_symmetry = gtk_symmetry_get_symmetry(GTK_SYMMETRY(widget)); + return 0; +} + +void gsf_dialog_open() { + + GtkWidget *gsfw_window; + GtkWidget *gsfw_go; + GtkWidget *gsfw_stop; + GtkWidget *gsfw_reset; + GtkWidget *gsfw_gostop_hbox; + GtkWidget *gsfw_centroid; + GtkWidget *gsfw_atomicity; + GtkWidget *gsfw_zerofzero; + GtkWidget *gsfw_antistag; + GtkWidget *gsfw_fracsup; + GtkWidget *gsfw_fracsup_phi; + GtkWidget *gsfw_fracsup_hbox; + GtkWidget *gsfw_hio_beta; + GtkWidget *gsfw_hio_hbox; + GtkWidget *gsfw_threshsup; + GtkWidget *gsfw_threshsup_delta; + GtkWidget *gsfw_threshsup_hbox; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *sym_define; + GtkWidget *gsfw_support_label; + GtkWidget *gsfw_process_label; + char *text; + + if ( gsf_window_open ) { + return; + } + gsf_window_open = 1; + + gsfw_window = gtk_dialog_new_with_buttons("Projection Iteration", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(gsfw_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + gsfw_iterations = gtk_label_new(""); + text = malloc(40); + snprintf(text, 39, "Iterations performed so far: %i", gsf_n_iterations); + gtk_label_set_text(GTK_LABEL(gsfw_iterations), text); + free(text); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_iterations), FALSE, FALSE, 5); + + sym_define = gtk_symmetry_new(2, 2, TRUE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(sym_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(sym_define), "changed", G_CALLBACK(gsf_symmetry_changed), NULL); + gsf_symmetry = PLANEGROUP_P1; + + gsfw_centroid = gtk_check_button_new_with_label("Pin centroid"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_centroid), FALSE, FALSE, 5); + if ( gsf_centroid ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_centroid), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_centroid), FALSE); + } + gsfw_atomicity = gtk_check_button_new_with_label("Atomicity"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_atomicity), FALSE, FALSE, 5); + if ( gsf_atomicity ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_atomicity), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_atomicity), FALSE); + } + gsfw_zerofzero = gtk_check_button_new_with_label("Zero F(00)"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_zerofzero), FALSE, FALSE, 5); + if ( gsf_zerofzero ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_zerofzero), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_zerofzero), FALSE); + } + gsfw_antistag = gtk_check_button_new_with_label("Anti-stagnation"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_antistag), FALSE, FALSE, 5); + if ( gsf_antistag ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_antistag), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_antistag), FALSE); + } + + gsfw_support_label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(gsfw_support_label), "Dynamic Support"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_support_label), FALSE, FALSE, 10); + + gsfw_fracsup = gtk_radio_button_new_with_label(NULL, "Fraction of most positive points"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_fracsup), FALSE, FALSE, 5); + if ( gsf_fracsup ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_fracsup), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_fracsup), FALSE); + } + gsfw_fracsup_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_fracsup_hbox), FALSE, FALSE, 5); + gsfw_fracsup_phi_label = gtk_label_new("Φ:"); + gtk_box_pack_start(GTK_BOX(gsfw_fracsup_hbox), GTK_WIDGET(gsfw_fracsup_phi_label), FALSE, FALSE, 5); + gsfw_fracsup_phi = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_value_pos(GTK_SCALE(gsfw_fracsup_phi), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(gsfw_fracsup_hbox), GTK_WIDGET(gsfw_fracsup_phi), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(gsfw_fracsup_phi), gsf_hio_beta); + if ( gsf_fracsup) { + gtk_widget_set_sensitive(gsfw_fracsup_phi, TRUE); + gtk_widget_set_sensitive(gsfw_fracsup_phi_label, TRUE); + } else { + gtk_widget_set_sensitive(gsfw_fracsup_phi, FALSE); + gtk_widget_set_sensitive(gsfw_fracsup_phi_label, FALSE); + } + + gsfw_threshsup = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gsfw_fracsup), "Points above threshold"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_threshsup), FALSE, FALSE, 5); + if ( gsf_threshsup ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_threshsup), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_threshsup), FALSE); + } + gsfw_threshsup_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_threshsup_hbox), FALSE, FALSE, 5); + gsfw_threshsup_delta_label = gtk_label_new("δ:"); + gtk_box_pack_start(GTK_BOX(gsfw_threshsup_hbox), GTK_WIDGET(gsfw_threshsup_delta_label), FALSE, FALSE, 5); + gsfw_threshsup_delta = gtk_hscale_new_with_range(-100, 100, 1); + gtk_scale_set_value_pos(GTK_SCALE(gsfw_threshsup_delta), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(gsfw_threshsup_hbox), GTK_WIDGET(gsfw_threshsup_delta), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(gsfw_threshsup_delta), gsf_threshsup_delta); + if ( gsf_threshsup ) { + gtk_widget_set_sensitive(gsfw_threshsup_delta, TRUE); + gtk_widget_set_sensitive(gsfw_threshsup_delta_label, TRUE); + } else { + gtk_widget_set_sensitive(gsfw_threshsup_delta, FALSE); + gtk_widget_set_sensitive(gsfw_threshsup_delta_label, FALSE); + } + + gsfw_process_label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(gsfw_process_label), "Points Outside Support"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_process_label), FALSE, FALSE, 10); + + gsfw_hio_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_hio_hbox), FALSE, FALSE, 5); + gsfw_hio_beta_label = gtk_label_new("β:"); + gtk_box_pack_start(GTK_BOX(gsfw_hio_hbox), GTK_WIDGET(gsfw_hio_beta_label), FALSE, FALSE, 5); + gsfw_hio_beta = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_value_pos(GTK_SCALE(gsfw_hio_beta), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(gsfw_hio_hbox), GTK_WIDGET(gsfw_hio_beta), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(gsfw_hio_beta), gsf_hio_beta); + + gsfw_gostop_hbox = gtk_hbox_new(FALSE, 0); + gsfw_go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(gsfw_go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + gsfw_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + gsfw_reset = gtk_button_new_with_label("Reset"); + gtk_button_set_image(GTK_BUTTON(gsfw_reset), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(gsfw_gostop_hbox), GTK_WIDGET(gsfw_reset), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(gsfw_gostop_hbox), GTK_WIDGET(gsfw_stop), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(gsfw_gostop_hbox), GTK_WIDGET(gsfw_go), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_gostop_hbox), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(gsfw_go), "clicked", G_CALLBACK(gsf_window_go), NULL); + g_signal_connect(G_OBJECT(gsfw_stop), "clicked", G_CALLBACK(gsf_window_stop), NULL); + g_signal_connect(G_OBJECT(gsfw_reset), "clicked", G_CALLBACK(gsf_window_reset), NULL); + + g_signal_connect(G_OBJECT(gsfw_window), "response", G_CALLBACK(gsf_window_close), NULL); + g_signal_connect(G_OBJECT(gsfw_window), "delete-event", G_CALLBACK(gsf_window_stop), NULL); + g_signal_connect(G_OBJECT(gsfw_centroid), "toggled", G_CALLBACK(gsf_centroid_toggled), NULL); + g_signal_connect(G_OBJECT(gsfw_atomicity), "toggled", G_CALLBACK(gsf_atomicity_toggled), NULL); + g_signal_connect(G_OBJECT(gsfw_zerofzero), "toggled", G_CALLBACK(gsf_zerofzero_toggled), NULL); + g_signal_connect(G_OBJECT(gsfw_fracsup), "toggled", G_CALLBACK(gsf_fracsup_toggled), gsfw_fracsup_phi); + g_signal_connect(G_OBJECT(gsfw_fracsup_phi), "value-changed", G_CALLBACK(gsf_fracsup_phichanged), NULL); + g_signal_connect(G_OBJECT(gsfw_threshsup), "toggled", G_CALLBACK(gsf_threshsup_toggled), gsfw_threshsup_delta); + g_signal_connect(G_OBJECT(gsfw_threshsup_delta), "value-changed", G_CALLBACK(gsf_threshsup_deltachanged), NULL); + g_signal_connect(G_OBJECT(gsfw_hio_beta), "value-changed", G_CALLBACK(gsf_hio_betachanged), NULL); + g_signal_connect(G_OBJECT(gsfw_antistag), "toggled", G_CALLBACK(gsf_antistag_toggled), NULL); + g_signal_connect(G_OBJECT(displaywindow_gtkwindow()), "delete-event", G_CALLBACK(gsf_window_stop), NULL); + + main_gsf_initialise(); + displaywindow_forceview(DWV_REALSPACE); + + gtk_widget_show_all(gsfw_window); + +} diff --git a/src/gsf.h b/src/gsf.h new file mode 100644 index 0000000..a2bd5d8 --- /dev/null +++ b/src/gsf.h @@ -0,0 +1,30 @@ +/* + * gsf.h + * + * Gerchberg-Saxton-Fienup and other Projection iteration algorithms + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef GSF_H +#define GSF_H + +#include "reflist.h" +#include "symmetry.h" + +extern void gsf_dpsynth_main_update(ReflectionList *gsf_listout); +extern void gsf_dpsynth_main_open(ReflectionList *gsf_listout); + +extern void gsf_dialog_open(void); +extern void gsf_initialise(ReflectionList *reflections); +extern void gsf_reset(ReflectionList *reflections); +extern void gsf_mask(ReflectionList *reflections, fftw_complex *in, unsigned int width, unsigned int height); + +#endif /* GSF_H */ diff --git a/src/gtk-symmetry.c b/src/gtk-symmetry.c new file mode 100644 index 0000000..60b06b8 --- /dev/null +++ b/src/gtk-symmetry.c @@ -0,0 +1,243 @@ +/* + * gtk-symmetry.c + * + * A simple widget to select a symmetry group + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#include + +#include "gtk-symmetry.h" + +static GtkObjectClass *parent_class = NULL; + +static void gtk_symmetry_destroy(GtkObject *gtk_symmetry) { + + parent_class->destroy(gtk_symmetry); + +} + +static gint gtk_symmetry_changed(GtkWidget *selection, GtkSymmetry *gtk_symmetry) { + g_signal_emit_by_name(G_OBJECT(gtk_symmetry), "changed"); + return 0; +} + +static gint gtk_symmetry_do_packing(GtkWidget *widget, GtkObject *old_parent, GtkSymmetry *gtk_symmetry) { + + if ( gtk_symmetry->has_friedel ) { + if ( gtk_symmetry->friedel_pos == GTK_POS_RIGHT ) { + gtk_symmetry->table = gtk_table_new(1, 3, FALSE); + } else { + gtk_symmetry->table = gtk_table_new(2, 2, FALSE); + } + } else { + gtk_symmetry->table = gtk_table_new(1, 2, FALSE); + } + gtk_table_set_row_spacings(GTK_TABLE(gtk_symmetry->table), 5); + gtk_table_set_col_spacings(GTK_TABLE(gtk_symmetry->table), 5); + + gtk_symmetry->label = gtk_label_new("Planegroup:"); + gtk_misc_set_alignment(GTK_MISC(gtk_symmetry->label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), GTK_WIDGET(gtk_symmetry->label), 1, 2, 1, 2); + + gtk_symmetry->selection = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p1"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pm (m // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pm (m // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pg (g // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pg (g // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "cm (m // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "cm (m // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2mm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2mg (m // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2mg (m // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2gg"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "c2mm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p4"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p4mm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p4gm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p3"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p3m1"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p31m"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p6"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p6mm"); + gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_symmetry->selection), 0); + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), GTK_WIDGET(gtk_symmetry->selection), 2, 3, 1, 2); + g_signal_connect(G_OBJECT(gtk_symmetry->selection), "changed", G_CALLBACK(gtk_symmetry_changed), gtk_symmetry); + + gtk_symmetry->friedel = gtk_check_button_new_with_label("Friedel"); + if ( gtk_symmetry->has_friedel ) { + if ( gtk_symmetry->friedel_pos == GTK_POS_RIGHT ) { + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), gtk_symmetry->friedel, 3, 4, 1, 2); + } else { + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), gtk_symmetry->friedel, 2, 3, 2, 3); + } + } + g_signal_connect(G_OBJECT(gtk_symmetry->friedel), "toggled", G_CALLBACK(gtk_symmetry_changed), gtk_symmetry); + + gtk_box_pack_start(GTK_BOX(gtk_symmetry), GTK_WIDGET(gtk_symmetry->table), TRUE, TRUE, 5); + + return 0; + +} + +GtkWidget *gtk_symmetry_new(unsigned int dimensions, unsigned int trans_dimensions, gboolean has_friedel) { + + GtkSymmetry *gtk_symmetry; + + g_return_val_if_fail(dimensions >= trans_dimensions, NULL); + g_return_val_if_fail(dimensions < 4, NULL); + g_return_val_if_fail(dimensions < 4, NULL); + + gtk_symmetry = GTK_SYMMETRY(gtk_type_new(gtk_symmetry_get_type())); + + gtk_symmetry->dimensions = dimensions; + gtk_symmetry->trans_dimensions = trans_dimensions; + gtk_symmetry->has_friedel = has_friedel; + gtk_symmetry->friedel_pos = GTK_POS_BOTTOM; + + g_signal_connect(G_OBJECT(gtk_symmetry), "parent-set", G_CALLBACK(gtk_symmetry_do_packing), gtk_symmetry); + + return GTK_WIDGET(gtk_symmetry); + +} + +static GObject *gtk_symmetry_constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { + + GtkSymmetryClass *class; + GObjectClass *p_class; + GObject *obj; + + class = GTK_SYMMETRY_CLASS(g_type_class_peek(gtk_symmetry_get_type())); + p_class = G_OBJECT_CLASS(g_type_class_peek_parent(class)); + + obj = p_class->constructor(type, n_construct_properties, construct_properties); + + return obj; + +} + +static void gtk_symmetry_class_init(GtkSymmetryClass *class) { + + GtkObjectClass *object_class; + GObjectClass *g_object_class; + + object_class = (GtkObjectClass *) class; + g_object_class = G_OBJECT_CLASS(class); + + object_class->destroy = gtk_symmetry_destroy; + g_object_class->constructor = gtk_symmetry_constructor; + + parent_class = gtk_type_class(gtk_hbox_get_type()); + + g_signal_new("changed", gtk_symmetry_get_type(), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(GtkSymmetryClass, changed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + +} + +Symmetry gtk_symmetry_get_symmetry(GtkSymmetry *gtk_symmetry) { + + Symmetry symmetry = PLANEGROUP_P1; + + g_return_val_if_fail(GTK_IS_SYMMETRY(gtk_symmetry), symmetry); + + switch ( gtk_combo_box_get_active(GTK_COMBO_BOX(GTK_SYMMETRY(gtk_symmetry)->selection)) ) { + case 0 : symmetry = PLANEGROUP_P1; break; + case 1 : symmetry = PLANEGROUP_P2; break; + case 2 : symmetry = PLANEGROUP_PM_X; break; + case 3 : symmetry = PLANEGROUP_PM_Y; break; + case 4 : symmetry = PLANEGROUP_PG_X; break; + case 5 : symmetry = PLANEGROUP_PG_Y; break; + case 6 : symmetry = PLANEGROUP_CM_X; break; + case 7 : symmetry = PLANEGROUP_CM_Y; break; + case 8 : symmetry = PLANEGROUP_P2MM; break; + case 9 : symmetry = PLANEGROUP_P2MG_X; break; + case 10 : symmetry = PLANEGROUP_P2MG_Y; break; + case 11 : symmetry = PLANEGROUP_P2GG; break; + case 12 : symmetry = PLANEGROUP_C2MM; break; + case 13 : symmetry = PLANEGROUP_P4; break; + case 14 : symmetry = PLANEGROUP_P4MM; break; + case 15 : symmetry = PLANEGROUP_P4GM; break; + case 16 : symmetry = PLANEGROUP_P3; break; + case 17 : symmetry = PLANEGROUP_P3M1; break; + case 18 : symmetry = PLANEGROUP_P31M; break; + case 19 : symmetry = PLANEGROUP_P6; break; + case 20 : symmetry = PLANEGROUP_P6MM; break; + } + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(GTK_SYMMETRY(gtk_symmetry)->friedel)) ) { + symmetry = symmetry | SYMMETRY_FRIEDEL; + } + + return symmetry; + +} + +Symmetry gtk_symmetry_set_symmetry(GtkSymmetry *symmetry, Symmetry new_sym) { + + Symmetry old_sym; + gint idx = 0; + + old_sym = gtk_symmetry_get_symmetry(symmetry); + + if ( new_sym == PLANEGROUP_P1 ) idx = 0; + if ( new_sym == PLANEGROUP_P2 ) idx = 1; + if ( new_sym == PLANEGROUP_PM_X ) idx = 2; + if ( new_sym == PLANEGROUP_PM_Y ) idx = 3; + if ( new_sym == PLANEGROUP_PG_X ) idx = 4; + if ( new_sym == PLANEGROUP_PG_Y ) idx = 5; + if ( new_sym == PLANEGROUP_CM_X ) idx = 6; + if ( new_sym == PLANEGROUP_CM_Y ) idx = 7; + if ( new_sym == PLANEGROUP_P2MM ) idx = 8; + if ( new_sym == PLANEGROUP_P2MG_X ) idx = 9; + if ( new_sym == PLANEGROUP_P2MG_Y ) idx = 10; + if ( new_sym == PLANEGROUP_P2GG ) idx = 11; + if ( new_sym == PLANEGROUP_C2MM ) idx = 12; + if ( new_sym == PLANEGROUP_P4 ) idx = 13; + if ( new_sym == PLANEGROUP_P4MM ) idx = 14; + if ( new_sym == PLANEGROUP_P4GM ) idx = 15; + if ( new_sym == PLANEGROUP_P3 ) idx = 16; + if ( new_sym == PLANEGROUP_P3M1 ) idx = 17; + if ( new_sym == PLANEGROUP_P31M ) idx = 18; + if ( new_sym == PLANEGROUP_P6 ) idx = 19; + if ( new_sym ==PLANEGROUP_P6MM ) idx = 20; + + gtk_combo_box_set_active(GTK_COMBO_BOX(GTK_SYMMETRY(symmetry)->selection), idx); + + return old_sym; + +} + +static void gtk_symmetry_init(GtkSymmetry *gtk_symmetry) { + +} + +guint gtk_symmetry_get_type(void) { + + static guint gtk_symmetry_type = 0; + + if ( !gtk_symmetry_type ) { + + GtkTypeInfo gtk_symmetry_info = { + "GtkSymmetry", + sizeof(GtkSymmetry), + sizeof(GtkSymmetryClass), + (GtkClassInitFunc) gtk_symmetry_class_init, + (GtkObjectInitFunc) gtk_symmetry_init, + NULL, + NULL, + (GtkClassInitFunc) NULL, + }; + gtk_symmetry_type = gtk_type_unique(gtk_hbox_get_type(), >k_symmetry_info); + + } + + return gtk_symmetry_type; + +} + diff --git a/src/gtk-symmetry.h b/src/gtk-symmetry.h new file mode 100644 index 0000000..d50651e --- /dev/null +++ b/src/gtk-symmetry.h @@ -0,0 +1,50 @@ +/* + * gtk-symmetry.h + * + * A simple widget to select a symmetry group + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef GTKSYMMETRY_H +#define GTKSYMMETRY_H + +#include + +#include "symmetry.h" + +typedef struct { + + GtkHBox parent; /* Parent widget */ + + unsigned int dimensions; /* 1D, 2D, 3D, more...? */ + unsigned int trans_dimensions; /* Number of dimensions of translational symmetry */ + gboolean has_friedel; + GtkPositionType friedel_pos; + + GtkWidget *label; + GtkWidget *selection; + GtkWidget *table; + GtkWidget *friedel; + +} GtkSymmetry; + +typedef struct { + GtkHBoxClass parent_class; + void (* changed) (GtkSymmetry *gtksymmetry); +} GtkSymmetryClass; + +extern guint gtk_symmetry_get_type(void); +extern GtkWidget *gtk_symmetry_new(unsigned int dimensions, unsigned int trans_dimensions, gboolean has_friedel); +extern Symmetry gtk_symmetry_get_symmetry(GtkSymmetry *symmetry); +extern Symmetry gtk_symmetry_set_symmetry(GtkSymmetry *symmetry, Symmetry new_sym); + +#define GTK_SYMMETRY(obj) GTK_CHECK_CAST(obj, gtk_symmetry_get_type(), GtkSymmetry) +#define GTK_SYMMETRY_CLASS(class) GTK_CHECK_CLASS_CAST(class, gtk_symmetry_get_type(), GtkSymmetryClass) +#define GTK_IS_SYMMETRY(obj) GTK_CHECK_TYPE(obj, gtk_symmetry_get_type()) + +#endif /* GTKSYMMETRY_H */ + diff --git a/src/gtk-valuegraph.c b/src/gtk-valuegraph.c new file mode 100644 index 0000000..90a3469 --- /dev/null +++ b/src/gtk-valuegraph.c @@ -0,0 +1,234 @@ +/* + * gtk-valuegraph.c + * + * A widget to display a graph of a sequence of values + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#include +#include + +#include "gtk-valuegraph.h" + +static GtkObjectClass *parent_class = NULL; + +static void gtk_value_graph_destroy(GtkObject *gtk_value_graph) { + parent_class->destroy(gtk_value_graph); +} + +GtkWidget *gtk_value_graph_new() { + + GtkValueGraph *gtk_value_graph; + + gtk_value_graph = GTK_VALUE_GRAPH(gtk_type_new(gtk_value_graph_get_type())); + gtk_value_graph->data = NULL; + gtk_value_graph->n = 0; + + return GTK_WIDGET(gtk_value_graph); + +} + +static GObject *gtk_value_graph_constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { + + GtkValueGraphClass *class; + GObjectClass *p_class; + GObject *obj; + + class = GTK_VALUE_GRAPH_CLASS(g_type_class_peek(gtk_value_graph_get_type())); + p_class = G_OBJECT_CLASS(g_type_class_peek_parent(class)); + + obj = p_class->constructor(type, n_construct_properties, construct_properties); + + return obj; + +} + +static void gtk_value_graph_class_init(GtkValueGraphClass *class) { + + GtkObjectClass *object_class; + GObjectClass *g_object_class; + + object_class = (GtkObjectClass *) class; + g_object_class = G_OBJECT_CLASS(class); + + object_class->destroy = gtk_value_graph_destroy; + g_object_class->constructor = gtk_value_graph_constructor; + + parent_class = gtk_type_class(gtk_drawing_area_get_type()); + +} + + +static gint gtk_value_graph_draw(GtkWidget *graph, GdkEventExpose *event, gpointer data) { + + GtkValueGraph *vg; + unsigned int bw_left, bw_right, bw_top, bw_bottom; + PangoLayout *y0_layout; + PangoLayout *y1_layout; + PangoLayout *x0_layout; + PangoLayout *x1_layout; + PangoRectangle y0_extent, y1_extent, x0_extent, x1_extent; + unsigned int width, height; + char tmp[32]; + unsigned int i; + + vg = GTK_VALUE_GRAPH(graph); + + /* Blank white background */ + gdk_draw_rectangle(graph->window, graph->style->white_gc, TRUE, 0, 0, graph->allocation.width, graph->allocation.height); + + /* Create PangoLayouts for labels */ + y0_layout = gtk_widget_create_pango_layout(graph, "0"); + pango_layout_get_pixel_extents(y0_layout, NULL, &y0_extent); + + if ( fabs(log(vg->ymax)/log(10)) < 3 ) { + snprintf(tmp, 31, "%.4f", vg->ymax); + } else { + snprintf(tmp, 31, "%1.1e", vg->ymax); + } + y1_layout = gtk_widget_create_pango_layout(graph, tmp); + pango_layout_get_pixel_extents(y1_layout, NULL, &y1_extent); + + x0_layout = gtk_widget_create_pango_layout(graph, "0"); + pango_layout_get_pixel_extents(x0_layout, NULL, &x0_extent); + + if ( vg->xmax < 1000 ) { + snprintf(tmp, 31, "%i", vg->xmax); + } else { + snprintf(tmp, 31, "%1.1e", (double)vg->xmax); + } + x1_layout = gtk_widget_create_pango_layout(graph, tmp); + pango_layout_get_pixel_extents(x1_layout, NULL, &x1_extent); + + /* Determine border widths */ + bw_left = 1+((y1_extent.width > y0_extent.width) ? y1_extent.width : y0_extent.width); + bw_right = 1+x1_extent.width/2; + bw_top = 1+y1_extent.height/2; + bw_bottom = 1+((x1_extent.height > x0_extent.height) ? x1_extent.height : x0_extent.height); + width = graph->allocation.width; + height = graph->allocation.height; + + /* Draw axis lines */ + gdk_draw_line(graph->window, graph->style->black_gc, bw_left, height-1-bw_bottom, bw_left, bw_top); + gdk_draw_line(graph->window, graph->style->black_gc, bw_left, height-1-bw_bottom, width-1-bw_right, height-1-bw_bottom); + + /* Label axes */ + gdk_draw_layout(graph->window, graph->style->black_gc, 1+bw_left-x0_extent.width/2, height-1-bw_bottom, x0_layout); + gdk_draw_layout(graph->window, graph->style->black_gc, width-bw_right-x1_extent.width/2, height-1-bw_bottom, x1_layout); + gdk_draw_layout(graph->window, graph->style->black_gc, bw_left-y0_extent.width-1, height-1-bw_bottom-y0_extent.height/2, y0_layout); + gdk_draw_layout(graph->window, graph->style->black_gc, bw_left-y1_extent.width-1, 1, y1_layout); + + /* Plot data */ + for ( i=0; in; i++ ) { + + unsigned int x, y; + double xd, yd; + + xd = (((double)width-bw_left-bw_right)/(double)vg->xmax)*(double)(i+1); /* Graph axes go from 1 */ + x = bw_left + xd; + yd = (((double)height-bw_top-bw_bottom)/(double)vg->ymax)*(double)vg->data[i]; + y = height-bw_bottom - yd; + + gdk_draw_point(graph->window, graph->style->black_gc, x, y); + + } + + return 0; + +} + +static void gtk_value_graph_init(GtkValueGraph *gtk_value_graph) { + gtk_widget_set_size_request(GTK_WIDGET(gtk_value_graph), 100, 200); + g_signal_connect(G_OBJECT(gtk_value_graph), "expose_event", G_CALLBACK(gtk_value_graph_draw), NULL); +} + +guint gtk_value_graph_get_type(void) { + + static guint gtk_value_graph_type = 0; + + if ( !gtk_value_graph_type ) { + + GtkTypeInfo gtk_value_graph_info = { + "GtkValueGraph", + sizeof(GtkValueGraph), + sizeof(GtkValueGraphClass), + (GtkClassInitFunc) gtk_value_graph_class_init, + (GtkObjectInitFunc) gtk_value_graph_init, + NULL, + NULL, + (GtkClassInitFunc) NULL, + }; + gtk_value_graph_type = gtk_type_unique(gtk_drawing_area_get_type(), >k_value_graph_info); + + } + + return gtk_value_graph_type; + +} + +static double gtk_value_graph_peak(double *data, unsigned int n) { + + unsigned int i; + double max; + + if ( n == 0 ) return 1; + + max = 0; + for ( i=0; i max ) max = data[i]; + } + + return max; + +} + +/* Calculate the best range for a axis with maximum value n */ +static double gtk_value_graph_axis_max(double n) { + + double mantissa, exponent, test; +return n; + if ( n == 0 ) return 1; + + /* Convert to standard form */ + exponent = rint(log(n)/log(10)); + mantissa = n / pow(10, exponent); + + /* Check if the value can be exactly represented */ + test = mantissa * 10; + test = rint(test); + test /= 10; + if ( fabs(test - mantissa) > 0.001 ) { + + /* Round the mantissa upwards */ + mantissa += 0.1; + mantissa *= 10; + mantissa = rint(mantissa); + mantissa /= 10; + + } /* Else don't touch it */ + + return mantissa*pow(10, exponent); + +} + +void gtk_value_graph_set_data(GtkValueGraph *vg, double *data, unsigned int n) { + + double dmax; + + /* Recalculate axes */ + dmax = gtk_value_graph_peak(data, n); + vg->data = data; + vg->n = n; + vg->xmax = gtk_value_graph_axis_max(n); + vg->ymax = gtk_value_graph_axis_max(dmax); + + //printf("n=%i, dmax=%f => xmax=%i, ymax=%f\n", n, dmax, vg->xmax, vg->ymax); + + /* Schedule redraw */ + gtk_widget_queue_draw_area(GTK_WIDGET(vg), 0, 0, GTK_WIDGET(vg)->allocation.width, GTK_WIDGET(vg)->allocation.height); + +} diff --git a/src/gtk-valuegraph.h b/src/gtk-valuegraph.h new file mode 100644 index 0000000..e048739 --- /dev/null +++ b/src/gtk-valuegraph.h @@ -0,0 +1,41 @@ +/* + * gtk-valuegraph.c + * + * A widget to display a graph of a sequence of values + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifndef GTKVALUEGRAPH_H +#define GTKVALUEGRAPH_H + +#include + +typedef struct { + + GtkDrawingArea parent; /* Parent widget */ + + double *data; /* Data to be graphed */ + unsigned int n; /* Number of data points */ + unsigned int xmax; /* Maximum value on x (index) axis */ + double ymax; /* Maximum value on y (data) axis */ + +} GtkValueGraph; + +typedef struct { + GtkDrawingAreaClass parent_class; + void (* changed) (GtkValueGraph *gtkvaluegraph); +} GtkValueGraphClass; + +extern guint gtk_value_graph_get_type(void); +extern GtkWidget *gtk_value_graph_new(void); +extern void gtk_value_graph_set_data(GtkValueGraph *vg, double *data, unsigned int n); + +#define GTK_VALUE_GRAPH(obj) GTK_CHECK_CAST(obj, gtk_value_graph_get_type(), GtkValueGraph) +#define GTK_VALUE_GRAPH_CLASS(class) GTK_CHECK_CLASS_CAST(class, gtk_value_graph_get_type(), GtkValueGraphClass) +#define GTK_IS_VALUE_GRAPH(obj) GTK_CHECK_TYPE(obj, gtk_value_graph_get_type()) + +#endif /* GTKVALUEGRAPH_H */ diff --git a/src/luzzatti.c b/src/luzzatti.c new file mode 100644 index 0000000..4aca763 --- /dev/null +++ b/src/luzzatti.c @@ -0,0 +1,91 @@ +/* + * luzzatti.c + * + * Plot of R-factor against resolution + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "main.h" +#include "statistics.h" +#include "displaywindow.h" +#include "model.h" + +#define LUZZATTI_BINS 50 + +static void luzzatti_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void luzzatti_show() { + + FILE *fh; + float a = data_a(); + float b = data_b(); + float c = data_c(); + unsigned int i; + FILE *gnuplot; + double scale; + ReflectionList *reflections; + ReflectionList *model_reflections; + + reflections = main_reflist(); + model_reflections = model_calculate_f(reflections, NULL, 69); + + scale = stat_scale(reflections, model_reflections); + fh = fopen("synth2d-luzzatti.dat", "w"); + + for ( i=1; in_reflections; i++ ) { + + double s, residual; + signed int h, k, l; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + s = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + residual = 100*fabs((reflections->refs[i].amplitude - scale*model_reflections->refs[i].amplitude)/reflections->refs[i].amplitude); + printf("%3i %3i %3i: obs=%f calc=%f => residual=%f\n", h, k, l, reflections->refs[i].amplitude, + scale*model_reflections->refs[i].amplitude, residual); + fprintf(fh, "%f %f\n", s, residual); + } + + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + luzzatti_gpwrite(gnuplot, "set autoscale\n"); + luzzatti_gpwrite(gnuplot, "unset log\n"); + luzzatti_gpwrite(gnuplot, "unset label\n"); + luzzatti_gpwrite(gnuplot, "set xtic auto\n"); + luzzatti_gpwrite(gnuplot, "set ytic auto\n"); + luzzatti_gpwrite(gnuplot, "set grid\n"); + luzzatti_gpwrite(gnuplot, "set ylabel 'R (%)' font \"Helvetica,10\"\n"); + luzzatti_gpwrite(gnuplot, "set xlabel '1/d / nm^(-1)' font \"Helvetica,10\"\n"); + luzzatti_gpwrite(gnuplot, "set title 'Luzzatti Plot' font \"Helvetica,10\"\n"); + luzzatti_gpwrite(gnuplot, "plot 'synth2d-luzzatti.dat'\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} diff --git a/src/luzzatti.h b/src/luzzatti.h new file mode 100644 index 0000000..a334a8c --- /dev/null +++ b/src/luzzatti.h @@ -0,0 +1,21 @@ +/* + * luzzatti.h + * + * Plot of R-factor against resolution + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef LUZZATTI_H +#define LUZZATTI_H + +extern void luzzatti_show(void); + +#endif /* LUZZATTI_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a2fff85 --- /dev/null +++ b/src/main.c @@ -0,0 +1,471 @@ +/* + * main.c + * + * The Top Level Source File + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "data.h" +#include "main.h" +#include "reflist.h" +#include "normalise.h" +#include "dpsynth.h" +#include "argand.h" +#include "symmetry.h" +#include "gsf.h" +#include "cflip.h" +#include "cdm.h" +#include "clean.h" +#include "model.h" +#include "geometry.h" +#include "statistics.h" +#include "elements.h" +#include "refine.h" +#include "superlattice.h" +#include "options.h" + + +ReflectionList *main_reflections = NULL; + +/* A rather shameless glue layer to avoid sharing information */ +void main_show_patterson() { displaywindow_show_patterson(main_reflections); } +void main_show_pattersone() { displaywindow_show_pattersone(main_reflections); } +void main_show_knownphases() { displaywindow_show_knownphases(main_reflections); } +void main_show_calcphases() { displaywindow_show_calcphases(main_reflections); } +void main_show_difference() { + ReflectionList *reflections = reflist_copy(main_reflections); + model_calculate_difference_coefficients(reflections); + displaywindow_show_difference(reflections); + reflist_free(reflections); +} +void main_show_refsyn() { + ReflectionList *reflections = reflist_copy(main_reflections); + model_calculate_refinement_coefficients(reflections); + displaywindow_show_refsyn(reflections); + reflist_free(reflections); +} +void main_show_diffpatt() { + ReflectionList *reflections = reflist_copy(main_reflections); + model_calculate_difference_coefficients(reflections); + displaywindow_show_diffpatt(reflections); + reflist_free(reflections); +} +void main_wilsonplot() { normalise_wilsonplot(main_reflections); } +void main_falloffplot() { normalise_falloffplot(main_reflections); } +void main_dpsynth() { dpsynth_main_open(main_reflections); } +void main_dpsynth_update() { dpsynth_main_update(main_reflections); } +void main_argand() { argand_open(main_reflections); } +void main_argand_update() { argand_update(main_reflections); } +void main_dethermalise(double level) { normalise_dethermalise(main_reflections, level); } +void main_normalise_exponential(double a, double b, double c) { normalise_exponential(main_reflections, a, b, c); } +void main_normalise(double level) { normalise_execute(main_reflections, level); } +void main_gsf_initialise() { gsf_initialise(main_reflections); } +void main_gsf_reset() { gsf_reset(main_reflections); } +void main_aperture_open() { clean_aperture_open(main_reflections); } +unsigned int main_cdm_tangentexpansion(CDMContext *cdm) { + cdm->reflections = main_reflections; + return cdm_tangentexpansion(cdm); +} +void main_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol) { + cdm_display_phasing_solution(cdm, sol, main_reflections); +} +void main_symmetrise(Symmetry symmetry) { + + char string[256]; + double rsym; + SymFlags flags = 0; + + if ( displaywindow_mode() == DWV_KNOWNPHASE ) { + flags = flags | SYMFLAG_PHASES_KNOWN; + } else if ( displaywindow_mode() == DWV_CALCPHASE ) { + flags = flags | SYMFLAG_PHASES_CALC; + } /* else amplitudes only */ + + rsym = symmetry_symmetrise(main_reflections, symmetry, flags); + + main_displayr(); + main_dpsynth_update(); + + displaywindow_switchview(); + snprintf(string, 255, "Rsym=%.2f%%", rsym*100); + displaywindow_statusbar(string); + +} +void main_geometry_correct(GeometryCorrectionType correction_type, double wavenumber, double circleradius) { + geometry_correct(main_reflections, correction_type, wavenumber, circleradius); +} + +void main_superlattice_split(unsigned int xc, unsigned int yc) { + + char string[256]; + + main_substitutereflections(superlattice_split(main_reflections, xc, yc)); + displaywindow_switchview(); + snprintf(string, 255, "Split %ix%i superlattice", xc, yc); + displaywindow_statusbar(string); + +} + +/* Cut out reflections with |g|<2.5 nm^-1 */ +void main_hpfilter() { + + unsigned int i; + double a, b, c; + char string[256]; + unsigned int n_del, n_orig; + + a = data_a(); b = data_b(); c = data_c(); + n_del = 0; n_orig = main_reflections->n_reflections; + + for ( i=1; in_reflections; i++ ) { + + double g; + signed int h = main_reflections->refs[i].h; + signed int k = main_reflections->refs[i].k; + signed int l = main_reflections->refs[i].l; + + g = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + + if ( g < 2.5 ) { + reflist_delref(main_reflections, h, k, l); + n_del++; + } + } + + displaywindow_switchview(); + snprintf(string, 255, "%0.2f%% of reflections filtered out (|g|<2.5nm^-1)", ((double)n_del/n_orig)*100); + displaywindow_statusbar(string); + +} + +void main_savereflections(const char *filename) { + + FILE *fh; + unsigned int i; + int sim; + + fh = fopen(filename, "w"); + + fprintf(fh, "a %f\n", data_a()); + fprintf(fh, "b %f\n", data_b()); + fprintf(fh, "c %f\n", data_c()); + fprintf(fh, "angle %f\n", 180*data_gamma()/M_PI); + fprintf(fh, "scale %i\n", data_get_image_scale()); + + switch ( displaywindow_mode() ) { + case DWV_PATTERSON : sim = 0; break; + case DWV_PATTERSONE : sim = 0; break; + case DWV_KNOWNPHASE : sim = 0; break; + /* DWV_REALSPACE should never get here */ + case DWV_DIFFERENCE : sim = 1; break; + case DWV_DIFFPATT : sim = 1; break; + case DWV_REFSYN : sim = 1; break; + case DWV_MODEL : sim = 1; break; + case DWV_SIMPATT : sim = 1; break; + case DWV_SIMFOLZPATT : sim = 1; break; + case DWV_EXITWAVE : sim = 1; break; + default : sim = 0; break; + } + + if ( sim ) { + + ReflectionList *calc; + + switch ( displaywindow_mode() ) { + case DWV_DIFFERENCE : calc = reflist_copy(main_reflections); + model_calculate_difference_coefficients(calc); break; + case DWV_DIFFPATT : calc = reflist_copy(main_reflections); + model_calculate_difference_coefficients(calc); break; + case DWV_REFSYN : calc = reflist_copy(main_reflections); + model_calculate_refinement_coefficients(calc); break; + case DWV_MODEL : calc = model_calculate_f(NULL, NULL, 0); break; + case DWV_SIMPATT : calc = model_calculate_f(NULL, NULL, 0); break; + case DWV_SIMFOLZPATT : calc = model_calculate_f(NULL, NULL, 1); break; + case DWV_EXITWAVE : calc = model_calculate_f(NULL, NULL, 0); break; + default : calc = model_calculate_f(NULL, NULL, 0); break; + } + + for ( i=1; in_reflections; i++ ) { + signed int h = calc->refs[i].h; + signed int k = calc->refs[i].k; + signed int l = calc->refs[i].l; + double am = calc->refs[i].amplitude; + if ( (displaywindow_mode() != DWV_DIFFPATT) && (displaywindow_mode() != DWV_SIMPATT) && (displaywindow_mode() != DWV_SIMFOLZPATT) ) { + fprintf(fh, "%3i %3i %3i %8f %8f\n", h, k, l, am, calc->refs[i].phase_known); + } else { + fprintf(fh, "%3i %3i %3i %8f\n", h, k, l, am); + } + } + reflist_free(calc); + + } else { + + for ( i=1; in_reflections; i++ ) { + + signed int h = main_reflections->refs[i].h; + signed int k = main_reflections->refs[i].k; + signed int l = main_reflections->refs[i].l; + double am = main_reflections->refs[i].amplitude; + + switch ( displaywindow_mode() ) { + + case DWV_PATTERSON : fprintf(fh, "%3i %3i %3i %8f\n", h, k, l, am); break; + case DWV_PATTERSONE : fprintf(fh, "%3i %3i %3i %8f\n", h, k, l, am); break; + case DWV_KNOWNPHASE : if ( main_reflections->refs[i].phase_known_set ) + fprintf(fh, "%3i %3i %3i %8f %8f\n", h, k, l, am, main_reflections->refs[i].phase_known); + break; + case DWV_CALCPHASE : if ( main_reflections->refs[i].phase_calc_set ) + fprintf(fh, "%3i %3i %3i %8f %8f\n", h, k, l, am, main_reflections->refs[i].phase_calc); + break; + default : break; + + } + + } + + } + + fclose(fh); + +} + +static void main_reset() { + + unsigned int i; + unsigned int phases_known; + + if ( main_reflections ) { + free(main_reflections); + } + main_reflections = reflist_new(); + memcpy(main_reflections, data_getreflections(), sizeof(ReflectionList)); + data_free(); + + /* Decide whether to start with known phases or Patterson */ + phases_known = 0; + for ( i=0; in_reflections; i++ ) { + if ( main_reflections->refs[i].phase_known_set ) { + phases_known++;; + } + } + printf("MA: Have phase values for %i of %i reflections\n", phases_known, main_reflections->n_reflections); + if ( phases_known == 0 ) { + displaywindow_show_patterson(main_reflections); + printf("MA: No phase values known - displaying Patterson map instead.\n"); + displaywindow_statusbar("No phase values known - displaying Patterson map instead"); + } else { + displaywindow_show_knownphases(main_reflections); + displaywindow_statusbar("Displaying known phases from file"); + } + + displaywindow_brightness_auto(NULL, NULL); + +} + +int main(int argc, char *argv[]) { + + /* Sort out configuration */ + options_load(); + + g_thread_init(NULL); + gtk_init(&argc, &argv); + + if ( argc != 2 ) { + fprintf(stderr, "Syntax: %s \n", argv[0]); + return 1; + } + + gsl_set_error_handler_off(); + + /* Read data */ + if ( data_read(argv[1]) ) { + return 1; + } + + elements_initialise(); + model_default(); + + /* Open main window */ + displaywindow_open(argv[1]); + + /* Execute first transform */ + main_reset(); + + gtk_main(); + + options_save(); + + return 0; + +} + +void main_substitutereflections(ReflectionList *new) { + memcpy(main_reflections, new, sizeof(ReflectionList)); + displaywindow_switchview(); +} + +void main_displayr() { + + ReflectionList *model_reflections; + char r_string[256]; + + model_reflections = model_calculate_f(main_reflections, NULL, 69); + dpsynth_simdp_update(model_reflections); + if ( !model_current_is_blank() ) { + snprintf(r_string, 255, "R1=%.2f%%, R2=%.2f%%", 100*stat_r(main_reflections, model_reflections), + 100*stat_r2(main_reflections, model_reflections)); + displaywindow_statusbar(r_string); + } + reflist_free(model_reflections); + +} + +/* Caution! */ +ReflectionList *main_reflist() { return main_reflections; } + +unsigned int main_max_h() { + unsigned int max, i; + max = 0; + for ( i=1; in_reflections; i++ ) { + if ( abs(main_reflections->refs[i].h) > max ) max = abs(main_reflections->refs[i].h); + } + return max; +} + +unsigned int main_max_k() { + unsigned int max, i; + max = 0; + for ( i=1; in_reflections; i++ ) { + if ( abs(main_reflections->refs[i].k) > max ) max = abs(main_reflections->refs[i].k); + } + return max; +} + +void main_stripzero() { + + int i; + + i = reflist_inlist(main_reflections, 0, 0, 0); + assert(i == 0); + main_reflections->refs[i].amplitude = 0.0; + +} + +/* Anti-alias the pattern by restricting resolution to a circle */ +void main_antialias() { + + unsigned int i; + double max; + double ph, pk, mh, mk; + + /* Find the resolution in each direction */ + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].k != 0 ) continue; + if ( main_reflections->refs[i].h < 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + ph = max; + + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].k != 0 ) continue; + if ( main_reflections->refs[i].h > 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + mh = max; + + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].h != 0 ) continue; + if ( main_reflections->refs[i].k < 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + pk = max; + + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].h != 0 ) continue; + if ( main_reflections->refs[i].k > 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + mk = max; + + /* Find the smallest */ + double hm, km, m; + if ( mh < ph ) hm = mh; else hm = ph; + if ( mk < pk ) km = mk; else km = pk; + if ( hm < km ) m = hm; else m=km; + + printf("Data resolution is %f nm^-1\n", ph); + + i = 1; + while ( i < main_reflections->n_reflections ) { + + double res; + signed int h, k, l; + + h = main_reflections->refs[i].h; + k = main_reflections->refs[i].k; + l = main_reflections->refs[i].l; + res = resolution(h, k, l, data_a(), data_b(), data_c(), + data_gamma()); + //printf("Resolution of %3i %3i %3i is %f - ", h, k, l, res); + if ( res >= m ) { + reflist_delref(main_reflections, h, k, l); + // printf("eliminated\n"); + } else { + // main_reflections->refs[i].amplitude = 1000000000; + // printf("maximised\n"); + i++; + } + + } + +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..487e6cd --- /dev/null +++ b/src/main.h @@ -0,0 +1,69 @@ +/* + * main.h + * + * The Top Level Source File + * + * (c) 2006-2008 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef MAIN_H +#define MAIN_H + +#include + +#include "reflist.h" +#include "symmetry.h" +#include "geometry.h" +#include "refine.h" +#include "cdm.h" +#include "cflip.h" + + +extern void main_show_patterson(void); +extern void main_show_pattersone(void); +extern void main_show_knownphases(void); +extern void main_show_calcphases(void); +extern void main_show_difference(void); +extern void main_show_refsyn(void); +extern void main_show_diffpatt(void); + +extern void main_wilsonplot(void); +extern void main_falloffplot(void); +extern void main_dpsynth(void); +extern void main_dpsynth_update(void); +extern void main_argand(void); +extern void main_argand_update(void); +extern void main_dethermalise(double level); +extern void main_normalise_exponential(double a, double b, double c); +extern void main_normalise(double level); +extern void main_symmetrise(Symmetry symmetry); +extern void main_gsf_initialise(); +extern void main_gsf_reset(); +extern unsigned int main_cdm_tangentexpansion(CDMContext *cdm); +extern void main_aperture_open(void); +extern void main_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol); +extern void main_savereflections(const char *filename); + +extern void main_substitutereflections(ReflectionList *new); +extern void main_geometry_correct(GeometryCorrectionType correction_type, double wavenumber, double circleradius); +extern void main_displayr(void); +extern void main_superlattice_split(unsigned int xc, unsigned int yc); +extern void main_hpfilter(void); + +extern ReflectionList *main_reflist(void); + +extern unsigned int main_max_h(void); +extern unsigned int main_max_k(void); + +extern void main_stripzero(void); +extern void main_antialias(void); + +#endif /* MAIN_H */ + diff --git a/src/model-display.c b/src/model-display.c new file mode 100644 index 0000000..4eeb83a --- /dev/null +++ b/src/model-display.c @@ -0,0 +1,411 @@ +/* + * model-display.c + * + * Display atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#if HAVE_CAIRO +#include +#include +#endif + +#include "model.h" +#include "elements.h" +#include "symmetry.h" + +#if HAVE_CAIRO +static double model_oblique_x(double x, double y, int height, double gamma, int ny) { + + double offs; + + if ( gamma <= M_PI_2 ) { + offs = 0; + } else { + offs = (height*ny)*(-cos(gamma)); + } + + return 20.0 + offs + x + (y*cos(gamma)); + +} + +static double model_oblique_y(double x, double y, double gamma) { + + return 20.0 + y * sin(gamma); + +} + +static void model_display_draw_atoms(cairo_t *dctx, AtomicModel *model, int width, int height, double gamma, int nx, int ny, double h, int names, int heights) { + + cairo_surface_t *surface; + size_t i; + cairo_font_extents_t ext; + + surface = cairo_get_target(dctx); + + for ( i=0; in_atoms; i++ ) { + + AtomicModel *equivalents; + size_t j; + double x, y; + + if ( !model->atoms[i].active ) continue; + + equivalents = symmetry_generate_equivalent_atoms(model, i, MODEL_TARGET_DISPLAY); + + for ( j=0; jn_atoms; j++ ) { + + double R; + int xp, yp; + + x = width*(double)equivalents->atoms[j].x; + y = height*(double)equivalents->atoms[j].y; + R = sqrt(elements[equivalents->atoms[j].ref].z); + + for ( xp=0; xpatoms[i].occ < 1.0 ) { + cairo_set_source_rgb(dctx, 0.4, 0.4, 0.4); + } else if ( model->atoms[i].occ > 1.0 ) { + cairo_set_source_rgb(dctx, 0.4, 0, 0); + } else { + cairo_set_source_rgb(dctx, 0, 0, 0); + } + + cairo_arc(dctx, model_oblique_x(x+(width*xp), y+(height*yp), height, gamma, ny), + h-model_oblique_y(x+(width*xp), y+(height*yp), gamma), + R, 0, 2*M_PI); + cairo_fill(dctx); + } + } + + } + + model_free(equivalents); + + cairo_set_source_rgb(dctx, 0, 0, 1); + cairo_select_font_face(dctx, "Sans", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_NORMAL); + cairo_font_extents(dctx, &ext); + + /* Only the unique atoms are labelled */ + x = 5 + width*(double)model->atoms[i].x + sqrt(elements[model->atoms[i].ref].z); + y = height*(double)model->atoms[i].y; + cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma)); + if ( names ) { + cairo_show_text(dctx, elements[model->atoms[i].ref].element_name); + y -= ext.height; + } + x = 5 + width*(double)model->atoms[i].x + sqrt(elements[model->atoms[i].ref].z); + if ( heights ) { + + char tmp[32]; + + cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma)); + cairo_show_text(dctx, "z="); + snprintf(tmp, 31, "%.3f", model->atoms[i].z); + cairo_show_text(dctx, tmp); + y -= ext.height; + + } + if ( model->atoms[i].occ != 1.0 ) { + + char tmp[32]; + + cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma)); + cairo_show_text(dctx, "occ: "); + snprintf(tmp, 31, "%.2f", model->atoms[i].occ); + cairo_show_text(dctx, tmp); + y -= ext.height; + + } + + } + +} + +static void model_display_draw_diad(cairo_t *dctx, double x, double y) { + + cairo_matrix_t matrix; + + cairo_get_matrix(dctx, &matrix); + cairo_new_path(dctx); + cairo_set_source_rgb(dctx, 1, 0, 0); + cairo_translate(dctx, x, y); + cairo_scale(dctx, 1.0, 2.0); + cairo_arc(dctx, 0.0, 0.0, 3, 0, 2*M_PI); + cairo_fill(dctx); + cairo_set_matrix(dctx, &matrix); + +} + +static void model_display_draw_glide(cairo_t *dctx, double x1, double y1, double x2, double y2, int height, double gamma, int ny, double h) { + + double dash; + + cairo_new_path(dctx); + cairo_set_source_rgb(dctx, 1, 0, 0); + cairo_set_line_width(dctx, 1.0); + dash = 3.0; cairo_set_dash(dctx, &dash, 1, 0); + cairo_move_to(dctx, model_oblique_x(x1, y1, height, gamma, ny), h-model_oblique_y(x1, y1, gamma)); + cairo_line_to(dctx, model_oblique_x(x2, y2, height, gamma, ny), h-model_oblique_y(x2, y2, gamma)); + cairo_stroke(dctx); + dash = 0.0; cairo_set_dash(dctx, &dash, 0, 0); + +} + +static void model_display_draw_mirror(cairo_t *dctx, double x1, double y1, double x2, double y2, int height, double gamma, int ny, double h) { + + cairo_new_path(dctx); + cairo_set_source_rgb(dctx, 1, 0, 0); + cairo_set_line_width(dctx, 1.0); + cairo_move_to(dctx, model_oblique_x(x1, y1, height, gamma, ny), h-model_oblique_y(x1, y1, gamma)); + cairo_line_to(dctx, model_oblique_x(x2, y2, height, gamma, ny), h-model_oblique_y(x2, y2, gamma)); + cairo_stroke(dctx); + +} + +static void model_display_draw_symmetry(cairo_t *dctx, Symmetry sym, int width, int height, double gamma, int nx, int ny, double h) { + + cairo_surface_t *surface; + + surface = cairo_get_target(dctx); + + if ( sym & SYMMETRY_CENTRE ) { + int x, y; + for ( x=0; xsym, width, height, gamma, nx, ny, h); + + return dctx; + +} + +static void model_display_free_data(guchar *image_data, cairo_t *dctx) { + cairo_surface_finish(cairo_get_target(dctx)); + cairo_destroy(dctx); +} +#endif + +GdkPixbuf *model_display_render_pixbuf(AtomicModel *model, int width, int height, double gamma, int names, int heights, int nx, int ny) { + +#if HAVE_CAIRO + cairo_surface_t *surface; + cairo_t *dctx; + GdkPixbuf *pixbuf; + unsigned char *data; + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, model_display_surface_width(nx*width, ny*height, gamma), + model_display_surface_height(nx*width, ny*height, gamma)); + + if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { + fprintf(stderr, "Couldn't create Cairo surface\n"); + cairo_surface_destroy(surface); + return NULL; + } + + dctx = cairo_create(surface); + model_display_do_cairo(dctx, model, width, height, gamma, nx, ny, cairo_image_surface_get_height(surface), names, heights); + + cairo_surface_flush(surface); + + data = cairo_image_surface_get_data(surface); + model_display_swizzle_data(data, cairo_image_surface_get_width(surface) * cairo_image_surface_get_height(surface)); + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, 8, + cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface), + cairo_image_surface_get_stride(surface), + (GdkPixbufDestroyNotify)model_display_free_data, dctx); + + return pixbuf; +#else + return NULL; +#endif + +} + +int model_display_render_pdf(AtomicModel *model, int width, int height, double gamma, const char *filename, int names, int heights, int nx, int ny) { + +#if HAVE_CAIRO + cairo_surface_t *surface; + cairo_t *dctx; + + surface = cairo_pdf_surface_create(filename, model_display_surface_width(nx*width, ny*height, gamma), + model_display_surface_height(nx*width, ny*height, gamma)); + + if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { + fprintf(stderr, "Couldn't create Cairo surface\n"); + cairo_surface_destroy(surface); + return -1; + } + + dctx = cairo_create(surface); + + model_display_do_cairo(dctx, model, width, height, gamma, nx, ny, model_display_surface_height(nx*width, ny*height, gamma), names, heights); + + cairo_surface_finish(surface); + cairo_destroy(dctx); +#endif + + return 0; + +} + diff --git a/src/model-display.h b/src/model-display.h new file mode 100644 index 0000000..3142c70 --- /dev/null +++ b/src/model-display.h @@ -0,0 +1,27 @@ +/* + * model-display.h + * + * Display atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef MODEL_DISPLAY_H +#define MODEL_DISPLAY_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "model.h" + +extern GdkPixbuf *model_display_render_pixbuf(AtomicModel *model, int width, int height, double gamma, int names, int heights, int nx, int ny); +extern int model_display_render_pdf(AtomicModel *model, int width, int height, double gamma, const char *filename, int names, int heights, int nx, int ny); + +#endif /* MODEL_DISPLAY_H */ + diff --git a/src/model-editor.c b/src/model-editor.c new file mode 100644 index 0000000..3146b5d --- /dev/null +++ b/src/model-editor.c @@ -0,0 +1,831 @@ +/* + * model-editor.c + * + * GUI for editing atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "data.h" +#include "elements.h" +#include "main.h" +#include "statistics.h" +#include "refine.h" +#include "model.h" +#include "gtk-symmetry.h" + +enum { + MODEL_ATOMS_COLUMN_ACTIVE, + MODEL_ATOMS_COLUMN_REFINE, + MODEL_ATOMS_COLUMN_ELEMENT, + MODEL_ATOMS_COLUMN_X, + MODEL_ATOMS_COLUMN_Y, + MODEL_ATOMS_COLUMN_Z, /* Atomic coordinates */ + MODEL_ATOMS_COLUMN_B, /* Debye-Waller */ + MODEL_ATOMS_COLUMN_OCC, /* Occupancy */ + MODEL_ATOMS_COLUMNS +}; + +static gint model_save_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + model_save(filename, model_get_current()); + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +gint model_save_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save Atomic Model", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + #if HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-model.xyz"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(model_save_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint model_load_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + model_load_as_current(filename); + displaywindow_forceview(DWV_MODEL); + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +gint model_load_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Load Atomic Model", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(model_load_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +/* Not all model updates should trigger an update of the display and R-factor + * (e.g. changing a refinement flag) */ +static void model_editor_put_model_noupdate(ModelEditor *editor) { + + GtkTreeIter iter; + gboolean ival; + + editor->model->n_atoms = 0; + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(editor->list_store), &iter); + while ( ival ) { + + double x, y, z, B, occ; + gboolean refine, active; + gchar *tmp; + + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, &x, MODEL_ATOMS_COLUMN_Y, &y, MODEL_ATOMS_COLUMN_Z, &z, + MODEL_ATOMS_COLUMN_ELEMENT, &tmp, + MODEL_ATOMS_COLUMN_B, &B, MODEL_ATOMS_COLUMN_OCC, &occ, + MODEL_ATOMS_COLUMN_ACTIVE, &active, MODEL_ATOMS_COLUMN_REFINE, &refine, -1); + + editor->model->atoms[editor->model->n_atoms].x = x; + editor->model->atoms[editor->model->n_atoms].y = y; + editor->model->atoms[editor->model->n_atoms].z = z; + editor->model->atoms[editor->model->n_atoms].B = B; + editor->model->atoms[editor->model->n_atoms].occ = occ; + editor->model->atoms[editor->model->n_atoms].active = active; + editor->model->atoms[editor->model->n_atoms].refine = refine; + editor->model->atoms[editor->model->n_atoms].ref = elements_lookup(tmp); + editor->model->n_atoms++; + + ival = gtk_tree_model_iter_next(GTK_TREE_MODEL(editor->list_store), &iter); + + } + +} + +/* Transfer model from an editor to its corresponding model */ +static void model_editor_put_model(ModelEditor *editor) { + model_editor_put_model_noupdate(editor); + model_notify_update_editor(editor->model); +} + +/* Transfer model->editor */ +void model_editor_get_model(ModelEditor *editor) { + + size_t i; + char tmp[32]; + + gtk_list_store_clear(GTK_LIST_STORE(editor->list_store)); + for ( i=0; imodel->n_atoms; i++ ) { + + double x, y, z, B, occ; + gboolean refine, active; + GtkTreeIter iter; + + x = editor->model->atoms[i].x; + y = editor->model->atoms[i].y; + z = editor->model->atoms[i].z; + B = editor->model->atoms[i].B; + occ = editor->model->atoms[i].occ; + active = editor->model->atoms[i].active; + refine = editor->model->atoms[i].refine; + + gtk_list_store_append(GTK_LIST_STORE(editor->list_store), &iter); + gtk_list_store_set(editor->list_store, &iter, MODEL_ATOMS_COLUMN_X, x, MODEL_ATOMS_COLUMN_Y, y, MODEL_ATOMS_COLUMN_Z, z, + MODEL_ATOMS_COLUMN_ELEMENT, elements[editor->model->atoms[i].ref].element_name, + MODEL_ATOMS_COLUMN_B, B, MODEL_ATOMS_COLUMN_OCC, occ, + MODEL_ATOMS_COLUMN_ACTIVE, active, MODEL_ATOMS_COLUMN_REFINE, refine, -1); + + }; + + snprintf(tmp, 31, "%.2f", editor->model->thickness); + gtk_entry_set_text(GTK_ENTRY(editor->thickness), tmp); + gtk_symmetry_set_symmetry(GTK_SYMMETRY(editor->symmetry), editor->model->sym); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(editor->point_atoms), editor->model->point_atoms); + + +} + +static gint model_editor_symmetry_changed(GtkWidget *thickness, ModelEditor *editor) { + + editor->model->sym = gtk_symmetry_get_symmetry(GTK_SYMMETRY(editor->symmetry)); + model_notify_update_editor(editor->model); + + return 0; + +} + +static gint model_editor_thickness_edited(GtkWidget *thickness, ModelEditor *editor) { + + const char *str; + float tf; + double t; + char tmp[32]; + + str = gtk_entry_get_text(GTK_ENTRY(thickness)); + sscanf(str, "%f", &tf); t = tf; + editor->model->thickness = t; + + snprintf(tmp, 31, "%.2f", editor->model->thickness); + gtk_entry_set_text(GTK_ENTRY(thickness), tmp); + + model_notify_update_editor(editor->model); + + return 0; + +} + +void model_editor_unlock(ModelEditor *editor) { + + gtk_widget_set_sensitive(editor->add_button, TRUE); + gtk_widget_set_sensitive(editor->model_atoms_tree, TRUE); + +} + +void model_editor_lock(ModelEditor *editor) { + + gtk_widget_set_sensitive(editor->add_button, FALSE); + gtk_widget_set_sensitive(editor->model_atoms_tree, FALSE); + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(editor->model_atoms_tree))); + +} + +static gint model_atoms_delete_atom(GtkWidget *widget, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_remove(GTK_LIST_STORE(editor->list_store), &iter); + model_editor_put_model(editor); + + return 0; + +} + +static gint model_atoms_add_atom(GtkWidget *widget, ModelEditor *editor) { + + GtkTreeIter iter; + unsigned int number; + + number = gtk_combo_box_get_active(GTK_COMBO_BOX(editor->atom_define)); + gtk_list_store_append(GTK_LIST_STORE(editor->list_store), &iter); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, + MODEL_ATOMS_COLUMN_ELEMENT, elements[number].element_name, + MODEL_ATOMS_COLUMN_ACTIVE, TRUE, MODEL_ATOMS_COLUMN_REFINE, TRUE, + MODEL_ATOMS_COLUMN_B, 0.0005, + MODEL_ATOMS_COLUMN_OCC, 1.0, -1); + model_editor_put_model(editor); + + return 0; + +} + +static gint model_atoms_selection_changed(GtkTreeSelection *selection, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double x, y, z, B, occ; + char tmp[32]; + + gtk_widget_set_sensitive(editor->delete_button, gtk_tree_selection_get_selected(selection, NULL, NULL)); + gtk_widget_set_sensitive(editor->position_table, gtk_tree_selection_get_selected(selection, NULL, NULL)); + + if ( !gtk_tree_selection_get_selected(selection, NULL, NULL) ) return 0; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, &x, + MODEL_ATOMS_COLUMN_Y, &y, MODEL_ATOMS_COLUMN_Z, &z, + MODEL_ATOMS_COLUMN_B, &B, MODEL_ATOMS_COLUMN_OCC, &occ, -1); + + /* Set the scales, but don't trigger a callback */ + editor->scale_lock++; + gtk_range_set_value(GTK_RANGE(editor->x_scale), x); + gtk_range_set_value(GTK_RANGE(editor->y_scale), y); + gtk_range_set_value(GTK_RANGE(editor->z_scale), z); + gtk_range_set_value(GTK_RANGE(editor->b_scale), B); + gtk_range_set_value(GTK_RANGE(editor->occ_scale), occ); + editor->scale_lock--; + + snprintf(tmp, 31, "%f", x); gtk_entry_set_text(GTK_ENTRY(editor->x_edit), tmp); + snprintf(tmp, 31, "%f", y); gtk_entry_set_text(GTK_ENTRY(editor->y_edit), tmp); + snprintf(tmp, 31, "%f", z); gtk_entry_set_text(GTK_ENTRY(editor->z_edit), tmp); + snprintf(tmp, 31, "%f", B); gtk_entry_set_text(GTK_ENTRY(editor->b_edit), tmp); + snprintf(tmp, 31, "%f", occ); gtk_entry_set_text(GTK_ENTRY(editor->occ_edit), tmp); + + return 0; + +} + +static gint model_atoms_active_toggled(GtkCellRendererToggle *cell, gchar *path_string, ModelEditor *editor) { + + GtkTreeIter iter; + gboolean active; + + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(editor->list_store), &iter, path_string); + + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_ACTIVE, &active, -1); + active = !active; + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_ACTIVE, active, -1); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_atoms_refine_toggled(GtkCellRendererToggle *cell, gchar *path_string, ModelEditor *editor) { + + GtkTreeIter iter; + gboolean refine; + + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(editor->list_store), &iter, path_string); + + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_REFINE, &refine, -1); + refine = !refine; + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_REFINE, refine, -1); + + model_editor_put_model_noupdate(editor); + + return 0; + +} + +static gint model_editor_x_edit(GtkWidget *x_edit, ModelEditor *editor) { + + const char *str; + float xf; + double x; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(x_edit)); + sscanf(str, "%f", &xf); x = xf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, x, -1); + + /* This triggers an x_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->x_scale), x); + + return 0; + +} + +static gint model_editor_y_edit(GtkWidget *y_edit, ModelEditor *editor) { + + const char *str; + float yf; + double y; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(y_edit)); + sscanf(str, "%f", &yf); y = yf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Y, y, -1); + + /* This triggers a y_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->y_scale), y); + + return 0; + +} + +static gint model_editor_z_edit(GtkWidget *z_edit, ModelEditor *editor) { + + const char *str; + float zf; + double z; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(z_edit)); + sscanf(str, "%f", &zf); z = zf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Z, z, -1); + + /* This triggers a z_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->z_scale), z); + + return 0; + +} + +static gint model_editor_b_edit(GtkWidget *b_edit, ModelEditor *editor) { + + const char *str; + float bf; + double b; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(b_edit)); + sscanf(str, "%f", &bf); b = bf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_B, b, -1); + + /* This triggers a b_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->b_scale), b); + + return 0; + +} + +static gint model_editor_occ_edit(GtkWidget *occ_edit, ModelEditor *editor) { + + const char *str; + float occf; + double occ; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(occ_edit)); + sscanf(str, "%f", &occf); occ = occf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_OCC, occ, -1); + + /* This triggers an occ_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->occ_scale), occ); + + return 0; + +} + +static gint model_editor_x_scale(GtkWidget *x_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double x; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + x = gtk_range_get_value(GTK_RANGE(x_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, x, -1); + + snprintf(tmp, 31, "%f", x); gtk_entry_set_text(GTK_ENTRY(editor->x_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_y_scale(GtkWidget *y_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double y; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + y = gtk_range_get_value(GTK_RANGE(y_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Y, y, -1); + + snprintf(tmp, 31, "%f", y); gtk_entry_set_text(GTK_ENTRY(editor->y_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_z_scale(GtkWidget *z_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double z; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + z = gtk_range_get_value(GTK_RANGE(z_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Z, z, -1); + + snprintf(tmp, 31, "%f", z); gtk_entry_set_text(GTK_ENTRY(editor->z_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_b_scale(GtkWidget *b_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double b; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + b = gtk_range_get_value(GTK_RANGE(b_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_B, b, -1); + + snprintf(tmp, 31, "%f", b); gtk_entry_set_text(GTK_ENTRY(editor->b_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_occ_scale(GtkWidget *occ_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double occ; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + occ = gtk_range_get_value(GTK_RANGE(occ_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_OCC, occ, -1); + + snprintf(tmp, 31, "%f", occ); gtk_entry_set_text(GTK_ENTRY(editor->occ_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_pointatoms_toggled(GtkWidget *point_atoms, ModelEditor *editor) { + + editor->model->point_atoms = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(point_atoms)); + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_close(GtkWidget *widget, gint response, ModelEditor *editor) { + + /* Bye bye */ + editor->model->editor = NULL; + gtk_widget_destroy(editor->window); + free(editor); + + return 0; + +} + +static void model_editor_do_list(GtkWidget *vbox, ModelEditor *editor) { + + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkCellRenderer *togglerenderer_active; + GtkCellRenderer *togglerenderer_refine; + + GtkWidget *scrolledwindow; + GtkWidget *add_button; + GtkWidget *button_box; + + unsigned int i = 0; + + editor->list_store = gtk_list_store_new(MODEL_ATOMS_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_DOUBLE, + G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + model_editor_get_model(editor); + editor->model_atoms_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(editor->list_store)); + + renderer = gtk_cell_renderer_text_new(); + togglerenderer_active = gtk_cell_renderer_toggle_new(); + togglerenderer_refine = gtk_cell_renderer_toggle_new(); + + column = gtk_tree_view_column_new_with_attributes("On", togglerenderer_active, "active", MODEL_ATOMS_COLUMN_ACTIVE, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + g_signal_connect(G_OBJECT(togglerenderer_active), "toggled", G_CALLBACK(model_atoms_active_toggled), editor); + + column = gtk_tree_view_column_new_with_attributes("R?", togglerenderer_refine, "active", MODEL_ATOMS_COLUMN_REFINE, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + g_signal_connect(G_OBJECT(togglerenderer_refine), "toggled", G_CALLBACK(model_atoms_refine_toggled), editor); + + column = gtk_tree_view_column_new_with_attributes("Element", renderer, "text", MODEL_ATOMS_COLUMN_ELEMENT, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 100); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("x", renderer, "text", MODEL_ATOMS_COLUMN_X, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("y", renderer, "text", MODEL_ATOMS_COLUMN_Y, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("z", renderer, "text", MODEL_ATOMS_COLUMN_Z, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("B", renderer, "text", MODEL_ATOMS_COLUMN_B, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("Occupancy", renderer, "text", MODEL_ATOMS_COLUMN_OCC, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + scrolledwindow = gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(editor->model_atoms_tree)), + gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(editor->model_atoms_tree))); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolledwindow), GTK_WIDGET(editor->model_atoms_tree)); + + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolledwindow), TRUE, TRUE, 5); + + button_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), button_box, FALSE, FALSE, 5); + + add_button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_box_pack_end(GTK_BOX(button_box), add_button, FALSE, FALSE, 5); + + editor->atom_define = gtk_combo_box_new_text(); + while ( elements[i].z > 0 ) { + gtk_combo_box_append_text(GTK_COMBO_BOX(editor->atom_define), elements[i].element_name); + i++; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(editor->atom_define), 0); + gtk_box_pack_end(GTK_BOX(button_box), GTK_WIDGET(editor->atom_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(add_button), "clicked", G_CALLBACK(model_atoms_add_atom), editor); + + editor->delete_button = gtk_button_new_from_stock(GTK_STOCK_DELETE); + gtk_box_pack_end(GTK_BOX(button_box), editor->delete_button, FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(editor->delete_button), "clicked", G_CALLBACK(model_atoms_delete_atom), editor); + + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(editor->model_atoms_tree))), "changed", + G_CALLBACK(model_atoms_selection_changed), editor); + gtk_widget_set_sensitive(editor->delete_button, + gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(editor->model_atoms_tree)), NULL, NULL)); + + editor->add_button = add_button; + +} + +/* Open the model editor for a given model */ +ModelEditor *model_editor_open(AtomicModel *model) { + + ModelEditor *editor; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *x_scale_label; + GtkWidget *y_scale_label; + GtkWidget *z_scale_label; + GtkWidget *b_scale_label; + GtkWidget *occ_scale_label; + GtkWidget *top_hbox; + GtkWidget *label; + + /* Don't proceed if an editor is already open for this model */ + if ( model->editor ) return model->editor; + editor = malloc(sizeof(ModelEditor)); + editor->model = model; + editor->scale_lock = 0; + + editor->window = gtk_dialog_new_with_buttons("Edit Atomic Model", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size(GTK_WINDOW(editor->window), 550, 600); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(editor->window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + top_hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new("Thickness: "); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_box_pack_start(GTK_BOX(top_hbox), GTK_WIDGET(label), TRUE, TRUE, 5); + editor->thickness = gtk_entry_new(); + gtk_entry_set_alignment(GTK_ENTRY(editor->thickness), 1); + gtk_entry_set_max_length(GTK_ENTRY(editor->thickness), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->thickness), 8); + gtk_box_pack_start(GTK_BOX(top_hbox), GTK_WIDGET(editor->thickness), TRUE, TRUE, 5); + label = gtk_label_new(" nm"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(top_hbox), GTK_WIDGET(label), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(editor->thickness), "activate", G_CALLBACK(model_editor_thickness_edited), editor); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(top_hbox), FALSE, FALSE, 2); + + editor->point_atoms = gtk_check_button_new_with_label("Point atoms"); + g_signal_connect(G_OBJECT(editor->point_atoms), "toggled", G_CALLBACK(model_editor_pointatoms_toggled), editor); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(editor->point_atoms), FALSE, TRUE, 2); + + editor->symmetry = gtk_symmetry_new(2, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(editor->symmetry), FALSE, FALSE, 2); + g_signal_connect(G_OBJECT(editor->symmetry), "changed", G_CALLBACK(model_editor_symmetry_changed), editor); + + /* Add the list of atoms and Add/Remove widgets */ + model_editor_do_list(vbox, editor); + + /* Add the atomic coordinate editor */ + editor->position_table = gtk_table_new(5, 3, FALSE); + gtk_table_set_homogeneous(GTK_TABLE(editor->position_table), FALSE); + gtk_table_set_row_spacings(GTK_TABLE(editor->position_table), 2); + gtk_table_set_col_spacings(GTK_TABLE(editor->position_table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(editor->position_table), FALSE, FALSE, 5); + + editor->x_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->x_scale), FALSE); + //gtk_range_set_update_policy(GTK_RANGE(editor->x_scale), GTK_UPDATE_DELAYED); + g_signal_connect(G_OBJECT(editor->x_scale), "value-changed", G_CALLBACK(model_editor_x_scale), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->x_scale), 2, 3, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + x_scale_label = gtk_label_new("x"); + gtk_misc_set_alignment(GTK_MISC(x_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(x_scale_label), 1, 2, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->x_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->x_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->x_edit), 8); + g_signal_connect(G_OBJECT(editor->x_edit), "activate", G_CALLBACK(model_editor_x_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->x_edit), 3, 4, 1, 2, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->y_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->y_scale), FALSE); + g_signal_connect(G_OBJECT(editor->y_scale), "value-changed", G_CALLBACK(model_editor_y_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->y_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->y_scale), 2, 3, 2, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + y_scale_label = gtk_label_new("y"); + gtk_misc_set_alignment(GTK_MISC(y_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(y_scale_label), 1, 2, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->y_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->y_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->y_edit), 8); + g_signal_connect(G_OBJECT(editor->y_edit), "activate", G_CALLBACK(model_editor_y_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->y_edit), 3, 4, 2, 3, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->z_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->z_scale), FALSE); + g_signal_connect(G_OBJECT(editor->z_scale), "value-changed", G_CALLBACK(model_editor_z_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->z_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->z_scale), 2, 3, 3, 4, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + z_scale_label = gtk_label_new("z"); + gtk_misc_set_alignment(GTK_MISC(z_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(z_scale_label), 1, 2, 3, 4, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->z_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->z_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->z_edit), 8); + g_signal_connect(G_OBJECT(editor->z_edit), "activate", G_CALLBACK(model_editor_z_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->z_edit), 3, 4, 3, 4, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->b_scale = gtk_hscale_new_with_range(0, 0.001, 0.00001); + gtk_scale_set_draw_value(GTK_SCALE(editor->b_scale), FALSE); + g_signal_connect(G_OBJECT(editor->b_scale), "value-changed", G_CALLBACK(model_editor_b_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->b_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->b_scale), 2, 3, 4, 5, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + b_scale_label = gtk_label_new("B"); + gtk_misc_set_alignment(GTK_MISC(b_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(b_scale_label), 1, 2, 4, 5, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->b_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->b_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->b_edit), 8); + g_signal_connect(G_OBJECT(editor->b_edit), "activate", G_CALLBACK(model_editor_b_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->b_edit), 3, 4, 4, 5, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->occ_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->occ_scale), FALSE); + g_signal_connect(G_OBJECT(editor->occ_scale), "value-changed", G_CALLBACK(model_editor_occ_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->occ_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->occ_scale), 2, 3, 5, 6, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + occ_scale_label = gtk_label_new("Occ"); + gtk_misc_set_alignment(GTK_MISC(occ_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(occ_scale_label), 1, 2, 5, 6, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->occ_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->occ_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->occ_edit), 8); + g_signal_connect(G_OBJECT(editor->occ_edit), "activate", G_CALLBACK(model_editor_occ_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->occ_edit), 3, 4, 5, 6, GTK_SHRINK, GTK_SHRINK, 2, 2); + + g_signal_connect(G_OBJECT(editor->window), "response", G_CALLBACK(model_editor_close), editor); + + gtk_widget_set_sensitive(editor->delete_button, FALSE); + gtk_widget_set_sensitive(editor->position_table, FALSE); + /* If the model is locked, disable things appropriately from the start */ + if ( editor->model->lock_count > 0 ) model_editor_lock(editor); + + gtk_widget_show_all(editor->window); + + return editor; + +} + diff --git a/src/model-editor.h b/src/model-editor.h new file mode 100644 index 0000000..6dc55da --- /dev/null +++ b/src/model-editor.h @@ -0,0 +1,69 @@ +/* + * model-editor.h + * + * GUI for editing atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef MODEL_EDITOR_H +#define MODEL_EDITOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +typedef struct { + + struct struct_atomicmodel *model; + + GtkWidget *window; + GtkListStore *list_store; + + GtkWidget *thickness; /* Text entry for thickness */ + GtkWidget *symmetry; /* GtkSymmetry for model */ + GtkWidget *point_atoms; + + GtkWidget *model_atoms_tree; + + GtkWidget *add_button; + GtkWidget *atom_define; + GtkWidget *delete_button; + + GtkWidget *position_table; + + GtkWidget *x_scale; + GtkWidget *x_edit; + + GtkWidget *y_scale; + GtkWidget *y_edit; + + GtkWidget *z_scale; + GtkWidget *z_edit; + + GtkWidget *b_scale; + GtkWidget *b_edit; + + GtkWidget *occ_scale; + GtkWidget *occ_edit; + + int scale_lock; /* Don't respond to scale value-changed if this >1 */ + +} ModelEditor; + +extern ModelEditor *model_editor_open(struct struct_atomicmodel *model); +extern void model_editor_get_model(ModelEditor *editor); + +extern gint model_load_open(GtkWidget *widget, gpointer data); +extern gint model_save_open(GtkWidget *widget, gpointer data); +extern void model_editor_update(struct struct_atomicmodel *model); +extern void model_editor_lock(ModelEditor *editor); +extern void model_editor_unlock(ModelEditor *editor); + +#endif /* MODEL_EDITOR_H */ + diff --git a/src/model.c b/src/model.c new file mode 100644 index 0000000..08c1bb2 --- /dev/null +++ b/src/model.c @@ -0,0 +1,523 @@ +/* + * model.c + * + * Atomic Model Structures + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "elements.h" +#include "main.h" +#include "statistics.h" +#include "symmetry.h" +#include "model.h" +#include "displaywindow.h" +#include "model-editor.h" +#include "multislice.h" +#include "dpsynth.h" +#include "normalise.h" + +/* This is the SPOT for the "current atomic model", but don't assume it's the only model around... */ +AtomicModel *model_current = NULL; + +/* --------------------------------------------------- Loading and saving of models --------------------------------------------------- */ + +/* NB This doesn't exactly replicate Perl's chomp()... */ +static void chomp(char *line) { + + size_t i; + + for ( i=0; iatoms[model->n_atoms].x = x; + model->atoms[model->n_atoms].y = y; + model->atoms[model->n_atoms].z = z; + model->atoms[model->n_atoms].B = B; + model->atoms[model->n_atoms].occ = occ; + model->atoms[model->n_atoms].ref = elements_lookup(tmp); + model->atoms[model->n_atoms].active = (active == 'A')?1:0; /* Anything other than "A" means "inactive" */ + model->atoms[model->n_atoms].refine = (refine == 'R')?1:0; /* Anything other than "R" means "don't refine" */ + model->n_atoms++; + } else if ( strncmp(line, "symmetry ", 9) == 0 ) { + model->sym = symmetry_encode(line+9); + } else if ( sscanf(line, "thickness %f nm", &thickness) == 1 ) { + model->thickness = thickness; + } else { + fprintf(stderr, "Unrecognised line in model file (line %i)\n", i); + } + + i++; + + } + fclose(fh); + + return model; + +} + +void model_load_as_current(const char *filename) { + + AtomicModel *new; + double new_thickness; + Symmetry new_sym; + + new = model_load(filename); + new_thickness = new->thickness; + new_sym = new->sym; + + model_move(model_current, new); /* Preserves lock_count, editor, refinementwindow, thickness and sym */ + model_free(new); + + /* Restore thickness and symmetry */ + model_current->thickness = new_thickness; + model_current->sym = new_sym; + + model_notify_update(model_current); + +} + +void model_save(const char *filename, AtomicModel *model) { + + FILE *fh; + size_t i; + + fh = fopen(filename, "w"); + fprintf(fh, "symmetry %s\n", symmetry_decode(model->sym)); + fprintf(fh, "thickness %.2f nm\n", model->thickness); + for ( i=0; in_atoms; i++ ) { + + fprintf(fh, "%f %f %f %s %f %f %c %c\n", model->atoms[i].x, model->atoms[i].y, model->atoms[i].z, + elements[model->atoms[i].ref].element_name, model->atoms[i].B, + model->atoms[i].occ, + model->atoms[i].active?'A':'a', model->atoms[i].refine?'R':'r'); + + } + + fclose(fh); + +} + +/* --------------------------------------------------- Basic model handling functions ------------------------------------------------- */ + +AtomicModel *model_new() { + + AtomicModel *model; + + model = malloc(sizeof(AtomicModel)); + + model->n_atoms = 0; + model->sym = PLANEGROUP_P1; + model->thickness = 0.0; + model->point_atoms = 0; + + model->editor = NULL; + model->refine_window = NULL; + model->lock_count = 0; + + return model; + +} + +AtomicModel *model_copy(const AtomicModel *a) { + AtomicModel *model; + model = malloc(sizeof(AtomicModel)); + memcpy(model, a, sizeof(AtomicModel)); + return model; +} + +void model_move(AtomicModel *to, AtomicModel *from) { + + ModelEditor *old_editor; + RefinementWindow *old_refine; + int old_lock_count; + Symmetry old_sym; + double old_thickness; + + old_editor = to->editor; + old_refine = to->refine_window; + old_lock_count = to->lock_count; + old_sym = to->sym; + old_thickness = to->thickness; + + memcpy(to, from, sizeof(AtomicModel)); + + to->editor = old_editor; + to->refine_window = old_refine; + to->lock_count = old_lock_count; + to->sym = old_sym; + to->thickness = old_thickness; + +} + +void model_free(AtomicModel *model) { + free(model); +} + +/* --------------------------------------------------- Structure-Factor Calculations -------------------------------------------------- */ + +/* Generate a template for writing structure factors when there's nothing better to use */ +extern void model_generate_template(ReflectionList *reflections, signed int layer) { + + signed int h; + signed int k; + for ( h=-40; h<=40; h++ ) { + for ( k=-40; k<=40; k++ ) { + reflist_addref(reflections, h, k, layer); + } + } + +} + +/* Return f(j, hkl) */ +static double model_sfac(AtomicModel *model, unsigned int j, signed int h, signed int k, signed int l) { + + unsigned int ref; + double x, y, z; + double sfac; + double s; + double a = data_a() * 10; + double b = data_b() * 10; + double c = data_c() * 10; /* Change to Angstroms */ + double gamma = data_gamma(); + double B; + + if ( model->point_atoms ) return 1; + + x = model->atoms[j].x; y = model->atoms[j].y; z = model->atoms[j].z; + ref = model->atoms[j].ref; B = model->atoms[j].B; + + s = resolution(h, k, l, a, b, c, gamma); + + /* Calculate f(j,hkl) */ + sfac = elements[ref].sfac_c; + sfac += elements[ref].sfac_a1 * exp(-elements[ref].sfac_b1 * s * s); + sfac += elements[ref].sfac_a2 * exp(-elements[ref].sfac_b2 * s * s); + sfac += elements[ref].sfac_a3 * exp(-elements[ref].sfac_b3 * s * s); + sfac += elements[ref].sfac_a4 * exp(-elements[ref].sfac_b4 * s * s); + + /* Thermal factor */ + sfac = sfac * exp(- B * s * s); + + return sfac; + +} + +/* Real part of a particular atom's (and it's equivalents') contribution to F */ +static double model_f_contribution_re(AtomicModel *model, unsigned int j, signed int h, signed int k, signed int l) { + + double sfac, cont; + AtomicModel *equivalents; + size_t i; + + sfac = model_sfac(model, j, h, k, l); + equivalents = symmetry_generate_equivalent_atoms(model, j, MODEL_TARGET_CALCULATION); + + cont = 0; + for ( i=0; in_atoms; i++ ) { + double dot, x, y, z; + x = equivalents->atoms[i].x; y = equivalents->atoms[i].y; z = equivalents->atoms[i].z; + dot = h * (1-x) + k * (1-y) + l * z; + cont += model->atoms[j].occ * sfac * cos(-2 * M_PI * dot); + } + + model_free(equivalents); + + return cont; + +} + +/* Imaginary part of a particular atom's (and it's equivalents') contribution to F */ +static double model_f_contribution_im(AtomicModel *model, unsigned int j, signed int h, signed int k, signed int l) { + + double sfac, cont; + AtomicModel *equivalents; + size_t i; + + sfac = model_sfac(model, j, h, k, l); + equivalents = symmetry_generate_equivalent_atoms(model, j, MODEL_TARGET_CALCULATION); + + cont = 0; + for ( i=0; in_atoms; i++ ) { + double dot, x, y, z; + x = equivalents->atoms[i].x; y = equivalents->atoms[i].y; z = equivalents->atoms[i].z; + dot = h * (1-x) + k * (1-y) + l * z; + cont += model->atoms[j].occ * sfac * sin(-2 * M_PI * dot); + } + + model_free(equivalents); + + return cont; + +} + +double model_mod_f(AtomicModel *model, signed int h, signed int k, signed int l) { + + double sf_re, sf_im; + unsigned int j; + + sf_re = 0; sf_im = 0; + /* Work out the total contribution */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].active ) { + sf_re += model_f_contribution_re(model, j, h, k, l); + sf_im += model_f_contribution_im(model, j, h, k, l); + } + } + + return sqrt((sf_re*sf_re) + (sf_im*sf_im)); + +} + +/* Calculate kinematical structure factor amplitudes. + * "layer" is meaningless and is ignored if "given_template" is not NULL */ +ReflectionList *model_calculate_f(ReflectionList *given_template, AtomicModel *given_model, signed int layer) { + + ReflectionList *model_reflections; + unsigned int i; + AtomicModel *model; + ReflectionList *template; + + model_reflections = reflist_new(); + + if ( !given_template ) { + template = reflist_new(); + model_generate_template(template, layer); + } else { + template = given_template; + } + + if ( !given_model ) { + model = model_get_current(); + } else { + model = given_model; + } + + if ( model->thickness > 0.0001 ) { + + /* Dynamical diffraction amplitudes */ + model_reflections = multislice_calculate_f_dyn(model, template); + + } else { + + /* Kinematical structure factors */ + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + signed int h = template->refs[i].h; + signed int k = template->refs[i].k; + signed int l = template->refs[i].l; + double sf_re = 0; + double sf_im = 0; + unsigned int j; + + /* Work out the total contribution */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].active ) { + sf_re += model_f_contribution_re(model, j, h, k, l); + sf_im += model_f_contribution_im(model, j, h, k, l); + } + } + + //printf("%3i %3i %3i am=%f ph=%f re=%f im=%f\n", h, k, l, sqrt((sf_re*sf_re) + (sf_im*sf_im)), atan2(sf_im, sf_re), sf_re, sf_im); + reflist_addref_am_ph(model_reflections, h, k, l, sqrt((sf_re*sf_re) + (sf_im*sf_im)), atan2(sf_im, sf_re)); + reflist_set_components(model_reflections, h, k, l, sf_re, sf_im); + + } + + } + + if ( given_template == NULL ) free(template); + + return model_reflections; + +} + +#if 0 +/* Apply a smoothing filter to the amplitudes */ +static void model_smooth(ReflectionList *reflections) { + + unsigned int i; + float a = data_a(); + float b = data_b(); + float c = data_c(); + double sigma = 5.19; + + for ( i=1; in_reflections; i++ ) { + + double s; + signed int h, k, l; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + s = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + + reflections->refs[i].amplitude *= exp(-(s*s)/(2*sigma*sigma)); + + } + + +} +#endif + +/* For the difference synthesis */ +void model_calculate_difference_coefficients(ReflectionList *reflections) { + + unsigned int i; + double scale; + ReflectionList *model_reflections; + + if ( reflections->n_reflections == 0 ) return; /* No reflections */ + model_reflections = model_calculate_f(reflections, NULL, 69); + if ( !model_reflections ) return; + + scale = stat_scale(reflections, model_reflections); + //printf("Scale for difference coefficients = %f\n", scale); + + for ( i=0; in_reflections; i++ ) { /* 'hkl' loop */ + + if ( model_reflections->refs[i].amplitude > 0.0000001 ) { + reflections->refs[i].amplitude = reflections->refs[i].amplitude - (scale * model_reflections->refs[i].amplitude); + reflections->refs[i].phase_known = model_reflections->refs[i].phase_known; + reflections->refs[i].phase_known_set = 1; + } else { + reflections->refs[i].phase_known_set = 0; + } + + } + + //model_smooth(reflections); + + free(model_reflections); + +} + +/* For the Fourier refinement synthesis */ +void model_calculate_refinement_coefficients(ReflectionList *reflections) { + + unsigned int i; + ReflectionList *model_reflections; + + if ( reflections->n_reflections == 0 ) return; /* No reflections */ + model_reflections = model_calculate_f(reflections, NULL, 69); + if ( !model_reflections ) return; + + for ( i=0; in_reflections; i++ ) { /* 'hkl' loop */ + + reflections->refs[i].amplitude = reflections->refs[i].amplitude; + reflections->refs[i].phase_known = model_reflections->refs[i].phase_known; + + } + + //model_smooth(reflections); + + free(model_reflections); + +} + +/* ---------------------------------------------------------------- Misc -------------------------------------------------------------- */ + +void model_open_editor() { + model_current->editor = model_editor_open(model_current); +} + +AtomicModel *model_get_current() { + return model_current; +} + +/* Notify that a model has been changed. + * Special version for when a ModelEditor initiates the update + * (don't try and update the editor in this case!) */ +void model_notify_update_editor(AtomicModel *model) { + + if ( model == model_current ) { + displaywindow_switchview(); + } + +} + +/* Notify that a model has been changed */ +void model_notify_update(AtomicModel *model) { + + model_notify_update_editor(model); + + if ( model->editor ) { + model_editor_get_model(model->editor); + } + +} + +/* Initial setting of the current model. Many functions assume that + * a model structure exists. */ +void model_default() { + model_current = model_new(); +} + +void model_lock(AtomicModel *model) { + model->lock_count++; + if ( model->editor ) { + model_editor_lock(model->editor); + } +} + +void model_unlock(AtomicModel *model) { + model->lock_count--; + if ( model->lock_count == 0 ) { + if ( model->editor ) { + model_editor_unlock(model->editor); + } + } +} + +int model_current_is_blank() { + if ( model_get_current()->n_atoms) { + return 0; + } + return 1; +} diff --git a/src/model.h b/src/model.h new file mode 100644 index 0000000..9c78683 --- /dev/null +++ b/src/model.h @@ -0,0 +1,79 @@ +/* + * model.h + * + * Atomic Model Structures + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef MODEL_H +#define MODEL_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "symmetry.h" +#include "reflist.h" +#include "model-editor.h" +#include "refine.h" + +#define MAX_ATOMS 255 + +typedef struct { + + unsigned int ref; /* Atom type reference */ + double x; + double y; + double z; + double B; /* Debye-Waller thermal parameter */ + double occ; /* Site occupancy */ + + unsigned int refine; /* Refine this atom? */ + unsigned int active; /* Include this atom in calculations? */ + +} ModelAtom; + +typedef struct struct_atomicmodel { + + Symmetry sym; + double thickness; + int point_atoms; + + ModelEditor *editor; /* Editor for this model, if open */ + RefinementWindow *refine_window; /* Refinement window for this model, if open */ + int lock_count; + + unsigned int n_atoms; + ModelAtom atoms[MAX_ATOMS]; + +} AtomicModel; + +extern AtomicModel *model_new(void); +extern AtomicModel *model_copy(const AtomicModel *a); +extern void model_move(AtomicModel *a, AtomicModel *b); +extern void model_free(AtomicModel *model); + +extern AtomicModel *model_load(const char *filename); +extern void model_load_as_current(const char *filename); +extern void model_save(const char *filename, AtomicModel *model); + +extern ReflectionList *model_calculate_f(ReflectionList *template, AtomicModel *given_model, signed int layer); +extern void model_calculate_difference_coefficients(ReflectionList *reflections); +extern void model_calculate_refinement_coefficients(ReflectionList *reflections); +extern double model_mod_f(AtomicModel *model, signed int h, signed int k, signed int l); + +extern AtomicModel *model_get_current(); +extern void model_open_editor(void); +extern void model_notify_update(AtomicModel *model); +extern void model_notify_update_editor(AtomicModel *model); +extern void model_default(void); +extern void model_lock(AtomicModel *model); +extern void model_unlock(AtomicModel *model); +extern int model_current_is_blank(void); + +#endif /* MODEL_H */ + diff --git a/src/multislice.c b/src/multislice.c new file mode 100644 index 0000000..b314098 --- /dev/null +++ b/src/multislice.c @@ -0,0 +1,126 @@ +/* + * multislice.c + * + * Multislice Dynamical Simulations + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "reflist.h" +#include "model.h" +#include "data.h" + +/* Relativistic Electron Interaction Constant. Voltage in volts. */ +static double multislice_interaction(double voltage) { + + double a, b; + + a = (1 + voltage*1.9569341e-6) * 0.25615739; + b = sqrt(voltage) * sqrt(1 + voltage*0.97846707e-6); + + return a / b; + +} + +/* Unit cell volume */ +static double multislice_volume(double a, double b, double c, double alpha, double beta, double gamma) { + + + +} + +static fftw_complex *multislice_phasegrating(AtomicModel *model_orig, size_t width, size_t height) { + + AtomicModel *model; + fftw_complex *phase_grating_real; + fftw_complex *phase_grating_reciprocal; + fftw_plan plan; + ReflectionList *V; + ReflectionList *template; + + template = reflist_new(); + + model = model_copy(model_orig); + model->thickness = 0.0; + V = model_calculate_f(template, model, 69); + model_free(model); + reflist_free(template); + + plan = fftw_plan_dft_2d(width, height, phase_grating_real, phase_grating_reciprocal, FFTW_FORWARD, FFTW_MEASURE); + fftw_execute(plan); + fftw_destroy_plan(plan); + + return phase_grating_reciprocal; + +} + +static fftw_complex *multislice_propogator(AtomicModel *model_orig, size_t width, size_t height, double slice) { + + + + return NULL; + +} + +static void multislice_multiply(fftw_complex *a, fftw_complex *b, size_t size) { + size_t i; + for ( i=0; ithickness; t+=c ) { + multislice_multiply(wavefunction, propogator, width*height); + multislice_convolve(wavefunction, phasegrating, width*height); + printf("MS: Current thickness: %f\n", t); fflush(stdout); + } + + return NULL; + +} + diff --git a/src/multislice.h b/src/multislice.h new file mode 100644 index 0000000..4aa6014 --- /dev/null +++ b/src/multislice.h @@ -0,0 +1,25 @@ +/* + * multislice.h + * + * Multislice Dynamical Simulations + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef MULTISLICE_H +#define MULTISLICE_H + +#include "model.h" +#include "reflist.h" + +extern ReflectionList *multislice_calculate_f_dyn(AtomicModel *model, + ReflectionList *template); + +#endif /* MULTISLICE_H */ diff --git a/src/normalise.c b/src/normalise.c new file mode 100644 index 0000000..a08ff97 --- /dev/null +++ b/src/normalise.c @@ -0,0 +1,721 @@ +/* + * normalise.c + * + * Normalisation stuff + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "main.h" +#include "data.h" +#include "elements.h" + +static int normalise_window_open = 0; +GtkWidget *normalise_tree; +GtkListStore *normalise_list_store; +enum { + NORMALISE_ATOMS_COLUMN_NUMBER, + NORMALISE_ATOMS_COLUMN_Z, + NORMALISE_ATOMS_COLUMN_ELEMENT, + NORMALISE_ATOMS_COLUMN_REF, + NORMALISE_ATOMS_COLUMNS, +}; + +ReflectionList *normalise_undo_copy = NULL; + +static void normalise_undo() { + printf("NM: Undoing normalisation.\n"); + if ( normalise_undo_copy ) { + main_substitutereflections(normalise_undo_copy); + free(normalise_undo_copy); + normalise_undo_copy = NULL; + } else { + error_report("Nothing to undo..."); + return; + } +} + +/* Returns sin(theta)/lambda = 1/(2d) */ +double resolution(signed int h, signed int k, signed int l, + double a, double b, double c, double gamma) +{ + static int complained = 0; + double one_over_dsq; + + if ( (l != 0) && !complained ) { + printf("Warning: you asked for the resolution of a HOLZ " + "reflection. The value will be wrong unless alpha " + "and beta are 90 degrees.\n"); + complained = 1; + } + + /* This is just the formula for a monoclinic structure with + * b and c swapped round. */ + one_over_dsq = (h*h)/(a*a*sin(gamma)*sin(gamma)); + one_over_dsq += (k*k)/(b*b*sin(gamma)*sin(gamma)); + one_over_dsq += (l*l)/(c*c); + one_over_dsq -= (2*h*k*cos(gamma))/(a*b*sin(gamma)*sin(gamma)); + return 0.5 * sqrt(one_over_dsq); +} + +void normalise_execute(ReflectionList *reflections, double level) { + + GtkTreeIter iter; + unsigned int ref; + gboolean ival; + unsigned int i; + + printf("NM: Normalising (%f)\n", level); + + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(normalise_list_store), &iter); + if ( !ival ) { + error_report("You must define some atoms for the unit cell!"); + return; + } + + if ( normalise_undo_copy ) { + free(normalise_undo_copy); + } + normalise_undo_copy = malloc(sizeof(ReflectionList)); + memcpy(normalise_undo_copy, reflections, sizeof(ReflectionList)); + + for ( i=0; in_reflections; i++ ) { + + double am = reflections->refs[i].amplitude; + + /* Only attempt to normalise reflections which actually exist */ + if ( am > 0 ) { + + double sfac = 0; + double s; + double a = data_a() * 10; + double b = data_b() * 10; + double c = data_c() * 10; /* Change to Angstroms */ + double gamma = data_gamma(); + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + + /* Determine the value of 's' = sin(theta)/lambda = 1/2d */ + s = resolution(h, k, l, a, b, c, gamma); + + /* Step through the list of elements, adding up the contribution to this particular reflection from each */ + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(normalise_list_store), &iter); + while ( ival ) { + gtk_tree_model_get(GTK_TREE_MODEL(normalise_list_store), &iter, NORMALISE_ATOMS_COLUMN_REF, &ref, -1); + ival = gtk_tree_model_iter_next(GTK_TREE_MODEL(normalise_list_store), &iter); + sfac = elements[ref].sfac_c; + sfac += elements[ref].sfac_a1 * exp(-elements[ref].sfac_b1 * s * s); + sfac += elements[ref].sfac_a2 * exp(-elements[ref].sfac_b2 * s * s); + sfac += elements[ref].sfac_a3 * exp(-elements[ref].sfac_b3 * s * s); + sfac += elements[ref].sfac_a4 * exp(-elements[ref].sfac_b4 * s * s); + } + sfac = sfac*sfac; + + am = pow((am/sfac), level) * pow(am, 1-level); + + reflections->refs[i].amplitude = am; + + } + + } + + displaywindow_switchview(); + displaywindow_statusbar("Amplitudes normalised"); + +} + +static gint normalise_window_close(GtkWidget *widget, gint response, GtkWidget *normalise_level) { + + if ( response == GTK_RESPONSE_APPLY ) { + main_normalise(gtk_range_get_value(GTK_RANGE(normalise_level))); + } + + if ( response == GTK_RESPONSE_REJECT ) { + normalise_undo(); + } + + if ( (response == GTK_RESPONSE_CLOSE) || (response == GTK_RESPONSE_NONE) || (response == GTK_RESPONSE_DELETE_EVENT) ) { + normalise_window_open = 0; + gtk_widget_destroy(widget); + } + + return 0; +} + +static void normalise_atoms_number(GtkListStore *normalise_list_store) { + + GtkTreeIter iter; + gboolean ival; + unsigned int i = 1; + + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(normalise_list_store), &iter); + + while ( ival ) { + gtk_list_store_set(GTK_LIST_STORE(normalise_list_store), &iter, NORMALISE_ATOMS_COLUMN_NUMBER, i, -1); + ival = gtk_tree_model_iter_next(GTK_TREE_MODEL(normalise_list_store), &iter); + i++; + } + +} + +static void normalise_delete_atom(GtkWidget *widget, GtkWidget *tree_view) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(tree_view), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(normalise_list_store), &iter, path); + gtk_list_store_remove(GTK_LIST_STORE(normalise_list_store), &iter); + normalise_atoms_number(normalise_list_store); + +} + +static void normalise_add_atom(GtkWidget *widget, GtkWidget *atom_define) { + + GtkTreeIter iter; + unsigned int number; + + number = gtk_combo_box_get_active(GTK_COMBO_BOX(atom_define)); + gtk_list_store_append(normalise_list_store, &iter); + printf("Adding element #%i, Z=%i, name=%s\n", number, elements[number].z, elements[number].element_name); + gtk_list_store_set(normalise_list_store, &iter, NORMALISE_ATOMS_COLUMN_Z, elements[number].z, + NORMALISE_ATOMS_COLUMN_ELEMENT, elements[number].element_name, + NORMALISE_ATOMS_COLUMN_REF, number, -1); + normalise_atoms_number(normalise_list_store); + +} + +static void normalise_selection_changed(GtkTreeSelection *selection, GtkWidget *delete_button) { + + gtk_widget_set_sensitive(delete_button, gtk_tree_selection_get_selected(selection, NULL, NULL)); + +} + +static void normalise_dialog_do_list(GtkWidget *vbox) { + + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + GtkWidget *scrolledwindow; + GtkWidget *add_button; + GtkWidget *delete_button; + GtkWidget *button_box; + GtkWidget *atom_define; + + unsigned int i = 0; + + normalise_list_store = gtk_list_store_new(NORMALISE_ATOMS_COLUMNS, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT); + + normalise_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(normalise_list_store)); + g_object_unref(G_OBJECT(normalise_list_store)); + + renderer = gtk_cell_renderer_text_new(); + + column = gtk_tree_view_column_new_with_attributes("#", renderer, "text", NORMALISE_ATOMS_COLUMN_NUMBER, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(normalise_tree), column); + + column = gtk_tree_view_column_new_with_attributes("Z", renderer, "text", NORMALISE_ATOMS_COLUMN_Z, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(normalise_tree), column); + + column = gtk_tree_view_column_new_with_attributes("Element", renderer, "text", NORMALISE_ATOMS_COLUMN_ELEMENT, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(normalise_tree), column); + + scrolledwindow = gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(normalise_tree)), + gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(normalise_tree))); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolledwindow), GTK_WIDGET(normalise_tree)); + + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolledwindow), TRUE, TRUE, 5); + + button_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), button_box, FALSE, FALSE, 5); + + add_button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_box_pack_end(GTK_BOX(button_box), add_button, FALSE, FALSE, 5); + + atom_define = gtk_combo_box_new_text(); + elements_initialise(); + while ( elements[i].z > 0 ) { + gtk_combo_box_append_text(GTK_COMBO_BOX(atom_define), elements[i].element_name); + i++; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(atom_define), 0); + gtk_box_pack_end(GTK_BOX(button_box), GTK_WIDGET(atom_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(add_button), "clicked", G_CALLBACK(normalise_add_atom), atom_define); + + delete_button = gtk_button_new_from_stock(GTK_STOCK_DELETE); + gtk_box_pack_end(GTK_BOX(button_box), delete_button, FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(delete_button), "clicked", G_CALLBACK(normalise_delete_atom), normalise_tree); + + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(normalise_tree))), "changed", + G_CALLBACK(normalise_selection_changed), delete_button); + gtk_widget_set_sensitive(delete_button, + gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(normalise_tree)), NULL, NULL)); + +} + +void normalise_dialog_open() { + + GtkWidget *normalise_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *normalise_level; + GtkWidget *normalise_level_hbox; + + if ( normalise_window_open ) { + return; + } + normalise_window_open = 1; + + normalise_window = gtk_dialog_new_with_buttons("Sharpen / Normalise", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_UNDO, GTK_RESPONSE_REJECT, + GTK_STOCK_EXECUTE, GTK_RESPONSE_APPLY, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size(GTK_WINDOW(normalise_window), -1, 300); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(normalise_window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + normalise_dialog_do_list(vbox); + + normalise_level_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(normalise_level_hbox), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(normalise_level_hbox), GTK_WIDGET(gtk_label_new("No sharpening")), FALSE, FALSE, 5); + normalise_level = gtk_hscale_new_with_range(0, 1, 0.1); + gtk_box_pack_start(GTK_BOX(normalise_level_hbox), GTK_WIDGET(normalise_level), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(normalise_level), 1); + gtk_scale_set_draw_value(GTK_SCALE(normalise_level), TRUE); + gtk_box_pack_start(GTK_BOX(normalise_level_hbox), GTK_WIDGET(gtk_label_new("Normalisation")), FALSE, FALSE, 5); + + g_signal_connect(G_OBJECT(normalise_window), "response", G_CALLBACK(normalise_window_close), normalise_level); + + gtk_widget_show_all(normalise_window); + +} + +static void normalise_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void normalise_wilsonplot(ReflectionList *reflections) { + + FILE *fh; + double a = data_a(); + double b = data_b(); + double c = data_c(); + double gamma = data_gamma(); + unsigned int i; + FILE *gnuplot; + double sigma = 0; + unsigned int n = 0; + + for ( i=1; in_reflections; i++ ) { + if ( reflections->refs[i].amplitude > 0 ) { + sigma += reflections->refs[i].amplitude * reflections->refs[i].amplitude; + n++; + } + } + sigma = sigma / n; + + fh = fopen("synth2d-dethermalise.dat", "w"); + for ( i=1; in_reflections; i++ ) { + + double am_sq = reflections->refs[i].amplitude * reflections->refs[i].amplitude; + + /* Only include reflections which actually exist */ + if ( am_sq > 0 ) { + + double s2; + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + s2 = pow(resolution(h, k, l, a, b, c, gamma), 2); + fprintf(fh, "%f %f\n", s2, log(am_sq)); /* "log" is actually "ln" */ + + } + } + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + normalise_gpwrite(gnuplot, "set autoscale\n"); + normalise_gpwrite(gnuplot, "unset log\n"); + normalise_gpwrite(gnuplot, "unset label\n"); + normalise_gpwrite(gnuplot, "set xtic auto\n"); + normalise_gpwrite(gnuplot, "set ytic auto\n"); + normalise_gpwrite(gnuplot, "set grid\n"); + normalise_gpwrite(gnuplot, "set grid\n"); + normalise_gpwrite(gnuplot, "set bmargin 7cm\n"); + normalise_gpwrite(gnuplot, "set ylabel 'ln(I)' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set xlabel 's^2 / nm^(-2)' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set title 'Wilson Plot' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "plot 'synth2d-dethermalise.dat'\n"); + normalise_gpwrite(gnuplot, "K=7; B=0.05\n"); + normalise_gpwrite(gnuplot, "fit log(K)-2*B*x 'synth2d-dethermalise.dat' via K,B\n"); + normalise_gpwrite(gnuplot, "set label 'ln(K) = %3.5g',log(K),', B = %3.5g nm^2',B at screen 0.1, screen 0.1 font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "replot log(K)-2*B*x lw 2 lc 3\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} + +GtkWidget *normalise_k_box; +GtkWidget *normalise_b_box; +ReflectionList *normalise_dt_undo_copy = NULL; + +static void normalise_dt_undo() { + printf("NM: Undoing dethermalisationn.\n"); + if ( normalise_dt_undo_copy ) { + main_substitutereflections(normalise_dt_undo_copy); + free(normalise_dt_undo_copy); + normalise_dt_undo_copy = NULL; + } else { + error_report("Nothing to undo..."); + return; + } +} + +void normalise_dethermalise(ReflectionList *reflections, double level) +{ + unsigned int i; + double kf; + double bf; + const char *ks; + const char *bs; + double a = data_a(); + double b = data_b(); + double c = data_c(); + double gamma = data_gamma(); + + printf("NM: Wilson Normalising...\n"); + + if ( normalise_dt_undo_copy ) { + free(normalise_dt_undo_copy); + } + normalise_dt_undo_copy = malloc(sizeof(ReflectionList)); + memcpy(normalise_dt_undo_copy, reflections, sizeof(ReflectionList)); + + ks = gtk_entry_get_text(GTK_ENTRY(normalise_k_box)); + bs = gtk_entry_get_text(GTK_ENTRY(normalise_b_box)); + if ( sscanf(ks, "%lf", &kf) != 1 ) { + error_report("Invalid value for 'ln(K)'"); + return; + } + if ( sscanf(bs, "%lf", &bf) != 1 ) { + error_report("Invalid value for 'B'"); + return; + } + + printf("NM: ln(K)=%f, B=%f => K=%f\n", kf, bf, exp(kf)); + kf = exp(kf); + + for ( i=0; in_reflections; i++ ) { + + double am_sq = pow(reflections->refs[i].amplitude, 2.0); + + /* Only include reflections which actually exist */ + if ( am_sq > 0.0 ) { + + double am_e; + double am = sqrt(am_sq); + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + double s2 = pow(resolution(h, k, l, a, b, c, gamma), 2.0); + + am_e = sqrt(am_sq/(kf*exp(-2.0*bf*s2))); + + am = pow((am_e), level) * pow(am, 1.0-level); + reflections->refs[i].amplitude = am; + + } + + } + + displaywindow_switchview(); + displaywindow_statusbar("Amplitudes normalised"); +} + +static unsigned int normalise_dtw_open = 0; + +static gint normalise_dt_window_close(GtkWidget *widget, gint response, + GtkWidget *normalise_level) +{ + + if ( response == GTK_RESPONSE_APPLY ) { + main_dethermalise(gtk_range_get_value(GTK_RANGE(normalise_level))); + } + + if ( response == GTK_RESPONSE_REJECT ) { + normalise_dt_undo(); + } + + if ( (response == GTK_RESPONSE_CLOSE) || (response == GTK_RESPONSE_NONE) + || (response == GTK_RESPONSE_DELETE_EVENT) ) { + normalise_dtw_open = 0; + gtk_widget_destroy(widget); + } + + return 0; +} + +void normalise_dethermalise_open() { + + GtkWidget *normalise_dt_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *k_label; + GtkWidget *b_label; + GtkWidget *b_units; + GtkWidget *normalise_dt_level; + GtkWidget *normalise_dt_level_hbox; + + if ( normalise_dtw_open ) { + return; + } + normalise_dtw_open = 1; + + normalise_dt_window = gtk_dialog_new_with_buttons("Normalise", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_UNDO, GTK_RESPONSE_REJECT, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_EXECUTE, GTK_RESPONSE_APPLY, NULL); + gtk_window_set_default_size(GTK_WINDOW(normalise_dt_window), -1, 100); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(normalise_dt_window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + table = gtk_table_new(3, 2, FALSE); + k_label = gtk_label_new("ln(K) = "); + gtk_table_attach_defaults(GTK_TABLE(table), k_label, 1, 2, 1, 2); + gtk_misc_set_alignment(GTK_MISC(k_label), 1, 0.5); + b_label = gtk_label_new("B = "); + gtk_table_attach_defaults(GTK_TABLE(table), b_label, 1, 2, 2, 3); + gtk_misc_set_alignment(GTK_MISC(b_label), 1, 0.5); + normalise_k_box = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), normalise_k_box, 2, 3, 1, 2); + normalise_b_box = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), normalise_b_box, 2, 3, 2, 3); + b_units = gtk_label_new(" nm^-2"); + gtk_table_attach_defaults(GTK_TABLE(table), b_units, 3, 4, 2, 3); + gtk_misc_set_alignment(GTK_MISC(b_units), 0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 5); + + normalise_dt_level_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(normalise_dt_level_hbox), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(normalise_dt_level_hbox), GTK_WIDGET(gtk_label_new("No sharpening")), FALSE, FALSE, 5); + normalise_dt_level = gtk_hscale_new_with_range(0, 1, 0.1); + gtk_box_pack_start(GTK_BOX(normalise_dt_level_hbox), GTK_WIDGET(normalise_dt_level), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(normalise_dt_level), 1); + gtk_scale_set_draw_value(GTK_SCALE(normalise_dt_level), TRUE); + gtk_box_pack_start(GTK_BOX(normalise_dt_level_hbox), GTK_WIDGET(gtk_label_new("Normalisation")), FALSE, FALSE, 5); + + g_signal_connect(G_OBJECT(normalise_dt_window), "response", G_CALLBACK(normalise_dt_window_close), normalise_dt_level); + + gtk_widget_show_all(normalise_dt_window); + +} + +void normalise_falloffplot(ReflectionList *reflections) { + + FILE *fh; + double a = data_a(); + double b = data_b(); + double c = data_c(); + double gamma = data_gamma(); + unsigned int i; + FILE *gnuplot; + + fh = fopen("synth2d-falloff.dat", "w"); + for ( i=1; in_reflections; i++ ) { + + double am = reflections->refs[i].amplitude; + + /* Only include reflections which actually exist */ + if ( am > 0 ) { + + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + double s = resolution(h, k, l, a, b, c, gamma); + + fprintf(fh, "%f %f\n", s, log(am)); /* "log" is actually "ln" */ + + } + } + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + normalise_gpwrite(gnuplot, "set autoscale\n"); + normalise_gpwrite(gnuplot, "unset log\n"); + normalise_gpwrite(gnuplot, "unset label\n"); + normalise_gpwrite(gnuplot, "set xtic auto\n"); + normalise_gpwrite(gnuplot, "set ytic auto\n"); + normalise_gpwrite(gnuplot, "unset grid\n"); + normalise_gpwrite(gnuplot, "set ylabel 'ln F' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set xlabel 's / nm^(-1)' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set title 'Resolution Falloff Plot' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "plot 'synth2d-falloff.dat'\n"); + normalise_gpwrite(gnuplot, "a=-14; b=10; c=0.01\n"); + normalise_gpwrite(gnuplot, "fit a+b*exp(-c*x) 'synth2d-falloff.dat' via a,b,c\n"); + normalise_gpwrite(gnuplot, "set label 'a = %3.5g',a,', b = %3.5g',b, ', c = %3.5g',c at graph 0.4, graph 0.7 font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "replot a+b*exp(-c*x) lc 3 lw 2\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} + +typedef struct { + GtkWidget *a; + GtkWidget *b; + GtkWidget *c; +} NormaliseExpDialog; + +void normalise_exponential(ReflectionList *reflections, double a, double b, double c) { + + size_t i; + double al = data_a(); + double bl = data_b(); + double cl = data_c(); + double gamma = data_gamma(); + + printf("NM: Exponential normalisation, a=%f, b=%f, c=%f\n", a, b, c); + + for ( i=1; in_reflections; i++ ) { + + double am; + + am = reflections->refs[i].amplitude; + + if ( am > 0.0 ) { + + const signed int h = reflections->refs[i].h; + const signed int k = reflections->refs[i].k; + const signed int l = reflections->refs[i].l; + const double s = resolution(h, k, l, al, bl, cl, gamma); + const double n = a + b*exp(-c*s); + + reflections->refs[i].amplitude = am / exp(n); + + } + + } + + displaywindow_switchview(); + displaywindow_statusbar("Amplitudes normalised"); + + +} + +static gint normalise_exponential_response(GtkWidget *window, gint response, NormaliseExpDialog *dialog) { + + if ( response == GTK_RESPONSE_OK ) { + + const char *as; + const char *bs; + const char *cs; + double a, b, c; + + as = gtk_entry_get_text(GTK_ENTRY(dialog->a)); + bs = gtk_entry_get_text(GTK_ENTRY(dialog->b)); + cs = gtk_entry_get_text(GTK_ENTRY(dialog->c)); + + if ( sscanf(as, "%lf", &a) != 1 ) { + error_report("Invalid value for 'a'"); + return 0; + } + if ( sscanf(bs, "%lf", &b) != 1 ) { + error_report("Invalid value for 'b'"); + return 0; + } + if ( sscanf(cs, "%lf", &c) != 1 ) { + error_report("Invalid value for 'c'"); + return 0; + } + + main_normalise_exponential(a, b, c); + + } + + gtk_widget_destroy(window); + + return 0; +} + +void normalise_exponential_open() { + + NormaliseExpDialog *dialog; + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *label; + + dialog = malloc(sizeof(NormaliseExpDialog)); + + window = gtk_dialog_new_with_buttons("Exponential Normalisation", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 5); + + label = gtk_label_new("a = "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new("b = "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new("c = "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + dialog->a = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), dialog->a, 2, 3, 1, 2); + dialog->b = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), dialog->b, 2, 3, 2, 3); + dialog->c = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), dialog->c, 2, 3, 3, 4); + + g_signal_connect(G_OBJECT(window), "response", G_CALLBACK(normalise_exponential_response), dialog); + + gtk_widget_show_all(window); + +} diff --git a/src/normalise.h b/src/normalise.h new file mode 100644 index 0000000..5cf9839 --- /dev/null +++ b/src/normalise.h @@ -0,0 +1,32 @@ +/* + * normalise.h + * + * Normalisation stuff + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef NORMALISE_H +#define NORMALISE_H + +#include "reflist.h" + +extern void normalise_dialog_open(void); +extern void normalise_wilsonplot(ReflectionList *reflections); +extern void normalise_falloffplot(ReflectionList *reflections); +extern void normalise_dethermalise_open(void); +extern void normalise_exponential_open(void); +extern void normalise_exponential(ReflectionList *reflections, double a, double b, double c); +extern void normalise_dethermalise(ReflectionList *reflections, double level); +extern void normalise_execute(ReflectionList *reflections, double level); +extern double resolution(signed int h, signed int k, signed int l, + double a, double b, double c, double gamma); + +#endif /* NORMALISE_H */ diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..ccaa115 --- /dev/null +++ b/src/options.c @@ -0,0 +1,191 @@ +/* + * options.c + * + * Handle run-time options and stuff + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +static void options_createhomedir(char *filename) { + + /* Create directory. */ + if ( mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR) != 0 ) { + fprintf(stderr, "OP: Couldn't create ~/.synth2d directory.\n"); + exit(1); + } + +} + +static char *options_checkdir() { + + int glob_retval; + glob_t glob_result; + struct stat stat_buffer; + struct stat *statbuf; + char *dir_filename; + + glob_retval = glob("~", GLOB_TILDE, NULL, &glob_result); + statbuf = &stat_buffer; + if ( glob_retval != 0 ) { + + fprintf(stderr, "OP: glob() for ~/.synth2d failed: "); + switch ( glob_retval ) { + case GLOB_NOSPACE : fprintf(stderr, "GLOB_NOSPACE\n"); break; + case GLOB_ABORTED : fprintf(stderr, "GLOB_ABORTED\n"); break; + case GLOB_NOMATCH : fprintf(stderr, "GLOB_NOMATCH\n"); break; + default : fprintf(stderr, "Unknown!\n"); break; + } + return NULL; + + } + + dir_filename = malloc(strlen(glob_result.gl_pathv[0]) + 12); + strcpy(dir_filename, glob_result.gl_pathv[0]); + globfree(&glob_result); + strcat(dir_filename, "/.synth2d"); + + if ( stat(dir_filename, statbuf) != -1 ) { + + if ( S_ISDIR(stat_buffer.st_mode) ) { + + //printf("OP: Found '%s'.\n", dir_filename); + + } else { + + fprintf(stderr, "OP: Found '%s', but it isn't a directory!\n", dir_filename); + return NULL; + + } + + } else { + + printf("OP: ~/.synth2d directory not found: creating it.\n"); + options_createhomedir(dir_filename); + + } + + return dir_filename; + +} + + +void options_load() { + + char *dir_filename; + char *rc_filename; + char *wisdom_filename; + struct stat stat_buffer; + struct stat *statbuf; + + dir_filename = options_checkdir(); + + rc_filename = malloc(strlen(dir_filename) + 8); + strcpy(rc_filename, dir_filename); + strcat(rc_filename, "/config"); + statbuf = &stat_buffer; + if ( stat(rc_filename, statbuf) != -1 ) { + + if ( (S_ISREG(stat_buffer.st_mode)) || (S_ISLNK(stat_buffer.st_mode)) ) { + + + } else { + + fprintf(stderr, "OP: Config file isn't a regular file or a link. Giving up!\n"); + exit(1); + + } + + } else { + printf("OP: Config file doesn't exist. This is OK.\n"); + } + free(rc_filename); + + wisdom_filename = malloc(strlen(dir_filename) + 13); + strcpy(wisdom_filename, dir_filename); + strcat(wisdom_filename, "/fftw-wisdom"); + if ( stat(wisdom_filename, statbuf) != -1 ) { + + if ( (S_ISREG(stat_buffer.st_mode)) || (S_ISLNK(stat_buffer.st_mode)) ) { + + FILE *fh; + + fh = fopen(wisdom_filename, "r"); + if ( fh == NULL ) { + fprintf(stderr, "OP: Error opening FFTW wisdom file.\n"); + } else { + if ( fftw_import_wisdom_from_file(fh) == 1 ) { + //printf("OP: Successfully imported FFTW wisdom\n"); + } else { + fprintf(stderr, "OP: Failed to import FFTW wisdom\n"); + } + } + + fclose(fh); + + } else { + + fprintf(stderr, "OP: FFTW wisdom file isn't a regular file or a link. Giving up!\n"); + exit(1); + + } + + } else { + printf("OP: FFTW wisdom file doesn't exist. This is OK.\n"); + } + free(wisdom_filename); + + free(dir_filename); + +} + +void options_save() { + + char *dir_filename; + char *rc_filename; + char *wisdom_filename; + FILE *fh; + + dir_filename = options_checkdir(); + + rc_filename = malloc(strlen(dir_filename) + 8); + strcpy(rc_filename, dir_filename); + strcat(rc_filename, "/config"); + fh = fopen(rc_filename, "w"); + if ( fh == NULL ) { + fprintf(stderr, "OP: Error opening options file.\n"); + } else { + fprintf(fh, "# synth2d options file\n"); + } + free(rc_filename); + + wisdom_filename = malloc(strlen(dir_filename) + 13); + strcpy(wisdom_filename, dir_filename); + strcat(wisdom_filename, "/fftw-wisdom"); + fh = fopen(wisdom_filename, "w"); + if ( fh == NULL ) { + fprintf(stderr, "OP: Error opening options file.\n"); + } else { + fftw_export_wisdom_to_file(fh); + } + fclose(fh); + free(wisdom_filename); + + free(dir_filename); + +} + diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..27e3d7f --- /dev/null +++ b/src/options.h @@ -0,0 +1,23 @@ +/* + * options.h + * + * Handle run-time options and stuff + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef OPTIONS_H +#define OPTIONS_H + +extern void options_load(void); +extern void options_save(void); + +#endif /* OPTIONS_H */ + diff --git a/src/png-file.c b/src/png-file.c new file mode 100644 index 0000000..09925a7 --- /dev/null +++ b/src/png-file.c @@ -0,0 +1,125 @@ +/* + * png-file.c + * + * PNG output + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "data.h" +#include "colwheel.h" +#include "renderer.h" + +int png_write_real(const char *filename, fftw_complex *out, double brightness, double gamma_angle, int width, int height, int nx, int ny) { + + FILE *fh; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers; + int xn, yn; + int width_n, height_n; + ComplexArray cxar; + + fh = fopen(filename, "wb"); + if (!fh) { + fprintf(stderr, "Couldn't open output file.\n"); + return 1; + } + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if ( !png_ptr ) { + fprintf(stderr, "Couldn't create PNG write structure.\n"); + fclose(fh); + return 1; + } + info_ptr = png_create_info_struct(png_ptr); + if ( !info_ptr ) { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + fprintf(stderr, "Couldn't create PNG info structure.\n"); + fclose(fh); + return 1; + } + if ( setjmp(png_jmpbuf(png_ptr)) ) { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fh); + fprintf(stderr, "PNG write failed.\n"); + return 1; + } + png_init_io(png_ptr, fh); + + width_n = (int)renderer_width(width, height, gamma_angle, nx, ny); + height_n = (int)renderer_height(width, height, gamma_angle, nx, ny); + cxar = renderer_draw(out, width, height, gamma_angle, nx, ny); + + png_set_IHDR(png_ptr, info_ptr, width_n, height_n, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + /* Write the image data */ + row_pointers = malloc(height_n*sizeof(png_bytep *)); + + for ( yn=0; yn 1 ) am = 1; + + row_pointers[yn][3*xn] = (png_byte)255*colwheel_red(am, ph); + row_pointers[yn][3*xn+1] = (png_byte)255*colwheel_green(am, ph); + row_pointers[yn][3*xn+2] = (png_byte)255*colwheel_blue(am, ph); + + } + } + + for ( yn=0; yn + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef PNGFILE_H +#define PNGFILE_H + +#include + +extern int png_write(const char *filename, fftw_complex *out, double norm, int nx, int ny); + +#endif /* PNGFILE_H */ diff --git a/src/refine-brent.c b/src/refine-brent.c new file mode 100644 index 0000000..06a6f95 --- /dev/null +++ b/src/refine-brent.c @@ -0,0 +1,138 @@ +/* + * refine-brent.c + * + * Refinement by Sequential Brent Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +/* For the purposes of GSL */ +static double refine_calc_r(double loc, void *params) { + + double r; + ReflectionList *model_reflections; + RefinementPair *pair = params; + AtomicModel *model; + + model = model_copy(pair->model); + + switch ( pair->cur_mod_coor ) { + case COORDINATE_X : model->atoms[pair->cur_mod_atom].x = loc; break; + case COORDINATE_Y : model->atoms[pair->cur_mod_atom].y = loc; break; + case COORDINATE_Z : model->atoms[pair->cur_mod_atom].z = loc; break; + } + + model_reflections = model_calculate_f(pair->reflections, model, 69); + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r = stat_r2(pair->reflections, model_reflections); /* stat_r(Fobs, Fcalc) */ + } else { + r = stat_r(pair->reflections, model_reflections); /* stat_r(Fobs, Fcalc) */ + } + free(model_reflections); + + model_free(model); + + return r; + +} + +void refine_brent(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec) { + + gsl_function F; + gsl_min_fminimizer *s; + int status; + RefinementPair pair; + + pair.reflections = reflections; + pair.model = model; + pair.cur_mod_coor = COORDINATE_X; + pair.cur_mod_atom = 1; + + F.function = &refine_calc_r; + F.params = &pair; + + s = gsl_min_fminimizer_alloc(gsl_min_fminimizer_brent); + + while ( model->refine_window->run_semaphore ) { + + double loc_cur; + unsigned int iter = 0; + + /* Select the next thing to refine */ + pair.cur_mod_coor++; + if ( pair.cur_mod_coor > COORDINATE_Z ) { + pair.cur_mod_coor = COORDINATE_X; + pair.cur_mod_atom++; + if ( pair.cur_mod_atom >= pair.model->n_atoms ) pair.cur_mod_atom = 1; + } + // if ( random() < (0.3333*RAND_MAX) ) pair.cur_mod_coor = COORDINATE_Y; + // if ( random() > (0.6666*RAND_MAX) ) pair.cur_mod_coor = COORDINATE_Z; + // atom = 1 + (random()*(double)(pair.model->n_atoms-1))/RAND_MAX; + // pair.cur_mod_atom = atom; + + loc_cur = 0; + switch ( pair.cur_mod_coor ) { + case COORDINATE_X : loc_cur = pair.model->atoms[pair.cur_mod_atom].x; break; + case COORDINATE_Y : loc_cur = pair.model->atoms[pair.cur_mod_atom].y; break; + case COORDINATE_Z : loc_cur = pair.model->atoms[pair.cur_mod_atom].z; break; + } + + gsl_min_fminimizer_set(s, &F, loc_cur, loc_cur-0.1, loc_cur+0.1); + + do { + + double lo, up; + + /* Iterate */ + gsl_min_fminimizer_iterate(s); + iter++; + + /* Get the current estimate */ + loc_cur = gsl_min_fminimizer_x_minimum(s); + lo = gsl_min_fminimizer_x_lower(s); + up = gsl_min_fminimizer_x_upper(s); + + /* Check for convergence */ + status = gsl_min_test_interval(lo, up, 0.001, 0.0); + + } while ( status == GSL_CONTINUE ); + + if (status == GSL_SUCCESS) { + + if ( loc_cur < 0 ) loc_cur = 1+loc_cur; + if ( loc_cur >= 1 ) loc_cur = loc_cur-1; + + switch ( pair.cur_mod_coor ) { + case COORDINATE_X : pair.model->atoms[pair.cur_mod_atom].x = loc_cur; break; + case COORDINATE_Y : pair.model->atoms[pair.cur_mod_atom].y = loc_cur; break; + case COORDINATE_Z : pair.model->atoms[pair.cur_mod_atom].z = loc_cur; break; + } + + refine_schedule_update(model); + + } + + } + + gsl_min_fminimizer_free(s); + +} + diff --git a/src/refine-brent.h b/src/refine-brent.h new file mode 100644 index 0000000..019acd1 --- /dev/null +++ b/src/refine-brent.h @@ -0,0 +1,26 @@ +/* + * refine-brent.h + * + * Refinement by Sequential Brent Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_BRENT_H +#define REFINE_BRENT_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_brent(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_BRENT_H */ + diff --git a/src/refine-cgrad.c b/src/refine-cgrad.c new file mode 100644 index 0000000..5df9ac9 --- /dev/null +++ b/src/refine-cgrad.c @@ -0,0 +1,339 @@ +/* + * refine-cgrad.c + * + * Refinement by Conjugate Gradient Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +static double refine_cgrad_f(const gsl_vector *coordinates, void *params) { + + double r; + RefinementPair *pair = params; + AtomicModel *model; + unsigned int idx; + unsigned int j; + ReflectionList *obs; + ReflectionList *calc; + + obs = pair->reflections; + model = model_copy(pair->model); + + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + free(calc); + model_free(model); + + return r; + +} + +static void refine_cgrad_df(const gsl_vector *coordinates, void *params, gsl_vector *df) { + + RefinementPair *pair; + AtomicModel *model; + ReflectionList *calc; + unsigned int j; + AtomicModel *model_original; + unsigned int idx; + ReflectionList *obs; + double r_old; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_old = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r_old = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + reflist_free(calc); + + idx = 0; + model_original = model_copy(model); + + /* Determine gradients */ + for ( j=0; jn_atoms; j++ ) { + + ReflectionList *calc_new; + + if ( !(model->atoms[j].refine) ) continue; + + if ( pair->spec & REFINE_SPEC_X ) { + + double r_new, derivative; + + model->atoms[j].x += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_Y ) { + + double r_new, derivative; + + model->atoms[j].y += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_Z ) { + + double r_new, derivative; + + model->atoms[j].z += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_B ) { + + double r_new, derivative; + + model->atoms[j].B += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_OCC ) { + + double r_new, derivative; + + model->atoms[j].occ += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + } + + model_free(model_original); + model_free(model); + +} + +static void refine_cgrad_fdf(const gsl_vector *coordinates, void *params, double *f, gsl_vector *df) { + + *f = refine_cgrad_f(coordinates, params); + refine_cgrad_df(coordinates, params, df); + +} + +void refine_cgrad(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec) { + + gsl_multimin_fdfminimizer *s; + gsl_multimin_function_fdf f; + unsigned int iter = 0; + gsl_vector *coordinates; + unsigned int j; + RefinementPair pair; + int iter_status, conv_status; + unsigned int n_params, n_atoms, idx; + + n_params = 0; + if ( spec & REFINE_SPEC_X ) n_params++; + if ( spec & REFINE_SPEC_Y ) n_params++; + if ( spec & REFINE_SPEC_Z ) n_params++; + if ( spec & REFINE_SPEC_B ) n_params++; + if ( spec & REFINE_SPEC_OCC ) n_params++; + + /* Prevent origin from floating */ + if ( (spec & REFINE_SPEC_X) || (spec & REFINE_SPEC_Y) || (spec & REFINE_SPEC_Z) ) { + model->atoms[0].refine = 0; + } + + n_atoms = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) n_atoms++; + } + + f.n = n_params * n_atoms; + s = gsl_multimin_fdfminimizer_alloc(gsl_multimin_fdfminimizer_conjugate_fr, f.n); + f.f = &refine_cgrad_f; f.df = &refine_cgrad_df; f.fdf = &refine_cgrad_fdf; + coordinates = gsl_vector_alloc(f.n); + + /* Set initial estimates */ + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) gsl_vector_set(coordinates, idx++, model->atoms[j].x); + if ( spec & REFINE_SPEC_Y ) gsl_vector_set(coordinates, idx++, model->atoms[j].y); + if ( spec & REFINE_SPEC_Z ) gsl_vector_set(coordinates, idx++, model->atoms[j].z); + if ( spec & REFINE_SPEC_B ) gsl_vector_set(coordinates, idx++, model->atoms[j].B); + if ( spec & REFINE_SPEC_OCC ) gsl_vector_set(coordinates, idx++, model->atoms[j].occ); + } + } + + pair.model = model; pair.reflections = reflections; + pair.spec = spec; + f.params = &pair; + + gsl_multimin_fdfminimizer_set(s, &f, coordinates, 1e-4, 1e-6); + + do { + + /* Iterate */ + iter_status = gsl_multimin_fdfminimizer_iterate(s); + printf ("status = %s\n", gsl_strerror(iter_status)); + iter++; + + /* Check for error */ + if ( iter_status ) { + printf("status after iteration = %s\n", gsl_strerror(iter_status)); + break; + } + + /* Test for convergence */ + conv_status = gsl_multimin_test_gradient(s->gradient, 1e-6); + printf("status after convergence test = %s\n", gsl_strerror(conv_status)); + + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + refine_schedule_update(model); + + } while ( (conv_status == GSL_CONTINUE) && (iter < MAX_REFINEMENT_ITERATIONS) && (model->refine_window->run_semaphore) ); + + printf("%i iterations performed\n", iter); + if ( iter == MAX_REFINEMENT_ITERATIONS ) printf("Reached maximum allowed number of iterations"); + + gsl_multimin_fdfminimizer_free(s); + gsl_vector_free(coordinates); + +} + diff --git a/src/refine-cgrad.h b/src/refine-cgrad.h new file mode 100644 index 0000000..1f28a39 --- /dev/null +++ b/src/refine-cgrad.h @@ -0,0 +1,26 @@ +/* + * refine-cgrad.h + * + * Refinement by Conjugate Gradient Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_CGRAD_H +#define REFINE_CGRAD_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_cgrad(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_CGRAD_H */ + diff --git a/src/refine-lmder.c b/src/refine-lmder.c new file mode 100644 index 0000000..ba99eaf --- /dev/null +++ b/src/refine-lmder.c @@ -0,0 +1,439 @@ +/* + * refine-lmder.c + * + * Refinement by GSL's Levenberg-Marquardt LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +static int refine_lmder_f(const gsl_vector *coordinates, void *params, gsl_vector *f) { + + RefinementPair *pair; + AtomicModel *model; + ReflectionList *calc; + unsigned int i, j, idx; + double scale; + ReflectionList *obs; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + scale = gsl_vector_get(coordinates, idx++); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + /* Calculate residuals */ + for ( i=1; in_reflections; i++ ) { + double am_calc, residual; + am_calc = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + /* ( k|Icalc| - |Iobs| ) / error(|Iobs|) */ + residual = (scale*am_calc*am_calc - obs->refs[i].amplitude*obs->refs[i].amplitude)/(sqrt(2)*obs->refs[i].am_error); + } else { + residual = (scale*am_calc - obs->refs[i].amplitude)/obs->refs[i].am_error; /* ( k|Fcalc| - |Fobs| ) / error(|Fobs|) */ + } + gsl_vector_set(f, i-1, residual); + // printf("Residual for %3i %3i %3i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, residual); + } + + model_free(model); + free(calc); + + return GSL_SUCCESS; + +} + +static int refine_lmder_df(const gsl_vector *coordinates, void *params, gsl_matrix *J) { + + RefinementPair *pair; + AtomicModel *model_original; + AtomicModel *model; + ReflectionList *obs; + ReflectionList *calc; + unsigned int i, j, idx; + double scale; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + scale = gsl_vector_get(coordinates, idx++); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + idx = 0; + + model_original = model_copy(model); + + /* Gradient of the scale factor */ + for ( i=1; in_reflections; i++ ) { + double am; + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + gsl_matrix_set(J, i-1, idx, am*am); + } else { + gsl_matrix_set(J, i-1, idx, am); + } + } + idx++; + + /* Determine gradients of atomic coordinates and B-factors */ + for ( j=0; jn_atoms; j++ ) { + + if ( !(model->atoms[j].refine) ) continue; + double x, y, z; + ReflectionList *reflections_new; + + x = model->atoms[j].x; + y = model->atoms[j].y; + z = model->atoms[j].z; + + if ( pair->spec & REFINE_SPEC_X ) { + model->atoms[j].x += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt x%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_Y ) { + model->atoms[j].y += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt y%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_Z ) { + model->atoms[j].z += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt z%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_B ) { + model->atoms[j].B += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt B%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_OCC ) { + model->atoms[j].occ += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt B%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + } + + model_free(model_original); + model_free(model); + free(calc); + + return GSL_SUCCESS; + +} + +static int refine_lmder_fdf(const gsl_vector *coordinates, void *params, gsl_vector *f, gsl_matrix *J) { + + refine_lmder_f(coordinates, params, f); + refine_lmder_df(coordinates, params, J); + + return GSL_SUCCESS; + +} + +void refine_lmder(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec) { + + gsl_multifit_fdfsolver *s; + gsl_multifit_function_fdf f; + unsigned int iter = 0; + gsl_vector *coordinates; + unsigned int j; + RefinementPair pair; + int iter_status, conv_status; + gsl_matrix *covar; + unsigned int n_params, n_atoms, idx; + double scale; + ReflectionList *calc; + + n_params = 0; + if ( spec & REFINE_SPEC_X ) n_params++; + if ( spec & REFINE_SPEC_Y ) n_params++; + if ( spec & REFINE_SPEC_Z ) n_params++; + if ( spec & REFINE_SPEC_B ) n_params++; + if ( spec & REFINE_SPEC_OCC ) n_params++; + + /* Prevent origin from floating */ + if ( (spec & REFINE_SPEC_X) || (spec & REFINE_SPEC_Y) || (spec & REFINE_SPEC_Z) ) { + model->atoms[0].refine = 0; + } + + n_atoms = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) n_atoms++; + } + + f.n = reflections->n_reflections-1; f.p = 1 + n_params * n_atoms; + s = gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmsder, f.n, f.p); + f.f = &refine_lmder_f; f.df = &refine_lmder_df; f.fdf = &refine_lmder_fdf; + printf("Refining %i parameters against %i reflections\n", f.p, f.n); + coordinates = gsl_vector_alloc(f.p); + + /* Set initial estimates */ + idx = 0; + calc = model_calculate_f(reflections, NULL, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + scale = stat_scale_intensity(reflections, calc); + } else { + scale = stat_scale(reflections, calc); + } + reflist_free(calc); + gsl_vector_set(coordinates, idx++, scale); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) gsl_vector_set(coordinates, idx++, model->atoms[j].x); + if ( spec & REFINE_SPEC_Y ) gsl_vector_set(coordinates, idx++, model->atoms[j].y); + if ( spec & REFINE_SPEC_Z ) gsl_vector_set(coordinates, idx++, model->atoms[j].z); + if ( spec & REFINE_SPEC_B ) gsl_vector_set(coordinates, idx++, model->atoms[j].B); + if ( spec & REFINE_SPEC_OCC ) gsl_vector_set(coordinates, idx++, model->atoms[j].occ); + } + } + + pair.model = model; pair.reflections = reflections; pair.spec = spec; + f.params = &pair; + + covar = gsl_matrix_alloc(f.p, f.p); + gsl_multifit_fdfsolver_set(s, &f, coordinates); + + printf("initial: scale=%f, |f(x)|=%g\n", gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f)); + + do { + + ReflectionList *model_reflections; + + /* Iterate */ + printf("before iteration %i: scale=%f, |f(x)|=%g\n", iter, gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f)); + iter_status = gsl_multifit_fdfsolver_iterate(s); + printf("after iteration %i: scale=%f, |f(x)|=%g, status=%s, ", iter, gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f), gsl_strerror(iter_status)); + iter++; + + /* Check for error */ + if ( iter_status ) { + printf("\nError in LSQ refinement"); + break; + } + + /* Test for convergence */ + conv_status = gsl_multifit_test_delta(s->dx, s->x, 1e-6, 1e-6); + //printf("status after convergence test = %s\n", gsl_strerror(conv_status)); + + idx = 0; + scale = gsl_vector_get(s->x, idx++); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + model_reflections = model_calculate_f(reflections, model, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + printf("R2=%.2f%%\n\n", stat_r2(reflections, model_reflections)*100); /* stat_r(Fobs, Fcalc) */ + } else { + printf("R1=%.2f%%\n\n", stat_r(reflections, model_reflections)*100); /* stat_r(Fobs, Fcalc) */ + } + free(model_reflections); + + refine_schedule_update(model); + + } while ( (conv_status == GSL_CONTINUE) && (iter < MAX_REFINEMENT_ITERATIONS) && (model->refine_window->run_semaphore) ); + + printf("%i iterations performed\n", iter); + if ( iter == MAX_REFINEMENT_ITERATIONS ) printf("Reached maximum allowed number of iterations"); + + gsl_multifit_covar(s->J, 0.0, covar); + gsl_matrix_free(covar); + + gsl_multifit_fdfsolver_free(s); + gsl_vector_free(coordinates); + +} + diff --git a/src/refine-lmder.h b/src/refine-lmder.h new file mode 100644 index 0000000..1e2730d --- /dev/null +++ b/src/refine-lmder.h @@ -0,0 +1,26 @@ +/* + * refine-lmder.h + * + * Refinement by GSL's Levenberg-Marquardt LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_LMDER_H +#define REFINE_LMDER_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_lmder(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_LMDER_H */ + diff --git a/src/refine-lsq.c b/src/refine-lsq.c new file mode 100644 index 0000000..1845212 --- /dev/null +++ b/src/refine-lsq.c @@ -0,0 +1,320 @@ +/* + * refine-lsq.c + * + * Refinement by Simple LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +static AtomicModel *refine_model_from_parameters(const gsl_vector *parameters, RefinementSpec spec, AtomicModel *template) { + + unsigned int j, idx; + AtomicModel *model; + + model = model_copy(template); + + idx = 1; /* Ignore scale factor */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(parameters, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(parameters, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(parameters, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(parameters, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(parameters, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + return model; + +} + +static double refine_lsq_gradient(const gsl_vector *parameters, const ReflectionList *obs, RefinementSpec spec, + size_t param_idx, size_t obs_idx, ReflectionList *f_calc, AtomicModel *model) { + + unsigned int j, idx; + double f_calc_new, gradient; + AtomicModel *model_new; + double scale; + + if ( spec & REFINE_SPEC_INTENSITIES ) { + if ( param_idx == 0 ) return f_calc->refs[obs_idx].amplitude*f_calc->refs[obs_idx].amplitude; /* Scale factor */ + } else { + if ( param_idx == 0 ) return f_calc->refs[obs_idx].amplitude; /* Scale factor */ + } + + scale = gsl_vector_get(parameters, 0); + idx = 0; + model_new = model_copy(model); + for ( j=0; jn_atoms; j++ ) { + + if ( !(model->atoms[j].refine) ) continue; + + if ( spec & REFINE_SPEC_X ) idx++; + if ( idx == param_idx ) { + model->atoms[j].x += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_Y ) idx++; + if ( idx == param_idx ) { + model->atoms[j].y += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_Z ) idx++; + if ( idx == param_idx ) { + model->atoms[j].z += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_B ) idx++; + if ( idx == param_idx ) { + model->atoms[j].B += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_OCC ) idx++; + if ( idx == param_idx ) { + model->atoms[j].occ += LSQ_MSLS_SHIFT; + break; + } + + } + + f_calc_new = model_mod_f(model, f_calc->refs[obs_idx].h, f_calc->refs[obs_idx].k, f_calc->refs[obs_idx].l); + + if ( spec & REFINE_SPEC_INTENSITIES ) { + gradient = ((f_calc->refs[obs_idx].amplitude*f_calc->refs[obs_idx].amplitude) - (f_calc_new*f_calc_new))/LSQ_MSLS_SHIFT; + } else { + gradient = (f_calc->refs[obs_idx].amplitude - f_calc_new)/LSQ_MSLS_SHIFT; + } + model_free(model_new); + + return scale*gradient; + +} + +static gsl_matrix *refine_lsq_B(const gsl_vector *parameters, const ReflectionList *obs, RefinementSpec spec, AtomicModel *model, ReflectionList *f_calc) { + + gsl_matrix *B; + size_t j; + int n_params, n_obs; + + n_params = parameters->size; + n_obs = obs->n_reflections - 1; + + B = gsl_matrix_alloc(n_params, n_params); + for ( j=0; jsize; + n_obs = obs->n_reflections - 1; + scale = gsl_vector_get(parameters, 0); + + D = gsl_matrix_alloc(n_params, 1); + for ( j=0; jrefs[i].amplitude*obs->refs[i].amplitude) - scale*(f_calc->refs[i].amplitude*f_calc->refs[i].amplitude); + } else { + res = obs->refs[i].amplitude - scale*f_calc->refs[i].amplitude; + } + + total += grad*res; + + } + gsl_matrix_set(D, j, 0, total); + + } + + return D; + +} + +/* Determine and apply shift */ +static void refine_lsq_iterate(gsl_vector *parameters, ReflectionList *obs, RefinementSpec spec, AtomicModel *model_template) { + + gsl_matrix *B; + gsl_matrix *D; + gsl_matrix *Binv; + gsl_matrix *shift; + gsl_permutation *perm; + int s, n_params; + size_t i; + AtomicModel *model; + ReflectionList *f_calc; + + n_params = parameters->size; + model = refine_model_from_parameters(parameters, spec, model_template); + f_calc = model_calculate_f(obs, model, 69); + + /* Form 'B' */ + //printf("LSQ: Forming B\n"); + B = refine_lsq_B(parameters, obs, spec, model, f_calc); + + /* Form 'D' */ + //printf("LSQ: Forming D\n"); + D = refine_lsq_D(parameters, obs, spec, model, f_calc); + + /* Invert 'B' */ + //printf("LSQ: Inverting B\n"); + perm = gsl_permutation_alloc(B->size1); + Binv = gsl_matrix_alloc(B->size1, B->size2); + gsl_linalg_LU_decomp(B, perm, &s); + gsl_linalg_LU_invert(B, perm, Binv); + gsl_permutation_free(perm); + gsl_matrix_free(B); + + /* shift = B^-1 D */ + //printf("LSQ: Calculating shift\n"); + shift = gsl_matrix_alloc(n_params, 1); + gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, Binv, D, 0.0, shift); + gsl_matrix_free(Binv); + gsl_matrix_free(D); + + /* Apply shifts */ + //printf("LSQ: Applying shifts\n"); + for ( i=0; i %f\n", i, old, new); + } + + gsl_matrix_free(shift); + model_free(model); + //printf("LSQ: Done\n"); + + +} + +void refine_lsq(AtomicModel *model_orig, ReflectionList *reflections, RefinementSpec spec) { + + gsl_vector *parameters; + unsigned int n_obs, n_params, n_atoms, idx, j, iter; + double scale; + ReflectionList *calc; + AtomicModel *model; + + model = model_copy(model_orig); + + /* Count the number of parameters to refine */ + n_params = 0; + if ( spec & REFINE_SPEC_X ) n_params++; + if ( spec & REFINE_SPEC_Y ) n_params++; + if ( spec & REFINE_SPEC_Z ) n_params++; + if ( spec & REFINE_SPEC_B ) n_params++; + if ( spec & REFINE_SPEC_OCC ) n_params++; + + /* Count the number of atoms to refine (having first fixed the origin) */ + n_atoms = 0; + if ( (spec & REFINE_SPEC_X) || (spec & REFINE_SPEC_Y) || (spec & REFINE_SPEC_Z) ) model->atoms[0].refine = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) n_atoms++; + } + + n_obs = reflections->n_reflections-1; /* Number of observations */ + n_params = 1 + n_params * n_atoms; /* Number of parameters */ + printf("LSQ: Refining %i parameters against %i observations\n", n_params, n_obs); + + /* Set initial estimates of the parameters */ + parameters = gsl_vector_alloc(n_params); + idx = 0; + + /* Initial scale factor */ + calc = model_calculate_f(reflections, NULL, 69); + scale = stat_scale(reflections, calc); + reflist_free(calc); + gsl_vector_set(parameters, idx++, scale); + + /* Initial atomic coordinates and (iso-)B-factors */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) gsl_vector_set(parameters, idx++, model->atoms[j].x); + if ( spec & REFINE_SPEC_Y ) gsl_vector_set(parameters, idx++, model->atoms[j].y); + if ( spec & REFINE_SPEC_Z ) gsl_vector_set(parameters, idx++, model->atoms[j].z); + if ( spec & REFINE_SPEC_B ) gsl_vector_set(parameters, idx++, model->atoms[j].B); + if ( spec & REFINE_SPEC_OCC ) gsl_vector_set(parameters, idx++, model->atoms[j].occ); + } + } + + iter = 0; + do { + + /* Iterate */ + refine_lsq_iterate(parameters, reflections, spec, model); + printf("LSQ: after iteration %i:\tscale=%f\n", iter, gsl_vector_get(parameters, 0)); + iter++; + + /* Put the parameter vector back into the atomic model */ + model_free(model); + model = refine_model_from_parameters(parameters, spec, model); + model_move(model_orig, model); + + refine_schedule_update(model); + + } while ( (iter < MAX_REFINEMENT_ITERATIONS) && (model->refine_window->run_semaphore) ); + + printf("%i iterations performed\n", iter); + if ( iter == MAX_REFINEMENT_ITERATIONS ) printf("Reached maximum allowed number of iterations"); + + gsl_vector_free(parameters); + +} + diff --git a/src/refine-lsq.h b/src/refine-lsq.h new file mode 100644 index 0000000..ffb03b9 --- /dev/null +++ b/src/refine-lsq.h @@ -0,0 +1,26 @@ +/* + * refine-lsq.h + * + * Refinement by Simple LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_LSQ_H +#define REFINE_LSQ_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_lsq(AtomicModel *model_orig, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_LSQ_H */ + diff --git a/src/refine-rns.c b/src/refine-rns.c new file mode 100644 index 0000000..3bfcd21 --- /dev/null +++ b/src/refine-rns.c @@ -0,0 +1,122 @@ +/* + * refine-rns.c + * + * Refinement by Random Neighbour Search + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +void refine_neighboursearch(AtomicModel *model, ReflectionList *reflections, + RefinementSpec spec, double shift) +{ + unsigned int iterations_without_change; + double min_r; + ReflectionList *model_reflections; + + model_reflections = model_calculate_f(reflections, model, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + /* stat_r2(Fobs, Fcalc) */ + min_r = stat_r2(reflections, model_reflections); + } else { + /* stat_r(Fobs, Fcalc) */ + min_r = stat_r(reflections, model_reflections); + } + + iterations_without_change = 0; + while ( (model->refine_window->run_semaphore) + && (iterations_without_change < MAX_REFINEMENT_ITERATIONS) ) { + + unsigned int i; + double x, y, z; + double new_r; + AtomicModel *copy; + + /* Copy the structure */ + copy = model_copy(model); + + /* 'Jiggle' the atoms */ + for ( i=1; in_atoms; i++ ) { + + if ( model->atoms[i].refine ) { + + x = model->atoms[i].x; + y = model->atoms[i].y; + z = model->atoms[i].z; + + if ( spec & REFINE_SPEC_X ) { + if ( random() > (0.9*RAND_MAX) ) + x += shift; + if ( random() > (0.9*RAND_MAX) ) + x -= shift; + if ( x<0 ) x = 1+x; + if ( x>1 ) x = x-1; + model->atoms[i].x = x; + } + + if ( spec & REFINE_SPEC_Y ) { + if ( random() > (0.9*RAND_MAX) ) + y += shift; + if ( random() > (0.9*RAND_MAX) ) + y -= shift; + if ( y<0 ) y = 1+y; + if ( y>1 ) y = y-1; + model->atoms[i].y = y; + } + + if ( spec & REFINE_SPEC_Z ) { + if ( random() > (0.9*RAND_MAX) ) + z += shift; + if ( random() > (0.9*RAND_MAX) ) + z -= shift; + if ( z<0 ) z = 1+z; + if ( z>1 ) z = z-1; + model->atoms[i].z = z; + } + + } + + } + + /* Recalculate structure factors and check for improvement */ + model_reflections = model_calculate_f(reflections, model, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + /* stat_r(Fobs, Fcalc) */ + new_r = stat_r2(reflections, model_reflections); + } else { + /* stat_r(Fobs, Fcalc) */ + new_r = stat_r(reflections, model_reflections); + } + if ( new_r < min_r ) { + + min_r = new_r; + refine_schedule_update(model); + iterations_without_change = 0; + + model_free(copy); + + } else { + + model_move(model, copy); + model_free(copy); + iterations_without_change++; + + } + free(model_reflections); + + } +} diff --git a/src/refine-rns.h b/src/refine-rns.h new file mode 100644 index 0000000..fc06c76 --- /dev/null +++ b/src/refine-rns.h @@ -0,0 +1,26 @@ +/* + * refine-rns.h + * + * Refinement by Random Neighbour Search + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_RNS_H +#define REFINE_RNS_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_neighboursearch(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec, double shift); + +#endif /* REFINE_RNS_H */ + diff --git a/src/refine.c b/src/refine.c new file mode 100644 index 0000000..314cdc1 --- /dev/null +++ b/src/refine.c @@ -0,0 +1,359 @@ +/* + * refine.c + * + * Model Refintement + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" +#include "displaywindow.h" +#include "main.h" +#include "refine-rns.h" +#include "refine-lmder.h" +#include "refine-lsq.h" +#include "refine-cgrad.h" +#include "refine-brent.h" + +static gboolean refine_update_model(gpointer data) { + + AtomicModel *model = data; + + model_notify_update(model); + + model->refine_window->display_callback = 0; + g_static_mutex_unlock(&model->refine_window->display_mutex); + + return FALSE; + +} + +void refine_schedule_update(AtomicModel *model) { + g_static_mutex_lock(&model->refine_window->display_mutex); + model->refine_window->display_callback = g_idle_add(refine_update_model, model); +} + +static void *refine_work(void *data) { + + RefinementWindow *refinementwindow; + AtomicModel *model; + ReflectionList *reflections; + RefinementSpec spec; + + refinementwindow = data; + refinementwindow->running = 1; + + model = refinementwindow->model; + reflections = main_reflist(); + spec = refinementwindow->spec; + + switch ( refinementwindow->type ) { + case REFINE_TYPE_NONE : break; + case REFINE_TYPE_NEIGHBOURSEARCH : { + const char *str; + float shiftf; + str = gtk_entry_get_text(GTK_ENTRY(refinementwindow->nbsearch_shift)); + sscanf(str, "%f", &shiftf); + refine_neighboursearch(model, reflections, spec, shiftf); + break; + } + case REFINE_TYPE_BRENT : refine_brent(model, reflections, spec); break; + case REFINE_TYPE_LMDER : refine_lmder(model, reflections, spec); break; + case REFINE_TYPE_LSQ : refine_lsq(model, reflections, spec); break; + case REFINE_TYPE_CGRAD : refine_cgrad(model, reflections, spec); break; + } + + refinementwindow->running = 0; + + return NULL; + +} + +/* Refine the model described by model_atoms_list_store against reflections */ +static void refine_go(RefinementWindow *refinementwindow) { + + AtomicModel *model; + + model = model_get_current(); + + assert(refinementwindow->run_semaphore == 0); + model_lock(model); + refinementwindow->run_semaphore = 1; + g_static_mutex_init(&refinementwindow->display_mutex); + assert(refinementwindow->work_thread == NULL); + refinementwindow->display_callback = 0; + refinementwindow->work_thread = g_thread_create(refine_work, refinementwindow, TRUE, NULL); + +} + +static void refine_stop(RefinementWindow *refinementwindow) { + + assert(refinementwindow->run_semaphore == 1); + assert(refinementwindow->work_thread != NULL); + refinementwindow->run_semaphore = 0; + if ( refinementwindow->display_callback ) { + g_idle_remove_by_data(refinementwindow); + g_static_mutex_unlock(&refinementwindow->display_mutex); + } + g_thread_join(refinementwindow->work_thread); + refinementwindow->work_thread = NULL; + model_unlock(refinementwindow->model); + +} + +static gint refine_button_stop(GtkWidget *widget, RefinementWindow *refinementwindow) { + + gtk_widget_set_sensitive(refinementwindow->go, TRUE); + gtk_widget_set_sensitive(refinementwindow->stop, FALSE); + + refine_stop(refinementwindow); + + return 0; + +} + +static gint refine_button_go(GtkWidget *widget, RefinementWindow *refinementwindow) { + + RefinementSpec spec; + RefinementType type; + + spec = REFINE_SPEC_NONE; + type = REFINE_TYPE_NONE; + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_x)) ) spec = spec | REFINE_SPEC_X; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_y)) ) spec = spec | REFINE_SPEC_Y; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_z)) ) spec = spec | REFINE_SPEC_Z; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_b)) ) spec = spec | REFINE_SPEC_B; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_occ)) ) spec = spec | REFINE_SPEC_OCC; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_thickness)) ) spec = spec | REFINE_SPEC_THICKNESS; + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_neighboursearch)) ) type = REFINE_TYPE_NEIGHBOURSEARCH; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_brent)) ) type = REFINE_TYPE_BRENT; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_lmder)) ) type = REFINE_TYPE_LMDER; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_lsq)) ) type = REFINE_TYPE_LSQ; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_cgrad)) ) type = REFINE_TYPE_CGRAD; + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->target_intensities)) ) spec = spec | REFINE_SPEC_INTENSITIES; + + refinementwindow->spec = spec; + refinementwindow->type = type; + + gtk_widget_set_sensitive(refinementwindow->go, FALSE); + gtk_widget_set_sensitive(refinementwindow->stop, TRUE); + + refine_go(refinementwindow); + + return 0; + +} + +static gint refine_response(GtkWidget *refine_window, gint response, RefinementWindow *refinementwindow) { + + if ( refinementwindow->running ) refine_stop(refinementwindow); + + /* Avoid double-freeing if the refinement window already got closed */ + if ( refinementwindow->model->refine_window ) { + gtk_widget_destroy(refine_window); + refinementwindow->model->refine_window = NULL; + free(refinementwindow); + } + + return 0; + +} + +gint refine_type_neighboursearch(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_brent(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_cgrad(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_lmder(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_lsq(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +void refine_open(AtomicModel *model) { + + GtkWidget *refine_window; + GtkWidget *table; + GtkWidget *label; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *buttons; + + if ( model->refine_window ) return; + model->refine_window = malloc(sizeof(RefinementWindow)); + model->refine_window->model = model; + model->refine_window->run_semaphore = 0; + model->refine_window->running = 0; + model->refine_window->work_thread = NULL; + model->refine_window->display_callback = 0; + + refine_window = gtk_dialog_new_with_buttons("Refine Model", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + g_signal_connect(G_OBJECT(refine_window), "response", G_CALLBACK(refine_response), model->refine_window); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(refine_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(label), "Refinement Algorithm"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 5); + + table = gtk_table_new(5, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0); + + model->refine_window->type_neighboursearch = gtk_radio_button_new_with_label(NULL, "Random Neighbour Search"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_neighboursearch), 1, 3, 2, 3); + g_signal_connect(G_OBJECT(model->refine_window->type_neighboursearch), "toggled", G_CALLBACK(refine_type_neighboursearch), model->refine_window); + + label = gtk_label_new("Step Length:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(label), 1, 2, 3, 4); + + model->refine_window->nbsearch_shift = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(model->refine_window->nbsearch_shift), 10); + gtk_entry_set_width_chars(GTK_ENTRY(model->refine_window->nbsearch_shift), 8); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->nbsearch_shift), 2, 3, 3, 4); + gtk_entry_set_text(GTK_ENTRY(model->refine_window->nbsearch_shift), "0.001"); + + model->refine_window->type_brent = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "Sequential Brent Minimisation"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_brent), 1, 3, 4, 5); + g_signal_connect(G_OBJECT(model->refine_window->type_brent), "toggled", G_CALLBACK(refine_type_brent), model->refine_window); + + model->refine_window->type_lmder = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "GSL Levenberg-Marquardt"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_lmder), 1, 3, 5, 6); + g_signal_connect(G_OBJECT(model->refine_window->type_lmder), "toggled", G_CALLBACK(refine_type_lmder), model->refine_window); + + model->refine_window->type_lsq = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "Least Squares"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_lsq), 1, 3, 6, 7); + g_signal_connect(G_OBJECT(model->refine_window->type_lsq), "toggled", G_CALLBACK(refine_type_lsq), model->refine_window); + + model->refine_window->type_cgrad = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "Conjugate Gradient"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_cgrad), 1, 3, 7, 8); + g_signal_connect(G_OBJECT(model->refine_window->type_cgrad), "toggled", G_CALLBACK(refine_type_cgrad), model->refine_window); + + label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(label), "Parameters to Refine"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 10); + + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + model->refine_window->spec_x = gtk_check_button_new_with_label("x"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_x), FALSE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->spec_x), TRUE); + model->refine_window->spec_y = gtk_check_button_new_with_label("y"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_y), FALSE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->spec_y), TRUE); + model->refine_window->spec_z = gtk_check_button_new_with_label("z"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_z), FALSE, TRUE, 0); + + model->refine_window->spec_b = gtk_check_button_new_with_label("Debye-Waller parameters"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(model->refine_window->spec_b), FALSE, FALSE, 5); + + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + model->refine_window->spec_occ = gtk_check_button_new_with_label("Occupancies"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_occ), FALSE, FALSE, 0); + model->refine_window->spec_thickness = gtk_check_button_new_with_label("Thickness"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_thickness), FALSE, FALSE, 0); + + label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(label), "Refine Against"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 10); + + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + + model->refine_window->target_amplitudes = gtk_radio_button_new_with_label(NULL, "Amplitudes"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->target_amplitudes), FALSE, TRUE, 3); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->target_amplitudes), TRUE); + model->refine_window->target_intensities = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->target_amplitudes), + "Intensities"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->target_intensities), FALSE, TRUE, 3); + + /* Control buttons */ + buttons = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(buttons), FALSE, FALSE, 5); + /* Stop */ + model->refine_window->stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(model->refine_window->stop), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(model->refine_window->stop), "clicked", G_CALLBACK(refine_button_stop), model->refine_window); + /* Go */ + model->refine_window->go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(model->refine_window->go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(model->refine_window->go), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(model->refine_window->go), "clicked", G_CALLBACK(refine_button_go), model->refine_window); + + gtk_widget_set_sensitive(model->refine_window->go, TRUE); + gtk_widget_set_sensitive(model->refine_window->stop, FALSE); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->type_lmder), TRUE); + gtk_widget_show_all(refine_window); + +} + diff --git a/src/refine.h b/src/refine.h new file mode 100644 index 0000000..2646b23 --- /dev/null +++ b/src/refine.h @@ -0,0 +1,102 @@ +/* + * refine.h + * + * Model Refintement + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_H +#define REFINE_H + +#include + +typedef enum { + COORDINATE_X, + COORDINATE_Y, + COORDINATE_Z +} CoordinateSelector; + +typedef enum { + REFINE_SPEC_NONE = 0, + REFINE_SPEC_X = 1<<0, + REFINE_SPEC_Y = 1<<1, + REFINE_SPEC_Z = 1<<2, + REFINE_SPEC_B = 1<<3, + REFINE_SPEC_OCC = 1<<4, + REFINE_SPEC_THICKNESS = 1<<16, + REFINE_SPEC_INTENSITIES = 1<<18 +} RefinementSpec; + +typedef enum { + REFINE_TYPE_NONE = 0, + REFINE_TYPE_NEIGHBOURSEARCH, + REFINE_TYPE_BRENT, + REFINE_TYPE_LMDER, + REFINE_TYPE_LSQ, + REFINE_TYPE_CGRAD +} RefinementType; + +typedef struct { + + /* Model this refinement refers to */ + struct struct_atomicmodel *model; + + /* Refinement specifications */ + RefinementSpec spec; + RefinementType type; + + /* Dialog box bits */ + GtkWidget *spec_x; + GtkWidget *spec_y; + GtkWidget *spec_z; + GtkWidget *spec_b; + GtkWidget *spec_thickness; + GtkWidget *spec_occ; + GtkWidget *type_neighboursearch; + GtkWidget *type_brent; + GtkWidget *type_lmder; + GtkWidget *type_lsq; + GtkWidget *type_cgrad; + GtkWidget *nbsearch_shift; + GtkWidget *target_amplitudes; + GtkWidget *target_intensities; + GtkWidget *stop; + GtkWidget *go; + + /* Thread control */ + GThread *work_thread; + unsigned int running; + unsigned int run_semaphore; + GStaticMutex display_mutex; + guint display_callback; + +} RefinementWindow; + +#include "reflist.h" + +typedef struct { + struct struct_atomicmodel *model; + ReflectionList *reflections; /* Observations */ + unsigned int cur_mod_atom; + CoordinateSelector cur_mod_coor; + double scale; + RefinementSpec spec; +} RefinementPair; + +extern void refine_open(struct struct_atomicmodel *model); + +#define LSQ_MSLS_SHIFT 0.000000001 +#define MAX_REFINEMENT_ITERATIONS 400 + +extern void refine_schedule_update(struct struct_atomicmodel *model); + +#endif /* REFINE_H */ + diff --git a/src/reflist.c b/src/reflist.c new file mode 100644 index 0000000..e0224e9 --- /dev/null +++ b/src/reflist.c @@ -0,0 +1,287 @@ +/* + * reflist.c + * + * Reflection-handling code + * + * (c) 2006-2008 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#include +#include +#include +#include +#include + +#include "reflist.h" + +ReflectionList *reflist_new() { + + ReflectionList *new = malloc(sizeof(ReflectionList)); + assert(new != NULL); + + new->n_reflections = 0; + + /* DC component makes reflist_inlist() work */ + reflist_addref(new, 0, 0, 0); + new->n_reflections = 1; + + return new; + +} + +ReflectionList *reflist_new_parent(ReflectionList *parent) { + + ReflectionList *new = reflist_new(); + + new->parent = parent; + + return new; + +} + +ReflectionList *reflist_copy(ReflectionList *list) { + ReflectionList *new = malloc(sizeof(ReflectionList)); + memcpy(new, list, sizeof(ReflectionList)); + return new; +} + +void reflist_free(ReflectionList *list) { + free(list); +} + +/* Add a reflection to a list, including all associated data */ +static int reflist_addref_all(ReflectionList *list, signed int h, signed int k, signed int l, + double am, double phase, double phase_set, double alpha, double delta_theta, signed int multiplier, + unsigned int parent_index) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (addref)\n", + list->n_reflections); + return -1; + } + + //if ( (i = reflist_inlist(list, h, k, l)) ) { + //printf("RL: Duplicated reflection %i %i %i\n", h, k, l); + //} else { + i = list->n_reflections; + //} + if ( (h==0) && (k==0) && (l==0) ) i = 0; + + list->refs[i].h = h; + list->refs[i].k = k; + list->refs[i].l = l; + list->refs[i].amplitude = am; + list->refs[i].am_error = 1; + list->refs[i].weight = 1; + list->refs[i].phase_known = phase; + list->refs[i].phase_known_set = phase_set; + list->refs[i].alpha = alpha; + list->refs[i].delta_theta = delta_theta; + list->refs[i].multiplier = multiplier; + list->refs[i].parent_index = parent_index; + + if ( !((h==0) && (k==0) && (l==0)) ) list->n_reflections++; + + return i; + +} + +/* Add a reflection to a list */ +int reflist_addref(ReflectionList *list, signed int h, signed int k, signed int l) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, 0, 0, 1, 0); +} + +/* Add a reflection to a list */ +int reflist_addref_am(ReflectionList *list, signed int h, signed int k, signed int l, double am) { + return reflist_addref_all(list, h, k, l, am, 0, 0, 0, 0, 1, 0); +} + +int reflist_addref_am_ph(ReflectionList *list, signed int h, signed int k, signed int l, double am, double ph) { + return reflist_addref_all(list, h, k, l, am, ph, 1, 0, 0, 1, 0); +} + +/* Add a reflection to a list, including alpha value */ +int reflist_addref_alpha_parent(ReflectionList *list, signed int h, signed int k, signed int l, double alpha, unsigned int parent_index) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, alpha, 0, 1, parent_index); +} + +/* Add a reflection to a list, including phase value */ +int reflist_addref_phase(ReflectionList *list, signed int h, signed int k, signed int l, double phase) { + return reflist_addref_all(list, h, k, l, 0, phase, 1, 0, 0, 1, 0); +} + +/* Add a reflection to a list, including "delta-phase" value */ +int reflist_addref_deltatheta(ReflectionList *list, signed int h, signed int k, signed int l, double delta_theta, signed int multiplier) { + if ( !reflist_inlist(list, h, k, l) ) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, 0, delta_theta, multiplier, 0); + } else { + //printf("RL: Not re-adding reflection %3i %3i %3i\n", h, k, l); + return -1; + } +} + +int reflist_addref_parent(ReflectionList *list, signed int h, signed int k, signed int l, unsigned int parent_index) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, 0, 0, 1, parent_index); +} + +int reflist_addref_am_parent(ReflectionList *list, signed int h, signed int k, signed int l, double am, unsigned int parent_index) { + return reflist_addref_all(list, h, k, l, am, 0, 0, 0, 0, 1, parent_index); +} + +/* Delete a reflection from a list */ +void reflist_delref(ReflectionList *list, signed int h, signed int k, signed int l) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (delref)\n", + list->n_reflections); + return; + } + + if ( list->n_reflections == 0 ) { return; } + + for ( i=1; in_reflections; i++ ) { + if ( (list->refs[i].h == h) && (list->refs[i].k == k) ) { + + if ( i < list->n_reflections ) { + unsigned int j; + /* Shove everything up one place */ + for ( j=i+1; jn_reflections; j++ ) { + list->refs[j-1].h = list->refs[j].h; + list->refs[j-1].k = list->refs[j].k; + list->refs[j-1].l = list->refs[j].l; + list->refs[j-1].amplitude = list->refs[j].amplitude; + list->refs[j-1].phase_known = list->refs[j].phase_known; + list->refs[j-1].phase_known_set = list->refs[j].phase_known_set; + list->refs[j-1].phase_calc = list->refs[j].phase_calc; + list->refs[j-1].phase_calc_set = list->refs[j].phase_calc_set; + list->refs[j-1].alpha = list->refs[j].alpha; + list->refs[j-1].delta_theta = list->refs[j].delta_theta; + list->refs[j-1].multiplier = list->refs[j].multiplier; + list->refs[j-1].parent_index = list->refs[j].parent_index; + } + } /* else nothing to do */ + + list->n_reflections--; + + } + } + +} + +/* See if a reflection is in a list */ +int reflist_inlist(const ReflectionList *list, signed int h, signed int k, signed int l) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (refinlist)\n", + list->n_reflections); + return 0; + } + + if ( list->n_reflections == 0 ) { return 0; } + + for ( i=0; in_reflections; i++ ) { + if ( (list->refs[i].h == h) && (list->refs[i].k == k) && (list->refs[i].l == l) ) return i; + } + + return 0; + +} + +/* As above, but ignoring "l" */ +int reflist_inlist_2d(const ReflectionList *list, signed int h, signed int k) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (refinlist)\n", + list->n_reflections); + return 0; + } + + if ( list->n_reflections == 0 ) { return 0; } + + for ( i=0; in_reflections; i++ ) { + if ( (list->refs[i].h == h) && (list->refs[i].k == k) ) return i; + } + + return 0; + +} + +void reflist_set_components(ReflectionList *list, signed int h, signed int k, signed int l, double re, double im) { + + unsigned int idx = reflist_inlist(list, h, k, l); + if ( !idx ) return; + list->refs[idx].re = re; + list->refs[idx].im = im; + +} + +ReflectionList *reflist_new_from_array(fftw_complex *array, int width, int height) { + + ReflectionList *reflections; + signed int h, k; + + reflections = reflist_new(); + + for ( h=-(width-1)/2; h<=(width-1)/2; h++ ) { + for ( k=-(height-1)/2; k<=(height-1)/2; k++ ) { + + double re, im, am, ph; + signed int hc, kc; + + hc = h; kc = k; + if ( h < 0 ) { hc = width+hc; } + if ( k < 0 ) { kc = height+kc; } + re = array[kc+height*hc][0]; im = array[kc+height*hc][1]; + am = sqrt(re*re + im*im); ph = atan2(im, re); + + if ( am > 0 ) { + reflist_addref_am_ph(reflections, h, k, 0, am, ph); + //printf("Got %3i %3i am=%f ph=%f\n", h, k, am, ph); + } + + } + } + + return reflections; + +} + +void reflist_fill_array(fftw_complex *array, const ReflectionList *reflections, int width, int height) { + + signed int h, k; + unsigned int i; + + bzero(array, width*height*sizeof(fftw_complex)); + + for ( i=0; in_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + if ( reflections->refs[i].phase_known_set ) { + array[k + height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + array[k + height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + } + + } + +} diff --git a/src/reflist.h b/src/reflist.h new file mode 100644 index 0000000..87dbbea --- /dev/null +++ b/src/reflist.h @@ -0,0 +1,83 @@ +/* + * reflist.h + * + * Reflection-handling code + * + * (c) 2006-2008 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifndef REFLIST_H +#define REFLIST_H + +#include + +#define MAX_REFLECTIONS 20000 + +typedef struct { + + signed int h; + signed int k; + signed int l; + + /* Some, all or none of the following may be filled in, depending on the context */ + + double amplitude; /* Structure amplitude F (not I=F^2). Must be positive */ + double am_error; /* Standard error in amplitude */ + double weight; /* Statistical weight for this data point */ + + double phase_known; /* Phase known a priori */ + unsigned int phase_known_set; + double phase_calc; /* Phase calculated */ + unsigned int phase_calc_set; + + double re; + double im; /* LSQ routine does crazy things with these */ + + double alpha; /* For convergence procedure and tangent refinement */ + double delta_theta; /* Some kind of phase relationship, */ + signed int multiplier; /* for symmetry stuff */ + + double amplitude_dyn; + double phase_dyn; /* As calculated by the multislice routine */ + + unsigned int parent_index; /* Position of the same reflection in a related list */ + +} Reflection; + +typedef struct _reflectionlist { + Reflection refs[MAX_REFLECTIONS]; + unsigned int n_reflections; + struct _reflectionlist *parent; + double scale; +} ReflectionList; + +typedef struct { + ReflectionList *a; + ReflectionList *b; +} ReflectionListPair; + +extern ReflectionList *reflist_new(void); +extern ReflectionList *reflist_new_parent(ReflectionList *parent); +extern ReflectionList *reflist_copy(ReflectionList *list); +extern void reflist_free(ReflectionList *list); + +extern int reflist_addref(ReflectionList *list, signed int h, signed int k, signed int l); +extern int reflist_addref_parent(ReflectionList *list, signed int h, signed int k, signed int l, unsigned int parent_index); +extern int reflist_addref_am(ReflectionList *list, signed int h, signed int k, signed int l, double am); +extern int reflist_addref_am_ph(ReflectionList *list, signed int h, signed int k, signed int l, double am, double ph); +extern int reflist_addref_alpha_parent(ReflectionList *list, signed int h, signed int k, signed int l, double alpha, unsigned int parent_index); +extern int reflist_addref_phase(ReflectionList *list, signed int h, signed int k, signed int l, double phase); +extern int reflist_addref_deltatheta(ReflectionList *list, signed int h, signed int k, signed int l, double delta_theta, signed int multiplier); +extern void reflist_delref(ReflectionList *list, signed int h, signed int k, signed int l); +extern int reflist_inlist(const ReflectionList *list, signed int h, signed int k, signed int l); +extern int reflist_inlist_2d(const ReflectionList *list, signed int h, signed int k); +extern void reflist_set_components(ReflectionList *list, signed int h, signed int k, signed int l, double re, double im); +extern int reflist_addref_am_parent(ReflectionList *list, signed int h, signed int k, signed int l, double am, unsigned int parent_index); + +extern ReflectionList *reflist_new_from_array(fftw_complex *array, int width, int height); +extern void reflist_fill_array(fftw_complex *array, const ReflectionList *reflections, int width, int height); + +#endif /* REFLIST_H */ diff --git a/src/renderer.c b/src/renderer.c new file mode 100644 index 0000000..9384e30 --- /dev/null +++ b/src/renderer.c @@ -0,0 +1,205 @@ +/* + * renderer.c + * + * Render Fourier Transform output array into display array + * + * (c) 2006-2007 Thomas White + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "renderer.h" + +double renderer_width(int width, int height, double gamma, int nx, int ny) { + return nx*width + ny*height*fabs(cos(gamma)); +} + +double renderer_height(int width, int height, double gamma, int nx, int ny) { + return height*sin(gamma)*ny; +} + +/* Provide the coordinate in the image (ie xn,yn) given x,y in the Fourier array */ +double renderer_map_x(double x, double y, int width, int height, double gamma, int nx, int ny) { + + double xn, offs; + + if ( gamma > M_PI_2 ) { + offs = (double)ny*height*cos(M_PI-gamma); + } else { + offs = 0.0; + } + xn = x + offs + y*cos(gamma); + + return xn; + +} + +/* Same, but return y */ +double renderer_map_y(double x, double y, int width, int height, double gamma, int nx, int ny) { + return y*sin(gamma); +} + +static double renderer_map_reverse_x(double xn, double yn, int width, int height, double gamma, int nx, int ny) { + + double offs, xd, yd; + + if ( gamma > M_PI_2 ) { + offs = (double)ny*height*cos(M_PI-gamma); + } else { + offs = 0.0; + } + + yd = (double)yn / sin(gamma); + xd = (double)xn - offs - yd*cos(gamma); + + while ( xd < 0 ) xd += width; + xd = fmod(xd, width); + + return xd; + +} + +static double renderer_map_reverse_y(double xn, double yn, int width, int height, double gamma, int nx, int ny) { + + double yd; + + yd = (double)yn / sin(gamma); + while ( yd < 0 ) yd += height; + yd = fmod(yd, height); + + return yd; + +} + +static double renderer_interpolate_linear_re(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, re, re1, re2; + int x, y; + + /* Get the left-hand point value */ + x = floor(xd); y = floor(yd); + if ( x >= width ) x -= width; + if ( y >= height ) y -= height; + re1 = out[(height-1-y)+height*(width-1-x)][0]; + + /* Get the right-hand point value */ + x++; + if ( x >= width ) x -= width; + re2 = out[(height-1-y)+height*(width-1-x)][0]; + + frac = fmod(xd, 1); + re = (1-frac)*re1 + frac*re2; + + return re; + +} + +static double renderer_interpolate_linear_im(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, im, im1, im2; + int x, y; + + /* Get the left-hand point value */ + x = floor(xd); y = floor(yd); + if ( x >= width ) x -= width; + if ( y >= height ) y -= height; + im1 = out[(height-1-y)+height*(width-1-x)][1]; + + /* Get the right-hand point value */ + x++; + if ( x >= width ) x -= width; + im2 = out[(height-1-y)+height*(width-1-x)][1]; + + frac = fmod(xd, 1); + im = (1-frac)*im1 + frac*im2; + + return im; + +} + +static double renderer_interpolate_bilinear_re(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, re, re1, re2; + + /* Get the lower interpolated value */ + re1 = renderer_interpolate_linear_re(xd, yd, out, width, height); + + /* Get the upper interpolated value */ + yd++; + if ( yd >= height ) yd -= height; + re2 = renderer_interpolate_linear_re(xd, yd, out, width, height); + + frac = fmod(yd, 1); + re = (1-frac)*re1 + frac*re2; + + return re; + +} + +static double renderer_interpolate_bilinear_im(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, im, im1, im2; + + /* Get the lower interpolated value */ + im1 = renderer_interpolate_linear_im(xd, yd, out, width, height); + + /* Get the upper interpolated value */ + yd++; + if ( yd >= height ) yd -= height; + im2 = renderer_interpolate_linear_im(xd, yd, out, width, height); + + frac = fmod(yd, 1); + im = (1-frac)*im1 + frac*im2; + + return im; + +} + +ComplexArray renderer_draw(fftw_complex *out, int width, int height, double gamma, int nx, int ny) { + + ComplexArray cxar; + int width_n, height_n; + int xn, yn; + + width_n = (int)renderer_width(width, height, gamma, nx, ny); + height_n = (int)renderer_height(width, height, gamma, nx, ny); + + cxar.re = malloc(height_n*width_n*sizeof(double)); + cxar.im = malloc(height_n*width_n*sizeof(double)); + + for ( yn=0; yn + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef RENDERER_H +#define RENDERER_H + +typedef struct { + double *re; + double *im; +} ComplexArray; + +extern double renderer_width(int width, int height, double gamma, int nx, int ny); +extern double renderer_height(int width, int height, double gamma, int nx, int ny); +extern ComplexArray renderer_draw(fftw_complex *out, int width, int height, double gamma, int nx, int ny); +extern double renderer_map_x(double x, double y, int width, int height, double gamma, int nx, int ny); +extern double renderer_map_y(double x, double y, int width, int height, double gamma, int nx, int ny); + +#endif /* RENDERER_H */ + diff --git a/src/statistics.c b/src/statistics.c new file mode 100644 index 0000000..5fa4634 --- /dev/null +++ b/src/statistics.c @@ -0,0 +1,223 @@ +/* + * statistics.c + * + * Structure Factor Statistics + * + * (c) 2006 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "reflist.h" +#include "statistics.h" + +/* Return the least-squares estimate of the optimum scaling factor for intensities */ +double stat_scale_intensity(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double top = 0; + double bot = 0; + + if ( obs->n_reflections == 0 ) return 0; + if ( calc->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + top += (obs->refs[i].amplitude*obs->refs[i].amplitude) * (calc->refs[i].amplitude*calc->refs[i].amplitude); + bot += (calc->refs[i].amplitude*calc->refs[i].amplitude) * (calc->refs[i].amplitude*calc->refs[i].amplitude); + + } + + return top/bot; + +} + +/* Return the least-squares estimate of the optimum scaling factor */ +double stat_scale(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double top = 0; + double bot = 0; + + if ( obs->n_reflections == 0 ) return 0; + if ( calc->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + top += obs->refs[i].amplitude * calc->refs[i].amplitude; + bot += calc->refs[i].amplitude * calc->refs[i].amplitude; + + } + + return top/bot; + +} + +/* R-factor in terms of diffracted intensities */ +double stat_r2(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double scale; + double err; + double den; + + scale = stat_scale_intensity(obs, calc); + err = 0; den = 0; + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + err += fabs( (obs->refs[i].amplitude*obs->refs[i].amplitude) - (scale * (calc->refs[i].amplitude*calc->refs[i].amplitude)) ); + den += obs->refs[i].amplitude * obs->refs[i].amplitude; + + } + + return err/den; + +} + +/* R-factor in terms of amplitudes of structure factors */ +double stat_r1(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double scale; + double err; + double den; + + scale = stat_scale(obs, calc); + err = 0; den = 0; + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + err += fabs( obs->refs[i].amplitude - (scale * calc->refs[i].amplitude) ); + den += obs->refs[i].amplitude; + + } + + return err/den; + +#if 0 + double scale; + gsl_function F; + gsl_min_fminimizer *s; + int status; + int iter = 0, max_iter = 100; + + printf("Estimate: Scale = %f, R=%.2f%%\n", opt.scale, opt.r*100); + + opt.r = 999999999; + opt.scale = 0; + + F.function = &stat_calc_r; + F.params = &pair; + + s = gsl_min_fminimizer_alloc(gsl_min_fminimizer_brent); + gsl_min_fminimizer_set(s, &F, 10, 1, 1000); + + do { + + double lo, up; + + /* Iterate */ + gsl_min_fminimizer_iterate(s); + iter++; + + /* Get the current estimate */ + scale = gsl_min_fminimizer_x_minimum(s); + lo = gsl_min_fminimizer_x_lower(s); + up = gsl_min_fminimizer_x_upper(s); + + /* Check for convergence */ + status = gsl_min_test_interval(lo, up, 0.001, 0.0); + if (status == GSL_SUCCESS) { + + OptimumR opt; + + opt.r = stat_calc_r(scale, &pair); + opt.scale = scale; + + printf("Minimum: Scale=%f, R=%.2f%%\n", opt.scale, opt.r*100); + + gsl_min_fminimizer_free(s); + + return opt; + + } + + } while (status == GSL_CONTINUE && iter < max_iter); + + return opt; +#endif + +} + +double stat_sigma_f(ReflectionList *reflections) { + + unsigned int i; + double sigma_f = 0; + + if ( reflections->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + assert(reflections->refs[i].amplitude >= 0); + sigma_f += reflections->refs[i].amplitude; + } + + return sigma_f; + +} + +double stat_stddev(ReflectionList *a) { + + unsigned int i; + double sigma_f, mean; + double sigma_dev = 0; + + if ( a->n_reflections == 0 ) return 0; /* No reflections */ + + sigma_f = stat_sigma_f(a); + mean = sigma_f / a->n_reflections; + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + sigma_dev += (a->refs[i].amplitude - mean) * (a->refs[i].amplitude - mean); + } + + return sqrt(sigma_dev/a->n_reflections); + +} + +double stat_maxam(ReflectionList *a) { + + unsigned int i; + double max_am = 0; + + if ( a->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { + if ( a->refs[i].amplitude > max_am ) max_am = a->refs[i].amplitude; + } + + return max_am; + +} diff --git a/src/statistics.h b/src/statistics.h new file mode 100644 index 0000000..be49098 --- /dev/null +++ b/src/statistics.h @@ -0,0 +1,31 @@ +/* + * statistics.h + * + * Structure Factor Statistics + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef STATISTICS_H +#define STATISTICS_H + +#include "reflist.h" + +extern double stat_scale(ReflectionList *obs, ReflectionList *calc); +extern double stat_scale_intensity(ReflectionList *obs, ReflectionList *calc); + +extern double stat_r1(ReflectionList *a, ReflectionList *b); +extern double stat_r2(ReflectionList *a, ReflectionList *b); +#define stat_r(a, b) stat_r1((a), (b)) + +extern double stat_sigma_f(ReflectionList *reflections); +extern double stat_stddev(ReflectionList *a); +extern double stat_maxam(ReflectionList *a); + +#endif /* STATISTICS_H */ diff --git a/src/superlattice.c b/src/superlattice.c new file mode 100644 index 0000000..b07005a --- /dev/null +++ b/src/superlattice.c @@ -0,0 +1,142 @@ +/* + * superlattice.c + * + * Superlattice Operations + * + * (c) 2006-2007 Thomas White + * + * synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "main.h" +#include "reflist.h" +#include "data.h" + +typedef struct { + + GtkWidget *window; + GtkWidget *xcells; + GtkWidget *ycells; + +} SuperlatticeSplitWindow; + +SuperlatticeSplitWindow *superlattice_sls = NULL; + +ReflectionList *superlattice_split(ReflectionList *old, unsigned int xcells, unsigned int ycells) { + + ReflectionList *new; + unsigned int i; + + printf("SL: Splitting %ix%i superlattice cell\n", xcells, ycells); + + new = reflist_new_parent(old); + for ( i=1; in_reflections; i++ ) { + + signed int h, k, l; + + h = old->refs[i].h; k = old->refs[i].k; l = old->refs[i].l; + if ( (h % (signed)xcells == 0) && (k % (signed)ycells == 0) ) { + reflist_addref_am_parent(new, h/(signed)xcells, k/(signed)ycells, l, old->refs[i].amplitude, i); + } + + } + data_dividecell(xcells, ycells, 1); + displaywindow_createfourier(); + displaywindow_kicksize(); + + return new; + +} + +static gint superlattice_split_response(GtkWidget *widget, gint response, SuperlatticeSplitWindow *sls) { + + int done = 1; + + if ( response == GTK_RESPONSE_OK ) { + const char *xcells; + const char *ycells; + unsigned int xc, yc; + int scanval; + xcells = gtk_entry_get_text(GTK_ENTRY(sls->xcells)); + ycells = gtk_entry_get_text(GTK_ENTRY(sls->ycells)); + scanval = sscanf(xcells, "%u", &xc); + scanval += sscanf(ycells, "%u", &yc); + if ( scanval != 2 ) { + error_report("Please enter valid values for both superlattice cell dimensions."); + done = 0; + } else { + main_superlattice_split(xc, yc); + } + } + + if ( done ) { + gtk_widget_destroy(sls->window); + free(sls); + superlattice_sls = NULL; + } + + return 0; +} + +void superlattice_split_open() { + + SuperlatticeSplitWindow *sls; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *xcells_label; + GtkWidget *ycells_label; + + if ( superlattice_sls ) { + return; + } + sls = malloc(sizeof(SuperlatticeSplitWindow)); + superlattice_sls = sls; + + sls->window = gtk_dialog_new_with_buttons("Superlattice Split", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(sls->window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + table = gtk_table_new(2, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + xcells_label = gtk_label_new("Number of cells along x:"); + gtk_misc_set_alignment(GTK_MISC(xcells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(xcells_label), 1, 2, 1, 2); + + sls->xcells = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(superlattice_sls->xcells), "1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(sls->xcells), 2, 3, 1, 2); + + ycells_label = gtk_label_new("Number of cells along y:"); + gtk_misc_set_alignment(GTK_MISC(ycells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ycells_label), 1, 2, 2, 3); + + sls->ycells = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(sls->ycells), "1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(sls->ycells), 2, 3, 2, 3); + + g_signal_connect(G_OBJECT(sls->window), "response", G_CALLBACK(superlattice_split_response), sls); + gtk_widget_show_all(sls->window); + gtk_widget_grab_focus(GTK_WIDGET(sls->xcells)); + +} + diff --git a/src/superlattice.h b/src/superlattice.h new file mode 100644 index 0000000..4622b6e --- /dev/null +++ b/src/superlattice.h @@ -0,0 +1,23 @@ +/* + * superlattice.h + * + * Superlattice Operations + * + * (c) 2006-2007 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef SUPERLATTICE_H +#define SUPERLATTICE_H + +#include "reflist.h" + +extern ReflectionList *superlattice_split(ReflectionList *reflections, unsigned int xcells, unsigned int ycells); +extern void superlattice_split_open(void); + +#endif /* SUPERLATTICE_H */ diff --git a/src/symmetry.c b/src/symmetry.c new file mode 100644 index 0000000..65aef98 --- /dev/null +++ b/src/symmetry.c @@ -0,0 +1,464 @@ +/* + * symmetry.c + * + * Symmetry stuff + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "symmetry.h" +#include "reflist.h" +#include "model.h" + +#define is_odd(a) ((a)%2==1) + +/* Return a list of atoms containing the given atom and all its symmetry equivalents */ +AtomicModel *symmetry_generate_equivalent_atoms(AtomicModel *model, size_t j, ModelTarget target) { + + AtomicModel *list; + + list = model_new(); + list->atoms[list->n_atoms] = model->atoms[j]; list->n_atoms++; + + if ( target == MODEL_TARGET_DISPLAY ) { + + if ( model->atoms[j].x == 0 ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1.0; + list->n_atoms++; + } + + if ( model->atoms[j].y == 0 ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].y = 1.0; + list->n_atoms++; + } + + if ( (model->atoms[j].x == 0) && (model->atoms[j].y == 0) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1.0; + list->atoms[list->n_atoms].y = 1.0; + list->n_atoms++; + } + + } + + if ( model->sym & SYMMETRY_CENTRE ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1-list->atoms[list->n_atoms].x; + list->atoms[list->n_atoms].y = 1-list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_HORIZONTAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].y = 1-list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_VERTICAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1-list->atoms[list->n_atoms].x; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_HORIZONTAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(0.5 + list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = 1-list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_VERTICAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1-list->atoms[list->n_atoms].x; + list->atoms[list->n_atoms].y = fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(0.5 + list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = fmod(1.5 - list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_VERTICAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + /* Fix cm */ + if ( (model->sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER) && (model->sym & SYMMETRY_MIRROR_HORIZONTAL) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(0.5 + list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = 1 - fmod(1.5 - list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } else if ( (model->sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (model->sym & SYMMETRY_MIRROR_VERTICAL) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1 - fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + /* Fix c2mm */ + if ( (model->sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (model->sym & SYMMETRY_MIRROR_HORIZONTAL) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = 1 - fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_HORIZONTAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = list->atoms[list->n_atoms].x; + list->atoms[list->n_atoms].y = fmod(1.5 - list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_VERTICAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + return list; + +} + +ReflectionList *symmetry_generate_equivalent_reflections(Symmetry sym, signed int h, signed int k, signed int l) { + + ReflectionList *result; + + result = reflist_new(); + + /* Remove the minus in front of 'l' to make even HOLZ conditional transforms real. + * This is generally the wrong thing to do. */ + if ( sym & SYMMETRY_FRIEDEL ) reflist_addref_deltatheta(result, -h, -k, -l, 0, -1); + + if ( sym & SYMMETRY_MIRROR_HORIZONTAL ) reflist_addref_deltatheta(result, h, -k, l, 0, 1); + if ( sym & SYMMETRY_MIRROR_VERTICAL ) reflist_addref_deltatheta(result, -h, k, l, 0, 1); + if ( sym & SYMMETRY_MIRROR_DIAGONAL ) reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + + if ( sym & SYMMETRY_GLIDE_HORIZONTAL ) reflist_addref_deltatheta(result, h, -k, l, M_PI, 1); + if ( sym & SYMMETRY_GLIDE_VERTICAL ) reflist_addref_deltatheta(result, -h, k, l, M_PI, 1); + + if ( sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER ) { + if ( is_odd(abs(h)+abs(k)) ) { + reflist_addref_deltatheta(result, h, -k, l, M_PI, 1); + } else { + reflist_addref_deltatheta(result, h, -k, l, 0, 1); + } + } + + if ( sym & SYMMETRY_GLIDE_VERTICAL_QUARTER ) { + if ( is_odd(abs(h)+abs(k)) ) { + reflist_addref_deltatheta(result, -h, k, l, M_PI, 1); + } else { + reflist_addref_deltatheta(result, -h, k, l, 0, 1); + } + } + + if ( sym & SYMMETRY_ROTATION_4 ) { + if ( sym & SYMMETRY_GLIDE_DIAGONAL ) { + if ( is_odd(abs(h)+abs(k)) ) { + reflist_addref_deltatheta(result, h, -k, l, M_PI, 1); + reflist_addref_deltatheta(result, -h, k, l, M_PI, 1); + reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + reflist_addref_deltatheta(result, k, h, l, 0, 1); + reflist_addref_deltatheta(result, -k, h, l, M_PI, 1); + reflist_addref_deltatheta(result, k, -h, l, M_PI, 1); + reflist_addref_deltatheta(result, -k, -h, l, 0, 1); + } else { + reflist_addref_deltatheta(result, h, -k, l, 0, 1); + reflist_addref_deltatheta(result, -h, k, l, 0, 1); + reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + reflist_addref_deltatheta(result, k, h, l, 0, 1); + reflist_addref_deltatheta(result, -k, h, l, 0, 1); + reflist_addref_deltatheta(result, k, -h, l, 0, 1); + reflist_addref_deltatheta(result, -k, -h, l, 0, 1); + } + } else { + reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + reflist_addref_deltatheta(result, -h, k, l, 0, 1); + reflist_addref_deltatheta(result, h, -k, l, 0, 1); + } + } + + return result; + +} + +static double symmetry_realphase(double phk) { + + if ( (phk >= 0) && (phk < M_PI_2) ) phk = 0; + else if ( (phk > M_PI_2) && (phk < M_PI) ) phk = M_PI; + else if ( (phk < 0) && (phk > -M_PI/2) ) phk = 0; + else if ( (phk <= -M_PI_2) && (phk > -M_PI) ) phk = M_PI; + + return phk; + +} + +void symmetry_centricity(ReflectionList *reflections, unsigned int i, Symmetry sym, SymFlags flags) { + + double am, ph; + signed int h, k; + + ph = 0; + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + am = reflections->refs[i].amplitude; + if ( flags & SYMFLAG_PHASES_KNOWN ) ph = fmod(reflections->refs[i].phase_known, 2*M_PI); + if ( flags & SYMFLAG_PHASES_CALC ) ph = fmod(reflections->refs[i].phase_calc, 2*M_PI); + + if ( (sym & SYMMETRY_MIRROR_HORIZONTAL) && (sym & SYMMETRY_FRIEDEL) && (h == 0) ) { + ph = symmetry_realphase(ph); + } + + if ( (sym & SYMMETRY_MIRROR_VERTICAL) && (sym & SYMMETRY_FRIEDEL) && (k == 0) ) { + ph = symmetry_realphase(ph); + } + + if ( (sym & SYMMETRY_CENTRE) && (sym & SYMMETRY_FRIEDEL) ) { + ph = symmetry_realphase(ph); + } + + if (( ( (sym & SYMMETRY_GLIDE_HORIZONTAL) && (sym & SYMMETRY_MIRROR_VERTICAL) ) + || ( (sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER) && (sym & SYMMETRY_MIRROR_VERTICAL) ) + || ( (sym & SYMMETRY_GLIDE_VERTICAL) && (sym & SYMMETRY_MIRROR_HORIZONTAL) ) + || ( (sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (sym & SYMMETRY_MIRROR_HORIZONTAL) ) ) + && (is_odd(abs(h)+abs(k))) ) { + if ( (ph >= 0) && (ph < M_PI) ) ph = M_PI_2; + else if ( (ph >= M_PI_2) && (ph < 2*M_PI) ) ph = -M_PI_2; + else if ( (ph < 0) && (ph > -M_PI) ) ph = -M_PI_2; + else if ( (ph <= -M_PI) ) ph = M_PI_2; + } + + if ( flags & SYMFLAG_PHASES_KNOWN ) reflections->refs[i].phase_known = ph; + if ( flags & SYMFLAG_PHASES_CALC ) reflections->refs[i].phase_calc = ph; + +} + +unsigned int symmetry_reflection_allowed(signed int h, signed int k, signed int l, Symmetry sym) { + + if ( (sym & SYMMETRY_GLIDE_HORIZONTAL) && (h == 0) && (k % 2) ) return 0; + if ( (sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER) && (h == 0) && (k % 2) ) return 0; + if ( (sym & SYMMETRY_GLIDE_VERTICAL) && (k == 0) && (h % 2) ) return 0; + if ( (sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (k == 0) && (h % 2) ) return 0; + if ( (sym & SYMMETRY_ROTATION_4 ) && ( sym & SYMMETRY_GLIDE_DIAGONAL ) && (k == 0) && (h % 2) ) return 0; + if ( (sym & SYMMETRY_ROTATION_4 ) && ( sym & SYMMETRY_GLIDE_DIAGONAL ) && (h == 0) && (k % 2) ) return 0; + + return 1; + +} + +/* Symmetrise a list of reflections (expanding all equivalents to P1) */ +double symmetry_symmetrise(ReflectionList *reflections, Symmetry sym, SymFlags flags) { + + unsigned int i = 0; + double r_sym = 0; + double r_sym_norm = 0; + unsigned int new_ref = 0; + unsigned int elim_ref = 0; + double am_elim = 0; + unsigned int n = reflections->n_reflections; + + for ( i=1; irefs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + if ( (h==0) && (k==0) && (l==0) ) continue; + if ( symmetry_reflection_allowed(h, k, l, sym) ) { + + double ph = 69; + unsigned int j; + double av; + unsigned int av_n; + ReflectionList *equivalents; + + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + symmetry_centricity(reflections, i, sym, flags); + + /* First pass: calculate average amplitude, determine phase */ + av = reflections->refs[i].amplitude; av_n = 1; + for ( j=1; jn_reflections; j++ ) { + unsigned int p; + p = reflist_inlist(reflections, equivalents->refs[j].h, equivalents->refs[j].k, equivalents->refs[j].l); + if ( p ) { + av += reflections->refs[p].amplitude; + av_n++; + } + } + av = av / av_n; + r_sym += fabs(av - reflections->refs[i].amplitude); + r_sym_norm += reflections->refs[i].amplitude; + if ( flags & SYMFLAG_PHASES_KNOWN ) ph = fmod(reflections->refs[i].phase_known, 2*M_PI); + if ( flags & SYMFLAG_PHASES_CALC ) ph = fmod(reflections->refs[i].phase_calc, 2*M_PI); + //printf("Phase of %3i %3i %3i is %f\n", h, k, l, ph); + + /* Second pass: set all equivalent reflections to the average amplitude */ + reflections->refs[i].amplitude = av; + for ( j=1; jn_reflections; j++ ) { + + unsigned int p; + double phn; + p = reflist_inlist(reflections, equivalents->refs[j].h, equivalents->refs[j].k, equivalents->refs[j].l); + if ( p ) { + r_sym += fabs(av - reflections->refs[p].amplitude); + r_sym_norm += reflections->refs[p].amplitude; + reflections->refs[p].amplitude = av; + } else { + p = reflist_addref_am(reflections, equivalents->refs[j].h, equivalents->refs[j].k, equivalents->refs[j].l, av); + //printf("SY: Generating %3i %3i %3i am=%f\n", equivalents->refs[j].h, + // equivalents->refs[j].k, equivalents->refs[j].l, av); + new_ref++; + } + phn = (ph + equivalents->refs[j].delta_theta) * equivalents->refs[j].multiplier; + if ( flags & SYMFLAG_PHASES_KNOWN ) { + reflections->refs[p].phase_known = phn; + reflections->refs[p].phase_known_set = 1; + } + if ( flags & SYMFLAG_PHASES_CALC ) { + reflections->refs[p].phase_calc = phn; + reflections->refs[p].phase_calc_set = 1; + } + //printf("Set phase of %3i %3i %3i to %f (dt=%f, mul=%i)\n", equivalents->refs[j].h, + //equivalents->refs[j].k, equivalents->refs[j].l, + //phn, equivalents->refs[j].delta_theta, equivalents->refs[j].multiplier); + + + } + + reflist_free(equivalents); + + } else { + + am_elim += reflections->refs[i].amplitude; + reflist_delref(reflections, h, k, l); + //printf("SY: Eliminating systematically absent reflection %i %i %i\n", h, k, l); + elim_ref++; + i--; + + } + + } + + if ( r_sym > 0 ) { + r_sym = r_sym / r_sym_norm; + } + //printf("SY: R_sym = %.2f%%, %i reflections generated, %i reflections eliminated (total amplitude %f)\n", r_sym*100, new_ref, elim_ref, am_elim); + + return r_sym; + +} + +Symmetry symmetry_encode(const char *symmetry) { + + if ( strcmp(symmetry, "p1") == 0 ) return PLANEGROUP_P1; + if ( strcmp(symmetry, "p2") == 0 ) return PLANEGROUP_P2; + if ( strcmp(symmetry, "pm (m // x)") == 0 ) return PLANEGROUP_PM_X; + if ( strcmp(symmetry, "pm (m // y)") == 0 ) return PLANEGROUP_PM_Y; + if ( strcmp(symmetry, "pg (g // x)") == 0 ) return PLANEGROUP_PG_X; + if ( strcmp(symmetry, "pg (g // y)") == 0 ) return PLANEGROUP_PG_Y; + if ( strcmp(symmetry, "cm (m // x)") == 0 ) return PLANEGROUP_CM_X; + if ( strcmp(symmetry, "cm (m // y)") == 0 ) return PLANEGROUP_CM_Y; + if ( strcmp(symmetry, "p2mm") == 0 ) return PLANEGROUP_P2MM; + if ( strcmp(symmetry, "p2mg (m // x)") == 0 ) return PLANEGROUP_P2MG_X; + if ( strcmp(symmetry, "p2mg (m // y)") == 0 ) return PLANEGROUP_P2MG_Y; + if ( strcmp(symmetry, "p2gg") == 0 ) return PLANEGROUP_P2GG; + if ( strcmp(symmetry, "c2mm") == 0 ) return PLANEGROUP_C2MM; + if ( strcmp(symmetry, "p4") == 0 ) return PLANEGROUP_P4; + if ( strcmp(symmetry, "p4mm") == 0 ) return PLANEGROUP_P4MM; + if ( strcmp(symmetry, "p4gm") == 0 ) return PLANEGROUP_P4GM; + if ( strcmp(symmetry, "p3") == 0 ) return PLANEGROUP_P3; + if ( strcmp(symmetry, "p3m1") == 0 ) return PLANEGROUP_P3M1; + if ( strcmp(symmetry, "p31m") == 0 ) return PLANEGROUP_P31M; + if ( strcmp(symmetry, "p6") == 0 ) return PLANEGROUP_P6; + if ( strcmp(symmetry, "p6mm") == 0 ) return PLANEGROUP_P6MM; + + fprintf(stderr, "Unrecognised symmetry identifier '%s'\n", symmetry); + return SYMMETRY_IDENTITY; + +} + +const char *symmetry_decode(Symmetry sym) { + + if ( sym == PLANEGROUP_P1 ) return "p1"; + if ( sym == PLANEGROUP_P2 ) return "p2"; + if ( sym == PLANEGROUP_PM_X ) return "pm (m // x)"; + if ( sym == PLANEGROUP_PM_Y ) return "pm (m // y)"; + if ( sym == PLANEGROUP_PG_X ) return "pg (g // x)"; + if ( sym == PLANEGROUP_PG_Y ) return "pg (g // y)"; + if ( sym == PLANEGROUP_CM_X ) return "cm (m // x)"; + if ( sym == PLANEGROUP_CM_Y ) return "cm (m // y)"; + if ( sym == PLANEGROUP_P2MM ) return "p2mm"; + if ( sym == PLANEGROUP_P2MG_X ) return "p2mg (m // x)"; + if ( sym == PLANEGROUP_P2MG_Y ) return "p2mg (m // y)"; + if ( sym == PLANEGROUP_P2GG ) return "p2gg"; + if ( sym == PLANEGROUP_C2MM ) return "c2mm"; + if ( sym == PLANEGROUP_P4 ) return "p4"; + if ( sym == PLANEGROUP_P4MM ) return "p4mm"; + if ( sym == PLANEGROUP_P4GM ) return "p4gm"; + if ( sym == PLANEGROUP_P3 ) return "p3"; + if ( sym == PLANEGROUP_P3M1 ) return "p3m1"; + if ( sym == PLANEGROUP_P31M ) return "p31m"; + if ( sym == PLANEGROUP_P6 ) return "p6"; + if ( sym == PLANEGROUP_P6MM ) return "p6mm"; + if ( sym == (PLANEGROUP_P1 | SYMMETRY_FRIEDEL) ) return "p1 & Friedel"; + if ( sym == (PLANEGROUP_P2 | SYMMETRY_FRIEDEL) ) return "p2 & Friedel"; + if ( sym == (PLANEGROUP_PM_X | SYMMETRY_FRIEDEL) ) return "pm (m // x) & Friedel"; + if ( sym == (PLANEGROUP_PM_Y | SYMMETRY_FRIEDEL) ) return "pm (m // y) & Friedel"; + if ( sym == (PLANEGROUP_PG_X | SYMMETRY_FRIEDEL) ) return "pg (g // x) & Friedel"; + if ( sym == (PLANEGROUP_PG_Y | SYMMETRY_FRIEDEL) ) return "pg (g // y) & Friedel"; + if ( sym == (PLANEGROUP_CM_X | SYMMETRY_FRIEDEL) ) return "cm (m // x) & Friedel"; + if ( sym == (PLANEGROUP_CM_Y | SYMMETRY_FRIEDEL) ) return "cm (m // y) & Friedel"; + if ( sym == (PLANEGROUP_P2MM | SYMMETRY_FRIEDEL) ) return "p2mm & Friedel"; + if ( sym == (PLANEGROUP_P2MG_X | SYMMETRY_FRIEDEL) ) return "p2mg (m // x) & Friedel"; + if ( sym == (PLANEGROUP_P2MG_Y | SYMMETRY_FRIEDEL) ) return "p2mg (m // y) & Friedel"; + if ( sym == (PLANEGROUP_P2GG | SYMMETRY_FRIEDEL) ) return "p2gg & Friedel"; + if ( sym == (PLANEGROUP_C2MM | SYMMETRY_FRIEDEL) ) return "c2mm & Friedel"; + if ( sym == (PLANEGROUP_P4 | SYMMETRY_FRIEDEL) ) return "p4 & Friedel"; + if ( sym == (PLANEGROUP_P4MM | SYMMETRY_FRIEDEL) ) return "p4mm & Friedel"; + if ( sym == (PLANEGROUP_P4GM | SYMMETRY_FRIEDEL) ) return "p4gm & Friedel"; + if ( sym == (PLANEGROUP_P3 | SYMMETRY_FRIEDEL) ) return "p3 & Friedel"; + if ( sym == (PLANEGROUP_P3M1 | SYMMETRY_FRIEDEL) ) return "p3m1 & Friedel"; + if ( sym == (PLANEGROUP_P31M | SYMMETRY_FRIEDEL) ) return "p31m & Friedel"; + if ( sym == (PLANEGROUP_P6 | SYMMETRY_FRIEDEL) ) return "p6 & Friedel"; + if ( sym == (PLANEGROUP_P6MM | SYMMETRY_FRIEDEL) ) return "p6mm & Friedel"; + + return "(unknown symmetry)"; + +} + +void symmetry_symmetrise_array(fftw_complex *in, signed int width, signed int height, Symmetry sym) { + + ReflectionList *reflections; + + reflections = reflist_new_from_array(in, width, height); + symmetry_symmetrise(reflections, sym, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(in, reflections, width, height); + reflist_free(reflections); + +} diff --git a/src/symmetry.h b/src/symmetry.h new file mode 100644 index 0000000..bdcd63e --- /dev/null +++ b/src/symmetry.h @@ -0,0 +1,89 @@ +/* + * symmetry.h + * + * Symmetry stuff + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef SYMMETRY_H +#define SYMMETRY_H + +#include + +#include "reflist.h" + +/* Symmetry elements */ +typedef enum { + SYMMETRY_IDENTITY = 0, + SYMMETRY_FRIEDEL = 1<<0, + SYMMETRY_MIRROR_DIAGONAL = 1<<1, + SYMMETRY_CENTRE = 1<<2, + SYMMETRY_GLIDE_HORIZONTAL_QUARTER = 1<<3, + SYMMETRY_GLIDE_VERTICAL_QUARTER = 1<<4, + SYMMETRY_ROTATION_4 = 1<<5, + SYMMETRY_GLIDE_DIAGONAL = 1<<6, + SYMMETRY_MIRROR_VERTICAL = 1<<7, + SYMMETRY_MIRROR_HORIZONTAL = 1<<8, + SYMMETRY_GLIDE_HORIZONTAL = 1<<9, + SYMMETRY_GLIDE_VERTICAL = 1<<10, + SYMMETRY_TRIAD = 1<<11, + SYMMETRY_MIRROR_VERTICAL_QUARTER = 1<<12, + SYMMETRY_MIRROR_HORIZONTAL_QUARTER = 1<<13 +} Symmetry; + +/* Atoms at cell edges and corners need to be duplicated when displaying the model, + but not when calculating structure factors. */ +typedef enum { + MODEL_TARGET_DISPLAY, + MODEL_TARGET_CALCULATION +} ModelTarget; + +/* Planegroups */ +#define PLANEGROUP_P1 (SYMMETRY_IDENTITY) +#define PLANEGROUP_P2 (SYMMETRY_CENTRE) +#define PLANEGROUP_PM_X (SYMMETRY_MIRROR_HORIZONTAL) +#define PLANEGROUP_PM_Y (SYMMETRY_MIRROR_VERTICAL) +#define PLANEGROUP_PG_X (SYMMETRY_GLIDE_HORIZONTAL) +#define PLANEGROUP_PG_Y (SYMMETRY_GLIDE_VERTICAL) +#define PLANEGROUP_CM_X (SYMMETRY_MIRROR_HORIZONTAL | SYMMETRY_GLIDE_HORIZONTAL_QUARTER) +#define PLANEGROUP_CM_Y (SYMMETRY_MIRROR_VERTICAL | SYMMETRY_GLIDE_VERTICAL_QUARTER) +#define PLANEGROUP_P2MM (SYMMETRY_CENTRE | SYMMETRY_MIRROR_HORIZONTAL | SYMMETRY_MIRROR_VERTICAL) +#define PLANEGROUP_P2MG_X (SYMMETRY_CENTRE | SYMMETRY_GLIDE_VERTICAL | SYMMETRY_MIRROR_HORIZONTAL_QUARTER) +#define PLANEGROUP_P2MG_Y (SYMMETRY_CENTRE | SYMMETRY_GLIDE_HORIZONTAL | SYMMETRY_MIRROR_VERTICAL_QUARTER) +#define PLANEGROUP_P2GG (SYMMETRY_CENTRE | SYMMETRY_GLIDE_HORIZONTAL_QUARTER | SYMMETRY_GLIDE_VERTICAL_QUARTER) +#define PLANEGROUP_C2MM (SYMMETRY_CENTRE | SYMMETRY_GLIDE_HORIZONTAL_QUARTER | SYMMETRY_GLIDE_VERTICAL_QUARTER \ + | SYMMETRY_MIRROR_HORIZONTAL | SYMMETRY_MIRROR_VERTICAL) +#define PLANEGROUP_P4 (SYMMETRY_CENTRE | SYMMETRY_ROTATION_4) +#define PLANEGROUP_P4MM (SYMMETRY_CENTRE | SYMMETRY_MIRROR_DIAGONAL | SYMMETRY_ROTATION_4) /* Implies the other diagonal */ +#define PLANEGROUP_P4GM (SYMMETRY_CENTRE | SYMMETRY_ROTATION_4 | SYMMETRY_GLIDE_DIAGONAL) /*| SYMMETRY_GLIDE_HORIZONTAL_QUARTER */ +#define PLANEGROUP_P3 (SYMMETRY_TRIAD) +#define PLANEGROUP_P3M1 (SYMMETRY_TRIAD | SYMMETRY_MIRROR_DIAGONAL) +#define PLANEGROUP_P31M (SYMMETRY_TRIAD | SYMMETRY_MIRROR_HORIZONTAL) +#define PLANEGROUP_P6 (SYMMETRY_TRIAD | SYMMETRY_CENTRE) +#define PLANEGROUP_P6MM (SYMMETRY_TRIAD | SYMMETRY_CENTRE | SYMMETRY_MIRROR_VERTICAL | SYMMETRY_MIRROR_HORIZONTAL) + +typedef enum { + SYMFLAG_NONE = 0, + SYMFLAG_PHASES_KNOWN = 1<<0, + SYMFLAG_PHASES_CALC = 1<<1, +} SymFlags; + +/* Functions */ +extern struct struct_atomicmodel *symmetry_generate_equivalent_atoms(struct struct_atomicmodel *model, size_t j, ModelTarget target); +extern ReflectionList *symmetry_generate_equivalent_reflections(Symmetry sym, signed int h, signed int k, signed int l); +extern ReflectionList symmetry_reduce(ReflectionList *input, Symmetry sym); +extern double symmetry_symmetrise(ReflectionList *input, Symmetry sym, SymFlags flags); /* Returns R_sym */ +extern const char *symmetry_decode(Symmetry sym); +extern Symmetry symmetry_encode(const char *symmetry); +extern void symmetry_centricity(ReflectionList *reflections, unsigned int i, Symmetry sym, SymFlags flags); +extern void symmetry_symmetrise_array(fftw_complex *in, signed int width, signed int height, Symmetry sym); + +#endif /* SYMMETRY_H */ + diff --git a/src/testmain.c b/src/testmain.c new file mode 100644 index 0000000..6aeada6 --- /dev/null +++ b/src/testmain.c @@ -0,0 +1,68 @@ +/* + * main.c + * + * Test harness + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "data.h" +#include "reflist.h" + +int main(int argc, char *argv[]) { + + int i; + ReflectionList *reflist = NULL; + + if ( data_read(argv[1]) ) { + return 1; + } + reflist = data_getreflections(); + if ( reflist == NULL ) { + printf("data_getreflections() returned NULL\n"); + return 1; + } + + printf("---------------- Initial list\n"); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Adding 200 = 1.5\n"); + reflist_addref_am(reflist, 2, 0, 0, 1.5); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Removing 100\n"); + reflist_delref(reflist, 1, 0, 0); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Trying to remove 000\n"); + reflist_delref(reflist, 0, 0, 0); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Trying to add 000 = 5.0\n"); + reflist_addref_am(reflist, 0, 0, 0, 5.0); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + + return 0; + +} + -- cgit v1.2.3