Esempio n. 1
0
    def test_standardRestart(self):
        self.td = directoryChangers.TemporaryDirectoryChanger()
        with self.td:
            # make DB to load from
            o = self._getOperatorThatChangesVariables(
                settings.Settings(os.path.join(TEST_ROOT, "armiRun.yaml")))
            with o:
                o.operate()
                firstEndTime = o.r.p.time
                self.assertNotEqual(
                    firstEndTime, 0,
                    "Time should have advanced by the end of the run.")

            # run standard restart case
            loadDB = "loadFrom.h5"
            os.rename("armiRun.h5", loadDB)
            cs = settings.Settings(os.path.join(TEST_ROOT, "armiRun.yaml"))
            cs["loadStyle"] = "fromDB"
            cs["reloadDBName"] = loadDB
            cs["startCycle"] = 1
            cs["startNode"] = 1
            o = self._getOperatorThatChangesVariables(cs)

            # the interact BOL has historically failed due to trying to write inputs
            # which are already in the DB from the _mergeStandardRunDB call
            with o:
                o.operate()
                self.assertEqual(
                    firstEndTime,
                    o.r.p.time,
                    "End time should have been the same for the restart run.\n"
                    "First end time: {},\nSecond End time: {}".format(
                        firstEndTime, o.r.p.time),
                )
Esempio n. 2
0
    def test_autoDepletable(self):
        nuclideFlags = (inspect.cleandoc(r"""
            nuclide flags:
                U234: {burn: true, xs: true}
                U235: {burn: true, xs: true}
                U238: {burn: true, xs: true}
                B10: {burn: true, xs: true}
                B11: {burn: true, xs: true}
                C: {burn: true, xs: true}
                DUMP1: {burn: true, xs: true}
            custom isotopics:
                B4C:
                    input format: number densities
                    B10: 1.0
                    B11: 1.0
                    C: 1.0
            """) + "\n")
        bp = blueprints.Blueprints.load(
            nuclideFlags + self.componentString.format(
                material="Custom", isotopics="isotopics: B4C", flags=""))
        cs = settings.Settings()
        a = bp.constructAssem(cs, "assembly")
        expectedNuclides = ["B10", "B11", "C", "DUMP1"]
        unexpectedNuclides = ["U234", "U325", "U238"]
        for nuc in expectedNuclides:
            self.assertIn(nuc, a[0][0].getNuclides())
        for nuc in unexpectedNuclides:
            self.assertNotIn(nuc, a[0][0].getNuclides())

        c = a[0][0]

        # Since we didn't supply flags, we should get the DEPLETABLE flag added
        # automatically, since this one has depletable nuclides
        self.assertEqual(c.p.flags, Flags.DEPLETABLE)
        # More robust test, but worse unittest.py output when it fails
        self.assertTrue(c.hasFlags(Flags.DEPLETABLE))

        # repeat the process with some flags set explicitly
        bp = blueprints.Blueprints.load(
            nuclideFlags +
            self.componentString.format(material="Custom",
                                        isotopics="isotopics: B4C",
                                        flags="fuel test"))
        cs = settings.Settings()
        a = bp.constructAssem(cs, "assembly")
        c = a[0][0]

        # Since we supplied flags, we should NOT get the DEPLETABLE flag added
        self.assertEqual(c.p.flags, Flags.FUEL | Flags.TEST)
        # More robust test, but worse unittest.py output when it fails
        self.assertTrue(c.hasFlags(Flags.FUEL | Flags.TEST))
Esempio n. 3
0
    def setUp(self):
        self.suite = cases.CaseSuite(settings.Settings())

        geom = systemLayoutInput.SystemLayoutInput()
        geom.readGeomFromStream(io.StringIO(GEOM_INPUT))
        bp = blueprints.Blueprints.load(BLUEPRINT_INPUT)

        self.c1 = cases.Case(cs=settings.Settings(), geom=geom, bp=bp)
        self.c1.cs.path = "c1.yaml"
        self.suite.add(self.c1)

        self.c2 = cases.Case(cs=settings.Settings(), geom=geom, bp=bp)
        self.c2.cs.path = "c2.yaml"
        self.suite.add(self.c2)
Esempio n. 4
0
    def setUp(self):
        cs = settings.Settings(f"{CASE_TITLE}.yaml")
        newSettings = {}
        newSettings["db"] = True
        newSettings["reloadDBName"] = pathlib.Path(
            f"{CASE_TITLE}.h5").absolute()
        newSettings["loadStyle"] = "fromDB"
        newSettings["detailAssemLocationsBOL"] = ["001-001"]
        newSettings["startNode"] = 1
        cs = cs.modified(newSettings=newSettings)

        self.td = directoryChangers.TemporaryDirectoryChanger()
        self.td.__enter__()

        c = case.Case(cs)
        case2 = c.clone(title="armiRun")
        settings.setMasterCs(case2.cs)
        self.o = case2.initializeOperator()
        self.r = self.o.r

        self.o.getInterface("main").interactBOL()

        dbi = self.o.getInterface("database")
        # Get to the database state at the end of stack of time node 1.
        # The end of the interface stack is when history tracker tends to run.
        dbi.loadState(0, 1)
Esempio n. 5
0
    def setUpClass(cls):
        # Not using a directory changer since it isn't important that we go back in the
        # first place, and we don't want to get tangled with the directory change below.
        # We need to be in the TUTORIAL_DIR in the first place so that for `filesToMove`
        # to work right.
        os.chdir(TUTORIAL_DIR)

        # Make sure to do this work in a temporary directory to avoid race conditions
        # when running tests in parallel with xdist.
        cls.dirChanger = directoryChangers.TemporaryDirectoryChanger(
            filesToMove=TUTORIAL_FILES)
        cls.dirChanger.__enter__()
        runTutorialNotebook()

        reloadCs = settings.Settings(f"{CASE_TITLE}.yaml")

        newSettings = {}
        newSettings["db"] = True
        newSettings["reloadDBName"] = pathlib.Path(
            f"{CASE_TITLE}.h5").absolute()
        newSettings["runType"] = "Snapshots"
        newSettings["loadStyle"] = "fromDB"
        newSettings["detailAssemLocationsBOL"] = ["001-001"]

        reloadCs = reloadCs.modified(newSettings=newSettings)
        reloadCs.caseTitle = "armiRun"

        o = armi_init(cs=reloadCs)
        cls.o = o
    def test_setDefaultSettingsByLowestBuGroupOneDimensional(self):
        # Initialize some micro suffix in the cross sections
        cs = settings.Settings()
        xsModel = XSSettings()
        rq = XSModelingOptions(
            "RQ",
            geometry="1D cylinder",
            blockRepresentation="Average",
            meshSubdivisionsPerCm=1.0,
        )
        xsModel["RQ"] = rq
        xsModel.setDefaults(cs["xsBlockRepresentation"],
                            cs["disableBlockTypeExclusionInXsGeneration"])

        # Check that new micro suffix `RY` with higher burn-up group gets assigned the same settings as `RQ`
        self.assertNotIn("RY", xsModel)
        self.assertEqual(xsModel["RY"], xsModel["RQ"])

        # Check that new micro suffix `RZ` with higher burn-up group gets assigned the same settings as `RQ`
        self.assertNotIn("RZ", xsModel)
        self.assertEqual(xsModel["RZ"], xsModel["RQ"])

        # Check that new micro suffix `RA` with lower burn-up group does NOT get assigned the same settings as `RQ`
        self.assertNotIn("RA", xsModel)
        self.assertNotEqual(xsModel["RA"], xsModel["RQ"])
Esempio n. 7
0
    def test_addInterfaceSubclassCollision(self):

        self.cs = settings.Settings()
        o, r = test_reactors.loadTestReactor()

        interfaceA = InterfaceA(r, self.cs)

        interfaceB = InterfaceB(r, self.cs)
        o.addInterface(interfaceA)

        # 1) Adds B and gets rid of A
        o.addInterface(interfaceB)
        self.assertEqual(o.getInterface("Second"), interfaceB)
        self.assertEqual(o.getInterface("First"), None)

        # 2) Now we have B which is a subclass of A,
        #    we want to not add A (but also not have an error)

        o.addInterface(interfaceA)
        self.assertEqual(o.getInterface("Second"), interfaceB)
        self.assertEqual(o.getInterface("First"), None)

        # 3) Also if another class not a subclass has the same function,
        #    raise an error
        interfaceC = InterfaceC(r, self.cs)

        self.assertRaises(RuntimeError, o.addInterface, interfaceC)

        # 4) Check adding a different function Interface

        interfaceC.function = "C"

        o.addInterface(interfaceC)
        self.assertEqual(o.getInterface("Second"), interfaceB)
        self.assertEqual(o.getInterface("Third"), interfaceC)
Esempio n. 8
0
    def test_NeutronicConvergenceModifier(self):
        cs = settings.Settings()

        with self.assertRaises(ValueError):
            suiteBuilder.NeutronicConvergenceModifier(0.0)

        with self.assertRaises(ValueError):
            suiteBuilder.NeutronicConvergenceModifier(1e-2 + 1e-15)

        suiteBuilder.NeutronicConvergenceModifier(1e-2)(cs, None, None)
        self.assertAlmostEqual(cs["epsEig"], 1e-2)
        self.assertAlmostEqual(cs["epsFSAvg"], 1.0)
        self.assertAlmostEqual(cs["epsFSPoint"], 1.0)

        # since there is a specific test to adjust these, we should maybe not allow a generic
        # settings modifier to work...
        with six.assertRaisesRegex(self, ValueError,
                                   "use .*NeutronicConvergenceModifier"):
            suiteBuilder.SettingsModifier("epsEig", 1e-5)

        with six.assertRaisesRegex(self, ValueError,
                                   "use .*NeutronicConvergenceModifier"):
            suiteBuilder.SettingsModifier("epsFSAvg", 1e-5)

        with six.assertRaisesRegex(self, ValueError,
                                   "use .*NeutronicConvergenceModifier"):
            suiteBuilder.SettingsModifier("epsFSPoint", 1e-5)
Esempio n. 9
0
 def test_componentInitializationControlCustomIsotopics(self):
     nuclideFlags = (inspect.cleandoc(r"""
         nuclide flags:
             U234: {burn: true, xs: true}
             U235: {burn: true, xs: true}
             U238: {burn: true, xs: true}
             B10: {burn: true, xs: true}
             B11: {burn: true, xs: true}
             C: {burn: true, xs: true}
             DUMP1: {burn: true, xs: true}
         custom isotopics:
             B4C:
                 input format: number densities
                 B10: 1.0
                 B11: 1.0
                 C: 1.0
         """) + "\n")
     bp = blueprints.Blueprints.load(
         nuclideFlags + self.componentString.format(
             material="Custom", isotopics="isotopics: B4C"))
     cs = settings.Settings()
     a = bp.constructAssem("hex", cs, "assembly")
     expectedNuclides = ["B10", "B11", "C", "DUMP1"]
     unexpectedNuclides = ["U234", "U325", "U238"]
     for nuc in expectedNuclides:
         self.assertIn(nuc, a[0][0].getNuclides())
     for nuc in unexpectedNuclides:
         self.assertNotIn(nuc, a[0][0].getNuclides())
Esempio n. 10
0
    def test_enforcements(self):
        mock = settings.Settings()

        xml = six.StringIO(
            '<settings-definitions><okaySetting type="int" default="17"/></settings-definitions>'
        )
        reader = settingsIO.SettingsDefinitionReader(mock)

        # put 'okaySetting' into the mock settings object
        reader.readFromStream(xml, fmt=reader.SettingsInputFormat.XML)

        self.assertEqual(mock["okaySetting"], 17)

        # we'll allow ARMI to run while ignoring old settings, but will issue warnings.
        xml = six.StringIO('<settings><OOGLYBOOGLY value="18"/></settings>')
        reader = settingsIO.SettingsReader(mock)
        reader.readFromStream(xml, fmt=reader.SettingsInputFormat.XML)
        with self.assertRaises(exceptions.NonexistentSetting):
            mock["OOGLYBOOGLY"]

        settingsRules.RENAMES["OOGLYBOOGLY"] = "okaySetting"
        xml = six.StringIO('<settings><OOGLYBOOGLY value="18"/></settings>')
        reader = settingsIO.SettingsReader(mock)
        reader.readFromStream(xml, fmt=reader.SettingsInputFormat.XML)

        self.assertEqual(mock["okaySetting"], 18)
Esempio n. 11
0
    def test_conversions(self):
        """Tests that settings convert based on a set of rules before being created

        :ref:`REQfbefba64-3de7-4aea-b155-102c7b375722`
        """
        mock = settings.Settings()
        mock.settings["newSetting"] = setting.FloatSetting(
            "newSetting",
            {
                "description": "Hola",
                "label": "hello",
                "type": "float",
                "default": "2.0",
            },
        )
        # make sure everything is as expected
        self.assertEqual(2.0, mock["newSetting"])

        # read some settings, and see that everything makes sense
        xml = six.StringIO('<settings><deprecated value="17"/></settings>')
        reader = settingsIO.SettingsReader(mock)

        # add a rename
        settingsRules.RENAMES["deprecated"] = "newSetting"

        reader.readFromStream(xml, fmt=reader.SettingsInputFormat.XML)
        self.assertEqual(17.0, mock["newSetting"])
        del settingsRules.RENAMES["deprecated"]

        # read settings
        xml2 = six.StringIO('<settings><newSetting value="92"/></settings>')
        reader2 = settingsIO.SettingsReader(mock)
        reader2.readFromStream(xml2, fmt=reader.SettingsInputFormat.XML)
        self.assertEqual(92.0, mock["newSetting"])
Esempio n. 12
0
    def test_malformedCreation(self):
        """Setting creation test

        Tests that a few unsupported types properly fail to create

        """
        s = settings.Settings()

        key = "bugMaker"
        attrib = {
            "type": "tuple",
            "default": 5.0,
            "description": "d",
            "label": "l"
        }

        with self.assertRaises(TypeError):
            s.settings[key] = setting.Setting.factory(key, attrib)
        attrib["type"] = tuple
        with self.assertRaises(TypeError):
            s.settings[key] = setting.Setting.factory(key, attrib)

        attrib["type"] = "dict"
        with self.assertRaises(TypeError):
            s.settings[key] = setting.Setting.factory(key, attrib)
        attrib["type"] = dict
        with self.assertRaises(TypeError):
            s.settings[key] = setting.Setting.factory(key, attrib)
Esempio n. 13
0
    def test_CladThickenessByIDModifier(self):
        """
        Adjust the clad thickness by inner diameter.

        .. math::

            cladThickness = (clad.od - clad.id) / 2
            clad.id = cladod - 2 * cladThicness

        when ``clad.id = 1.1`` and ``cladThickness = 0.025``,

        .. math::

            clad.od = 1.1 - 2 * 0.025
            clad.od = 1.05
        """
        bp = self.bp
        self.assertEqual(1.0, bp.blockDesigns["fuel 1"]["clad"].id)
        self.assertEqual(1.0, bp.blockDesigns["fuel 2"]["clad"].id)
        self.assertEqual(1.0, bp.blockDesigns["block 3"]["clad"].id)
        self.assertEqual(1.0, bp.blockDesigns["block 4"]["clad"].id)
        self.assertEqual(1.0, bp.blockDesigns["block 5"]["clad"].id)

        pinTypeInputModifiers.CladThicknessByIDModifier(0.025)(
            settings.Settings(), bp, MockGeom)

        self.assertEqual(1.05, bp.blockDesigns["fuel 1"]["clad"].id)
        self.assertEqual(1.05, bp.blockDesigns["fuel 2"]["clad"].id)
        self.assertEqual(1.05, bp.blockDesigns["block 3"]["clad"].id)
        self.assertEqual(1.05, bp.blockDesigns["block 4"]["clad"].id)
        self.assertEqual(
            1.05, bp.blockDesigns["block 5"]["clad"].id)  # modifies all blocks
Esempio n. 14
0
    def test_duplicate(self):
        """Tests the duplication function

        """
        origSettings = settings.Settings()
        dupSettings = origSettings.duplicate()
        self.checkSettingsMatch(origSettings, dupSettings, defaults=True)
Esempio n. 15
0
    def test_AdjustSmearDensity(self):
        r"""
        Compute the smear density where clad.id is 1.0.

        .. math::

            areaFuel = smearDensity * innerCladArea
            fuelOD^2 / 4 = 0.5 * cladID^2 / 4
            fuelOD = \sqrt{0.5}


        .. note:: the area of fuel is 0.5 * inner area of clad

        """
        bp = self.bp
        self.assertEqual(1.0, bp.blockDesigns["fuel 1"]["clad"].id)
        self.assertEqual(0.5, bp.blockDesigns["fuel 1"]["fuel"].od)
        self.assertEqual(0.5, bp.blockDesigns["fuel 2"]["fuel"].od)
        self.assertEqual(0.5, bp.blockDesigns["block 3"]["fuel"].od)
        self.assertEqual(0.5, bp.blockDesigns["block 4"]["fuel"].od)
        self.assertEqual(0.5, bp.blockDesigns["block 5"]["fuel"].od)

        pinTypeInputModifiers.SmearDensityModifier(0.5)(settings.Settings(),
                                                        bp, MockGeom)

        self.assertEqual(math.sqrt(0.5), bp.blockDesigns["fuel 1"]["fuel"].od)
        self.assertEqual(math.sqrt(0.5), bp.blockDesigns["fuel 2"]["fuel"].od)
        self.assertEqual(math.sqrt(0.5), bp.blockDesigns["block 3"]["fuel"].od)
        self.assertEqual(math.sqrt(0.5), bp.blockDesigns["block 4"]["fuel"].od)
        self.assertEqual(
            0.5, bp.blockDesigns["block 5"]["fuel"].od)  # unique instance
Esempio n. 16
0
    def test_CladThickenessByODModifier(self):
        """
        Adjust the clad thickness by outer diameter.

        .. math::

            cladThickness = (clad.od - clad.id) / 2
            clad.od = 2 * cladThicness - clad.id

        when ``clad.id = 1.0`` and ``cladThickness = 0.12``,

        .. math::

            clad.od = 2 * 0.12 - 1.0
            clad.od = 1.24
        """
        bp = self.bp
        self.assertEqual(1.1, bp.blockDesigns["fuel 1"]["clad"].od)
        self.assertEqual(1.1, bp.blockDesigns["fuel 2"]["clad"].od)
        self.assertEqual(1.1, bp.blockDesigns["block 3"]["clad"].od)
        self.assertEqual(1.1, bp.blockDesigns["block 4"]["clad"].od)
        self.assertEqual(1.1, bp.blockDesigns["block 5"]["clad"].od)

        pinTypeInputModifiers.CladThicknessByODModifier(0.12)(
            settings.Settings(), bp, MockGeom)

        self.assertEqual(1.24, bp.blockDesigns["fuel 1"]["clad"].od)
        self.assertEqual(1.24, bp.blockDesigns["fuel 2"]["clad"].od)
        self.assertEqual(1.24, bp.blockDesigns["block 3"]["clad"].od)
        self.assertEqual(1.24, bp.blockDesigns["block 4"]["clad"].od)
        self.assertEqual(
            1.24, bp.blockDesigns["block 5"]["clad"].od)  # modifies all blocks
Esempio n. 17
0
    def setUp(self):

        self.td = directoryChangers.TemporaryDirectoryChanger()
        self.td.__enter__()
        cs = settings.Settings(os.path.join(TEST_ROOT, "armiRun.yaml"))
        self.o = getSimpleDBOperator(cs)
        self.r = self.o.r
Esempio n. 18
0
    def setUp(self):
        self.cs = settings.Settings()
        isotopicDepletion.applyDefaultBurnChain()

        with io.StringIO(FULL_BP) as stream:
            self.blueprints = blueprints.Blueprints.load(stream)
            self.blueprints._prepConstruction(self.cs)
Esempio n. 19
0
    def setUpClass(cls):
        caseSetting = settings.Settings()
        _, cls.r = test_reactors.loadTestReactor()

        cls.hexBlock = cls.r.core.getBlocks()[0]

        cls.cartesianBlock = blocks.CartesianBlock("TestCartesianBlock",
                                                   caseSetting)
        cartesianComponent = components.HoledSquare(
            "duct",
            "UZr",
            Tinput=273.0,
            Thot=273.0,
            holeOD=68.0,
            widthOuter=12.5,
            mult=1.0,
        )
        cls.cartesianBlock.add(cartesianComponent)
        cls.cartesianBlock.add(
            components.Circle("clad",
                              "HT9",
                              Tinput=273.0,
                              Thot=273.0,
                              od=68.0,
                              mult=169.0))
Esempio n. 20
0
 def setUpClass(cls):
     geom = systemLayoutInput.SystemLayoutInput()
     geom.readGeomFromStream(GEOM_INPUT)
     bp = blueprints.Blueprints.load(BLUEPRINT_INPUT_LINKS)
     cs = settings.Settings()
     bp._prepConstruction(cs)
     cls.baseCase = cases.Case(cs=cs, bp=bp, geom=geom)
Esempio n. 21
0
    def loadCS(self):
        from armi import settings

        cs = settings.Settings()
        cs.caseTitle = os.path.splitext(os.path.basename(self._hdf_file.filename))[0]
        cs.loadFromString(self._hdf_file["inputs/settings"][()])
        return cs
Esempio n. 22
0
 def setUp(self):
     self.cs = settings.Settings(fName=ARMI_RUN_PATH)
     settings.setMasterCs(self.cs)
     self.o = OperatorMPI(self.cs)
     self.action = DistributeStateAction()
     self.action.o = self.o
     self.action.r = self.o.r
Esempio n. 23
0
 def test_writeInput(self):
     fName = os.path.join(TEST_ROOT, "armiRun.yaml")
     cs = settings.Settings(fName)
     baseCase = cases.Case(cs)
     with directoryChangers.TemporaryDirectoryChanger():
         case = baseCase.clone()
         case.writeInputs()
         self.assertTrue(os.path.exists(cs["shuffleLogic"]))
Esempio n. 24
0
 def setUp(self):
     self.init_mode = armi.CURRENT_MODE
     self.filepath = os.path.join(
         os.getcwd(), self._testMethodName + "test_setting_io.xml")
     self.filepathYaml = os.path.join(
         os.getcwd(), self._testMethodName + "test_setting_io.yaml")
     self.cs = settings.Settings()
     self.cs["nCycles"] = 55
Esempio n. 25
0
 def test_badDBName(self):
     cs = settings.Settings(os.path.join(TEST_ROOT, "armiRun.yaml"))
     cs["reloadDBName"] = "aRmIRuN.h5"  # weird casing to confirm robust checking
     dbi = DatabaseInterface(None, cs)
     with self.assertRaises(ValueError):
         # an error should be raised when the database loaded from
         # has the same name as the run to avoid overwriting.
         dbi.initDB()
Esempio n. 26
0
        def _getModifiedSettings(customSettings):
            cs = settings.Settings()

            newSettings = {}
            for key, val in customSettings.items():
                newSettings[key] = val

            return cs.modified(newSettings=newSettings)
Esempio n. 27
0
    def _initSettings():
        """
        Initialize settings for this entry point.

        Settings given on command line will update this data structure.
        Override to provide specific settings in the entry point.
        """
        return settings.Settings()
Esempio n. 28
0
 def test_independentVariables(self):
     """Ensure that independentVariables added to a case move with it."""
     geom = systemLayoutInput.SystemLayoutInput()
     geom.readGeomFromStream(io.StringIO(GEOM_INPUT))
     bp = blueprints.Blueprints.load(BLUEPRINT_INPUT)
     cs = settings.Settings(ARMI_RUN_PATH)
     cs["verbosity"] = "important"
     baseCase = cases.Case(cs, bp=bp, geom=geom)
     with directoryChangers.TemporaryDirectoryChanger() as cwd:  # ensure we are not in IN_USE_TEST_ROOT
         vals = {"cladThickness": 1, "control strat": "good", "enrich": 0.9}
         case = baseCase.clone()
         case._independentVariables = vals  # pylint: disable=protected-access
         case.writeInputs()
         newCs = settings.Settings(fName=case.title + ".yaml")
         newCase = cases.Case(newCs)
         for name, val in vals.items():
             self.assertEqual(newCase.independentVariables[name], val)
Esempio n. 29
0
    def test_validDefault(self):
        """Tests the settings for a default value on each setting

        :ref:`REQ7adc1f94-a423-46ca-9aff-e2276d07faa5`
        """
        cs = settings.Settings()
        for key in cs.settings.keys():
            cs[key]  # pylint: disable=pointless-statement
Esempio n. 30
0
 def setUp(self):
     self.td = directoryChangers.TemporaryDirectoryChanger()
     self.td.__enter__()
     self.init_mode = context.CURRENT_MODE
     self.filepathYaml = os.path.join(
         os.getcwd(), self._testMethodName + "test_setting_io.yaml")
     self.cs = settings.Settings()
     self.cs = self.cs.modified(newSettings={"nCycles": 55})