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"
27 #include <version.h> /* for Ruby version */
28 #include <node.h> /* for rb_add_event_hook() */
30 #if defined(__WXMAC__) || defined(__CMDMAC__)
31 #define USE_PTHREAD_FOR_TIMER 1
35 #if USE_PTHREAD_FOR_TIMER
36 #include <unistd.h> /* for usleep() */
37 #include <pthread.h> /* for pthread */
39 #include <signal.h> /* for sigaction() */
43 #include "../Missing.h"
45 #pragma mark ====== Global Values ======
49 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
50 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
52 VALUE gMolbyBacktrace;
54 int gMolbyRunLevel = 0;
55 int gMolbyIsCheckingInterrupt = 0;
57 char *gRubyVersion, *gRubyCopyright;
60 static VALUE s_ID_equal; /* rb_intern("==") */
62 /* Symbols for atom attributes */
64 s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
65 s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
66 s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
67 s_RSym, s_XSym, s_YSym, s_ZSym,
68 s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
69 s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
70 s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
71 s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
72 s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
73 s_HiddenSym, s_AnchorListSym;
75 /* Symbols for parameter attributes */
77 s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
78 s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
81 /* s_A14Sym, s_B14Sym, */
82 s_Req14Sym, s_Eps14Sym,
83 s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
84 s_CommentSym, s_SourceSym;
88 * Get ary[i] by calling "[]" method
91 Ruby_ObjectAtIndex(VALUE ary, int idx)
93 static ID index_method = 0;
94 if (TYPE(ary) == T_ARRAY) {
95 int len = RARRAY_LEN(ary);
96 if (idx >= 0 && idx < len)
97 return (RARRAY_PTR(ary))[idx];
100 if (index_method == 0)
101 index_method = rb_intern("[]");
102 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
106 Ruby_FileStringValuePtr(VALUE *valp)
109 char *p = strdup(StringValuePtr(*valp));
110 translate_char(p, '/', '\\');
111 *valp = rb_str_new2(p);
113 return StringValuePtr(*valp);
115 return StringValuePtr(*valp);
120 Ruby_NewFileStringValue(const char *fstr)
124 char *p = strdup(fstr);
125 translate_char(p, '\\', '/');
126 retval = rb_str_new2(p);
130 return rb_str_new2(fstr);
135 Ruby_ObjToStringObj(VALUE val)
141 return rb_str_new2(rb_id2name(SYM2ID(val)));
143 return rb_str_to_str(val);
147 #pragma mark ====== Message input/output ======
151 * message_box(str, title, button = nil, icon = :info)
153 * Show a message box.
154 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
155 * Icon: :info, :warning, :error
158 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
160 char *str, *title, *s;
162 VALUE sval, tval, bval, ival;
163 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
164 str = StringValuePtr(sval);
165 title = StringValuePtr(tval);
167 bval = Ruby_ObjToStringObj(bval);
168 s = RSTRING_PTR(bval);
169 if (strncmp(s, "ok", 2) == 0)
171 else if (strncmp(s, "cancel", 6) == 0)
174 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
177 ival = Ruby_ObjToStringObj(ival);
178 s = RSTRING_PTR(ival);
179 if (strncmp(s, "info", 4) == 0)
181 else if (strncmp(s, "warn", 4) == 0)
183 else if (strncmp(s, "err", 3) == 0)
186 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
188 MyAppCallback_messageBox(str, title, buttons, icon);
194 * error_message_box(str)
196 * Show an error message box.
199 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
201 char *str = StringValuePtr(sval);
202 MyAppCallback_errorMessageBox("%s", str);
208 * ask(prompt, default = nil) -> string
210 * Open a modal dialog and get a line of text.
213 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
215 volatile VALUE prompt, message;
218 rb_scan_args(argc, argv, "11", &prompt, &message);
219 if (message != Qnil) {
220 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
221 buf[sizeof buf - 1] = 0;
223 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
225 return rb_str_new2(buf);
232 * show_console_window
234 * Show the console window and bring to the front.
237 s_Kernel_ShowConsoleWindow(VALUE self)
239 MyAppCallback_showConsoleWindow();
245 * hide_console_window
247 * Hide the console window.
250 s_Kernel_HideConsoleWindow(VALUE self)
252 MyAppCallback_hideConsoleWindow();
260 * Put the message in the main text view in black color.
263 s_StandardOutput(VALUE self, VALUE str)
266 MyAppCallback_setConsoleColor(0);
267 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
275 * Put the message in the main text view in red color.
278 s_StandardErrorOutput(VALUE self, VALUE str)
281 MyAppCallback_setConsoleColor(1);
282 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
283 MyAppCallback_setConsoleColor(0);
289 * stdin.gets(rs = $/)
291 * Read one line message via dialog box.
294 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
297 pval = rb_str_new2("Enter a line:");
298 rval = s_Kernel_Ask(1, &pval, self);
301 rb_str_cat2(rval, "\n");
307 * stdin.method_missing(name, args, ...)
309 * Throw an exception, noting only gets and readline are defined.
312 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
315 rb_scan_args(argc, argv, "10", &nval);
316 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
317 return Qnil; /* Not reached */
320 #pragma mark ====== Track key events ======
322 /* User interrupt handling
323 * User interrupt (command-period on Mac OS) is handled by periodic polling of
324 * key events. This polling should only be enabled during "normal" execution
325 * of scripts and must be disabled when the rest of the application (or Ruby
326 * script itself) is handling GUI. This is ensured by appropriate calls to
327 * enable_interrupt and disable_interrupt. */
329 static VALUE s_interrupt_flag = Qfalse;
332 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
334 volatile VALUE message;
336 if (Ruby_GetInterruptFlag() == Qtrue) {
337 rb_scan_args(argc, argv, "01", &message);
339 p = StringValuePtr(message);
342 MyAppCallback_showProgressPanel(p);
348 s_HideProgressPanel(VALUE self)
350 MyAppCallback_hideProgressPanel();
355 s_SetProgressValue(VALUE self, VALUE val)
357 double dval = NUM2DBL(rb_Float(val));
358 MyAppCallback_setProgressValue(dval);
363 s_SetProgressMessage(VALUE self, VALUE msg)
368 else p = StringValuePtr(msg);
369 MyAppCallback_setProgressMessage(p);
374 s_SetInterruptFlag(VALUE self, VALUE val)
378 if (val == Qfalse || val == Qnil)
382 oldval = s_interrupt_flag;
384 s_interrupt_flag = val;
386 s_HideProgressPanel(self);
393 s_GetInterruptFlag(VALUE self)
395 return s_SetInterruptFlag(self, Qundef);
400 s_Ruby_CallMethod(VALUE val)
402 void **ptr = (void **)val;
403 VALUE receiver = (VALUE)ptr[0];
404 ID method_id = (ID)ptr[1];
405 VALUE args = (VALUE)ptr[2];
407 if (method_id == 0) {
408 /* args should be a string, which is evaluated */
409 if (receiver == Qnil) {
410 retval = rb_eval_string(StringValuePtr(args));
412 retval = rb_obj_instance_eval(1, &args, receiver);
415 /* args should be an array of arguments */
416 retval = rb_apply(receiver, method_id, args);
422 Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
424 VALUE retval, save_interrupt_flag;
426 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
427 ptr[0] = (void *)receiver;
428 ptr[1] = (void *)method_id;
429 ptr[2] = (void *)args;
430 MyAppCallback_beginUndoGrouping();
431 retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
432 MyAppCallback_endUndoGrouping();
433 s_SetInterruptFlag(Qnil, save_interrupt_flag);
434 MyAppCallback_hideProgressPanel(); /* In case when the progress panel is still onscreen */
\v
440 Ruby_SetInterruptFlag(VALUE val)
442 return s_SetInterruptFlag(Qnil, val);
446 Ruby_GetInterruptFlag(void)
448 return s_SetInterruptFlag(Qnil, Qundef);
453 * check_interrupt -> integer
455 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
458 s_Kernel_CheckInterrupt(VALUE self)
460 if (Ruby_GetInterruptFlag() == Qfalse)
462 else if (MyAppCallback_checkInterrupt())
464 else return INT2NUM(0);
467 static volatile unsigned long sITimerCount = 0;
470 static HANDLE sITimerEvent;
471 static HANDLE sITimerThread;
472 static int sITimerInterval;
474 static __stdcall unsigned
475 s_ITimerThreadFunc(void *p)
477 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
483 #elif USE_PTHREAD_FOR_TIMER
486 static pthread_t sTimerThread;
488 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
489 static volatile signed char sTimerFlag = -1;
490 static volatile int sTimerIntervalMicrosec = 0;
493 s_TimerThreadEntry(void *param)
496 usleep(sTimerIntervalMicrosec);
499 else if (sTimerFlag == -2)
508 s_SignalAction(int n)
514 s_SetIntervalTimer(int n, int msec)
518 /* Start interval timer */
519 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
520 sITimerInterval = msec;
522 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
525 /* Stop interval timer */
527 SetEvent(sITimerEvent); /* Tell thread to terminate */
529 WaitForSingleObject(sITimerThread, 1000);
530 CloseHandle(sITimerThread);
533 CloseHandle(sITimerEvent);
535 sITimerThread = NULL;
537 #elif USE_PTHREAD_FOR_TIMER
539 if (sTimerFlag == -1) {
540 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
542 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
545 sTimerFlag = 0; /* Active */
546 sTimerIntervalMicrosec = msec * 1000;
547 } else if (sTimerFlag != -1)
548 sTimerFlag = 1; /* Inactive */
550 static struct itimerval sOldValue;
551 static struct sigaction sOldAction;
552 struct itimerval val;
553 struct sigaction act;
556 act.sa_handler = s_SignalAction;
559 sigaction(SIGALRM, &act, &sOldAction);
560 val.it_value.tv_sec = 0;
561 val.it_value.tv_usec = msec * 1000;
562 val.it_interval.tv_sec = 0;
563 val.it_interval.tv_usec = msec * 1000;
564 setitimer(ITIMER_REAL, &val, &sOldValue);
566 setitimer(ITIMER_REAL, &sOldValue, &val);
567 sigaction(SIGALRM, &sOldAction, &act);
573 s_GetTimerCount(void)
579 s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
581 if (s_interrupt_flag != Qfalse) {
582 static unsigned long sLastTime = 0;
583 unsigned long currentTime;
585 currentTime = s_GetTimerCount();
586 if (currentTime != sLastTime) {
587 sLastTime = currentTime;
588 gMolbyIsCheckingInterrupt = 1;
589 flag = MyAppCallback_checkInterrupt();
590 gMolbyIsCheckingInterrupt = 0;
592 s_SetInterruptFlag(Qnil, Qfalse);
599 #pragma mark ====== Menu handling ======
603 * register_menu(title, method)
605 * Register the method (specified as a symbol) in the script menu.
606 * The method must be either an instance method of Molecule with no argument,
607 * or a class method of Molecule with one argument (the current molecule).
608 * The menu associated with the class method can be invoked even when no document
609 * is open (the argument is set to Qnil in this case). On the other hand, the
610 * menu associated with the instance method can only be invoked when at least one
611 * document is active.
614 s_Kernel_RegisterMenu(VALUE self, VALUE title, VALUE method)
616 if (TYPE(method) == T_SYMBOL) {
617 method = rb_funcall(method, rb_intern("to_s"), 0);
619 MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title));
624 s_Kernel_LookupMenu(VALUE self, VALUE title)
626 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
631 s_Ruby_methodType_sub(VALUE data)
633 const char **p = (const char **)data;
634 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
635 ID mid = rb_intern(p[1]);
637 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
639 else if (rb_respond_to(klass, mid))
642 return INT2FIX(ival);
645 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
646 has the singleton method (class method) with the given name, 0 otherwise. */
648 Ruby_methodType(const char *className, const char *methodName)
655 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
657 return FIX2INT(retval);
663 * execute_script_file(fname)
665 * Execute the script in the given file. If a molecule is active, then
666 * the script is evaluated as Molecule.current.instance_eval(script).
667 * Before entering the script, the current directory is set to the parent
668 * directory of the script.
671 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
674 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
675 if (retval == (VALUE)6 && status == -1)
676 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
686 * Get the directory suitable for storing user documents. On Windows
687 * it is the home directory + "My Documents". On other platforms
688 * it is the home directory.
691 s_Kernel_DocumentHome(VALUE self)
693 char *s = MyAppCallback_getDocumentHomeDir();
694 VALUE retval = Ruby_NewFileStringValue(s);
699 /* The callback functions for call_subprocess */
701 s_Kernel_CallSubProcess_CallbackSub(VALUE val)
703 return rb_funcall(val, rb_intern("call"), 0);
707 s_Kernel_CallSubProcess_Callback(void *data)
710 VALUE retval = rb_protect(s_Kernel_CallSubProcess_CallbackSub, (VALUE)data, &status);
711 if (status != 0 || retval == Qnil || retval == Qfalse)
718 * call_subprocess(cmd, process_name [, callback_proc])
720 * Call subprocess. A progress dialog window is displayed, with a message
721 * "Running #{process_name}...".
722 * A callback proc can be given, which is called periodically during execution. If the proc returns
723 * nil or false, then the execution will be interrupted.
726 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
728 VALUE cmd, procname, cproc;
730 rb_scan_args(argc, argv, "21", &cmd, &procname, &cproc);
731 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc));
735 #pragma mark ====== User defaults ======
739 * get_global_settings(key)
741 * Get a setting data for key from the application preferences.
744 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
746 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
748 VALUE retval = rb_eval_string(p);
756 * set_global_settings(key, value)
758 * Set a setting data for key to the application preferences.
761 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
763 VALUE sval = rb_inspect(value);
764 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
768 #pragma mark ====== Utility functions (protected funcall) ======
770 struct Ruby_funcall2_record {
778 s_Ruby_funcall2_sub(VALUE data)
780 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
781 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
785 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
787 struct Ruby_funcall2_record rec;
792 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
795 #pragma mark ====== ParameterRef Class ======
798 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
802 Data_Get_Struct(self, ParameterRef, pref);
804 *typep = pref->parType;
805 if (pref->parType == kElementParType) {
806 up = (UnionPar *)&gElementParameters[pref->idx];
808 up = ParameterRefGetPar(pref);
811 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
812 if (up->bond.src != 0 && up->bond.src != -1)
813 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
820 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
824 Data_Get_Struct(self, ParameterRef, pref);
825 if (pref->mol == NULL)
827 up = ParameterRefGetPar(pref);
828 if (key != s_SourceSym)
829 up->bond.src = 0; /* Becomes automatically molecule-local */
830 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
833 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
834 MolActionCallback_registerUndo(pref->mol, act);
835 MoleculeCallback_notifyModification(pref->mol, 0);
836 pref->mol->needsMDRebuild = 1;
841 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
843 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
845 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
847 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
851 s_AtomTypeIndexFromValue(VALUE val)
853 if (rb_obj_is_kind_of(val, rb_cNumeric))
856 return AtomTypeEncodeToUInt(StringValuePtr(val));
859 static const char *s_ParameterTypeNames[] = {
860 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
862 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
865 s_ParTypeFromValue(VALUE val)
869 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
870 if (s_ParameterTypeIDs[0] == 0) {
871 for (i = 0; i < n; i++)
872 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
874 valid = rb_to_id(val);
875 for (i = 0; i < n; i++) {
876 if (valid == s_ParameterTypeIDs[i]) {
878 return kElementParType;
879 else return kFirstParType + i;
882 return kInvalidParType;
889 * Get the index in the parameter list.
891 static VALUE s_ParameterRef_GetIndex(VALUE self) {
893 Data_Get_Struct(self, ParameterRef, pref);
894 return INT2NUM(pref->idx);
901 * Get the parameter type, like "bond", "angle", etc.
903 static VALUE s_ParameterRef_GetParType(VALUE self) {
905 s_UnionParFromValue(self, &tp, 0);
906 if (tp == kElementParType)
907 return rb_str_new2("element");
909 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
910 return rb_str_new2(s_ParameterTypeNames[tp]);
911 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
916 * atom_type -> String or Array of String
917 * atom_types -> String or Array of String
919 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
920 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
921 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
922 * The atom type may be "X", which is a wildcard that matches any atom type.
924 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
929 up = s_UnionParFromValue(self, &tp, 0);
930 n = ParameterGetAtomTypes(tp, up, types);
932 rb_raise(rb_eMolbyError, "invalid member atom_types");
933 for (i = 0; i < n; i++) {
934 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
935 vals[i] = INT2NUM(types[i]);
937 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
942 return rb_ary_new4(n, vals);
949 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
951 static VALUE s_ParameterRef_GetK(VALUE self) {
955 up = s_UnionParFromValue(self, &tp, 0);
958 return rb_float_new(up->bond.k * INTERNAL2KCAL);
960 return rb_float_new(up->angle.k * INTERNAL2KCAL);
961 case kDihedralParType:
962 case kImproperParType:
963 if (up->torsion.mult == 1)
964 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
965 n = up->torsion.mult;
968 for (i = 0; i < n; i++)
969 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
970 return rb_ary_new4(n, vals);
972 rb_raise(rb_eMolbyError, "invalid member k");
980 * Get the equilibrium bond length. Only available for bond parameters.
982 static VALUE s_ParameterRef_GetR0(VALUE self) {
985 up = s_UnionParFromValue(self, &tp, 0);
986 if (tp == kBondParType)
987 return rb_float_new(up->bond.r0);
988 else rb_raise(rb_eMolbyError, "invalid member r0");
995 * Get the equilibrium angle (in degree). Only available for angle parameters.
997 static VALUE s_ParameterRef_GetA0(VALUE self) {
1000 up = s_UnionParFromValue(self, &tp, 0);
1001 if (tp == kAngleParType)
1002 return rb_float_new(up->angle.a0 * kRad2Deg);
1003 else rb_raise(rb_eMolbyError, "invalid member a0");
1010 * Get the multiplicity. Only available for dihedral and improper parameters.
1011 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1013 static VALUE s_ParameterRef_GetMult(VALUE self) {
1016 up = s_UnionParFromValue(self, &tp, 0);
1017 if (tp == kDihedralParType || tp == kImproperParType)
1018 return rb_float_new(up->torsion.mult);
1019 else rb_raise(rb_eMolbyError, "invalid member mult");
1024 * period -> Integer or Array of Integers
1026 * Get the periodicity. Only available for dihedral and improper parameters.
1027 * If the multiplicity is larger than 1, then an array of integers is returned.
1028 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1030 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1034 up = s_UnionParFromValue(self, &tp, 0);
1035 if (tp == kDihedralParType || tp == kImproperParType) {
1036 if (up->torsion.mult == 1)
1037 return INT2NUM(up->torsion.period[0]);
1038 n = up->torsion.mult;
1041 for (i = 0; i < n; i++)
1042 vals[i] = INT2NUM(up->torsion.period[i]);
1043 return rb_ary_new4(n, vals);
1044 } else rb_raise(rb_eMolbyError, "invalid member period");
1049 * phi0 -> Float or Array of Floats
1051 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1052 * If the multiplicity is larger than 1, then an array of floats is returned.
1053 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1055 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1059 up = s_UnionParFromValue(self, &tp, 0);
1060 if (tp == kDihedralParType || tp == kImproperParType) {
1061 if (up->torsion.mult == 1)
1062 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1063 n = up->torsion.mult;
1066 for (i = 0; i < n; i++)
1067 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1068 return rb_ary_new4(n, vals);
1069 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1076 * Get the "A" value for the van der Waals parameter.
1079 static VALUE s_ParameterRef_GetA(VALUE self) {
1082 up = s_UnionParFromValue(self, &tp, 0);
1083 if (tp == kVdwParType)
1084 return rb_float_new(up->vdw.A);
1085 else if (tp == kVdwPairParType)
1086 return rb_float_new(up->vdwp.A);
1087 else rb_raise(rb_eMolbyError, "invalid member A");
1095 * Get the "B" value for the van der Waals parameter.
1098 static VALUE s_ParameterRef_GetB(VALUE self) {
1101 up = s_UnionParFromValue(self, &tp, 0);
1102 if (tp == kVdwParType)
1103 return rb_float_new(up->vdw.B);
1104 else if (tp == kVdwPairParType)
1105 return rb_float_new(up->vdwp.B);
1106 else rb_raise(rb_eMolbyError, "invalid member B");
1114 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1116 static VALUE s_ParameterRef_GetReq(VALUE self) {
1119 /* Double a, b, r; */
1121 up = s_UnionParFromValue(self, &tp, 0);
1122 if (tp == kVdwParType) {
1126 } else if (tp == kVdwPairParType) {
1130 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1131 /* if (a == 0.0 || b == 0.0) */
1132 return rb_float_new(r);
1133 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1140 * Get the minimum energy for the van der Waals parameter.
1142 static VALUE s_ParameterRef_GetEps(VALUE self) {
1147 up = s_UnionParFromValue(self, &tp, 0);
1148 if (tp == kVdwParType) {
1152 } else if (tp == kVdwPairParType) {
1156 } else rb_raise(rb_eMolbyError, "invalid member eps");
1157 /* if (a == 0.0 || b == 0.0) */
1158 return rb_float_new(eps * INTERNAL2KCAL);
1159 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1166 * Get the "A" value for the 1-4 van der Waals parameter.
1169 static VALUE s_ParameterRef_GetA14(VALUE self) {
1172 up = s_UnionParFromValue(self, &tp, 0);
1173 if (tp == kVdwParType)
1174 return rb_float_new(up->vdw.A14);
1175 else if (tp == kVdwPairParType)
1176 return rb_float_new(up->vdwp.A14);
1177 else rb_raise(rb_eMolbyError, "invalid member A14");
1185 * Get the "B" value for the 1-4 van der Waals parameter.
1188 static VALUE s_ParameterRef_GetB14(VALUE self) {
1191 up = s_UnionParFromValue(self, &tp, 0);
1192 if (tp == kVdwParType)
1193 return rb_float_new(up->vdw.B14);
1194 else if (tp == kVdwPairParType)
1195 return rb_float_new(up->vdwp.B14);
1196 else rb_raise(rb_eMolbyError, "invalid member B14");
1204 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1206 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1209 /* Double a, b, r; */
1211 up = s_UnionParFromValue(self, &tp, 0);
1212 if (tp == kVdwParType) {
1216 } else if (tp == kVdwPairParType) {
1217 /* a = up->vdwp.A14;
1218 b = up->vdwp.B14; */
1219 r = up->vdwp.r_eq14;
1220 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1221 /* if (a == 0.0 || b == 0.0) */
1222 return rb_float_new(r);
1223 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1230 * Get the minimum energy for the 1-4 van der Waals parameter.
1232 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1237 up = s_UnionParFromValue(self, &tp, 0);
1238 if (tp == kVdwParType) {
1242 } else if (tp == kVdwPairParType) {
1243 /* a = up->vdwp.A14;
1244 b = up->vdwp.B14; */
1246 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1247 /* if (a == 0.0 || b == 0.0) */
1248 return rb_float_new(eps * INTERNAL2KCAL);
1249 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1256 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1258 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1261 up = s_UnionParFromValue(self, &tp, 0);
1262 if (tp == kVdwCutoffParType)
1263 return rb_float_new(up->vdwcutoff.cutoff);
1264 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1271 * Get the atomic radius for the atom display parameter.
1273 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1276 up = s_UnionParFromValue(self, &tp, 0);
1277 if (tp == kElementParType)
1278 return rb_float_new(up->atom.radius);
1279 else rb_raise(rb_eMolbyError, "invalid member radius");
1284 * color -> [Float, Float, Float]
1286 * Get the rgb color for the atom display parameter.
1288 static VALUE s_ParameterRef_GetColor(VALUE self) {
1291 up = s_UnionParFromValue(self, &tp, 0);
1292 if (tp == kElementParType)
1293 return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
1294 else rb_raise(rb_eMolbyError, "invalid member color");
1299 * atomic_number -> Integer
1301 * Get the atomic number for the vdw or atom parameter.
1303 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1306 up = s_UnionParFromValue(self, &tp, 0);
1307 if (tp == kElementParType)
1308 return INT2NUM(up->atom.number);
1309 else if (tp == kVdwParType)
1310 return INT2NUM(up->vdw.atomicNumber);
1311 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1318 * Get the name for the atom display parameter.
1320 static VALUE s_ParameterRef_GetName(VALUE self) {
1323 up = s_UnionParFromValue(self, &tp, 0);
1324 if (tp == kElementParType) {
1326 strncpy(name, up->atom.name, 4);
1328 return rb_str_new2(name);
1329 } else rb_raise(rb_eMolbyError, "invalid member name");
1336 * Get the atomic weight for the atom display parameter.
1338 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1341 up = s_UnionParFromValue(self, &tp, 0);
1342 if (tp == kElementParType)
1343 return rb_float_new(up->atom.weight);
1344 else if (tp == kVdwParType)
1345 return rb_float_new(up->vdw.weight);
1346 else rb_raise(rb_eMolbyError, "invalid member weight");
1351 * fullname -> String
1353 * Get the full name for the atom display parameter.
1355 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1358 up = s_UnionParFromValue(self, &tp, 0);
1359 if (tp == kElementParType) {
1361 strncpy(fullname, up->atom.fullname, 15);
1363 return rb_str_new2(fullname);
1364 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1371 * Get the comment for the parameter.
1373 static VALUE s_ParameterRef_GetComment(VALUE self) {
1376 up = s_UnionParFromValue(self, &tp, 0);
1380 else return rb_str_new2(ParameterGetComment(com));
1387 * Get the source string for the parameter. Returns false for undefined parameter,
1388 * and nil for "local" parameter that is specific for the molecule.
1390 static VALUE s_ParameterRef_GetSource(VALUE self) {
1393 up = s_UnionParFromValue(self, &tp, 0);
1396 return Qfalse; /* undefined */
1398 return Qnil; /* local */
1399 else return rb_str_new2(ParameterGetComment(src));
1403 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1410 if (rb_obj_is_kind_of(val, rb_cString)) {
1411 char *s = StringValuePtr(val);
1413 for (i = 0; i < n; i++) {
1416 /* Skip leading separaters */
1417 while (*s == '-' || *s == ' ' || *s == '\t')
1419 for (p = s; *p != 0; p++) {
1420 if (*p == '-' || *p == ' ' || *p == '\t')
1424 if (len >= sizeof(buf))
1425 len = sizeof(buf) - 1;
1426 strncpy(buf, s, len);
1428 /* Skip trailing blanks */
1429 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1432 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1433 if (buf[0] >= '0' && buf[0] <= '9')
1434 types[i] = atoi(buf);
1436 types[i] = AtomTypeEncodeToUInt(buf);
1437 if (p == NULL || *p == 0) {
1443 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1446 val = rb_ary_to_ary(val);
1447 if (RARRAY_LEN(val) != n)
1448 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1449 valp = RARRAY_PTR(val);
1451 for (i = 0; i < n; i++) {
1452 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1453 types[i] = NUM2INT(rb_Integer(valp[i]));
1455 VALUE sval = valp[i];
1456 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1461 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1466 up = s_UnionParFromValue(self, &tp, 1);
1467 oldval = s_ParameterRef_GetAtomTypes(self);
1468 oldsrc = up->bond.src;
1471 s_ScanAtomTypes(val, 2, types);
1472 up->bond.type1 = types[0];
1473 up->bond.type2 = types[1];
1476 s_ScanAtomTypes(val, 3, types);
1477 up->angle.type1 = types[0];
1478 up->angle.type2 = types[1];
1479 up->angle.type3 = types[2];
1481 case kDihedralParType:
1482 case kImproperParType:
1483 s_ScanAtomTypes(val, 4, types);
1484 up->torsion.type1 = types[0];
1485 up->torsion.type2 = types[1];
1486 up->torsion.type3 = types[2];
1487 up->torsion.type4 = types[3];
1490 s_ScanAtomTypes(val, 1, types);
1491 up->vdw.type1 = types[0];
1493 case kVdwPairParType:
1494 s_ScanAtomTypes(val, 2, types);
1495 up->vdwp.type1 = types[0];
1496 up->vdwp.type2 = types[1];
1498 case kVdwCutoffParType:
1499 s_ScanAtomTypes(val, 2, types);
1500 up->vdwcutoff.type1 = types[0];
1501 up->vdwcutoff.type2 = types[1];
1506 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1510 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1512 Int tp, i, n, oldsrc;
1513 VALUE *valp, oldval;
1514 up = s_UnionParFromValue(self, &tp, 1);
1515 oldval = s_ParameterRef_GetK(self);
1516 oldsrc = up->bond.src;
1519 val = rb_Float(val);
1520 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1523 val = rb_Float(val);
1524 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1526 case kDihedralParType:
1527 case kImproperParType:
1528 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1529 up->torsion.mult = 1;
1530 val = rb_Float(val);
1531 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1534 n = up->torsion.mult;
1537 val = rb_ary_to_ary(val);
1538 if (RARRAY_LEN(val) != n)
1539 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1540 valp = RARRAY_PTR(val);
1541 for (i = 0; i < n; i++) {
1542 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1546 rb_raise(rb_eMolbyError, "invalid member k");
1548 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1552 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1556 up = s_UnionParFromValue(self, &tp, 1);
1557 oldval = s_ParameterRef_GetR0(self);
1558 oldsrc = up->bond.src;
1559 if (tp == kBondParType) {
1560 val = rb_Float(val);
1561 up->bond.r0 = NUM2DBL(val);
1562 } else rb_raise(rb_eMolbyError, "invalid member r0");
1563 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1567 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1571 up = s_UnionParFromValue(self, &tp, 1);
1572 oldval = s_ParameterRef_GetA0(self);
1573 oldsrc = up->bond.src;
1574 if (tp == kAngleParType) {
1575 val = rb_Float(val);
1576 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1577 } else rb_raise(rb_eMolbyError, "invalid member a0");
1578 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1582 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1586 up = s_UnionParFromValue(self, &tp, 1);
1587 oldval = s_ParameterRef_GetMult(self);
1588 oldsrc = up->bond.src;
1589 if (tp == kDihedralParType || tp == kImproperParType) {
1591 val = rb_Integer(val);
1594 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1595 up->torsion.mult = i;
1596 } else rb_raise(rb_eMolbyError, "invalid member mult");
1597 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1601 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1603 Int tp, i, n, oldsrc;
1604 VALUE *valp, oldval;
1605 up = s_UnionParFromValue(self, &tp, 1);
1606 oldval = s_ParameterRef_GetPeriod(self);
1607 oldsrc = up->bond.src;
1608 if (tp == kDihedralParType || tp == kImproperParType) {
1609 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1610 up->torsion.mult = 1;
1611 val = rb_Integer(val);
1612 up->torsion.period[0] = NUM2INT(val);
1614 n = up->torsion.mult;
1617 val = rb_ary_to_ary(val);
1618 if (RARRAY_LEN(val) != n)
1619 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1620 valp = RARRAY_PTR(val);
1621 for (i = 0; i < n; i++) {
1622 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1625 } else rb_raise(rb_eMolbyError, "invalid member period");
1626 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1630 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1632 Int tp, i, n, oldsrc;
1633 VALUE *valp, oldval;
1634 up = s_UnionParFromValue(self, &tp, 1);
1635 oldval = s_ParameterRef_GetPhi0(self);
1636 oldsrc = up->bond.src;
1637 if (tp == kDihedralParType || tp == kImproperParType) {
1638 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1639 up->torsion.mult = 1;
1640 val = rb_Float(val);
1641 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1643 n = up->torsion.mult;
1646 val = rb_ary_to_ary(val);
1647 if (RARRAY_LEN(val) != n)
1648 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1649 valp = RARRAY_PTR(val);
1650 for (i = 0; i < n; i++)
1651 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
1653 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1654 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
1659 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
1664 up = s_UnionParFromValue(self, &tp, 1);
1665 oldval = s_ParameterRef_GetA(self);
1666 oldsrc = up->bond.src;
1667 val = rb_Float(val);
1669 if (tp == kVdwParType)
1671 else if (tp == kVdwPairParType)
1673 else rb_raise(rb_eMolbyError, "invalid member A");
1674 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
1678 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
1683 up = s_UnionParFromValue(self, &tp, 1);
1684 oldval = s_ParameterRef_GetB(self);
1685 oldsrc = up->bond.src;
1686 val = rb_Float(val);
1688 if (tp == kVdwParType)
1690 else if (tp == kVdwPairParType)
1692 else rb_raise(rb_eMolbyError, "invalid member B");
1693 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
1698 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
1703 up = s_UnionParFromValue(self, &tp, 1);
1704 oldval = s_ParameterRef_GetReq(self);
1705 oldsrc = up->bond.src;
1706 val = rb_Float(val);
1708 if (tp == kVdwParType) {
1710 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
1711 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
1712 } else if (tp == kVdwPairParType) {
1714 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
1715 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
1716 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1717 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
1721 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
1726 up = s_UnionParFromValue(self, &tp, 1);
1727 oldval = s_ParameterRef_GetEps(self);
1728 oldsrc = up->bond.src;
1729 val = rb_Float(val);
1730 e = NUM2DBL(val) * KCAL2INTERNAL;
1731 if (tp == kVdwParType) {
1733 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
1734 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
1735 } else if (tp == kVdwPairParType) {
1737 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
1738 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
1739 } else rb_raise(rb_eMolbyError, "invalid member eps");
1740 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
1745 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
1750 up = s_UnionParFromValue(self, &tp, 1);
1751 oldval = s_ParameterRef_GetA14(self);
1752 oldsrc = up->bond.src;
1753 val = rb_Float(val);
1755 if (tp == kVdwParType)
1757 else if (tp == kVdwPairParType)
1759 else rb_raise(rb_eMolbyError, "invalid member A14");
1760 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
1764 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
1769 up = s_UnionParFromValue(self, &tp, 1);
1770 oldval = s_ParameterRef_GetB14(self);
1771 oldsrc = up->bond.src;
1772 val = rb_Float(val);
1774 if (tp == kVdwParType)
1776 else if (tp == kVdwPairParType)
1778 else rb_raise(rb_eMolbyError, "invalid member B14");
1779 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
1784 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
1789 up = s_UnionParFromValue(self, &tp, 1);
1790 oldval = s_ParameterRef_GetReq14(self);
1791 oldsrc = up->bond.src;
1792 val = rb_Float(val);
1794 if (tp == kVdwParType) {
1796 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1797 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1798 } else if (tp == kVdwPairParType) {
1799 up->vdwp.r_eq14 = r;
1800 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1801 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1802 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1803 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
1807 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
1812 up = s_UnionParFromValue(self, &tp, 1);
1813 oldval = s_ParameterRef_GetEps14(self);
1814 oldsrc = up->bond.src;
1815 val = rb_Float(val);
1816 e = NUM2DBL(val) * KCAL2INTERNAL;
1817 if (tp == kVdwParType) {
1819 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1820 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1821 } else if (tp == kVdwPairParType) {
1823 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1824 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1825 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1826 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
1830 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
1834 oldval = s_ParameterRef_GetCutoff(self);
1835 oldsrc = up->bond.src;
1836 up = s_UnionParFromValue(self, &tp, 1);
1837 val = rb_Float(val);
1838 if (tp == kVdwCutoffParType) {
1839 up->vdwcutoff.cutoff = NUM2DBL(val);
1840 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
1841 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
1845 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
1849 up = s_UnionParFromValue(self, &tp, 1);
1850 oldval = s_ParameterRef_GetRadius(self);
1851 oldsrc = up->bond.src;
1852 val = rb_Float(val);
1853 if (tp == kElementParType) {
1854 up->atom.radius = NUM2DBL(val);
1855 } else rb_raise(rb_eMolbyError, "invalid member radius");
1856 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
1860 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
1863 VALUE *valp, oldval;
1864 up = s_UnionParFromValue(self, &tp, 1);
1865 oldval = s_ParameterRef_GetColor(self);
1866 oldsrc = up->bond.src;
1867 val = rb_ary_to_ary(val);
1868 if (RARRAY_LEN(val) != 3)
1869 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
1870 valp = RARRAY_PTR(val);
1871 if (tp == kElementParType) {
1872 up->atom.r = NUM2DBL(rb_Float(valp[0]));
1873 up->atom.g = NUM2DBL(rb_Float(valp[1]));
1874 up->atom.b = NUM2DBL(rb_Float(valp[2]));
1875 } else rb_raise(rb_eMolbyError, "invalid member color");
1876 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
1880 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
1884 up = s_UnionParFromValue(self, &tp, 1);
1885 oldval = s_ParameterRef_GetAtomicNumber(self);
1886 oldsrc = up->bond.src;
1887 val = rb_Integer(val);
1888 if (tp == kElementParType)
1889 up->atom.number = NUM2INT(val);
1890 else if (tp == kVdwParType) {
1891 up->vdw.atomicNumber = NUM2INT(val);
1892 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
1893 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1894 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
1898 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
1902 up = s_UnionParFromValue(self, &tp, 1);
1903 oldval = s_ParameterRef_GetName(self);
1904 oldsrc = up->bond.src;
1905 if (tp == kElementParType) {
1906 strncpy(up->atom.name, StringValuePtr(val), 4);
1907 } else rb_raise(rb_eMolbyError, "invalid member name");
1908 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
1912 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
1916 val = rb_Float(val);
1917 oldval = s_ParameterRef_GetWeight(self);
1918 up = s_UnionParFromValue(self, &tp, 1);
1919 oldsrc = up->bond.src;
1920 if (tp == kElementParType)
1921 up->atom.weight = NUM2DBL(val);
1922 else if (tp == kVdwParType)
1923 up->vdw.weight = NUM2DBL(val);
1924 else rb_raise(rb_eMolbyError, "invalid member weight");
1925 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
1929 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
1933 up = s_UnionParFromValue(self, &tp, 1);
1934 oldval = s_ParameterRef_GetFullName(self);
1935 oldsrc = up->bond.src;
1936 if (tp == kElementParType) {
1937 strncpy(up->atom.fullname, StringValuePtr(val), 15);
1938 up->atom.fullname[15] = 0;
1939 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1940 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
1944 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
1946 Int tp, com, oldsrc;
1948 up = s_UnionParFromValue(self, &tp, 1);
1949 oldval = s_ParameterRef_GetComment(self);
1950 oldsrc = up->bond.src;
1954 com = ParameterCommentIndex(StringValuePtr(val));
1957 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
1961 /* Only false (undefined) and nil (local) can be set */
1962 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
1966 up = s_UnionParFromValue(self, &tp, 1);
1967 if (val != Qfalse && val != Qnil)
1968 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
1969 oldval = s_ParameterRef_GetSource(self);
1970 oldsrc = up->bond.src;
1971 if (oldsrc != 0 && oldsrc != -1)
1972 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
1973 up->bond.src = (val == Qfalse ? -1 : 0);
1974 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
1978 static struct s_ParameterAttrDef {
1980 VALUE *symref; /* Address of s_IndexSymbol etc. */
1981 ID id; /* Will be set within InitMolby() */
1982 VALUE (*getter)(VALUE);
1983 VALUE (*setter)(VALUE, VALUE);
1984 } s_ParameterAttrDefTable[] = {
1985 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
1986 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
1987 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
1988 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
1989 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
1990 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
1991 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
1992 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
1993 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
1994 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
1995 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
1996 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
1997 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
1998 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
1999 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2000 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2001 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2002 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2003 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2004 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2005 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2006 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2007 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2008 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2009 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2010 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2011 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2012 {NULL} /* Sentinel */
2016 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2020 if (TYPE(key) != T_SYMBOL) {
2021 kid = rb_intern(StringValuePtr(key));
2023 } else kid = SYM2ID(key);
2024 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2025 if (s_ParameterAttrDefTable[i].id == kid) {
2026 if (value == Qundef)
2027 return (*(s_ParameterAttrDefTable[i].getter))(self);
2029 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2032 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2033 return Qnil; /* not reached */
2037 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2039 return s_ParameterRef_SetAttr(self, key, Qundef);
2044 * keys(idx) -> array of valid parameter attributes
2046 * Returns an array of valid parameter attributes (as Symbols).
2049 s_ParameterRef_Keys(VALUE self)
2052 Data_Get_Struct(self, ParameterRef, pref);
2053 switch (pref->parType) {
2055 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2057 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2058 case kDihedralParType:
2059 case kImproperParType:
2060 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2062 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);
2063 case kVdwPairParType:
2064 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2065 case kVdwCutoffParType:
2066 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2067 case kElementParType:
2068 return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_CommentSym, s_SourceSym);
2070 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2072 return Qnil; /* Not reached */
2077 * to_hash(idx) -> Hash
2079 * Returns a hash containing valid parameter names and values
2082 s_ParameterRef_ToHash(VALUE self)
2084 VALUE keys = s_ParameterRef_Keys(self);
2089 retval = rb_hash_new();
2090 for (i = 0; i < RARRAY_LEN(keys); i++) {
2091 VALUE key = RARRAY_PTR(keys)[i];
2092 VALUE val = s_ParameterRef_GetAttr(self, key);
2093 rb_hash_aset(retval, key, val);
2100 * parameter.to_s(idx) -> String
2102 * Returns a string representation of the given parameter
2105 s_ParameterRef_ToString(VALUE self)
2108 char buf[1024], types[4][8];
2109 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2112 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);
2115 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);
2117 case kDihedralParType:
2118 case kImproperParType:
2119 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]));
2121 for (i = 0; i < up->torsion.mult; i++) {
2122 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);
2127 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);
2129 case kVdwPairParType:
2130 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);
2132 case kVdwCutoffParType:
2133 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);
2135 case kElementParType:
2136 snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s", up->atom.name, up->atom.number, up->atom.radius, up->atom.r, up->atom.g, up->atom.b, up->atom.weight, up->atom.fullname);
2139 return rb_str_new2(buf);
2144 * self == parameterRef -> boolean
2146 * True if the parameters point to the same parameter record.
2149 s_ParameterRef_Equal(VALUE self, VALUE val)
2152 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2153 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2154 } else return Qfalse;
2157 #pragma mark ====== Parameter Class ======
2159 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2160 * is NULL, then the global parameters are looked for. */
2162 /* Rebuild the MD parameter record if necessary: may throw an exception */
2163 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2165 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2168 Data_Get_Struct(val, Molecule, mol);
2170 rb_raise(rb_eMolbyError, "the molecule is empty");
2171 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2172 /* Do self.md_arena.prepare */
2173 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2175 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2180 s_NewParameterValueFromValue(VALUE val)
2183 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2184 Data_Get_Struct(val, Molecule, mol);
2185 s_RebuildMDParameterIfNecessary(val, Qtrue);
2186 MoleculeRetain(mol);
2187 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2190 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2195 s_MoleculeFromParameterValue(VALUE val)
2198 Data_Get_Struct(val, Molecule, mol);
2203 s_ParameterFromParameterValue(VALUE val)
2206 Data_Get_Struct(val, Molecule, mol);
2209 return gBuiltinParameters;
2212 /* Forward declarations */
2213 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2214 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2217 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2219 if (val == rb_cParameter) {
2220 return NULL; /* Parameter class method: builtin parameters */
2221 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2222 return s_MoleculeFromParameterValue(val);
2223 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2224 return s_MoleculeFromParEnumerableValue(val);
2230 * builtin -> Parameter
2232 * Returns a parameter value that points to the global (builtin) parameters.
2233 * Equivalent to Parameter::Builtin (constant).
2236 s_Parameter_Builtin(VALUE self)
2238 static ID s_builtin_id = 0;
2239 if (s_builtin_id == 0)
2240 s_builtin_id = rb_intern("Builtin");
2241 return rb_const_get(rb_cParameter, s_builtin_id);
2246 * bond(idx) -> ParameterRef
2248 * The index-th bond parameter record is returned.
2251 s_Parameter_Bond(VALUE self, VALUE ival)
2255 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2256 idx = NUM2INT(rb_Integer(ival));
2258 n = gBuiltinParameters->nbondPars;
2259 else if (mol->par != NULL)
2260 n = mol->par->nbondPars;
2262 if (idx < -n || idx >= n)
2263 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2266 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2271 * angle(idx) -> ParameterRef
2273 * The index-th angle parameter record is returned.
2276 s_Parameter_Angle(VALUE self, VALUE ival)
2280 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2281 idx = NUM2INT(rb_Integer(ival));
2283 n = gBuiltinParameters->nanglePars;
2284 else if (mol->par != NULL)
2285 n = mol->par->nanglePars;
2287 if (idx < -n || idx >= n)
2288 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2291 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2296 * dihedral(idx) -> ParameterRef
2298 * The index-th dihedral parameter record is returned.
2301 s_Parameter_Dihedral(VALUE self, VALUE ival)
2305 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2306 idx = NUM2INT(rb_Integer(ival));
2308 n = gBuiltinParameters->ndihedralPars;
2309 else if (mol->par != NULL)
2310 n = mol->par->ndihedralPars;
2312 if (idx < -n || idx >= n)
2313 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2316 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2321 * improper(idx) -> ParameterRef
2323 * The index-th improper parameter record is returned.
2326 s_Parameter_Improper(VALUE self, VALUE ival)
2330 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2331 idx = NUM2INT(rb_Integer(ival));
2333 n = gBuiltinParameters->nimproperPars;
2334 else if (mol->par != NULL)
2335 n = mol->par->nimproperPars;
2337 if (idx < -n || idx >= n)
2338 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2341 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2346 * vdw(idx) -> ParameterRef
2348 * The index-th vdw parameter record is returned.
2351 s_Parameter_Vdw(VALUE self, VALUE ival)
2355 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2356 idx = NUM2INT(rb_Integer(ival));
2358 n = gBuiltinParameters->nvdwPars;
2359 else if (mol->par != NULL)
2360 n = mol->par->nvdwPars;
2362 if (idx < -n || idx >= n)
2363 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2366 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2371 * vdw_pair(idx) -> ParameterRef
2373 * The index-th vdw pair parameter record is returned.
2376 s_Parameter_VdwPair(VALUE self, VALUE ival)
2380 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2381 idx = NUM2INT(rb_Integer(ival));
2383 n = gBuiltinParameters->nvdwpPars;
2384 else if (mol->par != NULL)
2385 n = mol->par->nvdwpPars;
2387 if (idx < -n || idx >= n)
2388 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2391 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2396 * vdw_cutoff(idx) -> ParameterRef
2398 * The index-th vdw cutoff parameter record is returned.
2401 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2405 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2406 idx = NUM2INT(rb_Integer(ival));
2408 n = gBuiltinParameters->nvdwCutoffPars;
2409 else if (mol->par != NULL)
2410 n = mol->par->nvdwCutoffPars;
2412 if (idx < -n || idx >= n)
2413 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2416 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2421 * element(idx) -> ParameterRef
2422 * element(t1) -> ParameterRef
2424 * In the first form, the index-th element parameter record is returned. In the second
2425 * form, the element parameter for t1 is looked up (the last index first). t1
2426 * is the element name string (up to 4 characters).
2427 * Unlike other Parameter methods, this is used only for the global parameter.
2430 s_Parameter_Element(VALUE self, VALUE ival)
2433 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2434 int n = gCountElementParameters;
2435 idx1 = NUM2INT(rb_Integer(ival));
2436 if (idx1 < -n || idx1 >= n)
2437 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2440 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2445 strncpy(name, StringValuePtr(ival), 4);
2447 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2448 if (strncmp(ep->name, name, 4) == 0)
2449 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2459 * Returns the number of bond parameters.
2462 s_Parameter_Nbonds(VALUE self)
2465 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2467 n = gBuiltinParameters->nbondPars;
2468 else if (mol->par != NULL)
2469 n = mol->par->nbondPars;
2476 * nangles -> Integer
2478 * Returns the number of angle parameters.
2481 s_Parameter_Nangles(VALUE self)
2484 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2486 n = gBuiltinParameters->nanglePars;
2487 else if (mol->par != NULL)
2488 n = mol->par->nanglePars;
2495 * ndihedrals -> Integer
2497 * Returns the number of dihedral parameters.
2500 s_Parameter_Ndihedrals(VALUE self)
2503 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2505 n = gBuiltinParameters->ndihedralPars;
2506 else if (mol->par != NULL)
2507 n = mol->par->ndihedralPars;
2514 * nimpropers -> Integer
2516 * Returns the number of improper parameters.
2519 s_Parameter_Nimpropers(VALUE self)
2522 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2524 n = gBuiltinParameters->nimproperPars;
2525 else if (mol->par != NULL)
2526 n = mol->par->nimproperPars;
2535 * Returns the number of vdw parameters.
2538 s_Parameter_Nvdws(VALUE self)
2541 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2543 n = gBuiltinParameters->nvdwPars;
2544 else if (mol->par != NULL)
2545 n = mol->par->nvdwPars;
2552 * nvdw_pairs -> Integer
2554 * Returns the number of vdw pair parameters.
2557 s_Parameter_NvdwPairs(VALUE self)
2560 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2562 n = gBuiltinParameters->nvdwpPars;
2563 else if (mol->par != NULL)
2564 n = mol->par->nvdwpPars;
2571 * nvdw_cutoffs -> Integer
2573 * Returns the number of vdw cutoff parameters.
2576 s_Parameter_NvdwCutoffs(VALUE self)
2579 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2581 n = gBuiltinParameters->nvdwCutoffPars;
2582 else if (mol->par != NULL)
2583 n = mol->par->nvdwCutoffPars;
2590 * nelements -> Integer
2592 * Returns the number of element parameters.
2595 s_Parameter_Nelements(VALUE self)
2597 return INT2NUM(gCountElementParameters);
2602 * bonds -> ParEnumerable
2604 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2605 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2606 * useful when all accessible parameters should be examined by use of 'each' method.
2609 s_Parameter_Bonds(VALUE self)
2611 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2612 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2617 * angles -> ParEnumerable
2619 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2620 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2621 * useful when all accessible parameters should be examined by use of 'each' method.
2624 s_Parameter_Angles(VALUE self)
2626 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2627 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2632 * dihedrals -> ParEnumerable
2634 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
2635 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
2636 * useful when all accessible parameters should be examined by use of 'each' method.
2639 s_Parameter_Dihedrals(VALUE self)
2641 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2642 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
2647 * impropers -> ParEnumerable
2649 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
2650 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
2651 * useful when all accessible parameters should be examined by use of 'each' method.
2654 s_Parameter_Impropers(VALUE self)
2656 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2657 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
2662 * vdws -> ParEnumerable
2664 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
2665 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
2666 * useful when all accessible parameters should be examined by use of 'each' method.
2669 s_Parameter_Vdws(VALUE self)
2671 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2672 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
2677 * vdw_pairs -> ParEnumerable
2679 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
2680 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
2681 * useful when all accessible parameters should be examined by use of 'each' method.
2684 s_Parameter_VdwPairs(VALUE self)
2686 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2687 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
2692 * vdw_cutoffs -> ParEnumerable
2694 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
2695 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
2696 * useful when all accessible parameters should be examined by use of 'each' method.
2699 s_Parameter_VdwCutoffs(VALUE self)
2701 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2702 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
2707 * elements -> ParEnumerable
2709 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
2710 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
2711 * useful when all accessible parameters should be examined by use of 'each' method.
2714 s_Parameter_Elements(VALUE self)
2716 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2717 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
2721 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
2723 VALUE atval, optval;
2726 int i, n, idx, flags, is_global;
2728 rb_scan_args(argc, argv, "1*", &atval, &optval);
2730 /* Get the atom types */
2732 case kBondParType: n = 2; break;
2733 case kAngleParType: n = 3; break;
2734 case kDihedralParType: n = 4; break;
2735 case kImproperParType: n = 4; break;
2736 case kVdwParType: n = 1; break;
2737 case kVdwPairParType: n = 2; break;
2738 default: return Qnil;
2740 s_ScanAtomTypes(atval, n, t);
2741 for (i = 0; i < n; i++) {
2742 if (t[i] < kAtomTypeMinimum) {
2743 /* Explicit atom index */
2745 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
2746 if (t[i] >= mol->natoms)
2747 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
2749 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
2753 /* Analyze options */
2755 n = RARRAY_LEN(optval);
2756 for (i = 0; i < n; i++) {
2757 VALUE oval = RARRAY_PTR(optval)[i];
2758 if (oval == ID2SYM(rb_intern("global")))
2759 flags |= kParameterLookupGlobal;
2760 else if (oval == ID2SYM(rb_intern("local")))
2761 flags |= kParameterLookupLocal;
2762 else if (oval == ID2SYM(rb_intern("missing")))
2763 flags |= kParameterLookupMissing;
2764 else if (oval == ID2SYM(rb_intern("nowildcard")))
2765 flags |= kParameterLookupNoWildcard;
2766 else if (oval == ID2SYM(rb_intern("nobasetype")))
2767 flags |= kParameterLookupNoBaseAtomType;
2768 else if (oval == ID2SYM(rb_intern("create")))
2772 flags = kParameterLookupGlobal | kParameterLookupLocal;
2777 case kBondParType: {
2780 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
2782 idx = bp - mol->par->bondPars;
2786 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
2788 idx = bp - gBuiltinParameters->bondPars;
2793 case kAngleParType: {
2796 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
2798 idx = ap - mol->par->anglePars;
2802 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
2804 idx = ap - gBuiltinParameters->anglePars;
2809 case kDihedralParType: {
2812 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
2814 idx = tp - mol->par->dihedralPars;
2818 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2820 idx = tp - gBuiltinParameters->dihedralPars;
2825 case kImproperParType: {
2828 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
2830 idx = tp - mol->par->improperPars;
2834 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2836 idx = tp - gBuiltinParameters->improperPars;
2844 vp = ParameterLookupVdwPar(mol->par, t[0], flags);
2846 idx = vp - mol->par->vdwPars;
2850 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], flags);
2852 idx = vp - gBuiltinParameters->vdwPars;
2857 case kVdwPairParType: {
2860 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], flags);
2862 idx = vp - mol->par->vdwpPars;
2866 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], flags);
2868 idx = vp - gBuiltinParameters->vdwpPars;
2877 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
2880 /* Insert a new parameter record */
2882 Int count = ParameterGetCountForType(mol->par, parType);
2883 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
2884 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
2885 IntGroupRelease(ig);
2888 /* Set atom types */
2889 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
2894 up->bond.type1 = t[0];
2895 up->bond.type2 = t[1];
2898 up->angle.type1 = t[0];
2899 up->angle.type2 = t[1];
2900 up->angle.type3 = t[2];
2902 case kDihedralParType:
2903 case kImproperParType:
2904 up->torsion.type1 = t[0];
2905 up->torsion.type2 = t[1];
2906 up->torsion.type3 = t[2];
2907 up->torsion.type4 = t[3];
2910 up->vdw.type1 = t[0];
2912 case kVdwPairParType:
2913 up->vdwp.type1 = t[0];
2914 up->vdwp.type2 = t[1];
2921 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
2926 * lookup(par_type, atom_types, options, ...) -> ParameterRef
2927 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
2929 * Find the parameter record that matches the given atom types. The atom types are given
2930 * either as an array of string, or a single string delimited by whitespaces or hyphens.
2931 * Options are given as symbols. Valid values are :global (look for global parameters), :local
2932 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
2933 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
2936 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
2939 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2941 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
2942 parType = s_ParTypeFromValue(argv[0]);
2943 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
2948 * self == parameter -> boolean
2950 * True if the parameters point to the same parameter table.
2953 s_Parameter_Equal(VALUE self, VALUE val)
2955 if (rb_obj_is_kind_of(val, rb_cParameter)) {
2956 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
2957 } else return Qfalse;
2960 #pragma mark ====== ParEnumerable Class ======
2962 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
2963 and the parameter type. If the Molecule is NULL, then it refers to the
2964 global (built-in) parameters. Note that, even when the Molecule is not NULL,
2965 the global parameters are always accessible. */
2967 typedef struct ParEnumerable {
2969 Int parType; /* Same as parType in ParameterRef */
2972 static ParEnumerable *
2973 s_ParEnumerableNew(Molecule *mol, Int parType)
2975 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
2979 MoleculeRetain(mol);
2980 pen->parType = parType;
2986 s_ParEnumerableRelease(ParEnumerable *pen)
2989 if (pen->mol != NULL)
2990 MoleculeRelease(pen->mol);
2996 s_MoleculeFromParEnumerableValue(VALUE val)
2999 Data_Get_Struct(val, ParEnumerable, pen);
3004 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3006 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3008 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3009 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3014 * par_type -> String
3016 * Get the parameter type, like "bond", "angle", etc.
3019 s_ParEnumerable_ParType(VALUE self) {
3022 Data_Get_Struct(self, ParEnumerable, pen);
3024 if (tp == kElementParType)
3025 return rb_str_new2("element");
3026 tp -= kFirstParType;
3027 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3028 return rb_str_new2(s_ParameterTypeNames[tp]);
3029 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3034 * self[idx] -> ParameterRef
3036 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3037 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3038 * parent Parameter object of self.
3040 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3041 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3044 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3047 Data_Get_Struct(self, ParEnumerable, pen);
3048 switch (pen->parType) {
3049 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3050 case kBondParType: return s_Parameter_Bond(self, ival);
3051 case kAngleParType: return s_Parameter_Angle(self, ival);
3052 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3053 case kImproperParType: return s_Parameter_Improper(self, ival);
3054 case kVdwParType: return s_Parameter_Vdw(self, ival);
3055 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3056 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3057 case kElementParType: return s_Parameter_Element(self, ival);
3059 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3061 return Qnil; /* Not reached */
3068 * Returns the number of parameters included in this enumerable.
3071 s_ParEnumerable_Length(VALUE self)
3074 Data_Get_Struct(self, ParEnumerable, pen);
3075 switch (pen->parType) {
3076 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3077 case kBondParType: return s_Parameter_Nbonds(self);
3078 case kAngleParType: return s_Parameter_Nangles(self);
3079 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3080 case kImproperParType: return s_Parameter_Nimpropers(self);
3081 case kVdwParType: return s_Parameter_Nvdws(self);
3082 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3083 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3084 case kElementParType: return s_Parameter_Nelements(self);
3086 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3088 return Qnil; /* Not reached */
3095 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3098 s_ParEnumerable_Each(VALUE self)
3104 Data_Get_Struct(self, ParEnumerable, pen);
3105 if (pen->parType == kElementParType)
3106 n = gCountElementParameters;
3108 switch (pen->parType) {
3109 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3110 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3111 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3112 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3113 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3114 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3115 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3117 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3119 if (pen->mol == NULL)
3120 n = *((Int *)((char *)gBuiltinParameters + ofs));
3121 else if (pen->mol->par != NULL)
3122 n = *((Int *)((char *)(pen->mol->par) + ofs));
3125 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3126 Data_Get_Struct(aval, ParameterRef, pref);
3127 for (i = 0; i < n; i++) {
3136 * reverse_each {|pref| ...}
3138 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3141 s_ParEnumerable_ReverseEach(VALUE self)
3147 Data_Get_Struct(self, ParEnumerable, pen);
3148 if (pen->parType == kElementParType)
3149 n = gCountElementParameters;
3151 switch (pen->parType) {
3152 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3153 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3154 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3155 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3156 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3157 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3158 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3160 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3162 if (pen->mol == NULL)
3163 n = *((Int *)((char *)gBuiltinParameters + ofs));
3164 else if (pen->mol->par != NULL)
3165 n = *((Int *)((char *)(pen->mol->par) + ofs));
3168 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3169 Data_Get_Struct(aval, ParameterRef, pref);
3170 for (i = n - 1; i >= 0; i--) {
3179 * insert(idx = nil, pref = nil) -> ParameterRef
3181 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3182 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3183 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3184 * parameter is left undefined.
3185 * Throws an exception if ParEnumerable points to the global parameter.
3188 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3196 Data_Get_Struct(self, ParEnumerable, pen);
3197 if (pen->mol == NULL)
3198 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3199 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3200 rb_scan_args(argc, argv, "02", &ival, &pval);
3202 i = NUM2INT(rb_Integer(ival));
3204 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3209 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3210 if (up == NULL || type != pen->parType)
3211 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3212 ParameterCopyOneWithType(&u, up, pen->parType);
3215 memset(&u, 0, sizeof(u));
3218 ig = IntGroupNewWithPoints(n, 1, -1);
3219 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3221 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3222 MolActionCallback_registerUndo(pen->mol, act);
3223 MolActionRelease(act);
3225 IntGroupRelease(ig);
3226 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3234 * Delete the parameter(s) specified by the argument.
3237 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3242 Data_Get_Struct(self, ParEnumerable, pen);
3243 if (pen->mol == NULL)
3244 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3245 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3246 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3247 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3250 ig = IntGroupFromValue(ival);
3251 if ((i = IntGroupGetCount(ig)) == 0) {
3252 IntGroupRelease(ig);
3256 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3257 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3260 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3261 IntGroupRelease(ig);
3267 * lookup(atom_types, options, ...) -> ParameterRef
3268 * lookup(atom_type_string, options, ...) -> ParameterRef
3270 * Find the parameter record that matches the given atom types. The arguments are
3271 * the same as Parameter#lookup, except for the parameter type which is implicitly
3275 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3278 Data_Get_Struct(self, ParEnumerable, pen);
3279 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3284 * self == parEnumerable -> boolean
3286 * True if the arguments point to the same parameter table and type.
3289 s_ParEnumerable_Equal(VALUE self, VALUE val)
3291 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3292 ParEnumerable *pen1, *pen2;
3293 Data_Get_Struct(self, ParEnumerable, pen1);
3294 Data_Get_Struct(val, ParEnumerable, pen2);
3295 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3296 } else return Qfalse;
3299 #pragma mark ====== AtomRef Class ======
3301 /* Forward declaration for register undo */
3302 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3304 /* Ruby string "set_atom_attr" */
3305 static VALUE s_SetAtomAttrString;
3308 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3312 Data_Get_Struct(self, AtomRef, aref);
3313 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3314 if (idx < 0 || idx >= aref->mol->natoms)
3315 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3317 *app = aref->mol->atoms + idx;
3324 s_AtomFromValue(VALUE self)
3327 s_AtomIndexFromValue(self, &ap, NULL);
3332 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3335 s_AtomIndexFromValue(self, &ap, mpp);
3340 s_NotifyModificationForAtomRef(VALUE self)
3343 Data_Get_Struct(self, AtomRef, aref);
3344 MoleculeIncrementModifyCount(aref->mol);
3348 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3351 Data_Get_Struct(self, AtomRef, aref);
3352 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3355 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3356 MolActionCallback_registerUndo(aref->mol, act);
3357 MoleculeCallback_notifyModification(aref->mol, 0);
3358 /* Request MD rebuilt if necessary */
3359 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3360 aref->mol->needsMDRebuild = 1;
3365 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3368 aref = AtomRefNew(mol, idx);
3369 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3373 s_AtomRef_GetMolecule(VALUE self)
3376 s_AtomIndexFromValue(self, NULL, &mpp);
3377 return ValueFromMolecule(mpp);
3380 static VALUE s_AtomRef_GetIndex(VALUE self) {
3381 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3384 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3385 return INT2NUM(s_AtomFromValue(self)->segSeq);
3388 static VALUE s_AtomRef_GetSegName(VALUE self) {
3389 char *p = s_AtomFromValue(self)->segName;
3390 return rb_str_new(p, strlen_limit(p, 4));
3393 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3394 return INT2NUM(s_AtomFromValue(self)->resSeq);
3397 static VALUE s_AtomRef_GetResName(VALUE self) {
3398 char *p = s_AtomFromValue(self)->resName;
3399 return rb_str_new(p, strlen_limit(p, 4));
3402 static VALUE s_AtomRef_GetName(VALUE self) {
3403 char *p = s_AtomFromValue(self)->aname;
3404 return rb_str_new(p, strlen_limit(p, 4));
3407 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3408 int type = s_AtomFromValue(self)->type;
3409 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3410 return rb_str_new(p, strlen_limit(p, 6));
3413 static VALUE s_AtomRef_GetCharge(VALUE self) {
3414 return rb_float_new(s_AtomFromValue(self)->charge);
3417 static VALUE s_AtomRef_GetWeight(VALUE self) {
3418 return rb_float_new(s_AtomFromValue(self)->weight);
3421 static VALUE s_AtomRef_GetElement(VALUE self) {
3422 char *p = s_AtomFromValue(self)->element;
3423 return rb_str_new(p, strlen_limit(p, 4));
3426 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3427 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3430 static VALUE s_AtomRef_GetConnects(VALUE self) {
3433 Atom *ap = s_AtomFromValue(self);
3434 retval = rb_ary_new();
3435 cp = AtomConnectData(&ap->connect);
3436 for (i = 0; i < ap->connect.count; i++)
3437 rb_ary_push(retval, INT2NUM(cp[i]));
3441 static VALUE s_AtomRef_GetR(VALUE self) {
3442 return ValueFromVector(&(s_AtomFromValue(self)->r));
3445 static VALUE s_AtomRef_GetX(VALUE self) {
3446 return rb_float_new(s_AtomFromValue(self)->r.x);
3449 static VALUE s_AtomRef_GetY(VALUE self) {
3450 return rb_float_new(s_AtomFromValue(self)->r.y);
3453 static VALUE s_AtomRef_GetZ(VALUE self) {
3454 return rb_float_new(s_AtomFromValue(self)->r.z);
3457 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3461 s_AtomIndexFromValue(self, &ap, &mp);
3463 if (mp->cell != NULL)
3464 TransformVec(&r1, mp->cell->rtr, &r1);
3468 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3469 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3470 return ValueFromVector(&r1);
3473 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3474 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3477 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3478 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3481 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3482 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3485 static VALUE s_AtomRef_GetSigma(VALUE self) {
3486 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3489 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3490 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3493 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3494 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3497 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3498 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3501 static VALUE s_AtomRef_GetV(VALUE self) {
3502 return ValueFromVector(&(s_AtomFromValue(self)->v));
3505 static VALUE s_AtomRef_GetF(VALUE self) {
3506 Vector v = s_AtomFromValue(self)->f;
3507 VecScaleSelf(v, INTERNAL2KCAL);
3508 return ValueFromVector(&v);
3511 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3512 return rb_float_new(s_AtomFromValue(self)->occupancy);
3515 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3516 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3519 static VALUE s_AtomRef_GetAniso(VALUE self) {
3522 Atom *ap = s_AtomFromValue(self);
3523 if (ap->aniso == NULL)
3525 retval = rb_ary_new();
3526 for (i = 0; i < 6; i++)
3527 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3528 if (ap->aniso->has_bsig) {
3529 rb_ary_push(retval, INT2NUM(0));
3530 for (i = 0; i < 6; i++)
3531 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3536 static VALUE s_AtomRef_GetSymop(VALUE self) {
3538 Atom *ap = s_AtomFromValue(self);
3539 if (!ap->symop.alive)
3541 retval = rb_ary_new();
3542 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3543 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3544 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3545 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3546 rb_ary_push(retval, INT2NUM(ap->symbase));
3550 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3551 return INT2NUM(s_AtomFromValue(self)->intCharge);
3554 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3555 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3558 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3559 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3562 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3566 MDExclusion *exinfo;
3569 idx = s_AtomIndexFromValue(self, &ap, &mol);
3570 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3571 VALUE mval = ValueFromMolecule(mol);
3572 s_RebuildMDParameterIfNecessary(mval, Qnil);
3574 if (mol->arena->exinfo == NULL)
3576 exinfo = mol->arena->exinfo + idx;
3577 exlist = mol->arena->exlist;
3578 retval = rb_ary_new();
3579 aval = rb_ary_new();
3580 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3581 rb_ary_push(aval, INT2NUM(exlist[i]));
3582 rb_ary_push(retval, aval);
3583 aval = rb_ary_new();
3584 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3585 rb_ary_push(aval, INT2NUM(exlist[i]));
3586 rb_ary_push(retval, aval);
3587 aval = rb_ary_new();
3588 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3589 rb_ary_push(aval, INT2NUM(exlist[i]));
3590 rb_ary_push(retval, aval);
3594 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3595 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3598 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3599 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3602 static VALUE s_AtomRef_GetHidden(VALUE self) {
3603 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3606 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3609 Atom *ap = s_AtomFromValue(self);
3610 if (ap->anchor == NULL)
3612 count = ap->anchor->connect.count;
3613 retval = rb_ary_new2(count * 2);
3614 cp = AtomConnectData(&ap->anchor->connect);
3615 for (i = 0; i < count; i++) {
3616 rb_ary_store(retval, i, INT2NUM(cp[i]));
3617 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3622 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3623 rb_raise(rb_eMolbyError, "index cannot be directly set");
3627 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
3628 VALUE oval = s_AtomRef_GetSegSeq(self);
3629 val = rb_Integer(val);
3630 s_AtomFromValue(self)->segSeq = NUM2INT(val);
3631 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
3635 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
3636 char *p = StringValuePtr(val);
3637 VALUE oval = s_AtomRef_GetSegName(self);
3638 strncpy(s_AtomFromValue(self)->segName, p, 4);
3639 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
3643 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
3644 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
3645 return val; /* Not reached */
3648 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
3649 Atom *ap = s_AtomFromValue(self);
3650 char *p = StringValuePtr(val);
3651 VALUE oval = s_AtomRef_GetName(self);
3652 if (ap->anchor != NULL && p[0] == '_')
3653 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
3654 strncpy(ap->aname, p, 4);
3655 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
3659 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
3661 char *p = StringValuePtr(val);
3662 VALUE oval = s_AtomRef_GetAtomType(self);
3663 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
3664 if (type != 0 && type < kAtomTypeMinimum)
3665 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
3666 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
3667 mp->needsMDRebuild = 1;
3668 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
3672 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
3674 VALUE oval = s_AtomRef_GetCharge(self);
3675 val = rb_Float(val);
3676 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
3677 mp->needsMDRebuild = 1;
3678 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
3682 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
3684 VALUE oval = s_AtomRef_GetWeight(self);
3685 val = rb_Float(val);
3686 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
3687 mp->needsMDRebuild = 1;
3688 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
3692 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
3695 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3696 char *p = StringValuePtr(val);
3697 VALUE oval = s_AtomRef_GetElement(self);
3698 ap->atomicNumber = ElementToInt(p);
3699 ElementToString(ap->atomicNumber, ap->element);
3700 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3702 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
3703 mp->needsMDRebuild = 1;
3707 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
3710 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3711 VALUE oval = s_AtomRef_GetAtomicNumber(self);
3712 val = rb_Integer(val);
3713 ap->atomicNumber = NUM2INT(val);
3714 ElementToString(ap->atomicNumber, ap->element);
3715 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3717 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
3718 mp->needsMDRebuild = 1;
3722 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
3723 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
3724 return val; /* Not reached */
3727 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
3730 VALUE oval = s_AtomRef_GetR(self);
3731 VectorFromValue(val, &v);
3732 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
3733 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
3734 mp->needsMDCopyCoordinates = 1;
3738 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
3741 VALUE oval = s_AtomRef_GetX(self);
3742 val = rb_Float(val);
3744 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
3745 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
3746 mp->needsMDCopyCoordinates = 1;
3750 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
3753 VALUE oval = s_AtomRef_GetY(self);
3754 val = rb_Float(val);
3756 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
3757 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
3758 mp->needsMDCopyCoordinates = 1;
3762 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
3765 VALUE oval = s_AtomRef_GetZ(self);
3766 val = rb_Float(val);
3768 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
3769 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
3770 mp->needsMDCopyCoordinates = 1;
3774 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
3778 s_AtomIndexFromValue(self, &ap, &mp);
3780 VectorFromValue(val, &v);
3781 if (mp->cell != NULL)
3782 TransformVec(&v, mp->cell->tr, &v);
3784 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3785 mp->needsMDCopyCoordinates = 1;
3789 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
3794 s_AtomIndexFromValue(self, &ap, &mp);
3796 val = rb_Float(val);
3798 if (mp->cell != NULL) {
3799 TransformVec(&v, mp->cell->rtr, &v);
3801 TransformVec(&v, mp->cell->tr, &v);
3804 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3805 mp->needsMDCopyCoordinates = 1;
3809 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
3814 s_AtomIndexFromValue(self, &ap, &mp);
3816 val = rb_Float(val);
3818 if (mp->cell != NULL) {
3819 TransformVec(&v, mp->cell->rtr, &v);
3821 TransformVec(&v, mp->cell->tr, &v);
3824 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3825 mp->needsMDCopyCoordinates = 1;
3829 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
3834 s_AtomIndexFromValue(self, &ap, &mp);
3836 val = rb_Float(val);
3838 if (mp->cell != NULL) {
3839 TransformVec(&v, mp->cell->rtr, &v);
3841 TransformVec(&v, mp->cell->tr, &v);
3844 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3845 mp->needsMDCopyCoordinates = 1;
3849 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
3852 VALUE oval = s_AtomRef_GetSigma(self);
3853 VectorFromValue(val, &v);
3854 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
3855 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
3856 mp->needsMDCopyCoordinates = 1;
3860 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
3863 VALUE oval = s_AtomRef_GetSigmaX(self);
3864 val = rb_Float(val);
3866 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
3867 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
3868 mp->needsMDCopyCoordinates = 1;
3872 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
3875 VALUE oval = s_AtomRef_GetSigmaY(self);
3876 val = rb_Float(val);
3878 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
3879 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
3880 mp->needsMDCopyCoordinates = 1;
3884 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
3887 VALUE oval = s_AtomRef_GetSigmaZ(self);
3888 val = rb_Float(val);
3890 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
3891 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
3892 mp->needsMDCopyCoordinates = 1;
3896 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
3900 VALUE oval = s_AtomRef_GetV(self);
3901 VectorFromValue(val, &v);
3902 s_AtomIndexFromValue(self, &ap, &mp);
3904 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
3905 mp->needsMDCopyCoordinates = 1;
3909 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
3912 VALUE oval = s_AtomRef_GetF(self);
3913 VectorFromValue(val, &v);
3914 VecScaleSelf(v, KCAL2INTERNAL);
3915 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
3916 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
3917 mp->needsMDCopyCoordinates = 1;
3921 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
3922 VALUE oval = s_AtomRef_GetOccupancy(self);
3924 val = rb_Float(val);
3925 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
3926 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
3927 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
3931 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
3932 VALUE oval = s_AtomRef_GetTempFactor(self);
3933 val = rb_Float(val);
3934 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
3935 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
3939 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
3944 VALUE oval = s_AtomRef_GetAniso(self);
3945 Data_Get_Struct(self, AtomRef, aref);
3946 val = rb_funcall(val, rb_intern("to_a"), 0);
3947 n = RARRAY_LEN(val);
3948 valp = RARRAY_PTR(val);
3949 for (i = 0; i < 6; i++) {
3951 f[i] = NUM2DBL(rb_Float(valp[i]));
3955 type = NUM2INT(rb_Integer(valp[6]));
3958 for (i = 0; i < 6; i++)
3959 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
3961 for (i = 0; i < 6; i++)
3964 i = s_AtomIndexFromValue(self, NULL, NULL);
3965 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
3966 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
3970 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
3976 VALUE oval = s_AtomRef_GetSymop(self);
3977 i = s_AtomIndexFromValue(self, &ap, &mol);
3979 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
3981 val = rb_funcall(val, rb_intern("to_a"), 0);
3982 n = RARRAY_LEN(val);
3983 valp = RARRAY_PTR(val);
3984 for (i = 0; i < 5; i++) {
3986 if (valp[i] == Qnil)
3989 ival[i] = NUM2INT(rb_Integer(valp[i]));
3990 } else ival[i] = -100000;
3993 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
3994 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));
3995 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
3996 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
3997 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
3998 return val; /* No need to change */
3999 if (ival[0] != -100000)
4000 ap->symop.sym = ival[0];
4001 if (ival[1] != -100000)
4002 ap->symop.dx = ival[1];
4003 if (ival[2] != -100000)
4004 ap->symop.dy = ival[2];
4005 if (ival[3] != -100000)
4006 ap->symop.dz = ival[3];
4007 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4008 if (ival[4] != -100000)
4009 ap->symbase = ival[4];
4010 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4011 /* The anisotropic parameters should be recalculated */
4012 VALUE oaval = s_AtomRef_GetAniso(self);
4013 MoleculeSetAnisoBySymop(mol, i);
4014 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4016 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4020 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4021 VALUE oval = s_AtomRef_GetIntCharge(self);
4022 val = rb_Integer(val);
4023 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4024 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4028 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4030 VALUE oval = s_AtomRef_GetFixForce(self);
4031 val = rb_Float(val);
4032 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4033 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4034 mp->needsMDRebuild = 1;
4038 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4041 VALUE oval = s_AtomRef_GetFixPos(self);
4042 VectorFromValue(val, &v);
4043 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4044 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4045 mp->needsMDRebuild = 1;
4049 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4050 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4051 return val; /* Not reached */
4054 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4055 VALUE oval = s_AtomRef_GetIntCharge(self);
4056 val = rb_Integer(val);
4057 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4058 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4062 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4063 VALUE oval = s_AtomRef_GetIntCharge(self);
4064 val = rb_Integer(val);
4065 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4066 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4070 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4071 Atom *ap = s_AtomFromValue(self);
4072 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4074 ap->exflags |= kAtomHiddenFlag;
4076 ap->exflags &= ~kAtomHiddenFlag;
4078 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4082 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4083 Int idx, i, j, k, n, *ip;
4090 MolAction **undoActions;
4091 memset(&ac, 0, sizeof(ac));
4092 idx = s_AtomIndexFromValue(self, &ap, &mol);
4093 oval = s_AtomRef_GetAnchorList(self);
4095 val = rb_ary_to_ary(val);
4096 n = RARRAY_LEN(val);
4099 if (ap->anchor != NULL) {
4100 AtomConnectResize(&ap->anchor->connect, 0);
4101 free(ap->anchor->coeffs);
4104 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4109 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4110 if (ap->aname[0] == '_')
4111 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4112 ip = (Int *)malloc(sizeof(Int) * n);
4114 for (i = 0; i < n; i++) {
4115 v = RARRAY_PTR(val)[i];
4116 if (rb_obj_is_kind_of(v, rb_cFloat))
4118 j = NUM2INT(rb_Integer(v));
4119 if (j < 0 || j >= mol->natoms)
4120 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4121 for (k = 0; k < i; k++) {
4123 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4129 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4130 else if (i * 2 != n)
4131 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4132 dp = (Double *)malloc(sizeof(Double) * n / 2);
4133 for (i = 0; i < n / 2; i++) {
4134 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4136 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4142 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4146 rb_raise(rb_eMolbyError, "invalid argument");
4147 if (nUndoActions > 0) {
4148 for (i = 0; i < nUndoActions; i++) {
4149 MolActionCallback_registerUndo(mol, undoActions[i]);
4150 MolActionRelease(undoActions[i]);
4154 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4158 static struct s_AtomAttrDef {
4160 VALUE *symref; /* Address of s_IndexSymbol etc. */
4161 ID id; /* Will be set within InitMolby() */
4162 VALUE (*getter)(VALUE);
4163 VALUE (*setter)(VALUE, VALUE);
4164 } s_AtomAttrDefTable[] = {
4165 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4166 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4167 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4168 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4169 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4170 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4171 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4172 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4173 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4174 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4175 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4176 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4177 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4178 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4179 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4180 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4181 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4182 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4183 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4184 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4185 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4186 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4187 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4188 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4189 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4190 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4191 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4192 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4193 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4194 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4195 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4196 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4197 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4198 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4199 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4200 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4201 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4202 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4203 {NULL} /* Sentinel */
4207 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4211 if (TYPE(key) != T_SYMBOL) {
4212 kid = rb_intern(StringValuePtr(key));
4214 } else kid = SYM2ID(key);
4215 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4216 if (s_AtomAttrDefTable[i].id == kid) {
4217 if (value == Qundef)
4218 return (*(s_AtomAttrDefTable[i].getter))(self);
4220 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4223 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4224 return Qnil; /* not reached */
4228 s_AtomRef_GetAttr(VALUE self, VALUE key)
4230 return s_AtomRef_SetAttr(self, key, Qundef);
4235 * self == atomRef -> boolean
4237 * True if the two references point to the same atom.
4240 s_AtomRef_Equal(VALUE self, VALUE val)
4242 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4243 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4244 } else return Qfalse;
4247 #pragma mark ====== MolEnumerable Class ======
4249 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4253 * self[idx] -> AtomRef or Array of Integers
4255 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4256 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4257 * value is a String. Otherwise, the return value is an Array of Integers.
4260 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4262 MolEnumerable *mseq;
4265 Data_Get_Struct(self, MolEnumerable, mseq);
4267 if (mseq->kind == kAtomKind) {
4268 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4270 idx1 = NUM2INT(arg1);
4271 switch (mseq->kind) {
4273 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4274 if (idx2 < 0 || idx2 >= mol->nbonds)
4275 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4276 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4279 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4280 if (idx2 < 0 || idx2 >= mol->nangles)
4281 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4282 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4284 case kDihedralKind: {
4285 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4286 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4287 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4288 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]));
4290 case kImproperKind: {
4291 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4292 if (idx2 < 0 || idx2 >= mol->nimpropers)
4293 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4294 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]));
4296 case kResidueKind: {
4298 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4299 if (idx2 < 0 || idx2 >= mol->nresidues)
4300 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4301 p = mol->residues[idx2];
4302 return rb_str_new(p, strlen_limit(p, 4));
4312 * Returns the number of objects included in this enumerable.
4315 s_MolEnumerable_Length(VALUE self)
4317 MolEnumerable *mseq;
4318 Data_Get_Struct(self, MolEnumerable, mseq);
4319 switch (mseq->kind) {
4321 return INT2NUM(mseq->mol->natoms);
4323 return INT2NUM(mseq->mol->nbonds);
4325 return INT2NUM(mseq->mol->nangles);
4327 return INT2NUM(mseq->mol->ndihedrals);
4329 return INT2NUM(mseq->mol->nimpropers);
4331 return INT2NUM(mseq->mol->nresidues);
4340 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4341 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4342 * For the atoms, a same AtomRef object is passed (with different internal information)
4343 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4344 * for each iteration.
4347 s_MolEnumerable_Each(VALUE self)
4349 MolEnumerable *mseq;
4351 int len = NUM2INT(s_MolEnumerable_Length(self));
4352 Data_Get_Struct(self, MolEnumerable, mseq);
4353 if (mseq->kind == kAtomKind) {
4354 /* The same AtomRef object will be used during the loop */
4355 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4356 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4357 for (i = 0; i < len; i++) {
4362 /* A new ruby object will be created at each iteration (not very efficient) */
4363 for (i = 0; i < len; i++) {
4364 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4372 * self == molEnumerable -> boolean
4374 * True if the two arguments point to the same molecule and enumerable type.
4377 s_MolEnumerable_Equal(VALUE self, VALUE val)
4379 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4380 MolEnumerable *mseq1, *mseq2;
4381 Data_Get_Struct(self, MolEnumerable, mseq1);
4382 Data_Get_Struct(val, MolEnumerable, mseq2);
4383 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4384 } else return Qfalse;
4388 #pragma mark ====== Molecule Class ======
4390 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
4391 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4392 char *gLoadSaveErrorMessage = NULL;
4394 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4397 MoleculeFromValue(VALUE val)
4400 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4401 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4402 Data_Get_Struct(val, Molecule, mol);
4406 static VALUE sMoleculeRetainArray = Qnil;
4408 /* The function is called from MoleculeRelease() */
4409 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4410 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4411 /* object is always returned for the same Molecule. */
4412 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4413 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4414 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4415 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4416 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4418 /* Register/unregister the exmolobj Ruby object */
4420 MoleculeReleaseExternalObj(Molecule *mol)
4422 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4423 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4424 mol->exmolobjProtected = 0;
4429 MoleculeRetainExternalObj(Molecule *mol)
4431 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4432 if (sMoleculeRetainArray == Qnil) {
4433 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4434 sMoleculeRetainArray = rb_ary_new();
4437 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4438 mol->exmolobjProtected = 1;
4442 /* Release hook function for Ruby */
4444 MoleculeReleaseHook(Molecule *mol)
4446 if (mol->exmolobj != NULL) {
4447 /* No need to remove from sMoleculeRetainArray */
4448 mol->exmolobj = NULL;
4449 mol->exmolobjProtected = 0;
4451 MoleculeRelease(mol);
4455 ValueFromMolecule(Molecule *mol)
4459 if (mol->exmolobj != NULL)
4460 return (VALUE)mol->exmolobj;
4461 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4462 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4463 return (VALUE)mol->exmolobj;
4468 s_Molecule_Alloc(VALUE klass)
4471 Molecule *mol = MoleculeNew();
4472 val = ValueFromMolecule(mol);
4473 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4478 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4482 if (FIXNUM_P(val)) {
4484 if (n >= 0 && n < mol->natoms)
4486 n = -1; /* No such atom */
4487 val = rb_inspect(val);
4489 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4491 if (n >= 0 && n < mol->natoms)
4493 p = StringValuePtr(val);
4495 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4497 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4499 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4500 return 0; /* Not reached */
4504 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4507 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4508 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4509 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4510 Data_Get_Struct(val, IntGroup, ig);
4516 s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
4518 if (gLoadSaveErrorMessage != NULL) {
4519 MyAppCallback_setConsoleColor(1);
4520 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
4521 MyAppCallback_setConsoleColor(0);
4524 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4531 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4532 * created object does not affect the old object in any sense.
4535 s_Molecule_InitCopy(VALUE self, VALUE arg)
4537 Molecule *mp1, *mp2;
4538 Data_Get_Struct(self, Molecule, mp1);
4539 mp2 = MoleculeFromValue(arg);
4540 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4541 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4547 * loadmbsf(file) -> bool
4549 * Read a structure from a mbsf file.
4550 * Return true if successful.
4553 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4559 MoleculeClearLoadSaveErrorMessage();
4560 Data_Get_Struct(self, Molecule, mol);
4561 rb_scan_args(argc, argv, "1", &fname);
4562 fstr = FileStringValuePtr(fname);
4563 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4564 s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
4570 * loadpsf(file, pdbfile = nil) -> bool
4572 * Read a structure from a psf file. molecule must be empty. The psf may be
4573 * an "extended" version, which also contains coordinates. If pdbfile
4574 * is given, then atomic coordinates are read from that file.
4575 * Return true if successful.
4578 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4580 VALUE fname, pdbname;
4581 char *fstr, *pdbstr;
4584 Data_Get_Struct(self, Molecule, mol);
4585 if (mol->natoms > 0)
4586 return Qnil; /* Must be a new molecule */
4587 MoleculeClearLoadSaveErrorMessage();
4588 rb_scan_args(argc, argv, "11", &fname, &pdbname);
4589 fstr = FileStringValuePtr(fname);
4590 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4591 s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
4593 if (!NIL_P(pdbname)) {
4594 pdbstr = strdup(FileStringValuePtr(pdbname));
4595 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
4597 s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
4604 * loadpdb(file) -> bool
4606 * Read coordinates from a pdb file. If molecule is empty, then structure is build
4607 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
4608 * Return true if successful.
4611 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4617 Data_Get_Struct(self, Molecule, mol);
4618 rb_scan_args(argc, argv, "1", &fname);
4619 MoleculeClearLoadSaveErrorMessage();
4620 fstr = FileStringValuePtr(fname);
4621 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4622 s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
4628 * loaddcd(file) -> bool
4630 * Read coordinates from a dcd file. The molecule should not empty.
4631 * Return true if successful.
4634 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
4640 Data_Get_Struct(self, Molecule, mol);
4641 rb_scan_args(argc, argv, "1", &fname);
4642 MoleculeClearLoadSaveErrorMessage();
4643 fstr = FileStringValuePtr(fname);
4644 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
4645 s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
4651 * loadtep(file) -> bool
4653 * Read coordinates from an ortep .tep file.
4654 * Return true if successful.
4657 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
4663 Data_Get_Struct(self, Molecule, mol);
4664 rb_scan_args(argc, argv, "1", &fname);
4665 MoleculeClearLoadSaveErrorMessage();
4666 fstr = FileStringValuePtr(fname);
4667 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
4668 s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
4674 * loadres(file) -> bool
4676 * Read coordinates from a shelx .res file.
4677 * Return true if successful.
4680 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
4686 Data_Get_Struct(self, Molecule, mol);
4687 rb_scan_args(argc, argv, "1", &fname);
4688 MoleculeClearLoadSaveErrorMessage();
4689 fstr = FileStringValuePtr(fname);
4690 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
4691 s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
4697 * loadfchk(file) -> bool
4699 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
4700 * Return true if successful.
4703 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
4709 Data_Get_Struct(self, Molecule, mol);
4710 rb_scan_args(argc, argv, "1", &fname);
4711 MoleculeClearLoadSaveErrorMessage();
4712 fstr = FileStringValuePtr(fname);
4713 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
4714 s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
4720 * loaddat(file) -> bool
4722 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
4723 * Return true if successful.
4726 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
4732 Data_Get_Struct(self, Molecule, mol);
4733 rb_scan_args(argc, argv, "1", &fname);
4734 MoleculeClearLoadSaveErrorMessage();
4735 fstr = FileStringValuePtr(fname);
4736 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
4737 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
4738 MyAppCallback_hideProgressPanel();
4739 s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
4745 * savembsf(file) -> bool
4747 * Write structure as a mbsf file. Returns true if successful.
4750 s_Molecule_Savembsf(VALUE self, VALUE fname)
4755 Data_Get_Struct(self, Molecule, mol);
4756 MoleculeClearLoadSaveErrorMessage();
4757 fstr = FileStringValuePtr(fname);
4758 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4759 s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
4765 * savepsf(file) -> bool
4767 * Write structure as a psf file. Returns true if successful.
4770 s_Molecule_Savepsf(VALUE self, VALUE fname)
4775 Data_Get_Struct(self, Molecule, mol);
4776 MoleculeClearLoadSaveErrorMessage();
4777 fstr = FileStringValuePtr(fname);
4778 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4779 s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
4785 * savepdb(file) -> bool
4787 * Write coordinates as a pdb file. Returns true if successful.
4790 s_Molecule_Savepdb(VALUE self, VALUE fname)
4795 Data_Get_Struct(self, Molecule, mol);
4796 MoleculeClearLoadSaveErrorMessage();
4797 fstr = FileStringValuePtr(fname);
4798 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4799 s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
4805 * savedcd(file) -> bool
4807 * Write coordinates as a dcd file. Returns true if successful.
4810 s_Molecule_Savedcd(VALUE self, VALUE fname)
4815 Data_Get_Struct(self, Molecule, mol);
4816 MoleculeClearLoadSaveErrorMessage();
4817 fstr = FileStringValuePtr(fname);
4818 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
4819 s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
4825 * savetep(file) -> bool
4827 * Write coordinates as an ORTEP file. Returns true if successful.
4831 s_Molecule_Savetep(VALUE self, VALUE fname)
4836 Data_Get_Struct(self, Molecule, mol);
4837 fstr = FileStringValuePtr(fname);
4838 if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4839 rb_raise(rb_eMolbyError, errbuf);
4844 /* load([ftype, ] fname, ...) */
4846 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
4849 char *argstr, *methname, *p, *type = "";
4852 const char *ls = (loadFlag ? "load" : "save");
4853 int lslen = strlen(ls);
4858 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
4859 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
4860 if (argstr[0] == ':') {
4861 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
4862 methname = ALLOC_N(char, lslen + strlen(argstr));
4863 strcpy(methname, ls);
4864 strcat(methname, argstr + 1);
4866 for (i = lslen; methname[i] != 0; i++)
4867 methname[i] = tolower(methname[i]);
4868 mid = rb_intern(methname);
4872 rval = rb_funcall2(self, mid, argc, argv);
4878 /* Guess file type from extension */
4879 p = strrchr(argstr, '.');
4883 for (methname = p; *methname != 0; methname++) {
4884 if (!isalpha(*methname))
4887 if (*methname == 0) {
4888 methname = ALLOC_N(char, lslen + strlen(p) + 1);
4889 if (methname == NULL)
4890 rb_raise(rb_eMolbyError, "Low memory");
4891 strcpy(methname, ls);
4892 strcat(methname, p);
4893 for (i = lslen; methname[i] != 0; i++)
4894 methname[i] = tolower(methname[i]);
4895 mid = rb_intern(methname);
4898 if (rb_respond_to(self, mid)) {
4899 /* Load: try to call the load procedure only if it is available */
4900 rval = rb_funcall2(self, mid, argc, argv);
4905 /* Save: call the save procedure, and if not found then call 'method_missing' */
4906 rval = rb_funcall2(self, mid, argc, argv);
4913 rval = rb_str_to_str(argv[0]);
4914 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
4915 s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
4916 return Qnil; /* Does not reach here */
4920 /* Register the path */
4923 Data_Get_Struct(self, Molecule, mol);
4924 MoleculeSetPath(mol, StringValuePtr(argv[0]));
4926 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
4927 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
4928 if (ap->occupancy != 0.0)
4931 if (i == mol->natoms) {
4932 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
4933 ap->occupancy = 1.0;
4942 * molload(file, *args) -> bool
4944 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
4945 * file type given by the extension). If this method fails, then all defined (public)
4946 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
4949 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
4951 return s_Molecule_LoadSave(argc, argv, self, 1);
4956 * molsave(file, *args) -> bool
4958 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
4959 * (XXX is the file type given by the extension).
4962 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
4964 return s_Molecule_LoadSave(argc, argv, self, 0);
4971 * Returns the display name of the molecule. If the molecule has no associated
4972 * document, then returns nil.
4975 s_Molecule_Name(VALUE self)
4979 Data_Get_Struct(self, Molecule, mol);
4980 MoleculeCallback_displayName(mol, buf, sizeof buf);
4984 return rb_str_new2(buf);
4989 * set_name(string) -> self
4991 * Set the name of an untitled molecule. If the molecule is not associated with window
4992 * or it already has an associated file, then exception is thrown.
4995 s_Molecule_SetName(VALUE self, VALUE nval)
4998 Data_Get_Struct(self, Molecule, mol);
4999 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5000 rb_raise(rb_eMolbyError, "Cannot change the window title");
5009 * Returns the full path name of the molecule, if it is associated with a file.
5010 * If the molecule has no associated file, then returns nil.
5013 s_Molecule_Path(VALUE self)
5017 Data_Get_Struct(self, Molecule, mol);
5018 MoleculeCallback_pathName(mol, buf, sizeof buf);
5022 return Ruby_NewFileStringValue(buf);
5029 * Returns the full path name of the directory in which the file associated with the
5030 * molecule is located. If the molecule has no associated file, then returns nil.
5033 s_Molecule_Dir(VALUE self)
5037 Data_Get_Struct(self, Molecule, mol);
5038 MoleculeCallback_pathName(mol, buf, sizeof buf);
5040 translate_char(buf, '\\', '/');
5045 p = strrchr(buf, '/');
5048 return rb_str_new2(buf);
5056 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5057 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5058 * the Molecule structure) is returned.
5061 s_Molecule_Inspect(VALUE self)
5065 Data_Get_Struct(self, Molecule, mol);
5066 MoleculeCallback_displayName(mol, buf, sizeof buf);
5068 /* No associated document */
5069 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5070 return rb_str_new2(buf);
5072 /* Check whether the document name is duplicate */
5076 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5077 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5078 if (strcmp(buf, buf2) == 0) {
5085 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5087 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5089 return rb_str_new2(buf2);
5096 * open(file) -> Molecule
5098 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5101 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5107 rb_scan_args(argc, argv, "01", &fname);
5111 p = FileStringValuePtr(fname);
5112 iflag = Ruby_SetInterruptFlag(Qfalse);
5113 mp = MoleculeCallback_openNewMolecule(p);
5114 Ruby_SetInterruptFlag(iflag);
5117 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5119 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5121 return ValueFromMolecule(mp);
5127 * new(file, *args) -> Molecule
5129 * Create a new molecule and call "load" method with the same arguments.
5132 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5135 return s_Molecule_Load(argc, argv, self);
5136 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5140 s_Molecule_MolEnumerable(VALUE self, int kind)
5143 MolEnumerable *mseq;
5144 Data_Get_Struct(self, Molecule, mol);
5145 mseq = MolEnumerableNew(mol, kind);
5146 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5151 * atoms -> MolEnumerable
5153 * Returns a MolEnumerable object representing the array of atoms.
5156 s_Molecule_Atoms(VALUE self)
5158 return s_Molecule_MolEnumerable(self, kAtomKind);
5163 * bonds -> MolEnumerable
5165 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5166 * by an array of two atom indices.
5169 s_Molecule_Bonds(VALUE self)
5171 return s_Molecule_MolEnumerable(self, kBondKind);
5176 * angles -> MolEnumerable
5178 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5179 * by an array of three atom indices.
5182 s_Molecule_Angles(VALUE self)
5184 return s_Molecule_MolEnumerable(self, kAngleKind);
5189 * dihedrals -> MolEnumerable
5191 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5192 * by an array of four atom indices.
5195 s_Molecule_Dihedrals(VALUE self)
5197 return s_Molecule_MolEnumerable(self, kDihedralKind);
5202 * impropers -> MolEnumerable
5204 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5205 * by an array of four atom indices.
5208 s_Molecule_Impropers(VALUE self)
5210 return s_Molecule_MolEnumerable(self, kImproperKind);
5215 * residues -> MolEnumerable
5217 * Returns a MolEnumerable object representing the array of residue names.
5220 s_Molecule_Residues(VALUE self)
5222 return s_Molecule_MolEnumerable(self, kResidueKind);
5229 * Returns the number of atoms.
5232 s_Molecule_Natoms(VALUE self)
5235 Data_Get_Struct(self, Molecule, mol);
5236 return INT2NUM(mol->natoms);
5243 * Returns the number of bonds.
5246 s_Molecule_Nbonds(VALUE self)
5249 Data_Get_Struct(self, Molecule, mol);
5250 return INT2NUM(mol->nbonds);
5255 * nangles -> Integer
5257 * Returns the number of angles.
5260 s_Molecule_Nangles(VALUE self)
5263 Data_Get_Struct(self, Molecule, mol);
5264 return INT2NUM(mol->nangles);
5269 * ndihedrals -> Integer
5271 * Returns the number of dihedrals.
5274 s_Molecule_Ndihedrals(VALUE self)
5277 Data_Get_Struct(self, Molecule, mol);
5278 return INT2NUM(mol->ndihedrals);
5283 * nimpropers -> Integer
5285 * Returns the number of impropers.
5288 s_Molecule_Nimpropers(VALUE self)
5291 Data_Get_Struct(self, Molecule, mol);
5292 return INT2NUM(mol->nimpropers);
5297 * nresidues -> Integer
5299 * Returns the number of residues.
5302 s_Molecule_Nresidues(VALUE self)
5305 Data_Get_Struct(self, Molecule, mol);
5306 return INT2NUM(mol->nresidues);
5310 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5312 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.");
5317 * bond_par(idx) -> ParameterRef
5319 * Returns the MD parameter for the idx-th bond.
5323 s_Molecule_BondPar(VALUE self, VALUE val)
5330 Data_Get_Struct(self, Molecule, mol);
5331 ival = NUM2INT(rb_Integer(val));
5332 if (ival < -mol->nbonds || ival >= mol->nbonds)
5333 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5335 ival += mol->nbonds;
5336 s_RebuildMDParameterIfNecessary(self, Qtrue);
5337 i1 = mol->bonds[ival * 2];
5338 i2 = mol->bonds[ival * 2 + 1];
5339 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5340 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5341 bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5344 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5350 * angle_par(idx) -> ParameterRef
5352 * Returns the MD parameter for the idx-th angle.
5356 s_Molecule_AnglePar(VALUE self, VALUE val)
5363 Data_Get_Struct(self, Molecule, mol);
5364 ival = NUM2INT(rb_Integer(val));
5365 if (ival < -mol->nangles || ival >= mol->nangles)
5366 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5368 ival += mol->nangles;
5369 s_RebuildMDParameterIfNecessary(self, Qtrue);
5370 i1 = mol->angles[ival * 3];
5371 i2 = mol->angles[ival * 3 + 1];
5372 i3 = mol->angles[ival * 3 + 2];
5373 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5374 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5375 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5376 ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5379 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5384 * dihedral_par(idx) -> ParameterRef
5386 * Returns the MD parameter for the idx-th dihedral.
5390 s_Molecule_DihedralPar(VALUE self, VALUE val)
5395 UInt t1, t2, t3, t4;
5397 Data_Get_Struct(self, Molecule, mol);
5398 ival = NUM2INT(rb_Integer(val));
5399 if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5400 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5402 ival += mol->ndihedrals;
5403 s_RebuildMDParameterIfNecessary(self, Qtrue);
5404 i1 = mol->dihedrals[ival * 4];
5405 i2 = mol->dihedrals[ival * 4 + 1];
5406 i3 = mol->dihedrals[ival * 4 + 2];
5407 i4 = mol->dihedrals[ival * 4 + 3];
5408 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5409 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5410 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5411 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5412 tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5415 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5420 * improper_par(idx) -> ParameterRef
5422 * Returns the MD parameter for the idx-th improper.
5426 s_Molecule_ImproperPar(VALUE self, VALUE val)
5431 UInt t1, t2, t3, t4;
5433 Data_Get_Struct(self, Molecule, mol);
5434 ival = NUM2INT(rb_Integer(val));
5435 if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5436 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5438 ival += mol->nimpropers;
5439 s_RebuildMDParameterIfNecessary(self, Qtrue);
5440 i1 = mol->impropers[ival * 4];
5441 i2 = mol->impropers[ival * 4 + 1];
5442 i3 = mol->impropers[ival * 4 + 2];
5443 i4 = mol->impropers[ival * 4 + 3];
5444 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5445 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5446 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5447 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5448 tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5451 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5456 * vdw_par(idx) -> ParameterRef
5458 * Returns the vdw parameter for the idx-th atom.
5462 s_Molecule_VdwPar(VALUE self, VALUE val)
5468 Data_Get_Struct(self, Molecule, mol);
5469 ival = NUM2INT(rb_Integer(val));
5470 if (ival < -mol->natoms || ival >= mol->natoms)
5471 rb_raise(rb_eMolbyError, "atom index (%d) out of range", ival);
5473 ival += mol->natoms;
5474 s_RebuildMDParameterIfNecessary(self, Qtrue);
5475 t1 = ATOM_AT_INDEX(mol->atoms, ival)->type;
5476 vp = ParameterLookupVdwPar(mol->par, t1, 0);
5479 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, vp - mol->par->vdwPars);
5485 * start_step -> Integer
5487 * Returns the start step (defined by dcd format).
5490 s_Molecule_StartStep(VALUE self)
5493 Data_Get_Struct(self, Molecule, mol);
5494 return INT2NUM(mol->startStep);
5499 * start_step = Integer
5501 * Set the start step (defined by dcd format).
5504 s_Molecule_SetStartStep(VALUE self, VALUE val)
5507 Data_Get_Struct(self, Molecule, mol);
5508 mol->startStep = NUM2INT(rb_Integer(val));
5514 * steps_per_frame -> Integer
5516 * Returns the number of steps between frames (defined by dcd format).
5519 s_Molecule_StepsPerFrame(VALUE self)
5522 Data_Get_Struct(self, Molecule, mol);
5523 return INT2NUM(mol->stepsPerFrame);
5528 * steps_per_frame = Integer
5530 * Set the number of steps between frames (defined by dcd format).
5533 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5536 Data_Get_Struct(self, Molecule, mol);
5537 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5543 * ps_per_step -> Float
5545 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
5548 s_Molecule_PsPerStep(VALUE self)
5551 Data_Get_Struct(self, Molecule, mol);
5552 return rb_float_new(mol->psPerStep);
5557 * ps_per_step = Float
5559 * Set the time increment (in picoseconds) for one step (defined by dcd format).
5562 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5565 Data_Get_Struct(self, Molecule, mol);
5566 mol->psPerStep = NUM2DBL(rb_Float(val));
5572 * find_angles -> Integer
5574 * Find the angles from the bonds. Returns the number of angles newly created.
5578 s_Molecule_FindAngles(VALUE self)
5584 Data_Get_Struct(self, Molecule, mol);
5585 if (mol == NULL || mol->natoms == 0)
5589 for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5590 nc = ap->connect.count;
5592 for (i = 0; i < nc; i++) {
5593 n[0] = ap->connects[i];
5594 for (j = i + 1; j < nc; j++) {
5595 n[2] = ap->connects[j];
5596 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5597 AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5602 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
5605 return INT2NUM(nip);
5610 * find_dihedrals -> Integer
5612 * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5616 s_Molecule_FindDihedrals(VALUE self)
5620 int n1, i, j, k, nc1, nc2;
5622 Data_Get_Struct(self, Molecule, mol);
5623 if (mol == NULL || mol->natoms == 0)
5627 for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5628 nc1 = ap1->connect.count;
5630 for (i = 0; i < nc1; i++) {
5631 n[2] = ap1->connects[i];
5634 ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5635 nc2 = ap2->connect.count;
5636 for (j = 0; j < nc1; j++) {
5637 n[0] = ap1->connects[j];
5640 for (k = 0; k < nc2; k++) {
5641 n[3] = ap2->connects[k];
5642 if (n[3] == n1 || n[3] == n[0])
5644 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
5645 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
5651 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
5654 return INT2NUM(nip);
5660 * nresidues = Integer
5662 * Change the number of residues.
5665 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5668 int ival = NUM2INT(val);
5669 Data_Get_Struct(self, Molecule, mol);
5670 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5671 if (ival != mol->nresidues)
5672 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5678 * max_residue_number(atom_group = nil) -> Integer
5680 * Returns the maximum residue number actually used. If an atom group is given, only
5681 * these atoms are examined. If no atom is present, nil is returned.
5684 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5690 Data_Get_Struct(self, Molecule, mol);
5691 rb_scan_args(argc, argv, "01", &gval);
5692 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5693 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5694 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5699 * min_residue_number(atom_group = nil) -> Integer
5701 * Returns the minimum residue number actually used. If an atom group is given, only
5702 * these atoms are examined. If no atom is present, nil is returned.
5705 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5711 Data_Get_Struct(self, Molecule, mol);
5712 rb_scan_args(argc, argv, "01", &gval);
5713 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5714 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5715 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5720 * each_atom(atom_group = nil) {|aref| ...}
5722 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5723 * group is given, only these atoms are processed.
5724 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5725 * is self (a Molecule object).
5728 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5736 Data_Get_Struct(self, Molecule, mol);
5737 rb_scan_args(argc, argv, "01", &gval);
5738 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5739 arval = ValueFromMoleculeAndIndex(mol, 0);
5740 Data_Get_Struct(arval, AtomRef, aref);
5741 for (i = 0; i < mol->natoms; i++) {
5743 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5747 IntGroupRelease(ig);
5753 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5755 * Returns the unit cell parameters. If cell is not set, returns nil.
5758 s_Molecule_Cell(VALUE self)
5763 Data_Get_Struct(self, Molecule, mol);
5764 if (mol->cell == NULL)
5766 val = rb_ary_new2(6);
5767 for (i = 0; i < 6; i++)
5768 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
5769 if (mol->cell->has_sigma) {
5770 for (i = 0; i < 6; i++) {
5771 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
5779 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5780 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
5782 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
5783 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
5784 This operation is undoable.
5785 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
5788 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
5792 int i, convert_coord, n;
5794 Data_Get_Struct(self, Molecule, mol);
5795 rb_scan_args(argc, argv, "11", &val, &cval);
5800 val = rb_ary_to_ary(val);
5801 len = RARRAY_LEN(val);
5804 } else if (len >= 6) {
5806 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
5807 for (i = 0; i < n; i++)
5808 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
5810 convert_coord = (RTEST(cval) ? 1 : 0);
5811 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
5817 * box -> [avec, bvec, cvec, origin, flags]
5819 * Get the unit cell information in the form of a periodic bounding box.
5820 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
5821 * Integers which define whether the system is periodic along the axis.
5822 * If no unit cell is defined, nil is returned.
5825 s_Molecule_Box(VALUE self)
5829 Data_Get_Struct(self, Molecule, mol);
5830 if (mol == NULL || mol->cell == NULL)
5832 v[0] = ValueFromVector(&(mol->cell->axes[0]));
5833 v[1] = ValueFromVector(&(mol->cell->axes[1]));
5834 v[2] = ValueFromVector(&(mol->cell->axes[2]));
5835 v[3] = ValueFromVector(&(mol->cell->origin));
5836 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
5837 val = rb_ary_new4(5, v);
5843 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
5844 * set_box(d, origin = [0, 0, 0])
5847 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
5848 If it is a number, the x/y/z axis vector is multiplied with the given number and used
5850 Flags, if present, is a 3-member array of Integers defining whether the system is
5851 periodic along the axis.
5852 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
5853 In the second form, an isotropic box with cell-length d is set.
5854 In the third form, the existing box is cleared.
5855 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
5858 s_Molecule_SetBox(VALUE self, VALUE aval)
5862 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
5864 Vector origin = {0, 0, 0};
5867 int i, convertCoordinates = 0;
5868 Data_Get_Struct(self, Molecule, mol);
5870 MolActionCreateAndPerform(mol, gMolActionClearBox);
5873 aval = rb_ary_to_ary(aval);
5874 for (i = 0; i < 6; i++) {
5875 if (i < RARRAY_LEN(aval))
5876 v[i] = (RARRAY_PTR(aval))[i];
5880 MolActionCreateAndPerform(mol, gMolActionClearBox);
5883 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
5884 d = NUM2DBL(rb_Float(v[0]));
5885 for (i = 0; i < 3; i++)
5886 VecScale(vv[i], ax[i], d);
5888 VectorFromValue(v[1], &origin);
5889 flags[0] = flags[1] = flags[2] = 1;
5891 for (i = 0; i < 3; i++) {
5894 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
5895 d = NUM2DBL(rb_Float(v[i]));
5896 VecScale(vv[i], ax[i], d);
5898 VectorFromValue(v[i], &vv[i]);
5900 flags[i] = (VecLength2(vv[i]) > 0.0);
5903 VectorFromValue(v[3], &origin);
5905 for (i = 0; i < 3; i++) {
5906 VALUE val = Ruby_ObjectAtIndex(v[4], i);
5907 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
5911 convertCoordinates = 1;
5913 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
5919 * cell_periodicity -> [n1, n2, n3]
5921 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
5925 s_Molecule_CellPeriodicity(VALUE self)
5928 Data_Get_Struct(self, Molecule, mol);
5929 if (mol->cell == NULL)
5931 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
5936 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
5937 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
5939 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
5940 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
5941 * If cell is not defined, exception is raised.
5942 * This operation is undoable.
5945 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
5949 Data_Get_Struct(self, Molecule, mol);
5950 if (mol->cell == NULL)
5951 rb_raise(rb_eMolbyError, "periodic cell is not defined");
5954 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
5955 flag = NUM2INT(rb_Integer(arg));
5959 arg = rb_ary_to_ary(arg);
5961 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
5962 arg0 = RARRAY_PTR(arg)[i];
5963 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
5964 flag |= (1 << (2 - i));
5967 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
5973 * cell_flexibility -> bool
5975 * Returns the unit cell is flexible or not
5978 s_Molecule_CellFlexibility(VALUE self)
5980 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
5983 Data_Get_Struct(self, Molecule, mol);
5984 if (mol->cell == NULL)
5986 if (mol->useFlexibleCell)
5988 else return Qfalse; */
5993 * self.cell_flexibility = bool
5994 * set_cell_flexibility(bool)
5996 * Change the unit cell is flexible or not
5999 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
6001 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
6004 Data_Get_Struct(self, Molecule, mol);
6005 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
6011 * cell_transform -> Transform
6013 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
6014 * If cell is not defined, nil is returned.
6017 s_Molecule_CellTransform(VALUE self)
6020 Data_Get_Struct(self, Molecule, mol);
6021 if (mol == NULL || mol->cell == NULL)
6023 return ValueFromTransform(&(mol->cell->tr));
6028 * symmetry -> Array of Transforms
6029 * symmetries -> Array of Transforms
6031 * Get the currently defined symmetry operations. If no symmetry operation is defined,
6032 * returns an empty array.
6035 s_Molecule_Symmetry(VALUE self)
6040 Data_Get_Struct(self, Molecule, mol);
6041 if (mol->nsyms <= 0)
6042 return rb_ary_new();
6043 val = rb_ary_new2(mol->nsyms);
6044 for (i = 0; i < mol->nsyms; i++) {
6045 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
6052 * nsymmetries -> Integer
6054 * Get the number of currently defined symmetry operations.
6057 s_Molecule_Nsymmetries(VALUE self)
6060 Data_Get_Struct(self, Molecule, mol);
6061 return INT2NUM(mol->nsyms);
6066 * add_symmetry(Transform) -> Integer
6068 * Add a new symmetry operation. If no symmetry operation is defined and the
6069 * given argument is not an identity transform, then also add an identity
6070 * transform at the index 0.
6071 * Returns the total number of symmetries after operation.
6074 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
6078 Data_Get_Struct(self, Molecule, mol);
6079 TransformFromValue(trans, &tr);
6080 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
6081 return INT2NUM(mol->nsyms);
6086 * remove_symmetry(count = nil) -> Integer
6087 * remove_symmetries(count = nil) -> Integer
6089 * Remove the specified number of symmetry operations. The last added ones are removed
6090 * first. If count is nil, then all symmetry operations are removed. Returns the
6091 * number of leftover symmetries.
6094 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
6099 Data_Get_Struct(self, Molecule, mol);
6100 rb_scan_args(argc, argv, "01", &cval);
6104 n = NUM2INT(rb_Integer(cval));
6105 if (n < 0 || n > mol->nsyms)
6106 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
6107 if (n == mol->nsyms)
6110 for (i = 0; i < n; i++)
6111 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
6112 return INT2NUM(mol->nsyms);
6116 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6118 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6119 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6120 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6121 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6128 * atom_group {|aref| ...}
6129 * atom_group(arg1, arg2, ...)
6130 * atom_group(arg1, arg2, ...) {|aref| ...}
6132 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6133 * If arguments are given, then the atoms reprensented by the arguments are added to the
6134 * group. For a conversion of a string to an atom index, see the description
6135 * of Molecule#atom_index.
6136 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6137 * representing each atom, and the atoms are removed from the result if the block returns false.
6141 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6143 IntGroup *ig1, *ig2;
6145 Int i, startPt, interval;
6146 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6147 Data_Get_Struct(retval, IntGroup, ig1);
6148 Data_Get_Struct(self, Molecule, mol);
6150 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6153 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6154 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6155 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6156 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6157 ig2 = IntGroupFromValue(*argv);
6158 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6159 interval = IntGroupGetInterval(ig2, i);
6160 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6162 IntGroupRelease(ig2);
6163 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6165 values[0] = (VALUE)mol;
6166 values[1] = (VALUE)ig1;
6167 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6169 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6174 if (rb_block_given_p()) {
6175 /* Evaluate the given block with an AtomRef as the argument, and delete
6176 the index if the block returns false */
6177 AtomRef *aref = AtomRefNew(mol, 0);
6178 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6179 ig2 = IntGroupNew();
6180 IntGroupCopy(ig2, ig1);
6181 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6183 if (startPt >= mol->natoms)
6185 aref->idx = startPt;
6186 resval = rb_yield(arval);
6188 IntGroupRemove(ig1, startPt, 1);
6190 IntGroupRelease(ig2);
6193 /* Remove points that are out of bounds */
6194 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6201 * atom_index(val) -> Integer
6203 * Returns the atom index represented by val. val can be either a non-negative integer
6204 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
6205 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
6206 * where resname, resid, name are the residue name, residue id, and atom name respectively.
6207 * If val is a string and multiple atoms match the description, the atom with the lowest index
6211 s_Molecule_AtomIndex(VALUE self, VALUE val)
6214 Data_Get_Struct(self, Molecule, mol);
6215 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
6220 * extract(group, dummy_flag = nil) -> Molecule
6222 * Extract the atoms given by group and return as a new molecule object.
6223 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6224 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6225 * names beginning with an underscore) and included in the new molecule object.
6228 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6230 Molecule *mol1, *mol2;
6232 VALUE group, dummy_flag, retval;
6233 Data_Get_Struct(self, Molecule, mol1);
6234 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6235 ig = s_Molecule_AtomGroupFromValue(self, group);
6236 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6239 retval = ValueFromMolecule(mol2);
6241 IntGroupRelease(ig);
6247 * add(molecule2) -> self
6249 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6251 This operation is undoable.
6254 s_Molecule_Add(VALUE self, VALUE val)
6256 Molecule *mol1, *mol2;
6257 Data_Get_Struct(self, Molecule, mol1);
6258 mol2 = MoleculeFromValue(val);
6259 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6265 * remove(group) -> Molecule
6267 * The atoms designated by the given group are removed from the molecule.
6268 * This operation is undoable.
6271 s_Molecule_Remove(VALUE self, VALUE group)
6276 IntGroupIterator iter;
6278 Data_Get_Struct(self, Molecule, mol1);
6279 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6280 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6281 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6282 Data_Get_Struct(group, IntGroup, ig);
6284 /* Remove the bonds between the two fragments */
6285 /* (This is necessary for undo to work correctly) */
6286 IntGroupIteratorInit(ig, &iter);
6288 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6289 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6291 cp = AtomConnectData(&ap->connect);
6292 for (j = 0; j < ap->connect.count; j++) {
6294 if (!IntGroupLookup(ig, n, NULL)) {
6295 /* bond i-n, i is in ig and n is not */
6296 int k = MoleculeLookupBond(mol1, i, n);
6300 IntGroupAdd(bg, k, 1);
6305 IntGroupIteratorRelease(&iter);
6308 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6309 IntGroupRelease(bg);
6312 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6319 * create_atom(name, pos = -1) -> AtomRef
6321 * Create a new atom with the specified name (may contain residue
6322 * information) and position (if position is out of range, the atom is appended at
6323 * the end). Returns the reference to the new atom.
6324 * This operation is undoable.
6327 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6334 char *p, resName[6], atomName[6];
6336 Data_Get_Struct(self, Molecule, mol);
6337 rb_scan_args(argc, argv, "02", &name, &ival);
6339 pos = NUM2INT(rb_Integer(ival));
6342 p = StringValuePtr(name);
6344 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6345 if (atomName[0] == 0)
6346 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6349 if (p == NULL || p[0] == 0) {
6350 memset(atomName, 0, 4);
6353 memset(&arec, 0, sizeof(arec));
6354 strncpy(arec.aname, atomName, 4);
6356 strncpy(arec.resName, resName, 4);
6357 arec.resSeq = resSeq;
6359 arec.occupancy = 1.0;
6360 // i = MoleculeCreateAnAtom(mol, &arec);
6361 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6363 aref = AtomRefNew(mol, pos);
6364 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6369 * duplicate_atom(atomref, pos = -1) -> AtomRef
6371 * Create a new atom with the same attributes (but no bonding information)
6372 * with the specified atom. Returns the reference to the new atom.
6373 * This operation is undoable.
6376 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6382 VALUE retval, aval, ival;
6384 Data_Get_Struct(self, Molecule, mol);
6385 rb_scan_args(argc, argv, "11", &aval, &ival);
6386 if (FIXNUM_P(aval)) {
6387 int idx = NUM2INT(aval);
6388 if (idx < 0 || idx >= mol->natoms)
6389 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6390 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6392 apsrc = s_AtomFromValue(aval);
6395 rb_raise(rb_eMolbyError, "bad atom specification");
6397 pos = NUM2INT(rb_Integer(ival));
6399 AtomDuplicate(&arec, apsrc);
6400 arec.connect.count = 0;
6401 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6404 aref = AtomRefNew(mol, pos);
6405 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6413 * create_bond(n1, n2, ...) -> Integer
6415 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6416 * do nothing for that pair. Returns the number of bonds actually created.
6417 * This operation is undoable.
6420 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6423 Int i, j, k, *ip, old_nbonds;
6425 rb_raise(rb_eMolbyError, "missing arguments");
6427 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6428 Data_Get_Struct(self, Molecule, mol);
6429 ip = ALLOC_N(Int, argc + 1);
6430 for (i = j = 0; i < argc; i++, j++) {
6431 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6433 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6434 j -= 2; /* This bond is already present: skip it */
6436 for (k = 0; k < j - 1; k += 2) {
6437 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6438 j -= 2; /* The same entry is already in the argument */
6445 old_nbonds = mol->nbonds;
6447 ip[j] = kInvalidIndex;
6448 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6452 rb_raise(rb_eMolbyError, "atom index out of range");
6454 rb_raise(rb_eMolbyError, "too many bonds");
6456 rb_raise(rb_eMolbyError, "duplicate bonds");
6458 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6460 rb_raise(rb_eMolbyError, "error in creating bonds");
6461 return INT2NUM(mol->nbonds - old_nbonds);
6466 * molecule.remove_bonds(n1, n2, ...) -> Integer
6468 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6469 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6470 * This operation is undoable.
6473 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6479 rb_raise(rb_eMolbyError, "missing arguments");
6481 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6482 Data_Get_Struct(self, Molecule, mol);
6484 for (i = j = 0; i < argc; i++, j = 1 - j) {
6485 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6487 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6491 IntGroupAdd(bg, k, 1);
6496 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6497 i = IntGroupGetCount(bg);
6498 IntGroupRelease(bg);
6505 * add_angle(n1, n2, n3) -> Molecule
6507 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6508 * when a bond is created, so it is rarely necessary to use this method explicitly.
6509 * This operation is undoable.
6512 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6516 Data_Get_Struct(self, Molecule, mol);
6517 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6518 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6519 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6520 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6521 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6522 n[3] = kInvalidIndex;
6523 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6529 * remove_angle(n1, n2, n3) -> Molecule
6531 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6532 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6533 * This operation is undoable.
6536 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6541 Data_Get_Struct(self, Molecule, mol);
6542 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6543 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6544 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6545 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6546 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6547 ig = IntGroupNewWithPoints(n[3], 1, -1);
6548 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6549 IntGroupRelease(ig);
6555 * add_dihedral(n1, n2, n3, n4) -> Molecule
6557 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6558 * when a bond is created, so it is rarely necessary to use this method explicitly.
6559 * This operation is undoable.
6562 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6566 Data_Get_Struct(self, Molecule, mol);
6567 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6568 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6569 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6570 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6571 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6572 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6573 n[4] = kInvalidIndex;
6574 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6580 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6582 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6583 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6584 * This operation is undoable.
6587 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6592 Data_Get_Struct(self, Molecule, mol);
6593 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6594 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6595 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6596 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6597 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6598 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6599 ig = IntGroupNewWithPoints(n[4], 1, -1);
6600 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6601 IntGroupRelease(ig);
6607 * add_improper(n1, n2, n3, n4) -> Molecule
6609 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6610 * not automatically added when a new bond is created, so this method is more useful than
6611 * the angle/dihedral counterpart.
6612 * This operation is undoable.
6615 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6619 Data_Get_Struct(self, Molecule, mol);
6620 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6621 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6622 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6623 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6624 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6625 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6626 n[4] = kInvalidIndex;
6627 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6633 * remove_improper(n1, n2, n3, n4) -> Molecule
6634 * remove_improper(intgroup) -> Molecule
6636 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6637 * Returns self. Unlike angles and dihedrals, impropers are
6638 * not automatically added when a new bond is created, so this method is more useful than
6639 * the angle/dihedral counterpart.
6640 * This operation is undoable.
6643 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6646 VALUE v1, v2, v3, v4;
6649 Data_Get_Struct(self, Molecule, mol);
6651 ig = IntGroupFromValue(argv[0]);
6653 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6654 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6655 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6656 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6657 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6658 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6659 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6660 ig = IntGroupNewWithPoints(n[4], 1, -1);
6662 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6663 IntGroupRelease(ig);
6669 * assign_residue(group, res) -> Molecule
6671 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6672 * or "resname.resno". When the residue number is not specified, the residue number of
6673 * the first atom in the group is used.
6674 * This operation is undoable.
6677 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6681 char *p, *pp, buf[16];
6684 Data_Get_Struct(self, Molecule, mol);
6686 /* Parse the argument res */
6687 if (FIXNUM_P(res)) {
6688 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6689 resid = NUM2INT(res);
6692 p = StringValuePtr(res);
6693 pp = strchr(p, '.');
6695 resid = atoi(pp + 1);
6701 if (n > sizeof buf - 1)
6706 ig = s_Molecule_AtomGroupFromValue(self, range);
6707 if (ig == NULL || IntGroupGetCount(ig) == 0)
6711 /* Use the residue number of the first specified atom */
6712 n = IntGroupGetNthPoint(ig, 0);
6713 if (n >= mol->natoms)
6714 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6715 ap = ATOM_AT_INDEX(mol->atoms, n);
6718 /* Change the residue number */
6719 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6720 /* Change the residue name if necessary */
6724 seqs[1] = kInvalidIndex; */
6725 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6727 IntGroupRelease(ig);
6733 * offset_residue(group, offset) -> Molecule
6735 * Offset the residue number of the specified atoms. If any of the residue number gets
6736 * negative, then exception is thrown.
6737 * This operation is undoable.
6740 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6745 Data_Get_Struct(self, Molecule, mol);
6746 ig = s_Molecule_AtomGroupFromValue(self, range);
6747 ofs = NUM2INT(offset);
6748 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6750 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6751 IntGroupRelease(ig);
6757 * renumber_atoms(array) -> IntGroup
6759 * Change the order of atoms so that the atoms specified in the array argument appear
6760 * in this order from the top of the molecule. The atoms that are not included in array
6761 * are placed after these atoms, and these atoms are returned as an intGroup.
6762 * This operation is undoable.
6765 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6771 VALUE *valp, retval;
6772 Data_Get_Struct(self, Molecule, mol);
6773 if (TYPE(array) != T_ARRAY)
6774 array = rb_funcall(array, rb_intern("to_a"), 0);
6775 n = RARRAY_LEN(array);
6776 valp = RARRAY_PTR(array);
6777 new2old = ALLOC_N(Int, n + 1);
6778 for (i = 0; i < n; i++)
6779 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6780 new2old[i] = kInvalidIndex;
6781 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6783 rb_raise(rb_eMolbyError, "Atom index out of range");
6785 rb_raise(rb_eMolbyError, "Duplicate entry");
6787 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6788 retval = IntGroup_Alloc(rb_cIntGroup);
6789 Data_Get_Struct(retval, IntGroup, ig);
6790 if (mol->natoms > n)
6791 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6798 * find_close_atoms(atom, limit = 1.2) -> array of Integers (atom indices)
6800 * Find atoms that are within the threshold distance from the given atom.
6801 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
6802 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
6803 * If limit is not given, a default value of 1.2 is used.
6804 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
6807 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
6812 Int n1, nbonds, *bonds;
6813 Data_Get_Struct(self, Molecule, mol);
6814 rb_scan_args(argc, argv, "11", &aval, &limval);
6815 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
6819 limit = NUM2DBL(rb_Float(limval));
6820 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
6822 MoleculeFindCloseAtoms(mol, n1, limit, &nbonds, &bonds, 0);
6823 aval = rb_ary_new();
6825 for (n1 = 0; n1 < nbonds; n1++)
6826 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
6834 * guess_bonds(limit = 1.2) -> Integer
6836 * Create bonds between atoms that are within the threshold distance.
6837 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
6838 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
6839 * If limit is not given, a default value of 1.2 is used.
6840 * The number of the newly created bonds is returned.
6841 * This operation is undoable.
6844 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
6850 Data_Get_Struct(self, Molecule, mol);
6851 rb_scan_args(argc, argv, "01", &limval);
6855 limit = NUM2DBL(rb_Float(limval));
6856 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
6858 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
6861 return INT2NUM(nbonds);
6866 * register_undo(script, *args)
6868 * Register an undo operation with the current molecule.
6871 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6876 Data_Get_Struct(self, Molecule, mol);
6877 rb_scan_args(argc, argv, "1*", &script, &args);
6878 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6879 MolActionCallback_registerUndo(mol, act);
6885 * undo_enabled? -> bool
6887 * Returns true if undo is enabled for this molecule; otherwise no.
6890 s_Molecule_UndoEnabled(VALUE self)
6893 Data_Get_Struct(self, Molecule, mol);
6894 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6901 * undo_enabled = bool
6903 * Enable or disable undo.
6906 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6909 Data_Get_Struct(self, Molecule, mol);
6910 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6916 * selection -> IntGroup
6918 * Returns the current selection.
6921 s_Molecule_Selection(VALUE self)
6926 Data_Get_Struct(self, Molecule, mol);
6927 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6928 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6929 val = ValueFromIntGroup(ig);
6930 IntGroupRelease(ig);
6932 val = IntGroup_Alloc(rb_cIntGroup);
6938 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6942 Data_Get_Struct(self, Molecule, mol);
6946 ig = s_Molecule_AtomGroupFromValue(self, val);
6948 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6950 MoleculeSetSelection(mol, ig);
6952 IntGroupRelease(ig);
6958 * selection = IntGroup
6960 * Set the current selection. The right-hand operand may be nil.
6961 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6964 s_Molecule_SetSelection(VALUE self, VALUE val)
6966 return s_Molecule_SetSelectionSub(self, val, 0);
6971 * set_undoable_selection(IntGroup)
6973 * Set the current selection with undo registration. The right-hand operand may be nil.
6974 * This operation is undoable.
6977 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6979 return s_Molecule_SetSelectionSub(self, val, 1);
6984 * hidden_atoms -> IntGroup
6986 * Returns the currently hidden atoms.
6989 s_Molecule_HiddenAtoms(VALUE self)
6991 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
6992 return Qnil; /* Not reached */
6996 Data_Get_Struct(self, Molecule, mol);
7001 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7002 if (ap->exflags & kAtomHiddenFlag)
7003 IntGroupAdd(ig, i, 1);
7005 val = ValueFromIntGroup(ig);
7006 IntGroupRelease(ig);
7009 } else return Qnil; */
7014 * set_hidden_atoms(IntGroup)
7015 * self.hidden_atoms = IntGroup
7017 * Hide the specified atoms. This operation is _not_ undoable.
7020 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
7022 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7023 return Qnil; /* Not reached */
7026 Data_Get_Struct(self, Molecule, mol);
7034 ig = s_Molecule_AtomGroupFromValue(self, val);
7035 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7036 if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
7037 ap->exflags |= kAtomHiddenFlag;
7039 ap->exflags &= kAtomHiddenFlag;
7043 IntGroupRelease(ig);
7044 MoleculeCallback_notifyModification(mol, 0);
7051 * select_frame(index)
7054 * Select the specified frame. If successful, returns true, otherwise returns false.
7057 s_Molecule_SelectFrame(VALUE self, VALUE val)
7060 int ival = NUM2INT(val);
7061 Data_Get_Struct(self, Molecule, mol);
7062 ival = MoleculeSelectFrame(mol, ival, 1);
7072 * Get the current frame.
7075 s_Molecule_Frame(VALUE self)
7078 Data_Get_Struct(self, Molecule, mol);
7079 return INT2NUM(mol->cframe);
7084 * nframes -> Integer
7086 * Get the number of frames.
7089 s_Molecule_Nframes(VALUE self)
7092 Data_Get_Struct(self, Molecule, mol);
7093 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7098 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7099 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7101 * Insert new frames at the indices specified by the intGroup. If the first argument is
7102 * an integer, a single new frame is inserted at that index. If the first argument is
7103 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7104 * should be an array of arrays of Vector3Ds, then those coordinates are set
7105 * to the new frame. Otherwise, the coordinates of current molecule are copied
7107 * Returns an intGroup representing the inserted frames if successful, nil if not.
7110 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7112 VALUE val, coords, cells;
7115 int count, ival, i, j, len, len_c, len2, nframes;
7118 Data_Get_Struct(self, Molecule, mol);
7119 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7120 if (coords != Qnil) {
7121 if (TYPE(coords) != T_ARRAY)
7122 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7123 len = RARRAY_LEN(coords);
7125 if (cells != Qnil) {
7126 if (mol->cell == NULL)
7127 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7128 if (TYPE(cells) != T_ARRAY)
7129 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7130 len_c = RARRAY_LEN(cells);
7132 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7133 nframes = MoleculeGetNumberOfFrames(mol);
7135 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7136 val = ValueFromIntGroup(ig);
7138 ig = IntGroupFromValue(val);
7140 count = IntGroupGetCount(ig); /* Count is updated here */
7141 vp = ALLOC_N(Vector, mol->natoms * count);
7143 vp2 = ALLOC_N(Vector, 4 * count);
7147 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7148 ptr = RARRAY_PTR(coords);
7149 for (i = 0; i < count; i++) {
7150 if (TYPE(ptr[i]) != T_ARRAY)
7151 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7152 len2 = RARRAY_LEN(ptr[i]);
7153 if (len2 < mol->natoms)
7154 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7155 ptr2 = RARRAY_PTR(ptr[i]);
7156 for (j = 0; j < mol->natoms; j++)
7157 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7161 for (i = 0; i < count; i++) {
7162 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7163 vp[i * mol->natoms + j] = ap->r;
7169 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7170 ptr = RARRAY_PTR(cells);
7171 for (i = 0; i < count; i++) {
7172 if (TYPE(ptr[i]) != T_ARRAY)
7173 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7174 len2 = RARRAY_LEN(ptr[i]);
7176 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7177 ptr2 = RARRAY_PTR(ptr[i]);
7178 for (j = 0; j < 4; j++)
7179 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7182 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7183 IntGroupRelease(ig);
7187 return (ival >= 0 ? val : Qnil);
7192 * create_frame(coordinates = nil) -> Integer
7193 * create_frames(coordinates = nil) -> Integer
7195 * Same as molecule.insert_frames(nil, coordinates).
7198 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
7201 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
7203 return s_Molecule_InsertFrames(3, vals, self);
7208 * remove_frames(IntGroup, wantCoordinates = false)
7210 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
7211 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
7212 * removed frames is returned if operation is successful.
7215 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
7222 Data_Get_Struct(self, Molecule, mol);
7223 rb_scan_args(argc, argv, "11", &val, &flag);
7224 ig = IntGroupFromValue(val);
7225 count = IntGroupGetCount(ig);
7227 /* Create return value before removing frames */
7232 retval = rb_ary_new2(count);
7233 for (i = 0; i < count; i++) {
7234 n = IntGroupGetNthPoint(ig, i);
7235 coords = rb_ary_new2(mol->natoms);
7236 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7237 if (n < ap->nframes && n != mol->cframe)
7240 rb_ary_push(coords, ValueFromVector(&v));
7242 rb_ary_push(retval, coords);
7244 } else retval = Qtrue;
7245 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
7252 * each_frame {|n| ...}
7254 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
7255 * the frame number. After completion, the original frame number is restored.
7258 s_Molecule_EachFrame(VALUE self)
7260 int i, cframe, nframes;
7262 Data_Get_Struct(self, Molecule, mol);
7263 cframe = mol->cframe;
7264 nframes = MoleculeGetNumberOfFrames(mol);
7266 for (i = 0; i < nframes; i++) {
7267 MoleculeSelectFrame(mol, i, 1);
7268 rb_yield(INT2NUM(i));
7270 MoleculeSelectFrame(mol, cframe, 1);
7277 * set_atom_attr(index, key, value)
7279 * Set the atom attribute for the specified atom.
7280 * This operation is undoable.
7283 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7287 Data_Get_Struct(self, Molecule, mol);
7288 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7289 oldval = s_AtomRef_GetAttr(aref, key);
7292 s_AtomRef_SetAttr(aref, key, val);
7298 * get_atom_attr(index, key)
7300 * Get the atom attribute for the specified atom.
7303 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7305 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7310 * get_coord_from_frame(index, group = nil)
7312 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7313 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
7314 * copied; now they are always copied)
7317 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7320 VALUE ival, gval, cval;
7321 Int index, i, j, n, nn;
7323 IntGroupIterator iter;
7326 Data_Get_Struct(self, Molecule, mol);
7327 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7329 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
7330 index = NUM2INT(rb_Integer(ival));
7331 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7333 rb_raise(rb_eMolbyError, "No frame is present");
7335 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7338 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7340 ig = s_Molecule_AtomGroupFromValue(self, gval);
7342 n = IntGroupGetCount(ig);
7344 vp = (Vector *)calloc(sizeof(Vector), n);
7345 IntGroupIteratorInit(ig, &iter);
7348 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7349 ap = ATOM_AT_INDEX(mol->atoms, i);
7350 if (index < ap->nframes) {
7351 vp[j] = ap->frames[index];
7359 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7361 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7362 vp = mol->frame_cells + index * 4;
7363 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7365 IntGroupIteratorRelease(&iter);
7367 IntGroupRelease(ig);
7373 * fragment(n1, *exatoms) -> IntGroup
7374 * fragment(group, *exatoms) -> IntGroup
7376 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7377 * those atoms will not be counted during the search.
7380 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7383 IntGroup *baseg, *ig, *exatoms;
7385 volatile VALUE nval, exval;
7386 Data_Get_Struct(self, Molecule, mol);
7387 rb_scan_args(argc, argv, "1*", &nval, &exval);
7388 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7390 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7392 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7394 if (RARRAY_LEN(exval) == 0) {
7397 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7398 Data_Get_Struct(exval, IntGroup, exatoms);
7400 if (baseg == NULL) {
7401 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7403 IntGroupIterator iter;
7404 IntGroupIteratorInit(baseg, &iter);
7405 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7408 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7410 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7412 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7414 IntGroupAddIntGroup(ig, subg);
7415 IntGroupRelease(subg);
7420 IntGroupIteratorRelease(&iter);
7421 IntGroupRelease(baseg);
7424 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7425 nval = ValueFromIntGroup(ig);
7426 IntGroupRelease(ig);
7432 * each_fragment {|group| ...}
7434 * Execute the block, with the IntGroup object for each fragment as the argument.
7435 * Atoms or bonds should not be added or removed during the execution of the block.
7438 s_Molecule_EachFragment(VALUE self)
7443 Data_Get_Struct(self, Molecule, mol);
7444 if (mol == NULL || mol->natoms == 0)
7446 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7447 while (IntGroupGetCount(ag) > 0) {
7448 int n = IntGroupGetNthPoint(ag, 0);
7449 fg = MoleculeFragmentExcludingAtomGroup(mol, n, NULL);
7451 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7452 gval = ValueFromIntGroup(fg);
7454 IntGroupRemoveIntGroup(ag, fg);
7455 IntGroupRelease(fg);
7457 IntGroupRelease(ag);
7463 * detachable?(group) -> [n1, n2]
7465 * Check whether the group is 'detachable', i.e. the group is bound to the rest
7466 * of the molecule via only one bond. If it is, then the indices of the atoms
7467 * belonging to the bond is returned, the first element being the atom included
7468 * in the fragment. Otherwise, Qnil is returned.
7471 s_Molecule_Detachable_P(VALUE self, VALUE gval)
7477 Data_Get_Struct(self, Molecule, mol);
7478 ig = s_Molecule_AtomGroupFromValue(self, gval);
7479 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
7480 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
7481 } else retval = Qnil;
7482 IntGroupRelease(ig);
7488 * bonds_on_border(group = selection) -> Array of Array of two Integers
7490 * Returns an array of bonds that connect an atom in the group and an atom out
7491 * of the group. The first atom in the bond always belongs to the group. If no
7492 * such bonds are present, an empty array is returned.
7495 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
7500 Data_Get_Struct(self, Molecule, mol);
7501 rb_scan_args(argc, argv, "01", &gval);
7503 ig = MoleculeGetSelection(mol);
7507 ig = s_Molecule_AtomGroupFromValue(self, gval);
7509 retval = rb_ary_new();
7512 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
7514 IntGroupIterator iter;
7516 IntGroupIteratorInit(bg, &iter);
7517 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7518 /* The atoms at the border */
7520 n1 = mol->bonds[i * 2];
7521 n2 = mol->bonds[i * 2 + 1];
7522 if (IntGroupLookupPoint(ig, n1) < 0) {
7526 if (IntGroupLookupPoint(ig, n1) < 0)
7527 continue; /* Actually this is an internal error */
7529 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
7531 IntGroupIteratorRelease(&iter);
7533 IntGroupRelease(bg);
7534 IntGroupRelease(ig);
7540 * translate(vec, group = nil) -> Molecule
7542 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7543 * This operation is undoable.
7546 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7552 Data_Get_Struct(self, Molecule, mol);
7553 rb_scan_args(argc, argv, "11", &vec, &group);
7554 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7555 VectorFromValue(vec, &v);
7556 // MoleculeTranslate(mol, &v, ig);
7557 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7559 IntGroupRelease(ig);
7565 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7567 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7568 * If group is given, only atoms in the group are moved.
7569 * This operation is undoable.
7572 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7575 volatile VALUE aval, anval, cval, gval;
7580 Data_Get_Struct(self, Molecule, mol);
7581 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7582 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7583 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7584 VectorFromValue(aval, &av);
7586 cv.x = cv.y = cv.z = 0.0;
7588 VectorFromValue(cval, &cv);
7589 if (TransformForRotation(tr, &av, angle, &cv))
7590 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7591 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7593 IntGroupRelease(ig);
7599 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7601 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7602 * axis must not be a zero vector.
7603 * If group is given, only atoms in the group are moved.
7604 * This operation is undoable.
7607 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7610 volatile VALUE aval, cval, gval;
7614 Data_Get_Struct(self, Molecule, mol);
7615 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7616 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7617 VectorFromValue(aval, &av);
7619 cv.x = cv.y = cv.z = 0.0;
7621 VectorFromValue(cval, &cv);
7622 if (TransformForReflection(tr, &av, &cv))
7623 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7624 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7626 IntGroupRelease(ig);
7632 * invert(center = [0,0,0], group = nil) -> Molecule
7634 * Invert the molecule with the given center.
7635 * If group is given, only atoms in the group are moved.
7636 * This operation is undoable.
7639 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7642 volatile VALUE cval, gval;
7646 Data_Get_Struct(self, Molecule, mol);
7647 rb_scan_args(argc, argv, "02", &cval, &gval);
7648 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7650 cv.x = cv.y = cv.z = 0.0;
7652 VectorFromValue(cval, &cv);
7653 TransformForInversion(tr, &cv);
7654 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7656 IntGroupRelease(ig);
7662 * transform(transform, group = nil) -> Molecule
7664 * Transform the molecule by the given Transform object.
7665 * If group is given, only atoms in the group are moved.
7666 * This operation is undoable.
7669 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7675 Data_Get_Struct(self, Molecule, mol);
7676 rb_scan_args(argc, argv, "11", &trans, &group);
7677 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7678 TransformFromValue(trans, &tr);
7679 /* MoleculeTransform(mol, tr, ig); */
7680 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7682 IntGroupRelease(ig);
7687 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
7689 switch (MoleculeCenterOfMass(mol, outv, ig)) {
7690 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
7691 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
7693 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
7699 * center_of_mass(group = nil) -> Vector3D
7701 * Calculate the center of mass for the given set of atoms. The argument
7702 * group is null, then all atoms are considered.
7705 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
7711 Data_Get_Struct(self, Molecule, mol);
7712 rb_scan_args(argc, argv, "01", &group);
7713 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7714 s_Molecule_DoCenterOfMass(mol, &v, ig);
7716 IntGroupRelease(ig);
7717 return ValueFromVector(&v);
7722 * centralize(group = nil) -> self
7724 * Translate the molecule so that the center of mass of the given group is located
7725 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
7728 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
7734 Data_Get_Struct(self, Molecule, mol);
7735 rb_scan_args(argc, argv, "01", &group);
7736 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7737 s_Molecule_DoCenterOfMass(mol, &v, ig);
7739 IntGroupRelease(ig);
7743 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7749 * bounds(group = nil) -> [min, max]
7751 * Calculate the boundary. The return value is an array of two Vector3D objects.
7754 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7762 Data_Get_Struct(self, Molecule, mol);
7763 rb_scan_args(argc, argv, "01", &group);
7764 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7765 if (ig != NULL && IntGroupGetCount(ig) == 0)
7766 rb_raise(rb_eMolbyError, "atom group is empty");
7767 vmin.x = vmin.y = vmin.z = 1e30;
7768 vmax.x = vmax.y = vmax.z = -1e30;
7769 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7771 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7787 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7790 /* Get atom position or a vector */
7792 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7794 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7795 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7796 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7798 VectorFromValue(val, vp);
7804 * measure_bond(n1, n2) -> Float
7806 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
7807 * or Vector3D values.
7808 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7811 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7815 Data_Get_Struct(self, Molecule, mol);
7816 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7817 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7818 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7823 * measure_angle(n1, n2, n3) -> Float
7825 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
7826 * or Vector3D values. The return value is in degree.
7827 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7830 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7835 Data_Get_Struct(self, Molecule, mol);
7836 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7837 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7838 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7839 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7841 return Qnil; /* Cannot define */
7842 else return rb_float_new(d);
7847 * measure_dihedral(n1, n2, n3, n4) -> Float
7849 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7850 * or Vector3D values. The return value is in degree.
7851 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7854 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7857 Vector v1, v2, v3, v4;
7859 Data_Get_Struct(self, Molecule, mol);
7860 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7861 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7862 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7863 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7864 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7866 return Qnil; /* Cannot define */
7867 else return rb_float_new(d);
7872 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> Array
7874 * Expand the specified part of the molecule by the given symmetry operation.
7875 * Returns the array of atom indices corresponding to the expanded atoms.
7878 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7881 VALUE gval, sval, xval, yval, zval, rval;
7887 Data_Get_Struct(self, Molecule, mol);
7888 rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
7889 n[0] = NUM2INT(rb_Integer(sval));
7890 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7891 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7892 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7893 ig = s_Molecule_AtomGroupFromValue(self, gval);
7894 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7895 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7896 natoms = mol->natoms;
7898 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], &nidx, &idx);
7900 rval = rb_ary_new2(nidx);
7901 while (--nidx >= 0) {
7902 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7904 /* if (natoms == mol->natoms)
7907 rval = IntGroup_Alloc(rb_cIntGroup);
7908 Data_Get_Struct(rval, IntGroup, ig);
7909 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7916 * amend_by_symmetry(group = nil) -> IntGroup
7918 * Expand the specified part of the molecule by the given symmetry operation.
7919 * Returns an IntGroup containing the added atoms.
7922 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7927 Data_Get_Struct(self, Molecule, mol);
7928 rb_scan_args(argc, argv, "01", &gval);
7930 ig = s_Molecule_AtomGroupFromValue(self, gval);
7932 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7933 rval = ValueFromIntGroup(ig2);
7934 IntGroupRelease(ig2);
7940 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7942 * Get the transform corresponding to the symmetry operation. The symop can either be
7943 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7944 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7945 * Otherwise, the returned transform is for fractional coordinates.
7946 * Raises exception when no cell or no transform are defined.
7949 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7955 Data_Get_Struct(self, Molecule, mol);
7956 if (mol->cell == NULL)
7957 rb_raise(rb_eMolbyError, "no unit cell is defined");
7958 if (mol->nsyms == 0)
7959 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7960 rb_scan_args(argc, argv, "11", &sval, &fval);
7961 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7962 symop.sym = NUM2INT(rb_Integer(sval));
7963 symop.dx = symop.dy = symop.dz = 0;
7965 sval = rb_ary_to_ary(sval);
7966 if (RARRAY_LEN(sval) < 4)
7967 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7968 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7969 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7970 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7971 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7973 if (symop.sym >= mol->nsyms)
7974 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7975 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7976 return ValueFromTransform(&tr);
7981 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7983 * Get the symmetry operation corresponding to the given transform.
7984 * If is_cartesian is true, the given transform is for cartesian coordinates.
7985 * Otherwise, the given transform is for fractional coordinates.
7986 * Raises exception when no cell or no transform are defined.
7989 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7996 Data_Get_Struct(self, Molecule, mol);
7997 if (mol->cell == NULL)
7998 rb_raise(rb_eMolbyError, "no unit cell is defined");
7999 if (mol->nsyms == 0)
8000 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8001 rb_scan_args(argc, argv, "11", &tval, &fval);
8002 TransformFromValue(tval, &tr);
8003 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8005 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8007 return Qnil; /* Not found */
8013 * wrap_unit_cell(group) -> Vector3D
8015 * Move the specified group so that the center of mass of the group is within the
8016 * unit cell. The offset vector is returned. If no periodic box is defined,
8017 * exception is raised.
8020 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
8025 Data_Get_Struct(self, Molecule, mol);
8026 if (mol->cell == NULL)
8027 rb_raise(rb_eMolbyError, "no unit cell is defined");
8028 ig = s_Molecule_AtomGroupFromValue(self, gval);
8029 s_Molecule_DoCenterOfMass(mol, &cv, ig);
8030 TransformVec(&v, mol->cell->rtr, &cv);
8031 if (mol->cell->flags[0])
8033 if (mol->cell->flags[1])
8035 if (mol->cell->flags[2])
8037 TransformVec(&dv, mol->cell->tr, &v);
8039 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
8040 IntGroupRelease(ig);
8041 return ValueFromVector(&dv);
8046 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
8048 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
8049 * first and second atom in the pair should belong to group1 and group2, respectively.
8050 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
8053 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
8056 VALUE limval, gval1, gval2, rval, igval;
8057 IntGroup *ig1, *ig2;
8058 IntGroupIterator iter1, iter2;
8064 MDExclusion *exinfo;
8067 Data_Get_Struct(self, Molecule, mol);
8068 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
8069 lim = NUM2DBL(rb_Float(limval));
8071 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
8073 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
8075 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
8077 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
8079 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
8081 if (!RTEST(igval)) {
8082 /* Use the exclusion table in MDArena */
8083 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
8084 VALUE mval = ValueFromMolecule(mol);
8085 s_RebuildMDParameterIfNecessary(mval, Qnil);
8087 exinfo = mol->arena->exinfo; /* May be NULL */
8088 exlist = mol->arena->exlist;
8093 IntGroupIteratorInit(ig1, &iter1);
8094 IntGroupIteratorInit(ig2, &iter2);
8097 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
8099 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
8101 if (exinfo != NULL) {
8102 exn1 = exinfo[n[0]].index1;
8103 exn2 = exinfo[n[0] + 1].index1;
8104 } else exn1 = exn2 = -1;
8105 IntGroupIteratorReset(&iter2);
8106 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
8107 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
8109 continue; /* Same atom */
8110 if (exinfo != NULL) {
8111 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
8112 for (i = exn1; i < exn2; i++) {
8113 if (exlist[i] == n[1])
8117 continue; /* Should be excluded */
8119 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
8120 /* Is this pair already registered? */
8122 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
8123 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
8127 /* Not registered yet */
8128 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
8133 IntGroupIteratorRelease(&iter2);
8134 IntGroupIteratorRelease(&iter1);
8135 IntGroupRelease(ig2);
8136 IntGroupRelease(ig1);
8137 rval = rb_ary_new2(npairs);
8138 if (pairs != NULL) {
8139 for (i = 0; i < npairs; i++) {
8140 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
8147 /* Calculate the transform that moves the current coordinates to the reference
8148 coordinates with least displacements. */
8150 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8158 Double eigen_val[3];
8159 Vector eigen_vec[3];
8161 IntGroupIterator iter;
8163 natoms = mol->natoms;
8165 IntGroupIteratorInit(ig, &iter);
8167 /* Calculate the weighted center */
8171 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8172 ap1 = ATOM_AT_INDEX(ap, in);
8173 w1 = (weights != NULL ? weights[i] : ap1->weight);
8174 VecScaleInc(org1, ap1->r, w1);
8175 VecScaleInc(org2, ref[i], w1);
8179 VecScaleSelf(org1, w);
8180 VecScaleSelf(org2, w);
8182 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8183 /* Matrix to diagonalize = R * tR */
8184 memset(r, 0, sizeof(Mat33));
8185 memset(q, 0, sizeof(Mat33));
8186 memset(u, 0, sizeof(Mat33));
8188 IntGroupIteratorReset(&iter);
8189 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8191 ap1 = ATOM_AT_INDEX(ap, in);
8192 w1 = (weights != NULL ? weights[i] : ap1->weight);
8194 VecSub(v1, ap1->r, org1);
8195 VecSub(v2, ref[i], org2);
8196 r[0] += w1 * v1.x * v2.x;
8197 r[1] += w1 * v1.y * v2.x;
8198 r[2] += w1 * v1.z * v2.x;
8199 r[3] += w1 * v1.x * v2.y;
8200 r[4] += w1 * v1.y * v2.y;
8201 r[5] += w1 * v1.z * v2.y;
8202 r[6] += w1 * v1.x * v2.z;
8203 r[7] += w1 * v1.y * v2.z;
8204 r[8] += w1 * v1.z * v2.z;
8207 for (i = 0; i < 9; i++)
8209 for (i = 0; i < 3; i++) {
8210 for (j = 0; j < 3; j++) {
8211 for (k = 0; k < 3; k++) {
8212 q[i+j*3] += r[i+k*3] * r[j+k*3];
8217 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8218 IntGroupIteratorRelease(&iter);
8219 return -1.0; /* Cannot determine the eigenvector */
8222 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8223 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8224 MatrixTranspose(r, r);
8225 for (i = 0; i < 3; i++) {
8226 MatrixVec(&s[i], r, &eigen_vec[i]);
8227 w1 = 1.0 / sqrt(eigen_val[i]);
8228 VecScaleSelf(s[i], w1);
8230 for (k = 0; k < 3; k++) {
8231 u[0] += s[k].x * eigen_vec[k].x;
8232 u[1] += s[k].y * eigen_vec[k].x;
8233 u[2] += s[k].z * eigen_vec[k].x;
8234 u[3] += s[k].x * eigen_vec[k].y;
8235 u[4] += s[k].y * eigen_vec[k].y;
8236 u[5] += s[k].z * eigen_vec[k].y;
8237 u[6] += s[k].x * eigen_vec[k].z;
8238 u[7] += s[k].y * eigen_vec[k].z;
8239 u[8] += s[k].z * eigen_vec[k].z;
8242 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8243 MatrixVec(&org1, u, &org1);
8245 for (i = 0; i < 9; i++)
8251 /* Calculate rmsd */
8252 IntGroupIteratorReset(&iter);
8254 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8256 ap1 = ATOM_AT_INDEX(ap, in);
8257 TransformVec(&tv, trans, &ap1->r);
8259 w += VecLength2(tv);
8262 IntGroupIteratorRelease(&iter);
8268 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8270 * Calculate the transform to fit the given group to the set of reference coordinates.
8271 * The reference coordinates ref is given as either a frame number, an array of
8272 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8273 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8274 * Return values are the transform (that converts the present coordinates to the
8275 * target coordinates) and root mean square deviation (without weight).
8278 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8282 VALUE gval, rval, wval;
8284 IntGroupIterator iter;
8285 int nn, errno, i, j, in, status;
8287 Double *weights, dval[3];
8290 Data_Get_Struct(self, Molecule, mol);
8291 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8293 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8295 ig = IntGroupFromValue(gval);
8296 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8297 IntGroupRelease(ig);
8298 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8300 ref = (Vector *)calloc(sizeof(Vector), nn);
8301 weights = (Double *)calloc(sizeof(Double), nn);
8302 IntGroupIteratorInit(ig, &iter);
8303 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8304 int fn = NUM2INT(rb_Integer(rval));
8305 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8310 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8311 ap = ATOM_AT_INDEX(mol->atoms, in);
8312 if (fn < ap->nframes)
8313 ref[i] = ap->frames[fn];
8314 else ref[i] = ap->r;
8316 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8317 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8318 if (m->row * m->column < nn * 3) {
8322 for (i = 0; i < nn; i++) {
8323 ref[i].x = m->data[i * 3];
8324 ref[i].y = m->data[i * 3 + 1];
8325 ref[i].z = m->data[i * 3 + 2];
8329 rval = rb_protect(rb_ary_to_ary, rval, &status);
8334 if (RARRAY_LEN(rval) < nn) {
8338 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8339 /* Array of 3*nn numbers */
8340 if (RARRAY_LEN(rval) < nn * 3) {
8344 for (i = 0; i < nn; i++) {
8345 for (j = 0; j < 3; j++) {
8346 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8351 dval[j] = NUM2DBL(aval);
8358 /* Array of nn Vector3Ds or Arrays */
8359 for (i = 0; i < nn; i++) {
8360 aval = (RARRAY_PTR(rval))[i];
8361 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8362 VectorFromValue(aval, &ref[i]);
8364 aval = rb_protect(rb_ary_to_ary, aval, &status);
8369 if (RARRAY_LEN(aval) < 3) {
8374 for (j = 0; j < 3; j++) {
8375 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8380 dval[j] = NUM2DBL(aaval);
8390 /* Use atomic weights */
8391 IntGroupIteratorReset(&iter);
8392 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8393 ap = ATOM_AT_INDEX(mol->atoms, in);
8394 weights[i] = ap->weight;
8397 wval = rb_protect(rb_ary_to_ary, wval, &status);
8402 if (RARRAY_LEN(wval) < nn) {
8406 for (i = 0; i < nn; i++) {
8407 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8412 weights[i] = NUM2DBL(wwval);
8415 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8422 IntGroupIteratorRelease(&iter);
8426 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8427 } else if (errno == 1) {
8428 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8429 } else if (errno == 2) {
8430 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8431 } else if (errno == 3) {
8432 rb_jump_tag(status);
8433 } else if (errno == 4) {
8434 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8435 } else if (errno == 5) {
8436 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8437 } else if (errno == 6) {
8438 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8440 return Qnil; /* Not reached */
8447 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8450 s_Molecule_Display(VALUE self)
8453 Data_Get_Struct(self, Molecule, mol);
8454 if (mol->mview != NULL)
8455 MainViewCallback_display(mol->mview);
8463 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8466 s_Molecule_MakeFront(VALUE self)
8469 Data_Get_Struct(self, Molecule, mol);
8470 if (mol->mview != NULL)
8471 MainViewCallback_makeFront(mol->mview);
8477 * update_enabled? -> bool
8479 * Returns true if screen update is enabled; otherwise no.
8482 s_Molecule_UpdateEnabled(VALUE self)
8485 Data_Get_Struct(self, Molecule, mol);
8486 if (mol->mview != NULL && !mol->mview->freezeScreen)
8493 * update_enabled = bool
8495 * Enable or disable screen update. This is effective for automatic update on modification.
8496 * Explicit call to molecule.display() always updates the screen.
8499 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8502 Data_Get_Struct(self, Molecule, mol);
8503 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8504 if (mol->mview != NULL)
8505 mol->mview->freezeScreen = (val == Qfalse);
8513 * show_unitcell(bool)
8514 * show_unitcell = bool
8516 * Set the flag whether to show the unit cell. If no argument is given, the
8517 * current flag is returned.
8520 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8523 Data_Get_Struct(self, Molecule, mol);
8524 if (mol->mview == NULL)
8527 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8528 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8530 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8536 * show_hydrogens(bool)
8537 * show_hydrogens = bool
8539 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8540 * current flag is returned.
8543 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8546 Data_Get_Struct(self, Molecule, mol);
8547 if (mol->mview == NULL)
8550 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8551 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8553 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8559 * show_dummy_atoms(bool)
8560 * show_dummy_atoms = bool
8562 * Set the flag whether to show the dummy atoms. If no argument is given, the
8563 * current flag is returned.
8566 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8569 Data_Get_Struct(self, Molecule, mol);
8570 if (mol->mview == NULL)
8573 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8574 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8576 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8582 * show_expanded(bool)
8583 * show_expanded = bool
8585 * Set the flag whether to show the expanded atoms. If no argument is given, the
8586 * current flag is returned.
8589 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8592 Data_Get_Struct(self, Molecule, mol);
8593 if (mol->mview == NULL)
8596 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8597 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8599 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8605 * show_ellipsoids(bool)
8606 * show_ellipsoids = bool
8608 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8609 * current flag is returned.
8612 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8615 Data_Get_Struct(self, Molecule, mol);
8616 if (mol->mview == NULL)
8619 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8620 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8622 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8627 * is_atom_visible(index) -> Boolean
8629 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8630 * as well as the molecule attributes (showHydrogens, etc.)
8633 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8638 Data_Get_Struct(self, Molecule, mol);
8639 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8640 if (idx < 0 || idx >= mol->natoms)
8642 ap = ATOM_AT_INDEX(mol->atoms, idx);
8643 if (mol->mview != NULL) {
8644 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8646 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8648 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8651 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8656 * show_graphite -> Integer
8657 * show_graphite = Integer
8658 * show_graphite = boolean
8660 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
8661 * number of rings to display for each direction.
8662 * If the argument is boolean, only the show/hide flag is set.
8665 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
8668 Data_Get_Struct(self, Molecule, mol);
8669 if (mol->mview == NULL)
8672 if (argv[0] == Qnil || argv[0] == Qfalse)
8673 mol->mview->showGraphiteFlag = 0;
8674 else if (argv[0] == Qtrue)
8675 mol->mview->showGraphiteFlag = 1;
8677 int n = NUM2INT(rb_Integer(argv[0]));
8679 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
8680 mol->mview->showGraphite = n;
8682 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8684 return INT2NUM(mol->mview->showGraphite);
8689 * show_graphite? -> boolean
8691 * Return whether the graphite is set visible or not.
8694 s_Molecule_ShowGraphiteFlag(VALUE self)
8697 Data_Get_Struct(self, Molecule, mol);
8698 if (mol->mview == NULL)
8700 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
8705 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
8706 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
8707 * show_periodic_image = boolean
8709 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
8710 * set but no visual effects are observed.
8711 * If the argument is boolean, only the show/hide flag is modified.
8714 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
8720 Data_Get_Struct(self, Molecule, mol);
8721 if (mol->mview == NULL)
8723 rb_scan_args(argc, argv, "01", &val);
8725 /* Change current settings */
8726 if (val == Qnil || val == Qfalse)
8727 mol->mview->showPeriodicImageFlag = 0;
8728 else if (val == Qtrue)
8729 mol->mview->showPeriodicImageFlag = 1;
8731 val = rb_ary_to_ary(val);
8732 for (i = 0; i < 6; i++) {
8733 if (i < RARRAY_LEN(val))
8734 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
8736 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
8737 rb_raise(rb_eMolbyError, "bad arguments");
8738 for (i = 0; i < 6; i++)
8739 mol->mview->showPeriodicImage[i] = ival[i];
8741 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8744 for (i = 0; i < 6; i++)
8745 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
8751 * show_periodic_image? -> boolean
8753 * Return whether the periodic images are set to visible or not. This flag is
8754 * independent from the show_periodic_image settings.
8757 s_Molecule_ShowPeriodicImageFlag(VALUE self)
8760 Data_Get_Struct(self, Molecule, mol);
8761 if (mol->mview == NULL)
8763 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
8772 * Set the flag whether to draw the model in line mode. If no argument is given, the
8773 * current flag is returned.
8776 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
8779 Data_Get_Struct(self, Molecule, mol);
8780 if (mol->mview == NULL)
8783 mol->mview->lineMode = (RTEST(argv[0]) != 0);
8784 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8786 return (mol->mview->lineMode ? Qtrue : Qfalse);
8793 * Resize the model drawing to fit in the window.
8796 s_Molecule_ResizeToFit(VALUE self)
8799 Data_Get_Struct(self, Molecule, mol);
8800 if (mol->mview != NULL)
8801 MainView_resizeToFit(mol->mview);
8807 * get_view_rotation -> [[ax, ay, az], angle]
8809 * Get the current rotation for the view. Angle is in degree, not radian.
8812 s_Molecule_GetViewRotation(VALUE self)
8817 Data_Get_Struct(self, Molecule, mol);
8818 if (mol->mview == NULL)
8820 TrackballGetRotate(mol->mview->track, f);
8821 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
8825 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
8830 * get_view_scale -> float
8832 * Get the current scale for the view.
8835 s_Molecule_GetViewScale(VALUE self)
8838 Data_Get_Struct(self, Molecule, mol);
8839 if (mol->mview == NULL)
8841 return rb_float_new(TrackballGetScale(mol->mview->track));
8846 * get_view_center -> Vector
8848 * Get the current center point of the view.
8851 s_Molecule_GetViewCenter(VALUE self)
8856 Data_Get_Struct(self, Molecule, mol);
8857 if (mol->mview == NULL)
8859 TrackballGetTranslate(mol->mview->track, f);
8860 v.x = -f[0] * mol->mview->dimension;
8861 v.y = -f[1] * mol->mview->dimension;
8862 v.z = -f[2] * mol->mview->dimension;
8863 return ValueFromVector(&v);
8868 * set_view_rotation([ax, ay, az], angle) -> self
8870 * Set the current rotation for the view. Angle is in degree, not radian.
8873 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
8878 Data_Get_Struct(self, Molecule, mol);
8879 if (mol->mview == NULL)
8881 VectorFromValue(aval, &v);
8882 if (NormalizeVec(&v, &v))
8883 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
8887 f[0] = -NUM2DBL(rb_Float(angval));
8888 TrackballSetRotate(mol->mview->track, f);
8889 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8895 * set_view_scale(scale) -> self
8897 * Set the current scale for the view.
8900 s_Molecule_SetViewScale(VALUE self, VALUE aval)
8903 Data_Get_Struct(self, Molecule, mol);
8904 if (mol->mview == NULL)
8906 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
8907 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8913 * set_view_center(vec) -> self
8915 * Set the current center point of the view.
8918 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
8923 Data_Get_Struct(self, Molecule, mol);
8924 if (mol->mview == NULL)
8926 VectorFromValue(aval, &v);
8927 f[0] = -v.x / mol->mview->dimension;
8928 f[1] = -v.y / mol->mview->dimension;
8929 f[2] = -v.z / mol->mview->dimension;
8930 TrackballSetTranslate(mol->mview->track, f);
8931 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8937 * set_background_color(red, green, blue)
8939 * Set the background color of the model window.
8942 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
8945 Data_Get_Struct(self, Molecule, mol);
8946 if (mol->mview != NULL) {
8947 VALUE rval, gval, bval;
8948 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
8949 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
8956 * create_graphic(kind, color, points, fill = nil) -> integer
8958 * Create a new graphic object.
8959 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
8960 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
8961 * points: an array of Vectors
8965 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
8971 VALUE kval, cval, pval, fval;
8972 Data_Get_Struct(self, Molecule, mol);
8973 if (mol->mview == NULL)
8974 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8975 rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
8976 kval = rb_obj_as_string(kval);
8977 memset(&g, 0, sizeof(g));
8979 p = RSTRING_PTR(kval);
8980 if (strcmp(p, "line") == 0)
8981 g.kind = kMainViewGraphicLine;
8982 else if (strcmp(p, "poly") == 0)
8983 g.kind = kMainViewGraphicPoly;
8984 else if (strcmp(p, "cylinder") == 0)
8985 g.kind = kMainViewGraphicCylinder;
8986 else if (strcmp(p, "cone") == 0)
8987 g.kind = kMainViewGraphicCone;
8988 else if (strcmp(p, "ellipsoid") == 0)
8989 g.kind = kMainViewGraphicEllipsoid;
8990 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
8991 g.closed = (RTEST(fval) ? 1 : 0);
8992 cval = rb_ary_to_ary(cval);
8993 n = RARRAY_LEN(cval);
8994 if (n < 3 || n >= 5)
8995 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
8998 for (i = 0; i < n; i++)
8999 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9000 pval = rb_ary_to_ary(pval);
9001 n = RARRAY_LEN(pval);
9002 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9004 rb_raise(rb_eArgError, "no control points are given");
9006 case kMainViewGraphicLine:
9008 rb_raise(rb_eArgError, "the line object must have at least two control points");
9010 case kMainViewGraphicPoly:
9012 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9014 case kMainViewGraphicCylinder:
9015 case kMainViewGraphicCone:
9017 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9020 case kMainViewGraphicEllipsoid:
9024 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9027 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9028 for (i = 0; i < n; i++) {
9031 v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
9034 VectorFromValue(RARRAY_PTR(pval)[i], &v);
9036 g.points[i * 3] = v.x;
9037 g.points[i * 3 + 1] = v.y;
9038 g.points[i * 3 + 2] = v.z;
9040 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9042 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9043 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9044 g.points[7] = g.points[11] = g.points[3];
9046 MainView_insertGraphic(mol->mview, -1, &g);
9047 return INT2NUM(mol->mview->ngraphics - 1);
9052 * remove_graphic(index) -> integer
9054 * Remove a graphic object.
9057 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9061 Data_Get_Struct(self, Molecule, mol);
9062 if (mol->mview == NULL)
9063 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9064 i = NUM2INT(rb_Integer(ival));
9065 if (i < 0 || i >= mol->mview->ngraphics)
9066 rb_raise(rb_eArgError, "graphic index is out of range");
9067 MainView_removeGraphic(mol->mview, i);
9073 * ngraphics -> integer
9075 * Get the number of graphic objects.
9078 s_Molecule_NGraphics(VALUE self)
9081 Data_Get_Struct(self, Molecule, mol);
9082 if (mol->mview == NULL)
9083 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9084 return INT2NUM(mol->mview->ngraphics);
9089 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9091 * Change the point_index-th control point of graphic_index-th graphic object
9095 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
9097 MainViewGraphic *gp;
9101 Data_Get_Struct(self, Molecule, mol);
9102 if (mol->mview == NULL)
9103 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9104 index = NUM2INT(rb_Integer(gval));
9105 if (index < 0 || index >= mol->mview->ngraphics)
9106 rb_raise(rb_eArgError, "the graphic index is out of range");
9107 gp = mol->mview->graphics + index;
9108 index = NUM2INT(rb_Integer(pval));
9109 if (index < 0 || index >= gp->npoints)
9110 rb_raise(rb_eArgError, "the point index is out of range");
9111 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9112 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
9113 v.x = NUM2DBL(rb_Float(nval));
9115 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
9116 gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
9117 gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9119 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9122 v.x = kInvalidFloat;
9124 } else VectorFromValue(nval, &v);
9126 gp->points[index * 3] = v.x;
9127 gp->points[index * 3 + 1] = v.y;
9128 gp->points[index * 3 + 2] = v.z;
9129 MoleculeCallback_notifyModification(mol, 0);
9135 * set_graphic_color(graphic_index, new_value) -> new_value
9137 * Change the color of graphic_index-th graphic object
9141 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9143 MainViewGraphic *gp;
9146 Data_Get_Struct(self, Molecule, mol);
9147 if (mol->mview == NULL)
9148 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9149 index = NUM2INT(rb_Integer(gval));
9150 if (index < 0 || index >= mol->mview->ngraphics)
9151 rb_raise(rb_eArgError, "the graphic index is out of range");
9152 gp = mol->mview->graphics + index;
9153 cval = rb_ary_to_ary(cval);
9154 n = RARRAY_LEN(cval);
9155 if (n != 3 && n != 4)
9156 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9157 for (index = 0; index < n; index++) {
9158 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
9162 MoleculeCallback_notifyModification(mol, 0);
9168 * show_graphic(graphic_index) -> self
9170 * Enable the visible flag of the graphic_index-th graphic object
9174 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9176 MainViewGraphic *gp;
9179 Data_Get_Struct(self, Molecule, mol);
9180 if (mol->mview == NULL)
9181 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9182 index = NUM2INT(rb_Integer(gval));
9183 if (index < 0 || index >= mol->mview->ngraphics)
9184 rb_raise(rb_eArgError, "the graphic index is out of range");
9185 gp = mol->mview->graphics + index;
9187 MoleculeCallback_notifyModification(mol, 0);
9193 * hide_graphic(graphic_index) -> self
9195 * Disable the visible flag of the graphic_index-th graphic object
9199 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9201 MainViewGraphic *gp;
9204 Data_Get_Struct(self, Molecule, mol);
9205 if (mol->mview == NULL)
9206 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9207 index = NUM2INT(rb_Integer(gval));
9208 if (index < 0 || index >= mol->mview->ngraphics)
9209 rb_raise(rb_eArgError, "the graphic index is out of range");
9210 gp = mol->mview->graphics + index;
9212 MoleculeCallback_notifyModification(mol, 0);
9220 * Show the string in the info text box.
9223 s_Molecule_ShowText(VALUE self, VALUE arg)
9226 Data_Get_Struct(self, Molecule, mol);
9227 if (mol->mview != NULL)
9228 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9234 * md_arena -> MDArena
9236 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
9237 * this molecule, a new arena is created.
9240 s_Molecule_MDArena(VALUE self)
9244 Data_Get_Struct(self, Molecule, mol);
9245 if (mol->arena == NULL)
9247 retval = ValueFromMDArena(mol->arena);
9253 * set_parameter_attr(type, index, key, value, src) -> value
9255 * This method is used only internally.
9258 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9260 /* This method is called from MolAction to change a MM parameter attribute. */
9265 Data_Get_Struct(self, Molecule, mol);
9266 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
9267 vval = s_ParameterRef_SetAttr(pval, kval, vval);
9269 /* This is the special part of this method; it allows modification of the src field. */
9270 /* (ParameterRef#set_attr sets 0 to the src field) */
9271 Data_Get_Struct(pval, ParameterRef, pref);
9272 up = ParameterRefGetPar(pref);
9273 up->bond.src = FIX2INT(sval);
9280 * parameter -> Parameter
9282 * Get the local parameter of this molecule. If not defined, returns nil.
9285 s_Molecule_Parameter(VALUE self)
9288 Data_Get_Struct(self, Molecule, mol);
9289 /* if (mol->par == NULL)
9291 return s_NewParameterValueFromValue(self);
9296 * selectedMO -> IntGroup
9298 * Returns a group of selected mo in the "MO Info" table. If the MO info table
9299 * is not selected, returns nil. If the MO info table is selected but no MOs
9300 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
9303 s_Molecule_SelectedMO(VALUE self)
9308 Data_Get_Struct(self, Molecule, mol);
9309 if (mol->mview == NULL)
9311 ig = MainView_selectedMO(mol->mview);
9314 IntGroupOffset(ig, 1);
9315 val = ValueFromIntGroup(ig);
9316 IntGroupRelease(ig);
9322 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
9324 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
9325 * If the molecule does not contain a basis set information, then returns nil.
9328 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
9331 Vector o, dx, dy, dz;
9334 Int npoints = 80 * 80 * 80;
9335 Data_Get_Struct(self, Molecule, mol);
9336 if (mol->bset == NULL)
9338 rb_scan_args(argc, argv, "01", &nval);
9340 npoints = NUM2INT(rb_Integer(nval));
9341 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9343 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));
9347 s_Cubegen_callback(double progress, void *ref)
9349 MyAppCallback_setProgressValue(progress);
9350 if (MyAppCallback_checkInterrupt())
9357 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
9358 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
9360 * Calculate the molecular orbital with number mo and create a 'cube' file.
9361 * In the first form, the cube size is estimated from the atomic coordinates. In the
9362 * second form, the cube dimension is explicitly given.
9363 * Returns fname when successful, nil otherwise.
9364 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
9365 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
9366 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
9369 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
9371 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
9373 Int mono, nx, ny, nz, npoints;
9374 Vector o, dx, dy, dz;
9377 Data_Get_Struct(self, Molecule, mol);
9378 if (mol->bset == NULL)
9379 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
9380 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
9382 /* Set up parameters */
9383 mono = NUM2INT(rb_Integer(mval));
9384 if (mono <= 0 || mono > mol->bset->ncomps)
9385 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
9387 if (mol->bset->rflag != 0)
9388 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
9389 mono += mol->bset->ncomps;
9392 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
9393 /* Automatic grid formation */
9395 npoints = NUM2INT(rb_Integer(oval));
9399 else if (npoints < 8)
9400 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
9401 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9402 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
9406 VectorFromValue(oval, &o);
9407 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
9408 VectorFromValue(dxval, &dx);
9410 dx.x = NUM2DBL(rb_Float(dxval));
9413 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
9414 VectorFromValue(dyval, &dy);
9416 dy.y = NUM2DBL(rb_Float(dyval));
9419 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
9420 VectorFromValue(dzval, &dz);
9422 dz.z = NUM2DBL(rb_Float(dzval));
9425 nx = NUM2INT(rb_Integer(nxval));
9426 ny = NUM2INT(rb_Integer(nyval));
9427 nz = NUM2INT(rb_Integer(nzval));
9428 if (nx <= 0 || ny <= 0 || nz <= 0)
9429 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
9430 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
9431 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);
9435 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
9439 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
9441 /* Output to file */
9442 MoleculeCallback_displayName(mol, buf, sizeof buf);
9443 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
9445 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
9447 /* Discard the cube */
9448 MoleculeClearCubeAtIndex(mol, index);
9456 * Get the number of electrostatic potential info.
9459 s_Molecule_NElpots(VALUE self)
9462 Data_Get_Struct(self, Molecule, mol);
9463 return INT2NUM(mol->nelpots);
9470 * Get the electrostatic potential info at the given index. If present, then the
9471 * return value is [Vector, Float] (position and potential). If not present, then
9475 s_Molecule_Elpot(VALUE self, VALUE ival)
9479 Data_Get_Struct(self, Molecule, mol);
9480 idx = NUM2INT(rb_Integer(ival));
9481 if (idx < 0 || idx >= mol->nelpots)
9483 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
9488 * add_gaussian_orbital_shell(sym, nprims, atom_index)
9490 * To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
9491 * and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
9495 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
9498 int sym, nprims, a_idx, n;
9499 Data_Get_Struct(self, Molecule, mol);
9500 sym = NUM2INT(rb_Integer(symval));
9501 nprims = NUM2INT(rb_Integer(npval));
9502 a_idx = NUM2INT(rb_Integer(aval));
9503 n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
9505 rb_raise(rb_eMolbyError, "Molecule is emptry");
9507 rb_raise(rb_eMolbyError, "Low memory");
9509 rb_raise(rb_eMolbyError, "Unknown orbital type");
9511 rb_raise(rb_eMolbyError, "Unknown error");
9517 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
9519 * To be used internally. Add a gaussian primitive coefficients.
9522 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
9526 Double exponent, contraction, contraction_sp;
9527 Data_Get_Struct(self, Molecule, mol);
9528 exponent = NUM2DBL(rb_Float(expval));
9529 contraction = NUM2DBL(rb_Float(cval));
9530 contraction_sp = NUM2DBL(rb_Float(cspval));
9531 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
9533 rb_raise(rb_eMolbyError, "Molecule is emptry");
9535 rb_raise(rb_eMolbyError, "Low memory");
9537 rb_raise(rb_eMolbyError, "Unknown error");
9545 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
9548 s_Molecule_MOType(VALUE self)
9551 Data_Get_Struct(self, Molecule, mol);
9552 if (mol != NULL && mol->bset != NULL) {
9554 int rflag = mol->bset->rflag;
9557 else if (rflag == 2)
9560 return rb_str_new2(s);
9566 * set_mo_coefficients(idx, energy, coefficients)
9568 * To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system,
9569 * beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
9570 * of MO coefficients.
9573 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
9579 Data_Get_Struct(self, Molecule, mol);
9580 idx = NUM2INT(rb_Integer(ival));
9581 energy = NUM2DBL(rb_Float(eval));
9582 aval = rb_ary_to_ary(aval);
9583 ncomps = RARRAY_LEN(aval);
9584 coeffs = (Double *)calloc(sizeof(Double), ncomps);
9585 if (coeffs == NULL) {
9589 for (i = 0; i < ncomps; i++)
9590 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
9591 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
9594 rb_raise(rb_eMolbyError, "Molecule is emptry");
9596 rb_raise(rb_eMolbyError, "Low memory");
9598 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
9600 rb_raise(rb_eMolbyError, "Bad MO index");
9602 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
9604 rb_raise(rb_eMolbyError, "Unknown error");
9610 * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
9612 * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
9613 * ne_alpha, ne_beta: number of alpha/beta electrons.
9616 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
9619 Int rflag, na, nb, n;
9620 Data_Get_Struct(self, Molecule, mol);
9621 rflag = NUM2INT(rb_Integer(rval));
9622 na = NUM2INT(rb_Integer(naval));
9623 nb = NUM2INT(rb_Integer(nbval));
9624 n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
9626 rb_raise(rb_eMolbyError, "Molecule is emptry");
9628 rb_raise(rb_eMolbyError, "Low memory");
9630 rb_raise(rb_eMolbyError, "Unknown error");
9636 * search_equivalent_atoms(ig = nil)
9638 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
9641 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
9647 Data_Get_Struct(self, Molecule, mol);
9648 if (mol->natoms == 0)
9650 rb_scan_args(argc, argv, "01", &val);
9652 ig = IntGroupFromValue(val);
9654 result = MoleculeSearchEquivalentAtoms(mol, ig);
9656 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
9658 IntGroupRelease(ig);
9659 val = rb_ary_new2(mol->natoms);
9660 for (i = 0; i < mol->natoms; i++)
9661 rb_ary_push(val, INT2NUM(result[i]));
9668 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
9670 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
9671 * Name is the name of the new pi anchor, and group is the atoms that define
9672 * the pi system. Type (a String) is an atom type for MM implementation.
9673 * Weights is the relative weights of the component atoms; if omitted, then
9674 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
9675 * The weight values will be normalized so that the sum of the weights is 1.0.
9676 * The weight values must be positive.
9677 * Index is the atom index where the created pi-anchor is inserted in the
9678 * atoms array; if omitted, the pi-anchor is inserted after the component atom
9679 * having the largest index.
9682 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
9687 Int i, n, idx, last_component;
9691 if (argc < 2 || argc >= 6)
9692 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
9696 Data_Get_Struct(self, Molecule, mol);
9697 ig = IntGroupFromValue(gval);
9698 memset(&a, 0, sizeof(a));
9699 memset(&an, 0, sizeof(an));
9700 strncpy(a.aname, StringValuePtr(nval), 4);
9701 if (a.aname[0] == '_')
9702 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
9703 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
9704 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
9705 if (n >= mol->natoms) {
9706 AtomConnectResize(&an.connect, 0);
9707 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
9709 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
9712 if (an.connect.count == 0)
9713 rb_raise(rb_eMolbyError, "no atoms are specified");
9714 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
9715 for (i = 0; i < an.connect.count; i++) {
9716 an.coeffs[i] = 1.0 / an.connect.count;
9718 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
9720 if (argv[0] != Qnil)
9721 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
9725 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
9726 if (argv[0] != Qnil) {
9727 VALUE aval = rb_ary_to_ary(argv[0]);
9729 if (RARRAY_LEN(aval) != an.connect.count)
9730 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
9731 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
9732 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
9734 rb_raise(rb_eMolbyError, "the weight value must be positive");
9738 for (i = 0; i < an.connect.count; i++)
9739 an.coeffs[i] /= sum;
9744 if (argc > 0 && argv[0] != Qnil) {
9746 idx = NUM2INT(rb_Integer(argv[0]));
9748 if (idx < 0 || idx > mol->natoms) {
9749 /* Immediately after the last specified atom */
9750 idx = last_component + 1;
9752 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
9753 memmove(a.anchor, &an, sizeof(PiAnchor));
9754 /* Use residue information of the last specified atom */
9755 ap = ATOM_AT_INDEX(mol->atoms, last_component);
9756 a.resSeq = ap->resSeq;
9757 strncpy(a.resName, ap->resName, 4);
9758 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
9760 MoleculeCalculatePiAnchorPosition(mol, idx);
9761 aref = AtomRefNew(mol, idx);
9762 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
9767 * current -> Molecule
9769 * Get the currently "active" molecule.
9772 s_Molecule_Current(VALUE klass)
9774 return ValueFromMolecule(MoleculeCallback_currentMolecule());
9779 * Molecule[] -> Molecule
9780 * Molecule[n] -> Molecule
9781 * Molecule[name] -> Molecule
9782 * Molecule[name, k] -> Molecule
9783 * Molecule[regex] -> Molecule
9784 * Molecule[regex, k] -> Molecule
9786 * Molecule[] is equivalent to Molecule.current.
9787 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
9788 * Molecule[name] gives the first document (in the order of creation time) that has
9789 * the given name. If a second argument (k) is given, the k-th document that has the
9790 * given name is returned.
9791 * Molecule[regex] gives the first document (in the order of creation time) that
9792 * has a name matching the regular expression. If a second argument (k) is given,
9793 * the k-th document that has a name matching the re is returned.
9796 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
9802 rb_scan_args(argc, argv, "02", &val, &kval);
9804 return s_Molecule_Current(klass);
9805 if (rb_obj_is_kind_of(val, rb_cInteger)) {
9807 mol = MoleculeCallback_moleculeAtIndex(idx);
9808 } else if (rb_obj_is_kind_of(val, rb_cString)) {
9809 char *p = StringValuePtr(val);
9810 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
9811 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
9812 MoleculeCallback_displayName(mol, buf, sizeof buf);
9813 if (strcmp(buf, p) == 0 && --k == 0)
9816 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
9817 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
9818 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
9820 MoleculeCallback_displayName(mol, buf, sizeof buf);
9821 name = rb_str_new2(buf);
9822 if (rb_reg_match(val, name) != Qnil && --k == 0)
9825 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
9829 else return ValueFromMolecule(mol);
9834 * list -> array of Molecules
9836 * Get the list of molecules associated to the documents, in the order of creation
9837 * time of the document. If no document is open, returns an empry array.
9840 s_Molecule_List(VALUE klass)
9847 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
9848 rb_ary_push(ary, ValueFromMolecule(mol));
9856 * ordered_list -> array of Molecules
9858 * Get the list of molecules associated to the documents, in the order of front-to-back
9859 * ordering of the associated window. If no document is open, returns an empry array.
9862 s_Molecule_OrderedList(VALUE klass)
9869 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
9870 rb_ary_push(ary, ValueFromMolecule(mol));
9878 * error_message -> String
9880 * Get the error_message from the last load/save method. If no error, returns nil.
9883 s_Molecule_ErrorMessage(VALUE klass)
9885 if (gLoadSaveErrorMessage == NULL)
9887 else return rb_str_new2(gLoadSaveErrorMessage);
9892 * set_error_message(String)
9893 * Molecule.error_message = String
9895 * Get the error_message from the last load/save method. If no error, returns nil.
9898 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
9900 if (gLoadSaveErrorMessage != NULL) {
9901 free(gLoadSaveErrorMessage);
9902 gLoadSaveErrorMessage = NULL;
9905 sval = rb_str_to_str(sval);
9906 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
9913 * self == Molecule -> boolean
9915 * True if the two arguments point to the same molecule.
9918 s_Molecule_Equal(VALUE self, VALUE val)
9920 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
9921 Molecule *mol1, *mol2;
9922 Data_Get_Struct(self, Molecule, mol1);
9923 Data_Get_Struct(val, Molecule, mol2);
9924 return (mol1 == mol2 ? Qtrue : Qfalse);
9925 } else return Qfalse;
9933 /* Define module Molby */
9934 rb_mMolby = rb_define_module("Molby");
9936 /* Define Vector3D, Transform, IntGroup */
9939 /* Define MDArena */
9940 Init_MolbyMDTypes();
9942 /* class Molecule */
9943 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
9944 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
9945 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
9946 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
9947 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
9948 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
9949 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
9950 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
9951 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
9952 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
9953 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
9954 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
9955 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
9956 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
9957 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
9958 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
9959 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
9960 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
9961 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
9962 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
9963 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
9964 /* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
9965 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
9966 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
9967 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
9968 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
9969 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
9970 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
9971 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
9972 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
9973 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
9974 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
9975 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
9976 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
9977 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
9978 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
9979 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
9980 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
9982 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
9983 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
9984 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
9985 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
9986 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
9988 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
9989 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
9990 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
9991 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
9992 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
9993 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
9995 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
9996 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
9997 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
9998 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
9999 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
10000 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
10001 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
10002 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
10003 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
10004 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
10005 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
10006 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
10007 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
10008 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
10009 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
10010 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
10011 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
10012 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
10013 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
10014 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
10015 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
10016 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
10017 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
10018 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
10019 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
10020 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
10021 rb_define_alias(rb_cMolecule, "+", "add");
10022 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
10023 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
10024 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
10025 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
10026 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
10027 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
10028 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
10029 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
10030 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
10031 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
10032 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
10033 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
10034 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
10035 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
10036 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
10037 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
10038 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
10039 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
10040 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
10041 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
10042 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
10043 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
10044 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
10045 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
10046 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
10047 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
10048 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
10049 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
10050 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
10051 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
10052 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
10053 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
10054 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
10055 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
10056 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
10057 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
10058 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
10059 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
10060 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
10061 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
10062 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
10063 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
10064 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
10065 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
10066 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, 0);
10067 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
10068 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
10069 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
10070 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
10071 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
10072 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
10073 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
10074 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
10075 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
10076 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
10077 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
10078 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
10079 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
10080 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
10081 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
10082 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
10083 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
10084 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
10085 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
10086 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
10087 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
10088 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
10089 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
10090 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
10091 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
10092 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
10093 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
10094 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
10095 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
10096 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
10097 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
10098 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
10099 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
10100 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
10101 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
10102 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
10103 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
10104 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
10105 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
10106 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
10107 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
10108 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
10109 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
10110 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
10111 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
10112 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
10113 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
10114 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
10115 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
10116 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
10117 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
10118 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
10119 #if 1 || !defined(__CMDMAC__)
10120 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
10121 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
10122 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
10123 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
10124 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
10125 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
10126 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
10127 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
10128 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
10129 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
10130 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
10131 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
10132 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
10133 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
10135 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
10136 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
10137 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
10138 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
10139 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
10140 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
10141 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
10142 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
10143 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
10144 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
10145 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
10146 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
10147 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
10148 rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
10149 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
10151 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
10152 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
10154 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
10155 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
10156 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
10157 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
10158 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
10159 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
10160 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
10161 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
10163 /* class MolEnumerable */
10164 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
10165 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
10166 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
10167 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
10168 rb_define_alias(rb_cMolEnumerable, "size", "length");
10169 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
10170 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
10172 /* class AtomRef */
10173 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
10174 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
10176 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
10177 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
10178 s_AtomAttrDefTable[i].id = rb_intern(buf);
10179 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
10181 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
10183 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
10184 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
10185 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
10186 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
10187 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
10188 rb_global_variable(&s_SetAtomAttrString);
10189 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
10190 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
10192 /* class Parameter */
10193 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
10194 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10195 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10196 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10197 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10198 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10199 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10200 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10201 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
10202 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10203 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10204 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10205 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10206 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10207 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10208 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10209 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10210 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10211 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10212 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10213 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10214 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10215 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10216 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10217 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10218 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10219 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
10220 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
10221 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10222 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10223 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10224 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10225 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10226 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10227 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10228 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
10229 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10230 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10231 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10232 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10233 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10234 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10235 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10236 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10237 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10238 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10239 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10240 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10241 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10242 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10243 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10244 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10245 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10246 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
10248 /* class ParEnumerable */
10249 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
10250 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
10251 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
10252 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
10253 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
10254 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
10255 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
10256 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
10257 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
10258 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
10259 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
10261 /* class ParameterRef */
10262 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
10263 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
10265 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
10266 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
10267 s_ParameterAttrDefTable[i].id = rb_intern(buf);
10268 if (s_ParameterAttrDefTable[i].symref != NULL)
10269 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
10270 if (s_ParameterAttrDefTable[i].setter != NULL) {
10272 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
10275 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
10276 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
10277 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
10278 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
10279 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
10280 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
10281 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
10282 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
10284 /* class MolbyError */
10285 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
10287 /* module Kernel */
10288 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
10289 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
10290 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
10291 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
10292 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
10293 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
10294 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
10295 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
10296 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
10297 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
10298 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
10299 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
10300 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
10301 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
10302 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
10303 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
10304 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
10305 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
10306 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
10308 s_ID_equal = rb_intern("==");
10311 #pragma mark ====== External functions ======
10313 static VALUE s_ruby_top_self = Qfalse;
10316 s_evalRubyScriptOnMoleculeSub(VALUE val)
10318 void **ptr = (void **)val;
10319 Molecule *mol = (Molecule *)ptr[1];
10320 VALUE sval = rb_str_new2((char *)ptr[0]);
10322 if (s_ruby_top_self == Qfalse) {
10323 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
10325 if (ptr[2] == NULL) {
10328 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
10332 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 1, sval);
10334 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 4, sval, Qnil, fnval, INT2FIX(1));
10336 VALUE mval = ValueFromMolecule(mol);
10338 return rb_funcall(mval, rb_intern("instance_eval"), 1, sval);
10339 else return rb_funcall(mval, rb_intern("instance_eval"), 3, sval, fnval, INT2FIX(1));
10344 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
10348 VALUE save_interrupt_flag;
10349 char *save_ruby_sourcefile;
10350 int save_ruby_sourceline;
10351 if (gMolbyIsCheckingInterrupt) {
10352 MolActionAlertRubyIsRunning();
10354 return (RubyValue)Qnil;
10357 args[0] = (void *)script;
10358 args[1] = (void *)mol;
10359 args[2] = (void *)fname;
10360 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
10361 save_ruby_sourcefile = ruby_sourcefile;
10362 save_ruby_sourceline = ruby_sourceline;
10363 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
10364 if (*status != 0) {
10365 /* Is this 'exit' exception? */
10366 VALUE last_exception = rb_gv_get("$!");
10367 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
10368 /* Capture exit and return the status value */
10369 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
10373 s_SetInterruptFlag(Qnil, save_interrupt_flag);
10374 ruby_sourcefile = save_ruby_sourcefile;
10375 ruby_sourceline = save_ruby_sourceline;
10381 Molby_showRubyValue(RubyValue value, char **outValueString)
10383 VALUE val = (VALUE)value;
10384 if (gMolbyIsCheckingInterrupt) {
10385 MolActionAlertRubyIsRunning();
10392 val = rb_protect(rb_inspect, val, &status);
10394 str = StringValuePtr(val);
10395 if (outValueString != NULL)
10396 *outValueString = strdup(str);
10397 MyAppCallback_showScriptMessage("%s", str);
10402 Molby_showError(int status)
10404 static const int tag_raise = 6;
10405 char *msg = NULL, *msg2;
10406 VALUE val, backtrace;
10407 int interrupted = 0;
10408 if (status == tag_raise) {
10409 VALUE eclass = CLASS_OF(ruby_errinfo);
10410 if (eclass == rb_eInterrupt) {
10416 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
10418 val = rb_eval_string_protect("$!.to_s", &status);
10420 msg = RSTRING_PTR(val);
10421 else msg = "(message not available)";
10423 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
10424 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
10430 Molby_getDescription(void)
10432 extern const char *gVersionString, *gCopyrightString;
10433 extern int gRevisionNumber;
10434 extern char *gLastBuildString;
10436 char *revisionString;
10437 if (gRevisionNumber > 0) {
10438 asprintf(&revisionString, ", revision %d", gRevisionNumber);
10439 } else revisionString = "";
10441 "Molby %s%s\n%s\nLast compile: %s\n"
10442 #if !defined(__CMDMAC__)
10448 "ruby %s, http://www.ruby-lang.org/\n"
10450 "FFTW 3.3.2, http://www.fftw.org/\n"
10451 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
10452 " and Massachusetts Institute of Technology",
10453 gVersionString, revisionString, gCopyrightString, gLastBuildString,
10454 #if !defined(__CMDMAC__)
10455 MyAppCallback_getGUIDescriptionString(),
10457 gRubyVersion, gRubyCopyright);
10458 if (revisionString[0] != 0)
10459 free(revisionString);
10464 Molby_startup(const char *script, const char *dir)
10469 char *respath, *p, *wbuf;
10471 /* Get version/copyright string from Ruby interpreter */
10473 gRubyVersion = strdup(ruby_version);
10474 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
10475 #if defined(__CMDMAC__)
10478 " ", /* Indent for displaying in About dialog */
10480 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
10483 /* Read build and revision information for Molby */
10486 extern int gRevisionNumber;
10487 extern char *gLastBuildString;
10488 FILE *fp = fopen("../buildInfo.txt", "r");
10489 gLastBuildString = "";
10491 if (fgets(buf, sizeof(buf), fp) != NULL) {
10492 char *p1 = strchr(buf, '\"');
10493 char *p2 = strrchr(buf, '\"');
10494 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
10495 memmove(buf, p1 + 1, p2 - p1 - 1);
10496 buf[p2 - p1 - 1] = 0;
10497 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
10502 fp = fopen("../revisionInfo.txt", "r");
10503 gRevisionNumber = 0;
10505 if (fgets(buf, sizeof(buf), fp) != NULL) {
10506 gRevisionNumber = strtol(buf, NULL, 0);
10512 #if defined(__CMDMAC__)
10513 wbuf = Molby_getDescription();
10514 printf("%s\n", wbuf);
10518 /* Read atom display parameters */
10519 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
10520 #if defined(__CMDMAC__)
10521 fprintf(stderr, "%s\n", wbuf);
10523 MyAppCallback_setConsoleColor(1);
10524 MyAppCallback_showScriptMessage("%s", wbuf);
10525 MyAppCallback_setConsoleColor(0);
10530 /* Read default parameters */
10531 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
10532 if (wbuf != NULL) {
10533 #if defined(__CMDMAC__)
10534 fprintf(stderr, "%s\n", wbuf);
10536 MyAppCallback_setConsoleColor(1);
10537 MyAppCallback_showScriptMessage("%s", wbuf);
10538 MyAppCallback_setConsoleColor(0);
10543 /* Initialize Ruby interpreter */
10546 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
10548 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
10549 ruby_incpush(libpath);
10553 ruby_script("Molby");
10555 /* Find the resource path (the parent directory of the given directory) */
10556 respath = strdup(dir);
10557 p = strrchr(respath, '/');
10558 if (p == NULL && PATH_SEPARATOR != '/')
10559 p = strrchr(respath, PATH_SEPARATOR);
10562 val = Ruby_NewFileStringValue(respath);
10563 rb_define_global_const("MolbyResourcePath", val);
10566 /* Define Molby classes */
10568 RubyDialogInitClass();
10570 rb_define_const(rb_mMolby, "ResourcePath", val);
10571 val = Ruby_NewFileStringValue(dir);
10572 rb_define_const(rb_mMolby, "ScriptPath", val);
10573 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
10574 val = Ruby_NewFileStringValue(p);
10575 rb_define_const(rb_mMolby, "MbsfPath", val);
10578 #if defined(__CMDMAC__)
10579 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
10581 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
10586 /* Create objects for stdout and stderr */
10587 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10588 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
10589 rb_gv_set("$stdout", val);
10590 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10591 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
10592 rb_gv_set("$stderr", val);
10594 /* Create objects for stdin */
10595 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10596 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
10597 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
10598 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
10599 rb_gv_set("$stdin", val);
10603 /* Global variable to hold backtrace */
10604 rb_define_variable("$backtrace", &gMolbyBacktrace);
10607 /* Register interrupt check code */
10608 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL);
10612 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
10613 s_SetIntervalTimer(0, 50);
10616 /* Read the startup script */
10617 if (script != NULL && script[0] != 0) {
10618 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
10620 rb_load_protect(rb_str_new2(script), 0, &status);
10623 Molby_showError(status);
10625 MyAppCallback_showScriptMessage("Done.\n");
10630 Molby_buildARGV(int argc, const char **argv)
10633 rb_ary_clear(rb_argv);
10634 for (i = 0; i < argc; i++) {
10635 VALUE arg = rb_tainted_str_new2(argv[i]);
10637 rb_ary_push(rb_argv, arg);