Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions examples/single-fit_basic-usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@

# Unit cell parameters
project.sample_models['lbco'].cell.length_a = 3.88
#project.sample_models['lbco'].cell.length_b = 3.8909 # Symmetry constraints are temporarily disabled
#project.sample_models['lbco'].cell.length_c = 3.8909 # Symmetry constraints are temporarily disabled

# Atom sites
project.sample_models['lbco'].atom_sites.add(label='La',
Expand Down Expand Up @@ -277,17 +275,16 @@

# Set aliases for parameters
project.analysis.aliases.add(
alias='biso_La',
param=project.sample_models['lbco'].atom_sites['La'].b_iso
label='biso_La',
param_uid=project.sample_models['lbco'].atom_sites['La'].b_iso.uid
)
project.analysis.aliases.add(
alias='biso_Ba',
param=project.sample_models['lbco'].atom_sites['Ba'].b_iso
label='biso_Ba',
param_uid=project.sample_models['lbco'].atom_sites['Ba'].b_iso.uid
)

# Set constraints
project.analysis.constraints.add(
id="1",
lhs_alias='biso_Ba',
rhs_expr='biso_La'
)
Expand All @@ -310,18 +307,17 @@

# Set more aliases for parameters
project.analysis.aliases.add(
alias='occ_La',
param=project.sample_models['lbco'].atom_sites['La'].occupancy
label='occ_La',
param_uid=project.sample_models['lbco'].atom_sites['La'].occupancy.uid
)
project.analysis.aliases.add(
alias='occ_Ba',
param=project.sample_models['lbco'].atom_sites['Ba'].occupancy
label='occ_Ba',
param_uid=project.sample_models['lbco'].atom_sites['Ba'].occupancy.uid
)

# Set more constraints
project.analysis.show_constraints()
project.analysis.constraints.add(
id="2",
lhs_alias='occ_Ba',
rhs_expr='1 - occ_La'
)
Expand Down
10 changes: 4 additions & 6 deletions src/easydiffraction/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
ProjectInfo
)

# Sample model management
from easydiffraction.sample_models.sample_models import (
SampleModel,
SampleModels
)
# Sample model
from easydiffraction.sample_models.sample_model import SampleModel
from easydiffraction.sample_models.sample_models import SampleModels

# Experiment creation and collection management
# Experiments
from easydiffraction.experiments.experiment import Experiment
from easydiffraction.experiments.experiments import Experiments

Expand Down
69 changes: 31 additions & 38 deletions src/easydiffraction/analysis/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,28 @@
ChartPlotter,
DEFAULT_HEIGHT
)
from easydiffraction.experiments.experiments import Experiments
from easydiffraction.core.objects import (
Descriptor,
Parameter
)
from easydiffraction.core.singletons import (
ConstraintsHandler,
UidMapHandler
)
from easydiffraction.core.singletons import ConstraintsHandler
from easydiffraction.experiments.experiments import Experiments

from .collections.aliases import ConstraintAliases
from .collections.constraints import ConstraintExpressions
from .collections.aliases import Aliases
from .collections.constraints import Constraints
from .collections.joint_fit_experiments import JointFitExperiments
from .calculators.calculator_factory import CalculatorFactory
from .minimization import DiffractionMinimizer
from .minimizers.minimizer_factory import MinimizerFactory
from easydiffraction.analysis.collections.joint_fit_experiments import JointFitExperiments


class Analysis:
_calculator = CalculatorFactory.create_calculator('cryspy')

def __init__(self, project: Project) -> None:
self.project = project
self.aliases = ConstraintAliases()
self.constraints = ConstraintExpressions()
self.aliases = Aliases()
self.constraints = Constraints()
self.constraints_handler = ConstraintsHandler.get()
self.calculator = Analysis._calculator # Default calculator shared by project
self._calculator_key: str = 'cryspy' # Added to track the current calculator
Expand Down Expand Up @@ -196,14 +193,16 @@ def how_to_access_parameters(self, show_description: bool = False) -> None:
if entry_id:
variable += f"['{entry_id}']"
variable += f".{param_key}"
rows.append({'variable': variable,
'description': description})
uid = param._generate_human_readable_unique_id()
rows.append({'Code variable': variable,
'Unique ID for CIF': uid,
'Description': description})

dataframe = pd.DataFrame(rows)

column_headers = ['variable']
column_headers = ['Code variable', 'Unique ID for CIF']
if show_description:
column_headers = ['variable', 'description']
column_headers.append('description')
dataframe = dataframe[column_headers]

indices = range(1, len(dataframe) + 1) # Force starting from 1
Expand Down Expand Up @@ -315,47 +314,31 @@ def show_constraints(self) -> None:
return

rows = []
for id, constraint in constraints_dict.items():
for constraint in constraints_dict.values():
row = {
'id': id,
'lhs_alias': constraint.lhs_alias.value,
'rhs_expr': constraint.rhs_expr.value,
'full expression': f'{constraint.lhs_alias.value} = {constraint.rhs_expr.value}'
}
rows.append(row)

dataframe = pd.DataFrame(rows)
indices = range(1, len(dataframe) + 1) # Force starting from 1

print(paragraph(f"User defined constraints"))
print(tabulate(dataframe,
headers=dataframe.columns,
tablefmt="fancy_outline",
showindex=False))

def _update_uid_map(self) -> None:
"""
Update the UID map for accessing parameters by UID.
This is needed for adding or removing constraints.
"""
sample_models_params = self.project.sample_models.get_all_params()
experiments_params = self.project.experiments.get_all_params()
params = sample_models_params + experiments_params

UidMapHandler.get().set_uid_map(params)
showindex=indices))

def apply_constraints(self) -> None:
def apply_constraints(self):
if not self.constraints._items:
print(warning(f"No constraints defined."))
return

sample_models_params = self.project.sample_models.get_fittable_params()
experiments_params = self.project.experiments.get_fittable_params()
fittable_params = sample_models_params + experiments_params

self._update_uid_map()
self.constraints_handler.set_aliases(self.aliases)
self.constraints_handler.set_expressions(self.constraints)
self.constraints_handler.apply(parameters=fittable_params)
self.constraints_handler.set_constraints(self.constraints)
self.constraints_handler.apply()

def show_calc_chart(self, expt_name: str, x_min: Optional[float] = None, x_max: Optional[float] = None) -> None:
self.calculate_pattern(expt_name)
Expand Down Expand Up @@ -442,12 +425,22 @@ def fit(self) -> None:
# After fitting, get the results
self.fit_results = self.fitter.results

def as_cif(self) -> str:
def as_cif(self):
current_minimizer = self.current_minimizer
if " " in current_minimizer:
current_minimizer = f'"{current_minimizer}"'

lines = []
lines.append(f"_analysis.calculator_engine {self.current_calculator}")
lines.append(f"_analysis.fitting_engine {self.current_minimizer}")
lines.append(f"_analysis.fitting_engine {current_minimizer}")
lines.append(f"_analysis.fit_mode {self.fit_mode}")

lines.append("")
lines.append(self.aliases.as_cif())

lines.append("")
lines.append(self.constraints.as_cif())

return "\n".join(lines)

def show_as_cif(self) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def calculate_pattern(self,

# Apply user constraints to all sample models
constraints = ConstraintsHandler.get()
constraints.apply(parameters=sample_models.get_all_params())
constraints.apply()

# Calculate contributions from valid linked sample models
y_calc_scaled = y_calc_zeros
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,11 @@ def _recreate_cryspy_obj(self, sample_model: SampleModels, experiment: Experimen
cryspy_sample_model_obj = str_to_globaln(cryspy_sample_model_cif)
cryspy_obj.add_items(cryspy_sample_model_obj.items)

cryspy_experiment_cif = self._convert_experiment_to_cryspy_cif(experiment, linked_phase=sample_model)
# Add single experiment to cryspy_obj
cryspy_experiment_cif = self._convert_experiment_to_cryspy_cif(
experiment,
linked_phase=sample_model)

cryspy_experiment_obj = str_to_globaln(cryspy_experiment_cif)
cryspy_obj.add_items(cryspy_experiment_obj.items)

Expand Down
56 changes: 34 additions & 22 deletions src/easydiffraction/analysis/collections/aliases.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
from typing import Type

from easydiffraction.core.objects import (
Descriptor,
Parameter,
Component,
Collection
)


class ConstraintAlias(Component):
def __init__(self, alias: str, param: Parameter) -> None:
super().__init__()

self.alias: Descriptor = Descriptor(
value=alias,
name="alias",
cif_name="alias"
)
self.param: Parameter = param
class Alias(Component):
@property
def category_key(self) -> str:
return "alias"

@property
def cif_category_key(self) -> str:
return "constraint_alias"
return "alias"

@property
def category_key(self) -> str:
return "constraint_alias"
def __init__(self,
label: str,
param_uid: str) -> None:
super().__init__()

@property
def _entry_id(self) -> str:
return self.alias.value
self.label: Descriptor = Descriptor(
value=label,
name="label",
cif_name="label"
)
self.param_uid: Descriptor = Descriptor(
value=param_uid,
name="param_uid",
cif_name="param_uid"
)

# Select which of the input parameters is used for the
# as ID for the whole object
self._entry_id = label

class ConstraintAliases(Collection):
# Lock further attribute additions to prevent
# accidental modifications by users
self._locked = True


class Aliases(Collection):
@property
def _type(self) -> str:
return "category" # datablock or category

def add(self, alias: str, param: Parameter) -> None:
alias_obj = ConstraintAlias(alias, param)
self._items[alias_obj.alias.value] = alias_obj
@property
def _child_class(self) -> Type[Alias]:
return Alias

45 changes: 21 additions & 24 deletions src/easydiffraction/analysis/collections/constraints.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
from typing import Type

from easydiffraction.core.objects import (
Descriptor,
Component,
Collection
)


class ConstraintExpression(Component):
class Constraint(Component):
@property
def category_key(self) -> str:
return "constraint"

@property
def cif_category_key(self) -> str:
return "constraint"

def __init__(self,
id: str,
lhs_alias: str,
rhs_expr: str) -> None:
super().__init__()

self.id: Descriptor = Descriptor(
value=id,
name="id",
cif_name="id"
)
self.lhs_alias: Descriptor = Descriptor(
value=lhs_alias,
name="lhs_alias",
Expand All @@ -28,27 +32,20 @@ def __init__(self,
cif_name="rhs_expr"
)

@property
def cif_category_key(self) -> str:
return "constraint_expression"
# Select which of the input parameters is used for the
# as ID for the whole object
self._entry_id = lhs_alias

@property
def category_key(self) -> str:
return "constraint_expression"

@property
def _entry_id(self) -> str:
return self.id.value
# Lock further attribute additions to prevent
# accidental modifications by users
self._locked = True


class ConstraintExpressions(Collection):
class Constraints(Collection):
@property
def _type(self) -> str:
return "category" # datablock or category

def add(self,
id: str,
lhs_alias: str,
rhs_expr: str) -> None:
expression_obj = ConstraintExpression(id, lhs_alias, rhs_expr)
self._items[expression_obj.id.value] = expression_obj
@property
def _child_class(self) -> Type[Constraint]:
return Constraint
Loading