5 * Created by Toshi Nagata on 07/11/09.
6 * Copyright 2007-2008 Toshi Nagata. All rights reserved.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation version 2 of the License.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
19 #include "ruby_dialog.h"
20 #include "../MD/MDCore.h"
28 #include "version.h" /* for Ruby version */
29 #include "ruby/version.h" /* for RUBY_BIRTH_YEAR etc. */
30 #include "ruby/encoding.h" /* for rb_str_encode() etc. */
31 /*#include <node.h> *//* for rb_add_event_hook() */
33 #if defined(__WXMAC__) || defined(__CMDMAC__)
34 #define USE_PTHREAD_FOR_TIMER 1
38 #if USE_PTHREAD_FOR_TIMER
39 #include <unistd.h> /* for usleep() */
40 #include <pthread.h> /* for pthread */
42 #include <signal.h> /* for sigaction() */
46 #include "../Missing.h"
48 #pragma mark ====== Global Values ======
52 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
53 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
55 VALUE gMolbyBacktrace;
56 VALUE gMolbyErrorHistory;
57 VALUE gScriptMenuCommands;
58 VALUE gScriptMenuEnablers;
60 int gMolbyRunLevel = 0;
61 int gMolbyIsCheckingInterrupt = 0;
63 char *gRubyVersion, *gRubyCopyright;
66 static ID s_ID_equal; /* rb_intern("==") */
70 /* Symbols for atom attributes */
72 s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
73 s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
74 s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
75 s_RSym, s_XSym, s_YSym, s_ZSym,
76 s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
77 s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
78 s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
79 s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
80 s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
81 s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
83 /* Symbols for parameter attributes */
85 s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
86 s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
89 /* s_A14Sym, s_B14Sym, */
90 s_Req14Sym, s_Eps14Sym,
91 s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym, s_VdwRadiusSym,
92 s_CommentSym, s_SourceSym;
96 * Get ary[i] by calling "[]" method
99 Ruby_ObjectAtIndex(VALUE ary, int idx)
101 static ID index_method = 0;
102 if (TYPE(ary) == T_ARRAY) {
103 int len = RARRAY_LEN(ary);
104 if (idx >= 0 && idx < len)
105 return (RARRAY_PTR(ary))[idx];
108 if (index_method == 0)
109 index_method = rb_intern("[]");
110 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
114 Ruby_FileStringValuePtr(VALUE *valp)
117 char *p = strdup(StringValuePtr(*valp));
118 translate_char(p, '/', '\\');
119 *valp = rb_str_new2(p);
121 return StringValuePtr(*valp);
123 return StringValuePtr(*valp);
128 Ruby_NewFileStringValue(const char *fstr)
132 char *p = strdup(fstr);
133 translate_char(p, '\\', '/');
134 retval = rb_enc_str_new(p, strlen(p), rb_default_external_encoding());
138 return rb_str_new2(fstr);
143 Ruby_EncodedStringValuePtr(VALUE *valp)
145 rb_string_value(valp);
146 *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
147 return RSTRING_PTR(*valp);
151 Ruby_NewEncodedStringValue(const char *str, int len)
155 return rb_enc_str_new(str, len, rb_default_external_encoding());
159 Ruby_ObjToStringObj(VALUE val)
165 return rb_str_new2(rb_id2name(SYM2ID(val)));
167 return rb_str_to_str(val);
171 #pragma mark ====== Message input/output ======
175 * message_box(str, title, button = nil, icon = :info)
177 * Show a message box.
178 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
179 * Icon: :info, :warning, :error
182 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
184 char *str, *title, *s;
186 VALUE sval, tval, bval, ival;
187 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
188 str = StringValuePtr(sval);
189 title = StringValuePtr(tval);
191 bval = Ruby_ObjToStringObj(bval);
192 s = RSTRING_PTR(bval);
193 if (strncmp(s, "ok", 2) == 0)
195 else if (strncmp(s, "cancel", 6) == 0)
198 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
201 ival = Ruby_ObjToStringObj(ival);
202 s = RSTRING_PTR(ival);
203 if (strncmp(s, "info", 4) == 0)
205 else if (strncmp(s, "warn", 4) == 0)
207 else if (strncmp(s, "err", 3) == 0)
210 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
212 MyAppCallback_messageBox(str, title, buttons, icon);
218 * error_message_box(str)
220 * Show an error message box.
223 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
225 char *str = StringValuePtr(sval);
226 MyAppCallback_errorMessageBox("%s", str);
232 * ask(prompt, default = nil) -> string
234 * Open a modal dialog and get a line of text.
237 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
239 volatile VALUE prompt, message;
242 rb_scan_args(argc, argv, "11", &prompt, &message);
243 if (message != Qnil) {
244 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
245 buf[sizeof buf - 1] = 0;
247 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
249 return rb_str_new2(buf);
256 * show_console_window
258 * Show the console window and bring to the front.
261 s_Kernel_ShowConsoleWindow(VALUE self)
263 MyAppCallback_showConsoleWindow();
269 * hide_console_window
271 * Hide the console window.
274 s_Kernel_HideConsoleWindow(VALUE self)
276 MyAppCallback_hideConsoleWindow();
284 * Ring the system bell.
287 s_Kernel_Bell(VALUE self)
289 MyAppCallback_bell();
295 * play_sound(filename, flag = 0)
297 * Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
298 * 1, play the sound asynchronously; 3, play the sound with loop asynchronously
301 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
306 rb_scan_args(argc, argv, "11", &fnval, &flval);
309 else flag = NUM2INT(rb_Integer(flval));
310 fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
311 fname = StringValuePtr(fnval);
312 retval = MyAppCallback_playSound(fname, flag);
313 return (retval ? Qtrue : Qnil);
320 * Stop the sound if playing.
323 s_Kernel_StopSound(VALUE self)
325 MyAppCallback_stopSound();
331 * export_to_clipboard(str)
333 * Export the given string to clipboard.
336 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
338 #if !defined(__CMDMAC__)
339 const char *s = StringValuePtr(sval);
342 /* Convert the end-of-line characters */
343 { const char *p; int nc; char *np;
345 for (p = s; *p != 0; p++) {
349 ns = (char *)malloc(strlen(s) + nc + 1);
350 for (np = ns, p = s; *p != 0; p++, np++) {
358 ns = (char *)malloc(strlen(s) + 1);
362 /* wxMac still has Carbon code. Oops. */
363 for (np = ns; *np != 0; np++) {
370 if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
371 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
380 * Put the message in the main text view in black color.
383 s_StandardOutput(VALUE self, VALUE str)
386 MyAppCallback_setConsoleColor(0);
387 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
395 * Put the message in the main text view in red color.
398 s_StandardErrorOutput(VALUE self, VALUE str)
401 MyAppCallback_setConsoleColor(1);
402 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
403 MyAppCallback_setConsoleColor(0);
412 * Flush the standard (error) output. Actually do nothing.
415 s_FlushConsoleOutput(VALUE self)
422 * stdin.gets(rs = $/)
424 * Read one line message via dialog box.
427 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
430 pval = rb_str_new2("Enter a line:");
431 rval = s_Kernel_Ask(1, &pval, self);
434 rb_str_cat2(rval, "\n");
440 * stdin.method_missing(name, args, ...)
442 * Throw an exception, noting only gets and readline are defined.
445 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
448 rb_scan_args(argc, argv, "10", &nval);
449 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
450 return Qnil; /* Not reached */
453 #pragma mark ====== Track key events ======
455 /* User interrupt handling
456 * User interrupt (command-period on Mac OS) is handled by periodic polling of
457 * key events. This polling should only be enabled during "normal" execution
458 * of scripts and must be disabled when the rest of the application (or Ruby
459 * script itself) is handling GUI. This is ensured by appropriate calls to
460 * enable_interrupt and disable_interrupt. */
462 static VALUE s_interrupt_flag = Qfalse;
465 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
467 volatile VALUE message;
469 if (Ruby_GetInterruptFlag() == Qtrue) {
470 rb_scan_args(argc, argv, "01", &message);
472 p = StringValuePtr(message);
475 MyAppCallback_showProgressPanel(p);
481 s_HideProgressPanel(VALUE self)
483 MyAppCallback_hideProgressPanel();
488 s_SetProgressValue(VALUE self, VALUE val)
490 double dval = NUM2DBL(rb_Float(val));
491 MyAppCallback_setProgressValue(dval);
496 s_SetProgressMessage(VALUE self, VALUE msg)
501 else p = StringValuePtr(msg);
502 MyAppCallback_setProgressMessage(p);
507 s_SetInterruptFlag(VALUE self, VALUE val)
511 if (val == Qfalse || val == Qnil)
515 oldval = s_interrupt_flag;
517 s_interrupt_flag = val;
519 s_HideProgressPanel(self);
526 s_GetInterruptFlag(VALUE self)
528 return s_SetInterruptFlag(self, Qundef);
532 Ruby_SetInterruptFlag(VALUE val)
534 return s_SetInterruptFlag(Qnil, val);
538 Ruby_GetInterruptFlag(void)
540 return s_SetInterruptFlag(Qnil, Qundef);
545 * check_interrupt -> integer
547 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
550 s_Kernel_CheckInterrupt(VALUE self)
552 if (Ruby_GetInterruptFlag() == Qfalse)
554 else if (MyAppCallback_checkInterrupt())
556 else return INT2NUM(0);
559 static volatile unsigned long sITimerCount = 0;
562 static HANDLE sITimerEvent;
563 static HANDLE sITimerThread;
564 static int sITimerInterval;
566 static __stdcall unsigned
567 s_ITimerThreadFunc(void *p)
569 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
575 #elif USE_PTHREAD_FOR_TIMER
578 static pthread_t sTimerThread;
580 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
581 static volatile signed char sTimerFlag = -1;
582 static volatile int sTimerIntervalMicrosec = 0;
585 s_TimerThreadEntry(void *param)
588 usleep(sTimerIntervalMicrosec);
591 else if (sTimerFlag == -2)
600 s_SignalAction(int n)
606 s_SetIntervalTimer(int n, int msec)
610 /* Start interval timer */
611 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
612 sITimerInterval = msec;
614 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
617 /* Stop interval timer */
619 SetEvent(sITimerEvent); /* Tell thread to terminate */
621 WaitForSingleObject(sITimerThread, 1000);
622 CloseHandle(sITimerThread);
625 CloseHandle(sITimerEvent);
627 sITimerThread = NULL;
629 #elif USE_PTHREAD_FOR_TIMER
631 if (sTimerFlag == -1) {
632 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
634 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
637 sTimerFlag = 0; /* Active */
638 sTimerIntervalMicrosec = msec * 1000;
639 } else if (sTimerFlag != -1)
640 sTimerFlag = 1; /* Inactive */
642 static struct itimerval sOldValue;
643 static struct sigaction sOldAction;
644 struct itimerval val;
645 struct sigaction act;
648 act.sa_handler = s_SignalAction;
651 sigaction(SIGALRM, &act, &sOldAction);
652 val.it_value.tv_sec = 0;
653 val.it_value.tv_usec = msec * 1000;
654 val.it_interval.tv_sec = 0;
655 val.it_interval.tv_usec = msec * 1000;
656 setitimer(ITIMER_REAL, &val, &sOldValue);
658 setitimer(ITIMER_REAL, &sOldValue, &val);
659 sigaction(SIGALRM, &sOldAction, &act);
665 s_GetTimerCount(void)
671 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
672 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
674 if (s_interrupt_flag != Qfalse) {
675 static unsigned long sLastTime = 0;
676 unsigned long currentTime;
678 currentTime = s_GetTimerCount();
679 if (currentTime != sLastTime) {
680 sLastTime = currentTime;
681 gMolbyIsCheckingInterrupt = 1;
682 flag = MyAppCallback_checkInterrupt();
683 gMolbyIsCheckingInterrupt = 0;
685 s_SetInterruptFlag(Qnil, Qfalse);
692 #pragma mark ====== Menu handling ======
696 * register_menu(title, method, enable_proc = nil)
698 * Register the method (specified as a symbol) in the script menu.
699 * The method must be either an instance method of Molecule with no argument,
700 * or a class method of Molecule with one argument (the current molecule),
701 * or a proc object with one argument (the current molecule).
702 * The menu associated with the class method can be invoked even when no document
703 * is open (the argument is set to Qnil in this case). On the other hand, the
704 * menu associated with the instance method can only be invoked when at least one
705 * document is active.
706 * If enable_proc is non-nil, then it is called whenever the availability of
707 * the menu command is tested. It is usually a proc object with one argument
708 * (the current molecule or nil). As a special case, the following symbols can
709 * be given; :mol (enabled when any molecule is open), :non_empty (enabled when
710 * the top-level molecule has at least one atom), :selection (enabled when
711 * the top-level molecule has one or more selected atoms).
714 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
717 VALUE tval, mval, pval;
718 static VALUE sMolSym, sNonEmptySym, sSelectionSym;
719 static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
720 rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
721 tval = rb_str_to_str(tval);
722 n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
725 if (TYPE(mval) == T_SYMBOL) {
726 /* Create an appropriate proc object */
727 const char *name = rb_id2name(SYM2ID(mval));
729 if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
730 /* Defined as a Molecule method */
731 asprintf(&s, "lambda { |m| m.%s }", name);
733 } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
734 /* Defined as a Molecule class method */
735 asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
737 } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
738 mval = rb_eval_string(s);
741 if (sMolSym == Qfalse) {
742 sMolSym = ID2SYM(rb_intern("mol"));
743 sNonEmptySym = ID2SYM(rb_intern("non_empty"));
744 sSelectionSym = ID2SYM(rb_intern("selection"));
745 sMolProc = rb_eval_string("lambda { |m| m != nil }");
746 sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
747 sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
748 sTrueProc = rb_eval_string("lambda { |m| true }");
749 rb_global_variable(&sMolProc);
750 rb_global_variable(&sNonEmptyProc);
751 rb_global_variable(&sSelectionProc);
752 rb_global_variable(&sTrueProc);
760 } else if (pval == sMolSym)
762 else if (pval == sNonEmptySym)
763 pval = sNonEmptyProc;
764 else if (pval == sSelectionSym)
765 pval = sSelectionProc;
766 rb_ary_store(gScriptMenuCommands, n, mval);
767 rb_ary_store(gScriptMenuEnablers, n, pval);
772 s_Kernel_LookupMenu(VALUE self, VALUE title)
774 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
779 s_Ruby_UpdateUI_handler(VALUE data)
781 void **p = (void **)data;
782 int index = (int)p[0];
783 Molecule *mol = (Molecule *)p[1];
784 int *outChecked = (int *)p[2];
785 char **outTitle = (char **)p[3];
786 VALUE mval = ValueFromMolecule(mol);
787 VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
788 static ID call_id = 0;
790 call_id = rb_intern("call");
793 pval = rb_funcall(pval, call_id, 1, mval);
794 if (rb_obj_is_kind_of(pval, rb_cArray)) {
796 if (outChecked != NULL) {
797 val = rb_ary_entry(pval, 1); /* Checked or not */
798 *outChecked = (RTEST(val) ? 1 : 0);
800 if (outTitle != NULL) {
801 val = rb_ary_entry(pval, 2); /* Text */
802 if (TYPE(val) == T_STRING) {
803 *outTitle = strdup(StringValuePtr(val));
806 pval = rb_ary_entry(pval, 0);
812 Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
817 p[0] = (void *)index;
821 retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
822 return (RTEST(retval) ? 1 : 0);
827 s_Ruby_methodType_sub(VALUE data)
829 const char **p = (const char **)data;
830 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
831 ID mid = rb_intern(p[1]);
833 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
835 else if (rb_respond_to(klass, mid))
838 return INT2FIX(ival);
841 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
842 has the singleton method (class method) with the given name, 0 otherwise. */
844 Ruby_methodType(const char *className, const char *methodName)
851 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
853 return FIX2INT(retval);
860 * execute_script_file(fname)
862 * Execute the script in the given file. If a molecule is active, then
863 * the script is evaluated as Molecule.current.instance_eval(script).
864 * Before entering the script, the current directory is set to the parent
865 * directory of the script.
868 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
871 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
872 if (retval == (VALUE)6 && status == -1)
873 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
883 * Get the directory suitable for storing user documents. On Windows
884 * it is the home directory + "My Documents". On other platforms
885 * it is the home directory.
888 s_Kernel_DocumentHome(VALUE self)
890 char *s = MyAppCallback_getDocumentHomeDir();
891 VALUE retval = Ruby_NewFileStringValue(s);
896 /* The callback function for call_subprocess */
898 s_Kernel_CallSubProcess_Callback(void *data)
901 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
902 if (status != 0 || retval == Qnil || retval == Qfalse)
909 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
911 * Call subprocess. A progress dialog window is displayed, with a message
912 * "Running #{process_name}...".
913 * A callback proc can be given, which is called periodically during execution. If the proc returns
914 * nil or false, then the execution will be interrupted.
915 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
916 * filename begins with ">>", then the message will be appended to the file.
917 * If the filename is "/dev/null" or "NUL", then the message will be lost.
918 * If the argument is nil, then the message will be sent to the Ruby console.
921 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
923 VALUE cmd, procname, cproc, stdout_val, stderr_val;
928 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
930 if (stdout_val == Qnil) {
933 sout = StringValuePtr(stdout_val);
934 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
937 if (strncmp(sout, ">>", 2) == 0) {
939 fpout = fopen(sout, "a");
943 fpout = fopen(sout, "w");
946 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
949 if (stderr_val == Qnil) {
952 serr = StringValuePtr(stderr_val);
953 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
956 if (strncmp(serr, ">>", 2) == 0) {
958 fpout = fopen(serr, "a");
962 fperr = fopen(serr, "w");
965 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
969 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
971 if (fpout != NULL && fpout != (FILE *)1)
973 if (fperr != NULL && fperr != (FILE *)1)
985 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
988 s_Kernel_Backquote(VALUE self, VALUE cmd)
993 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
995 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
997 val = Ruby_NewEncodedStringValue(buf, 0);
1000 val = Ruby_NewEncodedStringValue("", 0);
1005 #pragma mark ====== User defaults ======
1009 * get_global_settings(key)
1011 * Get a setting data for key from the application preferences.
1014 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1016 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1018 VALUE retval = rb_eval_string(p);
1026 * set_global_settings(key, value)
1028 * Set a setting data for key to the application preferences.
1031 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1033 VALUE sval = rb_inspect(value);
1034 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1038 #pragma mark ====== IO extension ======
1044 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1047 s_IO_gets_any_eol(VALUE self)
1052 static ID id_getbyte = 0, id_ungetbyte;
1053 if (id_getbyte == 0) {
1054 id_getbyte = rb_intern("getbyte");
1055 id_ungetbyte = rb_intern("ungetbyte");
1059 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1060 c = NUM2INT(rb_Integer(cval));
1062 cval = rb_funcall(self, id_getbyte, 0);
1064 c = NUM2INT(rb_Integer(cval));
1066 rb_funcall(self, id_ungetbyte, 1, cval);
1068 } else if (c != 0x0a) {
1073 val = rb_str_new(buf, i);
1075 rb_str_append(val, rb_str_new(buf, i));
1080 if (cval == Qnil && i == 0 && val == Qnil)
1081 return Qnil; /* End of file */
1084 val = rb_str_new(buf, i);
1086 rb_str_append(val, rb_str_new(buf, i));
1087 val = rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
1089 /* Needs a end-of-line mark */
1090 cval = rb_gv_get("$/");
1091 rb_str_append(val, cval);
1093 rb_gv_set("$_", val);
1097 #pragma mark ====== Utility functions (protected funcall) ======
1099 struct Ruby_funcall2_record {
1107 s_Ruby_funcall2_sub(VALUE data)
1109 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1110 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1114 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1116 struct Ruby_funcall2_record rec;
1121 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1125 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1127 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1130 #pragma mark ====== ParameterRef Class ======
1133 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1137 Data_Get_Struct(self, ParameterRef, pref);
1139 *typep = pref->parType;
1140 if (pref->parType == kElementParType) {
1141 up = (UnionPar *)&gElementParameters[pref->idx];
1143 up = ParameterRefGetPar(pref);
1144 if (checkEditable) {
1146 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1147 if (up->bond.src != 0 && up->bond.src != -1)
1148 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1155 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1159 Data_Get_Struct(self, ParameterRef, pref);
1160 if (pref->mol == NULL)
1162 up = ParameterRefGetPar(pref);
1163 if (key != s_SourceSym)
1164 up->bond.src = 0; /* Becomes automatically molecule-local */
1165 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1168 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1169 MolActionCallback_registerUndo(pref->mol, act);
1170 MoleculeCallback_notifyModification(pref->mol, 0);
1171 pref->mol->needsMDRebuild = 1;
1176 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1178 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1180 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1182 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1186 s_AtomTypeIndexFromValue(VALUE val)
1188 if (rb_obj_is_kind_of(val, rb_cNumeric))
1189 return NUM2INT(val);
1191 return AtomTypeEncodeToUInt(StringValuePtr(val));
1194 static const char *s_ParameterTypeNames[] = {
1195 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1197 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1200 s_ParTypeFromValue(VALUE val)
1204 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1205 if (s_ParameterTypeIDs[0] == 0) {
1206 for (i = 0; i < n; i++)
1207 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1209 valid = rb_to_id(val);
1210 for (i = 0; i < n; i++) {
1211 if (valid == s_ParameterTypeIDs[i]) {
1213 return kElementParType;
1214 else return kFirstParType + i;
1217 return kInvalidParType;
1224 * Get the index in the parameter list.
1226 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1228 Data_Get_Struct(self, ParameterRef, pref);
1229 return INT2NUM(pref->idx);
1234 * par_type -> String
1236 * Get the parameter type, like "bond", "angle", etc.
1238 static VALUE s_ParameterRef_GetParType(VALUE self) {
1240 s_UnionParFromValue(self, &tp, 0);
1241 if (tp == kElementParType)
1242 return rb_str_new2("element");
1243 tp -= kFirstParType;
1244 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1245 return rb_str_new2(s_ParameterTypeNames[tp]);
1246 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1251 * atom_type -> String or Array of String
1252 * atom_types -> String or Array of String
1254 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1255 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1256 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1257 * The atom type may be "X", which is a wildcard that matches any atom type.
1259 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1264 up = s_UnionParFromValue(self, &tp, 0);
1265 n = ParameterGetAtomTypes(tp, up, types);
1267 rb_raise(rb_eMolbyError, "invalid member atom_types");
1268 for (i = 0; i < n; i++) {
1269 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1270 vals[i] = INT2NUM(types[i]);
1272 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1277 return rb_ary_new4(n, vals);
1284 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1286 static VALUE s_ParameterRef_GetK(VALUE self) {
1290 up = s_UnionParFromValue(self, &tp, 0);
1293 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1295 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1296 case kDihedralParType:
1297 case kImproperParType:
1298 if (up->torsion.mult == 1)
1299 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1300 n = up->torsion.mult;
1303 for (i = 0; i < n; i++)
1304 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1305 return rb_ary_new4(n, vals);
1307 rb_raise(rb_eMolbyError, "invalid member k");
1315 * Get the equilibrium bond length. Only available for bond parameters.
1317 static VALUE s_ParameterRef_GetR0(VALUE self) {
1320 up = s_UnionParFromValue(self, &tp, 0);
1321 if (tp == kBondParType)
1322 return rb_float_new(up->bond.r0);
1323 else rb_raise(rb_eMolbyError, "invalid member r0");
1330 * Get the equilibrium angle (in degree). Only available for angle parameters.
1332 static VALUE s_ParameterRef_GetA0(VALUE self) {
1335 up = s_UnionParFromValue(self, &tp, 0);
1336 if (tp == kAngleParType)
1337 return rb_float_new(up->angle.a0 * kRad2Deg);
1338 else rb_raise(rb_eMolbyError, "invalid member a0");
1345 * Get the multiplicity. Only available for dihedral and improper parameters.
1346 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1348 static VALUE s_ParameterRef_GetMult(VALUE self) {
1351 up = s_UnionParFromValue(self, &tp, 0);
1352 if (tp == kDihedralParType || tp == kImproperParType)
1353 return rb_float_new(up->torsion.mult);
1354 else rb_raise(rb_eMolbyError, "invalid member mult");
1359 * period -> Integer or Array of Integers
1361 * Get the periodicity. Only available for dihedral and improper parameters.
1362 * If the multiplicity is larger than 1, then an array of integers is returned.
1363 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1365 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1369 up = s_UnionParFromValue(self, &tp, 0);
1370 if (tp == kDihedralParType || tp == kImproperParType) {
1371 if (up->torsion.mult == 1)
1372 return INT2NUM(up->torsion.period[0]);
1373 n = up->torsion.mult;
1376 for (i = 0; i < n; i++)
1377 vals[i] = INT2NUM(up->torsion.period[i]);
1378 return rb_ary_new4(n, vals);
1379 } else rb_raise(rb_eMolbyError, "invalid member period");
1384 * phi0 -> Float or Array of Floats
1386 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1387 * If the multiplicity is larger than 1, then an array of floats is returned.
1388 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1390 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1394 up = s_UnionParFromValue(self, &tp, 0);
1395 if (tp == kDihedralParType || tp == kImproperParType) {
1396 if (up->torsion.mult == 1)
1397 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1398 n = up->torsion.mult;
1401 for (i = 0; i < n; i++)
1402 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1403 return rb_ary_new4(n, vals);
1404 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1411 * Get the "A" value for the van der Waals parameter.
1414 static VALUE s_ParameterRef_GetA(VALUE self) {
1417 up = s_UnionParFromValue(self, &tp, 0);
1418 if (tp == kVdwParType)
1419 return rb_float_new(up->vdw.A);
1420 else if (tp == kVdwPairParType)
1421 return rb_float_new(up->vdwp.A);
1422 else rb_raise(rb_eMolbyError, "invalid member A");
1430 * Get the "B" value for the van der Waals parameter.
1433 static VALUE s_ParameterRef_GetB(VALUE self) {
1436 up = s_UnionParFromValue(self, &tp, 0);
1437 if (tp == kVdwParType)
1438 return rb_float_new(up->vdw.B);
1439 else if (tp == kVdwPairParType)
1440 return rb_float_new(up->vdwp.B);
1441 else rb_raise(rb_eMolbyError, "invalid member B");
1449 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1451 static VALUE s_ParameterRef_GetReq(VALUE self) {
1454 /* Double a, b, r; */
1456 up = s_UnionParFromValue(self, &tp, 0);
1457 if (tp == kVdwParType) {
1461 } else if (tp == kVdwPairParType) {
1465 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1466 /* if (a == 0.0 || b == 0.0) */
1467 return rb_float_new(r);
1468 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1475 * Get the minimum energy for the van der Waals parameter.
1477 static VALUE s_ParameterRef_GetEps(VALUE self) {
1482 up = s_UnionParFromValue(self, &tp, 0);
1483 if (tp == kVdwParType) {
1487 } else if (tp == kVdwPairParType) {
1491 } else rb_raise(rb_eMolbyError, "invalid member eps");
1492 /* if (a == 0.0 || b == 0.0) */
1493 return rb_float_new(eps * INTERNAL2KCAL);
1494 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1501 * Get the "A" value for the 1-4 van der Waals parameter.
1504 static VALUE s_ParameterRef_GetA14(VALUE self) {
1507 up = s_UnionParFromValue(self, &tp, 0);
1508 if (tp == kVdwParType)
1509 return rb_float_new(up->vdw.A14);
1510 else if (tp == kVdwPairParType)
1511 return rb_float_new(up->vdwp.A14);
1512 else rb_raise(rb_eMolbyError, "invalid member A14");
1520 * Get the "B" value for the 1-4 van der Waals parameter.
1523 static VALUE s_ParameterRef_GetB14(VALUE self) {
1526 up = s_UnionParFromValue(self, &tp, 0);
1527 if (tp == kVdwParType)
1528 return rb_float_new(up->vdw.B14);
1529 else if (tp == kVdwPairParType)
1530 return rb_float_new(up->vdwp.B14);
1531 else rb_raise(rb_eMolbyError, "invalid member B14");
1539 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1541 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1544 /* Double a, b, r; */
1546 up = s_UnionParFromValue(self, &tp, 0);
1547 if (tp == kVdwParType) {
1551 } else if (tp == kVdwPairParType) {
1552 /* a = up->vdwp.A14;
1553 b = up->vdwp.B14; */
1554 r = up->vdwp.r_eq14;
1555 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1556 /* if (a == 0.0 || b == 0.0) */
1557 return rb_float_new(r);
1558 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1565 * Get the minimum energy for the 1-4 van der Waals parameter.
1567 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1572 up = s_UnionParFromValue(self, &tp, 0);
1573 if (tp == kVdwParType) {
1576 eps = up->vdw.eps14;
1577 } else if (tp == kVdwPairParType) {
1578 /* a = up->vdwp.A14;
1579 b = up->vdwp.B14; */
1580 eps = up->vdwp.eps14;
1581 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1582 /* if (a == 0.0 || b == 0.0) */
1583 return rb_float_new(eps * INTERNAL2KCAL);
1584 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1591 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1593 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1596 up = s_UnionParFromValue(self, &tp, 0);
1597 if (tp == kVdwCutoffParType)
1598 return rb_float_new(up->vdwcutoff.cutoff);
1599 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1606 * Get the atomic (covalent) radius for the element parameter.
1608 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1611 up = s_UnionParFromValue(self, &tp, 0);
1612 if (tp == kElementParType)
1613 return rb_float_new(up->atom.radius);
1614 else rb_raise(rb_eMolbyError, "invalid member radius");
1619 * vdw_radius -> Float
1621 * Get the van der Waals radius for the element parameter. (0 if not given)
1623 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1626 up = s_UnionParFromValue(self, &tp, 0);
1627 if (tp == kElementParType)
1628 return rb_float_new(up->atom.vdw_radius);
1629 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1634 * color -> [Float, Float, Float]
1636 * Get the rgb color for the element parameter.
1638 static VALUE s_ParameterRef_GetColor(VALUE self) {
1641 up = s_UnionParFromValue(self, &tp, 0);
1642 if (tp == kElementParType)
1643 return rb_ary_new3(3, rb_float_new(up->atom.red / 65535.0), rb_float_new(up->atom.green / 65535.0), rb_float_new(up->atom.blue / 65535.0));
1644 else rb_raise(rb_eMolbyError, "invalid member color");
1649 * atomic_number -> Integer
1651 * Get the atomic number for the vdw or element parameter.
1653 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1656 up = s_UnionParFromValue(self, &tp, 0);
1657 if (tp == kElementParType)
1658 return INT2NUM(up->atom.number);
1659 else if (tp == kVdwParType)
1660 return INT2NUM(up->vdw.atomicNumber);
1661 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1668 * Get the name for the element parameter.
1670 static VALUE s_ParameterRef_GetName(VALUE self) {
1673 up = s_UnionParFromValue(self, &tp, 0);
1674 if (tp == kElementParType) {
1676 strncpy(name, up->atom.name, 4);
1678 return rb_str_new2(name);
1679 } else rb_raise(rb_eMolbyError, "invalid member name");
1686 * Get the atomic weight for the element parameter.
1688 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1691 up = s_UnionParFromValue(self, &tp, 0);
1692 if (tp == kElementParType)
1693 return rb_float_new(up->atom.weight);
1694 else if (tp == kVdwParType)
1695 return rb_float_new(up->vdw.weight);
1696 else rb_raise(rb_eMolbyError, "invalid member weight");
1701 * fullname -> String
1703 * Get the full name for the element parameter.
1705 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1708 up = s_UnionParFromValue(self, &tp, 0);
1709 if (tp == kElementParType) {
1711 strncpy(fullname, up->atom.fullname, 15);
1713 return rb_str_new2(fullname);
1714 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1721 * Get the comment for the parameter.
1723 static VALUE s_ParameterRef_GetComment(VALUE self) {
1726 up = s_UnionParFromValue(self, &tp, 0);
1730 else return rb_str_new2(ParameterGetComment(com));
1737 * Get the source string for the parameter. Returns false for undefined parameter,
1738 * and nil for "local" parameter that is specific for the molecule.
1740 static VALUE s_ParameterRef_GetSource(VALUE self) {
1743 up = s_UnionParFromValue(self, &tp, 0);
1746 return Qfalse; /* undefined */
1748 return Qnil; /* local */
1749 else return rb_str_new2(ParameterGetComment(src));
1753 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1760 if (rb_obj_is_kind_of(val, rb_cString)) {
1761 char *s = StringValuePtr(val);
1763 for (i = 0; i < n; i++) {
1766 /* Skip leading separaters */
1767 while (*s == '-' || *s == ' ' || *s == '\t')
1769 for (p = s; *p != 0; p++) {
1770 if (*p == '-' || *p == ' ' || *p == '\t')
1774 if (len >= sizeof(buf))
1775 len = sizeof(buf) - 1;
1776 strncpy(buf, s, len);
1778 /* Skip trailing blanks */
1779 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1782 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1783 if (buf[0] >= '0' && buf[0] <= '9')
1784 types[i] = atoi(buf);
1786 types[i] = AtomTypeEncodeToUInt(buf);
1787 if (p == NULL || *p == 0) {
1793 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1796 val = rb_ary_to_ary(val);
1797 if (RARRAY_LEN(val) != n)
1798 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1799 valp = RARRAY_PTR(val);
1801 for (i = 0; i < n; i++) {
1802 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1803 types[i] = NUM2INT(rb_Integer(valp[i]));
1805 VALUE sval = valp[i];
1806 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1811 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1816 up = s_UnionParFromValue(self, &tp, 1);
1817 oldval = s_ParameterRef_GetAtomTypes(self);
1818 oldsrc = up->bond.src;
1821 s_ScanAtomTypes(val, 2, types);
1822 up->bond.type1 = types[0];
1823 up->bond.type2 = types[1];
1826 s_ScanAtomTypes(val, 3, types);
1827 up->angle.type1 = types[0];
1828 up->angle.type2 = types[1];
1829 up->angle.type3 = types[2];
1831 case kDihedralParType:
1832 case kImproperParType:
1833 s_ScanAtomTypes(val, 4, types);
1834 up->torsion.type1 = types[0];
1835 up->torsion.type2 = types[1];
1836 up->torsion.type3 = types[2];
1837 up->torsion.type4 = types[3];
1840 s_ScanAtomTypes(val, 1, types);
1841 up->vdw.type1 = types[0];
1843 case kVdwPairParType:
1844 s_ScanAtomTypes(val, 2, types);
1845 up->vdwp.type1 = types[0];
1846 up->vdwp.type2 = types[1];
1848 case kVdwCutoffParType:
1849 s_ScanAtomTypes(val, 2, types);
1850 up->vdwcutoff.type1 = types[0];
1851 up->vdwcutoff.type2 = types[1];
1856 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1860 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1862 Int tp, i, n, oldsrc;
1863 VALUE *valp, oldval;
1864 up = s_UnionParFromValue(self, &tp, 1);
1865 oldval = s_ParameterRef_GetK(self);
1866 oldsrc = up->bond.src;
1869 val = rb_Float(val);
1870 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1873 val = rb_Float(val);
1874 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1876 case kDihedralParType:
1877 case kImproperParType:
1878 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1879 up->torsion.mult = 1;
1880 val = rb_Float(val);
1881 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1884 n = up->torsion.mult;
1887 val = rb_ary_to_ary(val);
1888 if (RARRAY_LEN(val) != n)
1889 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1890 valp = RARRAY_PTR(val);
1891 for (i = 0; i < n; i++) {
1892 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1896 rb_raise(rb_eMolbyError, "invalid member k");
1898 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1902 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1906 up = s_UnionParFromValue(self, &tp, 1);
1907 oldval = s_ParameterRef_GetR0(self);
1908 oldsrc = up->bond.src;
1909 if (tp == kBondParType) {
1910 val = rb_Float(val);
1911 up->bond.r0 = NUM2DBL(val);
1912 } else rb_raise(rb_eMolbyError, "invalid member r0");
1913 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1917 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1921 up = s_UnionParFromValue(self, &tp, 1);
1922 oldval = s_ParameterRef_GetA0(self);
1923 oldsrc = up->bond.src;
1924 if (tp == kAngleParType) {
1925 val = rb_Float(val);
1926 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1927 } else rb_raise(rb_eMolbyError, "invalid member a0");
1928 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1932 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1936 up = s_UnionParFromValue(self, &tp, 1);
1937 oldval = s_ParameterRef_GetMult(self);
1938 oldsrc = up->bond.src;
1939 if (tp == kDihedralParType || tp == kImproperParType) {
1941 val = rb_Integer(val);
1944 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1945 up->torsion.mult = i;
1946 } else rb_raise(rb_eMolbyError, "invalid member mult");
1947 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1951 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1953 Int tp, i, n, oldsrc;
1954 VALUE *valp, oldval;
1955 up = s_UnionParFromValue(self, &tp, 1);
1956 oldval = s_ParameterRef_GetPeriod(self);
1957 oldsrc = up->bond.src;
1958 if (tp == kDihedralParType || tp == kImproperParType) {
1959 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1960 up->torsion.mult = 1;
1961 val = rb_Integer(val);
1962 up->torsion.period[0] = NUM2INT(val);
1964 n = up->torsion.mult;
1967 val = rb_ary_to_ary(val);
1968 if (RARRAY_LEN(val) != n)
1969 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1970 valp = RARRAY_PTR(val);
1971 for (i = 0; i < n; i++) {
1972 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1975 } else rb_raise(rb_eMolbyError, "invalid member period");
1976 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1980 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1982 Int tp, i, n, oldsrc;
1983 VALUE *valp, oldval;
1984 up = s_UnionParFromValue(self, &tp, 1);
1985 oldval = s_ParameterRef_GetPhi0(self);
1986 oldsrc = up->bond.src;
1987 if (tp == kDihedralParType || tp == kImproperParType) {
1988 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1989 up->torsion.mult = 1;
1990 val = rb_Float(val);
1991 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1993 n = up->torsion.mult;
1996 val = rb_ary_to_ary(val);
1997 if (RARRAY_LEN(val) != n)
1998 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1999 valp = RARRAY_PTR(val);
2000 for (i = 0; i < n; i++)
2001 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2003 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2004 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2009 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2014 up = s_UnionParFromValue(self, &tp, 1);
2015 oldval = s_ParameterRef_GetA(self);
2016 oldsrc = up->bond.src;
2017 val = rb_Float(val);
2019 if (tp == kVdwParType)
2021 else if (tp == kVdwPairParType)
2023 else rb_raise(rb_eMolbyError, "invalid member A");
2024 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2028 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2033 up = s_UnionParFromValue(self, &tp, 1);
2034 oldval = s_ParameterRef_GetB(self);
2035 oldsrc = up->bond.src;
2036 val = rb_Float(val);
2038 if (tp == kVdwParType)
2040 else if (tp == kVdwPairParType)
2042 else rb_raise(rb_eMolbyError, "invalid member B");
2043 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2048 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2053 up = s_UnionParFromValue(self, &tp, 1);
2054 oldval = s_ParameterRef_GetReq(self);
2055 oldsrc = up->bond.src;
2056 val = rb_Float(val);
2058 if (tp == kVdwParType) {
2060 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2061 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2062 } else if (tp == kVdwPairParType) {
2064 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2065 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2066 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2067 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2071 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2076 up = s_UnionParFromValue(self, &tp, 1);
2077 oldval = s_ParameterRef_GetEps(self);
2078 oldsrc = up->bond.src;
2079 val = rb_Float(val);
2080 e = NUM2DBL(val) * KCAL2INTERNAL;
2081 if (tp == kVdwParType) {
2083 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2084 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2085 } else if (tp == kVdwPairParType) {
2087 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2088 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2089 } else rb_raise(rb_eMolbyError, "invalid member eps");
2090 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2095 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2100 up = s_UnionParFromValue(self, &tp, 1);
2101 oldval = s_ParameterRef_GetA14(self);
2102 oldsrc = up->bond.src;
2103 val = rb_Float(val);
2105 if (tp == kVdwParType)
2107 else if (tp == kVdwPairParType)
2109 else rb_raise(rb_eMolbyError, "invalid member A14");
2110 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2114 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2119 up = s_UnionParFromValue(self, &tp, 1);
2120 oldval = s_ParameterRef_GetB14(self);
2121 oldsrc = up->bond.src;
2122 val = rb_Float(val);
2124 if (tp == kVdwParType)
2126 else if (tp == kVdwPairParType)
2128 else rb_raise(rb_eMolbyError, "invalid member B14");
2129 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2134 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2139 up = s_UnionParFromValue(self, &tp, 1);
2140 oldval = s_ParameterRef_GetReq14(self);
2141 oldsrc = up->bond.src;
2142 val = rb_Float(val);
2144 if (tp == kVdwParType) {
2146 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2147 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2148 } else if (tp == kVdwPairParType) {
2149 up->vdwp.r_eq14 = r;
2150 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2151 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2152 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2153 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2157 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2162 up = s_UnionParFromValue(self, &tp, 1);
2163 oldval = s_ParameterRef_GetEps14(self);
2164 oldsrc = up->bond.src;
2165 val = rb_Float(val);
2166 e = NUM2DBL(val) * KCAL2INTERNAL;
2167 if (tp == kVdwParType) {
2169 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2170 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2171 } else if (tp == kVdwPairParType) {
2173 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2174 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2175 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2176 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2180 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2184 oldval = s_ParameterRef_GetCutoff(self);
2185 oldsrc = up->bond.src;
2186 up = s_UnionParFromValue(self, &tp, 1);
2187 val = rb_Float(val);
2188 if (tp == kVdwCutoffParType) {
2189 up->vdwcutoff.cutoff = NUM2DBL(val);
2190 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2191 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2195 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2199 up = s_UnionParFromValue(self, &tp, 1);
2200 oldval = s_ParameterRef_GetRadius(self);
2201 oldsrc = up->bond.src;
2202 val = rb_Float(val);
2203 if (tp == kElementParType) {
2204 up->atom.radius = NUM2DBL(val);
2205 } else rb_raise(rb_eMolbyError, "invalid member radius");
2206 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2210 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2214 up = s_UnionParFromValue(self, &tp, 1);
2215 oldval = s_ParameterRef_GetVdwRadius(self);
2216 oldsrc = up->bond.src;
2217 val = rb_Float(val);
2218 if (tp == kElementParType) {
2219 up->atom.vdw_radius = NUM2DBL(val);
2220 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2221 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2225 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2228 VALUE *valp, oldval;
2229 up = s_UnionParFromValue(self, &tp, 1);
2230 oldval = s_ParameterRef_GetColor(self);
2231 oldsrc = up->bond.src;
2232 val = rb_ary_to_ary(val);
2233 if (RARRAY_LEN(val) != 3)
2234 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2235 valp = RARRAY_PTR(val);
2236 if (tp == kElementParType) {
2237 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2238 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2239 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2240 } else rb_raise(rb_eMolbyError, "invalid member color");
2241 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2245 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2249 up = s_UnionParFromValue(self, &tp, 1);
2250 oldval = s_ParameterRef_GetAtomicNumber(self);
2251 oldsrc = up->bond.src;
2252 val = rb_Integer(val);
2253 if (tp == kElementParType)
2254 up->atom.number = NUM2INT(val);
2255 else if (tp == kVdwParType) {
2256 up->vdw.atomicNumber = NUM2INT(val);
2257 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2258 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2259 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2263 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2267 up = s_UnionParFromValue(self, &tp, 1);
2268 oldval = s_ParameterRef_GetName(self);
2269 oldsrc = up->bond.src;
2270 if (tp == kElementParType) {
2271 strncpy(up->atom.name, StringValuePtr(val), 4);
2272 } else rb_raise(rb_eMolbyError, "invalid member name");
2273 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2277 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2281 val = rb_Float(val);
2282 oldval = s_ParameterRef_GetWeight(self);
2283 up = s_UnionParFromValue(self, &tp, 1);
2284 oldsrc = up->bond.src;
2285 if (tp == kElementParType)
2286 up->atom.weight = NUM2DBL(val);
2287 else if (tp == kVdwParType)
2288 up->vdw.weight = NUM2DBL(val);
2289 else rb_raise(rb_eMolbyError, "invalid member weight");
2290 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2294 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2298 up = s_UnionParFromValue(self, &tp, 1);
2299 oldval = s_ParameterRef_GetFullName(self);
2300 oldsrc = up->bond.src;
2301 if (tp == kElementParType) {
2302 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2303 up->atom.fullname[15] = 0;
2304 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2305 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2309 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2311 Int tp, com, oldsrc;
2313 up = s_UnionParFromValue(self, &tp, 1);
2314 oldval = s_ParameterRef_GetComment(self);
2315 oldsrc = up->bond.src;
2319 com = ParameterCommentIndex(StringValuePtr(val));
2322 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2326 /* Only false (undefined) and nil (local) can be set */
2327 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2331 up = s_UnionParFromValue(self, &tp, 1);
2332 if (val != Qfalse && val != Qnil)
2333 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2334 oldval = s_ParameterRef_GetSource(self);
2335 oldsrc = up->bond.src;
2336 if (oldsrc != 0 && oldsrc != -1)
2337 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2338 up->bond.src = (val == Qfalse ? -1 : 0);
2339 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2343 static struct s_ParameterAttrDef {
2345 VALUE *symref; /* Address of s_IndexSymbol etc. */
2346 ID id; /* Will be set within InitMolby() */
2347 VALUE (*getter)(VALUE);
2348 VALUE (*setter)(VALUE, VALUE);
2349 } s_ParameterAttrDefTable[] = {
2350 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2351 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2352 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2353 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2354 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2355 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2356 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2357 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2358 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2359 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2360 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2361 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2362 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2363 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2364 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2365 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2366 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2367 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2368 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2369 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2370 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2371 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2372 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2373 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2374 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2375 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2376 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2377 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2378 {NULL} /* Sentinel */
2382 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2386 if (TYPE(key) != T_SYMBOL) {
2387 kid = rb_intern(StringValuePtr(key));
2389 } else kid = SYM2ID(key);
2390 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2391 if (s_ParameterAttrDefTable[i].id == kid) {
2392 if (value == Qundef)
2393 return (*(s_ParameterAttrDefTable[i].getter))(self);
2394 else if (s_ParameterAttrDefTable[i].setter == NULL)
2395 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2397 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2400 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2401 return Qnil; /* not reached */
2405 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2407 return s_ParameterRef_SetAttr(self, key, Qundef);
2412 * keys(idx) -> array of valid parameter attributes
2414 * Returns an array of valid parameter attributes (as Symbols).
2417 s_ParameterRef_Keys(VALUE self)
2420 Data_Get_Struct(self, ParameterRef, pref);
2421 switch (pref->parType) {
2423 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2425 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2426 case kDihedralParType:
2427 case kImproperParType:
2428 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2430 return rb_ary_new3(11, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_AtomicNumberSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_WeightSym, s_CommentSym, s_SourceSym);
2431 case kVdwPairParType:
2432 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2433 case kVdwCutoffParType:
2434 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2435 case kElementParType:
2436 return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_VdwRadiusSym, s_CommentSym, s_SourceSym);
2438 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2440 return Qnil; /* Not reached */
2445 * to_hash(idx) -> Hash
2447 * Returns a hash containing valid parameter names and values
2450 s_ParameterRef_ToHash(VALUE self)
2452 VALUE keys = s_ParameterRef_Keys(self);
2457 retval = rb_hash_new();
2458 for (i = 0; i < RARRAY_LEN(keys); i++) {
2459 VALUE key = RARRAY_PTR(keys)[i];
2460 VALUE val = s_ParameterRef_GetAttr(self, key);
2461 rb_hash_aset(retval, key, val);
2468 * parameter.to_s(idx) -> String
2470 * Returns a string representation of the given parameter
2473 s_ParameterRef_ToString(VALUE self)
2476 char buf[1024], types[4][8];
2477 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2480 snprintf(buf, sizeof buf, "bond %4.6s %4.6s %8.2f %8.3f", AtomTypeDecodeToString(up->bond.type1, types[0]), AtomTypeDecodeToString(up->bond.type2, types[1]), up->bond.k * INTERNAL2KCAL, up->bond.r0);
2483 snprintf(buf, sizeof buf, "angle %4.6s %4.6s %4.6s %8.2f %8.3f", AtomTypeDecodeToString(up->angle.type1, types[0]), AtomTypeDecodeToString(up->angle.type2, types[1]), AtomTypeDecodeToString(up->angle.type3, types[2]), up->angle.k * INTERNAL2KCAL, up->angle.a0 * kRad2Deg);
2485 case kDihedralParType:
2486 case kImproperParType:
2487 snprintf(buf, sizeof buf, "%s %4.6s %4.6s %4.6s %4.6s", (tp == kDihedralParType ? "dihe" : "impr"), AtomTypeDecodeToString(up->torsion.type1, types[0]), AtomTypeDecodeToString(up->torsion.type2, types[1]), AtomTypeDecodeToString(up->torsion.type3, types[2]), AtomTypeDecodeToString(up->torsion.type4, types[3]));
2489 for (i = 0; i < up->torsion.mult; i++) {
2490 snprintf(buf + n, sizeof buf - n, " %8.2f %2d %8.3f", up->torsion.k[i] * INTERNAL2KCAL, up->torsion.period[i], up->torsion.phi0[i] * kRad2Deg);
2495 snprintf(buf, sizeof buf, "nonbonded %4.6s %8.4f %8.4f %8.4f %8.4f", AtomTypeDecodeToString(up->vdw.type1, types[0]), up->vdw.A * INTERNAL2KCAL / pow(up->vdw.r_eq, 12.0), up->vdw.r_eq / 1.12246204830937, up->vdw.A14 * INTERNAL2KCAL / pow(up->vdw.r_eq14, 12.0), up->vdw.r_eq14 / 1.12246204830937);
2497 case kVdwPairParType:
2498 snprintf(buf, sizeof buf, "nbfi %4.6s %4.6s %12.8e %12.8e %12.8e %12.8e", AtomTypeDecodeToString(up->vdwp.type1, types[0]), AtomTypeDecodeToString(up->vdwp.type2, types[1]), up->vdwp.A * INTERNAL2KCAL, up->vdwp.B * INTERNAL2KCAL, up->vdwp.A14 * INTERNAL2KCAL, up->vdwp.B14 * INTERNAL2KCAL);
2500 case kVdwCutoffParType:
2501 snprintf(buf, sizeof buf, "vdwcutoff %4.6s %4.6s %8.4f", AtomTypeDecodeToString(up->vdwcutoff.type1, types[0]), AtomTypeDecodeToString(up->vdwcutoff.type2, types[1]), up->vdwcutoff.cutoff);
2503 case kElementParType:
2504 snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s %6.3f", up->atom.name, up->atom.number, up->atom.radius, up->atom.red / 65535.0, up->atom.green / 65535.0, up->atom.blue / 65535.0, up->atom.weight, up->atom.fullname, up->atom.vdw_radius);
2507 return rb_str_new2(buf);
2512 * self == parameterRef -> boolean
2514 * True if the parameters point to the same parameter record.
2517 s_ParameterRef_Equal(VALUE self, VALUE val)
2520 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2521 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2522 } else return Qfalse;
2525 #pragma mark ====== Parameter Class ======
2527 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2528 * is NULL, then the global parameters are looked for. */
2530 /* Rebuild the MD parameter record if necessary: may throw an exception */
2531 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2533 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2536 Data_Get_Struct(val, Molecule, mol);
2538 rb_raise(rb_eMolbyError, "the molecule is empty");
2539 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2540 /* Do self.md_arena.prepare */
2541 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2543 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2548 s_NewParameterValueFromValue(VALUE val)
2551 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2552 Data_Get_Struct(val, Molecule, mol);
2553 s_RebuildMDParameterIfNecessary(val, Qtrue);
2554 MoleculeRetain(mol);
2555 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2558 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2563 s_MoleculeFromParameterValue(VALUE val)
2566 Data_Get_Struct(val, Molecule, mol);
2571 s_ParameterFromParameterValue(VALUE val)
2574 Data_Get_Struct(val, Molecule, mol);
2577 return gBuiltinParameters;
2580 /* Forward declarations */
2581 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2582 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2585 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2587 if (val == rb_cParameter) {
2588 return NULL; /* Parameter class method: builtin parameters */
2589 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2590 return s_MoleculeFromParameterValue(val);
2591 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2592 return s_MoleculeFromParEnumerableValue(val);
2598 * builtin -> Parameter
2600 * Returns a parameter value that points to the global (builtin) parameters.
2601 * Equivalent to Parameter::Builtin (constant).
2604 s_Parameter_Builtin(VALUE self)
2606 static ID s_builtin_id = 0;
2607 if (s_builtin_id == 0)
2608 s_builtin_id = rb_intern("Builtin");
2609 return rb_const_get(rb_cParameter, s_builtin_id);
2614 * bond(idx) -> ParameterRef
2616 * The index-th bond parameter record is returned.
2619 s_Parameter_Bond(VALUE self, VALUE ival)
2623 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2624 idx = NUM2INT(rb_Integer(ival));
2626 n = gBuiltinParameters->nbondPars;
2627 else if (mol->par != NULL)
2628 n = mol->par->nbondPars;
2630 if (idx < -n || idx >= n)
2631 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2634 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2639 * angle(idx) -> ParameterRef
2641 * The index-th angle parameter record is returned.
2644 s_Parameter_Angle(VALUE self, VALUE ival)
2648 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2649 idx = NUM2INT(rb_Integer(ival));
2651 n = gBuiltinParameters->nanglePars;
2652 else if (mol->par != NULL)
2653 n = mol->par->nanglePars;
2655 if (idx < -n || idx >= n)
2656 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2659 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2664 * dihedral(idx) -> ParameterRef
2666 * The index-th dihedral parameter record is returned.
2669 s_Parameter_Dihedral(VALUE self, VALUE ival)
2673 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2674 idx = NUM2INT(rb_Integer(ival));
2676 n = gBuiltinParameters->ndihedralPars;
2677 else if (mol->par != NULL)
2678 n = mol->par->ndihedralPars;
2680 if (idx < -n || idx >= n)
2681 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2684 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2689 * improper(idx) -> ParameterRef
2691 * The index-th improper parameter record is returned.
2694 s_Parameter_Improper(VALUE self, VALUE ival)
2698 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2699 idx = NUM2INT(rb_Integer(ival));
2701 n = gBuiltinParameters->nimproperPars;
2702 else if (mol->par != NULL)
2703 n = mol->par->nimproperPars;
2705 if (idx < -n || idx >= n)
2706 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2709 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2714 * vdw(idx) -> ParameterRef
2716 * The index-th vdw parameter record is returned.
2719 s_Parameter_Vdw(VALUE self, VALUE ival)
2723 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2724 idx = NUM2INT(rb_Integer(ival));
2726 n = gBuiltinParameters->nvdwPars;
2727 else if (mol->par != NULL)
2728 n = mol->par->nvdwPars;
2730 if (idx < -n || idx >= n)
2731 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2734 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2739 * vdw_pair(idx) -> ParameterRef
2741 * The index-th vdw pair parameter record is returned.
2744 s_Parameter_VdwPair(VALUE self, VALUE ival)
2748 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2749 idx = NUM2INT(rb_Integer(ival));
2751 n = gBuiltinParameters->nvdwpPars;
2752 else if (mol->par != NULL)
2753 n = mol->par->nvdwpPars;
2755 if (idx < -n || idx >= n)
2756 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2759 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2764 * vdw_cutoff(idx) -> ParameterRef
2766 * The index-th vdw cutoff parameter record is returned.
2769 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2773 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2774 idx = NUM2INT(rb_Integer(ival));
2776 n = gBuiltinParameters->nvdwCutoffPars;
2777 else if (mol->par != NULL)
2778 n = mol->par->nvdwCutoffPars;
2780 if (idx < -n || idx >= n)
2781 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2784 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2789 * element(idx) -> ParameterRef
2790 * element(t1) -> ParameterRef
2792 * In the first form, the index-th element parameter record is returned. In the second
2793 * form, the element parameter for t1 is looked up (the last index first). t1
2794 * is the element name string (up to 4 characters).
2795 * Unlike other Parameter methods, this is used only for the global parameter.
2798 s_Parameter_Element(VALUE self, VALUE ival)
2801 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2802 int n = gCountElementParameters;
2803 idx1 = NUM2INT(rb_Integer(ival));
2804 if (idx1 < -n || idx1 >= n)
2805 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2808 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2813 strncpy(name, StringValuePtr(ival), 4);
2815 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2816 if (strncmp(ep->name, name, 4) == 0)
2817 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2827 * Returns the number of bond parameters.
2830 s_Parameter_Nbonds(VALUE self)
2833 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2835 n = gBuiltinParameters->nbondPars;
2836 else if (mol->par != NULL)
2837 n = mol->par->nbondPars;
2844 * nangles -> Integer
2846 * Returns the number of angle parameters.
2849 s_Parameter_Nangles(VALUE self)
2852 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2854 n = gBuiltinParameters->nanglePars;
2855 else if (mol->par != NULL)
2856 n = mol->par->nanglePars;
2863 * ndihedrals -> Integer
2865 * Returns the number of dihedral parameters.
2868 s_Parameter_Ndihedrals(VALUE self)
2871 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2873 n = gBuiltinParameters->ndihedralPars;
2874 else if (mol->par != NULL)
2875 n = mol->par->ndihedralPars;
2882 * nimpropers -> Integer
2884 * Returns the number of improper parameters.
2887 s_Parameter_Nimpropers(VALUE self)
2890 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2892 n = gBuiltinParameters->nimproperPars;
2893 else if (mol->par != NULL)
2894 n = mol->par->nimproperPars;
2903 * Returns the number of vdw parameters.
2906 s_Parameter_Nvdws(VALUE self)
2909 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2911 n = gBuiltinParameters->nvdwPars;
2912 else if (mol->par != NULL)
2913 n = mol->par->nvdwPars;
2920 * nvdw_pairs -> Integer
2922 * Returns the number of vdw pair parameters.
2925 s_Parameter_NvdwPairs(VALUE self)
2928 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2930 n = gBuiltinParameters->nvdwpPars;
2931 else if (mol->par != NULL)
2932 n = mol->par->nvdwpPars;
2939 * nvdw_cutoffs -> Integer
2941 * Returns the number of vdw cutoff parameters.
2944 s_Parameter_NvdwCutoffs(VALUE self)
2947 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2949 n = gBuiltinParameters->nvdwCutoffPars;
2950 else if (mol->par != NULL)
2951 n = mol->par->nvdwCutoffPars;
2958 * nelements -> Integer
2960 * Returns the number of element parameters.
2963 s_Parameter_Nelements(VALUE self)
2965 return INT2NUM(gCountElementParameters);
2970 * bonds -> ParEnumerable
2972 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2973 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2974 * useful when all accessible parameters should be examined by use of 'each' method.
2977 s_Parameter_Bonds(VALUE self)
2979 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2980 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2985 * angles -> ParEnumerable
2987 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2988 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2989 * useful when all accessible parameters should be examined by use of 'each' method.
2992 s_Parameter_Angles(VALUE self)
2994 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2995 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3000 * dihedrals -> ParEnumerable
3002 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3003 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3004 * useful when all accessible parameters should be examined by use of 'each' method.
3007 s_Parameter_Dihedrals(VALUE self)
3009 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3010 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3015 * impropers -> ParEnumerable
3017 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3018 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3019 * useful when all accessible parameters should be examined by use of 'each' method.
3022 s_Parameter_Impropers(VALUE self)
3024 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3025 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3030 * vdws -> ParEnumerable
3032 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3033 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3034 * useful when all accessible parameters should be examined by use of 'each' method.
3037 s_Parameter_Vdws(VALUE self)
3039 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3040 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3045 * vdw_pairs -> ParEnumerable
3047 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3048 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3049 * useful when all accessible parameters should be examined by use of 'each' method.
3052 s_Parameter_VdwPairs(VALUE self)
3054 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3055 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3060 * vdw_cutoffs -> ParEnumerable
3062 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3063 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3064 * useful when all accessible parameters should be examined by use of 'each' method.
3067 s_Parameter_VdwCutoffs(VALUE self)
3069 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3070 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3075 * elements -> ParEnumerable
3077 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3078 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3079 * useful when all accessible parameters should be examined by use of 'each' method.
3082 s_Parameter_Elements(VALUE self)
3084 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3085 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3089 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3091 VALUE atval, optval;
3094 int i, n, idx, flags, is_global;
3096 rb_scan_args(argc, argv, "1*", &atval, &optval);
3098 /* Get the atom types */
3100 case kBondParType: n = 2; break;
3101 case kAngleParType: n = 3; break;
3102 case kDihedralParType: n = 4; break;
3103 case kImproperParType: n = 4; break;
3104 case kVdwParType: n = 1; break;
3105 case kVdwPairParType: n = 2; break;
3106 default: return Qnil;
3108 s_ScanAtomTypes(atval, n, t);
3109 for (i = 0; i < n; i++) {
3110 if (t[i] < kAtomTypeMinimum) {
3111 /* Explicit atom index */
3113 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3114 if (t[i] >= mol->natoms)
3115 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3117 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3121 /* Analyze options */
3123 n = RARRAY_LEN(optval);
3124 for (i = 0; i < n; i++) {
3125 VALUE oval = RARRAY_PTR(optval)[i];
3126 if (oval == ID2SYM(rb_intern("global")))
3127 flags |= kParameterLookupGlobal;
3128 else if (oval == ID2SYM(rb_intern("local")))
3129 flags |= kParameterLookupLocal;
3130 else if (oval == ID2SYM(rb_intern("missing")))
3131 flags |= kParameterLookupMissing;
3132 else if (oval == ID2SYM(rb_intern("nowildcard")))
3133 flags |= kParameterLookupNoWildcard;
3134 else if (oval == ID2SYM(rb_intern("nobasetype")))
3135 flags |= kParameterLookupNoBaseAtomType;
3136 else if (oval == ID2SYM(rb_intern("create")))
3140 flags = kParameterLookupGlobal | kParameterLookupLocal;
3145 case kBondParType: {
3148 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3150 idx = bp - mol->par->bondPars;
3154 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3156 idx = bp - gBuiltinParameters->bondPars;
3161 case kAngleParType: {
3164 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3166 idx = ap - mol->par->anglePars;
3170 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3172 idx = ap - gBuiltinParameters->anglePars;
3177 case kDihedralParType: {
3180 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3182 idx = tp - mol->par->dihedralPars;
3186 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3188 idx = tp - gBuiltinParameters->dihedralPars;
3193 case kImproperParType: {
3196 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3198 idx = tp - mol->par->improperPars;
3202 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3204 idx = tp - gBuiltinParameters->improperPars;
3212 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3214 idx = vp - mol->par->vdwPars;
3218 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3220 idx = vp - gBuiltinParameters->vdwPars;
3225 case kVdwPairParType: {
3228 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3230 idx = vp - mol->par->vdwpPars;
3234 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3236 idx = vp - gBuiltinParameters->vdwpPars;
3245 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3248 /* Insert a new parameter record */
3250 Int count = ParameterGetCountForType(mol->par, parType);
3251 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3252 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3253 IntGroupRelease(ig);
3256 /* Set atom types */
3257 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3262 up->bond.type1 = t[0];
3263 up->bond.type2 = t[1];
3266 up->angle.type1 = t[0];
3267 up->angle.type2 = t[1];
3268 up->angle.type3 = t[2];
3270 case kDihedralParType:
3271 case kImproperParType:
3272 up->torsion.type1 = t[0];
3273 up->torsion.type2 = t[1];
3274 up->torsion.type3 = t[2];
3275 up->torsion.type4 = t[3];
3278 up->vdw.type1 = t[0];
3280 case kVdwPairParType:
3281 up->vdwp.type1 = t[0];
3282 up->vdwp.type2 = t[1];
3289 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3294 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3295 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3297 * Find the parameter record that matches the given atom types. The atom types are given
3298 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3299 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3300 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3301 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3304 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3307 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3309 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3310 parType = s_ParTypeFromValue(argv[0]);
3311 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3316 * self == parameter -> boolean
3318 * True if the parameters point to the same parameter table.
3321 s_Parameter_Equal(VALUE self, VALUE val)
3323 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3324 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3325 } else return Qfalse;
3328 #pragma mark ====== ParEnumerable Class ======
3330 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3331 and the parameter type. If the Molecule is NULL, then it refers to the
3332 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3333 the global parameters are always accessible. */
3335 typedef struct ParEnumerable {
3337 Int parType; /* Same as parType in ParameterRef */
3340 static ParEnumerable *
3341 s_ParEnumerableNew(Molecule *mol, Int parType)
3343 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3347 MoleculeRetain(mol);
3348 pen->parType = parType;
3354 s_ParEnumerableRelease(ParEnumerable *pen)
3357 if (pen->mol != NULL)
3358 MoleculeRelease(pen->mol);
3364 s_MoleculeFromParEnumerableValue(VALUE val)
3367 Data_Get_Struct(val, ParEnumerable, pen);
3372 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3374 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3376 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3377 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3382 * par_type -> String
3384 * Get the parameter type, like "bond", "angle", etc.
3387 s_ParEnumerable_ParType(VALUE self) {
3390 Data_Get_Struct(self, ParEnumerable, pen);
3392 if (tp == kElementParType)
3393 return rb_str_new2("element");
3394 tp -= kFirstParType;
3395 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3396 return rb_str_new2(s_ParameterTypeNames[tp]);
3397 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3402 * self[idx] -> ParameterRef
3404 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3405 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3406 * parent Parameter object of self.
3408 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3409 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3412 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3415 Data_Get_Struct(self, ParEnumerable, pen);
3416 switch (pen->parType) {
3417 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3418 case kBondParType: return s_Parameter_Bond(self, ival);
3419 case kAngleParType: return s_Parameter_Angle(self, ival);
3420 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3421 case kImproperParType: return s_Parameter_Improper(self, ival);
3422 case kVdwParType: return s_Parameter_Vdw(self, ival);
3423 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3424 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3425 case kElementParType: return s_Parameter_Element(self, ival);
3427 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3429 return Qnil; /* Not reached */
3436 * Returns the number of parameters included in this enumerable.
3439 s_ParEnumerable_Length(VALUE self)
3442 Data_Get_Struct(self, ParEnumerable, pen);
3443 switch (pen->parType) {
3444 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3445 case kBondParType: return s_Parameter_Nbonds(self);
3446 case kAngleParType: return s_Parameter_Nangles(self);
3447 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3448 case kImproperParType: return s_Parameter_Nimpropers(self);
3449 case kVdwParType: return s_Parameter_Nvdws(self);
3450 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3451 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3452 case kElementParType: return s_Parameter_Nelements(self);
3454 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3456 return Qnil; /* Not reached */
3463 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3466 s_ParEnumerable_Each(VALUE self)
3472 Data_Get_Struct(self, ParEnumerable, pen);
3473 if (pen->parType == kElementParType)
3474 n = gCountElementParameters;
3476 switch (pen->parType) {
3477 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3478 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3479 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3480 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3481 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3482 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3483 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3485 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3487 if (pen->mol == NULL)
3488 n = *((Int *)((char *)gBuiltinParameters + ofs));
3489 else if (pen->mol->par != NULL)
3490 n = *((Int *)((char *)(pen->mol->par) + ofs));
3493 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3494 Data_Get_Struct(aval, ParameterRef, pref);
3495 for (i = 0; i < n; i++) {
3504 * reverse_each {|pref| ...}
3506 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3509 s_ParEnumerable_ReverseEach(VALUE self)
3515 Data_Get_Struct(self, ParEnumerable, pen);
3516 if (pen->parType == kElementParType)
3517 n = gCountElementParameters;
3519 switch (pen->parType) {
3520 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3521 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3522 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3523 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3524 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3525 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3526 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3528 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3530 if (pen->mol == NULL)
3531 n = *((Int *)((char *)gBuiltinParameters + ofs));
3532 else if (pen->mol->par != NULL)
3533 n = *((Int *)((char *)(pen->mol->par) + ofs));
3536 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3537 Data_Get_Struct(aval, ParameterRef, pref);
3538 for (i = n - 1; i >= 0; i--) {
3547 * insert(idx = nil, pref = nil) -> ParameterRef
3549 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3550 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3551 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3552 * parameter is left undefined.
3553 * Throws an exception if ParEnumerable points to the global parameter.
3556 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3564 Data_Get_Struct(self, ParEnumerable, pen);
3565 if (pen->mol == NULL)
3566 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3567 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3568 rb_scan_args(argc, argv, "02", &ival, &pval);
3570 i = NUM2INT(rb_Integer(ival));
3572 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3577 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3578 if (up == NULL || type != pen->parType)
3579 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3580 ParameterCopyOneWithType(&u, up, pen->parType);
3583 memset(&u, 0, sizeof(u));
3586 ig = IntGroupNewWithPoints(n, 1, -1);
3587 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3589 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3590 MolActionCallback_registerUndo(pen->mol, act);
3591 MolActionRelease(act);
3593 IntGroupRelease(ig);
3594 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3602 * Delete the parameter(s) specified by the argument.
3605 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3610 Data_Get_Struct(self, ParEnumerable, pen);
3611 if (pen->mol == NULL)
3612 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3613 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3614 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3615 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3618 ig = IntGroupFromValue(ival);
3619 if ((i = IntGroupGetCount(ig)) == 0) {
3620 IntGroupRelease(ig);
3624 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3625 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3628 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3629 IntGroupRelease(ig);
3635 * lookup(atom_types, options, ...) -> ParameterRef
3636 * lookup(atom_type_string, options, ...) -> ParameterRef
3638 * Find the parameter record that matches the given atom types. The arguments are
3639 * the same as Parameter#lookup, except for the parameter type which is implicitly
3643 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3646 Data_Get_Struct(self, ParEnumerable, pen);
3647 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3652 * self == parEnumerable -> boolean
3654 * True if the arguments point to the same parameter table and type.
3657 s_ParEnumerable_Equal(VALUE self, VALUE val)
3659 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3660 ParEnumerable *pen1, *pen2;
3661 Data_Get_Struct(self, ParEnumerable, pen1);
3662 Data_Get_Struct(val, ParEnumerable, pen2);
3663 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3664 } else return Qfalse;
3667 #pragma mark ====== AtomRef Class ======
3669 /* Forward declaration for register undo */
3670 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3672 /* Ruby string "set_atom_attr" */
3673 static VALUE s_SetAtomAttrString;
3676 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3680 Data_Get_Struct(self, AtomRef, aref);
3681 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3682 if (idx < 0 || idx >= aref->mol->natoms)
3683 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3685 *app = aref->mol->atoms + idx;
3692 s_AtomFromValue(VALUE self)
3695 s_AtomIndexFromValue(self, &ap, NULL);
3700 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3703 s_AtomIndexFromValue(self, &ap, mpp);
3708 s_NotifyModificationForAtomRef(VALUE self)
3711 Data_Get_Struct(self, AtomRef, aref);
3712 MoleculeIncrementModifyCount(aref->mol);
3716 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3719 Data_Get_Struct(self, AtomRef, aref);
3720 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3723 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3724 MolActionCallback_registerUndo(aref->mol, act);
3725 MoleculeCallback_notifyModification(aref->mol, 0);
3726 /* Request MD rebuilt if necessary */
3727 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3728 aref->mol->needsMDRebuild = 1;
3733 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3736 aref = AtomRefNew(mol, idx);
3737 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3741 s_AtomRef_GetMolecule(VALUE self)
3744 s_AtomIndexFromValue(self, NULL, &mpp);
3745 return ValueFromMolecule(mpp);
3748 static VALUE s_AtomRef_GetIndex(VALUE self) {
3749 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3752 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3753 return INT2NUM(s_AtomFromValue(self)->segSeq);
3756 static VALUE s_AtomRef_GetSegName(VALUE self) {
3757 char *p = s_AtomFromValue(self)->segName;
3758 return rb_str_new(p, strlen_limit(p, 4));
3761 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3762 return INT2NUM(s_AtomFromValue(self)->resSeq);
3765 static VALUE s_AtomRef_GetResName(VALUE self) {
3766 char *p = s_AtomFromValue(self)->resName;
3767 return rb_str_new(p, strlen_limit(p, 4));
3770 static VALUE s_AtomRef_GetName(VALUE self) {
3771 char *p = s_AtomFromValue(self)->aname;
3772 return rb_str_new(p, strlen_limit(p, 4));
3775 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3776 int type = s_AtomFromValue(self)->type;
3777 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3778 return rb_str_new(p, strlen_limit(p, 6));
3781 static VALUE s_AtomRef_GetCharge(VALUE self) {
3782 return rb_float_new(s_AtomFromValue(self)->charge);
3785 static VALUE s_AtomRef_GetWeight(VALUE self) {
3786 return rb_float_new(s_AtomFromValue(self)->weight);
3789 static VALUE s_AtomRef_GetElement(VALUE self) {
3790 char *p = s_AtomFromValue(self)->element;
3791 return rb_str_new(p, strlen_limit(p, 4));
3794 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3795 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3798 static VALUE s_AtomRef_GetConnects(VALUE self) {
3801 Atom *ap = s_AtomFromValue(self);
3802 retval = rb_ary_new();
3803 cp = AtomConnectData(&ap->connect);
3804 for (i = 0; i < ap->connect.count; i++)
3805 rb_ary_push(retval, INT2NUM(cp[i]));
3809 static VALUE s_AtomRef_GetR(VALUE self) {
3810 return ValueFromVector(&(s_AtomFromValue(self)->r));
3813 static VALUE s_AtomRef_GetX(VALUE self) {
3814 return rb_float_new(s_AtomFromValue(self)->r.x);
3817 static VALUE s_AtomRef_GetY(VALUE self) {
3818 return rb_float_new(s_AtomFromValue(self)->r.y);
3821 static VALUE s_AtomRef_GetZ(VALUE self) {
3822 return rb_float_new(s_AtomFromValue(self)->r.z);
3825 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3829 s_AtomIndexFromValue(self, &ap, &mp);
3831 if (mp->cell != NULL)
3832 TransformVec(&r1, mp->cell->rtr, &r1);
3836 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3837 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3838 return ValueFromVector(&r1);
3841 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3842 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3845 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3846 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3849 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3850 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3853 static VALUE s_AtomRef_GetSigma(VALUE self) {
3854 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3857 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3858 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3861 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3862 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3865 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3866 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3869 static VALUE s_AtomRef_GetV(VALUE self) {
3870 return ValueFromVector(&(s_AtomFromValue(self)->v));
3873 static VALUE s_AtomRef_GetF(VALUE self) {
3874 Vector v = s_AtomFromValue(self)->f;
3875 VecScaleSelf(v, INTERNAL2KCAL);
3876 return ValueFromVector(&v);
3879 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3880 return rb_float_new(s_AtomFromValue(self)->occupancy);
3883 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3884 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3887 static VALUE s_AtomRef_GetAniso(VALUE self) {
3890 Atom *ap = s_AtomFromValue(self);
3891 if (ap->aniso == NULL)
3893 retval = rb_ary_new();
3894 for (i = 0; i < 6; i++)
3895 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3896 if (ap->aniso->has_bsig) {
3897 rb_ary_push(retval, INT2NUM(0));
3898 for (i = 0; i < 6; i++)
3899 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3904 static VALUE s_AtomRef_GetSymop(VALUE self) {
3906 Atom *ap = s_AtomFromValue(self);
3907 if (!ap->symop.alive)
3909 retval = rb_ary_new();
3910 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3911 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3912 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3913 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3914 rb_ary_push(retval, INT2NUM(ap->symbase));
3918 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3919 return INT2NUM(s_AtomFromValue(self)->intCharge);
3922 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3923 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3926 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3927 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3930 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3934 MDExclusion *exinfo;
3937 idx = s_AtomIndexFromValue(self, &ap, &mol);
3938 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3939 VALUE mval = ValueFromMolecule(mol);
3940 s_RebuildMDParameterIfNecessary(mval, Qnil);
3942 if (mol->arena->exinfo == NULL)
3944 exinfo = mol->arena->exinfo + idx;
3945 exlist = mol->arena->exlist;
3946 retval = rb_ary_new();
3947 aval = rb_ary_new();
3948 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3949 rb_ary_push(aval, INT2NUM(exlist[i]));
3950 rb_ary_push(retval, aval);
3951 aval = rb_ary_new();
3952 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3953 rb_ary_push(aval, INT2NUM(exlist[i]));
3954 rb_ary_push(retval, aval);
3955 aval = rb_ary_new();
3956 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3957 rb_ary_push(aval, INT2NUM(exlist[i]));
3958 rb_ary_push(retval, aval);
3962 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3963 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3966 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3967 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3970 static VALUE s_AtomRef_GetHidden(VALUE self) {
3971 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3974 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3977 Atom *ap = s_AtomFromValue(self);
3978 if (ap->anchor == NULL)
3980 count = ap->anchor->connect.count;
3981 retval = rb_ary_new2(count * 2);
3982 cp = AtomConnectData(&ap->anchor->connect);
3983 for (i = 0; i < count; i++) {
3984 rb_ary_store(retval, i, INT2NUM(cp[i]));
3985 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3990 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3991 char *p = s_AtomFromValue(self)->uff_type;
3992 return rb_str_new(p, strlen_limit(p, 5));
3995 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3996 rb_raise(rb_eMolbyError, "index cannot be directly set");
4000 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4001 VALUE oval = s_AtomRef_GetSegSeq(self);
4002 val = rb_Integer(val);
4003 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4004 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4008 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4009 char *p = StringValuePtr(val);
4010 VALUE oval = s_AtomRef_GetSegName(self);
4011 strncpy(s_AtomFromValue(self)->segName, p, 4);
4012 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4016 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4017 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4018 return val; /* Not reached */
4021 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4022 Atom *ap = s_AtomFromValue(self);
4023 char *p = StringValuePtr(val);
4024 VALUE oval = s_AtomRef_GetName(self);
4025 if (ap->anchor != NULL && p[0] == '_')
4026 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4027 strncpy(ap->aname, p, 4);
4028 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4032 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4034 char *p = StringValuePtr(val);
4035 VALUE oval = s_AtomRef_GetAtomType(self);
4036 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4037 if (type != 0 && type < kAtomTypeMinimum)
4038 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4039 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4040 mp->needsMDRebuild = 1;
4041 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4045 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4047 VALUE oval = s_AtomRef_GetCharge(self);
4048 val = rb_Float(val);
4049 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4050 mp->needsMDRebuild = 1;
4051 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4055 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4057 VALUE oval = s_AtomRef_GetWeight(self);
4058 val = rb_Float(val);
4059 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4060 mp->needsMDRebuild = 1;
4061 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4065 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4068 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4069 char *p = StringValuePtr(val);
4070 VALUE oval = s_AtomRef_GetElement(self);
4071 ap->atomicNumber = ElementToInt(p);
4072 ElementToString(ap->atomicNumber, ap->element);
4073 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4075 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4076 mp->needsMDRebuild = 1;
4080 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4083 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4084 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4085 val = rb_Integer(val);
4086 ap->atomicNumber = NUM2INT(val);
4087 ElementToString(ap->atomicNumber, ap->element);
4088 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4090 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4091 mp->needsMDRebuild = 1;
4095 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4096 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4097 return val; /* Not reached */
4100 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4103 VALUE oval = s_AtomRef_GetR(self);
4104 VectorFromValue(val, &v);
4105 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4106 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4107 mp->needsMDCopyCoordinates = 1;
4111 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4114 VALUE oval = s_AtomRef_GetX(self);
4115 val = rb_Float(val);
4117 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4118 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4119 mp->needsMDCopyCoordinates = 1;
4123 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4126 VALUE oval = s_AtomRef_GetY(self);
4127 val = rb_Float(val);
4129 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4130 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4131 mp->needsMDCopyCoordinates = 1;
4135 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4138 VALUE oval = s_AtomRef_GetZ(self);
4139 val = rb_Float(val);
4141 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4142 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4143 mp->needsMDCopyCoordinates = 1;
4147 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4151 s_AtomIndexFromValue(self, &ap, &mp);
4153 VectorFromValue(val, &v);
4154 if (mp->cell != NULL)
4155 TransformVec(&v, mp->cell->tr, &v);
4157 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4158 mp->needsMDCopyCoordinates = 1;
4162 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4167 s_AtomIndexFromValue(self, &ap, &mp);
4169 val = rb_Float(val);
4171 if (mp->cell != NULL) {
4172 TransformVec(&v, mp->cell->rtr, &v);
4174 TransformVec(&v, mp->cell->tr, &v);
4177 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4178 mp->needsMDCopyCoordinates = 1;
4182 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4187 s_AtomIndexFromValue(self, &ap, &mp);
4189 val = rb_Float(val);
4191 if (mp->cell != NULL) {
4192 TransformVec(&v, mp->cell->rtr, &v);
4194 TransformVec(&v, mp->cell->tr, &v);
4197 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4198 mp->needsMDCopyCoordinates = 1;
4202 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4207 s_AtomIndexFromValue(self, &ap, &mp);
4209 val = rb_Float(val);
4211 if (mp->cell != NULL) {
4212 TransformVec(&v, mp->cell->rtr, &v);
4214 TransformVec(&v, mp->cell->tr, &v);
4217 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4218 mp->needsMDCopyCoordinates = 1;
4222 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4225 VALUE oval = s_AtomRef_GetSigma(self);
4226 VectorFromValue(val, &v);
4227 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4228 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4229 mp->needsMDCopyCoordinates = 1;
4233 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4236 VALUE oval = s_AtomRef_GetSigmaX(self);
4237 val = rb_Float(val);
4239 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4240 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4241 mp->needsMDCopyCoordinates = 1;
4245 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4248 VALUE oval = s_AtomRef_GetSigmaY(self);
4249 val = rb_Float(val);
4251 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4252 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4253 mp->needsMDCopyCoordinates = 1;
4257 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4260 VALUE oval = s_AtomRef_GetSigmaZ(self);
4261 val = rb_Float(val);
4263 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4264 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4265 mp->needsMDCopyCoordinates = 1;
4269 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4273 VALUE oval = s_AtomRef_GetV(self);
4274 VectorFromValue(val, &v);
4275 s_AtomIndexFromValue(self, &ap, &mp);
4277 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4278 mp->needsMDCopyCoordinates = 1;
4282 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4285 VALUE oval = s_AtomRef_GetF(self);
4286 VectorFromValue(val, &v);
4287 VecScaleSelf(v, KCAL2INTERNAL);
4288 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4289 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4290 mp->needsMDCopyCoordinates = 1;
4294 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4295 VALUE oval = s_AtomRef_GetOccupancy(self);
4297 val = rb_Float(val);
4298 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4299 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4300 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4304 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4305 VALUE oval = s_AtomRef_GetTempFactor(self);
4306 val = rb_Float(val);
4307 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4308 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4312 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4317 VALUE oval = s_AtomRef_GetAniso(self);
4318 Data_Get_Struct(self, AtomRef, aref);
4319 val = rb_funcall(val, rb_intern("to_a"), 0);
4320 n = RARRAY_LEN(val);
4321 valp = RARRAY_PTR(val);
4322 for (i = 0; i < 6; i++) {
4324 f[i] = NUM2DBL(rb_Float(valp[i]));
4328 type = NUM2INT(rb_Integer(valp[6]));
4331 for (i = 0; i < 6; i++)
4332 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4334 for (i = 0; i < 6; i++)
4337 i = s_AtomIndexFromValue(self, NULL, NULL);
4338 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4339 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4343 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4349 VALUE oval = s_AtomRef_GetSymop(self);
4350 i = s_AtomIndexFromValue(self, &ap, &mol);
4352 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4354 val = rb_funcall(val, rb_intern("to_a"), 0);
4355 n = RARRAY_LEN(val);
4356 valp = RARRAY_PTR(val);
4357 for (i = 0; i < 5; i++) {
4359 if (valp[i] == Qnil)
4362 ival[i] = NUM2INT(rb_Integer(valp[i]));
4363 } else ival[i] = -100000;
4366 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4367 rb_raise(rb_eMolbyError, "index of symmetry (%d) is out of range (should be 0..%d)", ival[0], (mol->nsyms == 0 ? 0 : mol->nsyms - 1));
4368 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4369 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4370 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4371 return val; /* No need to change */
4372 if (ival[0] != -100000)
4373 ap->symop.sym = ival[0];
4374 if (ival[1] != -100000)
4375 ap->symop.dx = ival[1];
4376 if (ival[2] != -100000)
4377 ap->symop.dy = ival[2];
4378 if (ival[3] != -100000)
4379 ap->symop.dz = ival[3];
4380 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4381 if (ival[4] != -100000)
4382 ap->symbase = ival[4];
4383 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4384 /* The anisotropic parameters should be recalculated */
4385 VALUE oaval = s_AtomRef_GetAniso(self);
4386 MoleculeSetAnisoBySymop(mol, i);
4387 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4389 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4393 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4394 VALUE oval = s_AtomRef_GetIntCharge(self);
4395 val = rb_Integer(val);
4396 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4397 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4401 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4403 VALUE oval = s_AtomRef_GetFixForce(self);
4404 val = rb_Float(val);
4405 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4406 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4407 mp->needsMDRebuild = 1;
4411 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4414 VALUE oval = s_AtomRef_GetFixPos(self);
4415 VectorFromValue(val, &v);
4416 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4417 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4418 mp->needsMDRebuild = 1;
4422 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4423 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4424 return val; /* Not reached */
4427 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4428 VALUE oval = s_AtomRef_GetIntCharge(self);
4429 val = rb_Integer(val);
4430 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4431 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4435 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4436 VALUE oval = s_AtomRef_GetIntCharge(self);
4437 val = rb_Integer(val);
4438 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4439 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4443 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4444 Atom *ap = s_AtomFromValue(self);
4445 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4447 ap->exflags |= kAtomHiddenFlag;
4449 ap->exflags &= ~kAtomHiddenFlag;
4451 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4455 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4456 Int idx, i, j, k, n, *ip;
4463 MolAction **undoActions;
4464 memset(&ac, 0, sizeof(ac));
4465 idx = s_AtomIndexFromValue(self, &ap, &mol);
4466 oval = s_AtomRef_GetAnchorList(self);
4468 val = rb_ary_to_ary(val);
4469 n = RARRAY_LEN(val);
4472 if (ap->anchor != NULL) {
4473 AtomConnectResize(&ap->anchor->connect, 0);
4474 free(ap->anchor->coeffs);
4477 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4482 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4483 if (ap->aname[0] == '_')
4484 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4485 ip = (Int *)malloc(sizeof(Int) * n);
4487 for (i = 0; i < n; i++) {
4488 v = RARRAY_PTR(val)[i];
4489 if (rb_obj_is_kind_of(v, rb_cFloat))
4491 j = NUM2INT(rb_Integer(v));
4492 if (j < 0 || j >= mol->natoms)
4493 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4494 for (k = 0; k < i; k++) {
4496 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4502 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4503 else if (i * 2 != n)
4504 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4505 dp = (Double *)malloc(sizeof(Double) * n / 2);
4506 for (i = 0; i < n / 2; i++) {
4507 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4509 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4515 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4519 rb_raise(rb_eMolbyError, "invalid argument");
4520 if (nUndoActions > 0) {
4521 for (i = 0; i < nUndoActions; i++) {
4522 MolActionCallback_registerUndo(mol, undoActions[i]);
4523 MolActionRelease(undoActions[i]);
4527 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4531 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4532 Atom *ap = s_AtomFromValue(self);
4533 char *p = StringValuePtr(val);
4534 VALUE oval = s_AtomRef_GetUFFType(self);
4535 strncpy(ap->uff_type, p, 5);
4536 ap->uff_type[5] = 0;
4537 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4541 static struct s_AtomAttrDef {
4543 VALUE *symref; /* Address of s_IndexSymbol etc. */
4544 ID id; /* Will be set within InitMolby() */
4545 VALUE (*getter)(VALUE);
4546 VALUE (*setter)(VALUE, VALUE);
4547 } s_AtomAttrDefTable[] = {
4548 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4549 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4550 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4551 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4552 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4553 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4554 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4555 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4556 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4557 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4558 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4559 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4560 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4561 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4562 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4563 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4564 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4565 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4566 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4567 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4568 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4569 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4570 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4571 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4572 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4573 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4574 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4575 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4576 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4577 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4578 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4579 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4580 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4581 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4582 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4583 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4584 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4585 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4586 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4587 {NULL} /* Sentinel */
4591 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4595 if (TYPE(key) != T_SYMBOL) {
4596 kid = rb_intern(StringValuePtr(key));
4598 } else kid = SYM2ID(key);
4599 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4600 if (s_AtomAttrDefTable[i].id == kid) {
4601 if (value == Qundef)
4602 return (*(s_AtomAttrDefTable[i].getter))(self);
4604 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4607 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4608 return Qnil; /* not reached */
4612 s_AtomRef_GetAttr(VALUE self, VALUE key)
4614 return s_AtomRef_SetAttr(self, key, Qundef);
4619 * self == atomRef -> boolean
4621 * True if the two references point to the same atom.
4624 s_AtomRef_Equal(VALUE self, VALUE val)
4626 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4627 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4628 } else return Qfalse;
4631 #pragma mark ====== MolEnumerable Class ======
4633 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4637 * self[idx] -> AtomRef or Array of Integers
4639 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4640 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4641 * value is a String. Otherwise, the return value is an Array of Integers.
4644 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4646 MolEnumerable *mseq;
4649 Data_Get_Struct(self, MolEnumerable, mseq);
4651 if (mseq->kind == kAtomKind) {
4652 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4654 idx1 = NUM2INT(arg1);
4655 switch (mseq->kind) {
4657 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4658 if (idx2 < 0 || idx2 >= mol->nbonds)
4659 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4660 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4663 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4664 if (idx2 < 0 || idx2 >= mol->nangles)
4665 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4666 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4668 case kDihedralKind: {
4669 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4670 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4671 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4672 return rb_ary_new3(4, INT2NUM(mol->dihedrals[idx2 * 4]), INT2NUM(mol->dihedrals[idx2 * 4 + 1]), INT2NUM(mol->dihedrals[idx2 * 4 + 2]), INT2NUM(mol->dihedrals[idx2 * 4 + 3]));
4674 case kImproperKind: {
4675 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4676 if (idx2 < 0 || idx2 >= mol->nimpropers)
4677 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4678 return rb_ary_new3(4, INT2NUM(mol->impropers[idx2 * 4]), INT2NUM(mol->impropers[idx2 * 4 + 1]), INT2NUM(mol->impropers[idx2 * 4 + 2]), INT2NUM(mol->impropers[idx2 * 4 + 3]));
4680 case kResidueKind: {
4682 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4683 if (idx2 < 0 || idx2 >= mol->nresidues)
4684 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4685 p = mol->residues[idx2];
4686 return rb_str_new(p, strlen_limit(p, 4));
4696 * Returns the number of objects included in this enumerable.
4699 s_MolEnumerable_Length(VALUE self)
4701 MolEnumerable *mseq;
4702 Data_Get_Struct(self, MolEnumerable, mseq);
4703 switch (mseq->kind) {
4705 return INT2NUM(mseq->mol->natoms);
4707 return INT2NUM(mseq->mol->nbonds);
4709 return INT2NUM(mseq->mol->nangles);
4711 return INT2NUM(mseq->mol->ndihedrals);
4713 return INT2NUM(mseq->mol->nimpropers);
4715 return INT2NUM(mseq->mol->nresidues);
4724 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4725 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4726 * For the atoms, a same AtomRef object is passed (with different internal information)
4727 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4728 * for each iteration.
4731 s_MolEnumerable_Each(VALUE self)
4733 MolEnumerable *mseq;
4735 int len = NUM2INT(s_MolEnumerable_Length(self));
4736 Data_Get_Struct(self, MolEnumerable, mseq);
4737 if (mseq->kind == kAtomKind) {
4738 /* The same AtomRef object will be used during the loop */
4739 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4740 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4741 for (i = 0; i < len; i++) {
4746 /* A new ruby object will be created at each iteration (not very efficient) */
4747 for (i = 0; i < len; i++) {
4748 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4756 * self == molEnumerable -> boolean
4758 * True if the two arguments point to the same molecule and enumerable type.
4761 s_MolEnumerable_Equal(VALUE self, VALUE val)
4763 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4764 MolEnumerable *mseq1, *mseq2;
4765 Data_Get_Struct(self, MolEnumerable, mseq1);
4766 Data_Get_Struct(val, MolEnumerable, mseq2);
4767 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4768 } else return Qfalse;
4772 #pragma mark ====== Molecule Class ======
4774 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
4775 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4776 char *gLoadSaveErrorMessage = NULL;
4778 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4781 MoleculeFromValue(VALUE val)
4784 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4785 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4786 Data_Get_Struct(val, Molecule, mol);
4790 static VALUE sMoleculeRetainArray = Qnil;
4792 /* The function is called from MoleculeRelease() */
4793 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4794 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4795 /* object is always returned for the same Molecule. */
4796 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4797 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4798 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4799 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4800 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4802 /* Register/unregister the exmolobj Ruby object */
4804 MoleculeReleaseExternalObj(Molecule *mol)
4806 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4807 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4808 mol->exmolobjProtected = 0;
4813 MoleculeRetainExternalObj(Molecule *mol)
4815 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4816 if (sMoleculeRetainArray == Qnil) {
4817 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4818 sMoleculeRetainArray = rb_ary_new();
4821 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4822 mol->exmolobjProtected = 1;
4826 /* Release hook function for Ruby */
4828 MoleculeReleaseHook(Molecule *mol)
4830 if (mol->exmolobj != NULL) {
4831 /* No need to remove from sMoleculeRetainArray */
4832 mol->exmolobj = NULL;
4833 mol->exmolobjProtected = 0;
4835 MoleculeRelease(mol);
4839 ValueFromMolecule(Molecule *mol)
4843 if (mol->exmolobj != NULL)
4844 return (VALUE)mol->exmolobj;
4845 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4846 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4847 return (VALUE)mol->exmolobj;
4852 s_Molecule_Alloc(VALUE klass)
4855 Molecule *mol = MoleculeNew();
4856 val = ValueFromMolecule(mol);
4857 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4862 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4866 if (FIXNUM_P(val)) {
4868 if (n >= 0 && n < mol->natoms)
4870 n = -1; /* No such atom */
4871 val = rb_inspect(val);
4873 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4875 if (n >= 0 && n < mol->natoms)
4877 p = StringValuePtr(val);
4879 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4881 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4883 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4884 return 0; /* Not reached */
4888 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4891 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4892 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4893 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4894 Data_Get_Struct(val, IntGroup, ig);
4900 s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
4902 if (gLoadSaveErrorMessage != NULL) {
4903 MyAppCallback_setConsoleColor(1);
4904 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
4905 MyAppCallback_setConsoleColor(0);
4908 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4915 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4916 * created object does not affect the old object in any sense.
4919 s_Molecule_InitCopy(VALUE self, VALUE arg)
4921 Molecule *mp1, *mp2;
4922 Data_Get_Struct(self, Molecule, mp1);
4923 mp2 = MoleculeFromValue(arg);
4924 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4925 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4931 * loadmbsf(file) -> bool
4933 * Read a structure from a mbsf file.
4934 * Return true if successful.
4937 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4943 MoleculeClearLoadSaveErrorMessage();
4944 Data_Get_Struct(self, Molecule, mol);
4945 rb_scan_args(argc, argv, "1", &fname);
4946 fstr = FileStringValuePtr(fname);
4947 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4948 s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
4954 * loadpsf(file, pdbfile = nil) -> bool
4956 * Read a structure from a psf file. molecule must be empty. The psf may be
4957 * an "extended" version, which also contains coordinates. If pdbfile
4958 * is given, then atomic coordinates are read from that file.
4959 * Return true if successful.
4962 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4964 VALUE fname, pdbname;
4965 char *fstr, *pdbstr;
4968 Data_Get_Struct(self, Molecule, mol);
4969 if (mol->natoms > 0)
4970 return Qnil; /* Must be a new molecule */
4971 MoleculeClearLoadSaveErrorMessage();
4972 rb_scan_args(argc, argv, "11", &fname, &pdbname);
4973 fstr = FileStringValuePtr(fname);
4974 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4975 s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
4977 if (!NIL_P(pdbname)) {
4978 pdbstr = strdup(FileStringValuePtr(pdbname));
4979 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
4981 s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
4988 * loadpdb(file) -> bool
4990 * Read coordinates from a pdb file. If molecule is empty, then structure is build
4991 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
4992 * Return true if successful.
4995 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5001 Data_Get_Struct(self, Molecule, mol);
5002 rb_scan_args(argc, argv, "1", &fname);
5003 MoleculeClearLoadSaveErrorMessage();
5004 fstr = FileStringValuePtr(fname);
5005 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5006 s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
5012 * loaddcd(file) -> bool
5014 * Read coordinates from a dcd file. The molecule should not empty.
5015 * Return true if successful.
5018 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5024 Data_Get_Struct(self, Molecule, mol);
5025 rb_scan_args(argc, argv, "1", &fname);
5026 MoleculeClearLoadSaveErrorMessage();
5027 fstr = FileStringValuePtr(fname);
5028 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5029 s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
5035 * loadtep(file) -> bool
5037 * Read coordinates from an ortep .tep file.
5038 * Return true if successful.
5041 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5047 Data_Get_Struct(self, Molecule, mol);
5048 rb_scan_args(argc, argv, "1", &fname);
5049 MoleculeClearLoadSaveErrorMessage();
5050 fstr = FileStringValuePtr(fname);
5051 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5052 s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
5058 * loadres(file) -> bool
5060 * Read coordinates from a shelx .res file.
5061 * Return true if successful.
5064 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5070 Data_Get_Struct(self, Molecule, mol);
5071 rb_scan_args(argc, argv, "1", &fname);
5072 MoleculeClearLoadSaveErrorMessage();
5073 fstr = FileStringValuePtr(fname);
5074 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5075 s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
5081 * loadfchk(file) -> bool
5083 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5084 * Return true if successful.
5087 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5093 Data_Get_Struct(self, Molecule, mol);
5094 rb_scan_args(argc, argv, "1", &fname);
5095 MoleculeClearLoadSaveErrorMessage();
5096 fstr = FileStringValuePtr(fname);
5097 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5098 s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
5104 * loaddat(file) -> bool
5106 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5107 * Return true if successful.
5110 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5116 Data_Get_Struct(self, Molecule, mol);
5117 rb_scan_args(argc, argv, "1", &fname);
5118 MoleculeClearLoadSaveErrorMessage();
5119 fstr = FileStringValuePtr(fname);
5120 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5121 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5122 MyAppCallback_hideProgressPanel();
5123 s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
5129 * savembsf(file) -> bool
5131 * Write structure as a mbsf file. Returns true if successful.
5134 s_Molecule_Savembsf(VALUE self, VALUE fname)
5139 Data_Get_Struct(self, Molecule, mol);
5140 MoleculeClearLoadSaveErrorMessage();
5141 fstr = FileStringValuePtr(fname);
5142 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5143 s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
5149 * savepsf(file) -> bool
5151 * Write structure as a psf file. Returns true if successful.
5154 s_Molecule_Savepsf(VALUE self, VALUE fname)
5159 Data_Get_Struct(self, Molecule, mol);
5160 MoleculeClearLoadSaveErrorMessage();
5161 fstr = FileStringValuePtr(fname);
5162 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5163 s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
5169 * savepdb(file) -> bool
5171 * Write coordinates as a pdb file. Returns true if successful.
5174 s_Molecule_Savepdb(VALUE self, VALUE fname)
5179 Data_Get_Struct(self, Molecule, mol);
5180 MoleculeClearLoadSaveErrorMessage();
5181 fstr = FileStringValuePtr(fname);
5182 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5183 s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
5189 * savedcd(file) -> bool
5191 * Write coordinates as a dcd file. Returns true if successful.
5194 s_Molecule_Savedcd(VALUE self, VALUE fname)
5199 Data_Get_Struct(self, Molecule, mol);
5200 MoleculeClearLoadSaveErrorMessage();
5201 fstr = FileStringValuePtr(fname);
5202 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5203 s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
5209 * savetep(file) -> bool
5211 * Write coordinates as an ORTEP file. Returns true if successful.
5215 s_Molecule_Savetep(VALUE self, VALUE fname)
5220 Data_Get_Struct(self, Molecule, mol);
5221 fstr = FileStringValuePtr(fname);
5222 if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
5223 rb_raise(rb_eMolbyError, errbuf);
5228 /* load([ftype, ] fname, ...) */
5230 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5233 char *argstr, *methname, *p, *type = "";
5236 const char *ls = (loadFlag ? "load" : "save");
5237 int lslen = strlen(ls);
5242 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5243 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5244 if (argstr[0] == ':') {
5245 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5246 methname = ALLOC_N(char, lslen + strlen(argstr));
5247 strcpy(methname, ls);
5248 strcat(methname, argstr + 1);
5250 for (i = lslen; methname[i] != 0; i++)
5251 methname[i] = tolower(methname[i]);
5252 mid = rb_intern(methname);
5256 rval = rb_funcall2(self, mid, argc, argv);
5262 /* Guess file type from extension */
5263 p = strrchr(argstr, '.');
5267 for (methname = p; *methname != 0; methname++) {
5268 if (!isalpha(*methname))
5271 if (*methname == 0) {
5272 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5273 if (methname == NULL)
5274 rb_raise(rb_eMolbyError, "Low memory");
5275 strcpy(methname, ls);
5276 strcat(methname, p);
5277 for (i = lslen; methname[i] != 0; i++)
5278 methname[i] = tolower(methname[i]);
5279 mid = rb_intern(methname);
5282 if (rb_respond_to(self, mid)) {
5283 /* Load: try to call the load procedure only if it is available */
5284 rval = rb_funcall2(self, mid, argc, argv);
5289 /* Save: call the save procedure, and if not found then call 'method_missing' */
5290 rval = rb_funcall2(self, mid, argc, argv);
5297 rval = rb_str_to_str(argv[0]);
5298 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5299 s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
5300 return Qnil; /* Does not reach here */
5304 /* Register the path */
5307 Data_Get_Struct(self, Molecule, mol);
5308 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5310 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5311 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5312 if (ap->occupancy != 0.0)
5315 if (i == mol->natoms) {
5316 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5317 ap->occupancy = 1.0;
5326 * molload(file, *args) -> bool
5328 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5329 * file type given by the extension). If this method fails, then all defined (public)
5330 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5333 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5335 return s_Molecule_LoadSave(argc, argv, self, 1);
5340 * molsave(file, *args) -> bool
5342 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5343 * (XXX is the file type given by the extension).
5346 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5348 return s_Molecule_LoadSave(argc, argv, self, 0);
5355 * Returns the display name of the molecule. If the molecule has no associated
5356 * document, then returns nil.
5359 s_Molecule_Name(VALUE self)
5363 Data_Get_Struct(self, Molecule, mol);
5364 MoleculeCallback_displayName(mol, buf, sizeof buf);
5368 return rb_str_new2(buf);
5373 * set_name(string) -> self
5375 * Set the name of an untitled molecule. If the molecule is not associated with window
5376 * or it already has an associated file, then exception is thrown.
5379 s_Molecule_SetName(VALUE self, VALUE nval)
5382 Data_Get_Struct(self, Molecule, mol);
5383 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5384 rb_raise(rb_eMolbyError, "Cannot change the window title");
5393 * Returns the full path name of the molecule, if it is associated with a file.
5394 * If the molecule has no associated file, then returns nil.
5397 s_Molecule_Path(VALUE self)
5401 Data_Get_Struct(self, Molecule, mol);
5402 MoleculeCallback_pathName(mol, buf, sizeof buf);
5406 return Ruby_NewFileStringValue(buf);
5413 * Returns the full path name of the directory in which the file associated with the
5414 * molecule is located. If the molecule has no associated file, then returns nil.
5417 s_Molecule_Dir(VALUE self)
5421 Data_Get_Struct(self, Molecule, mol);
5422 MoleculeCallback_pathName(mol, buf, sizeof buf);
5424 translate_char(buf, '\\', '/');
5429 p = strrchr(buf, '/');
5432 return rb_str_new2(buf);
5440 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5441 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5442 * the Molecule structure) is returned.
5445 s_Molecule_Inspect(VALUE self)
5449 Data_Get_Struct(self, Molecule, mol);
5450 MoleculeCallback_displayName(mol, buf, sizeof buf);
5452 /* No associated document */
5453 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5454 return rb_str_new2(buf);
5456 /* Check whether the document name is duplicate */
5460 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5461 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5462 if (strcmp(buf, buf2) == 0) {
5469 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5471 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5473 return rb_str_new2(buf2);
5480 * open(file) -> Molecule
5482 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5485 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5491 rb_scan_args(argc, argv, "01", &fname);
5495 p = FileStringValuePtr(fname);
5496 iflag = Ruby_SetInterruptFlag(Qfalse);
5497 mp = MoleculeCallback_openNewMolecule(p);
5498 Ruby_SetInterruptFlag(iflag);
5501 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5503 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5505 return ValueFromMolecule(mp);
5511 * new(file, *args) -> Molecule
5513 * Create a new molecule and call "load" method with the same arguments.
5516 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5519 return s_Molecule_Load(argc, argv, self);
5520 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5524 s_Molecule_MolEnumerable(VALUE self, int kind)
5527 MolEnumerable *mseq;
5528 Data_Get_Struct(self, Molecule, mol);
5529 mseq = MolEnumerableNew(mol, kind);
5530 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5535 * atoms -> MolEnumerable
5537 * Returns a MolEnumerable object representing the array of atoms.
5540 s_Molecule_Atoms(VALUE self)
5542 return s_Molecule_MolEnumerable(self, kAtomKind);
5547 * bonds -> MolEnumerable
5549 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5550 * by an array of two atom indices.
5553 s_Molecule_Bonds(VALUE self)
5555 return s_Molecule_MolEnumerable(self, kBondKind);
5560 * angles -> MolEnumerable
5562 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5563 * by an array of three atom indices.
5566 s_Molecule_Angles(VALUE self)
5568 return s_Molecule_MolEnumerable(self, kAngleKind);
5573 * dihedrals -> MolEnumerable
5575 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5576 * by an array of four atom indices.
5579 s_Molecule_Dihedrals(VALUE self)
5581 return s_Molecule_MolEnumerable(self, kDihedralKind);
5586 * impropers -> MolEnumerable
5588 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5589 * by an array of four atom indices.
5592 s_Molecule_Impropers(VALUE self)
5594 return s_Molecule_MolEnumerable(self, kImproperKind);
5599 * residues -> MolEnumerable
5601 * Returns a MolEnumerable object representing the array of residue names.
5604 s_Molecule_Residues(VALUE self)
5606 return s_Molecule_MolEnumerable(self, kResidueKind);
5613 * Returns the number of atoms.
5616 s_Molecule_Natoms(VALUE self)
5619 Data_Get_Struct(self, Molecule, mol);
5620 return INT2NUM(mol->natoms);
5627 * Returns the number of bonds.
5630 s_Molecule_Nbonds(VALUE self)
5633 Data_Get_Struct(self, Molecule, mol);
5634 return INT2NUM(mol->nbonds);
5639 * nangles -> Integer
5641 * Returns the number of angles.
5644 s_Molecule_Nangles(VALUE self)
5647 Data_Get_Struct(self, Molecule, mol);
5648 return INT2NUM(mol->nangles);
5653 * ndihedrals -> Integer
5655 * Returns the number of dihedrals.
5658 s_Molecule_Ndihedrals(VALUE self)
5661 Data_Get_Struct(self, Molecule, mol);
5662 return INT2NUM(mol->ndihedrals);
5667 * nimpropers -> Integer
5669 * Returns the number of impropers.
5672 s_Molecule_Nimpropers(VALUE self)
5675 Data_Get_Struct(self, Molecule, mol);
5676 return INT2NUM(mol->nimpropers);
5681 * nresidues -> Integer
5683 * Returns the number of residues.
5686 s_Molecule_Nresidues(VALUE self)
5689 Data_Get_Struct(self, Molecule, mol);
5690 return INT2NUM(mol->nresidues);
5694 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5696 rb_raise(rb_eMolbyError, "Molecule#bond_par, angle_par, dihedral_par, improper_par, vdw_par are now obsolete. You can use MDArena#bond_par, angle_par, dihedral_par, improper_par, vdw_par instead, and probably these are what you really want.");
5701 * bond_par(idx) -> ParameterRef
5703 * Returns the MD parameter for the idx-th bond.
5707 s_Molecule_BondPar(VALUE self, VALUE val)
5714 Data_Get_Struct(self, Molecule, mol);
5715 ival = NUM2INT(rb_Integer(val));
5716 if (ival < -mol->nbonds || ival >= mol->nbonds)
5717 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5719 ival += mol->nbonds;
5720 s_RebuildMDParameterIfNecessary(self, Qtrue);
5721 i1 = mol->bonds[ival * 2];
5722 i2 = mol->bonds[ival * 2 + 1];
5723 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5724 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5725 bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5728 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5734 * angle_par(idx) -> ParameterRef
5736 * Returns the MD parameter for the idx-th angle.
5740 s_Molecule_AnglePar(VALUE self, VALUE val)
5747 Data_Get_Struct(self, Molecule, mol);
5748 ival = NUM2INT(rb_Integer(val));
5749 if (ival < -mol->nangles || ival >= mol->nangles)
5750 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5752 ival += mol->nangles;
5753 s_RebuildMDParameterIfNecessary(self, Qtrue);
5754 i1 = mol->angles[ival * 3];
5755 i2 = mol->angles[ival * 3 + 1];
5756 i3 = mol->angles[ival * 3 + 2];
5757 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5758 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5759 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5760 ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5763 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5768 * dihedral_par(idx) -> ParameterRef
5770 * Returns the MD parameter for the idx-th dihedral.
5774 s_Molecule_DihedralPar(VALUE self, VALUE val)
5779 UInt t1, t2, t3, t4;
5781 Data_Get_Struct(self, Molecule, mol);
5782 ival = NUM2INT(rb_Integer(val));
5783 if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5784 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5786 ival += mol->ndihedrals;
5787 s_RebuildMDParameterIfNecessary(self, Qtrue);
5788 i1 = mol->dihedrals[ival * 4];
5789 i2 = mol->dihedrals[ival * 4 + 1];
5790 i3 = mol->dihedrals[ival * 4 + 2];
5791 i4 = mol->dihedrals[ival * 4 + 3];
5792 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5793 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5794 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5795 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5796 tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5799 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5804 * improper_par(idx) -> ParameterRef
5806 * Returns the MD parameter for the idx-th improper.
5810 s_Molecule_ImproperPar(VALUE self, VALUE val)
5815 UInt t1, t2, t3, t4;
5817 Data_Get_Struct(self, Molecule, mol);
5818 ival = NUM2INT(rb_Integer(val));
5819 if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5820 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5822 ival += mol->nimpropers;
5823 s_RebuildMDParameterIfNecessary(self, Qtrue);
5824 i1 = mol->impropers[ival * 4];
5825 i2 = mol->impropers[ival * 4 + 1];
5826 i3 = mol->impropers[ival * 4 + 2];
5827 i4 = mol->impropers[ival * 4 + 3];
5828 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5829 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5830 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5831 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5832 tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5835 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5841 * start_step -> Integer
5843 * Returns the start step (defined by dcd format).
5846 s_Molecule_StartStep(VALUE self)
5849 Data_Get_Struct(self, Molecule, mol);
5850 return INT2NUM(mol->startStep);
5855 * start_step = Integer
5857 * Set the start step (defined by dcd format).
5860 s_Molecule_SetStartStep(VALUE self, VALUE val)
5863 Data_Get_Struct(self, Molecule, mol);
5864 mol->startStep = NUM2INT(rb_Integer(val));
5870 * steps_per_frame -> Integer
5872 * Returns the number of steps between frames (defined by dcd format).
5875 s_Molecule_StepsPerFrame(VALUE self)
5878 Data_Get_Struct(self, Molecule, mol);
5879 return INT2NUM(mol->stepsPerFrame);
5884 * steps_per_frame = Integer
5886 * Set the number of steps between frames (defined by dcd format).
5889 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5892 Data_Get_Struct(self, Molecule, mol);
5893 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5899 * ps_per_step -> Float
5901 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
5904 s_Molecule_PsPerStep(VALUE self)
5907 Data_Get_Struct(self, Molecule, mol);
5908 return rb_float_new(mol->psPerStep);
5913 * ps_per_step = Float
5915 * Set the time increment (in picoseconds) for one step (defined by dcd format).
5918 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5921 Data_Get_Struct(self, Molecule, mol);
5922 mol->psPerStep = NUM2DBL(rb_Float(val));
5928 * find_angles -> Integer
5930 * Find the angles from the bonds. Returns the number of angles newly created.
5934 s_Molecule_FindAngles(VALUE self)
5940 Data_Get_Struct(self, Molecule, mol);
5941 if (mol == NULL || mol->natoms == 0)
5945 for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5946 nc = ap->connect.count;
5948 for (i = 0; i < nc; i++) {
5949 n[0] = ap->connects[i];
5950 for (j = i + 1; j < nc; j++) {
5951 n[2] = ap->connects[j];
5952 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5953 AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5958 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
5961 return INT2NUM(nip);
5966 * find_dihedrals -> Integer
5968 * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5972 s_Molecule_FindDihedrals(VALUE self)
5976 int n1, i, j, k, nc1, nc2;
5978 Data_Get_Struct(self, Molecule, mol);
5979 if (mol == NULL || mol->natoms == 0)
5983 for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5984 nc1 = ap1->connect.count;
5986 for (i = 0; i < nc1; i++) {
5987 n[2] = ap1->connects[i];
5990 ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5991 nc2 = ap2->connect.count;
5992 for (j = 0; j < nc1; j++) {
5993 n[0] = ap1->connects[j];
5996 for (k = 0; k < nc2; k++) {
5997 n[3] = ap2->connects[k];
5998 if (n[3] == n1 || n[3] == n[0])
6000 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
6001 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
6007 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
6010 return INT2NUM(nip);
6016 * nresidues = Integer
6018 * Change the number of residues.
6021 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
6024 int ival = NUM2INT(val);
6025 Data_Get_Struct(self, Molecule, mol);
6026 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
6027 if (ival != mol->nresidues)
6028 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
6034 * max_residue_number(atom_group = nil) -> Integer
6036 * Returns the maximum residue number actually used. If an atom group is given, only
6037 * these atoms are examined. If no atom is present, nil is returned.
6040 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
6046 Data_Get_Struct(self, Molecule, mol);
6047 rb_scan_args(argc, argv, "01", &gval);
6048 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6049 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
6050 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
6055 * min_residue_number(atom_group = nil) -> Integer
6057 * Returns the minimum residue number actually used. If an atom group is given, only
6058 * these atoms are examined. If no atom is present, nil is returned.
6061 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
6067 Data_Get_Struct(self, Molecule, mol);
6068 rb_scan_args(argc, argv, "01", &gval);
6069 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6070 minSeq = MoleculeMinimumResidueNumber(mol, ig);
6071 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
6076 * each_atom(atom_group = nil) {|aref| ...}
6078 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
6079 * group is given, only these atoms are processed.
6080 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
6081 * is self (a Molecule object).
6084 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
6092 Data_Get_Struct(self, Molecule, mol);
6093 rb_scan_args(argc, argv, "01", &gval);
6094 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6095 arval = ValueFromMoleculeAndIndex(mol, 0);
6096 Data_Get_Struct(arval, AtomRef, aref);
6097 for (i = 0; i < mol->natoms; i++) {
6099 if (ig == NULL || IntGroupLookup(ig, i, NULL))
6103 IntGroupRelease(ig);
6109 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
6111 * Returns the unit cell parameters. If cell is not set, returns nil.
6114 s_Molecule_Cell(VALUE self)
6119 Data_Get_Struct(self, Molecule, mol);
6120 if (mol->cell == NULL)
6122 val = rb_ary_new2(6);
6123 for (i = 0; i < 6; i++)
6124 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
6125 if (mol->cell->has_sigma) {
6126 for (i = 0; i < 6; i++) {
6127 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
6135 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
6136 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
6138 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
6139 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
6140 This operation is undoable.
6141 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
6144 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
6148 int i, convert_coord, n;
6150 Data_Get_Struct(self, Molecule, mol);
6151 rb_scan_args(argc, argv, "11", &val, &cval);
6156 val = rb_ary_to_ary(val);
6157 len = RARRAY_LEN(val);
6160 } else if (len >= 6) {
6162 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
6163 for (i = 0; i < n; i++)
6164 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
6166 convert_coord = (RTEST(cval) ? 1 : 0);
6167 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
6173 * box -> [avec, bvec, cvec, origin, flags]
6175 * Get the unit cell information in the form of a periodic bounding box.
6176 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
6177 * Integers which define whether the system is periodic along the axis.
6178 * If no unit cell is defined, nil is returned.
6181 s_Molecule_Box(VALUE self)
6185 Data_Get_Struct(self, Molecule, mol);
6186 if (mol == NULL || mol->cell == NULL)
6188 v[0] = ValueFromVector(&(mol->cell->axes[0]));
6189 v[1] = ValueFromVector(&(mol->cell->axes[1]));
6190 v[2] = ValueFromVector(&(mol->cell->axes[2]));
6191 v[3] = ValueFromVector(&(mol->cell->origin));
6192 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
6193 val = rb_ary_new4(5, v);
6199 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
6200 * set_box(d, origin = [0, 0, 0])
6203 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
6204 If it is a number, the x/y/z axis vector is multiplied with the given number and used
6206 Flags, if present, is a 3-member array of Integers defining whether the system is
6207 periodic along the axis.
6208 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
6209 In the second form, an isotropic box with cell-length d is set.
6210 In the third form, the existing box is cleared.
6211 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
6214 s_Molecule_SetBox(VALUE self, VALUE aval)
6218 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
6220 Vector origin = {0, 0, 0};
6223 int i, convertCoordinates = 0;
6224 Data_Get_Struct(self, Molecule, mol);
6226 MolActionCreateAndPerform(mol, gMolActionClearBox);
6229 aval = rb_ary_to_ary(aval);
6230 for (i = 0; i < 6; i++) {
6231 if (i < RARRAY_LEN(aval))
6232 v[i] = (RARRAY_PTR(aval))[i];
6236 MolActionCreateAndPerform(mol, gMolActionClearBox);
6239 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
6240 d = NUM2DBL(rb_Float(v[0]));
6241 for (i = 0; i < 3; i++)
6242 VecScale(vv[i], ax[i], d);
6244 VectorFromValue(v[1], &origin);
6245 flags[0] = flags[1] = flags[2] = 1;
6247 for (i = 0; i < 3; i++) {
6250 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
6251 d = NUM2DBL(rb_Float(v[i]));
6252 VecScale(vv[i], ax[i], d);
6254 VectorFromValue(v[i], &vv[i]);
6256 flags[i] = (VecLength2(vv[i]) > 0.0);
6259 VectorFromValue(v[3], &origin);
6261 for (i = 0; i < 3; i++) {
6262 VALUE val = Ruby_ObjectAtIndex(v[4], i);
6263 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
6267 convertCoordinates = 1;
6269 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
6275 * cell_periodicity -> [n1, n2, n3]
6277 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
6281 s_Molecule_CellPeriodicity(VALUE self)
6284 Data_Get_Struct(self, Molecule, mol);
6285 if (mol->cell == NULL)
6287 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
6292 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
6293 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
6295 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
6296 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
6297 * If cell is not defined, exception is raised.
6298 * This operation is undoable.
6301 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
6305 Data_Get_Struct(self, Molecule, mol);
6306 if (mol->cell == NULL)
6307 rb_raise(rb_eMolbyError, "periodic cell is not defined");
6310 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
6311 flag = NUM2INT(rb_Integer(arg));
6315 arg = rb_ary_to_ary(arg);
6317 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
6318 arg0 = RARRAY_PTR(arg)[i];
6319 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
6320 flag |= (1 << (2 - i));
6323 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
6329 * cell_flexibility -> bool
6331 * Returns the unit cell is flexible or not
6334 s_Molecule_CellFlexibility(VALUE self)
6336 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
6339 Data_Get_Struct(self, Molecule, mol);
6340 if (mol->cell == NULL)
6342 if (mol->useFlexibleCell)
6344 else return Qfalse; */
6349 * self.cell_flexibility = bool
6350 * set_cell_flexibility(bool)
6352 * Change the unit cell is flexible or not
6355 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
6357 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
6360 Data_Get_Struct(self, Molecule, mol);
6361 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
6367 * cell_transform -> Transform
6369 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
6370 * If cell is not defined, nil is returned.
6373 s_Molecule_CellTransform(VALUE self)
6376 Data_Get_Struct(self, Molecule, mol);
6377 if (mol == NULL || mol->cell == NULL)
6379 return ValueFromTransform(&(mol->cell->tr));
6384 * symmetry -> Array of Transforms
6385 * symmetries -> Array of Transforms
6387 * Get the currently defined symmetry operations. If no symmetry operation is defined,
6388 * returns an empty array.
6391 s_Molecule_Symmetry(VALUE self)
6396 Data_Get_Struct(self, Molecule, mol);
6397 if (mol->nsyms <= 0)
6398 return rb_ary_new();
6399 val = rb_ary_new2(mol->nsyms);
6400 for (i = 0; i < mol->nsyms; i++) {
6401 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
6408 * nsymmetries -> Integer
6410 * Get the number of currently defined symmetry operations.
6413 s_Molecule_Nsymmetries(VALUE self)
6416 Data_Get_Struct(self, Molecule, mol);
6417 return INT2NUM(mol->nsyms);
6422 * add_symmetry(Transform) -> Integer
6424 * Add a new symmetry operation. If no symmetry operation is defined and the
6425 * given argument is not an identity transform, then also add an identity
6426 * transform at the index 0.
6427 * Returns the total number of symmetries after operation.
6430 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
6434 Data_Get_Struct(self, Molecule, mol);
6435 TransformFromValue(trans, &tr);
6436 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
6437 return INT2NUM(mol->nsyms);
6442 * remove_symmetry(count = nil) -> Integer
6443 * remove_symmetries(count = nil) -> Integer
6445 * Remove the specified number of symmetry operations. The last added ones are removed
6446 * first. If count is nil, then all symmetry operations are removed. Returns the
6447 * number of leftover symmetries.
6450 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
6455 Data_Get_Struct(self, Molecule, mol);
6456 rb_scan_args(argc, argv, "01", &cval);
6460 n = NUM2INT(rb_Integer(cval));
6461 if (n < 0 || n > mol->nsyms)
6462 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
6463 if (n == mol->nsyms)
6466 for (i = 0; i < n; i++)
6467 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
6468 return INT2NUM(mol->nsyms);
6472 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6474 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6475 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6476 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6477 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6484 * atom_group {|aref| ...}
6485 * atom_group(arg1, arg2, ...)
6486 * atom_group(arg1, arg2, ...) {|aref| ...}
6488 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6489 * If arguments are given, then the atoms reprensented by the arguments are added to the
6490 * group. For a conversion of a string to an atom index, see the description
6491 * of Molecule#atom_index.
6492 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6493 * representing each atom, and the atoms are removed from the result if the block returns false.
6497 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6499 IntGroup *ig1, *ig2;
6501 Int i, startPt, interval;
6502 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6503 Data_Get_Struct(retval, IntGroup, ig1);
6504 Data_Get_Struct(self, Molecule, mol);
6506 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6509 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6510 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6511 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6512 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6513 ig2 = IntGroupFromValue(*argv);
6514 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6515 interval = IntGroupGetInterval(ig2, i);
6516 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6518 IntGroupRelease(ig2);
6519 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6521 values[0] = (VALUE)mol;
6522 values[1] = (VALUE)ig1;
6523 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6525 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6530 if (rb_block_given_p()) {
6531 /* Evaluate the given block with an AtomRef as the argument, and delete
6532 the index if the block returns false */
6533 AtomRef *aref = AtomRefNew(mol, 0);
6534 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6535 ig2 = IntGroupNew();
6536 IntGroupCopy(ig2, ig1);
6537 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6539 if (startPt >= mol->natoms)
6541 aref->idx = startPt;
6542 resval = rb_yield(arval);
6544 IntGroupRemove(ig1, startPt, 1);
6546 IntGroupRelease(ig2);
6549 /* Remove points that are out of bounds */
6550 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6557 * atom_index(val) -> Integer
6559 * Returns the atom index represented by val. val can be either a non-negative integer
6560 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
6561 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
6562 * where resname, resid, name are the residue name, residue id, and atom name respectively.
6563 * If val is a string and multiple atoms match the description, the atom with the lowest index
6567 s_Molecule_AtomIndex(VALUE self, VALUE val)
6570 Data_Get_Struct(self, Molecule, mol);
6571 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
6576 * extract(group, dummy_flag = nil) -> Molecule
6578 * Extract the atoms given by group and return as a new molecule object.
6579 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6580 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6581 * names beginning with an underscore) and included in the new molecule object.
6584 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6586 Molecule *mol1, *mol2;
6588 VALUE group, dummy_flag, retval;
6589 Data_Get_Struct(self, Molecule, mol1);
6590 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6591 ig = s_Molecule_AtomGroupFromValue(self, group);
6592 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6595 retval = ValueFromMolecule(mol2);
6597 IntGroupRelease(ig);
6603 * add(molecule2) -> self
6605 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6607 This operation is undoable.
6610 s_Molecule_Add(VALUE self, VALUE val)
6612 Molecule *mol1, *mol2;
6613 Data_Get_Struct(self, Molecule, mol1);
6614 mol2 = MoleculeFromValue(val);
6615 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6621 * remove(group) -> Molecule
6623 * The atoms designated by the given group are removed from the molecule.
6624 * This operation is undoable.
6627 s_Molecule_Remove(VALUE self, VALUE group)
6632 IntGroupIterator iter;
6634 Data_Get_Struct(self, Molecule, mol1);
6635 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6636 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6637 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6638 Data_Get_Struct(group, IntGroup, ig);
6640 /* Remove the bonds between the two fragments */
6641 /* (This is necessary for undo to work correctly) */
6642 IntGroupIteratorInit(ig, &iter);
6644 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6645 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6647 cp = AtomConnectData(&ap->connect);
6648 for (j = 0; j < ap->connect.count; j++) {
6650 if (!IntGroupLookup(ig, n, NULL)) {
6651 /* bond i-n, i is in ig and n is not */
6652 int k = MoleculeLookupBond(mol1, i, n);
6656 IntGroupAdd(bg, k, 1);
6661 IntGroupIteratorRelease(&iter);
6664 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6665 IntGroupRelease(bg);
6668 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6675 * create_atom(name, pos = -1) -> AtomRef
6677 * Create a new atom with the specified name (may contain residue
6678 * information) and position (if position is out of range, the atom is appended at
6679 * the end). Returns the reference to the new atom.
6680 * This operation is undoable.
6683 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6690 char *p, resName[6], atomName[6];
6692 Data_Get_Struct(self, Molecule, mol);
6693 rb_scan_args(argc, argv, "02", &name, &ival);
6695 pos = NUM2INT(rb_Integer(ival));
6698 p = StringValuePtr(name);
6700 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6701 if (atomName[0] == 0)
6702 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6705 if (p == NULL || p[0] == 0) {
6706 memset(atomName, 0, 4);
6709 memset(&arec, 0, sizeof(arec));
6710 strncpy(arec.aname, atomName, 4);
6712 strncpy(arec.resName, resName, 4);
6713 arec.resSeq = resSeq;
6715 arec.occupancy = 1.0;
6716 // i = MoleculeCreateAnAtom(mol, &arec);
6717 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6719 aref = AtomRefNew(mol, pos);
6720 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6725 * duplicate_atom(atomref, pos = -1) -> AtomRef
6727 * Create a new atom with the same attributes (but no bonding information)
6728 * with the specified atom. Returns the reference to the new atom.
6729 * This operation is undoable.
6732 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6738 VALUE retval, aval, ival;
6740 Data_Get_Struct(self, Molecule, mol);
6741 rb_scan_args(argc, argv, "11", &aval, &ival);
6742 if (FIXNUM_P(aval)) {
6743 int idx = NUM2INT(aval);
6744 if (idx < 0 || idx >= mol->natoms)
6745 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6746 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6748 apsrc = s_AtomFromValue(aval);
6751 rb_raise(rb_eMolbyError, "bad atom specification");
6753 pos = NUM2INT(rb_Integer(ival));
6755 AtomDuplicate(&arec, apsrc);
6756 arec.connect.count = 0;
6757 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6760 aref = AtomRefNew(mol, pos);
6761 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6769 * create_bond(n1, n2, ...) -> Integer
6771 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6772 * do nothing for that pair. Returns the number of bonds actually created.
6773 * This operation is undoable.
6776 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6779 Int i, j, k, *ip, old_nbonds;
6781 rb_raise(rb_eMolbyError, "missing arguments");
6783 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6784 Data_Get_Struct(self, Molecule, mol);
6785 ip = ALLOC_N(Int, argc + 1);
6786 for (i = j = 0; i < argc; i++, j++) {
6787 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6789 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6790 j -= 2; /* This bond is already present: skip it */
6792 for (k = 0; k < j - 1; k += 2) {
6793 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6794 j -= 2; /* The same entry is already in the argument */
6801 old_nbonds = mol->nbonds;
6803 ip[j] = kInvalidIndex;
6804 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6808 rb_raise(rb_eMolbyError, "atom index out of range");
6810 rb_raise(rb_eMolbyError, "too many bonds");
6812 rb_raise(rb_eMolbyError, "duplicate bonds");
6814 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6816 rb_raise(rb_eMolbyError, "error in creating bonds");
6817 return INT2NUM(mol->nbonds - old_nbonds);
6822 * molecule.remove_bonds(n1, n2, ...) -> Integer
6824 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6825 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6826 * This operation is undoable.
6829 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6835 rb_raise(rb_eMolbyError, "missing arguments");
6837 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6838 Data_Get_Struct(self, Molecule, mol);
6840 for (i = j = 0; i < argc; i++, j = 1 - j) {
6841 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6843 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6847 IntGroupAdd(bg, k, 1);
6852 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6853 i = IntGroupGetCount(bg);
6854 IntGroupRelease(bg);
6861 * assign_bond_order(idx, d1)
6862 * assign_bond_orders(group, [d1, d2, ...])
6864 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6865 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6866 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6867 * (This may change in the future)
6868 * This operation is undoable.
6871 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6875 Data_Get_Struct(self, Molecule, mol);
6876 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6877 /* The first form */
6878 Int idx = NUM2INT(rb_Integer(idxval));
6879 Double d1 = NUM2DBL(rb_Float(dval));
6880 if (idx < 0 || idx >= mol->nbonds)
6881 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6882 ig = IntGroupNewWithPoints(idx, 1, -1);
6883 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6884 IntGroupRelease(ig);
6888 ig = IntGroupFromValue(idxval);
6889 n = IntGroupGetCount(ig);
6891 rb_raise(rb_eMolbyError, "the bond index is empty");
6892 dval = rb_ary_to_ary(dval);
6893 dp = (Double *)calloc(sizeof(Double), n);
6894 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6895 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6897 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6899 IntGroupRelease(ig);
6906 * get_bond_order(idx) -> Float
6907 * get_bond_orders(group) -> Array
6909 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6910 * In the second form, the bond orders at the indices in the group are returned as an array.
6911 * If no bond order information have been assigned, returns nil (the first form)
6912 * or an empty array (the second form).
6915 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6921 Int i, n, numericArg;
6922 Data_Get_Struct(self, Molecule, mol);
6923 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6924 /* The first form */
6925 Int idx = NUM2INT(rb_Integer(idxval));
6926 if (idx < 0 || idx >= mol->nbonds)
6927 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6928 if (mol->bondOrders == NULL)
6930 ig = IntGroupNewWithPoints(idx, 1, -1);
6934 if (mol->bondOrders == NULL)
6935 return rb_ary_new();
6936 ig = IntGroupFromValue(idxval);
6937 n = IntGroupGetCount(ig);
6939 rb_raise(rb_eMolbyError, "the bond index is empty");
6942 dp = (Double *)calloc(sizeof(Double), n);
6943 MoleculeGetBondOrders(mol, dp, ig);
6945 retval = rb_float_new(dp[0]);
6947 retval = rb_ary_new();
6948 for (i = 0; i < n; i++)
6949 rb_ary_push(retval, rb_float_new(dp[i]));
6952 IntGroupRelease(ig);
6958 * bond_exist?(idx1, idx2) -> bool
6960 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6961 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6964 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6970 Data_Get_Struct(self, Molecule, mol);
6971 idx1 = NUM2INT(rb_Integer(ival1));
6972 idx2 = NUM2INT(rb_Integer(ival2));
6973 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6974 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6975 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6976 cp = AtomConnectData(&ap->connect);
6977 for (i = 0; i < ap->connect.count; i++) {
6986 * add_angle(n1, n2, n3) -> Molecule
6988 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6989 * when a bond is created, so it is rarely necessary to use this method explicitly.
6990 * This operation is undoable.
6993 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6997 Data_Get_Struct(self, Molecule, mol);
6998 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6999 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7000 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7001 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
7002 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
7003 n[3] = kInvalidIndex;
7004 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
7010 * remove_angle(n1, n2, n3) -> Molecule
7012 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
7013 * when a bond is removed, so it is rarely necessary to use this method explicitly.
7014 * This operation is undoable.
7017 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
7022 Data_Get_Struct(self, Molecule, mol);
7023 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7024 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7025 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7026 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
7027 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
7028 ig = IntGroupNewWithPoints(n[3], 1, -1);
7029 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
7030 IntGroupRelease(ig);
7036 * add_dihedral(n1, n2, n3, n4) -> Molecule
7038 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
7039 * when a bond is created, so it is rarely necessary to use this method explicitly.
7040 * This operation is undoable.
7043 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7047 Data_Get_Struct(self, Molecule, mol);
7048 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7049 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7050 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7051 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7052 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
7053 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
7054 n[4] = kInvalidIndex;
7055 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
7061 * remove_dihedral(n1, n2, n3, n4) -> Molecule
7063 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
7064 * when a bond is removed, so it is rarely necessary to use this method explicitly.
7065 * This operation is undoable.
7068 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7073 Data_Get_Struct(self, Molecule, mol);
7074 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7075 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7076 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7077 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7078 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
7079 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
7080 ig = IntGroupNewWithPoints(n[4], 1, -1);
7081 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
7082 IntGroupRelease(ig);
7088 * add_improper(n1, n2, n3, n4) -> Molecule
7090 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
7091 * not automatically added when a new bond is created, so this method is more useful than
7092 * the angle/dihedral counterpart.
7093 * This operation is undoable.
7096 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
7100 Data_Get_Struct(self, Molecule, mol);
7101 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7102 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7103 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7104 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7105 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
7106 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
7107 n[4] = kInvalidIndex;
7108 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
7114 * remove_improper(n1, n2, n3, n4) -> Molecule
7115 * remove_improper(intgroup) -> Molecule
7117 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
7118 * Returns self. Unlike angles and dihedrals, impropers are
7119 * not automatically added when a new bond is created, so this method is more useful than
7120 * the angle/dihedral counterpart.
7121 * This operation is undoable.
7124 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
7127 VALUE v1, v2, v3, v4;
7130 Data_Get_Struct(self, Molecule, mol);
7132 ig = IntGroupFromValue(argv[0]);
7134 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
7135 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
7136 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
7137 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
7138 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
7139 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
7140 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
7141 ig = IntGroupNewWithPoints(n[4], 1, -1);
7143 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
7144 IntGroupRelease(ig);
7150 * assign_residue(group, res) -> Molecule
7152 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
7153 * or "resname.resno". When the residue number is not specified, the residue number of
7154 * the first atom in the group is used.
7155 * This operation is undoable.
7158 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
7162 char *p, *pp, buf[16];
7165 Data_Get_Struct(self, Molecule, mol);
7167 /* Parse the argument res */
7168 if (FIXNUM_P(res)) {
7169 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
7170 resid = NUM2INT(res);
7173 p = StringValuePtr(res);
7174 pp = strchr(p, '.');
7176 resid = atoi(pp + 1);
7182 if (n > sizeof buf - 1)
7187 ig = s_Molecule_AtomGroupFromValue(self, range);
7188 if (ig == NULL || IntGroupGetCount(ig) == 0)
7192 /* Use the residue number of the first specified atom */
7193 n = IntGroupGetNthPoint(ig, 0);
7194 if (n >= mol->natoms)
7195 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
7196 ap = ATOM_AT_INDEX(mol->atoms, n);
7199 /* Change the residue number */
7200 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
7201 /* Change the residue name if necessary */
7205 seqs[1] = kInvalidIndex; */
7206 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
7208 IntGroupRelease(ig);
7214 * offset_residue(group, offset) -> Molecule
7216 * Offset the residue number of the specified atoms. If any of the residue number gets
7217 * negative, then exception is thrown.
7218 * This operation is undoable.
7221 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
7226 Data_Get_Struct(self, Molecule, mol);
7227 ig = s_Molecule_AtomGroupFromValue(self, range);
7228 ofs = NUM2INT(offset);
7229 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
7231 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
7232 IntGroupRelease(ig);
7238 * renumber_atoms(array) -> IntGroup
7240 * Change the order of atoms so that the atoms specified in the array argument appear
7241 * in this order from the top of the molecule. The atoms that are not included in array
7242 * are placed after these atoms, and these atoms are returned as an intGroup.
7243 * This operation is undoable.
7246 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
7252 VALUE *valp, retval;
7253 Data_Get_Struct(self, Molecule, mol);
7254 if (TYPE(array) != T_ARRAY)
7255 array = rb_funcall(array, rb_intern("to_a"), 0);
7256 n = RARRAY_LEN(array);
7257 valp = RARRAY_PTR(array);
7258 new2old = ALLOC_N(Int, n + 1);
7259 for (i = 0; i < n; i++)
7260 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
7261 new2old[i] = kInvalidIndex;
7262 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
7264 rb_raise(rb_eMolbyError, "Atom index out of range");
7266 rb_raise(rb_eMolbyError, "Duplicate entry");
7268 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
7269 retval = IntGroup_Alloc(rb_cIntGroup);
7270 Data_Get_Struct(retval, IntGroup, ig);
7271 if (mol->natoms > n)
7272 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
7279 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7281 * Find atoms that are within the threshold distance from the given atom.
7282 * (The atom argument can also be a vector, representing a cartesian coordinate. In that case, the van der Waals of the atom can also be specified.)
7283 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7284 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7285 * If limit is not given, a default value of 1.2 is used.
7286 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7289 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7292 VALUE aval, limval, radval;
7293 double limit, radius;
7294 Int n1, nbonds, *bonds, an;
7296 Data_Get_Struct(self, Molecule, mol);
7297 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7298 if (rb_obj_is_kind_of(aval, rb_cVector3D) || rb_obj_is_kind_of(aval, rb_cLAMatrix) || rb_obj_is_kind_of(aval, rb_mEnumerable)) {
7299 VectorFromValue(aval, &v);
7301 radius = gElementParameters[6].radius;
7303 radius = NUM2DBL(rb_Float(radval));
7306 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7307 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7308 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7309 if (an >= 0 && an < gCountElementParameters)
7310 radius = gElementParameters[an].radius;
7311 else radius = gElementParameters[6].radius;
7316 limit = NUM2DBL(rb_Float(limval));
7317 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7319 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7320 aval = rb_ary_new();
7322 for (n1 = 0; n1 < nbonds; n1++)
7323 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7331 * guess_bonds(limit = 1.2) -> Integer
7333 * Create bonds between atoms that are within the threshold distance.
7334 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7335 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7336 * If limit is not given, a default value of 1.2 is used.
7337 * The number of the newly created bonds is returned.
7338 * This operation is undoable.
7341 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7347 Data_Get_Struct(self, Molecule, mol);
7348 rb_scan_args(argc, argv, "01", &limval);
7352 limit = NUM2DBL(rb_Float(limval));
7353 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7355 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7358 return INT2NUM(nbonds);
7363 * register_undo(script, *args)
7365 * Register an undo operation with the current molecule.
7368 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
7373 Data_Get_Struct(self, Molecule, mol);
7374 rb_scan_args(argc, argv, "1*", &script, &args);
7375 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
7376 MolActionCallback_registerUndo(mol, act);
7382 * undo_enabled? -> bool
7384 * Returns true if undo is enabled for this molecule; otherwise no.
7387 s_Molecule_UndoEnabled(VALUE self)
7390 Data_Get_Struct(self, Molecule, mol);
7391 if (MolActionCallback_isUndoRegistrationEnabled(mol))
7398 * undo_enabled = bool
7400 * Enable or disable undo.
7403 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
7406 Data_Get_Struct(self, Molecule, mol);
7407 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
7413 * selection -> IntGroup
7415 * Returns the current selection.
7418 s_Molecule_Selection(VALUE self)
7423 Data_Get_Struct(self, Molecule, mol);
7424 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
7425 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
7426 val = ValueFromIntGroup(ig);
7427 IntGroupRelease(ig);
7429 val = IntGroup_Alloc(rb_cIntGroup);
7435 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
7439 Data_Get_Struct(self, Molecule, mol);
7443 ig = s_Molecule_AtomGroupFromValue(self, val);
7445 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
7447 MoleculeSetSelection(mol, ig);
7449 IntGroupRelease(ig);
7455 * selection = IntGroup
7457 * Set the current selection. The right-hand operand may be nil.
7458 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
7461 s_Molecule_SetSelection(VALUE self, VALUE val)
7463 return s_Molecule_SetSelectionSub(self, val, 0);
7468 * set_undoable_selection(IntGroup)
7470 * Set the current selection with undo registration. The right-hand operand may be nil.
7471 * This operation is undoable.
7474 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
7476 return s_Molecule_SetSelectionSub(self, val, 1);
7481 * hidden_atoms -> IntGroup
7483 * Returns the currently hidden atoms.
7486 s_Molecule_HiddenAtoms(VALUE self)
7488 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7489 return Qnil; /* Not reached */
7493 Data_Get_Struct(self, Molecule, mol);
7498 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7499 if (ap->exflags & kAtomHiddenFlag)
7500 IntGroupAdd(ig, i, 1);
7502 val = ValueFromIntGroup(ig);
7503 IntGroupRelease(ig);
7506 } else return Qnil; */
7511 * set_hidden_atoms(IntGroup)
7512 * self.hidden_atoms = IntGroup
7514 * Hide the specified atoms. This operation is _not_ undoable.
7517 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
7519 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7520 return Qnil; /* Not reached */
7523 Data_Get_Struct(self, Molecule, mol);
7531 ig = s_Molecule_AtomGroupFromValue(self, val);
7532 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7533 if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
7534 ap->exflags |= kAtomHiddenFlag;
7536 ap->exflags &= kAtomHiddenFlag;
7540 IntGroupRelease(ig);
7541 MoleculeCallback_notifyModification(mol, 0);
7548 * select_frame(index)
7551 * Select the specified frame. If successful, returns true, otherwise returns false.
7554 s_Molecule_SelectFrame(VALUE self, VALUE val)
7557 int ival = NUM2INT(val);
7558 Data_Get_Struct(self, Molecule, mol);
7559 ival = MoleculeSelectFrame(mol, ival, 1);
7569 * Get the current frame.
7572 s_Molecule_Frame(VALUE self)
7575 Data_Get_Struct(self, Molecule, mol);
7576 return INT2NUM(mol->cframe);
7581 * nframes -> Integer
7583 * Get the number of frames.
7586 s_Molecule_Nframes(VALUE self)
7589 Data_Get_Struct(self, Molecule, mol);
7590 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7595 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7596 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7598 * Insert new frames at the indices specified by the intGroup. If the first argument is
7599 * an integer, a single new frame is inserted at that index. If the first argument is
7600 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7601 * should be an array of arrays of Vector3Ds, then those coordinates are set
7602 * to the new frame. Otherwise, the coordinates of current molecule are copied
7604 * Returns an intGroup representing the inserted frames if successful, nil if not.
7607 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7609 VALUE val, coords, cells;
7612 int count, ival, i, j, len, len_c, len2, nframes;
7615 Data_Get_Struct(self, Molecule, mol);
7616 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7617 if (coords != Qnil) {
7618 if (TYPE(coords) != T_ARRAY)
7619 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7620 len = RARRAY_LEN(coords);
7622 if (cells != Qnil) {
7623 if (mol->cell == NULL)
7624 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7625 if (TYPE(cells) != T_ARRAY)
7626 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7627 len_c = RARRAY_LEN(cells);
7629 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7630 nframes = MoleculeGetNumberOfFrames(mol);
7632 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7633 val = ValueFromIntGroup(ig);
7635 ig = IntGroupFromValue(val);
7637 count = IntGroupGetCount(ig); /* Count is updated here */
7638 vp = ALLOC_N(Vector, mol->natoms * count);
7640 vp2 = ALLOC_N(Vector, 4 * count);
7644 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7645 ptr = RARRAY_PTR(coords);
7646 for (i = 0; i < count; i++) {
7647 if (TYPE(ptr[i]) != T_ARRAY)
7648 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7649 len2 = RARRAY_LEN(ptr[i]);
7650 if (len2 < mol->natoms)
7651 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7652 ptr2 = RARRAY_PTR(ptr[i]);
7653 for (j = 0; j < mol->natoms; j++)
7654 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7658 for (i = 0; i < count; i++) {
7659 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7660 vp[i * mol->natoms + j] = ap->r;
7666 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7667 ptr = RARRAY_PTR(cells);
7668 for (i = 0; i < count; i++) {
7669 if (TYPE(ptr[i]) != T_ARRAY)
7670 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7671 len2 = RARRAY_LEN(ptr[i]);
7673 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7674 ptr2 = RARRAY_PTR(ptr[i]);
7675 for (j = 0; j < 4; j++)
7676 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7679 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7680 IntGroupRelease(ig);
7684 return (ival >= 0 ? val : Qnil);
7689 * create_frame(coordinates = nil) -> Integer
7690 * create_frames(coordinates = nil) -> Integer
7692 * Same as molecule.insert_frames(nil, coordinates).
7695 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
7698 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
7700 return s_Molecule_InsertFrames(3, vals, self);
7705 * remove_frames(IntGroup, wantCoordinates = false)
7707 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
7708 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
7709 * removed frames is returned if operation is successful.
7712 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
7719 Data_Get_Struct(self, Molecule, mol);
7720 rb_scan_args(argc, argv, "11", &val, &flag);
7721 ig = IntGroupFromValue(val);
7722 count = IntGroupGetCount(ig);
7724 /* Create return value before removing frames */
7729 retval = rb_ary_new2(count);
7730 for (i = 0; i < count; i++) {
7731 n = IntGroupGetNthPoint(ig, i);
7732 coords = rb_ary_new2(mol->natoms);
7733 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7734 if (n < ap->nframes && n != mol->cframe)
7737 rb_ary_push(coords, ValueFromVector(&v));
7739 rb_ary_push(retval, coords);
7741 } else retval = Qtrue;
7742 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
7749 * each_frame {|n| ...}
7751 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
7752 * the frame number. After completion, the original frame number is restored.
7755 s_Molecule_EachFrame(VALUE self)
7757 int i, cframe, nframes;
7759 Data_Get_Struct(self, Molecule, mol);
7760 cframe = mol->cframe;
7761 nframes = MoleculeGetNumberOfFrames(mol);
7763 for (i = 0; i < nframes; i++) {
7764 MoleculeSelectFrame(mol, i, 1);
7765 rb_yield(INT2NUM(i));
7767 MoleculeSelectFrame(mol, cframe, 1);
7774 * get_coord_from_frame(index, group = nil)
7776 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7777 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
7778 * copied; now they are always copied)
7781 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7784 VALUE ival, gval, cval;
7785 Int index, i, j, n, nn;
7787 IntGroupIterator iter;
7790 Data_Get_Struct(self, Molecule, mol);
7791 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7793 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
7794 index = NUM2INT(rb_Integer(ival));
7795 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7797 rb_raise(rb_eMolbyError, "No frame is present");
7799 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7802 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7804 ig = s_Molecule_AtomGroupFromValue(self, gval);
7806 n = IntGroupGetCount(ig);
7808 vp = (Vector *)calloc(sizeof(Vector), n);
7809 IntGroupIteratorInit(ig, &iter);
7812 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7813 ap = ATOM_AT_INDEX(mol->atoms, i);
7814 if (index < ap->nframes) {
7815 vp[j] = ap->frames[index];
7823 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7825 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7826 vp = mol->frame_cells + index * 4;
7827 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7829 IntGroupIteratorRelease(&iter);
7831 /* Copy the extra properties */
7832 IntGroupRelease(ig);
7833 for (i = 0; i < mol->nmolprops; i++) {
7834 Double *dp = (Double *)malloc(sizeof(Double));
7836 IntGroupAdd(ig, mol->cframe, 1);
7837 *dp = mol->molprops[i].propvals[index];
7838 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
7840 IntGroupRelease(ig);
7848 * reorder_frames(old_indices)
7850 * Reorder the frames. The argument is an array of integers that specify the 'old'
7851 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
7852 * same as the old frames 2/0/1, respectively.
7853 * The argument must have the same number of integers as the number of frames.
7856 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
7859 Int *ip, *ip2, i, n, nframes;
7860 Data_Get_Struct(self, Molecule, mol);
7861 aval = rb_ary_to_ary(aval);
7862 nframes = MoleculeGetNumberOfFrames(mol);
7863 if (RARRAY_LEN(aval) != nframes)
7864 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
7865 ip2 = (Int *)calloc(sizeof(Int), nframes);
7866 ip = (Int *)calloc(sizeof(Int), nframes);
7867 for (i = 0; i < nframes; i++) {
7868 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
7869 if (n < 0 || n >= nframes || ip2[n] != 0) {
7872 if (n < 0 || n >= nframes)
7873 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
7875 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
7881 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
7888 * set_atom_attr(index, key, value)
7890 * Set the atom attribute for the specified atom.
7891 * This operation is undoable.
7894 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7898 Data_Get_Struct(self, Molecule, mol);
7899 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7900 oldval = s_AtomRef_GetAttr(aref, key);
7903 s_AtomRef_SetAttr(aref, key, val);
7909 * get_atom_attr(index, key)
7911 * Get the atom attribute for the specified atom.
7914 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7916 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7921 * fragment(n1, *exatoms) -> IntGroup
7922 * fragment(group, *exatoms) -> IntGroup
7924 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7925 * those atoms will not be counted during the search.
7928 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7931 IntGroup *baseg, *ig, *exatoms;
7933 volatile VALUE nval, exval;
7934 Data_Get_Struct(self, Molecule, mol);
7935 rb_scan_args(argc, argv, "1*", &nval, &exval);
7936 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7938 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7940 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7942 if (RARRAY_LEN(exval) == 0) {
7945 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7946 Data_Get_Struct(exval, IntGroup, exatoms);
7948 if (baseg == NULL) {
7949 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7951 IntGroupIterator iter;
7952 IntGroupIteratorInit(baseg, &iter);
7953 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7956 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7958 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7960 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7962 IntGroupAddIntGroup(ig, subg);
7963 IntGroupRelease(subg);
7968 IntGroupIteratorRelease(&iter);
7969 IntGroupRelease(baseg);
7972 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7973 nval = ValueFromIntGroup(ig);
7974 IntGroupRelease(ig);
7980 * fragments(exclude = nil)
7982 * Returns the fragments as an array of IntGroups.
7983 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
7984 * in defining the fragment.
7987 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
7990 IntGroup *ag, *fg, *eg;
7991 VALUE gval, exval, retval;
7992 Data_Get_Struct(self, Molecule, mol);
7995 if (mol->natoms == 0)
7996 return rb_ary_new();
7997 rb_scan_args(argc, argv, "01", &exval);
8001 eg = IntGroupFromValue(exval);
8002 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8004 IntGroupRemoveIntGroup(ag, eg);
8005 retval = rb_ary_new();
8006 while (IntGroupGetCount(ag) > 0) {
8007 int n = IntGroupGetNthPoint(ag, 0);
8008 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8010 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8011 gval = ValueFromIntGroup(fg);
8012 rb_ary_push(retval, gval);
8013 IntGroupRemoveIntGroup(ag, fg);
8014 IntGroupRelease(fg);
8016 IntGroupRelease(ag);
8018 IntGroupRelease(eg);
8024 * each_fragment(exclude = nil) {|group| ...}
8026 * Execute the block, with the IntGroup object for each fragment as the argument.
8027 * Atoms or bonds should not be added or removed during the execution of the block.
8028 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8029 * in defining the fragment.
8032 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8035 IntGroup *ag, *fg, *eg;
8037 Data_Get_Struct(self, Molecule, mol);
8038 if (mol == NULL || mol->natoms == 0)
8040 rb_scan_args(argc, argv, "01", &exval);
8044 eg = IntGroupFromValue(exval);
8045 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8047 IntGroupRemoveIntGroup(ag, eg);
8048 while (IntGroupGetCount(ag) > 0) {
8049 int n = IntGroupGetNthPoint(ag, 0);
8050 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8052 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8053 gval = ValueFromIntGroup(fg);
8055 IntGroupRemoveIntGroup(ag, fg);
8056 IntGroupRelease(fg);
8058 IntGroupRelease(ag);
8060 IntGroupRelease(eg);
8066 * detachable?(group) -> [n1, n2]
8068 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8069 * of the molecule via only one bond. If it is, then the indices of the atoms
8070 * belonging to the bond is returned, the first element being the atom included
8071 * in the fragment. Otherwise, Qnil is returned.
8074 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8080 Data_Get_Struct(self, Molecule, mol);
8081 ig = s_Molecule_AtomGroupFromValue(self, gval);
8082 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8083 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8084 } else retval = Qnil;
8085 IntGroupRelease(ig);
8091 * bonds_on_border(group = selection) -> Array of Array of two Integers
8093 * Returns an array of bonds that connect an atom in the group and an atom out
8094 * of the group. The first atom in the bond always belongs to the group. If no
8095 * such bonds are present, an empty array is returned.
8098 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8103 Data_Get_Struct(self, Molecule, mol);
8104 rb_scan_args(argc, argv, "01", &gval);
8106 ig = MoleculeGetSelection(mol);
8110 ig = s_Molecule_AtomGroupFromValue(self, gval);
8112 retval = rb_ary_new();
8115 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8117 IntGroupIterator iter;
8119 IntGroupIteratorInit(bg, &iter);
8120 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8121 /* The atoms at the border */
8123 n1 = mol->bonds[i * 2];
8124 n2 = mol->bonds[i * 2 + 1];
8125 if (IntGroupLookupPoint(ig, n1) < 0) {
8129 if (IntGroupLookupPoint(ig, n1) < 0)
8130 continue; /* Actually this is an internal error */
8132 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8134 IntGroupIteratorRelease(&iter);
8136 IntGroupRelease(bg);
8137 IntGroupRelease(ig);
8143 * translate(vec, group = nil) -> Molecule
8145 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
8146 * This operation is undoable.
8149 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
8155 Data_Get_Struct(self, Molecule, mol);
8156 rb_scan_args(argc, argv, "11", &vec, &group);
8157 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8158 VectorFromValue(vec, &v);
8159 // MoleculeTranslate(mol, &v, ig);
8160 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
8162 IntGroupRelease(ig);
8168 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
8170 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
8171 * If group is given, only atoms in the group are moved.
8172 * This operation is undoable.
8175 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
8178 volatile VALUE aval, anval, cval, gval;
8183 Data_Get_Struct(self, Molecule, mol);
8184 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
8185 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8186 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
8187 VectorFromValue(aval, &av);
8189 cv.x = cv.y = cv.z = 0.0;
8191 VectorFromValue(cval, &cv);
8192 if (TransformForRotation(tr, &av, angle, &cv))
8193 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
8194 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8196 IntGroupRelease(ig);
8202 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
8204 * Reflect the molecule by the plane which is perpendicular to axis and including center.
8205 * axis must not be a zero vector.
8206 * If group is given, only atoms in the group are moved.
8207 * This operation is undoable.
8210 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
8213 volatile VALUE aval, cval, gval;
8217 Data_Get_Struct(self, Molecule, mol);
8218 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
8219 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8220 VectorFromValue(aval, &av);
8222 cv.x = cv.y = cv.z = 0.0;
8224 VectorFromValue(cval, &cv);
8225 if (TransformForReflection(tr, &av, &cv))
8226 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
8227 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8229 IntGroupRelease(ig);
8235 * invert(center = [0,0,0], group = nil) -> Molecule
8237 * Invert the molecule with the given center.
8238 * If group is given, only atoms in the group are moved.
8239 * This operation is undoable.
8242 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
8245 volatile VALUE cval, gval;
8249 Data_Get_Struct(self, Molecule, mol);
8250 rb_scan_args(argc, argv, "02", &cval, &gval);
8251 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8253 cv.x = cv.y = cv.z = 0.0;
8255 VectorFromValue(cval, &cv);
8256 TransformForInversion(tr, &cv);
8257 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8259 IntGroupRelease(ig);
8265 * transform(transform, group = nil) -> Molecule
8267 * Transform the molecule by the given Transform object.
8268 * If group is given, only atoms in the group are moved.
8269 * This operation is undoable.
8272 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
8278 Data_Get_Struct(self, Molecule, mol);
8279 rb_scan_args(argc, argv, "11", &trans, &group);
8280 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8281 TransformFromValue(trans, &tr);
8282 /* MoleculeTransform(mol, tr, ig); */
8283 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8285 IntGroupRelease(ig);
8290 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
8292 switch (MoleculeCenterOfMass(mol, outv, ig)) {
8293 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
8294 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
8296 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
8302 * center_of_mass(group = nil) -> Vector3D
8304 * Calculate the center of mass for the given set of atoms. The argument
8305 * group is null, then all atoms are considered.
8308 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
8314 Data_Get_Struct(self, Molecule, mol);
8315 rb_scan_args(argc, argv, "01", &group);
8316 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8317 s_Molecule_DoCenterOfMass(mol, &v, ig);
8319 IntGroupRelease(ig);
8320 return ValueFromVector(&v);
8325 * centralize(group = nil) -> self
8327 * Translate the molecule so that the center of mass of the given group is located
8328 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
8331 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
8337 Data_Get_Struct(self, Molecule, mol);
8338 rb_scan_args(argc, argv, "01", &group);
8339 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8340 s_Molecule_DoCenterOfMass(mol, &v, ig);
8342 IntGroupRelease(ig);
8346 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
8352 * bounds(group = nil) -> [min, max]
8354 * Calculate the boundary. The return value is an array of two Vector3D objects.
8357 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
8365 Data_Get_Struct(self, Molecule, mol);
8366 rb_scan_args(argc, argv, "01", &group);
8367 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8368 if (ig != NULL && IntGroupGetCount(ig) == 0)
8369 rb_raise(rb_eMolbyError, "atom group is empty");
8370 vmin.x = vmin.y = vmin.z = 1e30;
8371 vmax.x = vmax.y = vmax.z = -1e30;
8372 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
8374 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
8390 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
8393 /* Get atom position or a vector */
8395 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
8397 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
8398 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
8399 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
8401 VectorFromValue(val, vp);
8407 * measure_bond(n1, n2) -> Float
8409 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
8410 * or Vector3D values.
8411 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8414 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
8418 Data_Get_Struct(self, Molecule, mol);
8419 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8420 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8421 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
8426 * measure_angle(n1, n2, n3) -> Float
8428 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
8429 * or Vector3D values. The return value is in degree.
8430 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8433 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
8438 Data_Get_Struct(self, Molecule, mol);
8439 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8440 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8441 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8442 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
8444 return Qnil; /* Cannot define */
8445 else return rb_float_new(d);
8450 * measure_dihedral(n1, n2, n3, n4) -> Float
8452 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
8453 * or Vector3D values. The return value is in degree.
8454 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8457 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
8460 Vector v1, v2, v3, v4;
8462 Data_Get_Struct(self, Molecule, mol);
8463 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8464 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8465 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8466 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
8467 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
8469 return Qnil; /* Cannot define */
8470 else return rb_float_new(d);
8475 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
8477 * Expand the specified part of the molecule by the given symmetry operation.
8478 * Returns the array of atom indices corresponding to the expanded atoms.
8479 * If allow_overlap is true, then new atoms are created even when the
8480 * coordinates coincide with the some other atom (special position) of the
8481 * same element; otherwise, such atom will not be created and the index of the
8482 * existing atom is given in the returned array.
8485 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
8488 VALUE gval, sval, xval, yval, zval, rval, oval;
8490 Int n[4], allow_overlap;
8494 Data_Get_Struct(self, Molecule, mol);
8495 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
8496 n[0] = NUM2INT(rb_Integer(sval));
8497 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
8498 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
8499 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
8500 allow_overlap = (RTEST(oval) ? 1 : 0);
8501 ig = s_Molecule_AtomGroupFromValue(self, gval);
8502 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
8503 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
8504 natoms = mol->natoms;
8506 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
8508 rval = rb_ary_new2(nidx);
8509 while (--nidx >= 0) {
8510 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
8512 /* if (natoms == mol->natoms)
8515 rval = IntGroup_Alloc(rb_cIntGroup);
8516 Data_Get_Struct(rval, IntGroup, ig);
8517 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
8524 * amend_by_symmetry(group = nil) -> IntGroup
8526 * Expand the specified part of the molecule by the given symmetry operation.
8527 * Returns an IntGroup containing the added atoms.
8530 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
8535 Data_Get_Struct(self, Molecule, mol);
8536 rb_scan_args(argc, argv, "01", &gval);
8538 ig = s_Molecule_AtomGroupFromValue(self, gval);
8540 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
8541 rval = ValueFromIntGroup(ig2);
8542 IntGroupRelease(ig2);
8548 * transform_for_symop(symop, is_cartesian = nil) -> Transform
8550 * Get the transform corresponding to the symmetry operation. The symop can either be
8551 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
8552 * If is_cartesian is true, the returned transform is for cartesian coordinates.
8553 * Otherwise, the returned transform is for fractional coordinates.
8554 * Raises exception when no cell or no transform are defined.
8557 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
8563 Data_Get_Struct(self, Molecule, mol);
8564 if (mol->cell == NULL)
8565 rb_raise(rb_eMolbyError, "no unit cell is defined");
8566 if (mol->nsyms == 0)
8567 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8568 rb_scan_args(argc, argv, "11", &sval, &fval);
8569 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
8570 symop.sym = NUM2INT(rb_Integer(sval));
8571 symop.dx = symop.dy = symop.dz = 0;
8573 sval = rb_ary_to_ary(sval);
8574 if (RARRAY_LEN(sval) < 4)
8575 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
8576 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
8577 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
8578 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
8579 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
8581 if (symop.sym >= mol->nsyms)
8582 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
8583 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
8584 return ValueFromTransform(&tr);
8589 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8591 * Get the symmetry operation corresponding to the given transform.
8592 * If is_cartesian is true, the given transform is for cartesian coordinates.
8593 * Otherwise, the given transform is for fractional coordinates.
8594 * Raises exception when no cell or no transform are defined.
8597 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8604 Data_Get_Struct(self, Molecule, mol);
8605 if (mol->cell == NULL)
8606 rb_raise(rb_eMolbyError, "no unit cell is defined");
8607 if (mol->nsyms == 0)
8608 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8609 rb_scan_args(argc, argv, "11", &tval, &fval);
8610 TransformFromValue(tval, &tr);
8611 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8613 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8615 return Qnil; /* Not found */
8621 * wrap_unit_cell(group) -> Vector3D
8623 * Move the specified group so that the center of mass of the group is within the
8624 * unit cell. The offset vector is returned. If no periodic box is defined,
8625 * exception is raised.
8628 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
8633 Data_Get_Struct(self, Molecule, mol);
8634 if (mol->cell == NULL)
8635 rb_raise(rb_eMolbyError, "no unit cell is defined");
8636 ig = s_Molecule_AtomGroupFromValue(self, gval);
8637 s_Molecule_DoCenterOfMass(mol, &cv, ig);
8638 TransformVec(&v, mol->cell->rtr, &cv);
8639 if (mol->cell->flags[0])
8641 if (mol->cell->flags[1])
8643 if (mol->cell->flags[2])
8645 TransformVec(&dv, mol->cell->tr, &v);
8647 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
8648 IntGroupRelease(ig);
8649 return ValueFromVector(&dv);
8654 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
8656 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
8657 * first and second atom in the pair should belong to group1 and group2, respectively.
8658 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
8661 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
8664 VALUE limval, gval1, gval2, rval, igval;
8665 IntGroup *ig1, *ig2;
8666 IntGroupIterator iter1, iter2;
8672 MDExclusion *exinfo;
8675 Data_Get_Struct(self, Molecule, mol);
8676 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
8677 lim = NUM2DBL(rb_Float(limval));
8679 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
8681 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
8683 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
8685 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
8687 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
8689 if (!RTEST(igval)) {
8690 /* Use the exclusion table in MDArena */
8691 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
8692 VALUE mval = ValueFromMolecule(mol);
8693 s_RebuildMDParameterIfNecessary(mval, Qnil);
8695 exinfo = mol->arena->exinfo; /* May be NULL */
8696 exlist = mol->arena->exlist;
8701 IntGroupIteratorInit(ig1, &iter1);
8702 IntGroupIteratorInit(ig2, &iter2);
8705 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
8707 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
8709 if (exinfo != NULL) {
8710 exn1 = exinfo[n[0]].index1;
8711 exn2 = exinfo[n[0] + 1].index1;
8712 } else exn1 = exn2 = -1;
8713 IntGroupIteratorReset(&iter2);
8714 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
8715 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
8717 continue; /* Same atom */
8718 if (exinfo != NULL) {
8719 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
8720 for (i = exn1; i < exn2; i++) {
8721 if (exlist[i] == n[1])
8725 continue; /* Should be excluded */
8727 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
8728 /* Is this pair already registered? */
8730 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
8731 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
8735 /* Not registered yet */
8736 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
8741 IntGroupIteratorRelease(&iter2);
8742 IntGroupIteratorRelease(&iter1);
8743 IntGroupRelease(ig2);
8744 IntGroupRelease(ig1);
8745 rval = rb_ary_new2(npairs);
8746 if (pairs != NULL) {
8747 for (i = 0; i < npairs; i++) {
8748 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
8755 /* Calculate the transform that moves the current coordinates to the reference
8756 coordinates with least displacements. */
8758 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8766 Double eigen_val[3];
8767 Vector eigen_vec[3];
8769 IntGroupIterator iter;
8771 natoms = mol->natoms;
8773 IntGroupIteratorInit(ig, &iter);
8775 /* Calculate the weighted center */
8779 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8780 ap1 = ATOM_AT_INDEX(ap, in);
8781 w1 = (weights != NULL ? weights[i] : ap1->weight);
8782 VecScaleInc(org1, ap1->r, w1);
8783 VecScaleInc(org2, ref[i], w1);
8787 VecScaleSelf(org1, w);
8788 VecScaleSelf(org2, w);
8790 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8791 /* Matrix to diagonalize = R * tR */
8792 memset(r, 0, sizeof(Mat33));
8793 memset(q, 0, sizeof(Mat33));
8794 memset(u, 0, sizeof(Mat33));
8796 IntGroupIteratorReset(&iter);
8797 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8799 ap1 = ATOM_AT_INDEX(ap, in);
8800 w1 = (weights != NULL ? weights[i] : ap1->weight);
8802 VecSub(v1, ap1->r, org1);
8803 VecSub(v2, ref[i], org2);
8804 r[0] += w1 * v1.x * v2.x;
8805 r[1] += w1 * v1.y * v2.x;
8806 r[2] += w1 * v1.z * v2.x;
8807 r[3] += w1 * v1.x * v2.y;
8808 r[4] += w1 * v1.y * v2.y;
8809 r[5] += w1 * v1.z * v2.y;
8810 r[6] += w1 * v1.x * v2.z;
8811 r[7] += w1 * v1.y * v2.z;
8812 r[8] += w1 * v1.z * v2.z;
8815 for (i = 0; i < 9; i++)
8817 for (i = 0; i < 3; i++) {
8818 for (j = 0; j < 3; j++) {
8819 for (k = 0; k < 3; k++) {
8820 q[i+j*3] += r[i+k*3] * r[j+k*3];
8825 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8826 IntGroupIteratorRelease(&iter);
8827 return -1.0; /* Cannot determine the eigenvector */
8830 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8831 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8832 MatrixTranspose(r, r);
8833 for (i = 0; i < 3; i++) {
8834 MatrixVec(&s[i], r, &eigen_vec[i]);
8835 w1 = 1.0 / sqrt(eigen_val[i]);
8836 VecScaleSelf(s[i], w1);
8838 for (k = 0; k < 3; k++) {
8839 u[0] += s[k].x * eigen_vec[k].x;
8840 u[1] += s[k].y * eigen_vec[k].x;
8841 u[2] += s[k].z * eigen_vec[k].x;
8842 u[3] += s[k].x * eigen_vec[k].y;
8843 u[4] += s[k].y * eigen_vec[k].y;
8844 u[5] += s[k].z * eigen_vec[k].y;
8845 u[6] += s[k].x * eigen_vec[k].z;
8846 u[7] += s[k].y * eigen_vec[k].z;
8847 u[8] += s[k].z * eigen_vec[k].z;
8850 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8851 MatrixVec(&org1, u, &org1);
8853 for (i = 0; i < 9; i++)
8859 /* Calculate rmsd */
8860 IntGroupIteratorReset(&iter);
8862 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8864 ap1 = ATOM_AT_INDEX(ap, in);
8865 TransformVec(&tv, trans, &ap1->r);
8867 w += VecLength2(tv);
8870 IntGroupIteratorRelease(&iter);
8876 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8878 * Calculate the transform to fit the given group to the set of reference coordinates.
8879 * The reference coordinates ref is given as either a frame number, an array of
8880 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8881 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8882 * Return values are the transform (that converts the present coordinates to the
8883 * target coordinates) and root mean square deviation (without weight).
8886 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8890 VALUE gval, rval, wval;
8892 IntGroupIterator iter;
8893 int nn, errno, i, j, in, status;
8895 Double *weights, dval[3];
8898 Data_Get_Struct(self, Molecule, mol);
8899 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8901 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8903 ig = IntGroupFromValue(gval);
8904 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8905 IntGroupRelease(ig);
8906 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8908 ref = (Vector *)calloc(sizeof(Vector), nn);
8909 weights = (Double *)calloc(sizeof(Double), nn);
8910 IntGroupIteratorInit(ig, &iter);
8911 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8912 int fn = NUM2INT(rb_Integer(rval));
8913 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8918 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8919 ap = ATOM_AT_INDEX(mol->atoms, in);
8920 if (fn < ap->nframes)
8921 ref[i] = ap->frames[fn];
8922 else ref[i] = ap->r;
8924 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8925 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8926 if (m->row * m->column < nn * 3) {
8930 for (i = 0; i < nn; i++) {
8931 ref[i].x = m->data[i * 3];
8932 ref[i].y = m->data[i * 3 + 1];
8933 ref[i].z = m->data[i * 3 + 2];
8937 rval = rb_protect(rb_ary_to_ary, rval, &status);
8942 if (RARRAY_LEN(rval) < nn) {
8946 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8947 /* Array of 3*nn numbers */
8948 if (RARRAY_LEN(rval) < nn * 3) {
8952 for (i = 0; i < nn; i++) {
8953 for (j = 0; j < 3; j++) {
8954 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8959 dval[j] = NUM2DBL(aval);
8966 /* Array of nn Vector3Ds or Arrays */
8967 for (i = 0; i < nn; i++) {
8968 aval = (RARRAY_PTR(rval))[i];
8969 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8970 VectorFromValue(aval, &ref[i]);
8972 aval = rb_protect(rb_ary_to_ary, aval, &status);
8977 if (RARRAY_LEN(aval) < 3) {
8982 for (j = 0; j < 3; j++) {
8983 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8988 dval[j] = NUM2DBL(aaval);
8998 /* Use atomic weights */
8999 IntGroupIteratorReset(&iter);
9000 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
9001 ap = ATOM_AT_INDEX(mol->atoms, in);
9002 weights[i] = ap->weight;
9005 wval = rb_protect(rb_ary_to_ary, wval, &status);
9010 if (RARRAY_LEN(wval) < nn) {
9014 for (i = 0; i < nn; i++) {
9015 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
9020 weights[i] = NUM2DBL(wwval);
9023 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
9030 IntGroupIteratorRelease(&iter);
9034 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
9035 } else if (errno == 1) {
9036 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
9037 } else if (errno == 2) {
9038 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
9039 } else if (errno == 3) {
9040 rb_jump_tag(status);
9041 } else if (errno == 4) {
9042 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
9043 } else if (errno == 5) {
9044 rb_raise(rb_eMolbyError, "insufficient number of weight values");
9045 } else if (errno == 6) {
9046 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
9048 return Qnil; /* Not reached */
9055 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
9058 s_Molecule_Display(VALUE self)
9061 Data_Get_Struct(self, Molecule, mol);
9062 if (mol->mview != NULL)
9063 MainViewCallback_display(mol->mview);
9071 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
9074 s_Molecule_MakeFront(VALUE self)
9077 Data_Get_Struct(self, Molecule, mol);
9078 if (mol->mview != NULL)
9079 MainViewCallback_makeFront(mol->mview);
9085 * update_enabled? -> bool
9087 * Returns true if screen update is enabled; otherwise no.
9090 s_Molecule_UpdateEnabled(VALUE self)
9093 Data_Get_Struct(self, Molecule, mol);
9094 if (mol->mview != NULL && !mol->mview->freezeScreen)
9101 * update_enabled = bool
9103 * Enable or disable screen update. This is effective for automatic update on modification.
9104 * Explicit call to molecule.display() always updates the screen.
9107 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
9110 Data_Get_Struct(self, Molecule, mol);
9111 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
9112 if (mol->mview != NULL)
9113 mol->mview->freezeScreen = (val == Qfalse);
9121 * show_unitcell(bool)
9122 * show_unitcell = bool
9124 * Set the flag whether to show the unit cell. If no argument is given, the
9125 * current flag is returned.
9128 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
9131 Data_Get_Struct(self, Molecule, mol);
9132 if (mol->mview == NULL)
9135 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
9136 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9138 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
9144 * show_hydrogens(bool)
9145 * show_hydrogens = bool
9147 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
9148 * current flag is returned.
9151 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
9154 Data_Get_Struct(self, Molecule, mol);
9155 if (mol->mview == NULL)
9158 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
9159 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9161 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
9167 * show_dummy_atoms(bool)
9168 * show_dummy_atoms = bool
9170 * Set the flag whether to show the dummy atoms. If no argument is given, the
9171 * current flag is returned.
9174 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
9177 Data_Get_Struct(self, Molecule, mol);
9178 if (mol->mview == NULL)
9181 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9182 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9184 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9190 * show_expanded(bool)
9191 * show_expanded = bool
9193 * Set the flag whether to show the expanded atoms. If no argument is given, the
9194 * current flag is returned.
9197 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9200 Data_Get_Struct(self, Molecule, mol);
9201 if (mol->mview == NULL)
9204 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9205 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9207 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9213 * show_ellipsoids(bool)
9214 * show_ellipsoids = bool
9216 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9217 * current flag is returned.
9220 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9223 Data_Get_Struct(self, Molecule, mol);
9224 if (mol->mview == NULL)
9227 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9228 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9230 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9235 * is_atom_visible(index) -> Boolean
9237 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9238 * as well as the molecule attributes (showHydrogens, etc.)
9241 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9246 Data_Get_Struct(self, Molecule, mol);
9247 idx = s_Molecule_AtomIndexFromValue(mol, ival);
9248 if (idx < 0 || idx >= mol->natoms)
9250 ap = ATOM_AT_INDEX(mol->atoms, idx);
9251 if (mol->mview != NULL) {
9252 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9254 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9256 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9259 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9264 * show_graphite -> Integer
9265 * show_graphite = Integer
9266 * show_graphite = boolean
9268 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9269 * number of rings to display for each direction.
9270 * If the argument is boolean, only the show/hide flag is set.
9273 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9276 Data_Get_Struct(self, Molecule, mol);
9277 if (mol->mview == NULL)
9280 if (argv[0] == Qnil || argv[0] == Qfalse)
9281 mol->mview->showGraphiteFlag = 0;
9282 else if (argv[0] == Qtrue)
9283 mol->mview->showGraphiteFlag = 1;
9285 int n = NUM2INT(rb_Integer(argv[0]));
9287 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9288 mol->mview->showGraphite = n;
9290 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9292 return INT2NUM(mol->mview->showGraphite);
9297 * show_graphite? -> boolean
9299 * Return whether the graphite is set visible or not.
9302 s_Molecule_ShowGraphiteFlag(VALUE self)
9305 Data_Get_Struct(self, Molecule, mol);
9306 if (mol->mview == NULL)
9308 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9313 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9314 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9315 * show_periodic_image = boolean
9317 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9318 * set but no visual effects are observed.
9319 * If the argument is boolean, only the show/hide flag is modified.
9322 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9328 Data_Get_Struct(self, Molecule, mol);
9329 if (mol->mview == NULL)
9331 rb_scan_args(argc, argv, "01", &val);
9333 /* Change current settings */
9334 if (val == Qnil || val == Qfalse)
9335 mol->mview->showPeriodicImageFlag = 0;
9336 else if (val == Qtrue)
9337 mol->mview->showPeriodicImageFlag = 1;
9339 val = rb_ary_to_ary(val);
9340 for (i = 0; i < 6; i++) {
9341 if (i < RARRAY_LEN(val))
9342 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9344 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9345 rb_raise(rb_eMolbyError, "bad arguments");
9346 for (i = 0; i < 6; i++)
9347 mol->mview->showPeriodicImage[i] = ival[i];
9349 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9352 for (i = 0; i < 6; i++)
9353 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9359 * show_periodic_image? -> boolean
9361 * Return whether the periodic images are set to visible or not. This flag is
9362 * independent from the show_periodic_image settings.
9365 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9368 Data_Get_Struct(self, Molecule, mol);
9369 if (mol->mview == NULL)
9371 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9376 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9377 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9378 * show_rotation_center = boolean
9380 * Set to show the rotation center of the screen.
9381 * If the argument is boolean, only the show/hide flag is modified.
9384 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9387 Data_Get_Struct(self, Molecule, mol);
9388 if (mol->mview == NULL)
9391 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9392 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9394 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9403 * Set the flag whether to draw the model in line mode. If no argument is given, the
9404 * current flag is returned.
9407 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9410 Data_Get_Struct(self, Molecule, mol);
9411 if (mol->mview == NULL)
9414 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9415 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9417 return (mol->mview->lineMode ? Qtrue : Qfalse);
9422 * atom_radius = float
9425 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9427 * If no argument is given, the current value is returned.
9430 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9433 Data_Get_Struct(self, Molecule, mol);
9434 if (mol->mview == NULL)
9437 double rad = NUM2DBL(rb_Float(argv[0]));
9439 mol->mview->atomRadius = rad;
9440 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9444 return rb_float_new(mol->mview->atomRadius);
9449 * bond_radius = float
9452 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9453 * If no argument is given, the current value is returned.
9456 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9459 Data_Get_Struct(self, Molecule, mol);
9460 if (mol->mview == NULL)
9463 double rad = NUM2DBL(rb_Float(argv[0]));
9465 mol->mview->bondRadius = rad;
9466 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9470 return rb_float_new(mol->mview->bondRadius);
9475 * atom_resolution = integer
9478 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9479 * (Default = 12; minimum = 6)
9480 * If no argument is given, the current value is returned.
9483 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9486 Data_Get_Struct(self, Molecule, mol);
9487 if (mol->mview == NULL)
9490 int res = NUM2INT(rb_Integer(argv[0]));
9492 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9493 mol->mview->atomResolution = res;
9494 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9495 return INT2NUM(res);
9497 return INT2NUM(mol->mview->atomResolution);
9502 * bond_resolution = integer
9505 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9506 * (Default = 8; minimum = 4)
9507 * If no argument is given, the current value is returned.
9510 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9513 Data_Get_Struct(self, Molecule, mol);
9514 if (mol->mview == NULL)
9517 int res = NUM2INT(rb_Integer(argv[0]));
9519 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9520 mol->mview->bondResolution = res;
9521 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9522 return INT2NUM(res);
9524 return INT2NUM(mol->mview->bondResolution);
9531 * Resize the model drawing to fit in the window.
9534 s_Molecule_ResizeToFit(VALUE self)
9537 Data_Get_Struct(self, Molecule, mol);
9538 if (mol->mview != NULL)
9539 MainView_resizeToFit(mol->mview);
9545 * get_view_rotation -> [[ax, ay, az], angle]
9547 * Get the current rotation for the view. Angle is in degree, not radian.
9550 s_Molecule_GetViewRotation(VALUE self)
9555 Data_Get_Struct(self, Molecule, mol);
9556 if (mol->mview == NULL)
9558 TrackballGetRotate(mol->mview->track, f);
9559 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9563 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9568 * get_view_scale -> float
9570 * Get the current scale for the view.
9573 s_Molecule_GetViewScale(VALUE self)
9576 Data_Get_Struct(self, Molecule, mol);
9577 if (mol->mview == NULL)
9579 return rb_float_new(TrackballGetScale(mol->mview->track));
9584 * get_view_center -> Vector
9586 * Get the current center point of the view.
9589 s_Molecule_GetViewCenter(VALUE self)
9594 Data_Get_Struct(self, Molecule, mol);
9595 if (mol->mview == NULL)
9597 TrackballGetTranslate(mol->mview->track, f);
9598 v.x = -f[0] * mol->mview->dimension;
9599 v.y = -f[1] * mol->mview->dimension;
9600 v.z = -f[2] * mol->mview->dimension;
9601 return ValueFromVector(&v);
9606 * set_view_rotation([ax, ay, az], angle) -> self
9608 * Set the current rotation for the view. Angle is in degree, not radian.
9611 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9616 Data_Get_Struct(self, Molecule, mol);
9617 if (mol->mview == NULL)
9619 VectorFromValue(aval, &v);
9620 if (NormalizeVec(&v, &v))
9621 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9625 f[0] = -NUM2DBL(rb_Float(angval));
9626 TrackballSetRotate(mol->mview->track, f);
9627 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9633 * set_view_scale(scale) -> self
9635 * Set the current scale for the view.
9638 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9641 Data_Get_Struct(self, Molecule, mol);
9642 if (mol->mview == NULL)
9644 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9645 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9651 * set_view_center(vec) -> self
9653 * Set the current center point of the view.
9656 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9661 Data_Get_Struct(self, Molecule, mol);
9662 if (mol->mview == NULL)
9664 VectorFromValue(aval, &v);
9665 f[0] = -v.x / mol->mview->dimension;
9666 f[1] = -v.y / mol->mview->dimension;
9667 f[2] = -v.z / mol->mview->dimension;
9668 TrackballSetTranslate(mol->mview->track, f);
9669 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9675 * set_background_color(red, green, blue)
9677 * Set the background color of the model window.
9680 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9683 Data_Get_Struct(self, Molecule, mol);
9684 if (mol->mview != NULL) {
9685 VALUE rval, gval, bval;
9686 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9687 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9694 * create_graphic(kind, color, points, fill = nil) -> integer
9696 * Create a new graphic object.
9697 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9698 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9699 * points: an array of Vectors
9703 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9709 VALUE kval, cval, pval, fval;
9710 Data_Get_Struct(self, Molecule, mol);
9711 if (mol->mview == NULL)
9712 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9713 rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
9714 kval = rb_obj_as_string(kval);
9715 memset(&g, 0, sizeof(g));
9717 p = RSTRING_PTR(kval);
9718 if (strcmp(p, "line") == 0)
9719 g.kind = kMainViewGraphicLine;
9720 else if (strcmp(p, "poly") == 0)
9721 g.kind = kMainViewGraphicPoly;
9722 else if (strcmp(p, "cylinder") == 0)
9723 g.kind = kMainViewGraphicCylinder;
9724 else if (strcmp(p, "cone") == 0)
9725 g.kind = kMainViewGraphicCone;
9726 else if (strcmp(p, "ellipsoid") == 0)
9727 g.kind = kMainViewGraphicEllipsoid;
9728 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9729 g.closed = (RTEST(fval) ? 1 : 0);
9730 cval = rb_ary_to_ary(cval);
9731 n = RARRAY_LEN(cval);
9732 if (n < 3 || n >= 5)
9733 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9736 for (i = 0; i < n; i++)
9737 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9738 pval = rb_ary_to_ary(pval);
9739 n = RARRAY_LEN(pval);
9740 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9742 rb_raise(rb_eArgError, "no control points are given");
9744 case kMainViewGraphicLine:
9746 rb_raise(rb_eArgError, "the line object must have at least two control points");
9748 case kMainViewGraphicPoly:
9750 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9752 case kMainViewGraphicCylinder:
9753 case kMainViewGraphicCone:
9755 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9758 case kMainViewGraphicEllipsoid:
9762 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9765 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9766 for (i = 0; i < n; i++) {
9769 v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
9772 VectorFromValue(RARRAY_PTR(pval)[i], &v);
9774 g.points[i * 3] = v.x;
9775 g.points[i * 3 + 1] = v.y;
9776 g.points[i * 3 + 2] = v.z;
9778 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9780 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9781 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9782 g.points[7] = g.points[11] = g.points[3];
9784 MainView_insertGraphic(mol->mview, -1, &g);
9785 return INT2NUM(mol->mview->ngraphics - 1);
9790 * remove_graphic(index) -> integer
9792 * Remove a graphic object.
9795 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9799 Data_Get_Struct(self, Molecule, mol);
9800 if (mol->mview == NULL)
9801 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9802 i = NUM2INT(rb_Integer(ival));
9803 if (i < 0 || i >= mol->mview->ngraphics)
9804 rb_raise(rb_eArgError, "graphic index is out of range");
9805 MainView_removeGraphic(mol->mview, i);
9811 * ngraphics -> integer
9813 * Get the number of graphic objects.
9816 s_Molecule_NGraphics(VALUE self)
9819 Data_Get_Struct(self, Molecule, mol);
9820 if (mol->mview == NULL)
9821 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9822 return INT2NUM(mol->mview->ngraphics);
9827 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9829 * Change the point_index-th control point of graphic_index-th graphic object
9833 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
9835 MainViewGraphic *gp;
9839 Data_Get_Struct(self, Molecule, mol);
9840 if (mol->mview == NULL)
9841 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9842 index = NUM2INT(rb_Integer(gval));
9843 if (index < 0 || index >= mol->mview->ngraphics)
9844 rb_raise(rb_eArgError, "the graphic index is out of range");
9845 gp = mol->mview->graphics + index;
9846 index = NUM2INT(rb_Integer(pval));
9847 if (index < 0 || index >= gp->npoints)
9848 rb_raise(rb_eArgError, "the point index is out of range");
9849 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9850 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
9851 v.x = NUM2DBL(rb_Float(nval));
9853 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
9854 gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
9855 gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9857 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9860 v.x = kInvalidFloat;
9862 } else VectorFromValue(nval, &v);
9864 gp->points[index * 3] = v.x;
9865 gp->points[index * 3 + 1] = v.y;
9866 gp->points[index * 3 + 2] = v.z;
9867 MoleculeCallback_notifyModification(mol, 0);
9873 * set_graphic_color(graphic_index, new_value) -> new_value
9875 * Change the color of graphic_index-th graphic object
9879 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9881 MainViewGraphic *gp;
9884 Data_Get_Struct(self, Molecule, mol);
9885 if (mol->mview == NULL)
9886 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9887 index = NUM2INT(rb_Integer(gval));
9888 if (index < 0 || index >= mol->mview->ngraphics)
9889 rb_raise(rb_eArgError, "the graphic index is out of range");
9890 gp = mol->mview->graphics + index;
9891 cval = rb_ary_to_ary(cval);
9892 n = RARRAY_LEN(cval);
9893 if (n != 3 && n != 4)
9894 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9895 for (index = 0; index < n; index++) {
9896 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
9900 MoleculeCallback_notifyModification(mol, 0);
9906 * show_graphic(graphic_index) -> self
9908 * Enable the visible flag of the graphic_index-th graphic object
9912 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9914 MainViewGraphic *gp;
9917 Data_Get_Struct(self, Molecule, mol);
9918 if (mol->mview == NULL)
9919 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9920 index = NUM2INT(rb_Integer(gval));
9921 if (index < 0 || index >= mol->mview->ngraphics)
9922 rb_raise(rb_eArgError, "the graphic index is out of range");
9923 gp = mol->mview->graphics + index;
9925 MoleculeCallback_notifyModification(mol, 0);
9931 * hide_graphic(graphic_index) -> self
9933 * Disable the visible flag of the graphic_index-th graphic object
9937 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9939 MainViewGraphic *gp;
9942 Data_Get_Struct(self, Molecule, mol);
9943 if (mol->mview == NULL)
9944 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9945 index = NUM2INT(rb_Integer(gval));
9946 if (index < 0 || index >= mol->mview->ngraphics)
9947 rb_raise(rb_eArgError, "the graphic index is out of range");
9948 gp = mol->mview->graphics + index;
9950 MoleculeCallback_notifyModification(mol, 0);
9958 * Show the string in the info text box.
9961 s_Molecule_ShowText(VALUE self, VALUE arg)
9964 Data_Get_Struct(self, Molecule, mol);
9965 if (mol->mview != NULL)
9966 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9972 * md_arena -> MDArena
9974 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
9975 * this molecule, a new arena is created.
9978 s_Molecule_MDArena(VALUE self)
9982 Data_Get_Struct(self, Molecule, mol);
9983 if (mol->arena == NULL)
9985 retval = ValueFromMDArena(mol->arena);
9991 * set_parameter_attr(type, index, key, value, src) -> value
9993 * This method is used only internally.
9996 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9998 /* This method is called from MolAction to change a MM parameter attribute. */
10001 ParameterRef *pref;
10003 Data_Get_Struct(self, Molecule, mol);
10004 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10005 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10007 /* This is the special part of this method; it allows modification of the src field. */
10008 /* (ParameterRef#set_attr sets 0 to the src field) */
10009 Data_Get_Struct(pval, ParameterRef, pref);
10010 up = ParameterRefGetPar(pref);
10011 up->bond.src = FIX2INT(sval);
10018 * parameter -> Parameter
10020 * Get the local parameter of this molecule. If not defined, returns nil.
10023 s_Molecule_Parameter(VALUE self)
10026 Data_Get_Struct(self, Molecule, mol);
10027 /* if (mol->par == NULL)
10029 return s_NewParameterValueFromValue(self);
10034 * selectedMO -> IntGroup
10036 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10037 * is not selected, returns nil. If the MO info table is selected but no MOs
10038 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10041 s_Molecule_SelectedMO(VALUE self)
10046 Data_Get_Struct(self, Molecule, mol);
10047 if (mol->mview == NULL)
10049 ig = MainView_selectedMO(mol->mview);
10052 IntGroupOffset(ig, 1);
10053 val = ValueFromIntGroup(ig);
10054 IntGroupRelease(ig);
10060 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10062 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10063 * If the molecule does not contain a basis set information, then returns nil.
10066 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10069 Vector o, dx, dy, dz;
10072 Int npoints = 80 * 80 * 80;
10073 Data_Get_Struct(self, Molecule, mol);
10074 if (mol->bset == NULL)
10076 rb_scan_args(argc, argv, "01", &nval);
10078 npoints = NUM2INT(rb_Integer(nval));
10079 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10081 return rb_ary_new3(7, ValueFromVector(&o), rb_float_new(VecLength(dx)), rb_float_new(VecLength(dy)), rb_float_new(VecLength(dz)), INT2NUM(nx), INT2NUM(ny), INT2NUM(nz));
10085 s_Cubegen_callback(double progress, void *ref)
10087 MyAppCallback_setProgressValue(progress);
10088 if (MyAppCallback_checkInterrupt())
10095 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10096 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10098 * Calculate the molecular orbital with number mo and create a 'cube' file.
10099 * In the first form, the cube size is estimated from the atomic coordinates. In the
10100 * second form, the cube dimension is explicitly given.
10101 * Returns fname when successful, nil otherwise.
10102 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10103 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10104 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10107 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10109 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10111 Int mono, nx, ny, nz, npoints;
10112 Vector o, dx, dy, dz;
10115 Data_Get_Struct(self, Molecule, mol);
10116 if (mol->bset == NULL)
10117 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10118 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10120 /* Set up parameters */
10121 mono = NUM2INT(rb_Integer(mval));
10122 if (mono <= 0 || mono > mol->bset->ncomps)
10123 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
10125 if (mol->bset->rflag != 0)
10126 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10127 mono += mol->bset->ncomps;
10130 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10131 /* Automatic grid formation */
10133 npoints = NUM2INT(rb_Integer(oval));
10137 else if (npoints < 8)
10138 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10139 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10140 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10144 VectorFromValue(oval, &o);
10145 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10146 VectorFromValue(dxval, &dx);
10148 dx.x = NUM2DBL(rb_Float(dxval));
10151 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10152 VectorFromValue(dyval, &dy);
10154 dy.y = NUM2DBL(rb_Float(dyval));
10157 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10158 VectorFromValue(dzval, &dz);
10160 dz.z = NUM2DBL(rb_Float(dzval));
10163 nx = NUM2INT(rb_Integer(nxval));
10164 ny = NUM2INT(rb_Integer(nyval));
10165 nz = NUM2INT(rb_Integer(nzval));
10166 if (nx <= 0 || ny <= 0 || nz <= 0)
10167 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10168 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10169 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) seem to be too large; please keep them less than 10000 and nx*ny*nz < 100000000", nx, ny, nz);
10173 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10176 else if (index < 0)
10177 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10179 /* Output to file */
10180 MoleculeCallback_displayName(mol, buf, sizeof buf);
10181 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10183 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10185 /* Discard the cube */
10186 MoleculeClearCubeAtIndex(mol, index);
10192 * create_surface(mo, attr = nil)
10194 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10195 * then it denotes the beta orbital.
10196 * If mo is nil, then the attributes of the current surface are modified.
10198 * :npoints : the approximate number of grid points
10199 * :expand : the scale factor to expand/shrink the display box size for each atom,
10200 * :thres : the threshold for the isovalue surface
10201 * If the molecule does not contain MO information, raises exception.
10204 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10207 Vector o, dx, dy, dz;
10208 Int nmo, nx, ny, nz, i;
10209 Int need_recalc = 0;
10210 VALUE nval, hval, aval;
10215 Data_Get_Struct(self, Molecule, mol);
10216 rb_scan_args(argc, argv, "11", &nval, &hval);
10217 if (mol->bset == NULL)
10218 rb_raise(rb_eMolbyError, "No MO information is given");
10219 if (nval == Qnil) {
10221 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10222 nmo = mol->bset->nmos + 1;
10224 nmo = NUM2INT(rb_Integer(nval));
10225 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10226 rb_raise(rb_eMolbyError, "MO index (%d) is out of range; should be 1..%d (or -1..-%d for beta orbitals); (0 is acceptable as arbitrary vector)", nmo, mol->bset->nmos, mol->bset->ncomps);
10228 nmo = -nmo + mol->bset->ncomps;
10230 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10231 npoints = NUM2INT(rb_Integer(aval));
10233 } else if (mol->mcube != NULL) {
10234 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10235 } else npoints = 80 * 80 * 80;
10236 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10237 expand = NUM2DBL(rb_Float(aval));
10238 } else if (mol->mcube != NULL) {
10239 expand = mol->mcube->expand;
10240 } else expand = 1.0;
10241 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10242 thres = NUM2DBL(rb_Float(aval));
10243 } else if (mol->mcube != NULL) {
10244 thres = mol->mcube->thres;
10245 } else thres = 0.05;
10246 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10247 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10248 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10249 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10250 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10252 for (nx = 0; nx < 2; nx++) {
10253 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10254 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10255 aval = rb_ary_to_ary(aval);
10256 if (RARRAY_LEN(aval) < 3) {
10258 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10260 for (i = 0; i < 4; i++)
10261 d[i] = mol->mcube->c[nx].rgba[i];
10262 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10263 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10264 if (d[i] < 0.0 && d[i] > 1.0)
10267 for (i = 0; i < 4; i++)
10268 mol->mcube->c[nx].rgba[i] = d[i];
10271 if (mol->mcube->expand != expand)
10273 mol->mcube->thres = thres;
10274 mol->mcube->expand = expand;
10276 if (mol->mcube->idn < 0)
10277 return self; /* Only set attributes for now */
10279 nmo = mol->mcube->idn; /* Force recalculation */
10281 if (MoleculeUpdateMCube(mol, nmo) != 0)
10282 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10288 * set_surface_attr(attr = nil)
10290 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10293 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10298 return s_Molecule_CreateSurface(2, args, self);
10305 * Get the number of electrostatic potential info.
10308 s_Molecule_NElpots(VALUE self)
10311 Data_Get_Struct(self, Molecule, mol);
10312 return INT2NUM(mol->nelpots);
10319 * Get the electrostatic potential info at the given index. If present, then the
10320 * return value is [Vector, Float] (position and potential). If not present, then
10324 s_Molecule_Elpot(VALUE self, VALUE ival)
10328 Data_Get_Struct(self, Molecule, mol);
10329 idx = NUM2INT(rb_Integer(ival));
10330 if (idx < 0 || idx >= mol->nelpots)
10332 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10339 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10340 * cube and marching cube information are discarded. This operation is _not_ undoable!
10343 s_Molecule_ClearBasisSet(VALUE self)
10346 Data_Get_Struct(self, Molecule, mol);
10348 if (mol->bset != NULL) {
10349 BasisSetRelease(mol->bset);
10352 if (mol->mcube != NULL) {
10353 MoleculeDeallocateMCube(mol->mcube);
10362 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
10364 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10365 * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
10369 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
10372 int sym, nprims, a_idx, n;
10373 Data_Get_Struct(self, Molecule, mol);
10374 a_idx = NUM2INT(rb_Integer(aval));
10375 sym = NUM2INT(rb_Integer(symval));
10376 nprims = NUM2INT(rb_Integer(npval));
10377 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
10379 rb_raise(rb_eMolbyError, "Molecule is emptry");
10381 rb_raise(rb_eMolbyError, "Low memory");
10383 rb_raise(rb_eMolbyError, "Unknown orbital type");
10385 rb_raise(rb_eMolbyError, "Unknown error");
10391 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10393 * To be used internally. Add a gaussian primitive coefficients.
10396 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10400 Double exponent, contraction, contraction_sp;
10401 Data_Get_Struct(self, Molecule, mol);
10402 exponent = NUM2DBL(rb_Float(expval));
10403 contraction = NUM2DBL(rb_Float(cval));
10404 contraction_sp = NUM2DBL(rb_Float(cspval));
10405 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10407 rb_raise(rb_eMolbyError, "Molecule is emptry");
10409 rb_raise(rb_eMolbyError, "Low memory");
10411 rb_raise(rb_eMolbyError, "Unknown error");
10417 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10419 * Get the Gaussian shell information for the given MO coefficient index.
10420 * The symmetry code is the same as in add_gaussian_orbital_shell.
10421 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10422 * is the number of MO component belonging to this shell.
10425 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10430 Data_Get_Struct(self, Molecule, mol);
10431 if (mol->bset == NULL)
10432 rb_raise(rb_eMolbyError, "No basis set information is defined");
10433 s_idx = NUM2INT(rb_Integer(sval));
10434 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10436 sp = mol->bset->shells + s_idx;
10439 case kGTOType_S: sym = 0; break;
10440 case kGTOType_SP: sym = -1; break;
10441 case kGTOType_P: sym = 1; break;
10442 case kGTOType_D: sym = 2; break;
10443 case kGTOType_D5: sym = -2; break;
10444 case kGTOType_F: sym = 3; break;
10445 case kGTOType_F7: sym = -3; break;
10446 case kGTOType_G: sym = 4; break;
10447 case kGTOType_G9: sym = -4; break;
10449 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10451 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10456 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10458 * Get the Gaussian primitive coefficients for the given MO component.
10461 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10467 VALUE retval, aval;
10468 Data_Get_Struct(self, Molecule, mol);
10469 if (mol->bset == NULL)
10470 rb_raise(rb_eMolbyError, "No basis set information is defined");
10471 s_idx = NUM2INT(rb_Integer(sval));
10472 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10474 sp = mol->bset->shells + s_idx;
10475 pp = mol->bset->priminfos + sp->p_idx;
10476 retval = rb_ary_new2(sp->nprim);
10477 for (i = 0; i < sp->nprim; i++) {
10478 if (sp->sym == kGTOType_SP) {
10479 /* With P contraction coefficient */
10480 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10482 /* Without P contraction coefficient */
10483 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10485 rb_ary_store(retval, i, aval);
10492 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10494 * Get the Gaussian shell information for the given MO coefficient index.
10497 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10500 Int n, c, atom_idx, shell_idx;
10502 Data_Get_Struct(self, Molecule, mol);
10503 if (mol->bset == NULL)
10504 rb_raise(rb_eMolbyError, "No basis set information is defined");
10505 c = NUM2INT(rb_Integer(cval));
10506 if (c < 0 || c >= mol->bset->ncomps)
10508 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10510 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10511 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), rb_str_new2(label));
10516 * clear_mo_coefficients
10518 * Clear the existing MO coefficients.
10521 s_Molecule_ClearMOCoefficients(VALUE self)
10524 Data_Get_Struct(self, Molecule, mol);
10525 if (mol->bset != NULL) {
10526 if (mol->bset->moenergies != NULL) {
10527 free(mol->bset->moenergies);
10528 mol->bset->moenergies = NULL;
10530 if (mol->bset->mo != NULL) {
10531 free(mol->bset->mo);
10532 mol->bset->mo = NULL;
10534 mol->bset->nmos = 0;
10541 * set_mo_coefficients(idx, energy, coefficients)
10543 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10544 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10545 * Energy is the MO energy, and coefficients is an array
10546 * of MO coefficients.
10549 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10552 Int idx, ncomps, i;
10555 Data_Get_Struct(self, Molecule, mol);
10556 idx = NUM2INT(rb_Integer(ival));
10557 energy = NUM2DBL(rb_Float(eval));
10558 aval = rb_ary_to_ary(aval);
10559 ncomps = RARRAY_LEN(aval);
10560 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10561 if (coeffs == NULL) {
10565 for (i = 0; i < ncomps; i++)
10566 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10567 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10570 rb_raise(rb_eMolbyError, "Molecule is emptry");
10572 rb_raise(rb_eMolbyError, "Low memory");
10574 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10576 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10578 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10580 rb_raise(rb_eMolbyError, "Unknown error");
10586 * get_mo_coefficients(idx)
10588 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10591 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10594 Int idx, ncomps, n;
10598 Data_Get_Struct(self, Molecule, mol);
10599 idx = NUM2INT(rb_Integer(ival));
10602 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10604 rb_raise(rb_eMolbyError, "Molecule is emptry");
10606 rb_raise(rb_eMolbyError, "No basis set information is present");
10608 return Qnil; /* Silently returns nil */
10609 retval = rb_ary_new2(ncomps);
10610 for (n = 0; n < ncomps; n++)
10611 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10618 * get_mo_energy(idx)
10620 * To be used internally. Get the MO energy for the given MO index (1-based).
10623 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10628 Data_Get_Struct(self, Molecule, mol);
10629 idx = NUM2INT(rb_Integer(ival));
10630 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10632 rb_raise(rb_eMolbyError, "Molecule is emptry");
10634 rb_raise(rb_eMolbyError, "No basis set information is present");
10637 return rb_float_new(energy);
10640 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
10643 s_InitMOInfoKeys(void)
10645 if (sTypeSym == 0) {
10646 sTypeSym = ID2SYM(rb_intern("type"));
10647 sAlphaSym = ID2SYM(rb_intern("alpha"));
10648 sBetaSym = ID2SYM(rb_intern("beta"));
10649 sNcompsSym = ID2SYM(rb_intern("ncomps"));
10650 sNshellsSym = ID2SYM(rb_intern("nshells"));
10656 * set_mo_info(hash)
10658 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
10659 * :alpha=>integer, :beta=>integer
10662 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
10666 Int rflag, na, nb, n;
10668 Data_Get_Struct(self, Molecule, mol);
10669 if (mol->bset != NULL) {
10670 rflag = mol->bset->rflag;
10671 na = mol->bset->ne_alpha;
10672 nb = mol->bset->ne_beta;
10678 if (hval != Qnil) {
10679 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
10680 s = StringValuePtr(aval);
10681 if (strcasecmp(s, "RHF") == 0)
10683 else if (strcasecmp(s, "UHF") == 0)
10685 else if (strcasecmp(s, "ROHF") == 0)
10688 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
10689 n = NUM2INT(rb_Integer(aval));
10693 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
10694 n = NUM2INT(rb_Integer(aval));
10698 MoleculeSetMOInfo(mol, rflag, na, nb);
10707 * Get the MO info. The key is as described in set_mo_info.
10708 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
10711 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
10714 Data_Get_Struct(self, Molecule, mol);
10715 if (mol->bset == NULL)
10717 if (kval == sTypeSym) {
10718 switch (mol->bset->rflag) {
10719 case 0: return rb_str_new2("UHF");
10720 case 1: return rb_str_new2("RHF");
10721 case 2: return rb_str_new2("ROHF");
10722 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
10724 } else if (kval == sAlphaSym) {
10725 return INT2NUM(mol->bset->ne_alpha);
10726 } else if (kval == sBetaSym) {
10727 return INT2NUM(mol->bset->ne_beta);
10728 } else if (kval == sNcompsSym) {
10729 return INT2NUM(mol->bset->ncomps);
10730 } else if (kval == sNshellsSym) {
10731 return INT2NUM(mol->bset->nshells);
10733 kval = rb_inspect(kval);
10734 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
10735 return Qnil; /* Does not reach here */
10743 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
10746 s_Molecule_MOType(VALUE self)
10748 return s_Molecule_GetMOInfo(self, sTypeSym);
10755 * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
10757 * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
10758 * ne_alpha, ne_beta: number of alpha/beta electrons.
10761 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
10764 Int rflag, na, nb, n;
10765 Data_Get_Struct(self, Molecule, mol);
10766 rflag = NUM2INT(rb_Integer(rval));
10767 na = NUM2INT(rb_Integer(naval));
10768 nb = NUM2INT(rb_Integer(nbval));
10769 n = MoleculeSetMOInfo(mol, rflag, na, nb);
10771 rb_raise(rb_eMolbyError, "Molecule is emptry");
10773 rb_raise(rb_eMolbyError, "Low memory");
10775 rb_raise(rb_eMolbyError, "Unknown error");
10782 * search_equivalent_atoms(ig = nil)
10784 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10787 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10793 Data_Get_Struct(self, Molecule, mol);
10794 if (mol->natoms == 0)
10796 rb_scan_args(argc, argv, "01", &val);
10798 ig = IntGroupFromValue(val);
10800 result = MoleculeSearchEquivalentAtoms(mol, ig);
10801 if (result == NULL)
10802 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10804 IntGroupRelease(ig);
10805 val = rb_ary_new2(mol->natoms);
10806 for (i = 0; i < mol->natoms; i++)
10807 rb_ary_push(val, INT2NUM(result[i]));
10814 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10816 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10817 * Name is the name of the new pi anchor, and group is the atoms that define
10818 * the pi system. Type (a String) is an atom type for MM implementation.
10819 * Weights represent the relative significance of the component atoms; if omitted, then
10820 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10821 * The weight values will be normalized so that the sum of the weights is 1.0.
10822 * The weight values must be positive.
10823 * Index is the atom index where the created pi-anchor is inserted in the
10824 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10825 * having the largest index.
10826 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10827 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10830 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10835 Int i, n, idx, last_component;
10839 if (argc < 2 || argc >= 6)
10840 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10844 Data_Get_Struct(self, Molecule, mol);
10845 ig = IntGroupFromValue(gval);
10846 memset(&a, 0, sizeof(a));
10847 memset(&an, 0, sizeof(an));
10848 strncpy(a.aname, StringValuePtr(nval), 4);
10849 if (a.aname[0] == '_')
10850 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10851 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
10852 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10853 if (n >= mol->natoms) {
10854 AtomConnectResize(&an.connect, 0);
10855 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10857 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10858 last_component = n;
10860 if (an.connect.count == 0)
10861 rb_raise(rb_eMolbyError, "no atoms are specified");
10862 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10863 for (i = 0; i < an.connect.count; i++) {
10864 an.coeffs[i] = 1.0 / an.connect.count;
10866 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10868 if (argv[0] != Qnil)
10869 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10873 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10874 if (argv[0] != Qnil) {
10875 VALUE aval = rb_ary_to_ary(argv[0]);
10877 if (RARRAY_LEN(aval) != an.connect.count)
10878 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10879 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10880 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10882 rb_raise(rb_eMolbyError, "the weight value must be positive");
10886 for (i = 0; i < an.connect.count; i++)
10887 an.coeffs[i] /= sum;
10892 if (argc > 0 && argv[0] != Qnil) {
10894 idx = NUM2INT(rb_Integer(argv[0]));
10896 if (idx < 0 || idx > mol->natoms) {
10897 /* Immediately after the last specified atom */
10898 idx = last_component + 1;
10900 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10901 memmove(a.anchor, &an, sizeof(PiAnchor));
10902 /* Use residue information of the last specified atom */
10903 ap = ATOM_AT_INDEX(mol->atoms, last_component);
10904 a.resSeq = ap->resSeq;
10905 strncpy(a.resName, ap->resName, 4);
10906 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10908 MoleculeCalculatePiAnchorPosition(mol, idx);
10909 aref = AtomRefNew(mol, idx);
10910 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10915 * set_property(name, value[, index]) -> value
10916 * set_property(name, values, group) -> values
10918 * Set molecular property. A property is a floating-point number with a specified name,
10919 * and can be set for each frame separately. The name of the property is given as a String.
10920 * The value can be a single floating point number, which is set to the current frame.
10924 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
10927 VALUE nval, vval, ival;
10930 Int i, n, idx, fidx;
10932 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
10933 Data_Get_Struct(self, Molecule, mol);
10934 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
10935 idx = NUM2INT(rb_Integer(nval));
10936 if (idx < 0 || idx >= mol->nmolprops)
10937 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
10939 name = StringValuePtr(nval);
10940 idx = MoleculeLookUpProperty(mol, name);
10942 idx = MoleculeCreateProperty(mol, name);
10944 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
10947 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
10949 fidx = mol->cframe;
10951 fidx = NUM2INT(rb_Integer(ival));
10952 n = MoleculeGetNumberOfFrames(mol);
10953 if (fidx < 0 || fidx >= n)
10954 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
10956 ig = IntGroupNewWithPoints(fidx, 1, -1);
10957 dp = (Double *)malloc(sizeof(Double));
10958 *dp = NUM2DBL(rb_Float(vval));
10961 vval = rb_ary_to_ary(vval);
10962 ig = IntGroupFromValue(ival);
10963 n = IntGroupGetCount(ig);
10965 rb_raise(rb_eMolbyError, "No frames are specified");
10966 if (RARRAY_LEN(vval) < n)
10967 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
10968 dp = (Double *)calloc(sizeof(Double), n);
10969 for (i = 0; i < n; i++)
10970 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
10973 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
10975 IntGroupRelease(ig);
10981 * get_property(name[, index]) -> value
10982 * get_property(name, group) -> values
10984 * Get molecular property. In the first form, a property value for a single frame is returned.
10985 * (If index is omitted, then the value for the current frame is given)
10986 * In the second form, an array of property values for the given frames is returned.
10987 * If name is not one of known properties or a valid index integer, exception is raised.
10990 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
10996 Int i, n, idx, fidx;
10998 rb_scan_args(argc, argv, "11", &nval, &ival);
10999 Data_Get_Struct(self, Molecule, mol);
11000 if (mol->nmolprops == 0)
11001 rb_raise(rb_eMolbyError, "The molecule has no properties");
11002 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11003 idx = NUM2INT(rb_Integer(nval));
11004 if (idx < 0 || idx >= mol->nmolprops)
11005 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11007 name = StringValuePtr(nval);
11008 idx = MoleculeLookUpProperty(mol, name);
11010 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11012 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11014 fidx = mol->cframe;
11016 fidx = NUM2INT(rb_Integer(ival));
11017 n = MoleculeGetNumberOfFrames(mol);
11018 if (fidx < 0 || fidx >= n)
11019 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11021 ig = IntGroupNewWithPoints(fidx, 1, -1);
11022 ival = INT2FIX(fidx);
11025 ig = IntGroupFromValue(ival);
11026 n = IntGroupGetCount(ig);
11028 return rb_ary_new();
11030 dp = (Double *)calloc(sizeof(Double), n);
11031 MoleculeGetProperty(mol, idx, ig, dp);
11032 if (FIXNUM_P(ival))
11033 ival = rb_float_new(dp[0]);
11035 ival = rb_ary_new();
11036 for (i = n - 1; i >= 0; i--) {
11037 nval = rb_float_new(dp[i]);
11038 rb_ary_store(ival, i, nval);
11042 IntGroupRelease(ig);
11048 * property_names -> Array
11050 * Get an array of property names.
11053 s_Molecule_PropertyNames(VALUE self)
11058 Data_Get_Struct(self, Molecule, mol);
11059 rval = rb_ary_new();
11060 for (i = mol->nmolprops - 1; i >= 0; i--) {
11061 nval = rb_str_new2(mol->molprops[i].propname);
11062 rb_ary_store(rval, i, nval);
11069 * export_graphic(fname, scale = 1.0, bg_color = -1)
11071 * Export the current graphic to a PNG or TIF file (determined by the extension).
11072 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
11076 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
11079 VALUE fval, sval, bval;
11083 Data_Get_Struct(self, Molecule, mol);
11084 if (mol->mview == NULL)
11085 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
11086 rb_scan_args(argc, argv, "12", &fval, &sval, &bval);
11087 fname = FileStringValuePtr(fval);
11090 else scale = NUM2DBL(rb_Float(sval));
11093 else bg_color = NUM2INT(rb_Integer(bval));
11094 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color) == 0)
11101 * current -> Molecule
11103 * Get the currently "active" molecule.
11106 s_Molecule_Current(VALUE klass)
11108 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11113 * Molecule[] -> Molecule
11114 * Molecule[n] -> Molecule
11115 * Molecule[name] -> Molecule
11116 * Molecule[name, k] -> Molecule
11117 * Molecule[regex] -> Molecule
11118 * Molecule[regex, k] -> Molecule
11120 * Molecule[] is equivalent to Molecule.current.
11121 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11122 * Molecule[name] gives the first document (in the order of creation time) that has
11123 * the given name. If a second argument (k) is given, the k-th document that has the
11124 * given name is returned.
11125 * Molecule[regex] gives the first document (in the order of creation time) that
11126 * has a name matching the regular expression. If a second argument (k) is given,
11127 * the k-th document that has a name matching the re is returned.
11130 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11136 rb_scan_args(argc, argv, "02", &val, &kval);
11138 return s_Molecule_Current(klass);
11139 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11140 idx = NUM2INT(val);
11141 mol = MoleculeCallback_moleculeAtIndex(idx);
11142 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11143 char *p = StringValuePtr(val);
11144 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11145 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11146 MoleculeCallback_displayName(mol, buf, sizeof buf);
11147 if (strcmp(buf, p) == 0 && --k == 0)
11150 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11151 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11152 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11154 MoleculeCallback_displayName(mol, buf, sizeof buf);
11155 name = rb_str_new2(buf);
11156 if (rb_reg_match(val, name) != Qnil && --k == 0)
11159 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11163 else return ValueFromMolecule(mol);
11168 * list -> array of Molecules
11170 * Get the list of molecules associated to the documents, in the order of creation
11171 * time of the document. If no document is open, returns an empry array.
11174 s_Molecule_List(VALUE klass)
11180 ary = rb_ary_new();
11181 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11182 rb_ary_push(ary, ValueFromMolecule(mol));
11190 * ordered_list -> array of Molecules
11192 * Get the list of molecules associated to the documents, in the order of front-to-back
11193 * ordering of the associated window. If no document is open, returns an empry array.
11196 s_Molecule_OrderedList(VALUE klass)
11202 ary = rb_ary_new();
11203 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11204 rb_ary_push(ary, ValueFromMolecule(mol));
11212 * error_message -> String
11214 * Get the error_message from the last load/save method. If no error, returns nil.
11217 s_Molecule_ErrorMessage(VALUE klass)
11219 if (gLoadSaveErrorMessage == NULL)
11221 else return rb_str_new2(gLoadSaveErrorMessage);
11226 * set_error_message(String)
11227 * Molecule.error_message = String
11229 * Get the error_message from the last load/save method. If no error, returns nil.
11232 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
11234 if (gLoadSaveErrorMessage != NULL) {
11235 free(gLoadSaveErrorMessage);
11236 gLoadSaveErrorMessage = NULL;
11238 if (sval != Qnil) {
11239 sval = rb_str_to_str(sval);
11240 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
11247 * self == Molecule -> boolean
11249 * True if the two arguments point to the same molecule.
11252 s_Molecule_Equal(VALUE self, VALUE val)
11254 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
11255 Molecule *mol1, *mol2;
11256 Data_Get_Struct(self, Molecule, mol1);
11257 Data_Get_Struct(val, Molecule, mol2);
11258 return (mol1 == mol2 ? Qtrue : Qfalse);
11259 } else return Qfalse;
11262 /* The callback functions for call_subprocess_async */
11264 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11267 VALUE procval, retval, args[2];
11268 args[0] = ValueFromMolecule(mol);
11269 args[1] = INT2NUM(status);
11270 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11271 if (procval != Qnil) {
11272 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11273 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11280 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11283 VALUE procval, retval, args[2];
11284 args[0] = ValueFromMolecule(mol);
11285 args[1] = INT2NUM(tcount);
11286 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11287 if (procval != Qnil) {
11288 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11289 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11297 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11299 * Call subprocess asynchronically.
11300 * If end_callback is given, it will be called (with two arguments self and termination status)
11301 * when the subprocess terminated.
11302 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11303 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11304 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11305 * filename begins with ">>", then the message will be appended to the file.
11306 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11307 * If the argument is nil, then the message will be sent to the Ruby console.
11308 * Returns the process ID as an integer.
11311 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11313 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11317 FILE *fpout, *fperr;
11318 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11319 Data_Get_Struct(self, Molecule, mol);
11321 if (stdout_val == Qnil) {
11324 sout = StringValuePtr(stdout_val);
11325 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11328 if (strncmp(sout, ">>", 2) == 0) {
11330 fpout = fopen(sout, "a");
11334 fpout = fopen(sout, "w");
11337 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11340 if (stderr_val == Qnil) {
11343 serr = StringValuePtr(stderr_val);
11344 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11347 if (strncmp(serr, ">>", 2) == 0) {
11349 fpout = fopen(serr, "a");
11353 fperr = fopen(serr, "w");
11356 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11360 /* Register procs as instance variables */
11361 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11362 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11363 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11364 if (fpout != NULL && fpout != (FILE *)1)
11366 if (fperr != NULL && fperr != (FILE *)1)
11376 /* Define module Molby */
11377 rb_mMolby = rb_define_module("Molby");
11379 /* Define Vector3D, Transform, IntGroup */
11382 /* Define MDArena */
11383 Init_MolbyMDTypes();
11385 /* class Molecule */
11386 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11387 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11388 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11389 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11390 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11391 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11392 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11393 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11394 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11395 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11396 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11397 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11398 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11399 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11400 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11401 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11402 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11403 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11404 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11405 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11406 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11407 /* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
11408 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11409 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11410 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11411 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11412 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11413 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11414 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11415 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11416 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11417 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11418 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11419 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11420 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11421 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11422 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11423 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11425 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
11426 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
11427 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
11428 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
11429 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
11431 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11432 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11433 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11434 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11435 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11436 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11438 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11439 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11440 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11441 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11442 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11443 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11444 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11445 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11446 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11447 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11448 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11449 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11450 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11451 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11452 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11453 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11454 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11455 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11456 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11457 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11458 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11459 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11460 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11461 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11462 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11463 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11464 rb_define_alias(rb_cMolecule, "+", "add");
11465 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11466 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11467 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11468 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11469 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11470 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11471 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11472 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11473 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11474 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11475 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11476 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11477 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11478 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11479 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11480 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11481 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11482 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11483 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11484 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11485 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11486 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11487 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11488 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11489 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11490 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11491 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11492 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11493 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11494 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11495 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11496 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11497 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11498 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11499 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11500 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11501 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11502 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11503 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11504 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11505 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11506 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11507 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11508 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11509 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11510 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11511 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11512 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11513 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11514 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11515 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11516 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11517 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11518 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11519 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11520 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11521 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11522 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11523 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11524 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11525 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11526 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11527 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11528 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11529 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11530 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11531 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11532 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11533 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11534 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11535 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11536 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11537 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11538 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11539 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11540 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11541 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11542 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11543 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11544 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11545 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11546 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11547 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11548 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11549 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11550 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11551 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11552 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11553 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11554 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11555 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11556 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11557 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11558 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11559 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11560 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11561 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11562 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11563 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11564 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11565 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11566 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11567 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11568 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11569 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11570 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11571 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11572 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11573 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11574 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11575 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11576 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11577 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11578 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11579 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11580 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11581 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11582 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11583 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11584 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11585 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11586 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11587 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11588 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11589 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11590 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
11591 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11592 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11593 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11594 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11595 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11596 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11597 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11598 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11599 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11600 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11601 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11602 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11603 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11604 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11605 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11606 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
11607 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11608 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11609 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11610 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11611 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11612 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11613 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11614 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11615 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11616 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11617 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11618 /* rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3); */
11619 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11621 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11622 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11623 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11624 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11625 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11627 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11628 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11630 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11631 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11632 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11633 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11634 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11635 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11636 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11637 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11639 /* class MolEnumerable */
11640 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11641 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11642 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11643 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11644 rb_define_alias(rb_cMolEnumerable, "size", "length");
11645 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11646 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11648 /* class AtomRef */
11649 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11650 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11652 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11653 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11654 s_AtomAttrDefTable[i].id = rb_intern(buf);
11655 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11657 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11659 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11660 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11661 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11662 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11663 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
11664 rb_global_variable(&s_SetAtomAttrString);
11665 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11666 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11668 /* class Parameter */
11669 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11670 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11671 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11672 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11673 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11674 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11675 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11676 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11677 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11678 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11679 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11680 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11681 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11682 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11683 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11684 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11685 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11686 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11687 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11688 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11689 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11690 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11691 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11692 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11693 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11694 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11695 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
11696 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
11697 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11698 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11699 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11700 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11701 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11702 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11703 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11704 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
11705 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11706 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11707 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11708 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11709 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11710 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11711 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11712 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11713 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11714 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11715 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11716 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11717 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11718 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11719 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
11720 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
11721 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
11722 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
11724 /* class ParEnumerable */
11725 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
11726 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
11727 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
11728 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
11729 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
11730 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
11731 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
11732 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
11733 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
11734 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
11735 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
11737 /* class ParameterRef */
11738 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
11739 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
11741 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
11742 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
11743 s_ParameterAttrDefTable[i].id = rb_intern(buf);
11744 if (s_ParameterAttrDefTable[i].symref != NULL)
11745 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
11746 if (s_ParameterAttrDefTable[i].setter != NULL) {
11748 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
11751 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
11752 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
11753 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
11754 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
11755 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
11756 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
11757 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
11758 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
11760 /* class MolbyError */
11761 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
11763 /* module Kernel */
11764 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
11765 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
11766 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
11767 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
11768 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
11769 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
11770 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
11771 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
11772 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
11773 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
11774 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
11775 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
11776 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
11777 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
11778 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
11779 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
11780 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
11781 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
11782 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
11783 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
11784 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
11785 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
11786 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
11787 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
11790 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
11792 s_ID_equal = rb_intern("==");
11793 g_RubyID_call = rb_intern("call");
11795 s_InitMOInfoKeys();
11798 #pragma mark ====== External functions ======
11800 static VALUE s_ruby_top_self = Qfalse;
11801 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
11802 static VALUE s_ruby_export_local_variables = Qfalse;
11805 s_evalRubyScriptOnMoleculeSub(VALUE val)
11807 void **ptr = (void **)val;
11808 Molecule *mol = (Molecule *)ptr[1];
11809 VALUE sval, fnval, lnval, retval;
11812 /* Clear the error information (store in the history array if necessary) */
11813 sval = rb_errinfo();
11814 if (sval != Qnil) {
11815 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
11816 rb_set_errinfo(Qnil);
11819 if (s_ruby_top_self == Qfalse) {
11820 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
11822 if (s_ruby_get_binding_for_molecule == Qfalse) {
11824 "lambda { |_mol_, _bind_| \n"
11825 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
11826 " _proc_.call(_mol_) } ";
11827 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
11828 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
11830 if (s_ruby_export_local_variables == Qfalse) {
11832 "lambda { |_bind_| \n"
11833 " # find local variables newly defined in _bind_ \n"
11834 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
11835 " _a_.each { |_vsym_| \n"
11836 " _vname_ = _vsym_.to_s \n"
11837 " _vval_ = _bind_.eval(_vname_) \n"
11838 " # Define local variable \n"
11839 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
11840 " # Then set value \n"
11841 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
11844 s_ruby_export_local_variables = rb_eval_string(s2);
11845 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
11847 if (ptr[2] == NULL) {
11849 /* String literal: we need to specify string encoding */
11850 #if defined(__WXMSW__)
11851 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
11853 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
11855 sval = rb_str_new2(scr);
11857 fnval = rb_str_new2("(eval)");
11858 lnval = INT2FIX(0);
11860 sval = rb_str_new2((char *)ptr[0]);
11861 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
11862 lnval = INT2FIX(1);
11864 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
11866 VALUE mval = ValueFromMolecule(mol);
11867 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
11869 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
11871 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
11877 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
11881 VALUE save_interrupt_flag;
11882 /* char *save_ruby_sourcefile;
11883 int save_ruby_sourceline; */
11884 if (gMolbyIsCheckingInterrupt) {
11885 MolActionAlertRubyIsRunning();
11887 return (RubyValue)Qnil;
11890 args[0] = (void *)script;
11891 args[1] = (void *)mol;
11892 args[2] = (void *)fname;
11893 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
11894 /* save_ruby_sourcefile = ruby_sourcefile;
11895 save_ruby_sourceline = ruby_sourceline; */
11896 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
11897 if (*status != 0) {
11898 /* Is this 'exit' exception? */
11899 VALUE last_exception = rb_gv_get("$!");
11900 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
11901 /* Capture exit and return the status value */
11902 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
11904 rb_set_errinfo(Qnil);
11907 s_SetInterruptFlag(Qnil, save_interrupt_flag);
11908 /* ruby_sourcefile = save_ruby_sourcefile;
11909 ruby_sourceline = save_ruby_sourceline; */
11915 Molby_showRubyValue(RubyValue value, char **outValueString)
11917 VALUE val = (VALUE)value;
11918 if (gMolbyIsCheckingInterrupt) {
11919 MolActionAlertRubyIsRunning();
11926 val = rb_protect(rb_inspect, val, &status);
11930 str = StringValuePtr(val);
11931 if (outValueString != NULL)
11932 *outValueString = strdup(str);
11933 MyAppCallback_showScriptMessage("%s", str);
11935 if (outValueString != NULL)
11936 *outValueString = NULL;
11942 Molby_showError(int status)
11944 static const int tag_raise = 6;
11945 char *msg = NULL, *msg2;
11946 VALUE val, backtrace;
11947 int interrupted = 0;
11948 if (status == tag_raise) {
11949 VALUE errinfo = rb_errinfo();
11950 VALUE eclass = CLASS_OF(errinfo);
11951 if (eclass == rb_eInterrupt) {
11957 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
11959 val = rb_eval_string_protect("$!.to_s", &status);
11961 msg = RSTRING_PTR(val);
11962 else msg = "(message not available)";
11964 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
11965 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
11971 Molby_getDescription(void)
11973 extern const char *gVersionString, *gCopyrightString;
11974 extern int gRevisionNumber;
11975 extern char *gLastBuildString;
11977 char *revisionString;
11978 if (gRevisionNumber > 0) {
11979 asprintf(&revisionString, ", revision %d", gRevisionNumber);
11980 } else revisionString = "";
11982 "Molby %s%s\n%s\nLast compile: %s\n"
11983 #if !defined(__CMDMAC__)
11989 "ruby %s, http://www.ruby-lang.org/\n"
11991 "FFTW 3.3.2, http://www.fftw.org/\n"
11992 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
11993 " and Massachusetts Institute of Technology",
11994 gVersionString, revisionString, gCopyrightString, gLastBuildString,
11995 #if !defined(__CMDMAC__)
11996 MyAppCallback_getGUIDescriptionString(),
11998 gRubyVersion, gRubyCopyright);
11999 if (revisionString[0] != 0)
12000 free(revisionString);
12005 Molby_startup(const char *script, const char *dir)
12010 char *respath, *p, *wbuf;
12012 /* Get version/copyright string from Ruby interpreter */
12014 gRubyVersion = strdup(ruby_version);
12015 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12016 #if defined(__CMDMAC__)
12019 " ", /* Indent for displaying in About dialog */
12021 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12024 /* Read build and revision information for Molby */
12027 extern int gRevisionNumber;
12028 extern char *gLastBuildString;
12029 FILE *fp = fopen("../buildInfo.txt", "r");
12030 gLastBuildString = "";
12032 if (fgets(buf, sizeof(buf), fp) != NULL) {
12033 char *p1 = strchr(buf, '\"');
12034 char *p2 = strrchr(buf, '\"');
12035 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12036 memmove(buf, p1 + 1, p2 - p1 - 1);
12037 buf[p2 - p1 - 1] = 0;
12038 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12043 fp = fopen("../revisionInfo.txt", "r");
12044 gRevisionNumber = 0;
12046 if (fgets(buf, sizeof(buf), fp) != NULL) {
12047 gRevisionNumber = strtol(buf, NULL, 0);
12053 #if defined(__CMDMAC__)
12054 wbuf = Molby_getDescription();
12055 printf("%s\n", wbuf);
12059 /* Read atom display parameters */
12060 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12061 #if defined(__CMDMAC__)
12062 fprintf(stderr, "%s\n", wbuf);
12064 MyAppCallback_setConsoleColor(1);
12065 MyAppCallback_showScriptMessage("%s", wbuf);
12066 MyAppCallback_setConsoleColor(0);
12071 /* Read default parameters */
12072 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12073 if (wbuf != NULL) {
12074 #if defined(__CMDMAC__)
12075 fprintf(stderr, "%s\n", wbuf);
12077 MyAppCallback_setConsoleColor(1);
12078 MyAppCallback_showScriptMessage("%s", wbuf);
12079 MyAppCallback_setConsoleColor(0);
12084 /* Initialize Ruby interpreter */
12087 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12088 it causes rb_bug() (= fatal error) during ruby_init().
12089 As a workaround, these standard streams are reopend as
12091 freopen("NUL", "r", stdin);
12092 freopen("NUL", "w", stdout);
12093 freopen("NUL", "w", stderr);
12099 extern void Init_shift_jis(void), Init_trans_japanese_sjis(void);
12101 Init_trans_japanese_sjis();
12104 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12106 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12107 ruby_incpush(libpath);
12111 ruby_script("Molby");
12113 /* Find the resource path (the parent directory of the given directory) */
12114 respath = strdup(dir);
12115 p = strrchr(respath, '/');
12116 if (p == NULL && PATH_SEPARATOR != '/')
12117 p = strrchr(respath, PATH_SEPARATOR);
12120 val = Ruby_NewFileStringValue(respath);
12121 rb_define_global_const("MolbyResourcePath", val);
12124 /* Define Molby classes */
12126 RubyDialogInitClass();
12128 rb_define_const(rb_mMolby, "ResourcePath", val);
12129 val = Ruby_NewFileStringValue(dir);
12130 rb_define_const(rb_mMolby, "ScriptPath", val);
12131 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12132 val = Ruby_NewFileStringValue(p);
12133 rb_define_const(rb_mMolby, "MbsfPath", val);
12136 p = MyAppCallback_getHomeDir();
12137 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12138 rb_define_const(rb_mMolby, "HomeDirectory", val);
12140 p = MyAppCallback_getDocumentHomeDir();
12141 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12142 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12145 #if defined(__CMDMAC__)
12146 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12148 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12153 /* Create objects for stdout and stderr */
12154 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12155 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12156 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12157 rb_gv_set("$stdout", val);
12158 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12159 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12160 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12161 rb_gv_set("$stderr", val);
12163 /* Create objects for stdin */
12164 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12165 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12166 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12167 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12168 rb_gv_set("$stdin", val);
12172 /* Global variable to hold error information */
12173 rb_define_variable("$backtrace", &gMolbyBacktrace);
12174 rb_define_variable("$error_history", &gMolbyErrorHistory);
12175 gMolbyErrorHistory = rb_ary_new();
12177 /* Global variables for script menus */
12178 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12179 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12180 gScriptMenuCommands = rb_ary_new();
12181 gScriptMenuEnablers = rb_ary_new();
12184 /* Register interrupt check code */
12185 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12189 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12190 s_SetIntervalTimer(0, 50);
12193 /* Read the startup script */
12194 if (script != NULL && script[0] != 0) {
12195 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12197 rb_load_protect(rb_str_new2(script), 0, &status);
12200 Molby_showError(status);
12202 MyAppCallback_showScriptMessage("Done.\n");
12207 Molby_buildARGV(int argc, const char **argv)
12210 rb_ary_clear(rb_argv);
12211 for (i = 0; i < argc; i++) {
12212 VALUE arg = rb_tainted_str_new2(argv[i]);
12214 rb_ary_push(rb_argv, arg);