Skip to content
Snippets Groups Projects
Commit 0ff868ae authored by jmfisher's avatar jmfisher
Browse files

fixed python3 import problem, added custom FF to performance test

parent 4a08156c
No related branches found
No related tags found
No related merge requests found
This script replaces the performance test functionality of App.
Example usage:
python3 test_performance.py ../../dev-tools/log/perf_history.txt
This will perform the tests and append the summary to the file
"perf_history.txt". If the filename argument is left blank, the
script will simply write the results to stdout.
#!/usr/bin/python
# -*- coding: utf-8 -*-
## ************************************************************************** ##
##
## BornAgain: simulate and fit scattering at grazing incidence
##
##! @file customformfactor.py
##! @brief Test performance of customformfactor
##!
##! @homepage http:##www.bornagainproject.org
##! @license GNU General Public License v3 or higher (see COPYING)
##! @copyright Forschungszentrum Jülich GmbH 2016
##! @authors Scientific Computing Group at MLZ Garching
##! @authors C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
##
## ************************************************************************** ##
from __future__ import print_function
import sys
import os
import numpy
import math
import timeit
import resource
from libBornAgainCore import *
nrepetitions = 50
phi_min, phi_max = -1.0, 1.0
alpha_min, alpha_max = 0.0, 2.0
class CustomFormFactor(IFormFactorBorn):
"""
A custom defined form factor
The form factor is V sech(q L) with
V volume of particle
L length scale which defines mean radius
"""
def __init__(self, V, L):
IFormFactorBorn.__init__(self)
# parameters describing the form factor
self.V = V
self.L = L
def clone(self):
"""
IMPORTANT NOTE:
The clone method needs to call transferToCPP() on the cloned object
to transfer the ownership of the clone to the cpp code
"""
cloned_ff = CustomFormFactor(self.V, self.L)
cloned_ff.transferToCPP()
return cloned_ff
def evaluate_for_q(self, q):
return self.V*1.0/math.cosh(q.mag()*self.L)
def get_sample():
"""
Build and return the sample to calculate custom form factor in Distorted Wave Born Approximation.
"""
# defining materials
m_ambience = HomogeneousMaterial("Air", 0.0, 0.0)
m_substrate = HomogeneousMaterial("Substrate", 6e-6, 2e-8)
m_particle = HomogeneousMaterial("Particle", 6e-4, 2e-8)
# collection of particles
ff = CustomFormFactor(343.0*nanometer, 7.0*nanometer)
particle = Particle(m_particle, ff)
particle_layout = ParticleLayout()
particle_layout.addParticle(particle, 1.0)
air_layer = Layer(m_ambience)
air_layer.addLayout(particle_layout)
substrate_layer = Layer(m_substrate)
# assemble multilayer
multi_layer = MultiLayer()
multi_layer.addLayer(air_layer)
multi_layer.addLayer(substrate_layer)
return multi_layer
def get_simulation():
"""
Create and return GISAXS simulation with beam and detector defined
IMPORTANT NOTE:
Multithreading should be deactivated by putting ThreadInfo.n_threads to -1
"""
simulation = GISASSimulation()
thread_info = ThreadInfo()
thread_info.n_threads = -1
simulation.setThreadInfo(thread_info)
simulation.setDetectorParameters(100, phi_min*degree, phi_max*degree, 100, alpha_min*degree, alpha_max*degree)
simulation.setBeamParameters(1.0*angstrom, 0.2*degree, 0.0*degree)
return simulation
def run_test():
"""
Run simulation and plot results
"""
sample = get_sample()
simulation = get_simulation()
start_time_real = timeit.default_timer()
start_time_user = resource.getrusage(resource.RUSAGE_SELF)[0]
for i in range(nrepetitions):
simulation.setSample(sample)
simulation.runSimulation()
end_time_real = timeit.default_timer()
end_time_user = resource.getrusage(resource.RUSAGE_SELF)[0]
real_time = (end_time_real - start_time_real)
user_time = (end_time_real - start_time_real)
print("\ntime after %d repetitions:" % nrepetitions)
print("real time: %.4f" % real_time)
print("user time: %.4f\n" % user_time)
if __name__ == '__main__':
run_test()
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
## ************************************************************************** ## ## ************************************************************************** ##
## ##
## BornAgain: simulate and fit scattering at grazing incidence ## BornAgain: simulate and fit scattering at grazing incidence
...@@ -23,26 +22,180 @@ import sys ...@@ -23,26 +22,180 @@ import sys
import platform import platform
import timeit import timeit
from collections import OrderedDict from collections import OrderedDict
import resource import math
from libBornAgainCore import * from libBornAgainCore import *
# used for logging intermediate output
logfile = sys.stdout logfile = sys.stdout
class PerfTest: # for code readability
get_wall_time = timeit.default_timer
# for code readability and portability, since resource is not guaranteed to be available
try:
import resource
record_cpu_time = True
get_cpu_time = lambda: resource.getrusage(resource.RUSAGE_SELF)[0]
except:
record_cpu_time = False
get_cpu_time = lambda: None
# globals used in custom form factor
phi_min, phi_max = -1.0, 1.0
alpha_min, alpha_max = 0.0, 2.0
# user-defined custom form factor
class CustomFormFactor(IFormFactorBorn):
"""
A custom defined form factor
The form factor is V sech(q L) with
V volume of particle
L length scale which defines mean radius
"""
def __init__(self, V, L):
IFormFactorBorn.__init__(self)
# parameters describing the form factor
self.V = V
self.L = L
def clone(self):
"""
IMPORTANT NOTE:
The clone method needs to call transferToCPP() on the cloned object
to transfer the ownership of the clone to the cpp code
"""
cloned_ff = CustomFormFactor(self.V, self.L)
cloned_ff.transferToCPP()
return cloned_ff
def evaluate_for_q(self, q):
return self.V*1.0/math.cosh(q.mag()*self.L)
# class for performance test, constructed using sample factories
class FactoryTest:
def __init__(self, name, simulation_name, sample_builder, nrepetitions): def __init__(self, name, simulation_name, sample_builder, nrepetitions):
self.m_test_name = name self.m_test_name = name
self.m_simulation_name = simulation_name self.m_simulation_name = simulation_name
self.m_sample_builder_name = sample_builder self.m_sample_builder_name = sample_builder
self.m_nrepetitions = nrepetitions self.m_nrepetitions = nrepetitions
self.m_cpu_time = 0.0 self.m_cpu_time = 0.0
self.m_real_time = 0.0 self.m_wall_time = 0.0
self.m_sample = None
if simulation_name != None and sample_builder != None:
self.m_sample_factory = SampleBuilderFactory()
self.m_simulation_registry = SimulationRegistry()
self.m_simulation = self.m_simulation_registry.createSimulation(self.m_simulation_name)
self.m_sample_builder = self.m_sample_factory.createBuilder(self.m_sample_builder_name)
else:
self.m_sample_factory = None
self.m_simulation_registry = None
self.m_simulation = None
self.m_sample_builder = None
def prepare(self):
pass
# do the actual work
def run_loop(self):
# actual work is done here
for i in range(self.m_nrepetitions):
self.m_simulation.setSampleBuilder(self.m_sample_builder)
self.m_simulation.runSimulation()
def run(self):
self.prepare()
logfile.write("Running test: %-30s " % self.m_test_name)
logfile.flush()
# resource.getrusage is less accurate but returns user (cpu) time
start_wall_time = get_wall_time()
start_cpu_time = get_cpu_time()
self.run_loop()
class TestPerformance: end_wall_time = get_wall_time()
end_cpu_time = get_cpu_time()
# note that on multi-core system, cpu time can be higher than wall time
self.m_wall_time = end_wall_time - start_wall_time
if record_cpu_time:
self.m_cpu_time = end_cpu_time - start_cpu_time
logfile.write("OK: %-6.3f (wall sec), %-6.3f (cpu sec) \n" % (self.m_wall_time, self.m_cpu_time))
else:
logfile.write("OK: %-6.3f (wall sec)\n" % (self.m_wall_time))
# special performance test case: custom form factor
class CustomTest(FactoryTest):
def __init__(self, name, nrepetitions):
FactoryTest.__init__(self, name, None, None, nrepetitions)
self.m_sample = None
self.m_simulation = None
def prepare(self):
self.m_sample = self.get_sample()
self.m_simulation = self.get_simulation()
# do the actual work
def run_loop(self):
for i in range(self.m_nrepetitions):
self.m_simulation.setSample(self.m_sample)
self.m_simulation.runSimulation()
def get_sample(self):
"""
Build and return the sample to calculate custom form factor in Distorted Wave Born Approximation.
"""
# defining materials
m_ambience = HomogeneousMaterial("Air", 0.0, 0.0)
m_substrate = HomogeneousMaterial("Substrate", 6e-6, 2e-8)
m_particle = HomogeneousMaterial("Particle", 6e-4, 2e-8)
# collection of particles
ff = CustomFormFactor(343.0*nanometer, 7.0*nanometer)
particle = Particle(m_particle, ff)
particle_layout = ParticleLayout()
particle_layout.addParticle(particle, 1.0)
air_layer = Layer(m_ambience)
air_layer.addLayout(particle_layout)
substrate_layer = Layer(m_substrate)
# assemble multilayer
multi_layer = MultiLayer()
multi_layer.addLayer(air_layer)
multi_layer.addLayer(substrate_layer)
return multi_layer
def get_simulation(self):
"""
Create and return GISAXS simulation with beam and detector defined
IMPORTANT NOTE:
Multithreading should be deactivated by putting ThreadInfo.n_threads to -1
"""
simulation = GISASSimulation()
thread_info = ThreadInfo()
thread_info.n_threads = -1
simulation.setThreadInfo(thread_info)
simulation.setDetectorParameters(100, phi_min*degree, phi_max*degree, 100, alpha_min*degree, alpha_max*degree)
simulation.setBeamParameters(1.0*angstrom, 0.2*degree, 0.0*degree)
return simulation
# class for running all of the tests and collecting results
class PerformanceTests:
def __init__(self, filename=None): def __init__(self, filename=None):
self.m_tests = [] self.m_tests = []
self.m_datime = "" self.m_datime = ""
self.m_hostname = "" self.m_hostname = ""
self.m_sysinfo = "" self.m_sysinfo = ""
self.m_pyversion = ""
self.m_filename = filename self.m_filename = filename
self.add("MultiLayer", "MaxiGISAS", "MultiLayerWithRoughnessBuilder", 1); self.add("MultiLayer", "MaxiGISAS", "MultiLayerWithRoughnessBuilder", 1);
...@@ -55,104 +208,76 @@ class TestPerformance: ...@@ -55,104 +208,76 @@ class TestPerformance:
self.add("SSCA", "MaxiGISAS", "SizeDistributionSSCAModelBuilder", 10); self.add("SSCA", "MaxiGISAS", "SizeDistributionSSCAModelBuilder", 10);
self.add("Mesocrystal", "MaxiGISAS", "MesoCrystalBuilder", 2); self.add("Mesocrystal", "MaxiGISAS", "MesoCrystalBuilder", 2);
self.add("PolMagCyl", "MaxiGISAS00", "MagneticCylindersBuilder", 10); self.add("PolMagCyl", "MaxiGISAS00", "MagneticCylindersBuilder", 10);
# custom form factor is a special case since it's not in the registry
self.m_tests.append(CustomTest("Custom FF", 10))
logfile.write("\nPreparing to run %d performance tests.\n\n" % len(self.m_tests)) logfile.write("\nPreparing to run %d performance tests.\n\n" % len(self.m_tests))
def add(self, name, simulation_name, sample_builder, nrepetitions): def add(self, name, simulation_name, sample_builder, nrepetitions):
self.m_tests.append(PerfTest(name, simulation_name, sample_builder, nrepetitions)) self.m_tests.append(FactoryTest(name, simulation_name, sample_builder, nrepetitions))
# execute all performance tests
def execute(self): def execute(self):
self.init_sysinfo() self.init_sysinfo()
for test in self.m_tests: test.run()
for test in self.m_tests:
self.runTest(test)
self.write_results() self.write_results()
def runTest(self, test):
simulationRegistry = SimulationRegistry()
sampleFactory = SampleBuilderFactory()
# std::cout << "Running test: " << std::setw(20) << std::left << test->m_test_name << " ... ";
logfile.write("Running test: %-30s " % test.m_test_name)
logfile.flush()
# std::cout.flush();
# boost::scoped_ptr<Simulation> simulation(simulationRegistry.createSimulation(test->m_simulation_name));
simulation = simulationRegistry.createSimulation(test.m_simulation_name)
# SampleBuilder_t sample = sampleFactory.createBuilder(test->m_sample_builder_name);
sample = sampleFactory.createBuilder(test.m_sample_builder_name)
# timeit.default_timer picks the most accurate timer available
start_real_time = timeit.default_timer()
# resource.getrusage is less accurate but returns user time
start_cpu_time = resource.getrusage(resource.RUSAGE_SELF)[0]
for i in range(test.m_nrepetitions):
simulation.setSampleBuilder(sample)
simulation.runSimulation()
end_real_time = timeit.default_timer()
end_cpu_time = resource.getrusage(resource.RUSAGE_SELF)[0]
# note that on multi-core system, cpu time can be higher than wall time
test.m_real_time = end_real_time - start_real_time
test.m_cpu_time = end_cpu_time - start_cpu_time
logfile.write("OK: %-6.3f (real sec), %-6.3f (cpu sec) \n" % (test.m_real_time, test.m_cpu_time))
# write out system information and test results to file
def write_results(self): def write_results(self):
if ( self.m_filename != None ): if ( self.m_filename != None ):
try: try:
write_file = open(self.m_filename, "a") write_file = open(self.m_filename, "a")
except: except:
sys.stderr.write("Could not open filed %s for writing.\n" % self.m_filename) sys.stderr.write("Could not open filed '%' for writing. Writing to stdout instead.\n" % self.m_filename)
exit(1) write_file = sys.stdout
self.m_filename = None
else: else:
write_file = sys.stdout write_file = sys.stdout
logfile.write("\nWriting output to %s...\n\n" % write_file.name) logfile.write("\nWriting output to %s...\n\n" % write_file.name)
# record results into an ordered dict, which is used by pretty_write() below
dictionary = OrderedDict() dictionary = OrderedDict()
dictionary["date"] = self.m_datime dictionary["date"] = self.m_datime
dictionary["hostname"] = self.m_hostname dictionary["hostname"] = self.m_hostname
dictionary["sysinfo"] = self.m_sysinfo dictionary["sysinfo"] = self.m_sysinfo
dictionary["python"] = self.m_pyversion
sum_real = 0.0 sum_wall = 0.0
sum_cpu = 0.0 sum_cpu = 0.0
for test in self.m_tests: for test in self.m_tests:
sum_real += test.m_real_time; sum_wall += test.m_wall_time;
sum_cpu += test.m_cpu_time; sum_cpu += test.m_cpu_time;
dictionary["total cpu"] = "%-.4f" % sum_cpu if record_cpu_time: dictionary["total cpu"] = "%-.4f" % sum_cpu
dictionary["total real"] = "%-.4f" % sum_real dictionary["total wall"] = "%-.4f" % sum_wall
for test in self.m_tests: for test in self.m_tests:
dictionary[test.m_test_name] = "%-.4f" % test.m_real_time dictionary[test.m_test_name] = "%-.4f" % test.m_wall_time
write_file.write("\n");
pretty_write(dictionary, write_file) pretty_write(dictionary, write_file)
write_file.write("\n"); write_file.write("\n");
write_file.flush() write_file.flush()
if ( self.m_filename != None ): if ( self.m_filename != None ):
write_file.close() write_file.close()
# determine platform, architecture, python version, etc.
def init_sysinfo(self): def init_sysinfo(self):
system, node, release, version, machine, processor = platform.uname() system, node, release, version, machine, processor = platform.uname()
if system == 'Linux':
system, version, id = platform.linux_distribution()
self.m_sysinfo = system + " " + version + " " + machine
self.m_datime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S') self.m_datime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S')
self.m_hostname = node self.m_hostname = node
self.m_sysinfo = "%s %s" % (system, machine)
self.m_pyversion = "%d.%d" % (sys.version_info[:2])
# used for writing the summary with proper formatting
def pretty_write(dictionary, file): def pretty_write(dictionary, file):
header = "|" header = "|"
footer = "|" footer = "|"
...@@ -178,5 +303,5 @@ if __name__ == '__main__': ...@@ -178,5 +303,5 @@ if __name__ == '__main__':
else: else:
filename = None filename = None
testPerformance = TestPerformance(filename) tests = PerformanceTests(filename)
testPerformance.execute() tests.execute()
...@@ -6,5 +6,9 @@ sys.path.append(os.path.abspath( ...@@ -6,5 +6,9 @@ sys.path.append(os.path.abspath(
from libBornAgainCore import * from libBornAgainCore import *
from libBornAgainFit import * from libBornAgainFit import *
# this line needed to fix python3 import problem
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
from plot_utils import * from plot_utils import *
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment