def reaction() -> reaction_pb2.Reaction: resolver = units.UnitResolver() reaction = reaction_pb2.Reaction() reaction.setup.is_automated = True reaction.inputs["dummy_input"].components.add().CopyFrom( message_helpers.build_compound( name="n-hexane", smiles="CCCCCC", role="reactant", amount="1 milliliters", ) ) reaction.inputs["dummy_input"].components.add().CopyFrom( message_helpers.build_compound( name="THF", smiles="C1OCCC1", role="solvent", amount="40 liters", ) ) reaction.inputs["dummy_input2"].components.add().CopyFrom( message_helpers.build_compound( name="Pd", smiles="[Pd]", role="catalyst", amount="catalytic", ) ) reaction.conditions.pressure.atmosphere.type = reaction_pb2.PressureConditions.Atmosphere.OXYGEN reaction.conditions.stirring.rate.rpm = 100 reaction.conditions.temperature.control.type = reaction_pb2.TemperatureConditions.TemperatureControl.OIL_BATH reaction.conditions.temperature.setpoint.CopyFrom( reaction_pb2.Temperature(value=100, units=reaction_pb2.Temperature.CELSIUS) ) outcome = reaction.outcomes.add() outcome.reaction_time.CopyFrom(resolver.resolve("40 minutes")) outcome.products.add().identifiers.extend( message_helpers.build_compound(name="hexanone", smiles="CCCCC(=O)C").identifiers ) yield reaction
def setUp(self): super().setUp() self.test_subdirectory = tempfile.mkdtemp(dir=flags.FLAGS.test_tmpdir) self._resolver = units.UnitResolver() reaction = reaction_pb2.Reaction() reaction.setup.is_automated = True reaction.inputs['dummy_input'].components.add().CopyFrom( message_helpers.build_compound( name='n-hexane', smiles='CCCCCC', role='reactant', amount='1 milliliters', )) reaction.inputs['dummy_input'].components.add().CopyFrom( message_helpers.build_compound( name='THF', smiles='C1OCCC1', role='solvent', amount='40 liters', )) reaction.conditions.pressure.atmosphere.type = ( reaction_pb2.PressureConditions.Atmosphere.OXYGEN) reaction.conditions.stirring.rate.rpm = 100 reaction.conditions.temperature.control.type = ( reaction_pb2.TemperatureConditions.TemperatureControl.OIL_BATH) reaction.conditions.temperature.setpoint.CopyFrom( reaction_pb2.Temperature(value=100, units=reaction_pb2.Temperature.CELSIUS)) outcome = reaction.outcomes.add() outcome.reaction_time.CopyFrom(self._resolver.resolve('40 minutes')) outcome.products.add().identifiers.extend( message_helpers.build_compound( name='hexanone', smiles='CCCCC(=O)C', ).identifiers) reaction.reaction_id = 'dummy_reaction_id' self._reaction = reaction self._input = os.path.join(self.test_subdirectory, 'reaction.pbtxt') message_helpers.write_message(self._reaction, self._input)
def setUp(self): super().setUp() self._resolver = units.UnitResolver() reaction = reaction_pb2.Reaction() reaction.setup.is_automated = reaction_pb2.Boolean.TRUE reaction.inputs['dummy_input'].components.add().CopyFrom( message_helpers.build_compound( name='n-hexane', smiles='CCCCCC', role='reactant', amount='1 milliliters', )) reaction.inputs['dummy_input'].components.add().CopyFrom( message_helpers.build_compound( name='C1OCCC1', smiles='THF', role='solvent', amount='40 liters', )) reaction.conditions.pressure.atmosphere.type = ( reaction_pb2.PressureConditions.Atmosphere.OXYGEN) reaction.conditions.stirring.rate.rpm = 100 reaction.conditions.temperature.control.type = ( reaction_pb2.TemperatureConditions.TemperatureControl.OIL_BATH) reaction.conditions.temperature.setpoint.CopyFrom( reaction_pb2.Temperature(value=100, units=reaction_pb2.Temperature.CELSIUS)) outcome = reaction.outcomes.add() outcome.reaction_time.CopyFrom(self._resolver.resolve('40 minutes')) outcome.products.add().compound.CopyFrom( message_helpers.build_compound( name='hexanone', smiles='CCCCC(=O)C', role='product', )) reaction.reaction_id = 'dummy_reaction_id' self._reaction = reaction
def setUp(self): super().setUp() self._resolver = units.UnitResolver()
def build_compound(smiles=None, name=None, amount=None, role=None, is_limiting=None, prep=None, prep_details=None, vendor=None): """Builds a Compound message with the most common fields. Args: smiles: Text compound SMILES. name: Text compound name. amount: Text amount string, e.g. '1.25 g'. role: Text reaction role. Must match a value in ReactionRoleType. is_limiting: Boolean whether this compound is limiting for the reaction. prep: Text compound preparation type. Must match a value in PreparationType. prep_details: Text compound preparation details. If provided, `prep` is required. vendor: Text compound vendor/supplier. Returns: Compound message. Raises: KeyError: if `role` or `prep` does not match a supported enum value. TypeError: if `amount` units are not supported. ValueError: if `prep_details` is provided and `prep` is None. """ compound = reaction_pb2.Compound() if smiles: compound.identifiers.add(value=smiles, type='SMILES') if name: compound.identifiers.add(value=name, type='NAME') if amount: resolver = units.UnitResolver() amount_pb = resolver.resolve(amount) if isinstance(amount_pb, reaction_pb2.Mass): compound.amount.mass.CopyFrom(amount_pb) elif isinstance(amount_pb, reaction_pb2.Moles): compound.amount.moles.CopyFrom(amount_pb) elif isinstance(amount_pb, reaction_pb2.Volume): compound.amount.volume.CopyFrom(amount_pb) else: raise TypeError(f'unsupported units for amount: {amount_pb}') if role: field = reaction_pb2.Compound.DESCRIPTOR.fields_by_name[ 'reaction_role'] values_dict = field.enum_type.values_by_name try: compound.reaction_role = values_dict[role.upper()].number except KeyError as error: raise KeyError( f'{role} is not a supported type: {values_dict.keys()}' ) from error if is_limiting is not None: if not (is_limiting is True or is_limiting is False): raise TypeError( f'is_limiting must be a boolean value: {is_limiting}') compound.is_limiting = is_limiting if prep: field = reaction_pb2.CompoundPreparation.DESCRIPTOR.fields_by_name[ 'type'] values_dict = field.enum_type.values_by_name try: compound.preparations.add().type = values_dict[prep.upper()].number except KeyError as error: raise KeyError( f'{prep} is not a supported type: {values_dict.keys()}' ) from error if (compound.preparations[0].type == reaction_pb2.CompoundPreparation.CUSTOM and not prep_details): raise ValueError( 'prep_details must be provided when CUSTOM prep is used') if prep_details: if not prep: raise ValueError('prep must be provided when prep_details is used') compound.preparations[0].details = prep_details if vendor: compound.source.vendor = vendor return compound
def set_solute_moles(solute, solvents, concentration, overwrite=False): """Helps define components for stock solution inputs with a single solute and a one or more solvent compounds. Args: solute: Compound with identifiers, roles, etc.; this argument is modified in place to define an amount in moles. solvents: list of Compounds each with defined volume. concentration: string defining solute concentration. overwrite: whether to overwrite an existing solute amount if defined. Defaults to False Raises: ValueError: if any solvent does not have a defined volume. ValueError: if the solute has an existing amount field and overwrite is set to False. Returns: List of Compounds to assign to a repeated components field. """ # Check solute definition if solute.amount.WhichOneof('kind') and not overwrite: raise ValueError('solute has defined amount and overwrite is False') # Get total solvent volume in liters. volume_liter = 0 for solvent in solvents: amount = solvent.amount if not amount.HasField('volume') or not amount.volume.value: raise ValueError('solvent must have defined volume') if amount.volume.units == amount.volume.LITER: volume_liter += amount.volume.value elif amount.volume.units == amount.volume.MILLILITER: volume_liter += amount.volume.value * 1e-3 elif amount.volume.units == amount.volume.MICROLITER: volume_liter += amount.volume.value * 1e-6 elif amount.volume.units == amount.volume.NANOLITER: volume_liter += amount.volume.value * 1e-9 else: raise ValueError( 'solvent units not recognized by set_solute_moles') # Get solute concentration in molar. resolver = units.UnitResolver( unit_synonyms=units.CONCENTRATION_UNIT_SYNONYMS, forbidden_units={}) concentration_pb = resolver.resolve(concentration) if concentration_pb.units == concentration_pb.MOLAR: concentration_molar = concentration_pb.value elif concentration_pb.units == concentration_pb.MILLIMOLAR: concentration_molar = concentration_pb.value * 1e-3 elif concentration_pb.units == concentration_pb.MICROMOLAR: concentration_molar = concentration_pb.value * 1e-6 else: raise ValueError(f'unsupported units: {concentration_pb.units}') # Assign moles amount and return. moles = volume_liter * concentration_molar if moles < 1e-6: value = moles * 1e9 unit = reaction_pb2.Moles.NANOMOLE elif moles < 1e-3: value = moles * 1e6 unit = reaction_pb2.Moles.MICROMOLE elif moles < 1: value = moles * 1e3 unit = reaction_pb2.Moles.MILLIMOLE else: value = moles unit = reaction_pb2.Moles.MOLE solute.amount.moles.value = value solute.amount.moles.units = unit return [solute] + solvents