CCL Home Page
Up Directory CCL ncad021
;;   ncad021.scm - Top level stuff, data structures, atom/bond bookkeeping
;;   Copyright (C) 1996,1997 Will Ware
;;   
;;   This program is free software; you can redistribute it and/or
;;   modify it under the terms of the GNU General Public License
;;   as published by the Free Software Foundation; either version 2
;;   of the License, or (at your option) any later version.
;;   
;;   This program is distributed in the hope that it will be useful,
;;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;   GNU General Public License for more details.
;;   
;;   You should have received a copy of the GNU General Public License
;;   along with this program; if not, write to the Free Software
;;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;;
;;   I can be reached via email at .

;; These things are redefined in GUI
(define (error-msg txt) '())
(define (warning-msg txt) '())
(define this-session '())
(define (force-rotate-mode) '())

;; Hook for defining forces other than chemical
(define external-forces #f)

(define debugging #f)

(if debugging
    (define-macro dbgprintf
      (lambda (x . y)
	`(printf ,x ,@y)))
    (define-macro dbgprintf
      (lambda (x . y) '())))

(if debugging
    (define-macro entering
      (lambda (name)
	`(printf "Entering ~s~%" ,name)))
    (define-macro entering
      (lambda (name) '())))

;; ******************************************************************
;; Handy macros stolen from Common Lisp

(define-macro dolist
  (lambda (args . body)
    `(do ((local-list ,(cadr args) (cdr local-list))
	  (,(car args) '()))
	 ((null? local-list))
       (set! ,(car args) (car local-list))
       ,@body)))

(define-macro dotimes
  (lambda (args . body)
    `(do ((,(car args) 0 (+ 1 ,(car args)))
	  (iteration-limit ,(cadr args)))
	 ((>= ,(car args) iteration-limit))
       ,@body)))

;; ******************************************************************
;; DATA STRUCTURES

(define atom-list '())
(define bond-list '())
(define term-list '())  ;; terms are zero-arg procedures

(define (make-atom elem pos)
  (entering "make-atom")
  (let ((force #(0.0 0.0 0.0))
	(species (lookup-species (elem 'name)
				 (elem 'initial-hybridization))))
    (lambda (x . y)
      (case x
	('element elem)
	('set-element (set! elem (car y)))
	('species species)
	('set-species (set! species (car y)))
	('position pos)
	('add-pos (set! pos (vplus pos (car y))))
	('set-pos (set! pos (car y)))
	('force force)
	('zero-force (set! force #(0.0 0.0 0.0)))
	('add-force (set! force (vplus force (car y))))
	(else
	 (printf "Atom trouble, args: ~s, ~s~%" x y))))))

(define (make-bond order first second)
  (entering "make-bond")
  (lambda (x)
    (case x
      ('order order)
      ('first first)
      ('second second))))

(define (make-element name rvdw mass bonds init-hybrid ch-hybrid)
  (entering "make-element")
  (lambda (x)
    (case x
      ('name name)
      ('rvdw rvdw)
      ('mass mass)
      ('total-bonds bonds)
      ('initial-hybridization init-hybrid)
      ('how-to-hybridize ch-hybrid))))

(define (make-species name hybrid evdw mm2)
  (entering "make-species")
  (lambda (x)
    (case x
      ('name name)
      ('hybridization hybrid)
      ('evdw evdw)
      ('mm2-index mm2))))

(define (make-bond-count n)
  (entering "make-bond-count")
  (let ((singles 0)
	(doubles 0)
	(triples 0))
    (dolist (bond bond-list)
	    (if (or (= n (bond 'first)) (= n (bond 'second)))
		(case (bond 'order)
		  (1 (set! singles (+ singles 1)))
		  (2 (set! doubles (+ doubles 1)))
		  (else (set! triples (+ triples 1))))))
    (lambda (x)
      (case x
	('singles singles)
	('doubles doubles)
	('triples triples)
	('total-bonds (+ singles (* 2 doubles) (* 3 triples)))))))

;; ******************************************************************
;; The Periodic Table, with hybridized species

(define (hybridize-carbon bond-count)
  (entering "hybridize-carbon")
  (cond ((= (bond-count 'singles) 4) 'sp3)
	((= (bond-count 'triples) 1) 'sp)
	((= (bond-count 'doubles) 2) 'sp)
	(else 'sp2)))

;; when ionized, oxygen can form a triple bond, but ignore that for now...
(define (hybridize-oxygen bond-count)
  (entering "hybridize-oxygen")
  (cond ((= (bond-count 'doubles) 1) 'sp2)
	(else 'sp3)))

(define periodic-table
  (list
   (make-element "C"  1.94 19.925  4 'sp3 hybridize-carbon)
   (make-element "H"  1.5  1.674   1 #f   #f)
   (make-element "O"  1.74 26.565  2 'sp3 hybridize-oxygen)
   (make-element "N"  1.82 23.251  3 #f   #f)
   (make-element "F"  1.65 31.545  1 #f   #f)
   (make-element "Cl" 2.03 38.064  1 #f   #f)
   (make-element "Br" 2.18 131.038 1 #f   #f)
   (make-element "I"  2.32 210.709 1 #f   #f)
   (make-element "S"  2.11 53.087  2 #f   #f)
   (make-element "Si" 2.25 46.454  4 #f   #f)
   (make-element "LP" 1.2  0.0     1 #f   #f)
   (make-element "P"  2.11 51.464  3 #f   #f)))

(define (lookup-element name)
  (entering "lookup-element")
  (do ((L periodic-table (cdr L)))
      ((or (null? L)
	   (equal? name ((car L) 'name)))
       (if (null? L) #f (car L)))))

;; I've mostly succeeded in hiding MM2 as an implementation detail in
;; forces.scm, but there need to be a few concessions in this file.

(define mm2-table
  (list
   (make-species "C"  'sp3 0.357 1)        ;; sp3
   (make-species "C"  'sp2 0.357 2)        ;; sp2 alkene
   (make-species "C"  'sp2 0.357 3)        ;; sp2 carbonyl
   (make-species "C"  'sp  0.357 4)        ;; sp acetylene
   (make-species "H"  #f   0.382 5)        ;; hydrocarbon
   (make-species "O"  'sp2 0.406 6)        ;; C-O-[HC]
   (make-species "O"  'sp3 0.536 7)        ;; O carbonyl
   (make-species "N"  #f   0.447 8)        ;; sp3
   (make-species "F"  #f   0.634 11)       ;; flouride
   (make-species "Cl" #f   1.95  12)       ;; chloride
   (make-species "Br" #f   2.599 13)       ;; bromide
   (make-species "I"  #f   3.444 14)       ;; iodide
   (make-species "S"  #f   1.641 15)       ;; sulfide
   (make-species "Si" #f   1.137 19)       ;; silane
   (make-species "LP" #f   0.13  20)       ;; lone pair
   (make-species "H"  #f   0.292 21)       ;; alcohol
   (make-species "C"  #f   0.357 22)       ;; cyclopropane
   (make-species "P"  #f   1.641 25)))     ;; phosphide

;; given an element name and a hybridization, look up the species
;; in mm2-table

(define (lookup-species name hybrid)
  (entering "lookup-species")
  (do ((L mm2-table (cdr L)))
      ((or (null? L)
	   (and (equal? name ((car L) 'name))
		(equal? hybrid ((car L) 'hybridization))))
       (if (null? L) #f (car L)))))

;; given an atom and a bond-count, hybridize the atom correctly, and
;; assign it a new species

(define (hybridize-atom atom bond-count)
  (entering "hybridize-atom")
  (let* ((elem (atom 'element))
	 (name (elem 'name))
	 (howto (elem 'how-to-hybridize))
	 (hybrid (if howto (howto bond-count) #f)))
    (atom 'set-species
	  (lookup-species name hybrid))))

;; ******************************************************************
;; Rotations and Centering

(define (center-structure)
  (entering "center-structure")
  (if (> (length atom-list) 0)
      (let ((cog (vector 0.0 0.0 0.0)))  ;; center of gravity
	(dolist (a atom-list)
		(set! cog
		      (vplus cog (a 'position))))
	(set! cog
	      (vscale cog (/ -1.0 (length atom-list))))
	(dolist (a atom-list)
		(a 'add-pos cog)))))

(define (rotate-all-atoms-x-axis theta)
  (entering "rotate-all-atoms-x-axis")
  (let ((ct (cos theta))
	(st (sin theta))
	(temp 0.0)
	(tmpv #(0.0 0.0 0.0)))
    (dolist
     (a atom-list)
     (set! tmpv (a 'position))
     (set! temp (- (* ct (vector-ref tmpv 1))
		   (* st (vector-ref tmpv 2))))
     (vector-set! tmpv 2
		  (+ (* ct (vector-ref tmpv 2))
		     (* st (vector-ref tmpv 1))))
     (vector-set! tmpv 1 temp)
     (a 'set-pos tmpv))))

(define (rotate-all-atoms-y-axis theta)
  (entering "rotate-all-atoms-y-axis")
  (let ((ct (cos theta))
	(st (sin theta))
	(temp 0.0)
	(tmpv #(0.0 0.0 0.0)))
    (dolist
     (a atom-list)
     (set! tmpv (a 'position))
     (set! temp (+ (* ct (vector-ref tmpv 0))
		   (* st (vector-ref tmpv 2))))
     (vector-set! tmpv 2
		  (- (* ct (vector-ref tmpv 2))
		     (* st (vector-ref tmpv 0))))
     (vector-set! tmpv 0 temp)
     (a 'set-pos tmpv))))

(define negligible-angle-sq 0.00001)

(define (rotate-structure xt yt)
  (entering "rotate-structure")
  (if (> (* xt xt) negligible-angle-sq)
      (rotate-all-atoms-y-axis xt))
  (if (> (* yt yt) negligible-angle-sq)
      (rotate-all-atoms-x-axis yt)))

;; ******************************************************************
;; Add/delete/select atoms and bonds

(define (remove-from-list L n)
  (cond ((null? L) '())
        ((= n 0) (cdr L))
        (else (cons (car L)
                    (remove-from-list (cdr L) (- n 1))))))

(define (remove-from-list-if-test L test)
  (cond ((null? L) '())
        ((apply test (list (car L)))
	 (remove-from-list-if-test (cdr L) test))
        (else (cons (car L)
                    (remove-from-list-if-test (cdr L) test)))))

(define (add-bond order m n)
  (entering "add-bond")
  (if (not (= m n))
      (let ()
	(set! need-to-resetup-terms #t)
	(delete-bond m n)
	(set! bond-list (cons (make-bond order m n) bond-list)))))

(define (delete-bond m n)
  (entering "delete-bond")
  (set! need-to-resetup-terms #t)
  (set! bond-list
	(remove-from-list-if-test
	 bond-list
	 (lambda (bond) (or (and (= m (bond 'first))
				 (= n (bond 'second)))
			    (and (= n (bond 'first))
				 (= m (bond 'second))))))))

(define (add-atom e pos)
  (entering "add-atom")
  (set! need-to-resetup-terms #t)
  (set! atom-list
	(append
	 atom-list
	 (list
	  (make-atom (lookup-element e) pos)))))

(define (delete-atom n)
  (entering "delete-atom")
  (set! need-to-resetup-terms #t)
  (set! atom-list (remove-from-list atom-list n))
  (set! bond-list (remove-from-list-if-test
		   bond-list
		   (lambda (bond) (or (= n (bond 'first))
				      (= n (bond 'second))))))
  (set! bond-list
	(map
	 (lambda (bond)
	   (if (> (bond 'first) n)
	       (set! bond (make-bond (bond 'order)
				     (- (bond 'first) 1)
				     (bond 'second))))
	   (if (> (bond 'second) n)
	       (set! bond (make-bond (bond 'order)
				     (bond 'first)
				     (- (bond 'second) 1))))
	   bond)
	 bond-list)))

;; ******************************************************************
;; Loading and saving structures

(define (clear-structure)
  (set! atom-list '())
  (set! bond-list '())
  (set! need-to-resetup-terms #t))

(define (load-structure file-name)
  (entering "load-structure")
  (clear-structure)
  (if (not (null? file-name))
      (let* ((inf (open-input-file file-name))
	     (s (read inf)))
	(close-input-port inf)
	(set! atom-list
	      (map
	       (lambda (z)
		 (make-atom (lookup-element (car z))
			    (vector (cadr z) (caddr z) (cadddr z))))
	       (car s)))
	(set! bond-list
	      (map
	       (lambda (z)
		 (make-bond (car z) (cadr z) (caddr z)))
	       (cadr s))))))

(if (defined? 'pretty-print)
    (define (print-to-file outf s)
      (pretty-print s outf))
    (define (print-to-file outf s)
      (fprintf outf "~s~%" s)))

(define (save-structure file-name)
  (entering "save-structure")
  (if (not (null? file-name))
      (let ()
	(delete-file file-name)
	(let ((outf (open-output-file file-name)))
	  (print-to-file
	   outf
	   (list
	    (map (lambda (z)
		   (list ((z 'element) 'name)
			 (vector-ref (z 'position) 0)
			 (vector-ref (z 'position) 1)
			 (vector-ref (z 'position) 2)))
		 atom-list)
	    (map (lambda (z)
		   (list (z 'order)
			 (z 'first)
			 (z 'second)))
		 bond-list)))
	  (close-output-port outf)))))

(define (save-structure-xyz file-name)
  (entering "save-structure-xyz")
  (if (not (null? file-name))
      (let ()
	(delete-file file-name)
	(let ((outf (open-output-file file-name)))
	  (fprintf outf "~a~%Gray Goo~%"
		   (length atom-list))
	  (dolist
	   (L atom-list)
	   (fprintf
	    outf "~a ~a ~a ~a~%"
	    ((L 'element) 'name)
	    (vector-ref (L 'position) 0)
	    (vector-ref (L 'position) 1)
	    (- (vector-ref (L 'position) 2))))
	  (close-output-port outf)))))

;; ******************************************************************
;; Fire up the GUI

(load "forces.scm")

;; If we're in MrEd, launch the GUI. If we're in MzScheme, don't bother
(if (defined? 'mred:startup)
  (let ()
   (load "gui.scm")
   (set! this-session (make-object session%))))
Modified: Thu Jan 23 17:00:00 1997 GMT
Page accessed 5444 times since Sat Apr 17 22:02:58 1999 GMT