Skip to content

Commit

Permalink
Merge pull request #5 from luizfelippesr/optimize
Browse files Browse the repository at this point in the history
Performance improvements
  • Loading branch information
luizfelippesr committed Oct 26, 2020
2 parents feb0f1c + c9f60d9 commit 64690ff
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 189 deletions.
44 changes: 30 additions & 14 deletions galmag/B_generators/B_generator_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
import scipy.integrate
import scipy.special
from numpy import linalg as LA
from joblib import Parallel, delayed

from galmag.B_field import B_field_component

from .B_generator import B_generator
import galmag.disk_profiles as prof
from galmag.util import get_max_jobs

_default_disk_parameters = {
'disk_modes_normalization': np.array([1., 1., 1.]), # Cn_d
Expand Down Expand Up @@ -74,7 +77,7 @@ def __init__(self, grid=None, box=None, resolution=None,

@property
def _builtin_parameter_defaults(self):

return _default_disk_parameters


Expand Down Expand Up @@ -248,6 +251,7 @@ def _convert_coordinates_to_B_values(self, r_cylindrical,
inner_objects = [item[separator * active_separator] for item in item_list]
outer_objects = [item[~separator * active_separator] for item in item_list]

mode_normalizations = []
for mode_number in range(self.modes_count):

mode_normalization = \
Expand All @@ -262,25 +266,37 @@ def _convert_coordinates_to_B_values(self, r_cylindrical,
1.0,
parameters,
mode='inner')

renormalization = (Br_sun**2 + Bphi_sun**2 + Bz_sun**2)**-0.5

mode_normalization *= renormalization

temp_inner_fields = self._get_B_mode(inner_objects[3:],
mode_number,
mode_normalization,
parameters,
mode='inner')
temp_outer_fields = self._get_B_mode(outer_objects[3:],
mode_number,
mode_normalization,
parameters,
mode='outer')
mode_normalizations.append(mode_normalization)

n_jobs = min(self.modes_count, get_max_jobs())

# Computes free decay modes in parallel (inner part)
inner_fields_list = Parallel(n_jobs=n_jobs)(
delayed(self._get_B_mode)(inner_objects[3:], mode_number,
mode_norm, parameters, mode='inner')
for mode_number, mode_norm in enumerate(mode_normalizations))

# Adds to final solution
for fields in inner_fields_list:
for i in range(3):
inner_objects[i] += fields[i]
del inner_fields_list # Saves memory

# Computes free decay modes in parallel (outer part)
outer_fields_list = Parallel(n_jobs=n_jobs)(
delayed(self._get_B_mode)(outer_objects[3:], mode_number,
mode_norm, parameters, mode='outer')
for mode_number, mode_norm in enumerate(mode_normalizations))

# Adds to final solution
for fields in outer_fields_list:
for i in range(3):
inner_objects[i] += temp_inner_fields[i]
outer_objects[i] += temp_outer_fields[i]
outer_objects[i] += fields[i]
del outer_fields_list # Saves memory

for i in range(3):
result_fields[i][separator * active_separator] += inner_objects[i]
Expand Down
20 changes: 15 additions & 5 deletions galmag/B_generators/B_generator_halo.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@
# along with GalMag. If not, see <http://www.gnu.org/licenses/>.
#
# -*- coding: utf-8 -*-
from galmag.B_field import B_field_component
import numpy as np
from joblib import Parallel, delayed

from .B_generator import B_generator
from galmag.util import curl_spherical, simpson
from galmag.B_field import B_field_component
from galmag.util import simpson
import galmag.halo_free_decay_modes as halo_free_decay_modes
from galmag.halo_profiles import simple_V, simple_alpha
from galmag.galerkin import Galerkin_expansion_coefficients
from galmag.util import get_max_jobs


class B_generator_halo(B_generator):
"""
Expand Down Expand Up @@ -110,6 +114,8 @@ def get_B_field(self, **kwargs):

Bvec = [np.zeros_like(r_sph_grid) for i in range(3)]

n_jobs = min(len(coefficients), get_max_jobs())

if not (parsed_parameters['halo_growing_mode_only'] and
growth_rate<0):

Expand All @@ -125,11 +131,15 @@ def get_B_field(self, **kwargs):
# Computes the normalization at the reference radius
Bsun_p = np.array([0.])

# Comutes free decay modes in parallel
Bmodes = Parallel(n_jobs=n_jobs)(
delayed(halo_free_decay_modes.get_mode)(
r_sph_grid/halo_radius, theta_grid, phi_grid, i+1, symmetric)
for i, coefficient in enumerate(coefficients))

for i, coefficient in enumerate(coefficients):
# Calculates free-decay modes locally
Bmode = halo_free_decay_modes.get_mode(
r_sph_grid/halo_radius, theta_grid,
phi_grid, i+1, symmetric)
Bmode = Bmodes[i]

for j in range(3):
Bvec[j] += Bmode[j] * coefficient
Expand Down
4 changes: 4 additions & 0 deletions galmag/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@
from .Grid import Grid
from .B_field import *
#from Observables import Observables

# Parallel settings
max_jobs = None

97 changes: 53 additions & 44 deletions galmag/galerkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@
"""
import numpy as np
import galmag.halo_free_decay_modes as halo_free_decay_modes
from galmag.util import get_max_jobs
from .Grid import Grid
from .util import curl_spherical, simpson
#from numba import njit, jit
from joblib import Parallel, delayed

def Galerkin_expansion_coefficients(parameters, return_matrix=False,
dtype=np.float64):
Expand Down Expand Up @@ -102,15 +103,13 @@ def Galerkin_expansion_coefficients(parameters, return_matrix=False,
phi_grid = galerkin_grid.phi
theta_grid = galerkin_grid.theta

# local_B_r_spherical, local_B_phi, local_B_theta (for each mode)
Bmodes = [halo_free_decay_modes.get_mode(r_sph_grid,
theta_grid,
phi_grid,
imode, symmetric)
for imode in range(1,nmodes+1)]
n_jobs = min(nmodes, get_max_jobs())

Bmodes = Parallel(n_jobs=n_jobs)(
delayed(halo_free_decay_modes.get_mode)(r_sph_grid, theta_grid,
phi_grid, imode, symmetric)
for imode in range(1,nmodes+1))

# Computes sintheta
sintheta = np.sin(theta_grid)
# Computes alpha
alpha = function_alpha(r_sph_grid,
theta_grid,
Expand All @@ -123,34 +122,20 @@ def Galerkin_expansion_coefficients(parameters, return_matrix=False,
fraction_z=z_v/parameters['halo_radius'])

# Applies the perturbation operator
WBmodes = [perturbation_operator(r_sph_grid,
theta_grid,
phi_grid,
WBmodes = Parallel(n_jobs=n_jobs)(
delayed(perturbation_operator)(r_sph_grid, theta_grid, phi_grid,
Bmode[0], Bmode[1], Bmode[2],
Vs[0], Vs[1], Vs[2], alpha,
Ralpha, Romega,
parameters['halo_dynamo_type'])
for Bmode in Bmodes]

Wij = np.zeros((nmodes,nmodes))
for i in range(nmodes):
for j in range(nmodes):
if i==j:
continue
for Bmode in Bmodes)

integrand = np.zeros_like(r_sph_grid)

for k in range(3):
integrand += Bmodes[i][k]*WBmodes[j][k]

integrand *= r_sph_grid**2 * sintheta

# Integrates over phi assuming axisymmetry
integrand = integrand[:,:,0]*2.0*np.pi
# Integrates over theta
integrand = simpson(integrand, theta_grid[:,:,0])
# Integrates over r
Wij[i,j] += simpson(integrand,r_sph_grid[:,0,0])
# This performs better not in parallel in the tests
# Wij_list = Parallel(n_jobs=n_jobs)(
# delayed(_compute_Wij)(r_sph_grid, theta_grid, Bmodes[i], WBmodes[j])
# for i in range(nmodes) for j in range(nmodes) if i != j)
Wij_list = [_compute_Wij(r_sph_grid, theta_grid, Bmodes[i], WBmodes[j])
for i in range(nmodes) for j in range(nmodes) if i != j]

# Overwrites the diagonal with its correct (gamma) values
if symmetric == True:
Expand All @@ -162,8 +147,16 @@ def Galerkin_expansion_coefficients(parameters, return_matrix=False,
else:
raise ValueError

# (re)constructs the Wij array
Wij = np.zeros((nmodes,nmodes))
k = 0
for i in range(nmodes):
Wij[i,i] = gamma[i]
for j in range(nmodes):
if i==j:
Wij[i,i] = gamma[i]
else:
Wij[i,j] = Wij_list[k]
k+=1

# Solves the eigenvector problem and returns the result
val, vec = np.linalg.eig(Wij)
Expand All @@ -173,7 +166,25 @@ def Galerkin_expansion_coefficients(parameters, return_matrix=False,
return val, vec, Wij


#@jit
def _compute_Wij(r_sph_grid, theta_grid, Bmodes_i, WBmodes_j):

integrand = np.zeros_like(r_sph_grid)

for k in range(3):
integrand += Bmodes_i[k]*WBmodes_j[k]

integrand *= r_sph_grid**2 * np.sin(theta_grid)

# Integrates over phi assuming axisymmetry
integrand = integrand[:,:,0]*2.0*np.pi

# Integrates over theta
integrand = simpson(integrand, theta_grid[:,:,0])

# Integrates over r
return simpson(integrand, r_sph_grid[:, 0, 0])


def perturbation_operator(r, theta, phi, Br, Bt, Bp, Vr, Vt, Vp,
alpha, Ra, Ro, dynamo_type='alpha-omega'):
r"""
Expand Down Expand Up @@ -206,16 +217,14 @@ def perturbation_operator(r, theta, phi, Br, Bt, Bp, Vr, Vt, Vp,

curl_VcrossB = curl_spherical(r, theta, phi,
VcrossBr, VcrossBt, VcrossBp)
WBs = []
for i in range(3):
if dynamo_type=='alpha-omega':
WBs.append(Ra*(curl_aB[i] - curl_aB[2]) \
+ Ro*curl_VcrossB[i])

elif dynamo_type=='alpha2-omega':
WBs.append(Ra*curl_aB[i] + Ro*curl_VcrossB[i])

else:
raise AssertionError('Invalid option: dynamo_type={0}'.format(dynamo_type))
if dynamo_type == 'alpha-omega':
WBs = [Ra*(curl_aB[i] - curl_aB[2]) + Ro*curl_VcrossB[i]
for i in range(3)]
elif dynamo_type == 'alpha2-omega':
WBs = [Ra*curl_aB[i] + Ro*curl_VcrossB[i]
for i in range(3)]
else:
raise AssertionError('Invalid option: dynamo_type={0}'.format(dynamo_type))

return WBs
95 changes: 95 additions & 0 deletions galmag/test/test_free.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#! /usb/bin/env python
r""" Script that tests the free decay modes
This is done by calculating:
$ - \nabla \times \nabla \times \bm{B} / \bm{B} $
and comparing it with the decay rates $\gamma_l$
(they should be exactly equal).
The test is done for increasing grid resolutions.
"""
#! /usb/bin/env python
r""" Script that tests the free decay modes
This is done by calculating:
$ - \nabla \times \nabla \times \bm{B} / \bm{B} $
and comparing it with the decay rates $\gamma_l$
(they should be exactly equal).
The test is done for increasing grid resolutions.
"""
import galmag
import matplotlib.pyplot as plt
from galmag.util import curl_spherical
from galmag import halo_free_decay_modes as free
import numpy as np
from galmag.Grid import Grid

class TestHaloFreeDecayModes():
def test_s1(self):
self.__check_nmode(1, True)
def test_s2(self):
self.__check_nmode(2, True)
def test_s3(self):
self.__check_nmode(3, True)
def test_s4(self):
self.__check_nmode(4, True)

def test_a1(self):
self.__check_nmode(1, False)
def test_a2(self):
self.__check_nmode(2, False)
def test_a3(self):
self.__check_nmode(3, False)
def test_a4(self):
self.__check_nmode(4, False)

def __check_nmode(self, n_mode, symmetric):
Nop=21
No = 201

grid = Grid([[0.01,1.0], # r range
[0.000001,np.pi], # theta range
[17e-5,2.*np.pi]], # phi range
resolution=[No,No,Nop],
grid_type='spherical')

self.rr = grid.r_spherical
self.tt = grid.theta
self.pp = grid.phi

if symmetric:
symmetry = 's'
gamma = free.gamma_s[n_mode-1]
else:
symmetry = 'a'
gamma = free.gamma_a[n_mode-1]

print(r'B_{0}_{1}'.format(symmetry,n_mode), flush=True)
print('\t', 'comp\t max err')

B = free.get_mode(self.rr, self.tt, self.pp, n_mode, symmetric)

curl_B = curl_spherical(self.rr, self.tt, self.pp, B[0], B[1], B[2])
curl_curl_B = curl_spherical(self.rr, self.tt, self.pp,
curl_B[0], curl_B[1], curl_B[2])

for i, name in zip([0,1,2],['r ','theta','phi ']):
if np.all(B[i]==B[i]*0.0):
# If the magnetic field is 0, there is nothing to compare.
print('\t', name, '\t zero')
else:
# Removes the boundaries, as the double curl accumulates errors
# which are unlikely to be present in the actual GalMag use
valid_slice = (slice(4,-4),slice(4,-4),4)
curl_curl_Bi = curl_curl_B[i][valid_slice]
Bi = B[i][valid_slice]
max_error = (-curl_curl_Bi/gamma/Bi-1).max()
print('\t {0}\t {1:.2%}'.format(name, max_error))
assert np.allclose(curl_curl_Bi, -gamma*Bi, atol=5e-5,
rtol=1e-5)


if __name__ == "__main__":
test_free = TestHaloFreeDecayModes()
for test in dir(test_free):
if 'test' in test:
getattr(test_free, test)()

0 comments on commit 64690ff

Please sign in to comment.