Skip to content

Commit f3e8695

Browse files
Consistent use of chemical potential (#24)
* Started changing units for chemical potential * progressed in using chemical potential * keep improving units management * Improved compute_acceptance_probability * updated MC * Updated Widom * improved move writer * Improved writting * Improved and cleaned constant * prepare releavse of 0.3.3-beta
1 parent 3cf2a13 commit f3e8695

16 files changed

+470
-358
lines changed

mc-topology

src/constants.f90

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
module constants
22

33
use, intrinsic :: iso_fortran_env, only: real64
4+
use parameters
45

56
implicit none
67

7-
real(real64), parameter :: PI = 3.14159265358979323846_real64 ! Constant Pi
8-
real(real64), parameter :: TWOPI = 2.0_real64 * PI ! Two times Pi
9-
real(real64), parameter :: SQRTPI = SQRT(PI) ! Square root of Pi
10-
real(real64), parameter :: KB_JK = 1.380658D-23 ! Boltzmann constant (J/K)
11-
real(real64), parameter :: KB_kcalmol = 0.0019872041_real64 ! Boltzmann constant (kcal/mol)
12-
real(real64), parameter :: EPS0_INV_kcalA = 332.0637_real64 ! e^2 / 4 pi epsilon_0 (kcal Å/(mol e^2))
13-
real(real64), parameter :: KB_eVK = 8.6173852D-5 ! Boltzmann constant (eV/K)
8+
real(real64), parameter :: PI = 3.1415926536_real64 ! Constant Pi
9+
real(real64), parameter :: TWOPI = 2.0_real64 * PI ! Two times Pi
10+
real(real64), parameter :: SQRTPI = SQRT(PI) ! Square root of Pi
11+
real(real64), parameter :: H_PLANCK = 6.62607015d-34 ! Planck constant (J s)
12+
real(real64), parameter :: EPS0 = 8.854187817d-12 ! F/m = C^2/(N m^2)
13+
real(real64), parameter :: NA = 6.02214076e23 ! Na (mol-1)
14+
real(real64), parameter :: KB = 1.380658d-23 ! Boltzmann constant (J/K)
15+
real(real64), parameter :: E_CHARGE = 1.602176634d-19 ! Elementary charge (C)
16+
17+
real(real64), parameter :: EPS0_INV = E_CHARGE**2 / (4.0_real64 * PI * EPS0) ! Coulomb prefactor e^2 / 4 pi epsilon_0, SI units, N m^2
18+
real(real64), parameter :: EPS0_INV_real = EPS0_INV * J_to_kcal * m_to_A * NA ! Coulomb prefactor in real units,
19+
20+
real(real64) :: beta ! 1 / (kB T)
21+
real(real64), parameter :: KB_kcalmol = KB * NA * J_to_kcal ! Boltzmann constant (in kcal/mol)
22+
1423
real(real64), parameter :: zero = 0.0_real64 ! Zero in real64
1524
real(real64), parameter :: quarter = 0.25_real64 ! 1/4 in real64
1625
real(real64), parameter :: half = 0.5_real64 ! 1/2 in real64
@@ -19,8 +28,5 @@ module constants
1928
real(real64), parameter :: three = 3.0_real64 ! Three in real64
2029
real(real64), parameter :: four = 4.0_real64 ! Four in real64
2130
real(real64), parameter :: ten = 10.0_real64 ! Ten in real64
22-
real(real64), parameter :: error = 1.0D-10 ! Small number for error calculation
23-
real(real64), parameter :: Hplank = 6.62607015D-34 ! Planck constant (J s)
24-
real(real64), parameter :: HBAR = 1.05457182D-34 ! hbar (m^2 kg / s)
25-
real(real64), parameter :: NA = 6.022e23 ! Na (mol-1)
31+
2632
end module constants

src/data_parser.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,7 @@ subroutine TransformCoordinate(box)
14611461
com(1), com(2), com(3))
14621462

14631463
total_mass = ComputeMass(nb%atom_in_residue(i), tmp_atom_masses_1d)
1464-
res%mass_residue(i) = total_mass
1464+
res%mass(i) = total_mass
14651465

14661466
! Save original CoM before applying periodic boundary conditions
14671467
original_com = com ! Save the CoM

src/energy_utils.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ subroutine SingleMolPairwiseEnergy(box, residue_type_1, molecule_index_1, e_non_
189189
end do
190190

191191
! Re-scale energy
192-
e_coulomb = e_coulomb * EPS0_INV_kcalA ! In kcal/mol
192+
e_coulomb = e_coulomb * EPS0_INV_real ! In kcal/mol
193193

194194
end subroutine SingleMolPairwiseEnergy
195195

@@ -379,7 +379,7 @@ subroutine SingleMolEwaldSelf(residue_type, self_energy_1)
379379
end do
380380

381381
! Convert to kcal/mol at the end
382-
self_energy_1 = self_energy_1 * EPS0_INV_kcalA ! In kcal/mol
382+
self_energy_1 = self_energy_1 * EPS0_INV_real ! In kcal/mol
383383

384384
end subroutine SingleMolEwaldSelf
385385

@@ -455,7 +455,7 @@ subroutine ComputePairInteractionEnergy_singlemol(box, residue_type_1, molecule_
455455
end do
456456

457457
! Convert to kcal/mol at the end
458-
e_coulomb = e_coulomb * EPS0_INV_kcalA ! In kcal/mol
458+
e_coulomb = e_coulomb * EPS0_INV_real ! In kcal/mol
459459

460460
end subroutine ComputePairInteractionEnergy_singlemol
461461

src/ewald_energy.f90

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ subroutine ComputeReciprocalEnergy(u_recipCoulomb)
132132
end do
133133

134134
! Convert accumulated energy to correct units (kcal/mol)
135-
! e^2 x Å^2 * kcal Å / (mol e^2 Å^3) = kcal/mol
136-
u_recipCoulomb = u_recipCoulomb * EPS0_INV_kcalA * TWOPI / primary%volume ! In kcal/mol
135+
u_recipCoulomb = u_recipCoulomb * EPS0_INV_real * TWOPI / primary%volume ! In kcal/mol
137136

138137
end subroutine ComputeReciprocalEnergy
139138

@@ -253,10 +252,8 @@ subroutine ComputeRecipEnergySingleMol(residue_type, molecule_index, u_recipCoul
253252

254253
end do
255254

256-
!----------------------------------------------
257255
! Convert accumulated energy to physical units:
258-
!----------------------------------------------
259-
u_recipCoulomb_new = u_recipCoulomb_new * EPS0_INV_kcalA * TWOPI / primary%volume ! In kcal/mol
256+
u_recipCoulomb_new = u_recipCoulomb_new * EPS0_INV_real * TWOPI / primary%volume ! In kcal/mol
260257

261258
end subroutine ComputeRecipEnergySingleMol
262259

@@ -306,7 +303,7 @@ subroutine ComputeEwaldSelfInteractionSingleMol(residue_type, self_energy)
306303
self_energy = self_energy - ewald%alpha / SQRTPI * charge_1**2
307304
end do
308305

309-
self_energy = self_energy * EPS0_INV_kcalA ! In kcal/mol
306+
self_energy = self_energy * EPS0_INV_real ! In kcal/mol
310307

311308
end subroutine ComputeEwaldSelfInteractionSingleMol
312309

@@ -366,7 +363,7 @@ subroutine ComputeIntraResidueRealCoulombEnergySingleMol(residue_type, molecule_
366363
end do
367364
end do
368365

369-
u_intraCoulomb = u_intraCoulomb * EPS0_INV_kcalA ! In kcal/mol
366+
u_intraCoulomb = u_intraCoulomb * EPS0_INV_real ! In kcal/mol
370367

371368
end subroutine ComputeIntraResidueRealCoulombEnergySingleMol
372369

src/helper_utils.f90

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,42 @@ pure real(real64) function vector_norm(v)
175175

176176
end function vector_norm
177177

178+
!--------------------------------------------------------------------
179+
! Pads the given column title to 12 characters and adds one space (A12,1X),
180+
! ensuring consistent alignment with numeric columns printed using I12.
181+
! Appends the formatted field to the growing header line.
182+
!--------------------------------------------------------------------
183+
subroutine add_col(line, title)
184+
185+
! Input argument
186+
character(len=*), intent(inout) :: line
187+
character(len=*), intent(in) :: title
188+
189+
! Local variable
190+
character(len=16) :: tmp
191+
192+
! EXACT match to "(1X,I12)" used by data columns
193+
write(tmp,'(1X, A12)') title
194+
195+
! DO NOT trim, append raw padded field
196+
line = line // tmp
197+
198+
end subroutine add_col
199+
200+
subroutine add_first_col(line, title)
201+
202+
! Input arguments
203+
character(len=*), intent(inout) :: line
204+
character(len=*), intent(in) :: title
205+
206+
! Local variable
207+
character(len=16) :: tmp
208+
209+
! FIRST column: matches (I12)
210+
write(tmp,'(A12)') title
211+
line = line // tmp
212+
213+
end subroutine add_first_col
214+
215+
178216
end module helper_utils

src/input_parser.f90

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ subroutine AllocateAtomArrays()
259259
allocate(reservoir%mol_com(3, nb%type_residue, NB_MAX_MOLECULE))
260260
allocate(reservoir%site_offset(3, nb%type_residue, NB_MAX_MOLECULE, nb%max_atom_in_residue))
261261
allocate(reservoir%num_residues(nb%type_residue))
262-
allocate(res%mass_residue(nb%type_residue))
262+
allocate(res%mass(nb%type_residue))
263+
allocate(res%lambda(nb%type_residue))
263264

264265
! Allocate parameter arrays
265266
allocate(coeff%sigma(nb%type_residue, nb%type_residue,&
@@ -270,6 +271,7 @@ subroutine AllocateAtomArrays()
270271
allocate(res%types_2d(nb%type_residue, nb%max_type_per_residue))
271272
allocate(res%names_2d(nb%type_residue, nb%max_type_per_residue))
272273
allocate(input%fugacity(nb%type_residue))
274+
allocate(input%chemical_potential(nb%type_residue))
273275
allocate(nb%types_per_residue(nb%type_residue))
274276
allocate(nb%atom_in_residue(nb%type_residue))
275277
allocate(nb%bonds_per_residue(nb%type_residue))
@@ -325,6 +327,7 @@ subroutine ParseInputFile(INFILE)
325327
logical :: has_translation_step, has_rotation_step, has_recalibrate
326328
logical :: has_translation_proba, has_rotation_proba
327329
logical :: has_insertdel_proba, has_swap_proba, has_widom_proba
330+
logical :: has_fugacity, has_chemical_potential
328331

329332
has_nb_block = .false.
330333
has_nb_step = .false.
@@ -347,6 +350,8 @@ subroutine ParseInputFile(INFILE)
347350

348351
! Initialize all fugacities to -1.0 (indicating unset)
349352
input%fugacity(:) = -one
353+
! Initialize all chemical_potential to 0.0 (indicating unset)
354+
input%chemical_potential(:) = zero
350355

351356
do
352357
read(INFILE, '(A)', IOSTAT=ios) line
@@ -379,7 +384,7 @@ subroutine ParseInputFile(INFILE)
379384
read(rest_line, *, iostat=ios) val_real
380385
if (ios /= 0) error stop "Error reading temperature"
381386
if (val_real <= zero) error stop "Invalid temperature: must be > 0"
382-
input%temp_K = val_real
387+
input%temperature = val_real
383388
has_temp = .true.
384389

385390
case ("seed")
@@ -473,7 +478,7 @@ subroutine ParseInputFile(INFILE)
473478
end select
474479

475480
! ===============================
476-
! Residue parsing (types, names, fugacity, nb-atoms)
481+
! Residue parsing (types, names, fugacity, chemical potential nb-atoms)
477482
! ===============================
478483
if (in_residue_block) then
479484

@@ -520,6 +525,11 @@ subroutine ParseInputFile(INFILE)
520525
read(rest_line, *, iostat=ios) val_real
521526
input%fugacity(nb%type_residue + 1) = val_real
522527

528+
! Check if the line specifies the chemical potential
529+
else if (trim(token) == 'chemical_potential') then
530+
read(rest_line, *, iostat=ios) val_real
531+
input%chemical_potential(nb%type_residue + 1) = val_real
532+
523533
! Check if the line specifies the number of atoms
524534
else if (trim(token) == 'nb-atoms') then
525535
read(rest_line, *, iostat=ios) val_int
@@ -571,12 +581,24 @@ subroutine ParseInputFile(INFILE)
571581

572582
! Validate fugacity for active residues
573583
do val_int = 1, nb%type_residue
574-
if (input%is_active(val_int) == 1) then
575-
if (input%fugacity(val_int) < zero) then
576-
call AbortRun("Fugacity not provided or invalid for active residue: " &
577-
// trim(res%names_1d(val_int)))
578-
end if
584+
585+
if (input%is_active(val_int) == 0) cycle
586+
587+
has_fugacity = (input%fugacity(val_int) >= zero)
588+
has_chemical_potential = (input%chemical_potential(val_int) < zero)
589+
590+
! Rule 1: at least one must be provided
591+
if (.not.(has_fugacity .or. has_chemical_potential)) then
592+
call AbortRun("Neither fugacity nor chemical potential provided for active residue: " // &
593+
trim(res%names_1d(val_int)))
594+
end if
595+
596+
! Rule 2: cannot both be provided
597+
if (has_fugacity .and. has_chemical_potential) then
598+
call AbortRun("Both fugacity and chemical potential were specified for active residue: " // &
599+
trim(res%names_1d(val_int)))
579600
end if
601+
580602
end do
581603

582604
! === Validation of required parameters ===
@@ -628,7 +650,7 @@ subroutine SortResidues()
628650
integer, allocatable :: keys(:), order(:)
629651
character(len=10), allocatable :: tmp_names_1d(:)
630652
integer, allocatable :: tmp_is_active(:), tmp_atom_in_residue(:), tmp_types_per_residue(:)
631-
real(real64), allocatable :: tmp_fugacity(:)
653+
real(real64), allocatable :: tmp_fugacity(:), tmp_chemical_potential(:)
632654
integer, allocatable :: tmp_types_2d(:,:)
633655
character(len=10), allocatable :: tmp_names_2d(:,:)
634656

@@ -640,6 +662,7 @@ subroutine SortResidues()
640662
allocate(tmp_names_1d(n))
641663
allocate(tmp_is_active(n))
642664
allocate(tmp_fugacity(n))
665+
allocate(tmp_chemical_potential(n))
643666
allocate(tmp_atom_in_residue(n))
644667
allocate(tmp_types_per_residue(n))
645668
allocate(tmp_types_2d(size(res%types_2d,1), size(res%types_2d,2)))
@@ -670,6 +693,7 @@ subroutine SortResidues()
670693
tmp_names_1d(i) = res%names_1d(k)
671694
tmp_is_active(i) = input%is_active(k)
672695
tmp_fugacity(i) = input%fugacity(k)
696+
tmp_chemical_potential(i) = input%chemical_potential(k)
673697
tmp_atom_in_residue(i) = nb%atom_in_residue(k)
674698
tmp_types_per_residue(i) = nb%types_per_residue(k)
675699
tmp_types_2d(i,:) = res%types_2d(k,:)
@@ -680,13 +704,14 @@ subroutine SortResidues()
680704
res%names_1d = tmp_names_1d
681705
input%is_active = tmp_is_active
682706
input%fugacity = tmp_fugacity
707+
input%chemical_potential = tmp_chemical_potential
683708
nb%atom_in_residue = tmp_atom_in_residue
684709
nb%types_per_residue = tmp_types_per_residue
685710
res%types_2d = tmp_types_2d
686711
res%names_2d = tmp_names_2d
687712

688713
! Deallocate temporary arrays
689-
deallocate(keys, order, tmp_names_1d, tmp_is_active, tmp_fugacity, &
714+
deallocate(keys, order, tmp_names_1d, tmp_is_active, tmp_fugacity, tmp_chemical_potential, &
690715
tmp_atom_in_residue, tmp_types_per_residue, tmp_types_2d, tmp_names_2d)
691716

692717
end subroutine SortResidues

0 commit comments

Comments
 (0)