5 * Created by Toshi Nagata on 09/08/11.
6 * Copyright 2007-2009 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 "../MD/MDCore.h"
20 #include "../MD/MDSurface.h"
21 #include "../MD/MDPressure.h"
23 //#include "env.h" /* For ruby_frame */
30 #pragma mark ====== Global Values ======
34 #pragma mark ====== MDArena class ======
37 MDArenaFromValue(VALUE val)
39 if (rb_obj_is_kind_of(val, rb_cMDArena)) {
41 Data_Get_Struct(val, MDArena, arena);
47 ValueFromMDArena(struct MDArena *arena)
50 md_arena_retain(arena);
51 return Data_Wrap_Struct(rb_cMDArena, 0, (void (*)(void *))md_arena_release, arena);
55 s_MDArena_panic_func(MDArena *arena, const char *msg)
57 rb_raise(rb_eMolbyError, "MD Error: %s", msg);
61 s_MDArena_Run_or_minimize(VALUE self, VALUE arg, int minimize)
64 int nsteps, retval, start_step;
65 Data_Get_Struct(self, MDArena, arena);
66 nsteps = NUM2INT(rb_Integer(arg));
67 if (arena->is_running)
68 rb_raise(rb_eMolbyError, "the simulation is already running. You cannot do simulation recursively.");
70 rb_raise(rb_eMolbyError, "the number of steps should be non-negative integer");
72 if (arena->is_initialized < 2 || arena->xmol->needsMDRebuild) {
73 const char *msg = md_prepare(arena, 0);
75 rb_raise(rb_eMolbyError, "cannot initialize for MD: %s", msg);
77 arena->end_step = arena->start_step + nsteps;
78 md_log(arena, "%s for %d steps\n", (minimize ? "Minimizing" : "Running"), nsteps);
79 start_step = arena->start_step;
81 /* Create a new frame with current coordinates */
82 /* if (nsteps > 0 && MoleculeGetNumberOfFrames(arena->xmol) == 0) {
83 ig = IntGroupNewWithPoints(0, 1, -1);
84 MolActionCreateAndPerform(arena->xmol, gMolActionInsertFrames, ig, 0, NULL);
89 retval = md_main(arena, minimize);
91 if (retval == 0 || arena->step > start_step) {
92 int i, natoms = arena->xmol->natoms;
94 Vector *vp = (Vector *)malloc(sizeof(Vector) * (natoms > 4 ? natoms : 4));
95 IntGroup *ig = IntGroupNewWithPoints(0, natoms, -1);
97 if (arena->pressure != NULL && arena->pressure->disabled == 0)
100 if (arena->minimize_cell && minimize)
103 if (arena->step > start_step) {
104 /* Copy coordinates and velocities from mol to xmol */
105 for (i = 0, ap = arena->mol->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
107 MolActionCreateAndPerform(arena->xmol, gMolActionSetAtomPositions, ig, natoms, vp);
108 for (i = 0, ap = arena->mol->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
110 MolActionCreateAndPerform(arena->xmol, gMolActionSetAtomVelocities, ig, natoms, vp);
111 if (copy_cell && arena->mol->cell != NULL) {
112 vp[0] = arena->mol->cell->axes[0];
113 vp[1] = arena->mol->cell->axes[1];
114 vp[2] = arena->mol->cell->axes[2];
115 vp[3] = arena->mol->cell->origin;
116 MolActionCreateAndPerform(arena->xmol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
119 /* Copy forces (this is valid even for "zero-step" run) */
120 for (i = 0, ap = arena->mol->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
122 MolActionCreateAndPerform(arena->xmol, gMolActionSetAtomForces, ig, natoms, vp);
128 rb_raise(rb_eMolbyError, "MD Error: %s", arena->errmsg);
130 if (minimize && arena->minimize_complete && rb_block_given_p())
140 * Run the simulation for n steps.
143 s_MDArena_Run(VALUE self, VALUE arg)
145 return s_MDArena_Run_or_minimize(self, arg, 0);
150 * minimize(n) -> self
151 * minimize(n) { ... } -> self
153 * Minimize the energy for n steps. If a block is given, it is executed when minimization is complete.
156 s_MDArena_Minimize(VALUE self, VALUE arg)
158 return s_MDArena_Run_or_minimize(self, arg, 1);
163 * prepare(check_only = false) -> self or nil
165 * Prepare for the MD calculation; refresh the internal information even if initialization is
167 * If check_only is true, then only the parameter checking is done.
168 * Returns self when initialization is complete; returns nil if some MM parameters are missing;
169 * otherwise, an exception is raised.
172 s_MDArena_Prepare(int argc, VALUE *argv, VALUE self)
175 Int check_only = 0, status;
178 Data_Get_Struct(self, MDArena, arena);
179 rb_scan_args(argc, argv, "01", &fval);
182 status = MoleculePrepareMDArena(arena->xmol, check_only, &msg);
184 /* Exception object is created first to have a chance to do free(msg) */
185 VALUE exval = rb_exc_new2(rb_eMolbyError, msg);
188 } else if (status > 0) {
197 Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
200 IntGroup *ig1, *ig2, *ig3;
203 Data_Get_Struct(self, MDArena, arena);
204 rb_scan_args(argc, argv, "01", &fval);
208 arena->is_initialized = 0;
211 /* Rebuild the tables */
212 ig1 = ig2 = ig3 = NULL;
213 nangles = MoleculeFindMissingAngles(mol, &angles);
214 ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
215 nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
217 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
218 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
220 IntGroupRelease(ig1);
222 if (ndihedrals > 0) {
223 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
224 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
226 IntGroupRelease(ig2);
228 if (nimpropers > 0) {
229 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
230 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
232 IntGroupRelease(ig3);
235 /* Prepare parameters and internal information */
236 msg = md_prepare(arena, check_only);
238 /* Some parameters are missing? */
240 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
243 rb_raise(rb_eMolbyError, "cannot initialize for MD: %s", msg);
246 /* The local parameter list is updated */
249 if (mol->par == NULL)
250 mol->par = ParameterNew();
251 for (parType = kFirstParType; parType <= kLastParType; parType++) {
252 /* Delete global and undefined parameters */
253 UnionPar *up, *upbuf;
256 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
257 if (up->bond.src != 0)
258 IntGroupAdd(ig1, idx, 1);
260 if (IntGroupGetCount(ig1) > 0)
261 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
262 IntGroupRelease(ig1);
263 /* Copy global and undefined parameters from arena and insert to mol->par */
264 nparams = ParameterGetCountForType(arena->par, parType);
267 upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
270 for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
271 if (up->bond.src > 0)
272 IntGroupAdd(ig1, idx, 1); /* Global parameter */
273 else if (up->bond.src < 0)
274 IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
276 if ((count = IntGroupGetCount(ig1)) > 0) {
277 /* Insert global parameters (at the top) */
278 ParameterCopy(arena->par, parType, upbuf, ig1);
279 ig3 = IntGroupNewWithPoints(0, count, -1);
280 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
281 IntGroupRelease(ig3);
283 if ((count = IntGroupGetCount(ig2)) > 0) {
284 /* Insert undefined parameters (at the bottom) */
285 ParameterCopy(arena->par, parType, upbuf, ig2);
286 idx = ParameterGetCountForType(mol->par, parType);
287 ig3 = IntGroupNewWithPoints(idx, count, -1);
288 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
289 IntGroupRelease(ig3);
291 IntGroupRelease(ig2);
292 IntGroupRelease(ig1);
295 mol->needsMDRebuild = 0; /* We know the "modified" parameters are consistent with the MDArena */
306 * energies -> [total, bond, angle, dihedral, improper, vdw, electrostatic, auxiliary, surface, kinetic, net [, ewald]
308 * Get the current energies.
311 s_MDArena_Energies(VALUE self)
316 static Int s_indices[] = {kBondIndex, kAngleIndex, kDihedralIndex, kImproperIndex, kVDWIndex, kElectrostaticIndex, kAuxiliaryIndex, kSurfaceIndex, kKineticIndex };
317 Data_Get_Struct(self, MDArena, arena);
319 rb_ary_push(val, rb_float_new(arena->total_energy * INTERNAL2KCAL));
320 for (i = 0; i < sizeof(s_indices) / sizeof(s_indices[0]); i++)
321 rb_ary_push(val, rb_float_new(arena->energies[s_indices[i]] * INTERNAL2KCAL));
322 rb_ary_push(val, rb_float_new((arena->energies[kKineticIndex] + arena->total_energy) * INTERNAL2KCAL));
323 if (arena->use_ewald)
324 rb_ary_push(val, rb_float_new((arena->energies[kESCorrectionIndex] + arena->energies[kPMEIndex]) * INTERNAL2KCAL));
328 static VALUE s_LogFileSym, s_CoordFileSym, s_VelFileSym, s_ForceFileSym,
329 s_DebugFileSym, s_DebugOutputLevelSym, s_StepSym, s_CoordOutputFreqSym,
330 s_EnergyOutputFreqSym, s_CoordFrameSym, s_TimestepSym, s_CutoffSym,
331 s_ElectroCutoffSym, s_PairlistDistanceSym, s_SwitchDistanceSym, s_TemperatureSym, s_TransientTempSym,
332 s_AverageTempSym, s_AndersenFreqSym, s_AndersenCouplingSym, s_RandomSeedSym,
333 s_DielectricSym, s_GradientConvergenceSym, s_CoordinateConvergenceSym, s_UseXplorShiftSym,
334 s_Scale14VdwSym, s_Scale14ElectSym, s_RelocateCenterSym,
335 s_SurfaceProbeRadiusSym, s_SurfaceTensionSym, s_SurfacePotentialFreqSym, s_UseGraphiteSym,
336 s_AlchemicalLambdaSym, s_AlchemicalDeltaLambdaSym, s_AlchemicalEnergySym, s_MinimizeCellSym,
337 s_UseEwaldSym, s_EwaldBetaSym, s_EwaldGridSym, s_EwaldFreqSym, s_EwaldOrderSym;
339 struct s_MDArenaAttrDef {
341 VALUE *symref; /* Address of s_LogFileSym etc. */
342 ID id; /* Ruby ID of the symbol; will be set within Init_MolbyMDTypes() */
343 ID sid; /* Ruby ID of the symbol plus '='; will be set within Init_MolbyMDTypes() */
344 char type; /* s: string (const char *), i: Int, f: Double, e: Double in energy dimension (unit conversion is necessary). */
345 /* Uppercase: read-only. */
346 int offset; /* Offset in the MDArena structure. */
348 static struct s_MDArenaAttrDef s_MDArenaAttrDefTable[] = {
349 {"log_file", &s_LogFileSym, 0, 0, 's', offsetof(MDArena, log_result_name)},
350 {"coord_file", &s_CoordFileSym, 0, 0, 's', offsetof(MDArena, coord_result_name)},
351 {"vel_file", &s_VelFileSym, 0, 0, 's', offsetof(MDArena, vel_result_name)},
352 {"force_file", &s_ForceFileSym, 0, 0, 's', offsetof(MDArena, force_result_name)},
353 {"debug_file", &s_DebugFileSym, 0, 0, 's', offsetof(MDArena, debug_result_name)},
354 {"debug_output_level", &s_DebugOutputLevelSym, 0, 0, 'i', offsetof(MDArena, debug_output_level)},
355 {"step", &s_StepSym, 0, 0, 'I', offsetof(MDArena, step)},
356 {"coord_output_freq", &s_CoordOutputFreqSym, 0, 0, 'i', offsetof(MDArena, coord_output_freq)},
357 {"energy_output_freq", &s_EnergyOutputFreqSym, 0, 0, 'i', offsetof(MDArena, energy_output_freq)},
358 {"coord_frame", &s_CoordFrameSym, 0, 0, 'I', offsetof(MDArena, coord_result_frame)},
359 {"timestep", &s_TimestepSym, 0, 0, 'f', offsetof(MDArena, timestep)},
360 {"cutoff", &s_CutoffSym, 0, 0, 'f', offsetof(MDArena, cutoff)},
361 {"electro_cutoff", &s_ElectroCutoffSym, 0, 0, 'f', offsetof(MDArena, electro_cutoff)},
362 {"pairlist_distance", &s_PairlistDistanceSym, 0, 0, 'f', offsetof(MDArena, pairlist_distance)},
363 {"switch_distance", &s_SwitchDistanceSym, 0, 0, 'f', offsetof(MDArena, switch_distance)},
364 {"temperature", &s_TemperatureSym, 0, 0, 'f', offsetof(MDArena, temperature)},
365 {"transient_temperature", &s_TransientTempSym, 0, 0, 'F', offsetof(MDArena, transient_temperature)},
366 {"average_temperature", &s_AverageTempSym, 0, 0, 'F', offsetof(MDArena, average_temperature)},
367 {"andersen_freq", &s_AndersenFreqSym, 0, 0, 'i', offsetof(MDArena, andersen_thermo_freq)},
368 {"andersen_coupling", &s_AndersenCouplingSym, 0, 0, 'f', offsetof(MDArena, andersen_thermo_coupling)},
369 {"random_seed", &s_RandomSeedSym, 0, 0, 'i', offsetof(MDArena, random_seed)},
370 {"dielectric", &s_DielectricSym, 0, 0, 'f', offsetof(MDArena, dielectric)},
371 {"gradient_convergence", &s_GradientConvergenceSym, 0, 0, 'f', offsetof(MDArena, gradient_convergence)},
372 {"coordinate_convergence", &s_CoordinateConvergenceSym, 0, 0, 'f', offsetof(MDArena, coordinate_convergence)},
373 {"use_xplor_shift", &s_UseXplorShiftSym, 0, 0, 'i', offsetof(MDArena, use_xplor_shift)},
374 {"scale14_vdw", &s_Scale14VdwSym, 0, 0, 'f', offsetof(MDArena, scale14_vdw)},
375 {"scale14_elect", &s_Scale14ElectSym, 0, 0, 'f', offsetof(MDArena, scale14_elect)},
376 {"relocate_center", &s_RelocateCenterSym, 0, 0, 'i', offsetof(MDArena, relocate_center)},
377 {"surface_probe_radius", &s_SurfaceProbeRadiusSym, 0, 0, 'f', offsetof(MDArena, probe_radius)},
378 {"surface_tension", &s_SurfaceTensionSym, 0, 0, 'f', offsetof(MDArena, surface_tension)},
379 {"surface_potential_freq", &s_SurfacePotentialFreqSym, 0, 0, 'i', offsetof(MDArena, surface_potential_freq)},
380 {"use_graphite", &s_UseGraphiteSym, 0, 0, 'i', offsetof(MDArena, use_graphite)},
381 {"alchemical_lambda", &s_AlchemicalLambdaSym, 0, 0, 'f', offsetof(MDArena, alchem_lambda)},
382 {"alchemical_delta_lambda", &s_AlchemicalDeltaLambdaSym, 0, 0, 'f', offsetof(MDArena, alchem_dlambda)},
383 {"alchemical_energy", &s_AlchemicalEnergySym, 0, 0, 'E', offsetof(MDArena, alchem_energy)},
384 {"minimize_cell", &s_MinimizeCellSym, 0, 0, 'b', offsetof(MDArena, minimize_cell)},
385 {"use_ewald", &s_UseEwaldSym, 0, 0, 'i', offsetof(MDArena, use_ewald)},
386 {"ewald_beta", &s_EwaldBetaSym, 0, 0, 'f', offsetof(MDArena, ewald_beta)},
387 {"ewald_grid", &s_EwaldGridSym, 0, 0, 0, offsetof(MDArena, ewald_grid_x)},
388 {"ewald_freq", &s_EwaldFreqSym, 0, 0, 'i', offsetof(MDArena, ewald_freq)},
389 {"ewald_order", &s_EwaldOrderSym, 0, 0, 'i', offsetof(MDArena, ewald_order)},
390 {NULL} /* Sentinel */
393 static VALUE s_PresFreqSym, s_PresCouplingSym, s_PresSym, s_PresCellFlexSym, s_PresFluctCellOriginSym, s_PresFluctCellOrientSym;
394 static struct s_MDArenaAttrDef s_MDPressureAttrDefTable[] = {
395 {"pressure_freq", &s_PresFreqSym, 0, 0, 'i', offsetof(MDPressureArena, freq)},
396 {"pressure_coupling", &s_PresCouplingSym, 0, 0, 'f', offsetof(MDPressureArena, coupling)},
397 {"pressure", &s_PresSym, 0, 0, 'X', offsetof(MDPressureArena, apply)},
398 {"pressure_cell_flexibility", &s_PresCellFlexSym, 0, 0, 'Y', offsetof(MDPressureArena, cell_flexibility)},
399 {"pressure_fluctuate_cell_origin", &s_PresFluctCellOriginSym, 0, 0, 'f', offsetof(MDPressureArena, fluctuate_cell_origin)},
400 {"pressure_fluctuate_cell_orientation", &s_PresFluctCellOrientSym, 0, 0, 'f', offsetof(MDPressureArena, fluctuate_cell_orientation)},
401 {NULL} /* Sentinel */
408 * Get the attribute value. The attribute values also can be accessed by self.attribute_name.
411 s_MDArena_Get(VALUE self, VALUE attr)
415 struct s_MDArenaAttrDef *dp;
416 ID aid = rb_to_id(attr);
417 Data_Get_Struct(self, MDArena, arena);
418 if (aid == SYM2ID(s_EwaldGridSym)) {
419 /* Array of three grid values */
420 return rb_ary_new3(3, INT2NUM(arena->ewald_grid_x), INT2NUM(arena->ewald_grid_y), INT2NUM(arena->ewald_grid_z));
422 for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
424 char *p = (char *)arena + dp->offset;
428 const char *cp = *((const char **)p);
432 return Ruby_NewFileStringValue(cp);
436 return INT2NUM((Int)(*((Byte *)p)));
439 return INT2NUM(*((Int *)p));
442 return rb_float_new(*((Double *)p));
445 return rb_float_new(*((Double *)p) * INTERNAL2KCAL);
447 rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
451 for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
454 MDPressureArena *pres = arena->pressure;
457 pp = (char *)pres + dp->offset;
461 return INT2NUM(*((Int *)pp));
464 return INT2NUM((Int)(*((Byte *)pp)));
467 return rb_float_new(*((Double *)pp));
470 return rb_float_new(*((Double *)pp) * INTERNAL2KCAL);
472 /* Isotropic pressure only */
473 return rb_ary_new3(3, rb_float_new(pres->apply[0]), rb_float_new(pres->apply[4]), rb_float_new(pres->apply[8]));
475 VALUE aval = rb_ary_new();
477 for (j = 0; j < 8; j++)
478 rb_ary_push(aval, rb_float_new(pres->cell_flexibility[j]));
482 rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
486 rb_raise(rb_eMolbyError, "unknown attribute name (%s)", rb_id2name(aid));
487 return Qnil; /* Not reached */
491 s_MDArena_GetAttr(VALUE self)
493 ID aid = rb_frame_this_func();
494 return s_MDArena_Get(self, ID2SYM(aid));
501 * Set the attribute value. The attributes can also be modified by self.attribute_name = value.
504 s_MDArena_Set(VALUE self, VALUE attr, VALUE val)
508 struct s_MDArenaAttrDef *dp;
509 ID aid = rb_to_id(attr);
510 attr = ID2SYM(aid); /* May be used later */
511 Data_Get_Struct(self, MDArena, arena);
512 if (aid == SYM2ID(s_EwaldGridSym)) {
513 if (rb_obj_is_kind_of(val, rb_cNumeric)) {
514 i = NUM2INT(rb_Integer(val));
516 rb_raise(rb_eMolbyError, "The ewald grid must be positive integer");
517 arena->ewald_grid_x = arena->ewald_grid_y = arena->ewald_grid_z = i;
520 val = rb_ary_to_ary(val);
523 rb_raise(rb_eMolbyError, "The ewald grid must be an integer or an array of three integers");
524 for (i = 0; i < 3; i++) {
525 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
527 rb_raise(rb_eMolbyError, "The ewald grid must be positive integer");
529 arena->ewald_grid_x = ival[0];
530 arena->ewald_grid_y = ival[1];
531 arena->ewald_grid_z = ival[2];
535 for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
537 char *p = (char *)arena + dp->offset;
540 const char *cp = (val == Qnil ? NULL : (const char *)strdup(FileStringValuePtr(val)));
541 const char **cpp = (const char **)p;
543 if (*cpp == cp || (*cpp != NULL && cp != NULL && strcmp(*cpp, cp) == 0))
544 return val; /* No need to change */
547 if (cp != NULL && cp[0] == 0) {
551 /* Close the corresponding FILE if necessary */
552 if (attr == s_LogFileSym)
553 fpp = &(arena->log_result);
554 else if (attr == s_CoordFileSym)
555 fpp = &(arena->coord_result);
556 else if (attr == s_VelFileSym)
557 fpp = &(arena->vel_result);
558 else if (attr == s_ForceFileSym)
559 fpp = &(arena->force_result);
560 else if (attr == s_DebugFileSym)
561 fpp = &(arena->debug_result);
563 if (fpp != NULL && *fpp != NULL) {
571 *((Int *)p) = NUM2INT(rb_Integer(val));
574 *((Byte *)p) = NUM2INT(rb_Integer(val));
577 *((Double *)p) = NUM2DBL(rb_Float(val));
580 *((Double *)p) = NUM2DBL(rb_Float(val) * KCAL2INTERNAL);
582 case 'S': case 'I': case 'B': case 'F': case 'E':
583 rb_raise(rb_eMolbyError, "The attribute '%s' is read-only", rb_id2name(aid));
585 rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
589 for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
592 MDPressureArena *pres = arena->pressure;
594 arena->pressure = pres = pressure_new();
595 pp = (char *)pres + dp->offset;
598 *((Int *)pp) = NUM2INT(rb_Integer(val));
601 *((Byte *)pp) = NUM2INT(rb_Integer(val));
604 *((Double *)pp) = NUM2DBL(rb_Float(val));
607 *((Double *)pp) = NUM2DBL(rb_Float(val) * KCAL2INTERNAL);
610 /* Isotropic pressure only */
611 val = rb_ary_to_ary(val);
612 memset(pres->apply, 0, sizeof(Mat33));
613 for (j = 0; j < 3 && j < RARRAY_LEN(val); j++)
614 pres->apply[j * 4] = NUM2DBL(rb_Float(RARRAY_PTR(val)[j]));
617 val = rb_ary_to_ary(val);
618 for (j = 0; j < 8; j++) {
619 if (j < RARRAY_LEN(val))
620 pres->cell_flexibility[j] = NUM2DBL(rb_Float(RARRAY_PTR(val)[j]));
621 else pres->cell_flexibility[j] = 0.0;
624 case 'S': case 'I': case 'B': case 'F': case 'E':
625 rb_raise(rb_eMolbyError, "The attribute '%s' is read-only", rb_id2name(aid));
627 rb_raise(rb_eMolbyError, "Internal inconsistency: unknown type field");
631 rb_raise(rb_eMolbyError, "unknown attribute name (%s)", rb_id2name(aid));
632 return Qnil; /* Not reached */
636 s_MDArena_SetAttr(VALUE self, VALUE val)
639 struct s_MDArenaAttrDef *dp;
640 // ID aid = ruby_frame->last_func;
641 ID aid = rb_frame_this_func();
642 for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
644 return s_MDArena_Set(self, *(dp->symref), val);
646 for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
648 return s_MDArena_Set(self, *(dp->symref), val);
650 rb_raise(rb_eMolbyError, "unknown attribute name (%s)", rb_id2name(aid));
651 return Qnil; /* Not reached */
658 * Returns a (frozen) hash that contains the current value for all attribute keys.
661 s_MDArena_ToHash(VALUE self)
665 struct s_MDArenaAttrDef *dp;
666 hash = rb_hash_new();
667 for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
668 VALUE attr = ID2SYM(dp->id);
669 rb_hash_aset(hash, attr, s_MDArena_Get(self, attr));
677 * set_alchemical_perturbation(group1, group2) -> [group1, group2]
679 * Set vanishing and appearing atom groups for alchemical perturbation.
682 s_MDArena_SetAlchemicalPerturbation(VALUE self, VALUE gval1, VALUE gval2)
688 Data_Get_Struct(self, MDArena, arena);
689 if (gval1 == Qnil && gval2 == Qnil) {
690 md_set_alchemical_flags(arena, 0, NULL);
693 if (arena->mol == NULL)
694 rb_raise(rb_eMolbyError, "Molecule is not set");
695 n = arena->xmol->natoms;
696 flags = (char *)calloc(1, n);
697 ig1 = (gval1 == Qnil ? NULL : IntGroupFromValue(gval1));
698 ig2 = (gval2 == Qnil ? NULL : IntGroupFromValue(gval2));
699 for (i = 0; i < n; i++) {
700 if (ig1 != NULL && IntGroupLookupPoint(ig1, i) >= 0)
702 if (ig2 != NULL && IntGroupLookupPoint(ig2, i) >= 0) {
704 rb_raise(rb_eMolbyError, "duplicate atom (%d) in vanishing and appearing groups", i);
708 if (md_set_alchemical_flags(arena, n, flags) != 0)
709 rb_raise(rb_eMolbyError, "cannot set alchemical flags");
712 gval1 = ValueFromIntGroup(ig1);
713 IntGroupRelease(ig1);
716 gval2 = ValueFromIntGroup(ig2);
717 IntGroupRelease(ig2);
719 return rb_ary_new3(2, gval1, gval2);
724 * get_alchemical_perturbation -> [group1, group2] or nil
726 * If alchemical perturbation is enabled, get the current vanishing and appearing atom groups.
727 * Otherwise, return nil.
730 s_MDArena_GetAlchemicalPerturbation(VALUE self)
736 Data_Get_Struct(self, MDArena, arena);
737 if (arena->nalchem_flags == 0)
739 if (arena->mol == NULL)
740 rb_raise(rb_eMolbyError, "Molecule is not set");
743 for (i = 0; i < arena->nalchem_flags; i++) {
744 if (arena->alchem_flags[i] == 1)
745 IntGroupAdd(ig1, i, 1);
746 else if (arena->alchem_flags[i] == 2)
747 IntGroupAdd(ig2, i, 1);
749 gval1 = ValueFromIntGroup(ig1);
750 gval2 = ValueFromIntGroup(ig2);
751 IntGroupRelease(ig1);
752 IntGroupRelease(ig2);
753 return rb_ary_new3(2, gval1, gval2);
758 * set_external_forces(ary) -> self
760 * Set external forces. Ary should be an array of objects that can be converted to Vector3D.
763 s_MDArena_SetExternalForces(VALUE self, VALUE aval)
768 Data_Get_Struct(self, MDArena, arena);
769 if (arena->mol == NULL)
770 rb_raise(rb_eMolbyError, "Molecule is not set");
772 md_set_external_forces(arena, 0, NULL);
775 aval = rb_ary_to_ary(aval);
776 n = RARRAY_LEN(aval);
778 md_set_external_forces(arena, 0, NULL);
781 vp = (Vector *)calloc(sizeof(Vector), n);
782 for (i = 0; i < n; i++) {
783 VectorFromValue(RARRAY_PTR(aval)[i], vp + i);
784 VecScaleSelf(vp[i], KCAL2INTERNAL);
786 md_set_external_forces(arena, n, vp);
793 * get_external_force(index) -> Vector3D or nil
795 * Get the current external force for the atom. If the external force is not set, nil is returned.
798 s_MDArena_GetExternalForce(VALUE self, VALUE ival)
804 Data_Get_Struct(self, MDArena, arena);
805 if (arena->mol == NULL)
806 rb_raise(rb_eMolbyError, "Molecule is not set");
807 i = NUM2INT(rb_Integer(ival));
808 if (i < 0 || i >= arena->nexforces)
810 v = arena->exforces[i];
811 VecScaleSelf(v, INTERNAL2KCAL);
812 vval = ValueFromVector(&v);
818 * init_velocities([temperature]) -> self
820 * Give random (Boltzmann-weighted) velocities to all atoms. If temperature is given,
821 * it is set before giving velocities.
824 s_MDArena_InitVelocities(int argc, VALUE *argv, VALUE self)
828 Data_Get_Struct(self, MDArena, arena);
829 rb_scan_args(argc, argv, "01", &tval);
831 s_MDArena_Set(self, s_TemperatureSym, tval);
832 md_init_velocities(arena);
838 * scale_velocities([temperature]) -> self
840 * Scale atom velocities to match the current temperature. If temperature is given,
841 * it is set before giving velocities.
844 s_MDArena_ScaleVelocities(int argc, VALUE *argv, VALUE self)
848 Data_Get_Struct(self, MDArena, arena);
849 rb_scan_args(argc, argv, "01", &tval);
851 s_MDArena_Set(self, s_TemperatureSym, tval);
852 md_scale_velocities(arena);
860 * Returns an array of valid attributes.
863 s_MDArena_Keys(VALUE self)
867 struct s_MDArenaAttrDef *dp;
869 for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
870 VALUE attr = ID2SYM(dp->id);
871 rb_ary_push(ary, attr);
880 * Print the surface area information to standard output. (for debug)
883 s_MDArena_PrintSurfaceArea(VALUE self)
887 VALUE retval, outval;
888 Data_Get_Struct(self, MDArena, arena);
889 if (arena->sp_arena == NULL)
890 rb_raise(rb_eMolbyError, "surface potential is not available");
891 natoms = arena->mol->natoms;
892 retval = rb_str_new2("Atom area energy forcex1000\n");
893 for (i = 0; i < natoms; i++) {
895 Vector f = arena->forces[kSurfaceIndex * natoms + i];
896 Double area = arena->sp_arena->atom_area[i];
897 Double energy = area * arena->sp_arena->atom_pot[i];
898 VecScaleSelf(f, INTERNAL2KCAL * 1000);
899 energy *= INTERNAL2KCAL;
900 snprintf(buf, sizeof buf, "%5d %11.5f %11.8f %11.5f %11.5f %11.5f\n",
901 i+1, area, energy, f.x, f.y, f.z);
902 rb_str_cat(retval, buf, strlen(buf));
904 outval = rb_gv_get("$stdout");
905 rb_funcall(outval, rb_intern("write"), 1, retval);
911 * bond_par(idx) -> ParameterRef
913 * Returns a parameter that is used for the idx-th bond.
916 s_MDArena_BondPar(VALUE self, VALUE val)
920 Data_Get_Struct(self, MDArena, arena);
921 i = NUM2INT(rb_Integer(val));
922 if (arena->xmol->needsMDRebuild || arena->par == NULL)
923 md_prepare(arena, 1);
924 if (i < 0 || i >= arena->xmol->nbonds) {
925 rb_raise(rb_eMolbyError, "bond index (%d) out of range (0...%d)", i, arena->xmol->nbonds);
927 j = arena->bond_par_i[i];
928 if (j < 0 || j >= arena->par->nbondPars)
929 return Qnil; /* No parameter assigned */
930 return ValueFromMoleculeWithParameterTypeAndIndex(arena->xmol, kBondParType, -j - 1);
935 * angle_par(idx) -> ParameterRef
937 * Returns a parameter that is used for the idx-th angle.
940 s_MDArena_AnglePar(VALUE self, VALUE val)
944 Data_Get_Struct(self, MDArena, arena);
945 i = NUM2INT(rb_Integer(val));
946 if (arena->xmol->needsMDRebuild || arena->par == NULL)
947 md_prepare(arena, 1);
948 if (i < 0 || i >= arena->xmol->nangles) {
949 rb_raise(rb_eMolbyError, "angle index (%d) out of range (0...%d)", i, arena->xmol->nangles);
951 j = arena->angle_par_i[i];
952 if (j < 0 || j >= arena->par->nanglePars)
953 return Qnil; /* No parameter assigned */
954 return ValueFromMoleculeWithParameterTypeAndIndex(arena->xmol, kAngleParType, -j - 1);
959 * dihedral_par(idx) -> ParameterRef
961 * Returns a parameter that is used for the idx-th dihedral.
964 s_MDArena_DihedralPar(VALUE self, VALUE val)
968 Data_Get_Struct(self, MDArena, arena);
969 i = NUM2INT(rb_Integer(val));
970 if (arena->xmol->needsMDRebuild || arena->par == NULL)
971 md_prepare(arena, 1);
972 if (i < 0 || i >= arena->xmol->ndihedrals) {
973 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range (0...%d)", i, arena->xmol->ndihedrals);
975 j = arena->dihedral_par_i[i];
976 if (j < 0 || j >= arena->par->ndihedralPars)
977 return Qnil; /* No parameter assigned */
978 return ValueFromMoleculeWithParameterTypeAndIndex(arena->xmol, kDihedralParType, -j - 1);
983 * improper_par(idx) -> ParameterRef
985 * Returns a parameter that is used for the idx-th improper.
988 s_MDArena_ImproperPar(VALUE self, VALUE val)
992 Data_Get_Struct(self, MDArena, arena);
993 i = NUM2INT(rb_Integer(val));
994 if (arena->xmol->needsMDRebuild || arena->par == NULL)
995 md_prepare(arena, 1);
996 if (i < 0 || i >= arena->xmol->nimpropers) {
997 rb_raise(rb_eMolbyError, "improper index (%d) out of range (0...%d)", i, arena->xmol->nimpropers);
999 j = arena->improper_par_i[i];
1000 if (j < 0 || j >= arena->par->nimproperPars)
1001 return Qnil; /* No parameter assigned */
1002 return ValueFromMoleculeWithParameterTypeAndIndex(arena->xmol, kImproperParType, -j - 1);
1007 * vdw_par(idx) -> ParameterRef
1009 * Returns a vdw parameter that is used for the idx-th atom.
1012 s_MDArena_VdwPar(VALUE self, VALUE val)
1016 Data_Get_Struct(self, MDArena, arena);
1017 i = NUM2INT(rb_Integer(val));
1018 if (arena->xmol->needsMDRebuild || arena->par == NULL)
1019 md_prepare(arena, 1);
1020 if (i < 0 || i >= arena->xmol->natoms) {
1021 rb_raise(rb_eMolbyError, "atom index (%d) out of range (0...%d)", i, arena->xmol->natoms);
1023 j = arena->vdw_par_i[i];
1024 if (j < 0 || j >= arena->par->nvdwPars)
1025 return Qnil; /* No parameter assigned */
1026 return ValueFromMoleculeWithParameterTypeAndIndex(arena->xmol, kVdwParType, -j - 1);
1030 s_MDArena_testPME(int argc, VALUE *argv, VALUE self)
1032 extern int pme_test(MDArena *);
1034 if (rb_obj_is_kind_of(self, rb_cMDArena)) {
1035 Data_Get_Struct(self, MDArena, arena);
1036 if (argc == 0 || RTEST(argv[0])) {
1037 arena->use_ewald = 1;
1038 arena->xmol->needsMDRebuild = 1;
1039 if (argc > 0 && rb_obj_is_kind_of(argv[0], rb_cNumeric)) {
1040 Int n = NUM2INT(rb_Integer(argv[0]));
1042 arena->ewald_grid_x = n;
1043 arena->ewald_grid_y = n;
1044 arena->ewald_grid_z = n;
1046 arena->use_ewald = 2; /* Direct Ewald */
1050 arena->use_ewald = 0;
1051 arena->xmol->needsMDRebuild = 1;
1053 s_MDArena_Prepare(0, NULL, self);
1055 rb_raise(rb_eMolbyError, "class method MDArena.test_pme is not supported now");
1061 Init_MolbyMDTypes(void)
1064 struct s_MDArenaAttrDef *dp;
1068 rb_cMDArena = rb_define_class("MDArena", rb_cObject);
1069 /* rb_define_alloc_func(rb_cMDArena, s_MDArena_Alloc);
1070 rb_define_private_method(rb_cMDArena, "initialize", s_MDArena_Initialize, 1); */
1071 rb_define_method(rb_cMDArena, "run", s_MDArena_Run, 1);
1072 rb_define_method(rb_cMDArena, "minimize", s_MDArena_Minimize, 1);
1073 rb_define_method(rb_cMDArena, "prepare", s_MDArena_Prepare, -1);
1074 rb_define_method(rb_cMDArena, "energies", s_MDArena_Energies, 0);
1075 rb_define_method(rb_cMDArena, "[]", s_MDArena_Get, 1);
1076 rb_define_method(rb_cMDArena, "[]=", s_MDArena_Set, 2);
1077 rb_define_method(rb_cMDArena, "to_hash", s_MDArena_ToHash, 0);
1078 rb_define_method(rb_cMDArena, "keys", s_MDArena_Keys, 0);
1079 rb_define_method(rb_cMDArena, "set_alchemical_perturbation", s_MDArena_SetAlchemicalPerturbation, 2);
1080 rb_define_method(rb_cMDArena, "get_alchemical_perturbation", s_MDArena_GetAlchemicalPerturbation, 0);
1081 rb_define_method(rb_cMDArena, "set_external_forces", s_MDArena_SetExternalForces, 1);
1082 rb_define_method(rb_cMDArena, "get_external_force", s_MDArena_GetExternalForce, 1);
1083 rb_define_method(rb_cMDArena, "init_velocities", s_MDArena_InitVelocities, -1);
1084 rb_define_method(rb_cMDArena, "scale_velocities", s_MDArena_ScaleVelocities, -1);
1085 rb_define_method(rb_cMDArena, "print_surface_area", s_MDArena_PrintSurfaceArea, 0);
1087 rb_define_method(rb_cMDArena, "bond_par", s_MDArena_BondPar, 1);
1088 rb_define_method(rb_cMDArena, "angle_par", s_MDArena_AnglePar, 1);
1089 rb_define_method(rb_cMDArena, "dihedral_par", s_MDArena_DihedralPar, 1);
1090 rb_define_method(rb_cMDArena, "improper_par", s_MDArena_ImproperPar, 1);
1091 rb_define_method(rb_cMDArena, "vdw_par", s_MDArena_VdwPar, 1);
1093 rb_define_method(rb_cMDArena, "pme_test", s_MDArena_testPME, -1);
1094 // rb_define_singleton_method(rb_cMDArena, "pme_test", s_MDArena_testPME, -1);
1096 /* All setter and getter are handled with the same C function (attribute name is taken
1098 for (i = 0, dp = s_MDArenaAttrDefTable; dp->name != NULL; i++, dp++) {
1099 rb_define_method(rb_cMDArena, dp->name, s_MDArena_GetAttr, 0);
1100 strncpy(name, dp->name, 38);
1103 rb_define_method(rb_cMDArena, name, s_MDArena_SetAttr, 1);
1104 dp->id = rb_intern(dp->name);
1105 dp->sid = rb_intern(name);
1106 *(dp->symref) = ID2SYM(dp->id);
1108 for (i = 0, dp = s_MDPressureAttrDefTable; dp->name != NULL; i++, dp++) {
1109 rb_define_method(rb_cMDArena, dp->name, s_MDArena_GetAttr, 0);
1110 strncpy(name, dp->name, 38);
1113 rb_define_method(rb_cMDArena, name, s_MDArena_SetAttr, 1);
1114 dp->id = rb_intern(dp->name);
1115 dp->sid = rb_intern(name);
1116 *(dp->symref) = ID2SYM(dp->id);