Skip to content
Snippets Groups Projects
Commit 7f8fa34f authored by David Hendriks's avatar David Hendriks
Browse files

black format

parent 1e22474b
Branches develop
No related tags found
No related merge requests found
......@@ -213,10 +213,8 @@ def return_compilation_dict(verbose=0):
libs = defaults[
"libs"
] # = ($ENV{BINARY_GRID2_LIBS} // $defaults{libs}).' '.($ENV{BINARY_GRID2_EXTRALIBS}//'');
ccflags = defaults[
"ccflags"
] # = $ENV{BINARY_GRID2_CCFLAGS}
# // ($defaults{ccflags}) . ($ENV{BINARY_GRID2_EXTRACCFLAGS} // '');
ccflags = defaults["ccflags"] # = $ENV{BINARY_GRID2_CCFLAGS}
# // ($defaults{ccflags}) . ($ENV{BINARY_GRID2_EXTRACCFLAGS} // '');
# you must define _SEARCH_H to prevent it being loaded twice
ccflags += " -shared -D_SEARCH_H"
......
......@@ -9,16 +9,14 @@ generate probability distributions for sampling populations
import math
import numpy as np
from binarycpython.utils.useful_funcs import (
calc_period_from_sep,
)
from binarycpython.utils.useful_funcs import calc_period_from_sep
###
# File containing probability distributions
# Mostly copied from the perl modules
# TODO: make some things globally present? rob does this in his module.
# i guess it saves calculations but not sure if im gonna do that now
# TODO: make some things globally present? rob does this in his module.
# i guess it saves calculations but not sure if im gonna do that now
# TODO: Add the stuff from the IMF file
# TODO: call all of these functions to check whether they work
# TODO: make global constants stuff
......@@ -26,7 +24,9 @@ from binarycpython.utils.useful_funcs import (
LOG_LN_CONVERTER = 1.0 / math.log(10.0)
distribution_constants = {} # To store the constants in
distribution_constants = {} # To store the constants in
def prepare_dict(global_dict, list_of_sub_keys):
"""
Function that makes sure that the global dict is prepared to have a value set there
......@@ -44,7 +44,6 @@ def prepare_dict(global_dict, list_of_sub_keys):
internal_dict_value = internal_dict_value[k]
def flat():
"""
Dummy distribution function that returns 1
......@@ -467,7 +466,7 @@ def duquennoy1991(logper):
return gaussian(logper, 4.8, 2.3, -2, 12)
def sana12(M1, M2, a, P, amin, amax, x0, x1, p):
def sana12(M1, M2, a, P, amin, amax, x0, x1, p):
"""
distribution of initial orbital periods as found by Sana et al. (2012)
which is a flat distribution in ln(a) and ln(P) respectively for stars
......@@ -517,6 +516,7 @@ def sana12(M1, M2, a, P, amin, amax, x0, x1, p):
return res
def Izzard2012_period_distribution(P, M1, log10Pmin=1):
"""
period distribution which interpolates between
......@@ -544,42 +544,56 @@ def Izzard2012_period_distribution(P, M1, log10Pmin=1):
print("Izzard2012 called for M={} (trunc'd to {}), P={}\n".format(Mwas, M1, P))
# Calculate the normalisations
# need to normalize the distribution for this mass
# need to normalize the distribution for this mass
# (and perhaps secondary mass)
prepare_dict(distribution_constants, ['Izzard2012', M1])
if not distribution_constants['Izzard2012'][M1].get(log10Pmin):
distribution_constants['Izzard2012'][M1][log10Pmin] = 1 # To prevent this loop from going recursive
N = 200.0 # Resolution for normalisation. I hope 1000 is enough
dlP = (10.0 - log10Pmin)/N
C = 0 # normalisation const.
prepare_dict(distribution_constants, ["Izzard2012", M1])
if not distribution_constants["Izzard2012"][M1].get(log10Pmin):
distribution_constants["Izzard2012"][M1][
log10Pmin
] = 1 # To prevent this loop from going recursive
N = 200.0 # Resolution for normalisation. I hope 1000 is enough
dlP = (10.0 - log10Pmin) / N
C = 0 # normalisation const.
for lP in np.arange(log10Pmin, 10, dlP):
C += dlP * Izzard2012_period_distribution(10**lP, M1, log10Pmin)
C += dlP * Izzard2012_period_distribution(10 ** lP, M1, log10Pmin)
distribution_constants['Izzard2012'][M1][log10Pmin] = 1.0/C;
print("Normalization constant for Izzard2012 M={} (log10Pmin={}) is\
{}\n".format(M1, log10Pmin, distribution_constants['Izzard2012'][M1][log10Pmin]))
distribution_constants["Izzard2012"][M1][log10Pmin] = 1.0 / C
print(
"Normalization constant for Izzard2012 M={} (log10Pmin={}) is\
{}\n".format(
M1, log10Pmin, distribution_constants["Izzard2012"][M1][log10Pmin]
)
)
lP = math.log10(P); # log period
lP = math.log10(P)
# log period
# # fits
mu = interpolate_in_mass_izzard2012(M1, -17.8, 5.03)
sigma = interpolate_in_mass_izzard2012(M1, 9.18, 2.28)
K = interpolate_in_mass_izzard2012(M1, 6.93e-2, 0.0)
nu = interpolate_in_mass_izzard2012(M1, 0.3, -1)
g = 1.0/(1.0+1e-30**(lP-nu))
g = 1.0 / (1.0 + 1e-30 ** (lP - nu))
lPmu = lP - mu
print("M={} ({}) P={} : mu={} sigma={} K={} nu={} norm=%g\n".format(
Mwas, M1, P, mu, sigma, K, nu))
print(
"M={} ({}) P={} : mu={} sigma={} K={} nu={} norm=%g\n".format(
Mwas, M1, P, mu, sigma, K, nu
)
)
# print "FUNC $distdata{Izzard2012}{$M}{$log10Pmin} * (exp(- (x-$mu)**2/(2.0*$sigma*$sigma) ) + $K/MAX(0.1,$lP)) * $g;\n";
if ((lP < log10Pmin) or (lP > 10.0)):
if (lP < log10Pmin) or (lP > 10.0):
return 0
else:
return distribution_constants['Izzard2012'][M1][log10Pmin] * (math.exp(- lPmu * lPmu / (2.0 * sigma * sigma)) + K/max(0.1, lP)) * g;
return (
distribution_constants["Izzard2012"][M1][log10Pmin]
* (math.exp(-lPmu * lPmu / (2.0 * sigma * sigma)) + K / max(0.1, lP))
* g
)
def interpolate_in_mass_izzard2012(M, high, low):
"""
......@@ -592,9 +606,11 @@ def interpolate_in_mass_izzard2012(M, high, low):
log_interpolation = False
if log_interpolation:
return (high-low)/(math.log10(16.3)-math.log10(1.15)) * (math.log10(M)-math.log10(1.15)) + low
return (high - low) / (math.log10(16.3) - math.log10(1.15)) * (
math.log10(M) - math.log10(1.15)
) + low
else:
return (high-low)/(16.3-1.15) * (M-1.15) + low
return (high - low) / (16.3 - 1.15) * (M - 1.15) + low
# print(sana12(10, 2, 10, 100, 1, 1000, math.log(10), math.log(1000), 6))
......
......@@ -16,6 +16,7 @@ import argparse
import importlib.util
from pathos.helpers import mp as pathos_multiprocess
# from pathos.multiprocessing import ProcessingPool as Pool
from pathos.pools import _ProcessPool as Pool
......@@ -53,7 +54,7 @@ import binary_c_python_api
# that is stored into a python object. rather its just written to stdout
class Population():
class Population:
"""
Population Object. Contains all the necessary functions to set up, run and process a
population of systems
......@@ -68,10 +69,8 @@ class Population():
self.cleaned_up_defaults = self.cleanup_defaults()
# Different sections of options
self.bse_options = (
{}
) # bse_options is just empty.
# Setting stuff will check against the defaults to see if the input is correct.
self.bse_options = {} # bse_options is just empty.
# Setting stuff will check against the defaults to see if the input is correct.
self.grid_options = grid_options_defaults_dict.copy()
self.custom_options = {}
......@@ -136,7 +135,9 @@ class Population():
else:
print(
"!! Key doesnt match previously known parameter: \
adding: {}={} to custom_options".format(key, kwargs[key])
adding: {}={} to custom_options".format(
key, kwargs[key]
)
)
self.custom_options[key] = kwargs[key]
......@@ -206,17 +207,17 @@ class Population():
pass
def add_grid_variable(
self,
name,
longname,
valuerange,
resolution,
spacingfunc,
probdist,
dphasevol,
parameter_name,
precode=None,
condition=None,
self,
name,
longname,
valuerange,
resolution,
spacingfunc,
probdist,
dphasevol,
parameter_name,
precode=None,
condition=None,
):
"""spec
Function to add grid variables to the grid_options.
......@@ -316,11 +317,11 @@ class Population():
return self.defaults
def return_all_info(
self,
include_population_settings=True,
include_binary_c_defaults=True,
include_binary_c_version_info=True,
include_binary_c_help_all=True,
self,
include_population_settings=True,
include_binary_c_defaults=True,
include_binary_c_version_info=True,
include_binary_c_help_all=True,
):
"""
Function that returns all the information about the population and binary_c
......@@ -350,13 +351,13 @@ class Population():
return all_info
def export_all_info(
self,
use_datadir=True,
outfile=None,
include_population_settings=True,
include_binary_c_defaults=True,
include_binary_c_version_info=True,
include_binary_c_help_all=True,
self,
use_datadir=True,
outfile=None,
include_population_settings=True,
include_binary_c_defaults=True,
include_binary_c_version_info=True,
include_binary_c_help_all=True,
):
"""
Function that exports the all_info to a json file
......@@ -383,7 +384,7 @@ class Population():
# Clean the all_info_dict: (i.e. transform the function objects to strings)
if all_info_cleaned.get("population_settings", None):
if all_info_cleaned["population_settings"]["grid_options"][
"parse_function"
"parse_function"
]:
all_info_cleaned["population_settings"]["grid_options"][
"parse_function"
......@@ -680,7 +681,6 @@ class Population():
if self.grid_options["parse_function"]:
self.grid_options["parse_function"](self, out)
def evolve_population(self):
"""
Function to evolve populations. This is the main function. Handles the setting up, evolving
......@@ -696,8 +696,8 @@ class Population():
# Evolve systems: via grid_options one can choose to do this linearly, or
# multiprocessing method.
if (
self.grid_options["evolution_type"]
in self.grid_options["evolution_type_options"]
self.grid_options["evolution_type"]
in self.grid_options["evolution_type_options"]
):
if self.grid_options["evolution_type"] == "mp":
self.evolve_population_mp()
......@@ -837,8 +837,8 @@ class Population():
code_string += indent * depth + "# setting probability lists\n"
# Prepare the probability
for grid_variable_el in sorted(
self.grid_options["grid_variables"].items(),
key=lambda x: x[1]["grid_variable_number"],
self.grid_options["grid_variables"].items(),
key=lambda x: x[1]["grid_variable_number"],
):
# Make probabilities dict
grid_variable = grid_variable_el[1]
......@@ -853,10 +853,10 @@ class Population():
# Generate code
print("Generating grid code")
for loopnr, grid_variable_el in enumerate(
sorted(
self.grid_options["grid_variables"].items(),
key=lambda x: x[1]["grid_variable_number"],
)
sorted(
self.grid_options["grid_variables"].items(),
key=lambda x: x[1]["grid_variable_number"],
)
):
print("Constructing/adding: {}".format(grid_variable_el[0]))
grid_variable = grid_variable_el[1]
......@@ -1099,11 +1099,11 @@ class Population():
# this has to go in a reverse order:
# Here comes the stuff that is put after the deepest nested part that calls returns stuff.
for loopnr, grid_variable_el in enumerate(
sorted(
self.grid_options["grid_variables"].items(),
key=lambda x: x[1]["grid_variable_number"],
reverse=True,
)
sorted(
self.grid_options["grid_variables"].items(),
key=lambda x: x[1]["grid_variable_number"],
reverse=True,
)
):
grid_variable = grid_variable_el[1]
code_string += indent * (depth + 1) + "#" * 40 + "\n"
......@@ -1252,10 +1252,7 @@ class Population():
###################################################
def write_binary_c_calls_to_file(
self,
output_dir=None,
output_filename=None,
include_defaults=False
self, output_dir=None, output_filename=None, include_defaults=False
):
"""
Function that loops over the gridcode and writes the generated parameters to a file.
......@@ -1389,4 +1386,5 @@ class Population():
# Function to join the result dictionaries
# """
################################################################################################
......@@ -34,8 +34,8 @@ grid_options_defaults_dict = {
# Custom logging
##########################
"C_auto_logging": None, # Should contain a dictionary where the kes are they headers
# and the values are lists of parameters that should be logged.
# This will get parsed by autogen_C_logging_code in custom_loggion_functions.py
# and the values are lists of parameters that should be logged.
# This will get parsed by autogen_C_logging_code in custom_loggion_functions.py
"C_logging_code": None, # Should contain a string which holds the logging code.
"custom_logging_func_memaddr": -1, # Contains the custom_logging functions memory address
"custom_logging_shared_library_file": None,
......@@ -43,7 +43,7 @@ grid_options_defaults_dict = {
# Store pre-loading:
##########################
"store_memaddr": -1, # Contains the store object memory adress, useful for preloading.
# defaults to -1 and isnt used if thats the default then.
# defaults to -1 and isnt used if thats the default then.
##########################
# Log args: logging of arguments
##########################
......@@ -59,7 +59,7 @@ grid_options_defaults_dict = {
"linear",
], # available choices for type of population evolution
"system_generator": None, # value that holds the function that generates the system
# (result of building the grid script)
# (result of building the grid script)
"population_type": "grid", #
"population_type_options": [
"grid",
......@@ -144,7 +144,7 @@ grid_options_defaults_dict = {
# condor_postpone_join=>0, # if 1, data is not joined, e.g. if you
# # want to do it off the condor grid (e.g. with more RAM)
# condor_join_machine=>undef, # if defined then this is the machine on which the join command
#should be launched (must be sshable and not postponed)
# should be launched (must be sshable and not postponed)
# condor_join_pwd=>undef, # directory the join should be in
# # (defaults to $ENV{PWD} if undef)
# condor_memory=>1024, # in MB, the memory use (ImageSize) of the job
......@@ -153,10 +153,10 @@ grid_options_defaults_dict = {
# condor_load_from_snapshot=>0, # if 1 check for snapshot .sv file and load it if found
# condor_checkpoint_interval=>0, # checkpoint interval (seconds)
# condor_checkpoint_stamp_times=>0, # if 1 then files are given timestamped names
# (warning: lots of files!), otherwise just store the lates
# (warning: lots of files!), otherwise just store the lates
# condor_streams=>0, # stream stderr/stdout by default (warning: might cause heavy network load)
# condor_save_joined_file=>0, # if 1 then results/joined contains the results
# (useful for debugging, otherwise a lot of work)
# (useful for debugging, otherwise a lot of work)
# condor_requirements=>'', # used?
# # resubmit options : if the status of a condor script is
# # either 'finished','submitted','running' or 'crashed',
......
......@@ -21,9 +21,7 @@ import numpy as np
# import matplotlib.pyplot as plt
from binarycpython.utils.functions import (
output_lines,
)
from binarycpython.utils.functions import output_lines
from binarycpython.utils.run_system_wrapper import run_system
from binarycpython.utils.custom_logging_functions import binary_c_log_code
......@@ -67,6 +65,7 @@ def dummy():
"""Placeholder"""
pass
def parse_function_hr_diagram(output):
"""
Parsing function for the HR plotting routine
......
......@@ -40,10 +40,10 @@ author = "David Hendriks, Robert Izzard, Jeff Andrews"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.doctest",
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
"sphinx.ext.todo",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
"hawkmoth",
"m2r",
]
......
......@@ -8,14 +8,12 @@ from binarycpython.utils.distribution_functions import (
Kroupa2001,
Arenou2010_binary_fraction,
raghavan2010_binary_fraction,
imf_scalo1998,
imf_scalo1986,
imf_tinsley1980,
imf_scalo1998,
imf_chabrier2003,
flatsections,
duquennoy1991,
sana12,
Izzard2012_period_distribution,
......@@ -30,8 +28,9 @@ from binarycpython.utils.useful_funcs import calc_sep_from_period
# mass distribution plots
################################################
def plot_mass_distributions():
mass_values = np.arange(0.11, 80, .1)
mass_values = np.arange(0.11, 80, 0.1)
kroupa_probability = [Kroupa2001(mass) for mass in mass_values]
scalo1986 = [imf_scalo1986(mass) for mass in mass_values]
......@@ -39,64 +38,75 @@ def plot_mass_distributions():
scalo1998 = [imf_scalo1998(mass) for mass in mass_values]
chabrier2003 = [imf_chabrier2003(mass) for mass in mass_values]
plt.plot(mass_values, kroupa_probability, label='Kroupa')
plt.plot(mass_values, scalo1986, label='scalo1986')
plt.plot(mass_values, tinsley1980, label='tinsley1980')
plt.plot(mass_values, scalo1998, label='scalo1998')
plt.plot(mass_values, chabrier2003, label='chabrier2003')
plt.title('Probability distribution for mass of primary')
plt.ylabel(r'Probability')
plt.xlabel(r'Mass (M$_{\odot}$)')
plt.yscale('log')
plt.xscale('log')
plt.plot(mass_values, kroupa_probability, label="Kroupa")
plt.plot(mass_values, scalo1986, label="scalo1986")
plt.plot(mass_values, tinsley1980, label="tinsley1980")
plt.plot(mass_values, scalo1998, label="scalo1998")
plt.plot(mass_values, chabrier2003, label="chabrier2003")
plt.title("Probability distribution for mass of primary")
plt.ylabel(r"Probability")
plt.xlabel(r"Mass (M$_{\odot}$)")
plt.yscale("log")
plt.xscale("log")
plt.grid()
plt.legend()
plt.show()
################################################
# Binary fraction distributions
################################################
def plot_binary_fraction_distributions():
arenou_binary_distibution = [Arenou2010_binary_fraction(mass) for mass in mass_values]
raghavan2010_binary_distribution = [raghavan2010_binary_fraction(mass) for mass in mass_values ]
plt.plot(mass_values, arenou_binary_distibution, label='arenou 2010')
plt.plot(mass_values, raghavan2010_binary_distribution, label='Raghavan 2010')
plt.title('Binary fractions distributions')
plt.ylabel(r'Binary fraction')
plt.xlabel(r'Mass (M$_{\odot}$)')
arenou_binary_distibution = [
Arenou2010_binary_fraction(mass) for mass in mass_values
]
raghavan2010_binary_distribution = [
raghavan2010_binary_fraction(mass) for mass in mass_values
]
plt.plot(mass_values, arenou_binary_distibution, label="arenou 2010")
plt.plot(mass_values, raghavan2010_binary_distribution, label="Raghavan 2010")
plt.title("Binary fractions distributions")
plt.ylabel(r"Binary fraction")
plt.xlabel(r"Mass (M$_{\odot}$)")
# plt.yscale('log')
plt.xscale('log')
plt.xscale("log")
plt.grid()
plt.legend()
plt.show()
################################################
# Mass ratio distributions
################################################
def plot_mass_ratio_distributions():
mass_ratios = np.arange(0, 1, .01)
mass_ratios = np.arange(0, 1, 0.01)
example_mass = 2
flat_dist = [
flatsections(
q,
opts=[
{'min':0.1/example_mass, 'max':0.8, 'height':1},
{'min': 0.8, 'max':1.0, 'height': 1.0}
]
) for q in mass_ratios]
plt.plot(mass_ratios, flat_dist, label='Flat')
plt.title('Mass ratio distributions')
plt.ylabel(r'Probability')
plt.xlabel(r'Mass ratio (q = $\frac{M1}{M2}$) ')
{"min": 0.1 / example_mass, "max": 0.8, "height": 1},
{"min": 0.8, "max": 1.0, "height": 1.0},
],
)
for q in mass_ratios
]
plt.plot(mass_ratios, flat_dist, label="Flat")
plt.title("Mass ratio distributions")
plt.ylabel(r"Probability")
plt.xlabel(r"Mass ratio (q = $\frac{M1}{M2}$) ")
plt.grid()
plt.legend()
plt.show()
################################################
# Period distributions
################################################
......@@ -161,7 +171,6 @@ def plot_period_distributions():
for logper in logperiod_values
]
m1 = 30
m2 = 30
sana12_distribution_q1 = [
......@@ -179,17 +188,29 @@ def plot_period_distributions():
for logper in logperiod_values
]
Izzard2012_period_distribution_10 = [Izzard2012_period_distribution(10**logperiod, 10) for logperiod in logperiod_values]
Izzard2012_period_distribution_20 = [Izzard2012_period_distribution(10**logperiod, 20) for logperiod in logperiod_values]
Izzard2012_period_distribution_10 = [
Izzard2012_period_distribution(10 ** logperiod, 10)
for logperiod in logperiod_values
]
Izzard2012_period_distribution_20 = [
Izzard2012_period_distribution(10 ** logperiod, 20)
for logperiod in logperiod_values
]
plt.plot(logperiod_values, duquennoy1991_distribution, label="Duquennoy & Mayor 1991")
plt.plot(
logperiod_values, duquennoy1991_distribution, label="Duquennoy & Mayor 1991"
)
plt.plot(logperiod_values, sana12_distribution_q0033, label="Sana 12 (q=0.033)")
plt.plot(logperiod_values, sana12_distribution_q05, label="Sana 12 (q=0.5)")
plt.plot(logperiod_values, sana12_distribution_q01, label="Sana 12 (q=0.1)")
plt.plot(logperiod_values, sana12_distribution_q1, label="Sana 12 (q=1)")
plt.plot(logperiod_values, Izzard2012_period_distribution_10, label='Izzard2012 (M=10)')
plt.plot(logperiod_values, Izzard2012_period_distribution_20, label='Izzard2012 (M=20)')
plt.plot(
logperiod_values, Izzard2012_period_distribution_10, label="Izzard2012 (M=10)"
)
plt.plot(
logperiod_values, Izzard2012_period_distribution_20, label="Izzard2012 (M=20)"
)
plt.title("Period distributions")
plt.ylabel(r"Probability")
......@@ -198,6 +219,7 @@ def plot_period_distributions():
plt.legend()
plt.show()
plot_period_distributions()
################################################
# Sampling part of distribution and calculating probability ratio
......@@ -206,9 +228,6 @@ plot_period_distributions()
# TODO show the difference between sampling over the full range, or taking a smaller range initially and compensating for it.
# val = Izzard2012_period_distribution(1000, 10)
# print(val)
# val2 = Izzard2012_period_distribution(100, 10)
......
......@@ -28,12 +28,16 @@ CWD = os.getcwd()
BINARY_C_CONFIG = os.environ["BINARY_C"] + "/binary_c-config"
BINARY_C_INCDIRS = (
subprocess.run([BINARY_C_CONFIG, "incdirs_list"], stdout=subprocess.PIPE, check=True)
subprocess.run(
[BINARY_C_CONFIG, "incdirs_list"], stdout=subprocess.PIPE, check=True
)
.stdout.decode("utf-8")
.split()
)
BINARY_C_LIBDIRS = (
subprocess.run([BINARY_C_CONFIG, "libdirs_list"], stdout=subprocess.PIPE, check=True)
subprocess.run(
[BINARY_C_CONFIG, "libdirs_list"], stdout=subprocess.PIPE, check=True
)
.stdout.decode("utf-8")
.split()
)
......@@ -52,7 +56,9 @@ BINARY_C_LIBS = (
# create list of tuples of defined macros
BINARY_C_DEFINE_MACROS = []
DEFINES = (
subprocess.run([BINARY_C_CONFIG, "define_macros"], stdout=subprocess.PIPE, check=True)
subprocess.run(
[BINARY_C_CONFIG, "define_macros"], stdout=subprocess.PIPE, check=True
)
.stdout.decode("utf-8")
.split()
)
......@@ -123,7 +129,7 @@ BINARY_C_PYTHON_API_MODULE = Extension(
include_dirs=INCLUDE_DIRS,
libraries=LIBRARIES,
library_dirs=LIBRARY_DIRS,
runtime_library_dirs=RUNTIME_LIBRARY_DIRS,
runtime_library_dirs=RUNTIME_LIBRARY_DIRS,
define_macros=[] + BINARY_C_DEFINE_MACROS,
extra_objects=[],
extra_compile_args=[],
......
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