From a13fe76ab4acdc126a0df25ad8fef511a227c9b2 Mon Sep 17 00:00:00 2001 From: David Beyer Date: Wed, 12 Jun 2024 13:08:35 +0200 Subject: [PATCH] Continue changing charge to charge number --- pyMBE.py | 132 +++++++++++------------ testsuite/globular_protein_unit_tests.py | 14 ++- testsuite/set_particle_acidity_test.py | 14 +-- testsuite/setup_salt_ions_unit_tests.py | 6 +- 4 files changed, 82 insertions(+), 84 deletions(-) diff --git a/pyMBE.py b/pyMBE.py index 6147dd2..8f6467a 100644 --- a/pyMBE.py +++ b/pyMBE.py @@ -268,7 +268,7 @@ def calculate_HH(self, molecule_name, pH_list=None, pka_set=None): if pka_set is None: pka_set=self.get_pka_set() self.check_pka_set(pka_set=pka_set) - charge_map = self.get_charge_map() + charge_number_map = self.get_charge_number_map() Z_HH=[] for pH_value in pH_list: Z=0 @@ -305,7 +305,7 @@ def calculate_HH(self, molecule_name, pH_list=None, pka_set=None): Z+=psi/(1+10**(psi*(pH_value-pka_set[name]['pka_value']))) else: state_one_type = self.df.loc[self.df['name']==name].state_one.es_type.values[0] - Z+=charge_map[state_one_type] + Z+=charge_number_map[state_one_type] Z_HH.append(Z) return Z_HH @@ -452,18 +452,18 @@ def calculate_net_charge (self, espresso_system, molecule_name): raise ValueError("The pyMBE object with name {molecule_name} has a pmb_type {pmb_type}. This function only supports pyMBE types {valid_pmb_types}") id_map = self.get_particle_id_map(object_name=molecule_name) - def create_charge_map(espresso_system,id_map,label): - charge_map={} + def create_charge_number_map(espresso_system,id_map,label): + charge_number_map={} for super_id in id_map[label].keys(): net_charge=0 for pid in id_map[label][super_id]: net_charge+=espresso_system.part.by_id(pid).q - charge_map[super_id]=net_charge - return charge_map - net_charge_molecules=create_charge_map(label="molecule_map", + charge_number_map[super_id]=net_charge + return charge_number_map + net_charge_molecules=create_charge_number_map(label="molecule_map", espresso_system=espresso_system, id_map=id_map) - net_charge_residues=create_charge_map(label="residue_map", + net_charge_residues=create_charge_number_map(label="residue_map", espresso_system=espresso_system, id_map=id_map) mean_charge=np.mean(np.array(list(net_charge_molecules.values()))) @@ -591,7 +591,7 @@ def check_if_metal_ion(self,key): Returns: (`bool`): True if `key` is a supported metal ion, False otherwise. """ - if key in self.get_metal_ions_charge_map().keys(): + if key in self.get_metal_ions_charge_number_map().keys(): return True else: return False @@ -631,7 +631,7 @@ def convert_columns_to_original_format (self, df): """ - columns_dtype_int = ['particle_id','particle_id2', 'residue_id','molecule_id', 'model',('state_one','es_type'),('state_two','es_type'),('state_one','charge'),('state_two','charge') ] + columns_dtype_int = ['particle_id','particle_id2', 'residue_id','molecule_id', 'model',('state_one','es_type'),('state_two','es_type'),('state_one','z'),('state_two','z') ] columns_with_units = ['sigma', 'epsilon', 'cutoff', 'offset'] @@ -729,8 +729,8 @@ def create_added_salt (self, espresso_system, cation_name, anion_name, c_salt, v Returns: c_salt_calculated(`float`): Calculated salt concentration added to `espresso_system`. """ - cation_name_charge = self.df.loc[self.df['name']==cation_name].state_one.charge.values[0] - anion_name_charge = self.df.loc[self.df['name']==anion_name].state_one.charge.values[0] + cation_name_charge = self.df.loc[self.df['name']==cation_name].state_one.z.values[0] + anion_name_charge = self.df.loc[self.df['name']==anion_name].state_one.z.values[0] if cation_name_charge <= 0: raise ValueError('ERROR cation charge must be positive, charge ',cation_name_charge) if anion_name_charge >= 0: @@ -831,8 +831,8 @@ def create_counterions(self, object_name, cation_name, anion_name, espresso_syst Returns: counterion_number(`dict`): {"name": number} """ - cation_charge = self.df.loc[self.df['name']==cation_name].state_one.charge.iloc[0] - anion_charge = self.df.loc[self.df['name']==anion_name].state_one.charge.iloc[0] + cation_charge = self.df.loc[self.df['name']==cation_name].state_one.z.iloc[0] + anion_charge = self.df.loc[self.df['name']==anion_name].state_one.z.iloc[0] object_ids = self.get_particle_id_map(object_name=object_name)["all"] counterion_number={} object_charge={} @@ -1004,7 +1004,7 @@ def create_particle(self, name, espresso_system, number_of_particles, position=N column_name='particle_id', number_of_copies=number_of_particles) # Get information from the particle type `name` from the df - z = self.df.loc[self.df['name']==name].state_one.charge.values[0] + z = self.df.loc[self.df['name']==name].state_one.z.values[0] es_type = self.df.loc[self.df['name']==name].state_one.es_type.values[0] # Get a list of the index in `df` corresponding to the new particles to be created index = np.where(self.df['name']==name) @@ -1537,7 +1537,7 @@ def define_particle(self, name, z=0, acidity='inert', pka=None, sigma=None, epsi # Define particle acid/base properties self.set_particle_acidity(name=name, acidity=acidity, - default_charge=z, + default_charge_number=z, pka=pka, verbose=verbose, overwrite=overwrite) @@ -1613,7 +1613,7 @@ def define_protein(self, name,model, topology_dict, lj_setup_mode="wca", overwri epsilon = 1*self.units.Quantity("reduced_energy") part_dict={} sequence=[] - metal_ions_charge_map=self.get_metal_ions_charge_map() + metal_ions_charge_number_map=self.get_metal_ions_charge_number_map() for particle in topology_dict.keys(): particle_name = re.split(r'\d+', particle)[0] if particle_name not in part_dict.keys(): @@ -1623,7 +1623,7 @@ def define_protein(self, name,model, topology_dict, lj_setup_mode="wca", overwri "epsilon": epsilon, "name": particle_name} if self.check_if_metal_ion(key=particle_name): - z=metal_ions_charge_map[particle_name] + z=metal_ions_charge_number_map[particle_name] else: z=0 part_dict[particle_name]["z"]=z @@ -1993,12 +1993,12 @@ def get_bond_length(self, particle_name1, particle_name2, hard_check=False, use_ else: return - def get_charge_map(self): + def get_charge_number_map(self): ''' - Gets the charge of each `espresso_type` in `pymbe.df`. + Gets the charge number of each `espresso_type` in `pymbe.df`. Returns: - charge_map(`dict`): {espresso_type: charge}. + charge_number_map(`dict`): {espresso_type: z}. ''' if self.df.state_one['es_type'].isnull().values.any(): df_state_one = self.df.state_one.dropna() @@ -2009,10 +2009,10 @@ def get_charge_map(self): df_state_two = self.df.state_two.dropna() else: df_state_two = self.df.state_two - state_one = pd.Series (df_state_one.charge.values,index=df_state_one.es_type.values) - state_two = pd.Series (df_state_two.charge.values,index=df_state_two.es_type.values) - charge_map = pd.concat([state_one,state_two],axis=0).to_dict() - return charge_map + state_one = pd.Series (df_state_one.z.values,index=df_state_one.es_type.values) + state_two = pd.Series (df_state_two.z.values,index=df_state_two.es_type.values) + charge_number_map = pd.concat([state_one,state_two],axis=0).to_dict() + return charge_number_map def get_lj_parameters(self, particle_name1, particle_name2, combining_rule='Lorentz-Berthelot'): """ @@ -2053,16 +2053,16 @@ def get_lj_parameters(self, particle_name1, particle_name2, combining_rule='Lore lj_parameters["epsilon"]=np.sqrt(lj_parameters["epsilon"][0]*lj_parameters["epsilon"][1]) return lj_parameters - def get_metal_ions_charge_map(self): + def get_metal_ions_charge_number_map(self): """ - Gets a map with the charge of all the metal ions supported. + Gets a map with the charge numbers of all the metal ions supported. Returns: - metal_charge_map(dict): Has the structure {"metal_name": metal_charge} + metal_charge_number_map(dict): Has the structure {"metal_name": metal_charge_number} """ - metal_charge_map = {"Ca": 2} - return metal_charge_map + metal_charge_number_map = {"Ca": 2} + return metal_charge_number_map def get_particle_id_map(self, object_name): ''' @@ -2584,7 +2584,7 @@ def search_particles_in_residue(self, residue_name): return list_of_particles_in_residue - def set_particle_acidity(self, name, acidity='inert', default_charge=0, pka=None, verbose=True, overwrite=True): + def set_particle_acidity(self, name, acidity='inert', default_charge_number=0, pka=None, verbose=True, overwrite=True): """ Sets the particle acidity if it is acidic or basic, creates `state_one` and `state_two` with the protonated and deprotonated states. In each state is set: `label`,`charge` and `es_type`. If it is inert, it will define only `state_one`. @@ -2592,7 +2592,7 @@ def set_particle_acidity(self, name, acidity='inert', default_charge=0, pka=None Args: name(`str`): Unique label that identifies the `particle`. acidity(`str`): Identifies whether the particle is `acidic` or `basic`, used to setup constant pH simulations. Defaults to `inert`. - default_charge (`int`): Charge of the particle. Defaults to 0. + default_charge_number (`int`): Charge number of the particle. Defaults to 0. pka(`float`, optional): If `particle` is an acid or a base, it defines its pka-value. Defaults to None. verbose(`bool`, optional): Switch to activate/deactivate verbose. Defaults to True. overwrite(`bool`, optional): Switch to enable overwriting of already existing values in pmb.df. Defaults to False. @@ -2625,9 +2625,9 @@ def set_particle_acidity(self, name, acidity='inert', default_charge=0, pka=None verbose=verbose, overwrite=overwrite) if self.df.loc [self.df['name'] == name].acidity.iloc[0] == 'inert': - self.add_value_to_df(key=('state_one','charge'), + self.add_value_to_df(key=('state_one','z'), index=index, - new_value=default_charge, + new_value=default_charge_number, verbose=verbose, overwrite=overwrite) self.add_value_to_df(key=('state_one','label'), @@ -2654,21 +2654,21 @@ def set_particle_acidity(self, name, acidity='inert', default_charge=0, pka=None verbose=verbose, overwrite=overwrite) if self.df.loc [self.df['name'] == name].acidity.iloc[0] == 'acidic': - self.add_value_to_df(key=('state_one','charge'), + self.add_value_to_df(key=('state_one','z'), index=index,new_value=0, verbose=verbose, overwrite=overwrite) - self.add_value_to_df(key=('state_two','charge'), + self.add_value_to_df(key=('state_two','z'), index=index, new_value=-1, verbose=verbose, overwrite=overwrite) elif self.df.loc [self.df['name'] == name].acidity.iloc[0] == 'basic': - self.add_value_to_df(key=('state_one','charge'), + self.add_value_to_df(key=('state_one','z'), index=index,new_value=+1, verbose=verbose, overwrite=overwrite) - self.add_value_to_df(key=('state_two','charge'), + self.add_value_to_df(key=('state_two','z'), index=index, new_value=0, verbose=verbose, @@ -2746,7 +2746,7 @@ def setup_cpH (self, counter_ion, constant_pH, exclusion_range=None, pka_set=Non exclusion_radius_per_type = exclusion_radius_per_type ) sucessfull_reactions_labels=[] - charge_map = self.get_charge_map() + charge_number_map = self.get_charge_number_map() for name in pka_set.keys(): if self.check_if_name_is_defined_in_df(name,pmb_type_to_be_defined='particle') is False : print('WARNING: the acid-base reaction of ' + name +' has not been set up because its espresso type is not defined in sg.type_map') @@ -2758,9 +2758,9 @@ def setup_cpH (self, counter_ion, constant_pH, exclusion_range=None, pka_set=Non RE.add_reaction(gamma=gamma, reactant_types=[state_one_type], product_types=[state_two_type, counter_ion_type], - default_charges={state_one_type: charge_map[state_one_type], - state_two_type: charge_map[state_two_type], - counter_ion_type: charge_map[counter_ion_type]}) + default_charges={state_one_type: charge_number_map[state_one_type], + state_two_type: charge_number_map[state_two_type], + counter_ion_type: charge_number_map[counter_ion_type]}) sucessfull_reactions_labels.append(name) return RE, sucessfull_reactions_labels @@ -2801,8 +2801,8 @@ def setup_gcmc(self, c_salt_res, salt_cation_name, salt_anion_name, activity_coe salt_cation_es_type = self.df.loc[self.df['name']==salt_cation_name].state_one.es_type.values[0] salt_anion_es_type = self.df.loc[self.df['name']==salt_anion_name].state_one.es_type.values[0] - salt_cation_charge = self.df.loc[self.df['name']==salt_cation_name].state_one.charge.values[0] - salt_anion_charge = self.df.loc[self.df['name']==salt_anion_name].state_one.charge.values[0] + salt_cation_charge = self.df.loc[self.df['name']==salt_cation_name].state_one.z.values[0] + salt_anion_charge = self.df.loc[self.df['name']==salt_anion_name].state_one.z.values[0] if salt_cation_charge <= 0: raise ValueError('ERROR salt cation charge must be positive, charge ', salt_cation_charge) @@ -2879,10 +2879,10 @@ def setup_grxmc_reactions(self, pH_res, c_salt_res, proton_name, hydroxide_name, salt_cation_es_type = self.df.loc[self.df['name']==salt_cation_name].state_one.es_type.values[0] salt_anion_es_type = self.df.loc[self.df['name']==salt_anion_name].state_one.es_type.values[0] - proton_charge = self.df.loc[self.df['name']==proton_name].state_one.charge.values[0] - hydroxide_charge = self.df.loc[self.df['name']==hydroxide_name].state_one.charge.values[0] - salt_cation_charge = self.df.loc[self.df['name']==salt_cation_name].state_one.charge.values[0] - salt_anion_charge = self.df.loc[self.df['name']==salt_anion_name].state_one.charge.values[0] + proton_charge = self.df.loc[self.df['name']==proton_name].state_one.z.values[0] + hydroxide_charge = self.df.loc[self.df['name']==hydroxide_name].state_one.z.values[0] + salt_cation_charge = self.df.loc[self.df['name']==salt_cation_name].state_one.z.values[0] + salt_anion_charge = self.df.loc[self.df['name']==salt_anion_name].state_one.z.values[0] if proton_charge <= 0: raise ValueError('ERROR proton charge must be positive, charge ', proton_charge) @@ -2974,7 +2974,7 @@ def setup_grxmc_reactions(self, pH_res, c_salt_res, proton_name, hydroxide_name, ) sucessful_reactions_labels=[] - charge_map = self.get_charge_map() + charge_number_map = self.get_charge_number_map() for name in pka_set.keys(): if self.check_if_name_is_defined_in_df(name,pmb_type_to_be_defined='particle') is False : print('WARNING: the acid-base reaction of ' + name +' has not been set up because its espresso type is not defined in the type map.') @@ -2991,8 +2991,8 @@ def setup_grxmc_reactions(self, pH_res, c_salt_res, proton_name, hydroxide_name, reactant_coefficients=[1], product_types=[state_two_type, proton_es_type], product_coefficients=[1, 1], - default_charges={state_one_type: charge_map[state_one_type], - state_two_type: charge_map[state_two_type], + default_charges={state_one_type: charge_number_map[state_one_type], + state_two_type: charge_number_map[state_two_type], proton_es_type: proton_charge}) # Reaction in terms of salt cation: HA = A + Na+ @@ -3001,8 +3001,8 @@ def setup_grxmc_reactions(self, pH_res, c_salt_res, proton_name, hydroxide_name, reactant_coefficients=[1], product_types=[state_two_type, salt_cation_es_type], product_coefficients=[1, 1], - default_charges={state_one_type: charge_map[state_one_type], - state_two_type: charge_map[state_two_type], + default_charges={state_one_type: charge_number_map[state_one_type], + state_two_type: charge_number_map[state_two_type], salt_cation_es_type: salt_cation_charge}) # Reaction in terms of hydroxide: OH- + HA = A @@ -3011,8 +3011,8 @@ def setup_grxmc_reactions(self, pH_res, c_salt_res, proton_name, hydroxide_name, reactant_coefficients=[1, 1], product_types=[state_two_type], product_coefficients=[1], - default_charges={state_one_type: charge_map[state_one_type], - state_two_type: charge_map[state_two_type], + default_charges={state_one_type: charge_number_map[state_one_type], + state_two_type: charge_number_map[state_two_type], hydroxide_es_type: hydroxide_charge}) # Reaction in terms of salt anion: Cl- + HA = A @@ -3021,8 +3021,8 @@ def setup_grxmc_reactions(self, pH_res, c_salt_res, proton_name, hydroxide_name, reactant_coefficients=[1, 1], product_types=[state_two_type], product_coefficients=[1], - default_charges={state_one_type: charge_map[state_one_type], - state_two_type: charge_map[state_two_type], + default_charges={state_one_type: charge_number_map[state_one_type], + state_two_type: charge_number_map[state_two_type], salt_anion_es_type: salt_anion_charge}) sucessful_reactions_labels.append(name) @@ -3081,8 +3081,8 @@ def setup_grxmc_unified(self, pH_res, c_salt_res, cation_name, anion_name, activ cation_es_type = self.df.loc[self.df['name']==cation_name].state_one.es_type.values[0] anion_es_type = self.df.loc[self.df['name']==anion_name].state_one.es_type.values[0] - cation_charge = self.df.loc[self.df['name']==cation_name].state_one.charge.values[0] - anion_charge = self.df.loc[self.df['name']==anion_name].state_one.charge.values[0] + cation_charge = self.df.loc[self.df['name']==cation_name].state_one.z.values[0] + anion_charge = self.df.loc[self.df['name']==anion_name].state_one.z.values[0] if cation_charge <= 0: raise ValueError('ERROR cation charge must be positive, charge ', cation_charge) if anion_charge >= 0: @@ -3102,7 +3102,7 @@ def setup_grxmc_unified(self, pH_res, c_salt_res, cation_name, anion_name, activ ) sucessful_reactions_labels=[] - charge_map = self.get_charge_map() + charge_number_map = self.get_charge_number_map() for name in pka_set.keys(): if self.check_if_name_is_defined_in_df(name,pmb_type_to_be_defined='particle') is False : print('WARNING: the acid-base reaction of ' + name +' has not been set up because its espresso type is not defined in sg.type_map') @@ -3120,8 +3120,8 @@ def setup_grxmc_unified(self, pH_res, c_salt_res, cation_name, anion_name, activ reactant_coefficients=[1], product_types=[state_two_type, cation_es_type], product_coefficients=[1, 1], - default_charges={state_one_type: charge_map[state_one_type], - state_two_type: charge_map[state_two_type], + default_charges={state_one_type: charge_number_map[state_one_type], + state_two_type: charge_number_map[state_two_type], cation_es_type: cation_charge}) # Reaction in terms of small anion: X- + HA = A @@ -3130,8 +3130,8 @@ def setup_grxmc_unified(self, pH_res, c_salt_res, cation_name, anion_name, activ reactant_coefficients=[1, 1], product_types=[state_two_type], product_coefficients=[1], - default_charges={state_one_type: charge_map[state_one_type], - state_two_type: charge_map[state_two_type], + default_charges={state_one_type: charge_number_map[state_one_type], + state_two_type: charge_number_map[state_two_type], anion_es_type: anion_charge}) sucessful_reactions_labels.append(name) @@ -3181,11 +3181,11 @@ def setup_df (self): 'state_one': { 'label': str, 'es_type': object, - 'charge': object }, + 'z': object }, 'state_two': { 'label': str, 'es_type': object, - 'charge': object }, + 'z': object }, 'sequence': { '': object}, 'bond_object': { diff --git a/testsuite/globular_protein_unit_tests.py b/testsuite/globular_protein_unit_tests.py index 41d4ec7..c6bf8a3 100644 --- a/testsuite/globular_protein_unit_tests.py +++ b/testsuite/globular_protein_unit_tests.py @@ -35,7 +35,7 @@ print("*** Unit test passed ***\n") print("*** Unit test: Check that check_if_metal_ion returns True for any key corresponding to a supported metal ion ***") -for key in pmb.get_metal_ions_charge_map().keys(): +for key in pmb.get_metal_ions_charge_number_map().keys(): np.testing.assert_equal(actual=pmb.check_if_metal_ion(key=key), desired=True, verbose=True) @@ -46,13 +46,11 @@ verbose=True) print("*** Unit test passed ***\n") -print("*** Unit test: Check that get_metal_ions_charge_map returns the correct charge map for metals ***") -metal_charge_map = {"Ca": 2} -pmb_metal_charge_map = pmb.get_metal_ions_charge_map() +print("*** Unit test: Check that get_metal_ions_charge_number_map returns the correct charge map for metals ***") +metal_charge_number_map = {"Ca": 2} +pmb_metal_charge_number_map = pmb.get_metal_ions_charge_number_map() -np.testing.assert_equal(actual=pmb_metal_charge_map, - desired=metal_charge_map, +np.testing.assert_equal(actual=pmb_metal_charge_number_map, + desired=metal_charge_number_map, verbose=True) print("*** Unit test passed ***\n") - -metal_charge_map = {"Ca": 2} diff --git a/testsuite/set_particle_acidity_test.py b/testsuite/set_particle_acidity_test.py index fc06a12..eeaaf47 100644 --- a/testsuite/set_particle_acidity_test.py +++ b/testsuite/set_particle_acidity_test.py @@ -42,7 +42,7 @@ def check_acid_base_setup(input_parameters,acidity_setup): verbose=True) # Checks that the setup of the acid base properties is done correctly for state in ["state_one","state_two"]: - for state_atribute in ["label","charge"]: + for state_atribute in ["label","z"]: np.testing.assert_equal(actual=pmb.df[state][state_atribute].values[0], desired=acidity_setup[state][state_atribute], verbose=True) @@ -59,9 +59,9 @@ def check_acid_base_setup(input_parameters,acidity_setup): "pka": np.nan, "z":2} acidity_setup={"state_one":{"label":f"{input_parameters['name']}", - "charge":2}, + "z":2}, "state_two":{"label": np.nan, - "charge":np.nan},} + "z":np.nan},} check_acid_base_setup(input_parameters=input_parameters, acidity_setup=acidity_setup) @@ -74,9 +74,9 @@ def check_acid_base_setup(input_parameters,acidity_setup): "acidity": "acidic", "pka":4} acidity_setup={"state_one":{"label":f"{input_parameters['name']}H", - "charge":0}, + "z":0}, "state_two":{"label":f"{input_parameters['name']}", - "charge":-1},} + "z":-1},} check_acid_base_setup(input_parameters=input_parameters, acidity_setup=acidity_setup) @@ -88,9 +88,9 @@ def check_acid_base_setup(input_parameters,acidity_setup): "acidity": "basic", "pka":9} acidity_setup={"state_one":{"label":f"{input_parameters['name']}H", - "charge":1}, + "z":1}, "state_two":{"label":f"{input_parameters['name']}", - "charge":0},} + "z":0},} check_acid_base_setup(input_parameters=input_parameters, acidity_setup=acidity_setup) diff --git a/testsuite/setup_salt_ions_unit_tests.py b/testsuite/setup_salt_ions_unit_tests.py index cbef95b..1e98282 100644 --- a/testsuite/setup_salt_ions_unit_tests.py +++ b/testsuite/setup_salt_ions_unit_tests.py @@ -46,7 +46,7 @@ #### Unit tests for the added salt def check_salt_concentration(espresso_system,cation_name,anion_name,c_salt,N_SALT_ION_PAIRS, verbose=False): - charge_map=pmb.get_charge_map() + charge_number_map=pmb.get_charge_number_map() type_map=pmb.get_type_map() espresso_system.setup_type_map(type_map.values()) c_salt_calculated = pmb.create_added_salt(espresso_system=espresso_system, @@ -55,8 +55,8 @@ def check_salt_concentration(espresso_system,cation_name,anion_name,c_salt,N_SAL c_salt=c_salt, verbose=verbose) - np.testing.assert_equal(espresso_system.number_of_particles(type_map[cation_name]),N_SALT_ION_PAIRS*abs(charge_map[type_map[anion_name]])) - np.testing.assert_equal(espresso_system.number_of_particles(type_map[anion_name]),N_SALT_ION_PAIRS*abs(charge_map[type_map[cation_name]])) + np.testing.assert_equal(espresso_system.number_of_particles(type_map[cation_name]),N_SALT_ION_PAIRS*abs(charge_number_map[type_map[anion_name]])) + np.testing.assert_equal(espresso_system.number_of_particles(type_map[anion_name]),N_SALT_ION_PAIRS*abs(charge_number_map[type_map[cation_name]])) np.testing.assert_almost_equal(c_salt_calculated.m_as("mol/L"), c_salt.m_as("mol/L")) espresso_system.part.clear() print("*** Unit test: test that create_added_salt works for a 1:1 salt (NaCl-like). Should print the added salt concentration and number of ions ***")